123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- // Copyright (c) 1996-2000, 2002, 2004, 2006 Per M.A. Bothner.
- // This is free software; for terms and warranty disclaimer see ./COPYING.
- package gnu.mapping;
- import java.io.*;
- // Enable JAXP-QName if Symbol should extend javax.xml.namespace.QName.
- // This is not the default, even when JAXP-1.3 is defined, because it makes
- // symbols bigger and some operations (such as equals and handling of
- // uninterned symbols) slower, without much benefit.
- // This option needs some work.
- /* #ifdef JAXP-QName */
- // import javax.xml.namespace.QName;
- /* #endif */
- /** A Symbol is a name, usually in a specific Namespace.
- * A Symbol is stateless: Common Lisp-style "value", "function" and
- * "property list" bindings are not part of the Symbol itself, but
- * looked up in the current Environment.
- * A <code>Symbol</code> may be viewed as an <code>EnvironmentKey</code>
- * with a <code>null</code> property component.
- */
- public class Symbol
- /* #ifdef JAXP-QName */
- // extends QName
- /* #endif */
- implements
- EnvironmentKey,
- /* #ifdef JAVA2 */
- Comparable,
- /* #endif */
- Externalizable
- {
- /* #ifndef JAXP-QName */
- protected String name;
- /* #endif */
- Namespace namespace;
- public final Symbol getKeySymbol () { return this; }
- public final Object getKeyProperty () { return null; }
- public boolean matches (EnvironmentKey key)
- {
- return equals(key.getKeySymbol(), this) && key.getKeyProperty() == null;
- }
- public boolean matches (Symbol symbol, Object property)
- {
- return equals(symbol, this) && property == null;
- }
- /* #ifndef JAXP-QName */
- public final String getNamespaceURI()
- {
- Namespace ns = getNamespace();
- String uri = ns == null ? null : ns.getName();
- return uri == Namespace.UNKNOWN_NAMESPACE ? "" : uri;
- }
- public final String getLocalPart()
- {
- return name;
- }
- public final String getPrefix ()
- {
- Namespace ns = namespace;
- return ns == null ? "" : ns.prefix;
- }
- /* #endif */
- public final boolean hasEmptyNamespace ()
- {
- Namespace ns = getNamespace();
- String nsname;
- return (ns == null
- || (nsname = ns.getName()) == null || nsname.length() == 0);
- }
- public final boolean hasUnknownNamespace() {
- Namespace ns = getNamespace();
- return ns != null && ns.isUnknownNamespace();
- }
- /** Synonym for getName - the "print name" of the symbol without Namespace.
- * Useful when thinking of a Symbol as an XML QName. */
- public final String getLocalName()
- {
- /* #ifdef JAXP-QName */
- // return getLocalPart();
- /* #else */
- return name;
- /* #endif */
- }
- public final String getName()
- {
- /* #ifdef JAXP-QName */
- // return getLocalPart();
- /* #else */
- return name;
- /* #endif */
- }
- /** Find or create a symbol in a specificed namespace.
- * @param uri a namespace uri.
- * @param name The "local name" or "print name" of the desired symbol.
- * @param prefix namespace prefix, or {@code ""}
- */
- public static Symbol make (String uri, String name, String prefix)
- {
- return Namespace.valueOf(uri, prefix).getSymbol(name.intern());
- }
- /** Find or create a symbol in a specificed namespace.
- * @param namespace can be an Namespace, or a namespace/environment name
- * (resolved using Namespace.getInstance), or null (in which case
- * an uninterned symbol is created).
- * @param name The "local name" or "print name" of the desired symbol.
- */
- public static Symbol make (Object namespace, String name)
- {
- Namespace ns = namespace instanceof String
- ? Namespace.valueOf((String) namespace)
- : (Namespace) namespace;
- if (ns == null || name == null)
- return makeUninterned(name);
- return ns.getSymbol(name.intern());
- }
- public static SimpleSymbol valueOf (String name)
- {
- return (SimpleSymbol) Namespace.EmptyNamespace.getSymbol(name.intern());
- }
- public static Symbol valueOf (String name, Object spec)
- {
- if (spec == null || spec == Boolean.FALSE)
- return makeUninterned(name);
- Namespace ns;
- if (spec instanceof Namespace)
- ns = (Namespace) spec;
- else if (spec == Boolean.TRUE)
- ns = Namespace.EmptyNamespace;
- else
- ns = Namespace.valueOf(((CharSequence) spec).toString());
- return ns.getSymbol(name.intern());
- }
- /* Redundant, and cause method invocation to pick the wrong method.
- public static Symbol valueOf (String name, Namespace namespace)
- {
- return namespace.getSymbol(name.intern());
- }
- public static Symbol valueOf (String name, String namespace)
- {
- return Namespace.valueOf(namespace).getSymbol(name.intern());
- }
- */
- public static Symbol valueOf (String name, String namespace, String prefix)
- {
- return Namespace.valueOf(namespace, prefix).getSymbol(name.intern());
- }
- /** Parse a String as a Symbol.
- * Recognizes:
- * <ul>
- * <li>{@code "{namespace-uri}:local-name"} - which creates a
- * symbol with that namespace-uri and an empty prefix;
- * <li>{@code "{namespace-uri}local-name"} - which is the same as above
- * <li>{@code "prefix{namespace-uri}:local-name"} - which creates a
- * symbok with that prefix and namespace-uri
- * </li>
- * <li>{@code "prefix:local-name"}- which creates a symbol with that prefix
- * and an "unknown" namespace-uri, using {@link #makeWithUnknownNamespace};
- * </li>
- * <li>and plain {@code "local-name"} - which creates a symbol in
- * {@link Namespace#EmptyNamespace}.
- * </li></ul>
- */
- public static Symbol parse (String symbol)
- {
- int slen = symbol.length();
- int lbr = -1, rbr = -1;
- int braceCount = 0;
- int mainStart = 0;
- int prefixEnd = 0;
- for (int i = 0; i < slen; i++)
- {
- char ch = symbol.charAt(i);
- if (ch == ':' && braceCount == 0)
- {
- prefixEnd = i;
- mainStart = i+1;
- break;
- }
- if (ch == '{')
- {
- if (lbr < 0)
- {
- prefixEnd = i;
- lbr = i;
- }
- braceCount++;
- }
- if (ch == '}')
- {
- braceCount--;
- if (braceCount == 0)
- {
- rbr = i;
- mainStart = (i < slen && symbol.charAt(i+1) == ':') ? i+2 : i+1;
- break;
- }
- if (braceCount < 0) // error
- {
- mainStart = prefixEnd;
- break;
- }
- }
- }
- if (lbr >= 0 && rbr > 0)
- {
- String uri = symbol.substring(lbr+1, rbr);
- String prefix = prefixEnd > 0 ? symbol.substring(0, prefixEnd) : null;
- return Symbol.valueOf(symbol.substring(mainStart), uri, prefix);
- }
- else if (prefixEnd > 0)
- {
- return Symbol.makeWithUnknownNamespace(symbol.substring(mainStart),
- symbol.substring(0, prefixEnd));
- }
- else
- {
- return Symbol.valueOf(symbol);
- }
- }
- /** Make a placeholder symbol with a known prefix and unknown namespace-uri.
- * This is convenient for processing definition commands like
- * {@code "prefix:name=value"} - such as on the Kawa command-line -
- * where we don't yet know the namespace-uri. Code that later looks
- * for a value should look both under the true namespace-uri and
- * the prefix combined with {@link Namespace#makeUnknownNamespace(String)}.
- */
- public static Symbol makeWithUnknownNamespace (String local, String prefix)
- {
- return Namespace.makeUnknownNamespace(prefix).getSymbol(local.intern());
- }
- public Symbol ()
- {
- /* #ifdef JAXP-QName */
- // super("");
- /* #endif */
- }
- public static Symbol makeUninterned (String name)
- {
- /* #ifdef JAXP-QName */
- // Namespace ns = Namespace.getInstance("kawa.gensym");
- // String sname = name;
- // int i = 0;
- // for (;;)
- // {
- // int hash = sname.hashCode();
- // synchronized (ns)
- // {
- // Symbol sym = ns.lookup(sname, hash, false);
- // if (sym == null)
- // return ns.add(new Symbol(sname.intern(), ns), hash);
- // }
- // sname = name + '.' + ++i;
- // }
- /* #else */
- return new Symbol(name, null);
- /* #endif */
- }
- public static Symbol makeUninterned (String name, Namespace namespace)
- {
- return new Symbol(name, namespace);
- }
- /** Create new Symbol in a given namespace.
- * Does not enter the result in the namespace's symbol table.
- * @param name an interned String
- */
- protected Symbol (String name, Namespace ns)
- {
- /* #ifdef JAXP-QName */
- // super(ns == null ? "" : ns.getName(), name, ns == null ? "" : ns.prefix);
- /* #else */
- this.name = name;
- /* #endif */
- this.namespace = ns;
- }
- public int compareTo(Object o)
- {
- Symbol other = (Symbol) o;
- if (getNamespaceURI() != other.getNamespaceURI())
- throw new IllegalArgumentException("comparing Symbols in different namespaces");
- return getLocalName().compareTo(other.getLocalName());
- }
- public static boolean equals (Symbol sym1, Symbol sym2)
- {
- if (sym1 == sym2)
- return true;
- if (sym1 == null || sym2 == null)
- return false;
- /* #ifdef JAXP-QName */
- // if (sym1.getLocalPart() == sym2.getLocalPart())
- /* #else */
- if (sym1.name == sym2.name)
- /* #endif */
- {
- Namespace namespace1 = sym1.namespace;
- Namespace namespace2 = sym2.namespace;
- // If a namespace is null, it means an uninterned symbol,
- // which is only equals to the same Symbol instance.
- if (namespace1 != null && namespace2 != null)
- return namespace1.name == namespace2.name;
- }
- return false;
- }
- /* #ifndef JAXP-QName */
- /** Just tests for identity.
- * Otherwise hashTables that have Symbols as keys will break. */
- public final boolean equals (Object o)
- {
- return o instanceof Symbol && equals(this, (Symbol) o);
- }
- public int hashCode ()
- {
- return name == null ? 0 : name.hashCode();
- }
- /* #endif */
- public final Namespace getNamespace()
- {
- return namespace;
- }
- public final void setNamespace (Namespace ns)
- {
- namespace = ns;
- }
- /** Conventional value used as a property key for function bindings. */
- public static final Symbol FUNCTION = makeUninterned("(function)");
- /** Conventional value used as a <code>Symbol</code> name to
- * access an <code>Object</code>'s property list.
- * A <dfn>property list</dfn> is a list with a even number of
- * <code>Pair</code>s, containing alternating keys and values.
- * They are used in Common Lisp and Emacs Lisp.
- * Kawa (following XEmacs) allows arbitrary objects to have property lists,
- * thus the PLIST as used as the name and the object as the property.
- * (In the future we'll do somethingg clever so that get(SYMBOL, KEY)
- * as the same as getf(get(PLIST, SYMBOL), KEY) - but much faster.)
- */
- public static final Symbol PLIST = makeUninterned("(property-list)");
- public String toString()
- {
- return toString('P');
- }
- /** Convert a Symbol to a printable String.
- * @param style if 'P' then print prefix if available (otherwise Uri),
- if 'U' then print Uri if available (otherwise prefix),
- if '+' then print both if available.
- */
- public String toString(char style)
- {
- // String h = "@"+Integer.toHexString(System.identityHashCode(this));
- String uri = getNamespaceURI();
- String prefix = getPrefix();
- boolean hasUri = uri != null && uri.length() > 0;
- boolean hasPrefix = prefix != null && prefix.length() > 0;
- String name = getName();
- if (hasUri || hasPrefix)
- {
- StringBuilder sbuf = new StringBuilder();
- if (hasPrefix && (style != 'U' || ! hasUri))
- sbuf.append(prefix);
- if (hasUri && (style != 'P' || ! hasPrefix))
- {
- sbuf.append('{');
- sbuf.append(getNamespaceURI());
- sbuf.append('}');
- }
- sbuf.append(':');
- sbuf.append(name);
- return sbuf.toString();
- }
- else
- return name;
- }
- public void writeExternal(ObjectOutput out) throws IOException
- {
- Namespace ns = getNamespace();
- out.writeObject(ns);
- out.writeObject(getName());
- }
- public void readExternal(ObjectInput in)
- throws IOException, ClassNotFoundException
- {
- /* #ifdef JAXP-QName */
- // throw new Error("Symbol.readExternal not implemented"); // FIXME!
- /* #else */
- namespace = (Namespace) in.readObject();
- name = (String) in.readObject();
- /* #endif */
- }
- public Object readResolve() throws ObjectStreamException
- {
- if (namespace == null)
- return this;
- return make(namespace, getName());
- }
- }
|