001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/framework/xml/schema/XSDocument.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstraße 19
030: 53177 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042: ---------------------------------------------------------------------------*/
043: package org.deegree.framework.xml.schema;
044:
045: import java.net.URI;
046: import java.util.List;
047:
048: import org.deegree.datatypes.QualifiedName;
049: import org.deegree.framework.log.ILogger;
050: import org.deegree.framework.log.LoggerFactory;
051: import org.deegree.framework.xml.XMLFragment;
052: import org.deegree.framework.xml.XMLParsingException;
053: import org.deegree.framework.xml.XMLTools;
054: import org.w3c.dom.Element;
055: import org.w3c.dom.Node;
056:
057: /**
058: * Parser for XML schema documents.
059: *
060: * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
061: * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh </a>
062: * @author last edited by: $Author: apoth $
063: *
064: * @version $Revision: 9339 $, $Date: 2007-12-27 04:31:52 -0800 (Thu, 27 Dec 2007) $
065: */
066: public class XSDocument extends XMLFragment {
067:
068: private static final long serialVersionUID = 4371672452129797159L;
069:
070: private URI targetNamespace;
071:
072: private final ILogger LOG = LoggerFactory
073: .getLogger(XSDocument.class);
074:
075: /**
076: * Returns the class representation of the underlying schema document.
077: *
078: * @return class representation of the underlying schema document
079: * @throws XMLParsingException
080: * @throws XMLSchemaException
081: */
082: public XMLSchema parseXMLSchema() throws XMLParsingException,
083: XMLSchemaException {
084: SimpleTypeDeclaration[] simpleTypes = extractSimpleTypeDeclarations();
085: ComplexTypeDeclaration[] complexTypes = extractComplexTypeDeclarations();
086: ElementDeclaration[] elementDeclarations = extractElementDeclarations();
087: return new XMLSchema(getTargetNamespace(), simpleTypes,
088: complexTypes, elementDeclarations);
089: }
090:
091: /**
092: * Returns the target namespace of the underlying schema document.
093: *
094: * @return target namespace of the underlying schema document
095: * @throws XMLParsingException
096: */
097: public synchronized URI getTargetNamespace()
098: throws XMLParsingException {
099: if (this .targetNamespace == null) {
100: this .targetNamespace = XMLTools.getRequiredNodeAsURI(this
101: .getRootElement(), "@targetNamespace", nsContext);
102: }
103: return this .targetNamespace;
104: }
105:
106: /**
107: * Extracts all global (top-level) simple type declarations from the underlying schema document.
108: *
109: * @return all global (top-level) simple type declarations
110: * @throws XMLParsingException
111: * if the document is not a valid XML Schema document or does not match the
112: * limitations of this class
113: */
114: public SimpleTypeDeclaration[] extractSimpleTypeDeclarations()
115: throws XMLParsingException {
116: List simpleTypeElements = XMLTools
117: .getNodes(this .getRootElement(),
118: getFullName("simpleType"), nsContext);
119: LOG.logDebug("Found " + simpleTypeElements.size()
120: + " simple type declarations.");
121: SimpleTypeDeclaration[] simpleTypeDeclarations = new SimpleTypeDeclaration[simpleTypeElements
122: .size()];
123: for (int i = 0; i < simpleTypeDeclarations.length; i++) {
124: simpleTypeDeclarations[i] = parseSimpleTypeDeclaration((Element) simpleTypeElements
125: .get(i));
126: }
127: return simpleTypeDeclarations;
128: }
129:
130: /**
131: * Extracts all global (top-level) complex type declarations from the underlying schema
132: * document.
133: *
134: * @return all global (top-level) complex type declarations
135: * @throws XMLParsingException
136: * if the document is not a valid XML Schema document or does not match the
137: * limitations of this class
138: */
139: public ComplexTypeDeclaration[] extractComplexTypeDeclarations()
140: throws XMLParsingException {
141: List complexTypeElements = XMLTools.getNodes(this
142: .getRootElement(), getFullName("complexType"),
143: nsContext);
144: LOG.logDebug("Found " + complexTypeElements.size()
145: + " complex type declarations.");
146: ComplexTypeDeclaration[] complexTypeDeclarations = new ComplexTypeDeclaration[complexTypeElements
147: .size()];
148: for (int i = 0; i < complexTypeDeclarations.length; i++) {
149: complexTypeDeclarations[i] = parseComplexTypeDeclaration((Element) complexTypeElements
150: .get(i));
151: }
152: return complexTypeDeclarations;
153: }
154:
155: /**
156: * Extracts all global (top-level) element declarations from the underlying schema document.
157: *
158: * @return all global (top-level) element declarations
159: * @throws XMLParsingException
160: * if the document is not a valid XML Schema document or does not match the
161: * limitations of this class
162: */
163: public ElementDeclaration[] extractElementDeclarations()
164: throws XMLParsingException {
165: List complexTypeElements = XMLTools.getNodes(this
166: .getRootElement(), getFullName("element"), nsContext);
167: LOG.logDebug("Found " + complexTypeElements.size()
168: + " element declarations.");
169: ElementDeclaration[] elementDeclarations = new ElementDeclaration[complexTypeElements
170: .size()];
171: for (int i = 0; i < elementDeclarations.length; i++) {
172: elementDeclarations[i] = parseElementDeclaration((Element) complexTypeElements
173: .get(i));
174: }
175: return elementDeclarations;
176: }
177:
178: /**
179: * Returns the root element of the complex type declaration for the given name.
180: *
181: * @param name
182: * the name of the complex type declaration to look up (w/o namespace)
183: * @return the root element of the complex type declaration or null, if the requested complex
184: * type is not declared
185: */
186: public Element getComplexTypeDeclaration(String name) {
187: String xPath = getFullName("complexType[name=\"]") + name
188: + "\"]";
189: Element element = null;
190: try {
191: element = (Element) XMLTools.getNode(getRootElement(),
192: xPath, nsContext);
193: } catch (XMLParsingException e) {
194: // happens if requested complex type is not declared
195: }
196: return element;
197: }
198:
199: /**
200: * Returns the root element of the element declaration for the given name.
201: *
202: * @param name
203: * the name of the element declaration to look up (w/o namespace)
204: * @return the root element of the element declaration or null, if the requested element is not
205: * declared
206: */
207: public Element getElementDeclaration(String name) {
208: String xPath = getFullName("element[name=\"]") + name + "\"]";
209: Element element = null;
210: try {
211: element = (Element) XMLTools.getNode(getRootElement(),
212: xPath, nsContext);
213: } catch (XMLParsingException e) {
214: // happens if requested element is not declared
215: }
216: return element;
217: }
218:
219: /**
220: * Parses the given <code>Element</code> as an 'xs:element' declaration.
221: *
222: * @param element
223: * 'xs:element' declaration to be parsed
224: * @return object representation of the declaration
225: * @throws XMLParsingException
226: * if the document is not a valid XML Schema document or does not match the
227: * limitations of this class
228: */
229: protected ElementDeclaration parseElementDeclaration(Element element)
230: throws XMLParsingException {
231:
232: QualifiedName name = new QualifiedName(XMLTools
233: .getRequiredNodeAsString(element, "@name", nsContext),
234: getTargetNamespace());
235:
236: if (name.getLocalName().length() == 0) {
237: String msg = "Error in schema document. Empty name (\"\") in element declaration "
238: + "found.";
239: throw new XMLSchemaException(msg);
240: }
241:
242: LOG.logDebug("Parsing element declaration '" + name + "'.");
243:
244: boolean isAbstract = XMLTools.getNodeAsBoolean(element,
245: "@abstract", nsContext, false);
246:
247: TypeReference typeReference = null;
248: Node typeNode = XMLTools
249: .getNode(
250: element,
251: "@type|xs:simpleType/xs:restriction/@base|xs:simpleType/xs:extension/@base",
252: nsContext);
253: if (typeNode != null) {
254: typeReference = new TypeReference(
255: parseQualifiedName(typeNode));
256: } else {
257: // inline type declaration
258: Element elem = (Element) XMLTools.getRequiredNode(element,
259: getFullName("complexType"), nsContext);
260: TypeDeclaration type = parseComplexTypeDeclaration(elem);
261: typeReference = new TypeReference(type);
262: }
263:
264: int minOccurs = XMLTools.getNodeAsInt(element, "@minOccurs",
265: nsContext, 1);
266: int maxOccurs = -1;
267: String maxOccursString = XMLTools.getNodeAsString(element,
268: "@maxOccurs", nsContext, "1");
269: if (!"unbounded".equals(maxOccursString)) {
270: try {
271: maxOccurs = Integer.parseInt(maxOccursString);
272: } catch (NumberFormatException e) {
273: throw new XMLParsingException(
274: "Invalid value ('"
275: + maxOccursString
276: + "') in 'maxOccurs' attribute. "
277: + "Must be a valid integer value or 'unbounded'.");
278: }
279: }
280:
281: QualifiedName substitutionGroup = null;
282: Node substitutionGroupNode = XMLTools.getNode(element,
283: "@substitutionGroup", nsContext);
284: if (substitutionGroupNode != null) {
285: substitutionGroup = parseQualifiedName(substitutionGroupNode);
286: }
287:
288: return new ElementDeclaration(name, isAbstract, typeReference,
289: minOccurs, maxOccurs, substitutionGroup);
290: }
291:
292: /**
293: * Parses the given <code>Element</code> as an 'xs:simpleType' declaration.
294: * <p>
295: * The following limitations apply:
296: * <ul>
297: * <li>the type must be defined using 'restriction' (of a basic xsd type)</li>
298: * <li>the content model (enumeration, ...) is not evaluated</li>
299: * </ul>
300: * </p>
301: *
302: * @param element
303: * 'xs:simpleType' declaration to be parsed
304: * @return object representation of the declaration
305: * @throws XMLParsingException
306: * if the document is not a valid XML Schema document or does not match the
307: * limitations of this class
308: */
309: protected SimpleTypeDeclaration parseSimpleTypeDeclaration(
310: Element element) throws XMLParsingException {
311:
312: QualifiedName name = null;
313: String localName = XMLTools.getNodeAsString(element, "@name",
314: nsContext, null);
315: if (localName != null) {
316: name = new QualifiedName(localName, getTargetNamespace());
317: if (localName.length() == 0) {
318: String msg = "Error in schema document. Empty name (\"\") in simpleType "
319: + "declaration found.";
320: throw new XMLSchemaException(msg);
321: }
322: }
323:
324: LOG.logDebug("Parsing simple type declaration '" + name + "'.");
325:
326: Node restrictionBaseNode = XMLTools.getRequiredNode(element,
327: getFullName("restriction/@base"), nsContext);
328: TypeReference restrictionBase = new TypeReference(
329: parseQualifiedName(restrictionBaseNode));
330:
331: return new SimpleTypeDeclaration(name, restrictionBase);
332: }
333:
334: /**
335: * Parses the given <code>Element</code> as an 'xs:complexType' declaration.
336: *
337: * @param element
338: * 'xs:complexType' declaration to be parsed
339: * @return object representation of the declaration
340: * @throws XMLParsingException
341: * if the document is not a valid XML Schema document or does not match the
342: * limitations of this class
343: */
344: protected ComplexTypeDeclaration parseComplexTypeDeclaration(
345: Element element) throws XMLParsingException {
346:
347: QualifiedName name = null;
348: String localName = XMLTools.getNodeAsString(element, "@name",
349: nsContext, null);
350: if (localName != null) {
351: name = new QualifiedName(localName, getTargetNamespace());
352: if (localName.length() == 0) {
353: String msg = "Error in schema document. Empty name (\"\") for complexType "
354: + "declaration found.";
355: throw new XMLSchemaException(msg);
356: }
357: }
358: LOG
359: .logDebug("Parsing complex type declaration '" + name
360: + "'.");
361:
362: List subElementList = null;
363: TypeReference extensionBase = null;
364: Node extensionBaseNode = XMLTools.getNode(element,
365: getFullName("complexContent/")
366: + getFullName("extension/@base"), nsContext);
367: if (extensionBaseNode != null) {
368: extensionBase = new TypeReference(
369: parseQualifiedName(extensionBaseNode));
370: subElementList = XMLTools.getNodes(element,
371: getFullName("complexContent/")
372: + getFullName("extension/")
373: + getFullName("sequence/")
374: + getFullName("element"), nsContext);
375: } else {
376: subElementList = XMLTools.getRequiredNodes(element,
377: getFullName("sequence/") + getFullName("element"),
378: nsContext);
379: }
380:
381: ElementDeclaration[] subElements = new ElementDeclaration[subElementList
382: .size()];
383: for (int i = 0; i < subElements.length; i++) {
384: Element subElement = (Element) subElementList.get(i);
385: subElements[i] = parseElementDeclaration(subElement);
386: }
387:
388: return new ComplexTypeDeclaration(name, extensionBase,
389: subElements);
390: }
391:
392: /**
393: * Prepends the prefix of the RootElement to the given local name.
394: * <p>
395: * If the prefix of the RootElement is empty, "xs:" is prepended.
396: *
397: * @param localName
398: * to this the prefix will be prepended
399: * @return prefix + localName
400: */
401: protected String getFullName(String localName) {
402: String ret;
403: Element root = this .getRootElement();
404: String prefix = root.getPrefix();
405:
406: if (prefix != null && prefix.length() > 0) {
407: URI uri = nsContext.getURI(prefix);
408: if (null == uri) {
409: String nsUri = root.lookupNamespaceURI(prefix);
410: try {
411: nsContext.addNamespace(prefix, new URI(nsUri)); // synchronized ???
412: } catch (Exception exc) {
413: LOG.logError("failed to add namespace: " + nsUri,
414: exc);
415: }
416: }
417: ret = prefix + ':' + localName;
418: } else {
419: // fallback
420: ret = "xs:" + localName;
421: }
422: return ret;
423: }
424: }
|