001: // Copyright (c) 1996-2000, 2001, 2002, 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: import java.io.*;
008:
009: /** A mapping from strings ("print names") to <code>Symbol</code>s.
010: * Namespaces are normally named and can be accessed from a global table.
011: * They correspond to Common Lisp "packages" (which are implemented
012: * using <code>gnu.kawa.lispexpr.LispPackage</code>,
013: * which extends <code>Namespace</code>).
014: * A <code>Namespace</code> is a "weak" mapping in the sense that a
015: * <code>Symbol</code> can be garbage collected even though it is
016: * referenced from a <code>Namespace</code>.
017: * @author Per Bothner
018: */
019:
020: public class Namespace implements Externalizable, HasNamedParts {
021: /** Map namepsace names (and nick-names) to Namespaces. */
022: protected static final Hashtable nsTable = new Hashtable(50);
023:
024: /** The Namespace with the empty name. */
025: public static final Namespace EmptyNamespace = getInstance("");
026:
027: /** Should be interned. */
028: String name;
029:
030: protected String prefix = "";
031:
032: /** Get the name of this Namespace. */
033: public final String getName() {
034: return name;
035: }
036:
037: /** Set the name of this Namespace. */
038: public final void setName(String name) {
039: this .name = name;
040: }
041:
042: public Namespace() {
043: this (64);
044: }
045:
046: public Namespace(int capacity) {
047: log2Size = 4;
048: while (capacity > (1 << log2Size))
049: log2Size++;
050: capacity = 1 << log2Size;
051: table = new SymbolRef[capacity];
052: mask = capacity - 1;
053: }
054:
055: public static Namespace getDefault() {
056: return EmptyNamespace;
057: }
058:
059: public static Symbol getDefaultSymbol(String name) {
060: return EmptyNamespace.getSymbol(name);
061: }
062:
063: public static Namespace make(String name) // Needed for Literals.
064: {
065: return getInstance(name);
066: }
067:
068: public static Namespace getInstance(String name) {
069: if (name == null)
070: name = "";
071: synchronized (nsTable) {
072: Namespace ns = (Namespace) nsTable.get(name);
073: if (ns != null)
074: return ns;
075: ns = new Namespace();
076: ns.setName(name.intern());
077: nsTable.put(name, ns);
078: return ns;
079: }
080: }
081:
082: public static Namespace make(String uri, String prefix) {
083: if (prefix == null || prefix.length() == 0)
084: return getInstance(uri);
085: String xname = prefix + " -> " + uri;
086: synchronized (nsTable) {
087: Object old = nsTable.get(xname);
088: if (old instanceof Namespace)
089: return (Namespace) old;
090: Namespace ns = new Namespace();
091: ns.setName(uri);
092: ns.prefix = prefix;
093: nsTable.put(xname, ns);
094: return ns;
095: }
096: }
097:
098: /** Create a "placeholder" for a namespace with a known prefix
099: * but unknown uri.
100: * @see Symbol#makeWithUnknownNamespace
101: */
102: public static Namespace makeUnknownNamespace(String prefix) {
103: String uri;
104: if (prefix == null || prefix == "")
105: uri = "";
106: else
107: uri = "http://kawa.gnu.org/unknown-namespace/" + prefix;
108: return Namespace.make(uri, prefix);
109: }
110:
111: public Object get(String key) {
112: return Environment.getCurrent().get(getSymbol(key));
113: }
114:
115: public boolean isConstant(String key) {
116: return false;
117: }
118:
119: /** Get a Symbol matching the given name.
120: * Creates a new Symbol if one is not found.
121: * Equivalent to Common Lisp's "intern" function.
122: */
123: public Symbol getSymbol(String key) {
124: return lookup(key, key.hashCode(), true);
125: }
126:
127: /** Get a Symbol matching the given name.
128: * Returns null if one is not found.
129: */
130: public Symbol lookup(String key) {
131: return lookup(key, key.hashCode(), false);
132: }
133:
134: protected final Symbol lookupInternal(String key, int hash) {
135: int index = hash & mask;
136: SymbolRef prev = null;
137: for (SymbolRef ref = table[index]; ref != null;) {
138: SymbolRef next = ref.next;
139: Symbol sym = ref.getSymbol();
140: if (sym == null) {
141: // Weakly referenced object has been collected.
142: if (prev == null)
143: table[index] = next;
144: else
145: prev.next = next;
146: num_bindings--;
147: } else {
148: if (sym.getLocalPart().equals(key))
149: return sym;
150: prev = ref;
151: }
152: ref = next;
153: }
154: return null;
155: }
156:
157: public Symbol add(Symbol sym, int hash) {
158: int index = hash & mask;
159: SymbolRef ref = new SymbolRef(sym, this );
160: sym.namespace = this ;
161: ref.next = table[index];
162: table[index] = ref;
163: num_bindings++;
164: if (num_bindings >= table.length)
165: rehash();
166: return sym;
167: }
168:
169: public Symbol lookup(String key, int hash, boolean create) {
170: synchronized (this ) {
171: Symbol sym = lookupInternal(key, hash);
172: if (sym != null)
173: return sym;
174:
175: /*
176: // Do we need to synchronize on nsTable as well? FIXME
177: for (NamespaceUse used = imported; used != null;
178: used = used.nextImported)
179: {
180: el = lookup(key, hash, false);
181: if (el != null)
182: return el;
183: }
184: */
185: if (create)
186: return add(new Symbol(this , key), hash);
187: else
188: return null;
189: }
190: }
191:
192: public boolean remove(Symbol symbol) {
193: synchronized (this ) {
194: String name = symbol.getLocalPart();
195: int hash = name.hashCode();
196: int index = hash & mask;
197: SymbolRef prev = null;
198: SymbolRef ref = table[index];
199: while (ref != null) {
200: SymbolRef next = ref.next;
201: Symbol refsym = ref.getSymbol();
202: if (refsym == null || refsym == symbol) {
203: if (prev == null)
204: table[index] = next;
205: else
206: prev.next = next;
207: num_bindings--;
208: if (refsym != null)
209: return true;
210: } else
211: prev = ref;
212: ref = next;
213: }
214: return false;
215: }
216: }
217:
218: protected SymbolRef[] table;
219: int log2Size;
220: private int mask;
221: int num_bindings;
222:
223: protected void rehash() {
224: int oldCapacity = table.length;
225: int newCapacity = 2 * oldCapacity;
226: int newMask = newCapacity - 1;
227: int countInserted = 0;
228: SymbolRef[] oldTable = table;
229: SymbolRef[] newTable = new SymbolRef[newCapacity];
230:
231: for (int i = oldCapacity; --i >= 0;) {
232: for (SymbolRef ref = oldTable[i]; ref != null;) {
233: SymbolRef next = ref.next;
234: Symbol sym = ref.getSymbol();
235: if (sym != null) {
236: String key = sym.getName();
237: int hash = key.hashCode();
238: int index = hash & newMask;
239: countInserted++;
240: ref.next = newTable[index];
241: newTable[index] = ref;
242: }
243: ref = next;
244: }
245: }
246:
247: table = newTable;
248: log2Size++;
249: mask = newMask;
250: num_bindings = countInserted;
251: }
252:
253: public void writeExternal(ObjectOutput out) throws IOException {
254: out.writeObject(getName());
255: out.writeObject(prefix);
256: }
257:
258: public void readExternal(ObjectInput in) throws IOException,
259: ClassNotFoundException {
260: name = ((String) in.readObject()).intern();
261: prefix = (String) in.readObject();
262: }
263:
264: public Object readResolve() throws ObjectStreamException {
265: String name = getName();
266: if (name != null) {
267: String xname = (prefix == null || prefix.length() == 0 ? name
268: : (prefix + " -> " + name));
269: Namespace ns = (Namespace) nsTable.get(xname);
270: if (ns != null)
271: return ns;
272: nsTable.put(xname, this );
273: }
274: return this ;
275:
276: }
277:
278: public String toString() {
279: return "#,(namespace \"" + name + "\")";
280: }
281: }
282:
283: /** A week reference to a <code>Symbol</code>.
284: * This is to allow <code>Symbol</code>s to be garbage collected,
285: * even though they're referenced from a <code>Namespace</code>. */
286:
287: class SymbolRef
288: /* #ifdef JAVA2 */
289: extends java.lang.ref.WeakReference
290: /* #endif */
291: {
292: SymbolRef next;
293:
294: /* #ifndef JAVA2 */
295: // Symbol sym;
296: /* #endif */
297:
298: /*
299: String getName ()
300: {
301: Symbol sym = getSymbol();
302: return sym == null ? null : sym.getName();
303: }
304: */
305:
306: SymbolRef(Symbol sym, Namespace ns) {
307: /* #ifdef JAVA2 */
308: super (sym);
309: /* #else */
310: // this.sym = sym;
311: /* #endif */
312: }
313:
314: Symbol getSymbol() {
315: /* #ifdef JAVA2 */
316: return (Symbol) get();
317: /* #endif */
318: /* #ifndef JAVA2 */
319: // return sym;
320: /* #endif */
321: }
322:
323: public String toString() {
324: return "SymbolRef[" + getSymbol() + "]";
325: }
326: }
|