001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2005 - Javolution (http://javolution.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package javolution.util;
010:
011: import j2me.lang.Comparable;
012: import j2me.lang.Number;
013: import j2me.io.ObjectStreamException;
014: import j2mex.realtime.MemoryArea;
015: import javolution.lang.Configurable;
016: import javolution.lang.Immutable;
017: import javolution.util.FastCollection.Record;
018: import javolution.xml.XMLSerializable;
019:
020: /**
021: * <p> This class represents a <b>unique</b> index which can be used instead of
022: * <code>java.lang.Integer</code> for primitive data types collections.
023: * For example:[code]
024: * class SparseVector<F> {
025: * FastMap<Index, F> _elements = new FastMap<Index, F>();
026: * ...
027: * }[/code]</p>
028: *
029: * <p> Unicity is guaranteed and direct equality (<code>==</code>) can be used
030: * in place of object equality (<code>Index.equals(Object)</code>).</p>
031: *
032: * <p> Indices have no adverse effect on the garbage collector (persistent
033: * instances), but should not be used for large integer values as that
034: * would increase the permanent memory footprint significantly.</p>
035: *
036: * <p><b>RTSJ:</b> Instance of this classes are allocated in
037: * <code>ImmortalMemory</code>. Indices can be pre-allocated at start-up
038: * to avoid run-time allocation delays by configuring
039: * {@link #INITIAL_FIRST} and/or {@link #INITIAL_LAST} or through
040: * {@link #setMinimumRange}.</p>
041: *
042: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
043: * @version 5.1, July 26, 2007
044: */
045: public final class Index extends Number implements
046: Comparable/*<Index>*/, Record, Immutable, XMLSerializable {
047:
048: /**
049: * Holds the initial first index value (default <code>-1</code>).
050: */
051: public static final Configurable/*<Integer>*/INITIAL_FIRST = new Configurable(
052: new Integer(-1)) {
053: protected void notifyChange() {
054: // Ensures Index creation from minimum balue.
055: Index.valueOf(((Integer) INITIAL_FIRST.get()).intValue());
056: }
057: };
058:
059: /**
060: * Holds the initial last index value (default <code>16</code>).
061: */
062: public static final Configurable/*<Integer>*/INITIAL_LAST = new Configurable(
063: new Integer(16)) {
064: protected void notifyChange() {
065: // Ensures Index creation to maximum value.
066: Index.valueOf(((Integer) INITIAL_LAST.get()).intValue());
067: }
068: };
069:
070: /**
071: * Holds the index zero (value <code>0</code>).
072: */
073: public static final Index ZERO = new Index(0);
074:
075: /**
076: * Holds positive indices (immortal memory).
077: */
078: private static Index[] PositiveIndices = new Index[16];
079: static {
080: PositiveIndices[0] = ZERO;
081: }
082:
083: /**
084: * Holds positive indices length.
085: */
086: private static int PositiveIndicesLength = 1;
087:
088: /**
089: * Holds negative indices (immortal memory).
090: */
091: private static Index[] NegativeIndices = new Index[16];
092: static {
093: NegativeIndices[0] = ZERO;
094: }
095:
096: /**
097: * Holds the immortal memory area (static fields are initialized in
098: * immortal memory).
099: */
100: private static final MemoryArea IMMORTAL_MEMORY = MemoryArea
101: .getMemoryArea(new Object());
102:
103: /**
104: * Holds positive indices length.
105: */
106: private static int NegativeIndicesLength = 1;
107:
108: /**
109: * Holds the index position.
110: */
111: private final int _value;
112:
113: /**
114: * Creates an index at the specified position.
115: *
116: * @param i the index position.
117: */
118: private Index(int i) {
119: _value = i;
120: }
121:
122: /**
123: * Creates the indices for the specified range of values if they don't
124: * exist.
125: *
126: * @param first the first index value.
127: * @param last the last index value.
128: * @throws IllegalArgumentException if <code>first > last</code>
129: */
130: public static void setMinimumRange(int first, int last) {
131: if (first > last)
132: throw new IllegalArgumentException();
133: Index.valueOf(first);
134: Index.valueOf(last);
135: }
136:
137: /**
138: * Returns the unique index for the specified <code>int</code> value
139: * (creating it as well as the indices toward {@link #ZERO zero}
140: * if they do not exist).
141: *
142: * @param i the index value.
143: * @return the corresponding unique index.
144: */
145: public static Index valueOf(int i) { // Short to be inlined.
146: return (i >= 0) ? (i < Index.PositiveIndicesLength) ? PositiveIndices[i]
147: : createPositive(i)
148: : valueOfNegative(-i);
149: }
150:
151: private static Index valueOfNegative(int i) {
152: return i < Index.NegativeIndicesLength ? NegativeIndices[i]
153: : createNegative(i);
154: }
155:
156: private static synchronized Index createPositive(int i) {
157: if (i < PositiveIndicesLength) // Synchronized check.
158: return PositiveIndices[i];
159: while (i >= PositiveIndicesLength) {
160: IMMORTAL_MEMORY.executeInArea(AUGMENT_POSITIVE);
161: }
162: return PositiveIndices[i];
163: }
164:
165: private static synchronized Index createNegative(int i) {
166: if (i < NegativeIndicesLength) // Synchronized check.
167: return NegativeIndices[i];
168: while (i >= NegativeIndicesLength) {
169: IMMORTAL_MEMORY.executeInArea(AUGMENT_NEGATIVE);
170: }
171: return NegativeIndices[i];
172:
173: }
174:
175: private static final Runnable AUGMENT_POSITIVE = new Runnable() {
176: public void run() {
177: for (int i = Index.PositiveIndicesLength, n = Index.PositiveIndicesLength
178: + INCREASE_AMOUNT; i < n; i++) {
179:
180: Index index = new Index(i);
181:
182: if (Index.PositiveIndices.length <= i) { // Resize.
183: Index[] tmp = new Index[Index.PositiveIndices.length * 2];
184: System.arraycopy(Index.PositiveIndices, 0, tmp, 0,
185: Index.PositiveIndices.length);
186: Index.PositiveIndices = tmp;
187: }
188:
189: PositiveIndices[i] = index;
190: }
191: NoReordering = true; // Ensures instruction below is performed last.
192: PositiveIndicesLength += INCREASE_AMOUNT;
193: }
194: };
195:
196: private static final Runnable AUGMENT_NEGATIVE = new Runnable() {
197: public void run() {
198: for (int i = Index.NegativeIndicesLength, n = Index.NegativeIndicesLength
199: + INCREASE_AMOUNT; i < n; i++) {
200:
201: Index index = new Index(-i);
202:
203: if (Index.NegativeIndices.length <= i) { // Resize.
204: Index[] tmp = new Index[Index.NegativeIndices.length * 2];
205: System.arraycopy(Index.NegativeIndices, 0, tmp, 0,
206: Index.NegativeIndices.length);
207: Index.NegativeIndices = tmp;
208: }
209:
210: NegativeIndices[i] = index;
211: }
212: NoReordering = true; // Ensures instruction below is performed last.
213: NegativeIndicesLength += INCREASE_AMOUNT;
214: }
215: };
216:
217: private static final int INCREASE_AMOUNT = 16;
218:
219: static volatile boolean NoReordering;
220:
221: /**
222: * Returns the index value as <code>int</code>.
223: *
224: * @return the index value.
225: */
226: public final int intValue() {
227: return _value;
228: }
229:
230: /**
231: * Returns the index value as <code>long</code>.
232: *
233: * @return the index value.
234: */
235: public final long longValue() {
236: return intValue();
237: }
238:
239: /**
240: * Returns the index value as <code>float</code>.
241: *
242: * @return the index value.
243: *@JVM-1.1+@
244: public final float floatValue() {
245: return (float) intValue();
246: }
247: /**/
248:
249: /**
250: * Returns the index value as <code>int</code>.
251: *
252: * @return the index value.
253: *@JVM-1.1+@
254: public final double doubleValue() {
255: return (double) intValue();
256: }
257: /**/
258:
259: /**
260: * Returns the <code>String</code> representation of this index.
261: *
262: * @return this index value formatted as a string.
263: */
264: public final String toString() {
265: return String.valueOf(_value);
266: }
267:
268: /**
269: * Indicates if this index is equals to the one specified (unicity
270: * ensures that this method is equivalent to <code>==</code>).
271: *
272: * @return <code>this == obj</code>
273: */
274: public final boolean equals(Object obj) {
275: return this == obj;
276: }
277:
278: /**
279: * Returns the hash code for this index.
280: *
281: * @return the index value.
282: */
283: public final int hashCode() {
284: return _value;
285: }
286:
287: /**
288: * Ensures index unicity during deserialization.
289: *
290: * @return the unique instance for this deserialized index.
291: */
292: protected final Object readResolve() throws ObjectStreamException {
293: return Index.valueOf(_value);
294: }
295:
296: // Implements Comparable interface.
297: public int compareTo(Object/*{Index}*/that) {
298: return this ._value - ((Index) that)._value;
299: }
300:
301: // Implements Record interface.
302: public final Record getNext() {
303: return Index.valueOf(_value + 1);
304: }
305:
306: // Implements Record interface.
307: public final Record getPrevious() {
308: return Index.valueOf(_value - 1);
309: }
310:
311: // Start-up initialization.
312: static {
313: // Ensures initial instances creation.
314: Index.valueOf(((Integer) INITIAL_FIRST.get()).intValue());
315: Index.valueOf(((Integer) INITIAL_LAST.get()).intValue());
316: }
317:
318: private static final long serialVersionUID = 1L;
319:
320: }
|