001: // Copyright (c) 1996-2000 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.io.*;
007:
008: /** A Binding is a Location in an Environment object. */
009:
010: public class Binding extends Location implements Externalizable
011: // implements java.util.Map.Entry
012: {
013: /** The current value of the binding. */
014: Object value;
015:
016: Constraint constraint;
017:
018: /** Magic value used to indicate there is no property binding. */
019: public static final String UNBOUND = new String("(unbound)");
020:
021: /** Magic value used as a key for function bindings. */
022: static final String FUNCTION = new String("(function).");
023:
024: public final Object get() {
025: return constraint.get(this );
026: }
027:
028: public final Object get(Object defaultValue) {
029: return constraint.get(this , defaultValue);
030: }
031:
032: public Procedure getProcedure() {
033: try {
034: return constraint.getProcedure(this );
035: } catch (UnboundSymbol ex) {
036: // FIXME!!!
037: Object f = getFunctionValue(UNBOUND);
038: if (f != UNBOUND)
039: return (Procedure) f;
040: throw ex;
041: }
042: }
043:
044: public final void defineValue(Object value) {
045: Environment env = constraint.getEnvironment(this );
046: if (env.locked)
047: set(value);
048: else {
049: this .constraint = TrivialConstraint.getInstance(this );
050: this .value = value;
051: }
052: }
053:
054: public final void defineConstant(Object value) {
055: Environment env = constraint.getEnvironment(this );
056: if (env.locked)
057: set(value);
058: else {
059: this .constraint = ConstantConstraint.getInstance(env);
060: this .value = value;
061: }
062: }
063:
064: public final void set(Object value) {
065: constraint.set(this , value);
066: }
067:
068: public final Constraint getConstraint() {
069: return constraint;
070: }
071:
072: public final void setConstraint(Constraint constraint) {
073: this .constraint = constraint;
074: }
075:
076: public boolean isBound() {
077: return constraint.isBound(this );
078: }
079:
080: public Binding() {
081: }
082:
083: public Binding(String name) {
084: setName(name);
085: }
086:
087: // The compiler emits calls to this method.
088: public static Binding make(Object init, String name) {
089: Binding binding = new Binding(name);
090: binding.value = init;
091: binding.constraint = TrivialConstraint
092: .getInstance((Environment) null);
093: return binding;
094: }
095:
096: // gnu.expr.LitTable uses this.
097: public static Binding make(String name, Environment env) {
098: if (env == null)
099: return new Binding(name);
100: else
101: return env.getBinding(name);
102: }
103:
104: public void print(java.io.PrintWriter ps) {
105: ps.print("#<binding ");
106: String name = getName();
107: if (name != null)
108: ps.print(name);
109: if (isBound()) {
110: ps.print(" -> ");
111: SFormat.print(get(), ps);
112: } else
113: ps.print("(unbound)");
114: ps.print('>');
115: }
116:
117: // Methods that implement java.util.Map.Entry:
118:
119: public final Object getKey() {
120: return getName();
121: }
122:
123: public final Object getValue() {
124: return constraint.get(this );
125: }
126:
127: public final Object setValue(Object value) {
128: Object old = constraint.get(this );
129: constraint.set(this , value);
130: return old;
131: }
132:
133: /** Just tests for identity.
134: * Otherwise hashTables that have Bindings as keys will break. */
135: public boolean equals(Object o) {
136: return this == o;
137: }
138:
139: public int hashCode() {
140: // Note: The hashCode should not depend on the value.
141: // This is contrary to the Map.Entry specification.
142: return System.identityHashCode(this ); // ^ System.identityHashCode(env);
143: }
144:
145: /** Used to mark deleted elements in a hash table. */
146: public final static Binding hashDELETED = new Binding(new String(
147: "<Deleted>"));
148:
149: /** Search a hash table using double hashing and open addressing.
150: * @param table the hash table
151: * @param log2Size log2 of the (used) size of table
152: * @param mask must equal ((1 << log2Size) - 1)
153: * @param key the search key
154: * @param hash the hash of the search key
155: * @return the index of the element in table containing the match
156: * (such that table[index].getName()==key);
157: * if there is no such element, returns an index
158: * such that (table[index]==null || tabel[index]==DELETED). */
159: public static int hashSearch(Binding[] table, int log2Size,
160: int mask, String key, int hash) {
161: int index = hash & mask;
162: Binding element = table[index];
163: if (element == null || element.getName() == key)
164: return index;
165: int avail = -1;
166: int step = (((hash >> log2Size) ^ index) << 1) + 1;
167: for (;;) {
168: if (element == hashDELETED && avail < 0)
169: avail = index;
170: index = (index + step) & mask;
171: element = table[index];
172: if (element == null)
173: return avail < 0 ? index : avail;
174: if (element.getName() == key)
175: return index;
176: }
177: }
178:
179: public static int hashSearch(Binding[] table, int log2Size,
180: String key) {
181: return hashSearch(table, log2Size, (1 << log2Size) - 1, key,
182: System.identityHashCode(key));
183: }
184:
185: /** Find an entry in a hash table.
186: * @param table the hash table
187: * @param log2Size log2 of the (used) size of table
188: * @param key the search key
189: * @return null if the was no matching element in the hash table;
190: * otherwise the matching element. */
191: public static Binding hashGet(Binding[] table, int log2Size,
192: String key) {
193: int index = hashSearch(table, log2Size, (1 << log2Size) - 1,
194: key, System.identityHashCode(key));
195: Binding element = table[index];
196: if (element == null || element == hashDELETED)
197: return null;
198: return element;
199: }
200:
201: /** Set an entry in a hash table.
202: * @param table the hash table
203: * @param log2Size log2 of the (used) size of table
204: * @param value the new entry
205: * @return null if the was no matching element in the hash table;
206: * otherwise the old match. */
207: public static Binding hashSet(Binding[] table, int log2Size,
208: Binding value) {
209: String key = value.getName();
210: int index = hashSearch(table, log2Size, (1 << log2Size) - 1,
211: key, System.identityHashCode(key));
212: Binding element = table[index];
213: table[index] = value;
214: return element == hashDELETED ? null : element;
215: }
216:
217: /** Delete an entry from a hash table.
218: * @param table the hash table
219: * @param log2Size log2 of the (used) size of table
220: * @param key the search key
221: * @return null if the was no matching element in the hash table;
222: * otherwise the old element. */
223: public static Binding hashDelete(Binding[] table, int log2Size,
224: String key) {
225: int index = hashSearch(table, log2Size, (1 << log2Size) - 1,
226: key, System.identityHashCode(key));
227: Binding element = table[index];
228: table[index] = hashDELETED;
229: return element == hashDELETED ? null : element;
230: }
231:
232: public static int hashInsertAll(Binding[] tableDst,
233: int log2SizeDst, Binding[] tableSrc, int log2SizeSrc) {
234: int countInserted = 0;
235: int sizeSrc = 1 << log2SizeSrc;
236: int sizeDst = 1 << log2SizeDst;
237: int maskDst = (1 << log2SizeDst) - 1;
238: for (int i = sizeSrc; --i >= 0;) {
239: Binding element = tableSrc[i];
240: if (element != null && element != hashDELETED) {
241: String key = element.getName();
242: int index = hashSearch(tableDst, log2SizeDst, maskDst,
243: key, System.identityHashCode(key));
244: Binding oldElement = tableDst[index];
245: if (oldElement != null && oldElement != hashDELETED)
246: countInserted++;
247: tableDst[index] = element;
248: }
249: }
250: return countInserted;
251: }
252:
253: /** Get value of "function binding" of a Binding.
254: * Some languages (including Common Lisp and Emacs Lisp) associate both
255: * a value binding and a function binding with a symbol.
256: * @exception UnboundSymbol if no function binding.
257: */
258: public final Object getFunctionValue() {
259: Object value = constraint.getFunctionValue(this );
260: if (value == UNBOUND)
261: throw new UnboundSymbol(getName());
262: return value;
263: }
264:
265: /** Get value of "function binding" of a Binding.
266: * Some languages (including Common Lisp and Emacs Lisp) associate both
267: * a value binding and a function binding with a symbol.
268: * @param defaultValue value to return if there is no function binding
269: * @return the function value, or defaultValue if no function binding.
270: */
271: public final Object getFunctionValue(Object defaultValue) {
272: Object value = constraint.getFunctionValue(this );
273: return value == UNBOUND ? defaultValue : value;
274: }
275:
276: public boolean hasFunctionValue() {
277: Object value = constraint.getFunctionValue(this );
278: return value != UNBOUND;
279: }
280:
281: public void setFunctionValue(Object value) {
282: constraint.setFunctionValue(this , value);
283: }
284:
285: public void removeFunctionValue() {
286: constraint.setFunctionValue(this , UNBOUND);
287: }
288:
289: public final Environment getEnvironment() {
290: return constraint.getEnvironment(this );
291: }
292:
293: public String toString() {
294: return getName();
295: }
296:
297: public void writeExternal(ObjectOutput out) throws IOException {
298: out.writeObject(getName());
299: out.writeObject(getEnvironment());
300: }
301:
302: public void readExternal(ObjectInput in) throws IOException,
303: ClassNotFoundException {
304: String name = (String) in.readObject();
305: Environment env = (Environment) in.readObject();
306: if (env != null)
307: constraint = env.unboundConstraint;
308: setName(name);
309: }
310:
311: public Object readResolve() throws ObjectStreamException {
312: if (constraint == null)
313: return this ;
314: Environment env = constraint.getEnvironment(this);
315: if (env == null)
316: return this;
317: return make(env, getName());
318: }
319:
320: }
|