001: package gnu.mapping;
002:
003: import gnu.lists.*;
004:
005: /** Used to implement Lisp-style "property lists".
006: * A <code>PropertyLocation</code> is a location whose value is the
007: * <code>car</code> of a property list.
008: * This class also contains a number of static methods useful for
009: * working with property lists.
010: */
011:
012: public class PropertyLocation extends Location {
013: // Location plist;
014:
015: Pair pair;
016:
017: public final Object get(Object defaultValue) {
018: return pair.car;
019: }
020:
021: public boolean isBound() {
022: return true;
023: }
024:
025: public final void set(Object newValue) {
026: pair.car = newValue;
027: }
028:
029: /** Get the property list assocated with an object in a given Environment.
030: * @param symbol Usually but not necessarily a Symbol.
031: * (A String is <em>not </em> converted a Symbol by this method.)
032: */
033: public static Object getPropertyList(Object symbol, Environment env) {
034: return env.get(Symbol.PLIST, symbol, LList.Empty);
035: }
036:
037: /** Get the property list associated with object in the current Environment.
038: * Corresponds to Common Lisp's <code>symbol-plist</code> function.
039: * @param symbol Usually but not necessarily a Symbol.
040: * (A String is <em>not </em> converted a Symbol by this method.)
041: */
042: public static Object getPropertyList(Object symbol) {
043: return Environment.getCurrent().get(Symbol.PLIST, symbol,
044: LList.Empty);
045: }
046:
047: /** Set the property list assocated with an object in a given Environment.
048: * This function should be avoided, since a Symbol's property list may
049: * be used by unknown classes. It also can be slow.
050: * @param symbol Usually but not necessarily a Symbol.
051: * (A String is <em>not </em> converted a Symbol by this method.)
052: */
053: public static void setPropertyList(Object symbol, Object plist,
054: Environment env) {
055: synchronized (env) {
056: Location lloc = env.lookup(Symbol.PLIST, symbol);
057: if (symbol instanceof Symbol) {
058: Symbol sym = (Symbol) symbol;
059: Object old = lloc.get(LList.Empty);
060: Object p = old;
061: // Remove old PropertyLocation bindings not in plist.
062: for (;;) {
063: if (!(p instanceof Pair))
064: break;
065: Pair pair = (Pair) p;
066: Object property = pair.car;
067: if (plistGet(plist, property, null) != null)
068: env.remove(sym, property);
069: p = ((Pair) pair.cdr).cdr;
070: }
071: // Add/update PropertyLocation bindings from plist.
072: p = plist;
073: for (;;) {
074: if (!(p instanceof Pair))
075: break;
076: Pair pair = (Pair) p;
077: Object property = pair.car;
078: Location loc = env.lookup(sym, property);
079: PropertyLocation ploc;
080: if (loc != null
081: && (loc = loc.getBase()) instanceof PropertyLocation) {
082: ploc = (PropertyLocation) loc;
083: } else {
084: ploc = new PropertyLocation();
085: env.addLocation(sym, property, ploc);
086: }
087: Pair valuePair = (Pair) pair.cdr;
088: ploc.pair = valuePair;
089: //ploc.plist = plist;
090: p = valuePair.cdr;
091: }
092: }
093: lloc.set(plist);
094: }
095: }
096:
097: /** Set the property list assocated with an object in a given Environment.
098: * Corresponds to Common Lisp's <code>(setf symbol-plist)</code> function.
099: * @see #setPropertyList(Object, Object, Environment).
100: */
101: public static void setPropertyList(Object symbol, Object plist) {
102: setPropertyList(symbol, plist, Environment.getCurrent());
103:
104: }
105:
106: /** Gets a property value associated with an object.
107: * @param symbol Usually a <code>Symbol</code>, but can be any
108: * <code>Object>/code>. A <code>String</code> is converted to a
109: * <code>Symbol</code> using <code>env.getSymbol()</code>.
110: * Symbols require a constant-type hash lookup; other object
111: * are serached linearly.
112: */
113: public static Object getProperty(Object symbol, Object property,
114: Object defaultValue, Environment env) {
115: if (!(symbol instanceof Symbol)) {
116: if (symbol instanceof String)
117: symbol = Namespace.getDefaultSymbol((String) symbol);
118: else
119: return plistGet(env.get(Symbol.PLIST, symbol,
120: LList.Empty), property, defaultValue);
121: }
122: return env.get((Symbol) symbol, property, defaultValue);
123: }
124:
125: /** Gets a property value associated with an object.
126: * Corresponds to Common Lisp's <code>get</code> function.
127: * @see #getProperty(Object, Object, Object, Environment).
128: */
129: public static Object getProperty(Object symbol, Object property,
130: Object defaultValue) {
131: return getProperty(symbol, property, defaultValue, Environment
132: .getCurrent());
133: }
134:
135: public static void putProperty(Object symbol, Object property,
136: Object newValue, Environment env) {
137: if (!(symbol instanceof Symbol)) {
138: if (symbol instanceof String)
139: symbol = Namespace.getDefaultSymbol((String) symbol);
140: else {
141: Location lloc = env.getLocation(Symbol.PLIST, symbol);
142: lloc.set(plistPut(lloc.get(LList.Empty), property,
143: newValue));
144: return;
145: }
146: }
147: Location loc = env.lookup((Symbol) symbol, property);
148: if (loc != null
149: && (loc = loc.getBase()) instanceof PropertyLocation)
150: ((PropertyLocation) loc).set(newValue);
151: else {
152: Location lloc = env.getLocation(Symbol.PLIST, symbol);
153: Object plist = lloc.get(LList.Empty);
154: Pair pair = null;
155: /*
156: pair = null;
157: if (plist instanceof Pair)
158: {
159: pair = (Pair) plist;
160: for (;;)
161: {
162: if (pair.car == property)
163: break;
164: if (pair.cdr instanceof Pair)
165: pair = (Pair) pair.cdr;
166: else
167: {
168: pair = null;
169: break;
170: }
171: }
172: }
173: if (pair == null)
174: plist = pair = new Pair(property, new Pair(newValue, plist));
175: */
176: pair = new Pair(newValue, plist);
177: plist = new Pair(property, pair);
178: lloc.set(plist);
179: PropertyLocation ploc = new PropertyLocation();
180: //ploc.plist = lloc;
181: ploc.pair = pair;
182: env.addLocation((Symbol) symbol, property, ploc);
183: }
184: }
185:
186: /** Sets a property value associated with an object.
187: * Corresponds to Common Lisp's <code>(setf get)</code> function.
188: */
189: public static void putProperty(Object symbol, Object property,
190: Object newValue) {
191: putProperty(symbol, property, newValue, Environment
192: .getCurrent());
193: }
194:
195: /** Remove a properaty assocatied with an object.
196: */
197: public static boolean removeProperty(Object symbol,
198: Object property, Environment env) {
199: Location ploc = env.lookup(Symbol.PLIST, symbol);
200: if (ploc == null)
201: return false;
202: Object plist = ploc.get(LList.Empty);
203: if (!(plist instanceof Pair))
204: return false;
205: Pair pair = (Pair) plist;
206: Pair prev = null;
207: for (;;) {
208: if (pair.car == property)
209: break;
210: Object next = pair.cdr;
211: if (!(next instanceof Pair))
212: return false;
213: prev = pair;
214: pair = (Pair) next;
215: }
216: Object tail = ((Pair) pair.cdr).cdr;
217: if (prev == null) {
218: plist = tail;
219: ploc.set(plist);
220: } else
221: prev.cdr = tail;
222: if (symbol instanceof Symbol)
223: env.remove((Symbol) symbol, property);
224: return true;
225: }
226:
227: /** Remove a properaty assocatied with an object.
228: * Corresponds to Common Lisp's <code>remprop</code> function.
229: */
230: public static boolean removeProperty(Object symbol, Object property) {
231: return removeProperty(symbol, property, Environment
232: .getCurrent());
233: }
234:
235: /**
236: * Given a property list and a key, find the corresponding property value.
237: */
238: public static Object plistGet(Object plist, Object prop,
239: Object dfault) {
240: while (plist instanceof Pair) {
241: Pair pair = (Pair) plist;
242: if (pair.car == prop)
243: return ((Pair) pair.cdr).car;
244: }
245: return dfault;
246: }
247:
248: /** Modify and add a property binding to a property list.
249: * @return The updated property list.
250: */
251: public static Object plistPut(Object plist, Object prop,
252: Object value) {
253: for (Object p = plist; p instanceof Pair;) {
254: Pair pair = (Pair) p;
255: Pair next = (Pair) pair.cdr;
256: if (pair.car == prop) {
257: next.car = value;
258: return plist;
259: }
260: p = next.cdr;
261: }
262: return new Pair(prop, new Pair(value, plist));
263: }
264:
265: /** Remove a property binding from a property list.
266: * @return The updated property list.
267: */
268: public static Object plistRemove(Object plist, Object prop) {
269: Pair prev = null;
270: for (Object p = plist; p instanceof Pair;) {
271: Pair pair = (Pair) p;
272: Pair next = (Pair) pair.cdr;
273: p = next.cdr;
274: if (pair.car == prop) {
275: if (prev == null)
276: return p;
277: prev.cdr = p;
278: return plist;
279: }
280: prev = next;
281: }
282: return plist;
283: }
284: }
|