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 }