Namespace.java 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. // Copyright (c) 1996-2000, 2001, 2002, 2004, 2012 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.mapping;
  4. import gnu.kawa.util.*;
  5. import java.util.*;
  6. import java.io.*;
  7. /** A mapping from strings ("print names") to <code>Symbol</code>s.
  8. * Namespaces are normally named and can be accessed from a global table.
  9. * They correspond to Common Lisp "packages" (which are implemented
  10. * using <code>gnu.kawa.lispexpr.LispPackage</code>,
  11. * which extends <code>Namespace</code>).
  12. * A <code>Namespace</code> is a "weak" mapping in the sense that a
  13. * <code>Symbol</code> can be garbage collected even though it is
  14. * referenced from a <code>Namespace</code>.
  15. * @author Per Bothner
  16. */
  17. public class Namespace
  18. extends AbstractHashTable<SymbolRef, String, Symbol>
  19. implements Externalizable, HasNamedParts
  20. {
  21. /** Map namepsace names (and nick-names) to Namespaces. */
  22. protected static final Hashtable nsTable = new Hashtable(50);
  23. /** The Namespace with the empty name. */
  24. public static final Namespace EmptyNamespace = valueOf("");
  25. /** Should be interned. */
  26. String name;
  27. protected String prefix = "";
  28. /** Get the name of this Namespace. */
  29. public final String getName () { return name; }
  30. /** Set the name of this Namespace. */
  31. public final void setName (String name) { this.name = name; }
  32. public final String getPrefix () { return prefix; }
  33. public Namespace()
  34. {
  35. this(64);
  36. }
  37. protected Namespace (int capacity)
  38. {
  39. super(capacity);
  40. }
  41. public static Namespace create (int capacity)
  42. {
  43. return new Namespace(capacity);
  44. }
  45. public static Namespace create ()
  46. {
  47. return new Namespace(64);
  48. }
  49. public static Namespace getDefault ()
  50. {
  51. return EmptyNamespace;
  52. }
  53. public static Symbol getDefaultSymbol (String name)
  54. {
  55. return EmptyNamespace.getSymbol(name);
  56. }
  57. public static Namespace valueOf ()
  58. {
  59. return EmptyNamespace;
  60. }
  61. /** Return Namespace with the given name (namespace-URI).
  62. * Create it if needed.
  63. */
  64. public static Namespace valueOf (String name)
  65. {
  66. if (name == null)
  67. name = "";
  68. synchronized (nsTable)
  69. {
  70. Namespace ns = (Namespace) nsTable.get(name);
  71. if (ns != null)
  72. return ns;
  73. ns = new Namespace ();
  74. ns.setName(name.intern());
  75. nsTable.put(name, ns);
  76. return ns;
  77. }
  78. }
  79. /** Return Namespace with the given name (namespace-URI), if it exists.
  80. * Return null if no such namespace exists.
  81. */
  82. public static Namespace valueOfNoCreate(String name) {
  83. if (name == null)
  84. name = "";
  85. return (Namespace) nsTable.get(name);
  86. }
  87. public static Namespace valueOf (String uri, String prefix)
  88. {
  89. if (prefix == null || prefix.length() == 0)
  90. return valueOf(uri);
  91. String xname = prefix + " -> "+ uri;
  92. synchronized (nsTable)
  93. {
  94. Object old = nsTable.get(xname);
  95. if (old instanceof Namespace)
  96. return (Namespace) old;
  97. Namespace ns = new Namespace();
  98. if (uri != UNKNOWN_NAMESPACE)
  99. uri = uri.intern();
  100. ns.setName(uri);
  101. ns.prefix = prefix.intern();
  102. nsTable.put(xname, ns);
  103. return ns;
  104. }
  105. }
  106. public static Namespace valueOf (String uri, SimpleSymbol prefix)
  107. {
  108. return valueOf(uri, prefix == null ? null : prefix.getName());
  109. }
  110. public static final String UNKNOWN_NAMESPACE = new String("$unknown$");
  111. /** A namespace with known prefix but unknown uri. */
  112. public boolean isUnknownNamespace() {
  113. return name == UNKNOWN_NAMESPACE;
  114. }
  115. /** Create a "placeholder" for a namespace with a known prefix
  116. * but unknown uri.
  117. * @see Symbol#makeWithUnknownNamespace
  118. */
  119. public static Namespace makeUnknownNamespace (String prefix) {
  120. String uri = prefix == null || prefix == "" ? ""
  121. : UNKNOWN_NAMESPACE;
  122. return Namespace.valueOf(uri, prefix);
  123. }
  124. public Object get (String key)
  125. {
  126. return Environment.getCurrent().get(getSymbol(key));
  127. }
  128. public boolean isConstant (String key)
  129. {
  130. return false;
  131. }
  132. /** Get a Symbol matching the given name.
  133. * Creates a new Symbol if one is not found.
  134. * Equivalent to Common Lisp's "intern" function.
  135. */
  136. public Symbol getSymbol (String key)
  137. {
  138. return lookup(key, key.hashCode(), true);
  139. }
  140. /** Get a Symbol matching the given name.
  141. * Returns null if one is not found.
  142. */
  143. public Symbol lookup(String key)
  144. {
  145. return lookup(key, key.hashCode(), false);
  146. }
  147. /** Search for an existing Symbol with the give name.
  148. * @param key String - does not need to be interned.
  149. */
  150. protected final Symbol lookupInternal(String key, int hash)
  151. {
  152. int index = hashToIndex(hash);
  153. SymbolRef prev = null;
  154. for (SymbolRef ref = table[index]; ref != null; )
  155. {
  156. SymbolRef next = ref.next;
  157. Symbol sym = ref.getSymbol();
  158. if (sym == null)
  159. {
  160. // Weakly referenced object has been collected.
  161. if (prev == null)
  162. table[index] = next;
  163. else
  164. prev.next = next;
  165. num_bindings--;
  166. }
  167. else
  168. {
  169. if (sym.getLocalPart().equals(key))
  170. return sym;
  171. prev = ref;
  172. }
  173. ref = next;
  174. }
  175. return null;
  176. }
  177. public Symbol add(Symbol sym, int hash)
  178. {
  179. put(sym.getName(), hash, sym);
  180. return sym;
  181. }
  182. public Symbol get (Object key, Symbol defaultValue)
  183. {
  184. if (key instanceof String)
  185. {
  186. Symbol sym = lookup((String) key, key.hashCode(), false);
  187. if (sym != null)
  188. return sym;
  189. }
  190. return defaultValue;
  191. }
  192. public Symbol lookup(String key, int hash, boolean create)
  193. {
  194. synchronized (this)
  195. {
  196. Symbol sym = lookupInternal(key, hash);
  197. if (sym != null)
  198. return sym;
  199. /*
  200. // Do we need to synchronize on nsTable as well? FIXME
  201. for (NamespaceUse used = imported; used != null;
  202. used = used.nextImported)
  203. {
  204. el = lookup(key, hash, false);
  205. if (el != null)
  206. return el;
  207. }
  208. */
  209. if (create)
  210. {
  211. if (this == EmptyNamespace)
  212. sym = new SimpleSymbol(key);
  213. else
  214. sym = new Symbol(key, this);
  215. return add(sym, hash);
  216. }
  217. else
  218. return null;
  219. }
  220. }
  221. public boolean remove (Symbol symbol)
  222. {
  223. synchronized (this)
  224. {
  225. String name = symbol.getLocalPart();
  226. return remove(name) != null;
  227. }
  228. }
  229. protected int getEntryHashCode (SymbolRef entry) { return entry.hashCode(); }
  230. /** Extract next Entry in same hash-bucket. */
  231. protected SymbolRef getEntryNext (SymbolRef entry) { return entry.next; }
  232. /** Set next Entry in same hash-bucket. */
  233. protected void setEntryNext (SymbolRef entry, SymbolRef next) { entry.next = next; }
  234. /** Allocate Entry[n]. */
  235. protected SymbolRef[] allocEntries(int n) { return new SymbolRef[n]; }
  236. protected SymbolRef makeEntry (String key, int hash, Symbol value) {
  237. return new SymbolRef(value);
  238. }
  239. public void writeExternal(ObjectOutput out) throws IOException
  240. {
  241. out.writeObject(getName());
  242. out.writeObject(prefix);
  243. }
  244. public void readExternal(ObjectInput in)
  245. throws IOException, ClassNotFoundException
  246. {
  247. name = ((String) in.readObject()).intern();
  248. prefix = (String) in.readObject();
  249. }
  250. public Object readResolve() throws ObjectStreamException
  251. {
  252. String name = getName();
  253. if (name != null)
  254. {
  255. String xname = (prefix == null || prefix.length() == 0 ? name
  256. : (prefix + " -> "+ name));
  257. Namespace ns = (Namespace) nsTable.get(xname);
  258. if (ns != null)
  259. return ns;
  260. nsTable.put(xname, this);
  261. }
  262. return this;
  263. }
  264. public String toString()
  265. {
  266. StringBuilder sbuf = new StringBuilder("#,(namespace \"");
  267. sbuf.append(name);
  268. sbuf.append('\"');
  269. if (prefix != null && prefix != "")
  270. {
  271. sbuf.append(' ');
  272. sbuf.append(prefix);
  273. }
  274. sbuf.append(')');
  275. return sbuf.toString();
  276. }
  277. }
  278. /** A week reference to a <code>Symbol</code>.
  279. * This is to allow <code>Symbol</code>s to be garbage collected,
  280. * even though they're referenced from a <code>Namespace</code>. */
  281. class SymbolRef
  282. extends java.lang.ref.WeakReference<Symbol>
  283. implements Map.Entry<String,Symbol>
  284. {
  285. SymbolRef next;
  286. String getName () {
  287. Symbol sym = getSymbol();
  288. return sym == null ? null : sym.getName();
  289. }
  290. SymbolRef (Symbol sym) {
  291. super(sym);
  292. }
  293. public String getKey() {
  294. Symbol sym = getSymbol();
  295. return sym == null ? null : sym.getName();
  296. }
  297. public Symbol getValue() {
  298. return getSymbol();
  299. }
  300. public int hashCode() {
  301. Symbol sym = getSymbol();
  302. return sym == null ? 0 : sym.hashCode();
  303. }
  304. public Symbol setValue(Symbol value) {
  305. throw new UnsupportedOperationException();
  306. }
  307. public boolean equals (Object o) {
  308. return o instanceof SymbolRef
  309. && get() == ((SymbolRef) o).get();
  310. }
  311. Symbol getSymbol() {
  312. return get();
  313. }
  314. public String toString() {
  315. return "SymbolRef["+getSymbol()+"]";
  316. }
  317. }