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.opti;
019:
020: import java.util.ArrayList;
021: import java.util.Enumeration;
022:
023: import org.apache.xerces.util.XMLSymbols;
024: import org.apache.xerces.xni.NamespaceContext;
025: import org.apache.xerces.xni.QName;
026: import org.apache.xerces.xni.XMLAttributes;
027: import org.apache.xerces.xni.XMLString;
028: import org.w3c.dom.Attr;
029: import org.w3c.dom.Element;
030: import org.w3c.dom.NamedNodeMap;
031: import org.w3c.dom.Node;
032:
033: /**
034: * @xerces.internal
035: *
036: * @author Rahul Srivastava, Sun Microsystems Inc.
037: * @author Sandy Gao, IBM
038: *
039: * @version $Id: SchemaDOM.java 446728 2006-09-15 20:43:46Z mrglavas $
040: */
041: public class SchemaDOM extends DefaultDocument {
042:
043: static final int relationsRowResizeFactor = 15;
044: static final int relationsColResizeFactor = 10;
045:
046: NodeImpl[][] relations;
047: // parent must be an element in this scheme
048: ElementImpl parent;
049: int currLoc;
050: int nextFreeLoc;
051: boolean hidden;
052: boolean inCDATA;
053:
054: // for annotation support:
055: StringBuffer fAnnotationBuffer = null;
056:
057: public SchemaDOM() {
058: reset();
059: }
060:
061: public ElementImpl startElement(QName element,
062: XMLAttributes attributes, int line, int column, int offset) {
063: ElementImpl node = new ElementImpl(line, column, offset);
064: processElement(element, attributes, node);
065: // now the current node added, becomes the parent
066: parent = node;
067: return node;
068: }
069:
070: public ElementImpl emptyElement(QName element,
071: XMLAttributes attributes, int line, int column, int offset) {
072: ElementImpl node = new ElementImpl(line, column, offset);
073: processElement(element, attributes, node);
074: return node;
075: }
076:
077: public ElementImpl startElement(QName element,
078: XMLAttributes attributes, int line, int column) {
079: return startElement(element, attributes, line, column, -1);
080: }
081:
082: public ElementImpl emptyElement(QName element,
083: XMLAttributes attributes, int line, int column) {
084: return emptyElement(element, attributes, line, column, -1);
085: }
086:
087: private void processElement(QName element,
088: XMLAttributes attributes, ElementImpl node) {
089:
090: // populate node
091: node.prefix = element.prefix;
092: node.localpart = element.localpart;
093: node.rawname = element.rawname;
094: node.uri = element.uri;
095: node.schemaDOM = this ;
096:
097: // set the attributes
098: Attr[] attrs = new Attr[attributes.getLength()];
099: for (int i = 0; i < attributes.getLength(); i++) {
100: attrs[i] = new AttrImpl(null, attributes.getPrefix(i),
101: attributes.getLocalName(i), attributes.getQName(i),
102: attributes.getURI(i), attributes.getValue(i));
103: }
104: node.attrs = attrs;
105:
106: // check if array needs to be resized
107: if (nextFreeLoc == relations.length) {
108: resizeRelations();
109: }
110:
111: // store the current parent
112: //if (relations[currLoc][0] == null || relations[currLoc][0] != parent) {
113: if (relations[currLoc][0] != parent) {
114: relations[nextFreeLoc][0] = parent;
115: currLoc = nextFreeLoc++;
116: }
117:
118: // add the current node as child of parent
119: boolean foundPlace = false;
120: int i = 1;
121: for (i = 1; i < relations[currLoc].length; i++) {
122: if (relations[currLoc][i] == null) {
123: foundPlace = true;
124: break;
125: }
126: }
127:
128: if (!foundPlace) {
129: resizeRelations(currLoc);
130: }
131: relations[currLoc][i] = node;
132:
133: parent.parentRow = currLoc;
134: node.row = currLoc;
135: node.col = i;
136: }
137:
138: public void endElement() {
139: // the parent of current parent node becomes the parent
140: // for the next node.
141: currLoc = parent.row;
142: parent = (ElementImpl) relations[currLoc][0];
143: }
144:
145: // note that this will only be called within appinfo/documentation
146: void comment(XMLString text) {
147: fAnnotationBuffer.append("<!--");
148: if (text.length > 0) {
149: fAnnotationBuffer.append(text.ch, text.offset, text.length);
150: }
151: fAnnotationBuffer.append("-->");
152: }
153:
154: // note that this will only be called within appinfo/documentation
155: void processingInstruction(String target, XMLString data) {
156: fAnnotationBuffer.append("<?").append(target);
157: if (data.length > 0) {
158: fAnnotationBuffer.append(' ').append(data.ch, data.offset,
159: data.length);
160: }
161: fAnnotationBuffer.append("?>");
162: }
163:
164: // note that this will only be called within appinfo/documentation
165: void characters(XMLString text) {
166:
167: // escape characters if necessary
168: if (!inCDATA) {
169: for (int i = text.offset; i < text.offset + text.length; ++i) {
170: char ch = text.ch[i];
171: if (ch == '&') {
172: fAnnotationBuffer.append("&");
173: } else if (ch == '<') {
174: fAnnotationBuffer.append("<");
175: }
176: // character sequence "]]>" cannot appear in content,
177: // therefore we should escape '>'.
178: else if (ch == '>') {
179: fAnnotationBuffer.append(">");
180: }
181: // If CR is part of the document's content, it
182: // must not be printed as a literal otherwise
183: // it would be normalized to LF when the document
184: // is reparsed.
185: else if (ch == '\r') {
186: fAnnotationBuffer.append("
");
187: } else {
188: fAnnotationBuffer.append(ch);
189: }
190: }
191: } else {
192: fAnnotationBuffer.append(text.ch, text.offset, text.length);
193: }
194: }
195:
196: void endAnnotation(QName elemName, ElementImpl annotation) {
197: fAnnotationBuffer.append("\n</").append(elemName.rawname)
198: .append(">");
199: annotation.fAnnotation = fAnnotationBuffer.toString();
200: // apparently, there is no sensible way of resetting these things
201: fAnnotationBuffer = null;
202: }
203:
204: void endAnnotationElement(QName elemName) {
205: fAnnotationBuffer.append("</").append(elemName.rawname).append(
206: ">");
207: }
208:
209: void endSyntheticAnnotationElement(QName elemName, boolean complete) {
210: if (complete) {
211: fAnnotationBuffer.append("\n</").append(elemName.rawname)
212: .append(">");
213: // note that this is always called after endElement on <annotation>'s
214: // child and before endElement on annotation.
215: // hence, we must make this the child of the current
216: // parent's only child.
217: parent.fSyntheticAnnotation = fAnnotationBuffer.toString();
218:
219: // apparently, there is no sensible way of resetting
220: // these things
221: fAnnotationBuffer = null;
222: } else
223: //capturing character calls
224: fAnnotationBuffer.append("</").append(elemName.rawname)
225: .append(">");
226: }
227:
228: void startAnnotationCDATA() {
229: inCDATA = true;
230: fAnnotationBuffer.append("<![CDATA[");
231: }
232:
233: void endAnnotationCDATA() {
234: fAnnotationBuffer.append("]]>");
235: inCDATA = false;
236: }
237:
238: private void resizeRelations() {
239: NodeImpl[][] temp = new NodeImpl[relations.length
240: + relationsRowResizeFactor][];
241: System.arraycopy(relations, 0, temp, 0, relations.length);
242: for (int i = relations.length; i < temp.length; i++) {
243: temp[i] = new NodeImpl[relationsColResizeFactor];
244: }
245: relations = temp;
246: }
247:
248: private void resizeRelations(int i) {
249: NodeImpl[] temp = new NodeImpl[relations[i].length
250: + relationsColResizeFactor];
251: System.arraycopy(relations[i], 0, temp, 0, relations[i].length);
252: relations[i] = temp;
253: }
254:
255: public void reset() {
256:
257: // help out the garbage collector
258: if (relations != null)
259: for (int i = 0; i < relations.length; i++)
260: for (int j = 0; j < relations[i].length; j++)
261: relations[i][j] = null;
262: relations = new NodeImpl[relationsRowResizeFactor][];
263: parent = new ElementImpl(0, 0, 0);
264: parent.rawname = "DOCUMENT_NODE";
265: currLoc = 0;
266: nextFreeLoc = 1;
267: inCDATA = false;
268: for (int i = 0; i < relationsRowResizeFactor; i++) {
269: relations[i] = new NodeImpl[relationsColResizeFactor];
270: }
271: relations[currLoc][0] = parent;
272: }
273:
274: public void printDOM() {
275: /*
276: for (int i=0; i<relations.length; i++) {
277: if (relations[i][0] != null) {
278: for (int j=0; j<relations[i].length; j++) {
279: if (relations[i][j] != null) {
280: System.out.print(relations[i][j].nodeType+"-"+relations[i][j].parentRow+" ");
281: }
282: }
283: System.out.println("");
284: }
285: }
286: */
287: //traverse(getDocumentElement(), 0);
288: }
289:
290: // debug methods
291:
292: public static void traverse(Node node, int depth) {
293: indent(depth);
294: System.out.print("<" + node.getNodeName());
295:
296: if (node.hasAttributes()) {
297: NamedNodeMap attrs = node.getAttributes();
298: for (int i = 0; i < attrs.getLength(); i++) {
299: System.out.print(" "
300: + ((Attr) attrs.item(i)).getName() + "=\""
301: + ((Attr) attrs.item(i)).getValue() + "\"");
302: }
303: }
304:
305: if (node.hasChildNodes()) {
306: System.out.println(">");
307: depth += 4;
308: for (Node child = node.getFirstChild(); child != null; child = child
309: .getNextSibling()) {
310: traverse(child, depth);
311: }
312: depth -= 4;
313: indent(depth);
314: System.out.println("</" + node.getNodeName() + ">");
315: } else {
316: System.out.println("/>");
317: }
318: }
319:
320: public static void indent(int amount) {
321: for (int i = 0; i < amount; i++) {
322: System.out.print(' ');
323: }
324: }
325:
326: // org.w3c.dom methods
327: public Element getDocumentElement() {
328: // this returns a parent node, known to be an ElementImpl
329: return (ElementImpl) relations[0][1];
330: }
331:
332: // commence the serialization of an annotation
333: void startAnnotation(QName elemName, XMLAttributes attributes,
334: NamespaceContext namespaceContext) {
335: if (fAnnotationBuffer == null)
336: fAnnotationBuffer = new StringBuffer(256);
337: fAnnotationBuffer.append("<").append(elemName.rawname).append(
338: " ");
339:
340: // attributes are a bit of a pain. To get this right, we have to keep track
341: // of the namespaces we've seen declared, then examine the namespace context
342: // for other namespaces so that we can also include them.
343: // optimized for simplicity and the case that not many
344: // namespaces are declared on this annotation...
345: ArrayList namespaces = new ArrayList();
346: for (int i = 0; i < attributes.getLength(); ++i) {
347: String aValue = attributes.getValue(i);
348: String aPrefix = attributes.getPrefix(i);
349: String aQName = attributes.getQName(i);
350: // if it's xmlns:* or xmlns, must be a namespace decl
351: if (aPrefix == XMLSymbols.PREFIX_XMLNS
352: || aQName == XMLSymbols.PREFIX_XMLNS) {
353: namespaces
354: .add(aPrefix == XMLSymbols.PREFIX_XMLNS ? attributes
355: .getLocalName(i)
356: : XMLSymbols.EMPTY_STRING);
357: }
358: fAnnotationBuffer.append(aQName).append("=\"").append(
359: processAttValue(aValue)).append("\" ");
360: }
361: // now we have to look through currently in-scope namespaces to see what
362: // wasn't declared here
363: Enumeration currPrefixes = namespaceContext.getAllPrefixes();
364: while (currPrefixes.hasMoreElements()) {
365: String prefix = (String) currPrefixes.nextElement();
366: String uri = namespaceContext.getURI(prefix);
367: if (uri == null) {
368: uri = XMLSymbols.EMPTY_STRING;
369: }
370: if (!namespaces.contains(prefix)) {
371: // have to declare this one
372: if (prefix == XMLSymbols.EMPTY_STRING) {
373: fAnnotationBuffer.append("xmlns").append("=\"")
374: .append(processAttValue(uri)).append("\" ");
375: } else {
376: fAnnotationBuffer.append("xmlns:").append(prefix)
377: .append("=\"").append(processAttValue(uri))
378: .append("\" ");
379: }
380: }
381: }
382: fAnnotationBuffer.append(">\n");
383: }
384:
385: void startAnnotationElement(QName elemName, XMLAttributes attributes) {
386: fAnnotationBuffer.append("<").append(elemName.rawname);
387: for (int i = 0; i < attributes.getLength(); i++) {
388: String aValue = attributes.getValue(i);
389: fAnnotationBuffer.append(" ")
390: .append(attributes.getQName(i)).append("=\"")
391: .append(processAttValue(aValue)).append("\"");
392: }
393: fAnnotationBuffer.append(">");
394: }
395:
396: private static String processAttValue(String original) {
397: final int length = original.length();
398: // normally, nothing will happen
399: for (int i = 0; i < length; ++i) {
400: char currChar = original.charAt(i);
401: if (currChar == '"' || currChar == '<' || currChar == '&'
402: || currChar == 0x09 || currChar == 0x0A
403: || currChar == 0x0D) {
404: return escapeAttValue(original, i);
405: }
406: }
407: return original;
408: }
409:
410: private static String escapeAttValue(String original, int from) {
411: int i;
412: final int length = original.length();
413: StringBuffer newVal = new StringBuffer(length);
414: newVal.append(original.substring(0, from));
415: for (i = from; i < length; ++i) {
416: char currChar = original.charAt(i);
417: if (currChar == '"') {
418: newVal.append(""");
419: } else if (currChar == '<') {
420: newVal.append("<");
421: } else if (currChar == '&') {
422: newVal.append("&");
423: }
424: // Must escape 0x09, 0x0A and 0x0D if they appear in attribute
425: // value so that they may be round-tripped. They would otherwise
426: // be transformed to a 0x20 during attribute value normalization.
427: else if (currChar == 0x09) {
428: newVal.append("	");
429: } else if (currChar == 0x0A) {
430: newVal.append("
");
431: } else if (currChar == 0x0D) {
432: newVal.append("
");
433: } else {
434: newVal.append(currChar);
435: }
436: }
437: return newVal.toString();
438: }
439: }
|