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 }