View Javadoc

1   /**
2    * Copyright 2005 Steve Molloy
3    * 
4    * This file is part of OV4J.
5    * 
6    * OV4J is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
7    * published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
8    * 
9    * OV4J is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
10   * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11   * 
12   * You should have received a copy of the GNU General Public License along with OV4J; if not, write to the Free Software
13   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
14   * 
15   */
16  package org.ov4j.data;
17  
18  import java.io.Serializable;
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  import java.util.Arrays;
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.Vector;
25  import java.util.logging.Level;
26  import java.util.logging.Logger;
27  
28  /**
29   * This is the objects stored in the system, it holds all versions for a given ID.
30   * 
31   * @author smolloy
32   * 
33   */
34  public class Item<T extends Comparable<? super T> & Cloneable & Serializable, C extends Comparable<? super C>>
35  	implements Serializable, Cloneable {
36  	/**
37  	 * Logger for this class
38  	 */
39  	private static final Logger			logger				= Logger.getLogger(Item.class.getName());
40  
41  	/** Serial UID */
42  	private static final long			serialVersionUID	= -798415341492380859L;
43  
44  	/** Empty Item array. */
45  	public static final Item<?, ?>[]	EMPTY_ARRAY			= new Item[0];
46  
47  	/** Use this version rollover to keep whole history. */
48  	public static final int				NO_ROLLOVER			= -1;
49  
50  	/** The ID. */
51  	private Object						id;
52  
53  	/** Last commit timestamp */
54  	private long						modificationStamp;
55  
56  	/** The latest version. */
57  	private Version<T>					latest;
58  
59  	/** All of the versions. */
60  	private Vector<Version<T>>			versions;
61  
62  	/**
63  	 * Constructor.
64  	 */
65  	public Item() {
66  	}
67  
68  	/**
69  	 * Add a version to this item.
70  	 * 
71  	 * @param version
72  	 *            The new version to be added.
73  	 * @param versionRollover
74  	 *            How many versions to keep in history.
75  	 * @return True if the version was successfully added.
76  	 */
77  	public boolean addVersion(final Version<T> version, final int versionRollover) {
78  		if (Item.logger.isLoggable(Level.FINER)) {
79  			Item.logger
80  				.entering("Item", "addVersion(Version<T>=" + version + ", int=" + versionRollover + ")", "start");
81  		}
82  
83  		boolean res = false;
84  		int newVer = version.getVersionNumber();
85  		if (newVer < 0) {
86  			newVer = numVersions();
87  		}
88  		if (newVer == numVersions()) {
89  			version.setVersionNumber(newVer + 1);
90  			if (versions == null) {
91  				versions = new Vector<Version<T>>();
92  			} else if (versionRollover != Item.NO_ROLLOVER && versions.size() > versionRollover - 1) {
93  				versions.remove(versions.firstElement());
94  			}
95  			versions.add(version);
96  			latest = version;
97  			res = true;
98  		}
99  
100 		if (Item.logger.isLoggable(Level.FINER)) {
101 			Item.logger.exiting("Item", "addVersion(Version<T>=" + version + ", int=" + versionRollover + ")",
102 				"end - return value=" + res);
103 		}
104 		return res;
105 	}
106 
107 	/**
108 	 * @see java.lang.Object#clone()
109 	 */
110 	@Override
111 	public Item<T, C> clone() throws CloneNotSupportedException {
112 		if (Item.logger.isLoggable(Level.FINEST)) {
113 			Item.logger.entering("Item", "clone()", "start");
114 		}
115 
116 		final Item<T, C> it = (Item<T, C>) super.clone();
117 		if (id != null) {
118 			try {
119 				final Method clone = id.getClass().getMethod("clone", new Class[0]);
120 				it.id = clone.invoke(id, new Object[0]);
121 			} catch (final SecurityException e) {
122 				if (Item.logger.isLoggable(Level.FINE)) {
123 					Item.logger.logp(Level.FINE, "Item", "clone()", "Exception caught", e);
124 				}
125 
126 				throw new CloneNotSupportedException(e.toString());
127 			} catch (final NoSuchMethodException e) {
128 				if (Item.logger.isLoggable(Level.FINE)) {
129 					Item.logger.logp(Level.FINE, "Item", "clone()", "Exception caught", e);
130 				}
131 
132 				throw new CloneNotSupportedException(e.toString());
133 			} catch (final IllegalArgumentException e) {
134 				if (Item.logger.isLoggable(Level.FINE)) {
135 					Item.logger.logp(Level.FINE, "Item", "clone()", "Exception caught", e);
136 				}
137 
138 				throw new CloneNotSupportedException(e.toString());
139 			} catch (final IllegalAccessException e) {
140 				if (Item.logger.isLoggable(Level.FINE)) {
141 					Item.logger.logp(Level.FINE, "Item", "clone()", "Exception caught", e);
142 				}
143 
144 				throw new CloneNotSupportedException(e.toString());
145 			} catch (final InvocationTargetException e) {
146 				if (Item.logger.isLoggable(Level.FINE)) {
147 					Item.logger.logp(Level.FINE, "Item", "clone()", "Exception caught", e);
148 				}
149 
150 				throw new CloneNotSupportedException(e.toString());
151 			}
152 		}
153 		if (versions != null) {
154 			it.versions = new Vector<Version<T>>();
155 			final Iterator<Version<T>> verIt = versions.iterator();
156 			while (verIt.hasNext()) {
157 				it.versions.add(verIt.next().clone());
158 			}
159 		}
160 		if (latest != null) {
161 			it.setLatest(latest.clone());
162 		}
163 
164 		if (Item.logger.isLoggable(Level.FINEST)) {
165 			Item.logger.exiting("Item", "clone()", "end - return value=" + it);
166 		}
167 		return it;
168 	}
169 
170 	/**
171 	 * @see java.lang.Object#equals(java.lang.Object)
172 	 */
173 	@Override
174 	public boolean equals(final Object o) {
175 		if (Item.logger.isLoggable(Level.FINEST)) {
176 			Item.logger.entering("Item", "equals(Object=" + o + ")", "start");
177 		}
178 
179 		boolean returnboolean = (o != null && o instanceof Item);
180 		if (returnboolean) {
181 			final Item<T, C> it = (Item<T, C>) o;
182 			returnboolean = (it.getId().equals(getId()) && it.getModificationStamp() == getModificationStamp());
183 		}
184 
185 		if (Item.logger.isLoggable(Level.FINEST)) {
186 			Item.logger.exiting("Item", "equals(Object=" + o + ")", "end - return value=" + returnboolean);
187 		}
188 		return returnboolean;
189 	}
190 
191 	/**
192 	 * @return Returns the id.
193 	 */
194 	public C getId() {
195 		return (C) id;
196 	}
197 
198 	/**
199 	 * 
200 	 * @return The latest version of this item.
201 	 */
202 	public Version<T> getLatest() {
203 		return latest;
204 	}
205 
206 	/**
207 	 * @param tstamp
208 	 *            Timestamp to use for fetching the version.
209 	 * 
210 	 * @return the latest version of this item before the specified timstamp.
211 	 */
212 	public Version<T> getLatestBefore(final long tstamp) {
213 		if (Item.logger.isLoggable(Level.FINEST)) {
214 			Item.logger.entering("Item", "getLatestBefore(long=" + tstamp + ")", "start");
215 		}
216 
217 		Version<T> result = null;
218 		if (tstamp > modificationStamp) {
219 			result = latest;
220 		} else if (versions != null) {
221 			for (int i = versions.size(); result == null && i >= 0; i--) {
222 				final Version<T> current = versions.get(i);
223 				if (current.getVersionTimestamp() < tstamp) {
224 					result = current;
225 				}
226 			}
227 		}
228 
229 		if (Item.logger.isLoggable(Level.FINEST)) {
230 			Item.logger.entering("Item", "getLatestBefore(long=" + tstamp + ")", "end - return value=" + result);
231 		}
232 		return result;
233 	}
234 
235 	/**
236 	 * @return Returns the modificationStamp.
237 	 */
238 	public long getModificationStamp() {
239 		return modificationStamp;
240 	}
241 
242 	/**
243 	 * @return Returns the versions.
244 	 */
245 	public Version<T>[] getVersions() {
246 		if (Item.logger.isLoggable(Level.FINEST)) {
247 			Item.logger.entering("Item", "getVersions()", "start");
248 		}
249 
250 		if (versions != null) {
251 			final Version<T>[] returnVersionArray = versions.toArray((Version<T>[]) Version.EMPTY_ARRAY);
252 
253 			if (Item.logger.isLoggable(Level.FINEST)) {
254 				Item.logger.exiting("Item", "getVersions()", "end - return value="
255 					+ Arrays.toString(returnVersionArray));
256 			}
257 			return returnVersionArray;
258 		}
259 
260 		if (Item.logger.isLoggable(Level.FINEST)) {
261 			Item.logger.exiting("Item", "getVersions()", "end - return value=" + null);
262 		}
263 		return null;
264 	}
265 
266 	/**
267 	 * @see java.lang.Object#hashCode()
268 	 */
269 	@Override
270 	public int hashCode() {
271 		return (int) ((id == null ? 0 : id.hashCode()) + modificationStamp);
272 	}
273 
274 	/**
275 	 * @return Number of versions in this item.
276 	 */
277 	public int numVersions() {
278 		if (Item.logger.isLoggable(Level.FINEST)) {
279 			Item.logger.entering("Item", "numVersions()", "start");
280 		}
281 
282 		if (latest == null) {
283 			if (Item.logger.isLoggable(Level.FINEST)) {
284 				Item.logger.exiting("Item", "numVersions()", "end - return value=" + 0);
285 			}
286 			return 0;
287 		}
288 
289 		final int returnint = latest.getVersionNumber();
290 
291 		if (Item.logger.isLoggable(Level.FINEST)) {
292 			Item.logger.exiting("Item", "numVersions()", "end - return value=" + returnint);
293 		}
294 		return returnint;
295 	}
296 
297 	/**
298 	 * @param id
299 	 *            The id to set.
300 	 */
301 	public void setId(final C id) {
302 		this.id = id;
303 	}
304 
305 	/**
306 	 * 
307 	 * @param latest
308 	 *            The latest version to set.
309 	 */
310 	public void setLatest(final Version<T> latest) {
311 		this.latest = latest;
312 	}
313 
314 	/**
315 	 * @param modificationStamp
316 	 *            The modificationStamp to set.
317 	 */
318 	public void setModificationStamp(final long modificationStamp) {
319 		this.modificationStamp = modificationStamp;
320 	}
321 
322 	/**
323 	 * @param versions
324 	 *            The versions to set.
325 	 */
326 	public void setVersions(final Version<T>[] newVersions) {
327 		if (Item.logger.isLoggable(Level.FINEST)) {
328 			Item.logger.entering("Item", "setVersions(Version<T>[]=" + Arrays.toString(newVersions) + ")", "start");
329 		}
330 
331 		versions(newVersions);
332 		if (versions == null || versions.size() == 0) {
333 			latest = null;
334 		} else {
335 			latest = versions.lastElement();
336 		}
337 
338 		if (Item.logger.isLoggable(Level.FINEST)) {
339 			Item.logger.exiting("Item", "setVersions(Version<T>[]=" + Arrays.toString(newVersions) + ")", "end");
340 		}
341 	}
342 
343 	/**
344 	 * @see java.lang.Object#toString()
345 	 */
346 	@Override
347 	public String toString() {
348 		return "Item[" + String.valueOf(id) + "], (" + ((latest == null) ? 0 : latest.getVersionNumber())
349 			+ " versions) latest: " + ((latest == null) ? "null" : latest.toString() + "]");
350 	}
351 
352 	/**
353 	 * @param versions
354 	 *            The versions to set.
355 	 */
356 	public void versions(final Version<T>[] newVersions) {
357 		if (Item.logger.isLoggable(Level.FINEST)) {
358 			Item.logger.entering("Item", "versions(Version<T>[]=" + Arrays.toString(newVersions) + ")", "start");
359 		}
360 
361 		if (versions == null) {
362 			versions = new Vector<Version<T>>();
363 		} else {
364 			versions.clear();
365 		}
366 		if (newVersions != null) {
367 			Collections.addAll(versions, newVersions);
368 		}
369 
370 		if (Item.logger.isLoggable(Level.FINEST)) {
371 			Item.logger.exiting("Item", "versions(Version<T>[]=" + Arrays.toString(newVersions) + ")", "end");
372 		}
373 	}
374 }