KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > localstore > HistoryBucket


1 /*******************************************************************************
2  * Copyright (c) 2004, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.localstore;
12
13 import java.io.*;
14 import java.util.Arrays JavaDoc;
15 import java.util.Comparator JavaDoc;
16 import org.eclipse.core.internal.utils.UniversalUniqueIdentifier;
17 import org.eclipse.core.runtime.IPath;
18
19 public class HistoryBucket extends Bucket {
20
21     /**
22      * A entry in the bucket index. Each entry has one path and a collection
23      * of states, which by their turn contain a (UUID, timestamp) pair.
24      * <p>
25      * This class is intended as a lightweight way of hiding the internal data structure.
26      * Objects of this class are supposed to be short-lived. No instances
27      * of this class are kept stored anywhere. The real stuff (the internal data structure)
28      * is.
29      * </p>
30      */

31     public static final class HistoryEntry extends Bucket.Entry {
32
33         final static Comparator JavaDoc COMPARATOR = new Comparator JavaDoc() {
34             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
35                 byte[] state1 = (byte[]) o1;
36                 byte[] state2 = (byte[]) o2;
37                 return compareStates(state1, state2);
38             }
39         };
40
41         // the length of each component of the data array
42
private final static byte[][] EMPTY_DATA = new byte[0][];
43         // the length of a long in bytes
44
private final static int LONG_LENGTH = 8;
45         // the length of a UUID in bytes
46
private final static int UUID_LENGTH = UniversalUniqueIdentifier.BYTES_SIZE;
47         public final static int DATA_LENGTH = UUID_LENGTH + LONG_LENGTH;
48
49         /**
50          * The history states. The first array dimension is the number of states. The
51          * second dimension is an encoding of the {UUID,timestamp} pair for that entry.
52          */

53         private byte[][] data;
54
55         /**
56          * Comparison logic for states in byte[] form.
57          *
58          * @see Comparator#compare(java.lang.Object, java.lang.Object)
59          */

60         private static int compareStates(byte[] state1, byte[] state2) {
61             long timestamp1 = getTimestamp(state1);
62             long timestamp2 = getTimestamp(state2);
63             if (timestamp1 == timestamp2)
64                 return -UniversalUniqueIdentifier.compareTime(state1, state2);
65             return timestamp1 < timestamp2 ? +1 : -1;
66         }
67
68         /**
69          * Returns the byte array representation of a (UUID, timestamp) pair.
70          */

71         private static byte[] getState(UniversalUniqueIdentifier uuid, long timestamp) {
72             byte[] uuidBytes = uuid.toBytes();
73             byte[] state = new byte[DATA_LENGTH];
74             System.arraycopy(uuidBytes, 0, state, 0, uuidBytes.length);
75             for (int j = 0; j < LONG_LENGTH; j++) {
76                 state[UUID_LENGTH + j] = (byte) (0xFF & timestamp);
77                 timestamp >>>= 8;
78             }
79             return state;
80         }
81
82         private static long getTimestamp(byte[] state) {
83             long timestamp = 0;
84             for (int j = 0; j < LONG_LENGTH; j++)
85                 timestamp += (state[UUID_LENGTH + j] & 0xFFL) << j * 8;
86             return timestamp;
87         }
88
89         /**
90          * Inserts the given item into the given array at the right position.
91          * Returns the resulting array. Returns null if the item already exists.
92          */

93         private static byte[][] insert(byte[][] existing, byte[] toAdd) {
94             // look for the right spot where to insert the new guy
95
int index = search(existing, toAdd);
96             if (index >= 0)
97                 // already there - nothing else to be done
98
return null;
99             // not found - insert
100
int insertPosition = -index - 1;
101             byte[][] newValue = new byte[existing.length + 1][];
102             if (insertPosition > 0)
103                 System.arraycopy(existing, 0, newValue, 0, insertPosition);
104             newValue[insertPosition] = toAdd;
105             if (insertPosition < existing.length)
106                 System.arraycopy(existing, insertPosition, newValue, insertPosition + 1, existing.length - insertPosition);
107             return newValue;
108         }
109
110         /**
111          * Merges two entries (are always sorted). Duplicates are discarded.
112          */

113         private static byte[][] merge(byte[][] base, byte[][] additions) {
114             int additionPointer = 0;
115             int basePointer = 0;
116             int added = 0;
117             byte[][] result = new byte[base.length + additions.length][];
118             while (basePointer < base.length && additionPointer < additions.length) {
119                 int comparison = compareStates(base[basePointer], additions[additionPointer]);
120                 if (comparison == 0) {
121                     result[added++] = base[basePointer++];
122                     // duplicate, ignore
123
additionPointer++;
124                 } else if (comparison < 0)
125                     result[added++] = base[basePointer++];
126                 else
127                     result[added++] = additions[additionPointer++];
128             }
129             // copy the remaining states from either additions or base arrays
130
byte[][] remaining = basePointer == base.length ? additions : base;
131             int remainingPointer = basePointer == base.length ? additionPointer : basePointer;
132             int remainingCount = remaining.length - remainingPointer;
133             System.arraycopy(remaining, remainingPointer, result, added, remainingCount);
134             added += remainingCount;
135             if (added == base.length + additions.length)
136                 // no collisions
137
return result;
138             // there were collisions, need to compact
139
byte[][] finalResult = new byte[added][];
140             System.arraycopy(result, 0, finalResult, 0, finalResult.length);
141             return finalResult;
142         }
143
144         private static int search(byte[][] existing, byte[] element) {
145             return Arrays.binarySearch(existing, element, COMPARATOR);
146         }
147
148         public HistoryEntry(IPath path, byte[][] data) {
149             super(path);
150             this.data = data;
151         }
152
153         public HistoryEntry(IPath path, HistoryEntry base) {
154             super(path);
155             this.data = new byte[base.data.length][];
156             System.arraycopy(base.data, 0, this.data, 0, this.data.length);
157         }
158
159         /**
160          * Compacts the data array removing any null slots. If non-null slots
161          * are found, the entry is marked for removal.
162          */

163         private void compact() {
164             if (!isDirty())
165                 return;
166             int occurrences = 0;
167             for (int i = 0; i < data.length; i++)
168                 if (data[i] != null)
169                     data[occurrences++] = data[i];
170             if (occurrences == data.length)
171                 // no states deleted
172
return;
173             if (occurrences == 0) {
174                 // no states remaining
175
data = EMPTY_DATA;
176                 delete();
177                 return;
178             }
179             byte[][] result = new byte[occurrences][];
180             System.arraycopy(data, 0, result, 0, occurrences);
181             data = result;
182         }
183
184         public void deleteOccurrence(int i) {
185             markDirty();
186             data[i] = null;
187         }
188
189         byte[][] getData() {
190             return data;
191         }
192
193         public int getOccurrences() {
194             return data.length;
195         }
196
197         public long getTimestamp(int i) {
198             return getTimestamp(data[i]);
199         }
200
201         public UniversalUniqueIdentifier getUUID(int i) {
202             return new UniversalUniqueIdentifier(data[i]);
203         }
204
205         public Object JavaDoc getValue() {
206             return data;
207         }
208
209         public boolean isEmpty() {
210             return data.length == 0;
211         }
212
213         public void visited() {
214             compact();
215         }
216
217     }
218
219     /**
220      * Version number for the current implementation file's format.
221      * <p>
222      * Version 2 (3.1 M5):
223      * <pre>
224      * FILE ::= VERSION_ID ENTRY+
225      * ENTRY ::= PATH STATE_COUNT STATE+
226      * PATH ::= string (does not include project name)
227      * STATE_COUNT ::= int
228      * STATE ::= UUID LAST_MODIFIED
229      * UUID ::= byte[16]
230      * LAST_MODIFIED ::= byte[8]
231      * </pre>
232      * </p>
233      * <p>
234      * Version 1 (3.1 M4):
235      * <pre>
236      * FILE ::= VERSION_ID ENTRY+
237      * ENTRY ::= PATH STATE_COUNT STATE+
238      * PATH ::= string
239      * STATE_COUNT ::= int
240      * STATE ::= UUID LAST_MODIFIED
241      * UUID ::= byte[16]
242      * LAST_MODIFIED ::= byte[8]
243      * </pre>
244      * </p>
245      */

246     public final static byte VERSION = 2;
247
248     public HistoryBucket() {
249         super();
250     }
251
252     public void addBlob(IPath path, UniversalUniqueIdentifier uuid, long lastModified) {
253         byte[] state = HistoryEntry.getState(uuid, lastModified);
254         String JavaDoc pathAsString = path.toString();
255         byte[][] existing = (byte[][]) getEntryValue(pathAsString);
256         if (existing == null) {
257             setEntryValue(pathAsString, new byte[][] {state});
258             return;
259         }
260         byte[][] newValue = HistoryEntry.insert(existing, state);
261         if (newValue == null)
262             return;
263         setEntryValue(pathAsString, newValue);
264     }
265
266     public void addBlobs(HistoryEntry fileEntry) {
267         IPath path = fileEntry.getPath();
268         byte[][] additions = fileEntry.getData();
269         String JavaDoc pathAsString = path.toString();
270         byte[][] existing = (byte[][]) getEntryValue(pathAsString);
271         if (existing == null) {
272             setEntryValue(pathAsString, additions);
273             return;
274         }
275         setEntryValue(pathAsString, HistoryEntry.merge(existing, additions));
276     }
277
278     protected Bucket.Entry createEntry(IPath path, Object JavaDoc value) {
279         return new HistoryEntry(path, (byte[][]) value);
280     }
281
282     public HistoryEntry getEntry(IPath path) {
283         String JavaDoc pathAsString = path.toString();
284         byte[][] existing = (byte[][]) getEntryValue(pathAsString);
285         if (existing == null)
286             return null;
287         return new HistoryEntry(path, existing);
288     }
289
290     protected String JavaDoc getIndexFileName() {
291         return "history.index"; //$NON-NLS-1$
292
}
293     
294     protected byte getVersion() {
295         return VERSION;
296     }
297
298     protected String JavaDoc getVersionFileName() {
299         return "history.version"; //$NON-NLS-1$
300
}
301
302     protected Object JavaDoc readEntryValue(DataInputStream source) throws IOException {
303         int length = source.readUnsignedShort();
304         byte[][] uuids = new byte[length][HistoryEntry.DATA_LENGTH];
305         for (int j = 0; j < uuids.length; j++)
306             source.read(uuids[j]);
307         return uuids;
308     }
309
310     protected void writeEntryValue(DataOutputStream destination, Object JavaDoc entryValue) throws IOException {
311         byte[][] uuids = (byte[][]) entryValue;
312         destination.writeShort(uuids.length);
313         for (int j = 0; j < uuids.length; j++)
314             destination.write(uuids[j]);
315     }
316 }
317
Popular Tags