/* * Javolution - Java(TM) Solution for Real-Time and Embedded Systems * Copyright (C) 2005 - Javolution (http://javolution.org/) * All rights reserved. * * Permission to use, copy, modify, and distribute this software is * freely granted, provided that this notice is preserved. */ package javolution.util; import java.io.ObjectStreamException; import java.lang.Appendable; import java.lang.Comparable; import java.lang.Number; import java.util.List; import javax.realtime.MemoryArea; import java.lang.CharSequence; import javolution.lang.Configurable; import javolution.lang.Immutable; import javolution.lang.Realtime; import javolution.text.Cursor; import javolution.text.Text; import javolution.text.TextFormat; import javolution.text.TypeFormat; import javolution.util.FastCollection.Record; import javolution.xml.XMLSerializable; import java.io.IOException; /** * <p> This class represents a <b>unique</b> index which can be used instead of * <code>java.lang.Integer</code> for primitive data types collections. * For example:[code] * class SparseVector<F> { * FastMap<Index, F> _elements = new FastMap<Index, F>(); * ... * }[/code]</p> * * <p> Unicity is guaranteed and direct equality (<code>==</code>) can be used * in place of object equality (<code>Index.equals(Object)</code>).</p> * * <p> Indices have no adverse effect on the garbage collector (persistent * instances), but should not be used for large integer values as that * would increase the permanent memory footprint significantly.</p> * * <p><b>RTSJ:</b> Instance of this classes are allocated in * <code>ImmortalMemory</code>. Indices can be pre-allocated at start-up * to avoid run-time allocation delays by configuring * {@link #INITIAL_FIRST} and/or {@link #INITIAL_LAST} or through * {@link #setMinimumRange}.</p> * * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> * @version 5.1, July 26, 2007 */ public final class Index extends Number implements Comparable <Index> , Record, Realtime, Immutable, XMLSerializable { /** * Holds the index zero (value <code>0</code>). */ public static final Index ZERO = new Index(0); /** * Holds negative indices (immortal memory). */ private static Index[] _NegativeIndices = new Index[32]; static { _NegativeIndices[0] = ZERO; _NegativeIndices[1] = new Index(-1); } /** * Holds positive indices length. */ private static int _NegativeIndicesLength = 2; /** * Holds the initial first index value (default <code>-1</code>). */ public static final Configurable <Integer> INITIAL_FIRST = new Configurable(new Integer(-(_NegativeIndicesLength - 1))) { protected void notifyChange(Object oldValue, Object newValue) { // Ensures Index creation from minimum value. Index.valueOf(((Integer)newValue).intValue()); } }; /** * Holds positive indices (immortal memory). */ private static Index[] _PositiveIndices = new Index[32]; static { _PositiveIndices[0] = ZERO; for (int i=1; i < _PositiveIndices.length; i++) { _PositiveIndices[i] = new Index(i); } } /** * Holds positive indices length. */ private static int _PositiveIndicesLength = _PositiveIndices.length; /** * Holds the initial last index value (default <code>31</code>). */ public static final Configurable <Integer> INITIAL_LAST = new Configurable(new Integer(_PositiveIndicesLength - 1)) { protected void notifyChange(Object oldValue, Object newValue) { // Ensures Index creation to maximum value. Index.valueOf(((Integer)newValue).intValue()); } }; /** * Holds the immortal memory area (static fields are initialized in * immortal memory). */ private static final MemoryArea IMMORTAL_MEMORY = MemoryArea.getMemoryArea(new Object()); /** * Holds the index position. */ private final int _value; /** * Creates an index at the specified position. * * @param i the index position. */ private Index(int i) { _value = i; } /** * Creates the indices for the specified range of values if they don't * exist. * * @param first the first index value. * @param last the last index value. * @throws IllegalArgumentException if <code>first > last</code> */ public static void setMinimumRange(int first, int last) { if (first > last) throw new IllegalArgumentException(); Index.valueOf(first); Index.valueOf(last); } /** * Returns the unique index for the specified <code>int</code> value * (creating it as well as the indices toward {@link #ZERO zero} * if they do not exist). * * @param i the index value. * @return the corresponding unique index. */ public static Index valueOf(int i) { // Short to be inlined. return (i >= 0) ? (i < _PositiveIndicesLength) ? _PositiveIndices[i] : createPositive(i) : valueOfNegative(-i); } /** * Returns all the indices greater or equal to <code>start</code> * but less than <code>end</code>. * * @param start the start index. * @param end the end index. * @return <code>[start .. end[</code> */ public static List <Index> rangeOf(int start, int end) { FastTable <Index> list = FastTable.newInstance(); for (int i=start; i < end; i++) { list.add(Index.valueOf(i)); } return list; } /** * Returns the list of all the indices specified. * * @param indices the indices values. * @return <code>{indices[0], indices[1], ...}</code> * */ public static List<Index> valuesOf(int ... indices) { FastTable<Index> list = FastTable.newInstance(); for (int i:indices) { list.add(Index.valueOf(i)); } return list; } /**/ private static Index valueOfNegative(int i) { return i < _NegativeIndicesLength ? _NegativeIndices[i] : createNegative(i); } private static synchronized Index createPositive(int i) { if (i < _PositiveIndicesLength) // Synchronized check. return _PositiveIndices[i]; while (i >= _PositiveIndicesLength) { IMMORTAL_MEMORY.executeInArea(AUGMENT_POSITIVE); } return _PositiveIndices[i]; } private static synchronized Index createNegative(int i) { if (i < _NegativeIndicesLength) // Synchronized check. return _NegativeIndices[i]; while (i >= _NegativeIndicesLength) { IMMORTAL_MEMORY.executeInArea(AUGMENT_NEGATIVE); } return _NegativeIndices[i]; } private static final Runnable AUGMENT_POSITIVE = new Runnable() { public void run() { for (int i = _PositiveIndicesLength, n = _PositiveIndicesLength + INCREASE_AMOUNT; i < n; i++) { Index index = new Index(i); if (_PositiveIndices.length <= i) { // Resize. Index[] tmp = new Index[_PositiveIndices.length * 2]; System.arraycopy(_PositiveIndices, 0, tmp, 0, _PositiveIndices.length); _PositiveIndices = tmp; } _PositiveIndices[i] = index; } _PositiveIndicesLength += INCREASE_AMOUNT; } }; private static final Runnable AUGMENT_NEGATIVE = new Runnable() { public void run() { for (int i = _NegativeIndicesLength, n = _NegativeIndicesLength + INCREASE_AMOUNT; i < n; i++) { Index index = new Index(-i); if (_NegativeIndices.length <= i) { // Resize. Index[] tmp = new Index[_NegativeIndices.length * 2]; System.arraycopy(_NegativeIndices, 0, tmp, 0, _NegativeIndices.length); _NegativeIndices = tmp; } _NegativeIndices[i] = index; } _NegativeIndicesLength += INCREASE_AMOUNT; } }; private static final int INCREASE_AMOUNT = 32; /** * Returns the index value as <code>int</code>. * * @return the index value. */ public int intValue() { return _value; } /** * Returns the index value as <code>long</code>. * * @return the index value. */ public long longValue() { return intValue(); } /** * Returns the index value as <code>float</code>. * * @return the index value. */ public float floatValue() { return (float) intValue(); } /** * Returns the index value as <code>int</code>. * * @return the index value. */ public double doubleValue() { return (double) intValue(); } /** * Returns the <code>String</code> representation of this index. * * @return <code>TextFormat.getInstance(Cursor.class).formatToString(_value)</code> */ public String toString() { return TextFormat.getInstance(Index.class).formatToString(this); } /** * Indicates if this index is equals to the one specified (unicity * ensures that this method is equivalent to <code>==</code>). * * @return <code>this == obj</code> */ public final boolean equals(Object obj) { return this == obj; } /** * Returns the hash code for this index. * * @return the index value. */ public final int hashCode() { return _value; } /** * Ensures index unicity during deserialization. * * @return the unique instance for this deserialized index. */ protected final Object readResolve() throws ObjectStreamException { return Index.valueOf(_value); } // Implements Comparable interface. public final int compareTo( Index that) { return this._value - ((Index)that)._value; } // Implements Record interface. public final Record getNext() { return Index.valueOf(_value + 1); } // Implements Record interface. public final Record getPrevious() { return Index.valueOf(_value - 1); } // Implements Realtime interface. public Text toText() { return TextFormat.getInstance(Index.class).format(this); } /** * Holds the default text format. */ static final TextFormat TEXT_FORMAT = new TextFormat(Index.class) { public Appendable format(Object obj, Appendable dest) throws IOException { return TypeFormat.format(((Index) obj).intValue(), dest); } public Object parse(CharSequence csq, Cursor cursor) { return Index.valueOf(TypeFormat.parseInt(csq, 10, cursor)); } }; private static final long serialVersionUID = 1L; }