001: package net.sf.saxon.event;
002:
003: import net.sf.saxon.om.AttributeCollectionImpl;
004: import net.sf.saxon.om.NamePool;
005: import net.sf.saxon.om.NamespaceConstant;
006: import net.sf.saxon.om.NamespaceResolver;
007: import net.sf.saxon.trans.XPathException;
008:
009: import java.util.ArrayList;
010: import java.util.Iterator;
011: import java.util.List;
012:
013: /**
014: * StartTagBuffer is a ProxyReceiver that buffers attributes and namespace events within a start tag.
015: * It maintains details of the namespace context, and a full set of attribute information, on behalf
016: * of other filters that need access to namespace information or need to process attributes in arbitrary
017: * order.
018: */
019:
020: public class StartTagBuffer extends ProxyReceiver implements
021: NamespaceResolver {
022:
023: // Details of the pending element event
024:
025: int elementNameCode;
026: int elementTypeCode;
027: int elementLocationId;
028: int elementProperties;
029:
030: // Details of pending attribute events
031:
032: AttributeCollectionImpl bufferedAttributes;
033:
034: // We keep track of namespaces. The namespaces
035: // vector holds a list of all namespaces currently declared (organised as pairs of entries,
036: // prefix followed by URI). The stack contains an entry for each element currently open; the
037: // value on the stack is an Integer giving the number of namespaces added to the main
038: // namespace stack by that element.
039:
040: private int[] namespaces = new int[50]; // all namespace codes currently declared
041: private int namespacesSize = 0; // all namespaces currently declared
042: private int[] countStack = new int[50];
043: private int depth = 0;
044:
045: // TODO: support namespace undeclarations
046:
047: // TODO: the StartTagBuffer only knows about explicitly-generated namespace declarations, not
048: // namespace declarations generated by namespace fixup, which are handled further down the pipeline.
049: // This means that QName-valued attributes such as xsi:type are rejected if they depend on a namespace
050: // that comes only from namespace fixup.
051: // See test qischema064, removing the xmlns:z="uri". (Seems OK in XSLT, see schema092/3)
052:
053: public void setPipelineConfiguration(PipelineConfiguration config) {
054: super .setPipelineConfiguration(config);
055: bufferedAttributes = new AttributeCollectionImpl(getNamePool());
056: }
057:
058: /**
059: * startElement
060: */
061:
062: public void startElement(int nameCode, int typeCode,
063: int locationId, int properties) throws XPathException {
064:
065: elementNameCode = nameCode;
066: elementTypeCode = typeCode;
067: elementLocationId = locationId;
068: elementProperties = properties;
069:
070: bufferedAttributes.clear();
071:
072: // Record the current height of the namespace list so it can be reset at endElement time
073:
074: countStack[depth] = 0;
075: if (++depth >= countStack.length) {
076: int[] newstack = new int[depth * 2];
077: System.arraycopy(countStack, 0, newstack, 0, depth);
078: countStack = newstack;
079: }
080: }
081:
082: public void namespace(int namespaceCode, int properties)
083: throws XPathException {
084: addToStack(namespaceCode);
085: countStack[depth - 1]++;
086: }
087:
088: /**
089: * Notify an attribute. Attributes are notified after the startElement event, and before any
090: * children. Namespaces and attributes may be intermingled.
091: *
092: * @param nameCode The name of the attribute, as held in the name pool
093: * @param typeCode The type of the attribute, as held in the name pool
094: * @param properties Bit significant value. The following bits are defined:
095: * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
096: * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
097: * @throws IllegalStateException: attempt to output an attribute when there is no open element
098: * start tag
099: */
100:
101: public void attribute(int nameCode, int typeCode,
102: CharSequence value, int locationId, int properties)
103: throws XPathException {
104: bufferedAttributes.addAttribute(nameCode, typeCode, value
105: .toString(), locationId, properties);
106: }
107:
108: /**
109: * Add a namespace declaration (or undeclaration) to the stack
110: */
111:
112: private void addToStack(int nscode) {
113: // expand the stack if necessary
114: if (namespacesSize + 1 >= namespaces.length) {
115: int[] newlist = new int[namespacesSize * 2];
116: System.arraycopy(namespaces, 0, newlist, 0, namespacesSize);
117: namespaces = newlist;
118: }
119: namespaces[namespacesSize++] = nscode;
120: }
121:
122: /**
123: * startContent: Add any namespace undeclarations needed to stop
124: * namespaces being inherited from parent elements
125: */
126:
127: public void startContent() throws XPathException {
128: super .startElement(elementNameCode, elementTypeCode,
129: elementLocationId, elementProperties);
130: declareNamespacesForStartElement();
131:
132: final int length = bufferedAttributes.getLength();
133: for (int i = 0; i < length; i++) {
134: super .attribute(bufferedAttributes.getNameCode(i),
135: bufferedAttributes.getTypeAnnotation(i),
136: bufferedAttributes.getValue(i), bufferedAttributes
137: .getLocationId(i), bufferedAttributes
138: .getProperties(i));
139: }
140: super .startContent();
141: }
142:
143: protected void declareNamespacesForStartElement()
144: throws XPathException {
145: for (int i = namespacesSize - countStack[depth - 1]; i < namespacesSize; i++) {
146: super .namespace(namespaces[i], 0);
147: }
148: }
149:
150: protected void declareAllNamespaces() throws XPathException {
151: for (int i = 0; i < namespacesSize; i++) {
152: super .namespace(namespaces[i], 0);
153: }
154: }
155:
156: /**
157: * endElement: Discard the namespaces declared on this element.
158: */
159:
160: public void endElement() throws XPathException {
161: super .endElement();
162: undeclareNamespacesForElement();
163: }
164:
165: protected void undeclareNamespacesForElement() {
166: int nscount = countStack[--depth];
167: namespacesSize -= nscount;
168: }
169:
170: /**
171: * Get the name of the current element
172: */
173:
174: public int getElementNameCode() {
175: return elementNameCode;
176: }
177:
178: /**
179: * Determine if the current element has any attributes
180: */
181:
182: public boolean hasAttributes() {
183: return bufferedAttributes.getLength() > 0;
184: }
185:
186: /**
187: * Get the value of the current attribute with a given nameCode
188: * @return the attribute value, or null if the attribute is not present
189: */
190:
191: public String getAttribute(int nameCode) {
192: return bufferedAttributes
193: .getValueByFingerprint(nameCode & 0xfffff);
194: }
195:
196: /**
197: * Get the URI code corresponding to a given prefix code, by searching the
198: * in-scope namespaces. This is a service provided to subclasses.
199: * @param prefixCode the 16-bit prefix code required
200: * @return the 16-bit URI code, or -1 if the prefix is not found
201: */
202:
203: protected short getURICode(short prefixCode) {
204: for (int i = namespacesSize - 1; i >= 0; i--) {
205: if ((namespaces[i] >> 16) == (prefixCode)) {
206: return (short) (namespaces[i] & 0xffff);
207: }
208: }
209: if (prefixCode == 0) {
210: return 0; // by default, no prefix means no namespace URI
211: } else {
212: return -1;
213: }
214: }
215:
216: /**
217: * Get the namespace URI corresponding to a given prefix. Return null
218: * if the prefix is not in scope.
219: *
220: * @param prefix the namespace prefix
221: * @param useDefault true if the default namespace is to be used when the
222: * prefix is ""
223: * @return the uri for the namespace, or null if the prefix is not in scope
224: */
225:
226: public String getURIForPrefix(String prefix, boolean useDefault) {
227: NamePool pool = getNamePool();
228: if ("".equals(prefix) && !useDefault) {
229: return "";
230: } else if ("xml".equals(prefix)) {
231: return NamespaceConstant.XML;
232: } else {
233: short prefixCode = pool.getCodeForPrefix(prefix);
234: short uriCode = getURICode(prefixCode);
235: if (uriCode == -1) {
236: return null;
237: }
238: return pool.getURIFromURICode(uriCode);
239: }
240: }
241:
242: /**
243: * Get an iterator over all the prefixes declared in this namespace context. This will include
244: * the default namespace (prefix="") and the XML namespace where appropriate
245: */
246:
247: public Iterator iteratePrefixes() {
248: NamePool pool = getNamePool();
249: List prefixes = new ArrayList(namespacesSize);
250: for (int i = namespacesSize - 1; i >= 0; i--) {
251: String prefix = pool
252: .getPrefixFromNamespaceCode(namespaces[i]);
253: if (!prefixes.contains(prefix)) {
254: prefixes.add(prefix);
255: }
256: }
257: prefixes.add("xml");
258: return prefixes.iterator();
259: }
260: }
261:
262: //
263: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
264: // you may not use this file except in compliance with the License. You may obtain a copy of the
265: // License at http://www.mozilla.org/MPL/
266: //
267: // Software distributed under the License is distributed on an "AS IS" basis,
268: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
269: // See the License for the specific language governing rights and limitations under the License.
270: //
271: // The Original Code is: all this file.
272: //
273: // The Initial Developer of the Original Code is Michael H. Kay.
274: //
275: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
276: //
277: // Contributor(s): none.
278: //
|