001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2006 - 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.context;
010:
011: import j2me.lang.Comparable;
012: import j2me.util.Map;
013: import javolution.util.FastMap;
014:
015: /**
016: * <p> This class represents a context persistent accross multiple program
017: * executions. It is typically used to hold
018: * {@link Reference persistent references}.</p>
019: *
020: * <p> How this context is loaded/saved is application specific.
021: * Although, the simplest way is to use Javolution XML serialization
022: * facility. For example:[code]
023: * import javolution.xml.XMLObjectReader;
024: * import javolution.xml.XMLObjectWriter;
025: * public void main(String[]) {
026: * // Loads persistent context (typically at start-up).
027: * XMLObjectReader reader = XMLObjectReader.newInstance(new FileInputStream("C:/persistent.xml"));
028: * PersistentContext.setCurrent(reader.read(PersistentContext.class));
029: * ...
030: * ...
031: * // Saves persistent context for future execution.
032: * XMLObjectWriter writer = XMLObjectWriter.newInstance(new FileOutputStream("C:/persistent.xml"));
033: * writer.write(PersistentContext.getCurrent(), PersistentContext.class);
034: * }[/code]</p>
035: *
036: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
037: * @version 4.2, December 31, 2006
038: */
039: public class PersistentContext extends Context {
040:
041: /**
042: * Holds the single instance.
043: */
044: private static PersistentContext _PersistentContext = new PersistentContext();
045:
046: /**
047: * Holds current id to value mapping.
048: */
049: private final FastMap _idToValue = new FastMap();
050:
051: /**
052: * Default constructor.
053: */
054: public PersistentContext() {
055: }
056:
057: /**
058: * Sets the persistent instance.
059: *
060: * @param ctx the persistent instance.
061: */
062: public static void setCurrent(PersistentContext ctx) {
063: _PersistentContext = ctx;
064: synchronized (Reference.INSTANCES) {
065: for (FastMap.Entry e = Reference.INSTANCES.head(), end = Reference.INSTANCES
066: .tail(); (e = (FastMap.Entry) e.getNext()) != end;) {
067: Reference reference = (Reference) e.getValue();
068: if (ctx._idToValue.containsKey(reference._id)) {
069: reference.set(ctx._idToValue.get(reference._id));
070: }
071: }
072: }
073: }
074:
075: /**
076: * Returns the persistent context instance (singleton).
077: *
078: * @return the persistent context instance.
079: */
080: public static/*PersistentContext*/Context getCurrent() {
081: return _PersistentContext;
082: }
083:
084: /**
085: * Returns the ID to value mapping for this persistent context.
086: *
087: * @return the persistent value indexed by identifiers.
088: */
089: public Map/*<String, Object>*/getIdToValue() {
090: return _idToValue;
091: }
092:
093: /**
094: * Throws <code>UnsupportedOperationException</code> persistent context
095: * are global to all threads (singleton).
096: */
097: protected void enterAction() {
098: throw new j2me.lang.UnsupportedOperationException(
099: "Cannot enter persistent context (already in)");
100: }
101:
102: /**
103: * Throws <code>UnsupportedOperationException</code> persistent context
104: * are global to all threads (singleton).
105: */
106: protected void exitAction() {
107: throw new j2me.lang.UnsupportedOperationException(
108: "Cannot exit persistent context (always in)");
109: }
110:
111: /**
112: * <p> This class represents a reference over an object which can be kept
113: * persistent accross multiple program executions. Instances of this class
114: * are typically used to hold global data time consuming to regenerate.
115: * For example:[code]
116: * public class FastMap<K,V> implements Map<K, V> {
117: * // Provides a constructor for persistent maps.
118: * public FastMap(String id) {
119: * new PersistentContext<Map<K, V>.Reference(id, this) {
120: * protected void notifyChange() {
121: * FastMap.this.clear();
122: * FastMap.this.putAll(this.get());
123: * }
124: * };
125: * }
126: * }
127: * ...
128: * // Persistent lookup table for units multiplications.
129: * static FastMap<Unit, FastMap<Unit, Unit>> UNITS_MULT_LOOKUP
130: * = new FastMap<Unit, FastMap<Unit, Unit>>("UNITS_MULT_LOOKUP").setShared(true);
131: * [/code]</p>
132: *
133: * <p> Persistent references may also be used to hold optimum configuration
134: * values set from previous executions. For example:[code]
135: * public Targets {
136: * private static PersistentContext.Reference<Integer> CAPACITY
137: * = new PersistentContext.Reference<Integer>("Targets#CAPACITY", 256);
138: * private Target[] _targets = new Target[CAPACITY.get()];
139: * private int _count;
140: * public void add(Target target) {
141: * if (_count == _targets.length) { // Ooops, resizes.
142: * Target[] tmp = new Target[_count * 2];
143: * System.arraycopy(_targets, 0, tmp, 0, _count);
144: * _targets = tmp;
145: * CAPACITY.setMinimum(_targets.length); // Persists.
146: * }
147: * _targets[_count++] target;
148: * }
149: * }[/code]
150: *
151: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
152: * @version 4.0, September 4, 2006
153: */
154: public static class Reference /*<T>*/implements
155: javolution.lang.Reference/*<T>*/{
156:
157: /**
158: * Holds the instances.
159: */
160: private final static FastMap INSTANCES = new FastMap();
161:
162: /**
163: * Holds the unique identifier.
164: */
165: private final String _id;
166:
167: /**
168: * Holds the current value.
169: */
170: private Object/*{T}*/_value;
171:
172: /**
173: * Creates a persistent reference identified by the specified name and
174: * having the specified default value.
175: *
176: * @param id the unique identifier.
177: * @param defaultValue the default value.
178: * @throws IllegalArgumentException if the name is not unique.
179: */
180: public Reference(String id, Object/*{T}*/defaultValue) {
181: _id = id;
182: _value = defaultValue;
183: synchronized (INSTANCES) {
184: if (INSTANCES.containsKey(id))
185: throw new IllegalArgumentException("Identifier "
186: + id + " already in use");
187: INSTANCES.put(id, this );
188: }
189: if (_PersistentContext._idToValue.containsKey(id)) {
190: set((Object/*{T}*/) _PersistentContext._idToValue
191: .get(id));
192: }
193: }
194:
195: // Implements Reference interface.
196: public Object/*{T}*/get() {
197: return _value;
198: }
199:
200: // Implements Reference interface.
201: public void set(Object/*{T}*/value) {
202: _value = value;
203: notifyChange();
204: }
205:
206: /**
207: * Sets this reference to the specified value only if
208: * <code>(value.compareTo(this.get()) > 0)</code>.
209: *
210: * @param value the minimum value for this reference.
211: * @throws IllegalArgumentException if the specified value is not
212: * {@link Comparable} or an {@link Integer} instance (J2ME).
213: */
214: public void setMinimum(Object/*{T}*/value) {
215: synchronized (this ) {
216: if (value instanceof Comparable) {
217: Object prevValue = get();
218: if (((Comparable) value).compareTo(prevValue) > 0) {
219: set(value);
220: }
221: } else if (value instanceof Integer) {
222: Object prevValue = get();
223: if (((Integer) value).intValue() > ((Integer) prevValue)
224: .intValue()) {
225: set(value);
226: }
227: } else {
228: throw new IllegalArgumentException();
229: }
230: }
231: }
232:
233: /**
234: * Sets this reference to the specified value only if
235: * <code>(value.compareTo(this.get()) < 0)</code>.
236: *
237: * @param value the maximum value for this reference.
238: * @throws IllegalArgumentException if the specified value is not
239: * {@link Comparable} or an {@link Integer} instance (J2ME).
240: */
241: public void setMaximum(Object/*{T}*/value) {
242: synchronized (this ) {
243: if (value instanceof Comparable) {
244: Object prevValue = get();
245: if (((Comparable) value).compareTo(prevValue) < 0) {
246: set(value);
247: }
248: } else if (value instanceof Integer) {
249: Object prevValue = get();
250: if (((Integer) value).intValue() < ((Integer) prevValue)
251: .intValue()) {
252: set(value);
253: }
254: } else {
255: throw new IllegalArgumentException();
256: }
257: }
258: }
259:
260: /**
261: * Returns the string representation of the current value of this
262: * reference.
263: *
264: * @return <code>String.valueOf(this.get())</code>
265: */
266: public String toString() {
267: return String.valueOf(get());
268: }
269:
270: /**
271: * Notifies this reference that its value has changed (for example
272: * a new persistent context has been loaded).
273: * The default implementation does nothing.
274: */
275: protected void notifyChange() {
276: }
277: }
278: }
|