001: package net.sf.saxon.event;
002:
003: import net.sf.saxon.om.NamePool;
004: import net.sf.saxon.om.NamespaceConstant;
005: import net.sf.saxon.om.NamespaceResolver;
006: import net.sf.saxon.trans.XPathException;
007:
008: import java.util.ArrayList;
009: import java.util.Iterator;
010: import java.util.List;
011:
012: /**
013: * NamespaceReducer is a ProxyReceiver responsible for removing duplicate namespace
014: * declarations. It also ensures that an xmlns="" undeclaration is output when
015: * necessary. Used on its own, the NamespaceReducer simply eliminates unwanted
016: * namespace declarations. It can also be subclassed, in which case the subclass
017: * can use the services of the NamespaceReducer to resolve QNames.
018: * <p>
019: * The NamespaceReducer also validates namespace-sensitive content.
020: */
021:
022: public class NamespaceReducer extends ProxyReceiver implements
023: NamespaceResolver {
024: // TODO: this class could extend StartTagBuffer and re-use some of the code.
025:
026: // private int nscodeXML ;
027: // private int nscodeNull;
028:
029: // We keep track of namespaces to avoid outputting duplicate declarations. The namespaces
030: // vector holds a list of all namespaces currently declared (organised as pairs of entries,
031: // prefix followed by URI). The stack contains an entry for each element currently open; the
032: // value on the stack is an Integer giving the number of namespaces added to the main
033: // namespace stack by that element.
034:
035: private int[] namespaces = new int[50]; // all namespace codes currently declared
036: private int namespacesSize = 0; // all namespaces currently declared
037: private int[] countStack = new int[50];
038: private int depth = 0;
039:
040: // Creating an element does not automatically inherit the namespaces of the containing element.
041: // When the DISINHERIT property is set on startElement(), this indicates that the namespaces
042: // on that element are not to be automatically inherited by its children. So startElement()
043: // stacks a boolean flag indicating whether the children are to disinherit the parent's namespaces.
044:
045: private boolean[] disinheritStack = new boolean[50];
046:
047: private int[] pendingUndeclarations = null;
048:
049: /**
050: * startElement. This call removes redundant namespace declarations, and
051: * possibly adds an xmlns="" undeclaration.
052: */
053:
054: public void startElement(int nameCode, int typeCode,
055: int locationId, int properties) throws XPathException {
056:
057: super .startElement(nameCode, typeCode, locationId, properties);
058:
059: // If the parent element specified inherit=no, keep a list of namespaces that need to be
060: // undeclared
061:
062: if (depth > 0 && disinheritStack[depth - 1]) {
063: pendingUndeclarations = new int[namespacesSize];
064: System.arraycopy(namespaces, 0, pendingUndeclarations, 0,
065: namespacesSize);
066: } else {
067: pendingUndeclarations = null;
068: }
069:
070: // Record the current height of the namespace list so it can be reset at endElement time
071:
072: countStack[depth] = 0;
073: disinheritStack[depth] = (properties & ReceiverOptions.DISINHERIT_NAMESPACES) != 0;
074: if (++depth >= countStack.length) {
075: int[] newstack = new int[depth * 2];
076: System.arraycopy(countStack, 0, newstack, 0, depth);
077: boolean[] disStack2 = new boolean[depth * 2];
078: System.arraycopy(disinheritStack, 0, disStack2, 0, depth);
079: countStack = newstack;
080: disinheritStack = disStack2;
081: }
082:
083: // Ensure that the element namespace is output, unless this is done
084: // automatically by the caller (which is true, for example, for a literal
085: // result element).
086:
087: if ((properties & ReceiverOptions.NAMESPACE_OK) == 0) {
088: namespace(getNamePool().allocateNamespaceCode(nameCode), 0);
089: }
090:
091: }
092:
093: public void namespace(int namespaceCode, int properties)
094: throws XPathException {
095:
096: // Keep the namespace only if it is actually needed
097:
098: if (isNeeded(namespaceCode)) {
099: addToStack(namespaceCode);
100: countStack[depth - 1]++;
101: super .namespace(namespaceCode, properties);
102: }
103: }
104:
105: /**
106: * Handle an attribute
107: */
108:
109: // public void attribute(int nameCode, int typeCode, CharSequence value, int locationId, int properties)
110: // throws XPathException {
111: // if (typeCode != -1 && (properties & ReceiverOptions.NEEDS_PREFIX_CHECK) != 0) {
112: // // checking has been deferred until the namespace context is available
113: // SimpleType type = (SimpleType)getConfiguration().getSchemaType(typeCode);
114: // XPathException err = type.validateContent(value, this, getConfiguration());
115: // if (err != null) {
116: // throw err;
117: // }
118: // properties &= (~ReceiverOptions.NEEDS_PREFIX_CHECK);
119: // }
120: // super.attribute(nameCode, typeCode, value, locationId, properties);
121: // }
122: /**
123: * Determine whether a namespace declaration is needed
124: */
125:
126: private boolean isNeeded(int nscode) {
127: if (nscode == NamespaceConstant.XML_NAMESPACE_CODE) {
128: // Ignore the XML namespace
129: return false;
130: }
131:
132: // First cancel any pending undeclaration of this namespace prefix (there may be more than one)
133:
134: if (pendingUndeclarations != null) {
135: for (int p = 0; p < pendingUndeclarations.length; p++) {
136: if ((nscode >> 16) == (pendingUndeclarations[p] >> 16)) {
137: pendingUndeclarations[p] = -1;
138: //break;
139: }
140: }
141: }
142:
143: for (int i = namespacesSize - 1; i >= 0; i--) {
144: if (namespaces[i] == nscode) {
145: // it's a duplicate so we don't need it
146: return false;
147: }
148: if ((namespaces[i] >> 16) == (nscode >> 16)) {
149: // same prefix, different URI, so we do need it
150: return true;
151: }
152: }
153:
154: // we need it unless it's a redundant xmlns=""
155: return (nscode != NamespaceConstant.NULL_NAMESPACE_CODE);
156: }
157:
158: /**
159: * Add a namespace declaration to the stack
160: */
161:
162: private void addToStack(int nscode) {
163: // expand the stack if necessary
164: if (namespacesSize + 1 >= namespaces.length) {
165: int[] newlist = new int[namespacesSize * 2];
166: System.arraycopy(namespaces, 0, newlist, 0, namespacesSize);
167: namespaces = newlist;
168: }
169: namespaces[namespacesSize++] = nscode;
170: }
171:
172: /**
173: * startContent: Add any namespace undeclarations needed to stop
174: * namespaces being inherited from parent elements
175: */
176:
177: public void startContent() throws XPathException {
178:
179: if (pendingUndeclarations != null) {
180: for (int i = 0; i < pendingUndeclarations.length; i++) {
181: int nscode = pendingUndeclarations[i];
182: if (nscode != -1) {
183: namespace(nscode & 0xffff0000, 0);
184: // relies on the namespace() method to prevent duplicate undeclarations
185: }
186: }
187: }
188: pendingUndeclarations = null;
189: super .startContent();
190: }
191:
192: /**
193: * endElement: Discard the namespaces declared on this element.
194: */
195:
196: public void endElement() throws XPathException {
197: if (depth-- == 0) {
198: throw new IllegalStateException(
199: "Attempt to output end tag with no matching start tag");
200: }
201:
202: int nscount = countStack[depth];
203: namespacesSize -= nscount;
204:
205: super .endElement();
206:
207: }
208:
209: /**
210: * Get the URI code corresponding to a given prefix code, by searching the
211: * in-scope namespaces. This is a service provided to subclasses.
212: * @param prefixCode the 16-bit prefix code required
213: * @return the 16-bit URI code, or -1 if the prefix is not found
214: */
215:
216: protected short getURICode(short prefixCode) {
217: for (int i = namespacesSize - 1; i >= 0; i--) {
218: if ((namespaces[i] >> 16) == (prefixCode)) {
219: return (short) (namespaces[i] & 0xffff);
220: }
221: }
222: if (prefixCode == 0) {
223: return 0; // by default, no prefix means no namespace URI
224: } else {
225: return -1;
226: }
227: }
228:
229: /**
230: * Get the namespace URI corresponding to a given prefix. Return null
231: * if the prefix is not in scope.
232: *
233: * @param prefix the namespace prefix
234: * @param useDefault true if the default namespace is to be used when the
235: * prefix is ""
236: * @return the uri for the namespace, or null if the prefix is not in scope
237: */
238:
239: public String getURIForPrefix(String prefix, boolean useDefault) {
240: NamePool pool = getNamePool();
241: if ("".equals(prefix) && !useDefault) {
242: return "";
243: } else if ("xml".equals(prefix)) {
244: return NamespaceConstant.XML;
245: } else {
246: short prefixCode = pool.getCodeForPrefix(prefix);
247: short uriCode = getURICode(prefixCode);
248: if (uriCode == -1) {
249: return null;
250: }
251: return pool.getURIFromURICode(uriCode);
252: }
253: }
254:
255: /**
256: * Get an iterator over all the prefixes declared in this namespace context. This will include
257: * the default namespace (prefix="") and the XML namespace where appropriate
258: */
259:
260: public Iterator iteratePrefixes() {
261: NamePool pool = getNamePool();
262: List prefixes = new ArrayList(namespacesSize);
263: for (int i = namespacesSize - 1; i >= 0; i--) {
264: String prefix = pool
265: .getPrefixFromNamespaceCode(namespaces[i]);
266: if (!prefixes.contains(prefix)) {
267: prefixes.add(prefix);
268: }
269: }
270: prefixes.add("xml");
271: return prefixes.iterator();
272: }
273: }
274:
275: //
276: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
277: // you may not use this file except in compliance with the License. You may obtain a copy of the
278: // License at http://www.mozilla.org/MPL/
279: //
280: // Software distributed under the License is distributed on an "AS IS" basis,
281: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
282: // See the License for the specific language governing rights and limitations under the License.
283: //
284: // The Original Code is: all this file.
285: //
286: // The Initial Developer of the Original Code is Michael H. Kay.
287: //
288: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
289: //
290: // Contributor(s): none.
291: //
|