/* * @(#) Storage.java - Root abstract class for storage of objects. * (c) 2000-2001 Ivan Maidanski http://ivmai.chat.ru * Freeware class library sources. All rights reserved. ** * Language: Java [pure] * Tested with: JDK v1.1.6 * Last modified: 2001-07-30 14:20:00 GMT+04:00 */ /* * This software is the proprietary information of the author. ** * Permission to use, copy, and distribute this software and its * documentation for non-commercial purposes and without fee is * hereby granted provided that this copyright notice appears in all * copies. ** * This software should not be modified in any way; any found bug * should be reported to the author. ** * The author disclaims all warranties with regard to this software, * including all implied warranties of merchantability and fitness. * In no event shall the author be liable for any special, indirect * or consequential damages or any damages whatsoever resulting from * loss of use, data or profits, whether in an action of contract, * negligence or other tortuous action, arising out of or in * connection with the use or performance of this software. */ package ivmai.storage; import ivmai.util.GComparator; import ivmai.util.MultiObservable; import ivmai.util.ObservedCore; import ivmai.util.ReallyCloneable; import ivmai.util.Sortable; import ivmai.util.ToString; /** * Root abstract class for storage of objects. ** * @version 2.0 * @author Ivan Maidanski */ public abstract class Storage extends ObservedCore implements ReallyCloneable, Sortable { public Storage() {} /** * NOTE: Shallow "trim-to-size" is performed. Must be synchronized * outside. */ public void trimToSize() { super.trimToSize(); int location; try { minimizeCapacity(); } catch (OutOfMemoryError e) {} location = 0; } /** * NOTE: The effectiveness is linear. Locations are not re-ordered. * If OutOfMemoryError is thrown then storage remains unchanged. * Must be synchronized outside. */ protected abstract void minimizeCapacity(); /** * NOTE: Must be synchronized outside. */ public final void clear() { int location = nextLocation(0, false), prevLoc; while (location > 0) { try { prevLoc = nextLocation(location, false); } catch (IllegalArgumentException e) { if ((location = nextLocation(0, false)) <= 0) break; prevLoc = nextLocation(location, false); } try { setAt(location, null); } catch (ArrayStoreException e) { break; } location = prevLoc; } location = 0; } /** * NOTE: Result is an empty location (result > 0). Storage state is * not altered. The effectiveness is constant (typically). The order * of empty locations is undefined. Requires no synchronization. */ public abstract int emptyLocation(); /** * NOTE: This method must be called after any atomary changes * committed (inside insertAt/setAt operations mainly). If location * > 0 then storage event is addition or modification (if oldValue * != null) at location. Else if -location > 0 and oldValue != null * then storage event is removal of oldValue after prevLoc (if 0 >= * prevLoc then first child of -prevLoc is removed else next sibling * of prevLoc is removed) at -location. */ protected final void notifyObservers(int prevLoc, int location, Object oldValue) { if (hasObservers()) super.notifyObservers(this, new StorageEvent(this, prevLoc, location, oldValue)); } /** * NOTE: It is a dummy method which hides the same method in the * superclass. */ public final void notifyObservers(MultiObservable observed, Object argument) {} /** * NOTE: If prevLoc != 0 and (prevLoc > 0 ? prevLoc : -prevLoc) is * an "empty" (including negative) location then * IllegalArgumentException is thrown. If 0 > emptyLocation or * emptyLocation is not an "empty" location then * IllegalArgumentException is thrown. emptyLocation is the location * of the inserted value unless emptyLocation == 0 (means any empty * location). If value == null or insertion is impossible then * ArrayStoreException is thrown. Enough capacity is ensured before * any changes. New value is inserted as a first child node of * -prevLoc (if 0 >= prevLoc) or as a next sibling node of prevLoc * (if prevLoc > 0). Result is the location of the inserted value * (result is a "valid" location if prevLoc == 0 or (prevLoc > 0 ? * prevLoc : -prevLoc) is a "valid" location). Alters storage state. * value itself is not modified. The effectiveness is constant * (typically). Observers notification is performed. Must be * synchronized outside. If exception is thrown then storage remains * unchanged. */ public abstract int insertAt(int prevLoc, int emptyLocation, Object value) throws IllegalArgumentException, ArrayStoreException; /** * NOTE: If 0 >= location then IllegalArgumentException is thrown. * value may be == null (if accepted semantically), result * (oldValue) may be == null. If operation is impossible (according * to the storage semantics) then ArrayStoreException is thrown. * Enough capacity is ensured before any changes. Alters storage * state. value itself is not modified. The effectiveness is * constant (typically) or logarithmic (or linear when deleting an * inner tree node). Observers notification is performed. Must be * synchronized outside. If exception is thrown then storage remains * unchanged. */ public abstract Object setAt(int location, Object value) throws IllegalArgumentException, ArrayStoreException; /** * NOTE: Result != null if and only if location is not "empty" (only * positive locations may be not "empty"). Storage state is not * altered. The effectiveness is constant. Requires no * synchronization. */ public abstract Object getAt(int location); /** * NOTE: Result is true if and only if location is "valid" (only not * "empty" locations may be "valid"). Storage state is not altered. * The effectiveness is constant. Must be synchronized outside. */ public abstract boolean isValidAt(int location); /** * NOTE: If value == null then ArrayStoreException is thrown. If * unique is false or this storage has not contained the specified * value yet then value is added to the storage. Anyway, its * location is returned. Must be synchronized outside. */ public final int add(Object value, boolean unique) throws ArrayStoreException { if (value == null) throw new ArrayStoreException("value: null"); int location; if (!unique || (location = locationOf(value, 0, true)) <= 0) setAt(location = emptyLocation(), value); return location; } /** * NOTE: value may be == null. Only first found value occurance is * removed. If removal is denied then ArrayStoreException is thrown. * Result is either 0 (not found) or an "empty" location of removed * value. Must be synchronized outside. */ public final int remove(Object value) throws ArrayStoreException { int location; if ((location = locationOf(value, 0, true)) > 0) setAt(location, null); return location; } /** * NOTE: If 0 >= location then IllegalArgumentException is thrown. * If removal is denied then ArrayStoreException is thrown. Must be * synchronized outside. */ public final Object removeAt(int location) throws IllegalArgumentException, ArrayStoreException { return setAt(location, null); } /** * NOTE: If exception is thrown then nothing is changed. Result is * remainder of len (that is, result is the count of not processed * values due to ArrayStoreException). array and its elements are * not modified. Must be synchronized outside. */ public final int insertAtAll(int prevLoc, Object[] array, int offset, int len) throws NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException { if (len <= 0) { len = array.length; return 0; } Object value = array[offset]; value = array[offset + len - 1]; try { do { prevLoc = insertAt(prevLoc, 0, array[offset++]); } while (--len > 0); } catch (ArrayStoreException e) {} return len; } /** * NOTE: If exception is thrown then nothing is changed. If unique * is true and this storage has already contained value then it is * skipped. Result is remainder of len (that is, result is the count * of not processed values due to null or ArrayStoreException). * array and its elements are not modified. Must be synchronized * outside. */ public final int addAll(Object[] array, int offset, int len, boolean unique) throws NullPointerException, ArrayIndexOutOfBoundsException { if (len <= 0) { len = array.length; return 0; } Object value = array[offset]; value = array[offset + len - 1]; do { if ((value = array[offset++]) == null) break; if (!unique || locationOf(value, 0, true) <= 0) { try { setAt(emptyLocation(), value); } catch (ArrayStoreException e) { break; } } } while (--len > 0); return len; } /** * NOTE: If exception is thrown then nothing is changed. Result is * remainder of len (that is, result is the count of not processed * values due to ArrayStoreException). array and its elements are * not modified. Must be synchronized outside. */ public final int removeAll(Object[] array, int offset, int len) throws NullPointerException, ArrayIndexOutOfBoundsException { if (len <= 0) { len = array.length; return 0; } int location; Object value = array[offset]; value = array[offset + len - 1]; do { if ((location = locationOf(array[offset++], 0, true)) > 0) { try { setAt(location, null); } catch (ArrayStoreException e) { break; } } } while (--len > 0); return len; } /** * NOTE: If exception is thrown then nothing is changed. Result is * remainder of len (that is, result is the count of not processed * values due to null or not found). array and its elements are not * modified. Must be synchronized outside. */ public final int containsAll(Object[] array, int offset, int len) throws NullPointerException, ArrayIndexOutOfBoundsException { if (len <= 0) { len = array.length; return 0; } Object value = array[offset]; value = array[offset + len - 1]; while (locationOf(array[offset++], 0, true) > 0 && --len > 0); return len; } /** * NOTE: Result is either 0 or location of the first element in * storage (after storagePrevLocation) at which value insertion has * failed here. storage and its elements are not modified. Must be * synchronized outside, storage must be synchronized too. */ public final int insertAtAll(int prevLoc, Storage storage, int storagePrevLocation) throws NullPointerException, IllegalArgumentException { do { int storageNext; if ((storageNext = storage.childLocation(storagePrevLocation, true)) > 0) prevLoc = -prevLoc; else while (prevLoc > 0 && storagePrevLocation > 0 && (storageNext = storage.siblingLocation(storagePrevLocation, true)) <= 0) { storagePrevLocation = storage.parentLocation(storagePrevLocation); prevLoc = parentLocation(prevLoc); } if (storageNext <= 0) break; Object value = storage.getAt(storagePrevLocation = storageNext); try { prevLoc = insertAt(prevLoc, 0, value); } catch (ArrayStoreException e) { break; } } while (true); return storagePrevLocation; } /** * NOTE: If unique is true and this storage has already contained * value then it is skipped. Result is either 0 or location of the * first element in storage (after storagePrevLocation) at which * value addition has failed here. storage and its elements are not * modified. Must be synchronized outside, storage must be * synchronized too. */ public final int addAll(Storage storage, int storagePrevLocation, boolean unique) throws NullPointerException, IllegalArgumentException { while ((storagePrevLocation = storage.nextLocation(storagePrevLocation, true)) > 0) { Object value = storage.getAt(storagePrevLocation); if (!unique || locationOf(value, 0, true) <= 0) { try { setAt(emptyLocation(), value); } catch (ArrayStoreException e) { break; } } } return storagePrevLocation; } /** * NOTE: Result is either 0 (normally) or location of the first * element in storage (after storagePrevLocation) at which value * removal has failed here. storage and its elements are not * modified. Must be synchronized outside, storage must be * synchronized too. */ public final int removeAll(Storage storage, int storagePrevLocation) throws NullPointerException, IllegalArgumentException { int location; while ((storagePrevLocation = storage.nextLocation(storagePrevLocation, true)) > 0) if ((location = locationOf(storage.getAt(storagePrevLocation), 0, true)) > 0) { try { setAt(location, null); } catch (ArrayStoreException e) { break; } } return storagePrevLocation; } /** * NOTE: Result is either 0 or location of the first element in * storage (after storagePrevLocation) at which value is not * contained here. Must be synchronized outside, storage must be * synchronized too. */ public final int containsAll(Storage storage, int storagePrevLocation) throws NullPointerException, IllegalArgumentException { while ((storagePrevLocation = storage.nextLocation(storagePrevLocation, true)) > 0 && locationOf(storage.getAt(storagePrevLocation), 0, true) > 0); return storagePrevLocation; } /** * NOTE: If parentLocation != 0 and parentLocation is "empty" then * IllegalArgumentException is thrown. Result is either 0 (not * found) or a found (not "empty") location. If (parentLocation == 0 * or is "valid") and result != 0 then result is a "valid" location. * Storage state is not altered. The effectiveness is constant. Must * be synchronized outside. */ public abstract int childLocation(int parentLocation, boolean forward) throws IllegalArgumentException; /** * NOTE: If location is "empty" then IllegalArgumentException is * thrown. Result is either 0 (not found) or a found (not "empty") * location. If location is "valid" and result != 0 then result is * a "valid" location. Storage state is not altered. The * effectiveness is constant (typically). Must be synchronized * outside. */ public abstract int siblingLocation(int location, boolean forward) throws IllegalArgumentException; /** * NOTE: If location is "empty" then IllegalArgumentException is * thrown. Result is either 0 (not found) or a found (not "empty") * location. If location is "valid" and result != 0 then result is a * "valid" location. Storage state is not altered. The effectiveness * is constant. Must be synchronized outside. */ public abstract int parentLocation(int location) throws IllegalArgumentException; /** * NOTE: parentLocation may be == 0. Must be synchronized outside. */ public final boolean hasChildren(int parentLocation) throws IllegalArgumentException { return childLocation(parentLocation, true) > 0; } /** * NOTE: prevLocation may be == 0. If result == 0 then no next * location. This method is used for iteration on storage elements. * Must be synchronized outside. */ public final int nextLocation(int prevLocation, boolean forward) throws IllegalArgumentException { int next; if (!forward) if ((next = prevLocation) <= 0 || (next = siblingLocation(prevLocation, false)) > 0) while ((prevLocation = childLocation(next, false)) > 0) next = prevLocation; else next = parentLocation(prevLocation); else if ((next = childLocation(prevLocation, true)) <= 0) while (prevLocation > 0 && (next = siblingLocation(prevLocation, true)) <= 0) prevLocation = parentLocation(prevLocation); return next; } /** * NOTE: If prevLocation != 0 and prevLocation is an "empty" * location then IllegalArgumentException is thrown. If value * == null then 0 is returned. Else search is started after/before * prevLocation. Result is either 0 (no more values) or a (not * "empty") location of the first found element, which is equal to * value. If (prevLocation == 0 or prevLocation is "valid") and * result != 0 then result is a "valid" location. Storage state is * not altered. The effectiveness is linear (typically), logarithmic * or constant. Must be synchronized outside. */ public abstract int locationOf(Object value, int prevLocation, boolean forward) throws IllegalArgumentException; /** * NOTE: If prevLocation != 0 and prevLocation is an "empty" * location then IllegalArgumentException is thrown. If value * == null then 0 is returned. Search is started after/before * prevLocation. Result is either 0 (no more values) or a (not * "empty") location of the first found element, which is (greater * ? 'greater' : 'less') than value. If (prevLocation == 0 or * prevLocation is "valid") and result != 0 then result is a "valid" * location. Storage state is not altered. The effectiveness is * linear (typically), logarithmic or constant. Must be synchronized * outside. */ public abstract int findLessGreater(Object value, boolean greater, int prevLocation, boolean forward) throws IllegalArgumentException; /** * NOTE: value may be == null. Must be synchronized outside. */ public final boolean contains(Object value) { return locationOf(value, 0, true) > 0; } /** * NOTE: value may be == null. Result >= 0. Must be synchronized * outside. */ public final int containsCount(Object value) { int count = 0, location = 0; while ((location = locationOf(value, location, true)) > 0) count++; return count; } /** * NOTE: Result is exact instanceof Object[] with non-null elements. * Must be synchronized outside. */ public Object[] toArray() { int location, capacity = 0; if ((location = childLocation(0, true)) > 0) capacity = 15; Object[] array = new Object[capacity], newArray; if (location > 0) { int index = 0; do { if (index >= capacity) { if ((capacity += capacity >> 1) <= 0) capacity = -1 >>> 1; System.arraycopy(array, 0, newArray = new Object[capacity], 0, index); array = newArray; } array[index++] = getAt(location); } while ((location = nextLocation(location, true)) > 0); if (index < capacity) { System.arraycopy(array, 0, newArray = new Object[index], 0, index); array = newArray; } } return array; } /** * NOTE: Must be synchronized outside. */ public Object clone() { Object obj; try { if ((obj = super.clone()) instanceof Storage && obj != this) return obj; } catch (CloneNotSupportedException e) {} throw new InternalError("CloneNotSupportedException"); } /** * NOTE: Storage state is not altered. Must be synchronized outside. */ public int hashCode() { int location; if ((location = childLocation(0, true)) > 0) { int code = 0, next, count = 0; do { code ^= getAt(location).hashCode(); count++; if ((next = childLocation(location, true)) > 0) code = (code << 7) - code; else if ((next = siblingLocation(location, true)) > 0) code = (code << 5) - code; else do { if ((location = parentLocation(location)) > 0) code = (code << 4) + code; else return code ^ count; } while ((next = siblingLocation(location, true)) <= 0); location = next; } while (true); } return 0; } /** * NOTE: Storage state is not altered. Must be synchronized outside, * obj must be synchronized too if not null. */ public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof Storage) { int location = 0, next, storageLocation = 0, storageNext; Storage storage = (Storage)obj; do { storageNext = storage.childLocation(storageLocation, true); if ((next = childLocation(location, true)) <= 0) if (storageNext > 0) break; else do { if (location <= 0 || storageLocation <= 0) return true; storageNext = storage.siblingLocation(storageLocation, true); if ((next = siblingLocation(location, true)) > 0) break; if (storageNext > 0) return false; location = parentLocation(location); storageLocation = storage.parentLocation(storageLocation); } while (true); } while ((storageLocation = storageNext) > 0 && getAt(location = next).equals(storage.getAt(storageLocation))); } return false; } /** * NOTE: The first found non-equal elements pair is compared through * GComparator INSTANCE. Storage state is not altered. Must be * synchronized outside, obj must be synchronized too if not null. */ public boolean greaterThan(Object obj) { if (obj == this || !(obj instanceof Storage)) return false; Storage storage = (Storage)obj; int location = 0, next, storageLocation = 0; Object value, storageValue; do { int storageNext = storage.childLocation(storageLocation, true); if ((next = childLocation(location, true)) <= 0) if (storageNext > 0) return false; else do { if (location <= 0 || storageLocation <= 0) return false; storageNext = storage.siblingLocation(storageLocation, true); if ((next = siblingLocation(location, true)) > 0) break; if (storageNext > 0) return false; location = parentLocation(location); storageLocation = storage.parentLocation(storageLocation); } while (true); if (storageNext <= 0) return true; storageValue = storage.getAt(storageLocation = storageNext); } while ((value = getAt(location = next)).equals(storageValue)); return GComparator.INSTANCE.greater(value, storageValue); } /** * NOTE: To separate storage values ", " is used, to represent tree * layout/hierarchy " {" and " }" are used. converter must be * != null. Result != null, result is 'in-line' (of course, if * values toString() are 'in-line' too). Storage state is not * altered. Must be synchronized outside. */ public final String toInlineString(ToString converter) throws NullPointerException { int location, next; converter.equals(converter); StringBuffer sBuf = new StringBuffer(58); sBuf.append('{'); if ((location = childLocation(0, true)) > 0) do { sBuf.append(' ').append(converter.toString(getAt(location))); if ((next = childLocation(location, true)) <= 0) { while ((next = siblingLocation(location, true)) <= 0) { sBuf.append(' ').append('}'); if ((location = parentLocation(location)) <= 0) return new String(sBuf); } sBuf.append(','); } else sBuf.append(' ').append('{'); location = next; } while (true); return new String(sBuf.append(' ').append('}')); } /** * NOTE: To separate storage values "\n" is used, to represent tree * layout/hierarchy "`", "|" and "-- " are used. converter must be * != null. Result != null. Storage state is not altered. Must be * synchronized outside. */ public final String toOutlineString(ToString converter) throws NullPointerException { int location, next = 0, tab, sibling; converter.equals(converter); if ((location = childLocation(0, true)) > 0) next = 58; StringBuffer sBuf = new StringBuffer(next); if (location > 0) { char[] layout = new char[0]; tab = sibling = 0; do { sBuf.append(converter.toString(getAt(location))).append('\n'); if ((next = childLocation(location, true)) > 0) { if (tab > 0) { layout[tab - 3] = layout[tab - 2] = ' '; if (layout[tab - 4] != '|') layout[tab - 4] = ' '; } if ((tab += 4) > layout.length) { if ((location = ((tab >> 3) << 2) + tab) <= 0) location = -1 >>> 1; char[] newLayout; System.arraycopy(layout, 0, newLayout = new char[location], 0, tab - 4); (layout = newLayout)[tab - 1] = ' '; } layout[tab - 3] = layout[tab - 2] = '-'; } else if ((next = tab > 0 ? sibling : siblingLocation(location, true)) <= 0) { do { if ((tab -= 4) < 0 || (location = parentLocation(location)) <= 0) return new String(sBuf); } while (tab > 0 && layout[tab - 4] != '|' || (next = siblingLocation(location, true)) <= 0); if (tab > 0) layout[tab - 3] = layout[tab - 2] = '-'; } location = next; if (tab > 0) { char ch = '`'; if ((sibling = siblingLocation(location, true)) > 0) ch = '|'; layout[tab - 4] = ch; sBuf.append(layout, 0, tab); } } while (true); } return new String(sBuf); } /** * NOTE: Result != null, result is 'out-line' here. This method may * be overridden in subclasses if needed. Must be synchronized * outside. */ public String toString() { return toOutlineString(ToString.INSTANCE); } /** * NOTE: Shallow check for integrity of this object. The * effectiveness is linear. Should be overridden in subclasses. Must * be synchronized outside. For debug purpose only. */ public void integrityCheck() { super.integrityCheck(); } }