001: // Copyright (c) 2004 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.mapping;
005:
006: import java.util.Hashtable;
007:
008: /** A mapping from <code>EnvironmentKey</code> to <code>Location</code>s.
009: * An <code>EnvironmentKey</code> is either a <code>Symbol</code> or
010: * a (<code>Symbol</code>, property)-pair.
011: */
012:
013: public abstract class Environment extends PropertySet
014: // implements java.util.map.Map<EnvironmentKey, Object>
015: {
016: static Environment global;
017:
018: public static void setGlobal(Environment env) {
019: global = env;
020: }
021:
022: public static Environment getGlobal() {
023: return global;
024: }
025:
026: static final int CAN_DEFINE = 1;
027: static final int CAN_REDEFINE = 2;
028:
029: /** If 'put' can implicitly define an unbound location. */
030: static final int CAN_IMPLICITLY_DEFINE = 4;
031:
032: /** May be shared by multiple threads. */
033: static final int THREAD_SAFE = 8;
034:
035: /** If this flag is on, set DIRECT_ON_SET for inherited locations. */
036: static final int DIRECT_INHERITED_ON_SET = 16;
037:
038: /** Newly defined locations are created in inherited parent environment. */
039: public static final int INDIRECT_DEFINES = 32;
040:
041: int flags = (CAN_DEFINE | CAN_REDEFINE | CAN_IMPLICITLY_DEFINE | DIRECT_INHERITED_ON_SET);
042:
043: public int getFlags() {
044: return flags;
045: }
046:
047: public void setFlag(boolean setting, int flag) {
048: if (setting)
049: flags |= flag;
050: else
051: flags &= ~flag;
052: }
053:
054: /** True if new bindings (non-unbound Locations) can be added. */
055: public boolean getCanDefine() {
056: return (flags & CAN_DEFINE) != 0;
057: }
058:
059: public void setCanDefine(boolean canDefine) {
060: if (canDefine)
061: flags |= CAN_DEFINE;
062: else
063: flags &= ~CAN_DEFINE;
064: }
065:
066: /** True if bindings can be removed or replaced by other kinds of Location.*/
067: public boolean getCanRedefine() {
068: return (flags & CAN_REDEFINE) != 0;
069: }
070:
071: public void setCanRedefine(boolean canRedefine) {
072: if (canRedefine)
073: flags |= CAN_REDEFINE;
074: else
075: flags &= ~CAN_REDEFINE;
076: }
077:
078: /** True if this environment is locked - bindings cannot be added or removed. */
079: public final boolean isLocked() {
080: return (flags & (CAN_DEFINE | CAN_REDEFINE)) == 0;
081: }
082:
083: public void setLocked() {
084: flags &= ~(CAN_DEFINE | CAN_REDEFINE | CAN_IMPLICITLY_DEFINE);
085: }
086:
087: public final void setIndirectDefines() {
088: flags |= Environment.INDIRECT_DEFINES;
089: ((InheritingEnvironment) this ).baseTimestamp = 0x7fffffff;
090: }
091:
092: /** Return a location bound to (key, property).
093: * Create new unbound Location if no such Location exists. */
094: public final Location getLocation(Symbol key, Object property) {
095: return getLocation(key, property, true);
096: }
097:
098: /** Return a location bound to key (and null property).
099: * Create new unbound Location if no such Location exists. */
100: public final Location getLocation(Symbol key) {
101: return getLocation(key, null, true);
102: }
103:
104: /** Return a location bound to (key, property).
105: * Return null if no such Location exists. */
106: public final Location lookup(Symbol key, Object property) {
107: return getLocation(key, property, false);
108: }
109:
110: public abstract NamedLocation lookup(Symbol name, Object property,
111: int hash);
112:
113: public final Location lookup(Symbol key) {
114: return getLocation(key, null, false);
115: }
116:
117: public abstract NamedLocation getLocation(Symbol key,
118: Object property, int hash, boolean create);
119:
120: public final NamedLocation getLocation(Symbol name,
121: Object property, boolean create) {
122: int hash = name.hashCode() ^ System.identityHashCode(property);
123: return getLocation(name, property, hash, create);
124: }
125:
126: public final Location getLocation(Object key, boolean create) {
127: Object property = null;
128: if (key instanceof EnvironmentKey) {
129: EnvironmentKey k = (EnvironmentKey) key;
130: key = k.getKeySymbol();
131: property = k.getKeyProperty();
132: }
133: Symbol sym = key instanceof Symbol ? (Symbol) key
134: : getSymbol((String) key);
135: return getLocation(sym, property, create);
136: }
137:
138: public boolean isBound(Symbol key, Object property) {
139: Location loc = lookup(key, property);
140: if (loc == null)
141: return false;
142: return loc.isBound();
143: }
144:
145: public final boolean isBound(Symbol key) {
146: return isBound(key, null);
147: }
148:
149: public final boolean containsKey(Object key) {
150: Object property = null;
151: if (key instanceof EnvironmentKey) {
152: EnvironmentKey k = (EnvironmentKey) key;
153: key = k.getKeySymbol();
154: property = k.getKeyProperty();
155: }
156: Symbol sym = key instanceof Symbol ? (Symbol) key
157: : getSymbol((String) key);
158: return isBound(sym, property);
159: }
160:
161: /** Get the value bound to the given name.
162: * @exception gnu.mapping.UnboundLocationException the name has no binding
163: * @see Environment#get(Object)
164: */
165: public final Object getChecked(String name) {
166: Object value = get(name, Location.UNBOUND);
167: if (value == Location.UNBOUND)
168: throw new UnboundLocationException(name + " in " + this );
169: return value;
170: }
171:
172: public Object get(Symbol key, Object property, Object defaultValue) {
173: Location loc = lookup(key, property);
174: if (loc == null)
175: return defaultValue;
176: return loc.get(defaultValue);
177: }
178:
179: public final Object get(EnvironmentKey key, Object defaultValue) {
180: Symbol symbol = key.getKeySymbol();
181: Object property = key.getKeyProperty();
182: return get(symbol, property, defaultValue);
183: }
184:
185: public final Object get(String key, Object defaultValue) {
186: return get(getSymbol(key), null, defaultValue);
187: }
188:
189: public Object get(Symbol sym) {
190: Object unb = Location.UNBOUND;
191: Object val = get(sym, null, unb);
192: if (val == unb)
193: throw new UnboundLocationException(sym);
194: return val;
195: }
196:
197: public final Object getFunction(Symbol key, Object defaultValue) {
198: return get(key, EnvironmentKey.FUNCTION, defaultValue);
199: }
200:
201: public final Object getFunction(Symbol sym) {
202: Object unb = Location.UNBOUND;
203: Object val = get(sym, EnvironmentKey.FUNCTION, unb);
204: if (val == unb)
205: throw new UnboundLocationException(sym);
206: return val;
207: }
208:
209: /** Get the value bound to the given name.
210: * Returns null if the name has no binding
211: * (for compatibility with Java2 Collections framework).
212: * @see Environment#getChecked(String)
213: */
214: public final Object get(Object key) {
215: Object property = null;
216: if (key instanceof EnvironmentKey) {
217: EnvironmentKey k = (EnvironmentKey) key;
218: key = k.getKeySymbol();
219: property = k.getKeyProperty();
220: }
221: Symbol sym = key instanceof Symbol ? (Symbol) key
222: : getSymbol((String) key);
223: return get(sym, property, null);
224: }
225:
226: public void put(Symbol key, Object property, Object newValue) {
227: Location loc = getLocation(key, property);
228: if (loc.isConstant()) // FIXME - is this helpful?
229: define(key, property, newValue);
230: else
231: loc.set(newValue);
232: }
233:
234: public abstract void define(Symbol key, Object property,
235: Object newValue);
236:
237: public final void put(Symbol key, Object newValue) {
238: put(key, null, newValue);
239: }
240:
241: public final Object put(Object key, Object newValue) {
242: Location loc = getLocation(key, true);
243: Object oldValue = loc.get(null);
244: loc.set(newValue);
245: return oldValue;
246: }
247:
248: public final void putFunction(Symbol key, Object newValue) {
249: put(key, EnvironmentKey.FUNCTION, newValue);
250: }
251:
252: public final Object put(String key, Object value) {
253: return put((Object) key, value);
254: }
255:
256: /** Remove Location from this Environment.
257: * Does not explicitly undefine the location itself.
258: */
259: public Location unlink(Symbol key, Object property, int hash) {
260: throw new RuntimeException(
261: "unsupported operation: unlink (aka undefine)");
262: }
263:
264: /** Remove Location from this Environment and undefine it. */
265: public Object remove(Symbol key, Object property, int hash) {
266: Location loc = unlink(key, property, hash);
267: if (loc == null)
268: return null;
269: Object value = loc.get(null);
270: loc.undefine();
271: return value;
272: }
273:
274: /** Remove and undefine binding.
275: * @return Old value
276: */
277: public final Object remove(EnvironmentKey key) {
278: Symbol symbol = key.getKeySymbol();
279: Object property = key.getKeyProperty();
280: int hash = symbol.hashCode()
281: ^ System.identityHashCode(property);
282: return remove(symbol, property, hash);
283: }
284:
285: public final Object remove(Symbol symbol, Object property) {
286: int hash = symbol.hashCode()
287: ^ System.identityHashCode(property);
288: return remove(symbol, property, hash);
289: }
290:
291: public final void remove(Symbol sym) {
292: remove(sym, null, sym.hashCode());
293: }
294:
295: public final void removeFunction(Symbol sym) {
296: remove(sym, EnvironmentKey.FUNCTION);
297: }
298:
299: public final Object remove(Object key) {
300: Object property = null;
301: if (key instanceof EnvironmentKey) {
302: EnvironmentKey k = (EnvironmentKey) key;
303: return remove(k.getKeySymbol(), k.getKeyProperty());
304: }
305: Symbol symbol = key instanceof Symbol ? (Symbol) key
306: : getSymbol((String) key);
307: int hash = symbol.hashCode()
308: ^ System.identityHashCode(property);
309: return remove(symbol, property, hash);
310: }
311:
312: public Namespace defaultNamespace() {
313: // FIXME: return get("*package*, Namespace.getDefault())
314: return Namespace.getDefault();
315: }
316:
317: public Symbol getSymbol(String name) {
318: return defaultNamespace().getSymbol(name);
319: }
320:
321: static final Hashtable envTable = new Hashtable(50);
322:
323: public static Environment getInstance(String name) {
324: if (name == null)
325: name = "";
326: synchronized (envTable) {
327: Environment env = (Environment) envTable.get(name);
328: if (env != null)
329: return env;
330: env = new SimpleEnvironment();
331: env.setName(name);
332: envTable.put(name, env);
333: return env;
334: }
335: }
336:
337: /** Does not enumerate inherited Locations. */
338: public abstract LocationEnumeration enumerateLocations();
339:
340: /** Does enumerate inherited Locations. */
341: public abstract LocationEnumeration enumerateAllLocations();
342:
343: protected abstract boolean hasMoreElements(LocationEnumeration it);
344:
345: /**
346: * @deprecated
347: */
348: public static Environment current() {
349: return getCurrent();
350: }
351:
352: public static Environment getCurrent() {
353: return CallContext.getInstance().getEnvironment();
354: }
355:
356: public static void setCurrent(Environment env) {
357: CallContext.getInstance().curEnvironment = env;
358: }
359:
360: public static Environment user() {
361: return getCurrent();
362: }
363:
364: public final void addLocation(NamedLocation loc) {
365: addLocation(loc.getKeySymbol(), loc.getKeyProperty(), loc);
366: }
367:
368: public abstract NamedLocation addLocation(Symbol name, Object prop,
369: Location loc);
370:
371: public final void addLocation(EnvironmentKey key, Location loc) {
372: addLocation(key.getKeySymbol(), key.getKeyProperty(), loc);
373: }
374:
375: public static SimpleEnvironment make() {
376: return new SimpleEnvironment();
377: }
378:
379: public static SimpleEnvironment make(String name) {
380: return new SimpleEnvironment(name);
381: }
382:
383: public static InheritingEnvironment make(String name,
384: Environment parent) {
385: return new InheritingEnvironment(name, parent);
386: }
387:
388: public String toString() {
389: return "#<environment " + getName() + '>';
390: }
391:
392: /** Overridden in sub-classes - useful for more verbose debug output. */
393: public String toStringVerbose() {
394: return toString();
395: }
396: }
|