001: /**
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 2004 (C) Intalio, Inc. All Rights Reserved.
042: *
043: * $Id: RedefineUnmarshaller.java 6230 2006-09-19 07:56:07Z wguttmn $
044: */package org.exolab.castor.xml.schema.reader;
045:
046: //-- imported classes and packages
047: import org.exolab.castor.net.*;
048: import org.exolab.castor.xml.*;
049: import org.exolab.castor.xml.schema.*;
050: import org.xml.sax.*;
051:
052: /**
053: * The purpose of this class is to read redefined elements in an XML schema.
054: * The following xml schema structure can be redefined:
055: * <ul>
056: * <li>Complextypes</li>
057: * <li>Simpletypes</li>
058: * <li>AttributeGroup</li>
059: * <li>Group</li>
060: * </ul>
061: * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
062: * @version $Revision: 6230 $ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
063: **/
064: public class RedefineUnmarshaller extends ComponentReader {
065:
066: /**
067: * The current ComponentReader used to read nested structures
068: **/
069: private ComponentReader _unmarshaller;
070:
071: /**
072: * The current branch depth
073: **/
074: private int _depth = 0;
075:
076: /**
077: * The parent XML schema
078: */
079: private Schema _schema;
080:
081: /**
082: * The imported XML Schema
083: */
084: private Schema _importedSchema;
085:
086: private RedefineSchema _redefineSchema;
087:
088: /**
089: * The XML Schema imported
090: */
091:
092: public RedefineUnmarshaller(Schema schema, AttributeSet atts,
093: Resolver resolver, URIResolver uriResolver,
094: Locator locator, SchemaUnmarshallerState state)
095: throws XMLException {
096: super ();
097: if (schema == null) {
098: String err = SchemaNames.REDEFINE
099: + " must be used with an existing parent XML Schema.";
100: throw new SchemaException(err);
101: }
102: setResolver(resolver);
103: setURIResolver(uriResolver);
104:
105: URILocation uri = null;
106: //-- Get schemaLocation
107: String schemaLocation = atts
108: .getValue(SchemaNames.SCHEMALOCATION_ATTR);
109: _schema = schema;
110:
111: if (schemaLocation == null) {
112: //-- <redefine/> or <redefine> <annotation>(*) </redefine>
113: _redefineSchema = new RedefineSchema(schema);
114: _schema.addRedefineSchema(_redefineSchema);
115: return;
116: }
117:
118: if (schemaLocation.indexOf("\\") != -1) {
119: String err = "'"
120: + schemaLocation
121: + "' is not a valid URI as defined by IETF RFC 2396.";
122: err += "The URI mustn't contain '\\'.";
123: error(err);
124: }
125:
126: try {
127: String documentBase = locator.getSystemId();
128: if (documentBase != null) {
129: if (!documentBase.endsWith("/"))
130: documentBase = documentBase.substring(0,
131: documentBase.lastIndexOf('/') + 1);
132: }
133: uri = getURIResolver()
134: .resolve(schemaLocation, documentBase);
135: if (uri != null) {
136: schemaLocation = uri.getAbsoluteURI();
137: }
138: } catch (URIException urix) {
139: throw new XMLException(urix);
140: }
141:
142: //-- Schema object to hold import schema
143: boolean addSchema = false;
144: _redefineSchema = schema.getRedefineSchema(schemaLocation);
145: Schema importedSchema = null;
146:
147: boolean alreadyLoaded = false;
148:
149: //-- The schema is not yet loaded
150: if (_redefineSchema == null) {
151: if (uri instanceof SchemaLocation) {
152: importedSchema = ((SchemaLocation) uri).getSchema();
153: //-- set the main schema in order to handle
154: //-- redefinition at runtime
155:
156: // importedSchema.addMainSchema(schema);
157:
158: _redefineSchema = new RedefineSchema(schema,
159: importedSchema);
160: schema.addRedefineSchema(_redefineSchema);
161: alreadyLoaded = true;
162: } else {
163: importedSchema = new Schema();
164: addSchema = true;
165: }
166: } else {
167: //-- check schema location, if different, allow merge
168: String tmpLocation = _redefineSchema.getOriginalSchema()
169: .getSchemaLocation();
170: alreadyLoaded = schemaLocation.equals(tmpLocation);
171: }
172:
173: state.markAsProcessed(schemaLocation, importedSchema);
174:
175: if (alreadyLoaded)
176: return;
177:
178: //-- Parser Schema
179: Parser parser = null;
180: try {
181: parser = state.getConfiguration().getParser();
182: } catch (RuntimeException rte) {
183: }
184: if (parser == null) {
185: throw new SchemaException(
186: "Error failed to create parser for import");
187: }
188: //-- Create Schema object and setup unmarshaller
189: SchemaUnmarshaller schemaUnmarshaller = new SchemaUnmarshaller(
190: state);
191: schemaUnmarshaller.setURIResolver(getURIResolver());
192: schemaUnmarshaller.setSchema(importedSchema);
193: Sax2ComponentReader handler = new Sax2ComponentReader(
194: schemaUnmarshaller);
195: parser.setDocumentHandler(handler);
196: parser.setErrorHandler(handler);
197:
198: try {
199: InputSource source = new InputSource(uri.getReader());
200: source.setSystemId(uri.getAbsoluteURI());
201: parser.parse(source);
202: } catch (java.io.IOException ioe) {
203: throw new SchemaException("Error reading import file '"
204: + schemaLocation + "': " + ioe);
205: } catch (org.xml.sax.SAXException sx) {
206: throw new SchemaException(sx);
207: }
208:
209: //-- namespace checking
210: String namespace = importedSchema.getTargetNamespace();
211: if (namespace != null) {
212: //-- Make sure targetNamespace is not the same as the
213: //-- importing schema, see section 4.2.2 in the
214: //-- XML Schema Recommendation
215: if (!namespace.equals(schema.getTargetNamespace())) {
216: String err = "The 'namespace' attribute in the <redefine> element must be the same of the targetNamespace of the global schema.\n"
217: + namespace
218: + " is different from:"
219: + schema.getTargetNamespace();
220: error(err);
221: }
222: } else {
223: importedSchema.setTargetNamespace(schema
224: .getTargetNamespace());
225: }
226:
227: //-- set the main schema in order to handle
228: //-- redefinition at runtime
229:
230: // importedSchema.addMainSchema(schema);
231:
232: _importedSchema = importedSchema;
233: _redefineSchema = new RedefineSchema(schema, _importedSchema);
234: //-- Add schema to list of redefine schemas (if not already present)
235: if (addSchema) {
236: importedSchema.setSchemaLocation(schemaLocation);
237: _schema.addRedefineSchema(_redefineSchema);
238: }
239: }
240:
241: /**
242: * Signals the start of an element with the given name.
243: *
244: * @param name the NCName of the element. It is an error
245: * if the name is a QName (ie. contains a prefix).
246: * @param namespace the namespace of the element. This may be null.
247: * Note: A null namespace is not the same as the default namespace unless
248: * the default namespace is also null.
249: * @param atts the AttributeSet containing the attributes associated
250: * with the element.
251: * @param nsDecls the namespace declarations being declared for this
252: * element. This may be null.
253: **/
254: public void startElement(String name, String namespace,
255: AttributeSet atts, Namespaces nsDecls) throws XMLException {
256:
257: //-- DEBUG
258: //System.out.println("#startElement: " + name + " {" + namespace + "}");
259: //-- /DEBUG
260:
261: //-- Do delagation if necessary
262: if (_unmarshaller != null) {
263: try {
264: _unmarshaller.startElement(name, namespace, atts,
265: nsDecls);
266: _depth++;
267: return;
268: } catch (RuntimeException rtx) {
269: error(rtx);
270: }
271: }
272:
273: //-- <annotation>
274: if (name.equals(SchemaNames.ANNOTATION)) {
275: _unmarshaller = new AnnotationUnmarshaller(atts);
276: }
277: //-- <attributeGroup>
278: else if (name.equals(SchemaNames.ATTRIBUTE_GROUP)) {
279: _unmarshaller = new AttributeGroupUnmarshaller(_schema,
280: atts);
281: }
282: //-- <complexType>
283: else if (name.equals(SchemaNames.COMPLEX_TYPE)) {
284: _unmarshaller = new ComplexTypeUnmarshaller(_schema, atts,
285: getResolver());
286: }
287: //-- <simpleType>
288: else if (name.equals(SchemaNames.SIMPLE_TYPE)) {
289: _unmarshaller = new SimpleTypeUnmarshaller(_schema, atts);
290: }
291: //-- <group>
292: else if (name.equals(SchemaNames.GROUP)) {
293: _unmarshaller = new ModelGroupUnmarshaller(_schema, atts,
294: getResolver());
295: } else {
296: //--Exception here
297: String err = "<" + name
298: + "> elements cannot be used in a redefine.";
299: error(err);
300: }
301:
302: _unmarshaller.setDocumentLocator(getDocumentLocator());
303:
304: } //-- startElement
305:
306: /**
307: * Signals to end of the element with the given name.
308: *
309: * @param name the NCName of the element. It is an error
310: * if the name is a QName (ie. contains a prefix).
311: * @param namespace the namespace of the element.
312: **/
313: public void endElement(String name, String namespace)
314: throws XMLException {
315:
316: //-- DEBUG
317: //System.out.println("#endElement: " + name + " {" + namespace + "}");
318: //-- /DEBUG
319:
320: //-- Do delagation if necessary
321: if ((_unmarshaller != null) && (_depth > 0)) {
322: _unmarshaller.endElement(name, namespace);
323: --_depth;
324: return;
325: }
326:
327: //-- use internal JVM String
328: name = name.intern();
329:
330: //-- check for name mismatches
331: if ((_unmarshaller != null)) {
332: if (!name.equals(_unmarshaller.elementName())) {
333: String err = "error: missing end element for ";
334: err += _unmarshaller.elementName();
335: error(err);
336: }
337: } else {
338: String err = "error: missing start element for " + name;
339: throw new SchemaException(err);
340: }
341:
342: //-- call unmarshaller.finish() to perform any necessary cleanup
343: _unmarshaller.finish();
344:
345: //-- <annotation>
346: if (name.equals(SchemaNames.ANNOTATION)) {
347: _redefineSchema.addAnnotation((Annotation) _unmarshaller
348: .getObject());
349: }
350: //-- <attributeGroup>
351: else if (name.equals(SchemaNames.ATTRIBUTE_GROUP)) {
352: if (_redefineSchema.getSchemaLocation() == "") {
353: String err = "In a <redefine>, only annotations can be defined when no -schemaLocation- is specified.";
354: error(err);
355: }
356:
357: AttributeGroupDecl group = null;
358: group = (AttributeGroupDecl) (((AttributeGroupUnmarshaller) _unmarshaller)
359: .getAttributeGroup());
360:
361: String structureName = group.getName();
362: if (structureName == null) {
363: String err = "When redefining an AttributeGroup, the group must have a name.\n";
364: error(err);
365: }
366:
367: //1-- the attributeGroup must exist in the imported schema
368: AttributeGroup original = _importedSchema
369: .getAttributeGroup(structureName);
370: if (original == null) {
371: String err = "When redefining an AttributeGroup, the AttributeGroup must be present in the imported XML schema.\n"
372: + "AttributeGroup: "
373: + structureName
374: + " is not defined in XML Schema:"
375: + _importedSchema.getSchemaLocation();
376: error(err);
377: }
378:
379: //-- todo: add code to check the Derivation Valid (Restriction, Complex) constraint.
380: group.setRedefined();
381: _redefineSchema.addAttributeGroup(group);
382:
383: }
384: //-- <complexType>
385: else if (name.equals(SchemaNames.COMPLEX_TYPE)) {
386: if (_redefineSchema.getSchemaLocation() == "") {
387: String err = "In a <redefine>, only annotations can be defined when no -schemaLocation- is specified.";
388: error(err);
389: }
390: ComplexType complexType = null;
391: complexType = ((ComplexTypeUnmarshaller) _unmarshaller)
392: .getComplexType();
393: //-- Checks that the complexType exists in the imported schema
394: String structureName = complexType.getName();
395: if (structureName == null) {
396: String err = "When redefining a complexType, the complexType must have a name.\n";
397: error(err);
398: }
399:
400: //1-- the complexType must exist in the imported schema
401: ComplexType original = _importedSchema
402: .getComplexType(structureName);
403: if (original == null) {
404: String err = "When redefining a complexType, the complexType must be present in the imported XML schema.\n"
405: + "ComplexType: "
406: + structureName
407: + " is not defined in XML Schema:"
408: + _importedSchema.getSchemaLocation();
409: error(err);
410: }
411:
412: //2-- the base type must be itself
413: XMLType baseType = complexType.getBaseType();
414: //--just check the names since a top level complexType can only be defined once.
415: if (baseType == null
416: || !baseType.getName().equals(structureName)) {
417: String err = "When redefining a complexType, the complexType must use itself as the base type definition.\n"
418: + "ComplexType: "
419: + structureName
420: + " uses:"
421: + baseType + " as its base type.";
422: error(err);
423: }
424:
425: complexType.setRedefined();
426: _redefineSchema.addComplexType(complexType);
427: getResolver().addResolvable(complexType.getReferenceId(),
428: complexType);
429: }
430: //-- <simpleType>
431: else if (name.equals(SchemaNames.SIMPLE_TYPE)) {
432: if (_redefineSchema.getSchemaLocation() == "") {
433: String err = "In a <redefine>, only annotations can be defined when no -schemaLocation- is specified.";
434: error(err);
435: }
436:
437: SimpleType simpleType = null;
438: simpleType = ((SimpleTypeUnmarshaller) _unmarshaller)
439: .getSimpleType();
440: //-- Checks that the simpleType exists in the imported schema
441: String structureName = simpleType.getName();
442: if (structureName == null) {
443: String err = "When redefining a simpleType, the simpleType must have a name.\n";
444: error(err);
445: }
446:
447: //1-- the simpleType must exist in the imported schema
448: SimpleType original = _importedSchema.getSimpleType(
449: structureName, _schema.getTargetNamespace());
450: if (original == null) {
451: String err = "When redefining a simpleType, the simpleType must be present in the imported XML schema.\n"
452: + "SimpleType: "
453: + structureName
454: + " is not defined in XML Schema:"
455: + _importedSchema.getSchemaLocation();
456: error(err);
457: }
458:
459: //2-- the base type must be itself
460: XMLType baseType = simpleType.getBaseType();
461: //--just check the names since a top level complexType can only be defined once.
462: if (!baseType.getName().equals(structureName)) {
463: String err = "When redefining a simpleType, the simpleType must use itself as the base type definition.\n"
464: + "SimpleType: "
465: + structureName
466: + " uses:"
467: + baseType.getName() + " as its base type.";
468: error(err);
469: }
470:
471: simpleType.setRedefined();
472: _redefineSchema.addSimpleType(simpleType);
473: getResolver().addResolvable(simpleType.getReferenceId(),
474: simpleType);
475: }
476: //--<group>
477: else if (name.equals(SchemaNames.GROUP)) {
478: if (_redefineSchema.getSchemaLocation() == "") {
479: String err = "In a <redefine>, only annotations can be defined when no -schemaLocation- is specified.";
480: error(err);
481: }
482:
483: ModelGroup group = null;
484: group = (((ModelGroupUnmarshaller) _unmarshaller)
485: .getGroup());
486:
487: String structureName = group.getName();
488: if (structureName == null) {
489: String err = "When redefining a group, the group must have a name.\n";
490: error(err);
491: }
492:
493: //1-- the group must exist in the imported schema
494: Group original = _importedSchema
495: .getModelGroup(structureName);
496: if (original == null) {
497: String err = "When redefining a group, the group must be present in the imported XML schema.\n"
498: + "Group: "
499: + structureName
500: + " is not defined in XML Schema:"
501: + _importedSchema.getSchemaLocation();
502: error(err);
503: }
504:
505: //-- code needs to be added to check the Particle Valid (Restriction) constraint
506: //--TBD
507:
508: group.setRedefined();
509: _redefineSchema.addGroup(group);
510: } else {
511: String err = "In a <redefine>, only complexTypes|simpleTypes|groups or attributeGroups can be redefined.";
512: error(err);
513: }
514:
515: _unmarshaller = null;
516: } //-- endElement
517:
518: public void characters(char[] ch, int start, int length)
519: throws XMLException {
520: //-- Do delagation if necessary
521: if (_unmarshaller != null) {
522: _unmarshaller.characters(ch, start, length);
523: }
524: } //-- characters
525:
526: /**
527: * Sets the name of the element that this UnknownUnmarshaller handles
528: **/
529: public String elementName() {
530: return SchemaNames.REDEFINE;
531: } //-- elementName
532:
533: /**
534: * Returns the Object created by this ComponentReader
535: * @return the Object created by this ComponentReader
536: **/
537: public Object getObject() {
538: return _redefineSchema;
539: } //-- getObject
540:
541: }
|