001: // Copyright (c) 2005 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.mapping;
005:
006: /** A Location that forwards to a thread-specific Location.
007: */
008:
009: public class ThreadLocation extends Location implements Named {
010: final Symbol name;
011: final Object property;
012: static int counter;
013: boolean unlink;
014:
015: private static synchronized int nextCounter() {
016: return ++counter;
017: }
018:
019: /** Magic property value used for the "anonymous" ThreadLocations.
020: * These are thread-specific dynamic "parameters" (in the SRFI-39 sense)
021: * that are not tied to a specfic name. */
022: public static final String ANONYMOUS = new String("(dynamic)");
023:
024: Location global;
025:
026: /* #ifdef JAVA2 */
027: ThreadLocal thLocal = new ThreadLocal();
028:
029: /* #else */
030: // java.util.Hashtable threadMap = new java.util.Hashtable(50);
031: /* #endif */
032:
033: /** A new anonymous fluid location. */
034: public ThreadLocation() {
035: this ("param#" + nextCounter());
036: }
037:
038: /** A new anonymous fluid location but used a given name for printing.
039: * However, the binding is not bound to the name as a visible binding. */
040: public ThreadLocation(String name) {
041: this .name = Symbol.makeUninterned(name);
042: this .property = ANONYMOUS;
043: unlink = true;
044: global = new SharedLocation(this .name, null, 0);
045: }
046:
047: private ThreadLocation(Symbol name) {
048: this .name = name;
049: String str = name == null ? null : name.toString();
050: this .property = ANONYMOUS;
051: unlink = true;
052: global = new SharedLocation(Symbol.makeUninterned(str), null, 0);
053: }
054:
055: public ThreadLocation(Symbol name, Object property, Location global) {
056: this .name = name;
057: this .property = property;
058: this .global = global;
059: }
060:
061: /** Create a fresh ThreadLocation, independent of other ThreaDLocations.
062: * Creates new unique EnvironmentKey, using a unique property key.
063: * @param name used for printing, but not identification.
064: */
065: public static ThreadLocation makePrivate(String name) {
066: return new ThreadLocation(name);
067: }
068:
069: public static ThreadLocation makePrivate(Symbol name) {
070: return new ThreadLocation(name);
071: }
072:
073: /** Set the default/global value. */
074: public void setGlobal(Object value) {
075: synchronized (this ) {
076: if (global == null)
077: global = new SharedLocation(this .name, null, 0);
078: global.set(value);
079: }
080: }
081:
082: /** Get the thread-specific Location for this Location. */
083: public Location getLocation() {
084: Object entry;
085: /* #ifdef JAVA2 */
086: entry = thLocal.get();
087: /* #else */
088: // entry = threadMap.get(Thread.currentThread());
089: /* #endif */
090: if (entry == null) {
091: Environment env = Environment.getCurrent();
092: NamedLocation loc = env.getLocation(name, property, true);
093: if (global != null) {
094: synchronized (loc) {
095: if (loc.base == null
096: && loc.value == Location.UNBOUND)
097: loc.setBase(global);
098: }
099: }
100:
101: if (unlink) {
102: LocationRef lref = new LocationRef();
103: lref.env = env;
104: lref.loc = loc;
105: entry = lref;
106: } else
107: entry = loc;
108: /* #ifdef JAVA2 */
109: thLocal.set(entry);
110: /* #else */
111: // threadMap.put(Thread.currentThread(), entry);
112: /* #endif */
113: }
114: if (entry instanceof LocationRef)
115: return ((LocationRef) entry).loc;
116: else
117: return (Location) entry;
118: }
119:
120: public Object get(Object defaultValue) {
121: return getLocation().get(defaultValue);
122: }
123:
124: public void set(Object value) {
125: getLocation().set(value);
126: }
127:
128: public Object setWithSave(Object newValue, CallContext ctx) {
129: return getLocation().setWithSave(newValue, ctx);
130: }
131:
132: public void setRestore(Object oldValue, CallContext ctx) {
133: getLocation().setRestore(oldValue, ctx);
134: }
135:
136: public Symbol getKeySymbol() {
137: return name;
138: }
139:
140: public Object getKeyProperty() {
141: return property;
142: }
143:
144: public String getName() {
145: return name == null ? null : name.toString();
146: }
147:
148: public Object getSymbol() // Implements Named
149: {
150: // If this was allocated using makePrivate(String) it is better
151: // to return the original String, rather than a generated Symbol.
152: // One motivation is when a module is imported with a specified namespace
153: // URI (only in XQuery at this point); we want to use the latter namespace.
154: if (name != null && property == ANONYMOUS
155: && (((SharedLocation) global).getKeySymbol() == name))
156: return name.toString();
157: return name;
158: }
159:
160: public void setName(String name) {
161: throw new RuntimeException("setName not allowed");
162: }
163:
164: static SimpleEnvironment env;
165:
166: /** For a given (Symbol. property)-pair, find or create
167: * a matching ThreadLocation. */
168: public synchronized static ThreadLocation getInstance(Symbol name,
169: Object property) {
170: if (env == null)
171: env = new SimpleEnvironment("[thread-locations]");
172: IndirectableLocation loc = (IndirectableLocation) env
173: .getLocation(name, property);
174: if (loc.base != null)
175: return (ThreadLocation) loc.base;
176: ThreadLocation tloc = new ThreadLocation(name, property, null);
177: loc.base = tloc;
178: return tloc;
179: }
180: }
181:
182: class LocationRef {
183: Environment env;
184: Location loc;
185:
186: public void finalize() {
187: Symbol symbol = loc.getKeySymbol();
188: Object property = loc.getKeyProperty();
189: int hash = symbol.hashCode()
190: ^ System.identityHashCode(property);
191: env.unlink(symbol, property, hash);
192: }
193: }
|