Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 4e699321af1ad1f4e78d7a7ce87fe9b7f3f262d2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*******************************************************************************
 * Copyright (c) 2012, 2015 Wind River Systems, Inc. and others. All rights reserved.
 * This program and the accompanying materials are made available under the terms
 * of the Eclipse Public License v1.0 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.tcf.te.tcf.filesystem.core.internal.utils;

import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.OutputStream;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.tcf.te.runtime.callback.Callback;
import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation;
import org.eclipse.tcf.te.tcf.filesystem.core.internal.FSTreeNode;
import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.OpCacheFileDigest;
import org.eclipse.tcf.te.tcf.filesystem.core.model.CacheState;

/**
 * The state object to describe a file's state.
 */
public class FileState {
	/**
	 * The base digest of the file data.
	 */
	private byte[] base_digest = null;

	/**
	 * The message digest of the file data.
	 */
	private byte[] target_digest = null;

	/**
	 * The message digest of the local cache data
	 */
	private byte[] cache_digest = null;

	/**
	 * The cache file's modification time.
	 */
	private long cache_mtime;

	/**
	 * If the job that computes the local cache's digest is running.
	 */
	transient boolean cache_digest_running = false;

	/**
	 * If the job that computes the target file's digest is running.
	 */
	transient boolean target_digest_running = false;

	/**
	 * The file system node whose state is described.
	 */
	private transient FSTreeNode node;

	/**
	 * Create a file state using the node.
	 *
	 * @param node The file system node.
	 */
	public FileState(FSTreeNode node) {
		this.node = node;
	}

	/**
	 * Create a file state using the specified state data.
	 *
	 * @param mtime The cache file's modification time.
	 * @param cache_digest The cache file's digest.
	 * @param target_digest The target file's digest.
	 * @param base_digest The baseline digest.
	 */
	public FileState(long mtime, byte[] cache_digest, byte[] target_digest, byte[]base_digest) {
		this.cache_mtime = mtime;
		this.cache_digest = cache_digest;
		this.target_digest = target_digest;
		this.base_digest = base_digest;
	}

	/**
	 * Set the file system node.
	 *
	 * @param node The file system node.
	 */
	void setNode(FSTreeNode node) {
		this.node = node;
	}

	/**
	 * Get the node's target file digest.
	 *
	 * @return The target file digest.
	 */
	public byte[] getTargetDigest() {
		return target_digest;
	}

	/**
	 * Get the node's baseline digest.
	 *
	 * @return The baseline digest.
	 */
	public byte[] getBaseDigest() {
		return base_digest;
	}

	/**
	 * Get the node's cache file modification time.
	 *
	 * @return The cache file's modification time.
	 */
	public long getCacheMTime() {
		return cache_mtime;
	}

	/**
	 * Get the node's cache file digest.
	 *
	 * @return The cache file digest.
	 */
	public byte[] getCacheDigest() {
		return cache_digest;
	}

	/**
	 * Update the cache state of this file and invoke callback once the update is done.
	 * If the callback is null, then do not invoke any callback.
	 *
	 * @param callback Callback invoked after updating.
	 */
	public synchronized void updateState(final ICallback callback) {
		File file = CacheManager.getCacheFile(node);
		if (file.exists()) {
			long cache_mtime = file.lastModified();
			if (!cache_digest_running && (cache_digest == null || this.cache_mtime != cache_mtime)) {
				cache_digest_running = true;
				this.cache_mtime = cache_mtime;
				final OpCacheFileDigest op = new OpCacheFileDigest(node);
				op.runInJob(new Callback() {
					@Override
					protected void internalDone(Object caller, IStatus status) {
						if (status.isOK()) {
							updateCacheDigest(op.getDigest());
						}
						cache_digest_running = false;
						if (status.isOK()) {
							updateState(callback);
						}
						else if(callback != null){
							callback.done(this, status);
						}
					}
				});
			}
			else if (!target_digest_running && target_digest == null) {
				target_digest_running = true;
				final IOperation op = node.operationDownload(new OutputStream() {
					@Override
					public void write(int b) {
					}
				});
				op.runInJob(new Callback() {
					@Override
                    protected void internalDone(Object caller, IStatus status) {
						target_digest_running = false;
						if (status.isOK()) {
							updateState(callback);
						} else if(callback != null){
							callback.done(this, status);
						}
                    }
				});
			} else if (callback != null) {
				callback.done(this, Status.OK_STATUS);
			}
		} else if (callback != null) {
			callback.done(this, Status.OK_STATUS);
		}
	}

	/**
	 * Get this node's cache state using the current state data.
	 *
	 * @return The state expressed in a CacheState enum value.
	 */
	public synchronized CacheState getCacheState() {
		File file = CacheManager.getCacheFile(node);
		if (!file.exists()) return CacheState.consistent;
		updateState(null);
		if (cache_digest == null || target_digest == null)
			return CacheState.consistent;
		if (isUnchanged(target_digest, cache_digest)) {
			base_digest = target_digest;
			return CacheState.consistent;
		}
		if(isUnchanged(base_digest, cache_digest)){
			return CacheState.outdated;
		}
		if (isUnchanged(target_digest, base_digest)) {
			return CacheState.modified;
		}
		return CacheState.conflict;
	}

	/**
	 * Update the node's target digest and fire an event.
	 *
	 * @param target_digest The new target digest data.
	 */
	public void updateTargetDigest(byte[] target_digest) {
		this.target_digest = target_digest;
		PropertyChangeEvent event = new PropertyChangeEvent(this, "target_digest", null, target_digest); //$NON-NLS-1$
		node.getRuntimeModel().firePropertyChanged(event);
	}

	/**
	 * Compare the two digests to see if they are equal to each other.
	 *
	 * @param digest1 The first digest.
	 * @param digest2 The second digest.
	 * @return true if they are equal.
	 */
	private boolean isUnchanged(byte[] digest1, byte[] digest2){
		if(digest1 != null && digest2 != null && digest1.length == digest2.length) {
			for (int i = 0; i < digest1.length; i++) {
				if(digest1[i] != digest2[i]) return false;
			}
			return true;
		}
		return false;
	}

	/**
	 * Update the cache file digest data and fire an event.
	 *
	 * @param cache_digest The new cache file digest data.
	 */
	public void updateCacheDigest(byte[] cache_digest) {
		byte[] old_digest = cache_digest;
		this.cache_digest = cache_digest;
		PropertyChangeEvent event = new PropertyChangeEvent(node, "cache_digest", old_digest, cache_digest); //$NON-NLS-1$
		node.getRuntimeModel().firePropertyChanged(event);
    }

	/**
	 * Reset all of the node's digest data to a new digest data.
	 *
	 * @param digest The new digest data.
	 */
	public void reset(byte[] digest) {
		cache_digest = digest;
		target_digest = digest;
		base_digest = digest;
		PropertyChangeEvent event = new PropertyChangeEvent(node, "reset_digest", null, digest); //$NON-NLS-1$
		node.getRuntimeModel().firePropertyChanged(event);
    }
}

Back to the top