001: package sisc.modules.hashtable;
002:
003: import sisc.data.*;
004: import java.util.HashMap;
005: import java.util.Map;
006: import java.util.Iterator;
007: import java.io.IOException;
008:
009: import sisc.io.ValueWriter;
010: import sisc.ser.Serializer;
011: import sisc.ser.Deserializer;
012: import sisc.util.ExpressionVisitor;
013: import sisc.util.Util;
014: import sisc.nativefun.FixableProcedure;
015: import sisc.interpreter.Interpreter;
016: import sisc.interpreter.Context;
017: import sisc.interpreter.ContinuationException;
018: import sisc.interpreter.SchemeException;
019:
020: public class Hashtable extends HashtableBase {
021:
022: private HashMap ht;
023: private Pair alist;//tmp store during deserialisation;
024:
025: private Procedure equalsProc;
026: private Procedure hashProc;
027:
028: public Hashtable() {
029: this .ht = new HashMap(0);
030: }
031:
032: public Hashtable(Procedure equalsProc, Procedure hashProc) {
033: this ();
034: this .equalsProc = equalsProc;
035: this .hashProc = hashProc;
036: }
037:
038: public Procedure getEqualsProc() {
039: return equalsProc;
040: }
041:
042: public Procedure getHashProc() {
043: return hashProc;
044: }
045:
046: public boolean callEquals(Value v1, Value v2) {
047: Value rv = Util.VOID;
048: if (equalsProc instanceof FixableProcedure) {
049: try {
050: rv = ((FixableProcedure) equalsProc).apply(v1, v2);
051: } catch (ContinuationException ce) {
052: //TODO: handle this error better?
053: Procedure.throwPrimException(ce.getMessage());
054: }
055: } else {
056: Interpreter r = Context.enter();
057: try {
058: rv = r.eval(equalsProc, new Value[] { v1, v2 });
059: } catch (SchemeException e) {
060: Procedure.throwNestedPrimException(Util.liMessage(
061: Primitives.SHASHB, "equalsexception",
062: equalsProc.toString()), e);
063: } finally {
064: Context.exit();
065: }
066: }
067:
068: if (rv instanceof SchemeBoolean) {
069: return Util.truth((SchemeBoolean) rv);
070: } else {
071: Procedure.throwPrimException(Util.liMessage(
072: Primitives.SHASHB, "equalsreturn", equalsProc
073: .toString(), rv.synopsis()));
074: return false; //dummy
075: }
076: }
077:
078: public int callHashCode(Value v) {
079: Value rv = Util.VOID;
080:
081: if (hashProc instanceof FixableProcedure) {
082: try {
083: rv = ((FixableProcedure) hashProc).apply(v);
084: } catch (ContinuationException ce) {
085: //TODO: handle this error better?
086: Procedure.throwPrimException(ce.getMessage());
087: }
088: } else {
089: Interpreter r = Context.enter();
090: try {
091: rv = r.eval(hashProc, new Value[] { v });
092: } catch (SchemeException e) {
093: Procedure.throwNestedPrimException(Util.liMessage(
094: Primitives.SHASHB, "hashexception", hashProc
095: .toString()), e);
096: } finally {
097: Context.exit();
098: }
099: }
100:
101: if (rv instanceof Quantity) {
102: return ((Quantity) rv).intValue();
103: } else {
104: Procedure.throwPrimException(Util.liMessage(
105: Primitives.SHASHB, "hashreturn", hashProc
106: .toString(), rv.synopsis()));
107: return 0; //dummy
108: }
109: }
110:
111: private class Key implements HashtableKey {
112:
113: private Value key;
114:
115: public Key(Value key) {
116: this .key = key;
117: }
118:
119: public Value getValue() {
120: return key;
121: }
122:
123: public boolean equals(Object o) {
124: return (o instanceof Key) && callEquals(key, ((Key) o).key);
125: }
126:
127: public int hashCode() {
128: return callHashCode(key);
129: }
130:
131: }
132:
133: protected HashtableKey makeKey(Value k) {
134: return new Key(k);
135: }
136:
137: protected Map getMap() {
138: if (alist != null) {
139: addAList(ht, alist);
140: alist = null;
141: }
142: return ht;
143: }
144:
145: private void addAList(Map m, Pair p) {
146: for (; p != EMPTYLIST; p = pair(p.cdr())) {
147: Pair entry = pair(p.car());
148: m.put(makeKey(entry.car()), entry.cdr());
149: }
150: }
151:
152: //NB: getKey is allowed to return null, indicating that the key is
153: //no longer valid. This is never the case in this class, but
154: //sub-classes, notably WeakHashtable, exploit this feature.
155: private Value getKey(Object o) {
156: return ((HashtableKey) o).getValue();
157: }
158:
159: private Value getMapKey(Map.Entry e) {
160: return getKey(e.getKey());
161: }
162:
163: private Value getMapValue(Map.Entry e) {
164: return (Value) e.getValue();
165: }
166:
167: public Value get(Value k) {
168: return (Value) getMap().get(makeKey(k));
169: }
170:
171: public Value put(Value k, Value v) {
172: return (Value) getMap().put(makeKey(k), v);
173: }
174:
175: public Value remove(Value k) {
176: return (Value) getMap().remove(makeKey(k));
177: }
178:
179: public int size() {
180: return getMap().size();
181: }
182:
183: public void clear() {
184: getMap().clear();
185: }
186:
187: public void addAList(Pair p) {
188: Map m = getMap();
189: addAList(m, p);
190: }
191:
192: public Pair toAList() {
193: Iterator i = getMap().entrySet().iterator();
194: Pair res = EMPTYLIST;
195: while (i.hasNext()) {
196: Map.Entry e = (Map.Entry) i.next();
197: Value key = getMapKey(e);
198: Value val = getMapValue(e);
199: if (key != null) {
200: res = new Pair(new Pair(key, val), res);
201: }
202: }
203: return res;
204: }
205:
206: public Pair keys() {
207: Iterator i = getMap().keySet().iterator();
208: Pair res = EMPTYLIST;
209: while (i.hasNext()) {
210: Value key = getKey(i.next());
211: if (key != null) {
212: res = new Pair(key, res);
213: }
214: }
215: return res;
216: }
217:
218: public boolean valueEqual(Value v) {
219: if (v == this )
220: return true;
221: if (!(v instanceof Hashtable))
222: return false;
223: Hashtable o = (Hashtable) v;
224: if (size() != o.size())
225: return false;
226: if (!equalsProc.valueEqual(o.equalsProc)
227: || !hashProc.valueEqual(o.hashProc))
228: return false;
229: for (Iterator i = getMap().entrySet().iterator(); i.hasNext();) {
230: Map.Entry e = (Map.Entry) i.next();
231: Value key = getMapKey(e);
232: Value val = getMapValue(e);
233: if (key == null || !val.valueEqual(o.get(key)))
234: return false;
235: }
236: return true;
237: }
238:
239: public int valueHashCode() {
240: int res = equalsProc.valueHashCode() ^ hashProc.valueHashCode();
241: for (Iterator i = getMap().entrySet().iterator(); i.hasNext();) {
242: Map.Entry e = (Map.Entry) i.next();
243: Value key = getMapKey(e);
244: Value val = getMapValue(e);
245: if (key != null) {
246: res += key.valueHashCode() ^ val.valueHashCode();
247: }
248: }
249: return res;
250: }
251:
252: public void serialize(Serializer s) throws IOException {
253: s.writeExpression(equalsProc);
254: s.writeExpression(hashProc);
255: s.writeExpression(toAList());
256: }
257:
258: public void deserialize(Deserializer s) throws IOException {
259: equalsProc = (Procedure) s.readExpression();
260: hashProc = (Procedure) s.readExpression();
261: alist = (Pair) s.readExpression();
262: }
263:
264: public boolean visit(ExpressionVisitor v) {
265: if (!v.visit(equalsProc) || !v.visit(hashProc))
266: return false;
267: Iterator i = getMap().entrySet().iterator();
268: while (i.hasNext()) {
269: Map.Entry e = (Map.Entry) i.next();
270: Value key = getMapKey(e);
271: Value val = getMapValue(e);
272: if (key != null) {
273: if (!v.visit(key))
274: return false;
275: if (!v.visit(val))
276: return false;
277: }
278: }
279: return true;
280: }
281:
282: public void display(ValueWriter w) throws IOException {
283: w.append("#<").append(
284: Util.liMessage(Primitives.SHASHB, "hashtable")).append(
285: ' ').append(equalsProc).append(' ').append(hashProc)
286: .append(" (");
287: Iterator i = getMap().entrySet().iterator();
288: while (i.hasNext()) {
289: Map.Entry e = (Map.Entry) i.next();
290: Value key = getMapKey(e);
291: Value val = getMapValue(e);
292: if (key != null) {
293: w.append('(').append(key).append(" . ").append(val)
294: .append(')');
295: }
296: }
297: w.append(")>");
298: }
299:
300: }
301:
302: /*
303: * The contents of this file are subject to the Mozilla Public
304: * License Version 1.1 (the "License"); you may not use this file
305: * except in compliance with the License. You may obtain a copy of
306: * the License at http://www.mozilla.org/MPL/
307: *
308: * Software distributed under the License is distributed on an "AS
309: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
310: * implied. See the License for the specific language governing
311: * rights and limitations under the License.
312: *
313: * The Original Code is the Second Interpreter of Scheme Code (SISC).
314: *
315: * The Initial Developer of the Original Code is Scott G. Miller.
316: * Portions created by Scott G. Miller are Copyright (C) 2000-2007
317: * Scott G. Miller. All Rights Reserved.
318: *
319: * Contributor(s):
320: * Matthias Radestock
321: *
322: * Alternatively, the contents of this file may be used under the
323: * terms of the GNU General Public License Version 2 or later (the
324: * "GPL"), in which case the provisions of the GPL are applicable
325: * instead of those above. If you wish to allow use of your
326: * version of this file only under the terms of the GPL and not to
327: * allow others to use your version of this file under the MPL,
328: * indicate your decision by deleting the provisions above and
329: * replace them with the notice and other provisions required by
330: * the GPL. If you do not delete the provisions above, a recipient
331: * may use your version of this file under either the MPL or the
332: * GPL.
333: */
|