001: // Copyright (c) 2003, 2006 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.xml;
005:
006: import java.io.*;
007:
008: /** A "namespace node" as a link in a linked list.
009: *
010: * The list may contain duplicates - i.e. multiple namespace bindings
011: * for the same prefix but (usually) different uris. In that case the
012: * first binding "wins". One reason for allowing duplicates it to allow
013: * sharing of the lists between a child and its parent element.
014: */
015:
016: public final class NamespaceBinding implements Externalizable {
017: /** Namespace prefix. An interned String.
018: * A default namespace declaration is represented using null. */
019: public final String getPrefix() {
020: return prefix;
021: }
022:
023: public final void setPrefix(String prefix) {
024: this .prefix = prefix;
025: }
026:
027: String prefix;
028:
029: /** Namespace uri. An interned String.
030: * The value null "undeclares" any following namespaces; it corresponds
031: * to an empty uri as in the XML Namespaces 1.1 Candidate Recommendation. */
032: public final String getUri() {
033: return uri;
034: }
035:
036: public final void setUri(String uri) {
037: this .uri = uri;
038: }
039:
040: String uri;
041:
042: NamespaceBinding next;
043:
044: int depth;
045:
046: public final NamespaceBinding getNext() {
047: return next;
048: }
049:
050: public final void setNext(NamespaceBinding next) {
051: this .next = next;
052: this .depth = next == null ? 0 : next.depth + 1;
053: }
054:
055: /** Chain the first list in front of the second list.
056: * (The name {@code nconc} comes from Common Lisp.)
057: */
058: public final static NamespaceBinding nconc(NamespaceBinding list1,
059: NamespaceBinding list2) {
060: if (list1 == null)
061: return list2;
062: list1.setNext(nconc(list1.next, list2));
063: return list1;
064: }
065:
066: // public NamespaceBinding () { }
067:
068: public NamespaceBinding(String prefix, String uri,
069: NamespaceBinding next) {
070: this .prefix = prefix;
071: this .uri = uri;
072: setNext(next);
073: }
074:
075: public static final String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
076:
077: public static final NamespaceBinding predefinedXML = new NamespaceBinding(
078: "xml", XML_NAMESPACE, null);
079:
080: /** Resolve a prefix.
081: * @param prefix an interned namespace prefix to search for.
082: * @return a uri or null if not bound
083: */
084: public String resolve(String prefix) {
085: for (NamespaceBinding ns = this ; ns != null; ns = ns.next) {
086: if (ns.prefix == prefix)
087: return ns.uri;
088: }
089: return null;
090: }
091:
092: /** Resolve a prefix, in the initial part of this list.
093: * @param prefix an interned namespace prefix to search for.
094: * @param fencePost only search this list until then.
095: * @return a uri or null if not bound
096: */
097: public String resolve(String prefix, NamespaceBinding fencePost) {
098: for (NamespaceBinding ns = this ; ns != fencePost; ns = ns.next) {
099: if (ns.prefix == prefix)
100: return ns.uri;
101: }
102: return null;
103: }
104:
105: public static NamespaceBinding commonAncestor(NamespaceBinding ns1,
106: NamespaceBinding ns2) {
107: if (ns1.depth > ns2.depth) {
108: NamespaceBinding tmp = ns1;
109: ns1 = ns2;
110: ns2 = tmp;
111: }
112: while (ns2.depth > ns1.depth)
113: ns2 = ns2.next;
114: while (ns1 != ns2) {
115: ns1 = ns1.next;
116: ns2 = ns2.next;
117: }
118: return ns1;
119: }
120:
121: /* For debugging:
122: void check ()
123: {
124: NamespaceBinding ns = this;
125: int d = depth;
126: for (;;)
127: {
128: if (ns == null)
129: throw new Error("null ns");
130: if (ns.depth != d)
131: throw new Error("bad depth "+ns.depth+" shoudl be "+d);
132: ns = ns.next;
133: if (ns == null && d == 0)
134: return;
135: d--;
136: }
137: }
138: */
139:
140: /** Reverse the chain, until a fencePost. */
141: public NamespaceBinding reversePrefix(NamespaceBinding fencePost) {
142: NamespaceBinding prev = fencePost;
143: NamespaceBinding t = this ;
144: int depth = fencePost == null ? -1 : fencePost.depth;
145: while (t != fencePost) {
146: NamespaceBinding next = t.next;
147: t.next = prev;
148: prev = t;
149: t.depth = ++depth;
150: t = next;
151: }
152: return prev;
153: }
154:
155: /** Return the number of bindings before the <code>fencePost</code>. */
156: public int count(NamespaceBinding fencePost) {
157: int count = 0;
158: for (NamespaceBinding ns = this ; ns != fencePost; ns = ns.next)
159: count++;
160: return count;
161: }
162:
163: /** Append a new NamespaceBinding if not redundant. */
164: public static NamespaceBinding maybeAdd(String prefix, String uri,
165: NamespaceBinding bindings) {
166: if (bindings == null) {
167: if (uri == null)
168: return bindings;
169: bindings = predefinedXML;
170: }
171: String found = bindings.resolve(prefix);
172: if (found == null ? uri == null : found.equals(uri))
173: return bindings;
174: return new NamespaceBinding(prefix, uri, bindings);
175: }
176:
177: /** Return a String showing just a single namespace binding. */
178: public String toString() {
179: return "Namespace{" + prefix + "=" + uri + ", depth:" + depth
180: + "}";
181: }
182:
183: /** Return a String showing the full namespace binding list. */
184: public String toStringAll() {
185: StringBuffer sbuf = new StringBuffer("Namespaces{");
186: for (NamespaceBinding ns = this ; ns != null; ns = ns.next) {
187: sbuf.append(ns.prefix);
188: sbuf.append("=\"");
189: sbuf.append(ns.uri);
190: sbuf.append(ns == null ? "\"" : "\", ");
191: }
192: sbuf.append('}');
193: return sbuf.toString();
194: }
195:
196: public void writeExternal(ObjectOutput out) throws IOException {
197: out.writeUTF(prefix);
198: out.writeUTF(uri);
199: out.writeObject(next);
200: }
201:
202: public void readExternal(ObjectInput in) throws IOException,
203: ClassNotFoundException {
204: prefix = in.readUTF();
205: uri = in.readUTF();
206: next = (NamespaceBinding) in.readObject();
207: }
208:
209: }
|