001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl.xs;
019:
020: import org.apache.xerces.impl.xs.opti.ElementImpl;
021: import org.apache.xerces.util.NamespaceSupport;
022: import org.apache.xerces.util.SymbolTable;
023: import org.apache.xerces.util.XMLSymbols;
024: import org.apache.xerces.xni.NamespaceContext;
025: import org.apache.xerces.xni.QName;
026: import org.w3c.dom.Attr;
027: import org.w3c.dom.Document;
028: import org.w3c.dom.Element;
029: import org.w3c.dom.NamedNodeMap;
030: import org.w3c.dom.Node;
031:
032: /**
033: * This class customizes the behaviour of the util.NamespaceSupport
034: * class in order to easily implement some features that we need for
035: * efficient schema handling. It will not be generally useful.
036: *
037: * @xerces.internal
038: *
039: * @author Neil Graham, IBM
040: *
041: * @version $Id: SchemaNamespaceSupport.java 446734 2006-09-15 20:51:23Z mrglavas $
042: */
043: public class SchemaNamespaceSupport extends NamespaceSupport {
044:
045: private SchemaRootContext fSchemaRootContext = null;
046:
047: public SchemaNamespaceSupport(Element schemaRoot,
048: SymbolTable symbolTable) {
049: super ();
050: if (schemaRoot != null && !(schemaRoot instanceof ElementImpl)) {
051: Document ownerDocument = schemaRoot.getOwnerDocument();
052: if (ownerDocument != null
053: && schemaRoot != ownerDocument.getDocumentElement()) {
054: fSchemaRootContext = new SchemaRootContext(schemaRoot,
055: symbolTable);
056: }
057: }
058: } // constructor
059:
060: // more effecient than NamespaceSupport(NamespaceContext)
061: public SchemaNamespaceSupport(SchemaNamespaceSupport nSupport) {
062: fSchemaRootContext = nSupport.fSchemaRootContext;
063: fNamespaceSize = nSupport.fNamespaceSize;
064: if (fNamespace.length < fNamespaceSize)
065: fNamespace = new String[fNamespaceSize];
066: System.arraycopy(nSupport.fNamespace, 0, fNamespace, 0,
067: fNamespaceSize);
068: fCurrentContext = nSupport.fCurrentContext;
069: if (fContext.length <= fCurrentContext)
070: fContext = new int[fCurrentContext + 1];
071: System.arraycopy(nSupport.fContext, 0, fContext, 0,
072: fCurrentContext + 1);
073: } // end constructor
074:
075: /**
076: * This method takes a set of Strings, as stored in a
077: * NamespaceSupport object, and "fools" the object into thinking
078: * that this is one unified context. This is meant to be used in
079: * conjunction with things like local elements, whose declarations
080: * may be deeply nested but which for all practical purposes may
081: * be regarded as being one level below the global <schema>
082: * element--at least with regard to namespace declarations.
083: * It's worth noting that the context from which the strings are
084: * being imported had better be using the same SymbolTable.
085: */
086: public void setEffectiveContext(String[] namespaceDecls) {
087: if (namespaceDecls == null || namespaceDecls.length == 0)
088: return;
089: pushContext();
090: int newSize = fNamespaceSize + namespaceDecls.length;
091: if (fNamespace.length < newSize) {
092: // expand namespace's size...
093: String[] tempNSArray = new String[newSize];
094: System.arraycopy(fNamespace, 0, tempNSArray, 0,
095: fNamespace.length);
096: fNamespace = tempNSArray;
097: }
098: System.arraycopy(namespaceDecls, 0, fNamespace, fNamespaceSize,
099: namespaceDecls.length);
100: fNamespaceSize = newSize;
101: } // setEffectiveContext(String):void
102:
103: /**
104: * This method returns an array of Strings, as would be stored in
105: * a NamespaceSupport object. This array contains all
106: * declarations except those at the global level.
107: */
108: public String[] getEffectiveLocalContext() {
109: // the trick here is to recognize that all local contexts
110: // happen to start at fContext[3].
111: // context 1: empty
112: // context 2: decls for xml and xmlns;
113: // context 3: decls on <xs:schema>: the global ones
114: String[] returnVal = null;
115: if (fCurrentContext >= 3) {
116: int bottomLocalContext = fContext[3];
117: int copyCount = fNamespaceSize - bottomLocalContext;
118: if (copyCount > 0) {
119: returnVal = new String[copyCount];
120: System.arraycopy(fNamespace, bottomLocalContext,
121: returnVal, 0, copyCount);
122: }
123: }
124: return returnVal;
125: } // getEffectiveLocalContext():String
126:
127: // This method removes from this object all the namespaces
128: // returned by getEffectiveLocalContext.
129: public void makeGlobal() {
130: if (fCurrentContext >= 3) {
131: fCurrentContext = 3;
132: fNamespaceSize = fContext[3];
133: }
134: } // makeGlobal
135:
136: public String getURI(String prefix) {
137: String uri = super .getURI(prefix);
138: if (uri == null && fSchemaRootContext != null) {
139: if (!fSchemaRootContext.fDOMContextBuilt) {
140: fSchemaRootContext.fillNamespaceContext();
141: fSchemaRootContext.fDOMContextBuilt = true;
142: }
143: if (fSchemaRootContext.fNamespaceSize > 0
144: && !containsPrefix(prefix)) {
145: uri = fSchemaRootContext.getURI(prefix);
146: }
147: }
148: return uri;
149: }
150:
151: /**
152: * This class keeps track of the namespace bindings
153: * declared on ancestors of the schema root.
154: */
155: static final class SchemaRootContext {
156:
157: //
158: // Data
159: //
160:
161: /**
162: * Namespace binding information. This array is composed of a
163: * series of tuples containing the namespace binding information:
164: * <prefix, uri>.
165: */
166: String[] fNamespace = new String[16 * 2];
167:
168: /** The size of the namespace information array. */
169: int fNamespaceSize = 0;
170:
171: /**
172: * Flag indicating whether the namespace context
173: * has been from the root node's ancestors.
174: */
175: boolean fDOMContextBuilt = false;
176:
177: /** Schema root. **/
178: private final Element fSchemaRoot;
179:
180: /** Symbol table. **/
181: private final SymbolTable fSymbolTable;
182:
183: /** Temporary storage for attribute QNames. **/
184: private final QName fAttributeQName = new QName();
185:
186: SchemaRootContext(Element schemaRoot, SymbolTable symbolTable) {
187: fSchemaRoot = schemaRoot;
188: fSymbolTable = symbolTable;
189: }
190:
191: void fillNamespaceContext() {
192: if (fSchemaRoot != null) {
193: Node currentNode = fSchemaRoot.getParentNode();
194: while (currentNode != null) {
195: if (Node.ELEMENT_NODE == currentNode.getNodeType()) {
196: NamedNodeMap attributes = currentNode
197: .getAttributes();
198: final int attrCount = attributes.getLength();
199: for (int i = 0; i < attrCount; ++i) {
200: Attr attr = (Attr) attributes.item(i);
201: String value = attr.getValue();
202: if (value == null) {
203: value = XMLSymbols.EMPTY_STRING;
204: }
205: fillQName(fAttributeQName, attr);
206: // REVISIT: Should we be looking at non-namespace attributes
207: // for additional mappings? Should we detect illegal namespace
208: // declarations and exclude them from the context? -- mrglavas
209: if (fAttributeQName.uri == NamespaceContext.XMLNS_URI) {
210: // process namespace attribute
211: if (fAttributeQName.prefix == XMLSymbols.PREFIX_XMLNS) {
212: declarePrefix(
213: fAttributeQName.localpart,
214: value.length() != 0 ? fSymbolTable
215: .addSymbol(value)
216: : null);
217: } else {
218: declarePrefix(
219: XMLSymbols.EMPTY_STRING,
220: value.length() != 0 ? fSymbolTable
221: .addSymbol(value)
222: : null);
223: }
224: }
225: }
226:
227: }
228: currentNode = currentNode.getParentNode();
229: }
230: }
231: }
232:
233: String getURI(String prefix) {
234: // find prefix in the DOM context
235: for (int i = 0; i < fNamespaceSize; i += 2) {
236: if (fNamespace[i] == prefix) {
237: return fNamespace[i + 1];
238: }
239: }
240: // prefix not found
241: return null;
242: }
243:
244: private void declarePrefix(String prefix, String uri) {
245: // resize array, if needed
246: if (fNamespaceSize == fNamespace.length) {
247: String[] namespacearray = new String[fNamespaceSize * 2];
248: System.arraycopy(fNamespace, 0, namespacearray, 0,
249: fNamespaceSize);
250: fNamespace = namespacearray;
251: }
252:
253: // bind prefix to uri in current context
254: fNamespace[fNamespaceSize++] = prefix;
255: fNamespace[fNamespaceSize++] = uri;
256: }
257:
258: private void fillQName(QName toFill, Node node) {
259: final String prefix = node.getPrefix();
260: final String localName = node.getLocalName();
261: final String rawName = node.getNodeName();
262: final String namespace = node.getNamespaceURI();
263: toFill.prefix = (prefix != null) ? fSymbolTable
264: .addSymbol(prefix) : XMLSymbols.EMPTY_STRING;
265: toFill.localpart = (localName != null) ? fSymbolTable
266: .addSymbol(localName) : XMLSymbols.EMPTY_STRING;
267: toFill.rawname = (rawName != null) ? fSymbolTable
268: .addSymbol(rawName) : XMLSymbols.EMPTY_STRING;
269: toFill.uri = (namespace != null && namespace.length() > 0) ? fSymbolTable
270: .addSymbol(namespace)
271: : null;
272: }
273: }
274:
275: } // class NamespaceSupport
|