View Javadoc
1   /**
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 1999-2002 (C) Intalio Inc. All Rights Reserved.
32   *
33   * $Id$
34   */
35  
36  package org.exolab.castor.xml.schema.reader;
37  
38  import org.exolab.castor.xml.AttributeSet;
39  import org.exolab.castor.xml.Namespaces;
40  import org.exolab.castor.xml.XMLException;
41  import org.exolab.castor.xml.schema.Annotation;
42  import org.exolab.castor.xml.schema.AttributeDecl;
43  import org.exolab.castor.xml.schema.AttributeGroupReference;
44  import org.exolab.castor.xml.schema.ComplexType;
45  import org.exolab.castor.xml.schema.ContentType;
46  import org.exolab.castor.xml.schema.ElementDecl;
47  import org.exolab.castor.xml.schema.SchemaContext;
48  import org.exolab.castor.xml.schema.Group;
49  import org.exolab.castor.xml.schema.ModelGroup;
50  import org.exolab.castor.xml.schema.Schema;
51  import org.exolab.castor.xml.schema.SchemaException;
52  import org.exolab.castor.xml.schema.SchemaNames;
53  import org.exolab.castor.xml.schema.Wildcard;
54  import org.exolab.castor.xml.schema.XMLType;
55  
56  /**
57   * A class for unmarshalling XML Schema <complexType> definitions.
58   * 
59   * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
60   * @version $Revision$ $Date: 2003-03-03 00:05:44 -0700 (Mon, 03 Mar 2003) $
61   **/
62  public class ComplexTypeUnmarshaller extends ComponentReader {
63  
64    /**
65     * Represents the textual representation of the value '0'
66     */
67    private static final String VALUE_0 = "0";
68  
69    /**
70     * Represents the textual representation of the value '1'
71     */
72    private static final String VALUE_1 = "1";
73  
74    /**
75     * Represents the textual representation of the value 'false'
76     */
77    private static final String VALUE_FALSE = "false";
78  
79    /**
80     * Represents the textual representation of the value 'true'
81     */
82    private static final String VALUE_TRUE = "true";
83  
84    /**
85     * Represents the XML schema keyword 'restrictions'
86     */
87    private static final String KEYWORD_RESTRICTIONS = "restrictions";
88  
89    /**
90     * Represents the XML schema keyword 'extensions'
91     */
92    private static final String KEYWORD_EXTENSION = "extension";
93  
94    /**
95     * Represents the XML schema keyword 'derivedBy'
96     */
97    private static final String KEYWORD_DERIVED_BY = "derivedBy";
98  
99    /**
100    * The current ComponentReader
101    **/
102   private ComponentReader unmarshaller;
103 
104   /**
105    * The current branch depth
106    **/
107   private int depth = 0;
108 
109   /**
110    * The Attribute reference for the Attribute we are constructing
111    **/
112   private ComplexType _complexType = null;
113   private boolean allowAnnotation = true;
114   private boolean foundAnnotation = false;
115   private boolean foundAnyAttribute = false;
116   private boolean foundAttributes = false;
117   private boolean foundSimpleContent = false;
118   private boolean foundComplexContent = false;
119   private boolean foundModelGroup = false;
120 
121   private Schema _schema = null;
122 
123   /**
124    * Creates a new {@link ComplexTypeUnmarshaller} instance.
125    * 
126    * @param schemaContext the {@link SchemaContext} instance to get some configuration settings from
127    * @param schema the {@link Schema} to which the {@link ComplexType} belongs.
128    * @param atts the attribute list associated with this {@link ComplexType}.
129    * @see Schema
130    **/
131   public ComplexTypeUnmarshaller(final SchemaContext schemaContext, final Schema schema,
132       final AttributeSet atts) throws XMLException {
133     super(schemaContext);
134 
135     this._schema = schema;
136 
137     _complexType = schema.createComplexType();
138 
139     _complexType.useResolver(getResolver());
140 
141     // -- handle attributes
142     String attValue = null;
143 
144     // -- read @name attribute
145     _complexType.setName(atts.getValue(SchemaNames.NAME_ATTR));
146 
147     // -- read contentType
148     String content = atts.getValue(SchemaNames.MIXED);
149     if (content != null) {
150       if (isTurnedOn(content)) {
151         _complexType.setContentType(ContentType.mixed);
152       }
153       if (isTurnedOff(content)) {
154         _complexType.setContentType(ContentType.elemOnly);
155       }
156     }
157 
158     // -- base and derivedBy
159     String base = atts.getValue(SchemaNames.BASE_ATTR);
160     if ((base != null) && (base.length() > 0)) {
161 
162       String derivedBy = atts.getValue(KEYWORD_DERIVED_BY);
163       _complexType.setDerivationMethod(derivedBy);
164       if ((derivedBy == null) || (derivedBy.length() == 0)
165           || (derivedBy.equals(KEYWORD_EXTENSION))) {
166         XMLType baseType = schema.getType(base);
167         if (baseType == null)
168           _complexType.setBase(base); // the base type has not been read
169         else
170           _complexType.setBaseType(baseType);
171       } else if (derivedBy.equals(KEYWORD_RESTRICTIONS)) {
172         String err = "restrictions not yet supported for <type>.";
173         throw new SchemaException(err);
174       } else {
175         String err = "invalid value for derivedBy attribute of ";
176         err += "<type>: " + derivedBy;
177         throw new SchemaException(err);
178       }
179 
180     }
181 
182     // -- read @abstract attribute
183     attValue = atts.getValue(SchemaNames.ABSTRACT);
184     if (attValue != null) {
185       Boolean bool = Boolean.valueOf(attValue);
186       _complexType.setAbstract(bool.booleanValue());
187     }
188 
189     // -- read @block attribute
190     _complexType.setBlock(atts.getValue(SchemaNames.BLOCK_ATTR));
191 
192     // -- read @final attribute
193     _complexType.setFinal(atts.getValue(SchemaNames.FINAL_ATTR));
194 
195     // -- read @id attribute
196     _complexType.setId(atts.getValue(SchemaNames.ID_ATTR));
197 
198   }
199 
200   /**
201    * Checks whether a given property is turned on, i.e. its value is set to 'false' or '0'.
202    * 
203    * @param property Property value
204    * @return True of the property is 'turned off'.
205    */
206   private boolean isTurnedOff(String content) {
207     return content.equals(VALUE_FALSE) || content.equals(VALUE_0);
208   }
209 
210   /**
211    * Checks whether a given property is turned on, i.e. its value is set to 'true' or '1'.
212    * 
213    * @param property Property value
214    * @return True of the property is 'turned on'.
215    */
216   private boolean isTurnedOn(String property) {
217     return property.equals(VALUE_TRUE) || property.equals(VALUE_1);
218   }
219 
220   // -----------/
221   // - Methods -/
222   // -----------/
223 
224   /**
225    * Returns the name of the element that this ComponentReader handles
226    * 
227    * @return the name of the element that this ComponentReader handles
228    **/
229   public String elementName() {
230     return SchemaNames.COMPLEX_TYPE;
231   } // -- elementName
232 
233   /**
234    *
235   **/
236   public ComplexType getComplexType() {
237     return _complexType;
238   } // -- getComplexType
239 
240   /**
241    * Returns the Object created by this ComponentReader
242    * 
243    * @return the Object created by this ComponentReader
244    **/
245   public Object getObject() {
246     return getComplexType();
247   } // -- getObject
248 
249   /**
250    * Signals the start of an element with the given name.
251    *
252    * @param name the NCName of the element. It is an error if the name is a QName (ie. contains a
253    *        prefix).
254    * @param namespace the namespace of the element. This may be null. Note: A null namespace is not
255    *        the same as the default namespace unless the default namespace is also null.
256    * @param atts the AttributeSet containing the attributes associated with the element.
257    * @param nsDecls the namespace declarations being declared for this element. This may be null.
258    **/
259   public void startElement(String name, String namespace, AttributeSet atts, Namespaces nsDecls)
260       throws XMLException {
261     // -- Do delagation if necessary
262     if (unmarshaller != null) {
263       unmarshaller.startElement(name, namespace, atts, nsDecls);
264       ++depth;
265       return;
266     }
267 
268     // -- <anyAttribute>
269     if (SchemaNames.ANY_ATTRIBUTE.equals(name)) {
270       if (foundComplexContent)
271         error("an anyAttribute element cannot appear as a child "
272             + "of 'complexType' if 'complexContent' also exists");
273       if (foundSimpleContent)
274         error("an anyAttribute element cannot appear as a child "
275             + "of 'complexType' if 'simpleContent' also exists");
276       foundAnyAttribute = true;
277       allowAnnotation = true;
278       unmarshaller =
279           new WildcardUnmarshaller(getSchemaContext(), _complexType, _schema, name, atts);
280     }
281 
282     // -- attribute declarations
283     else if (SchemaNames.ATTRIBUTE.equals(name)) {
284       if (foundComplexContent)
285         error("an attribute definition cannot appear as a child "
286             + "of 'complexType' if 'complexContent' also exists");
287       if (foundSimpleContent)
288         error("an 'attribute' definition cannot appear as a child "
289             + "of 'complexType' if 'simpleContent' also exists");
290       if (foundAnyAttribute)
291         error("an attribute definition cannot appear after "
292             + "the definition of an 'anyAttribute' element");
293       foundAttributes = true;
294       allowAnnotation = false;
295       unmarshaller = new AttributeUnmarshaller(getSchemaContext(), _schema, atts);
296     }
297     // -- attribute group declarations
298     else if (SchemaNames.ATTRIBUTE_GROUP.equals(name)) {
299       // -- make sure we have an attribute group
300       // -- reference and not a definition
301 
302       if (atts.getValue(SchemaNames.REF_ATTR) == null) {
303         error("A 'complexType' may contain referring " + "attributeGroups, but not defining ones.");
304       }
305       if (foundComplexContent)
306         error("an attributeGroup reference cannot appear as a child "
307             + "of 'complexType' if 'complexContent' also exists");
308       if (foundSimpleContent)
309         error("an attributeGroup reference cannot appear as a child "
310             + "of 'complexType' if 'simpleContent' also exists");
311       if (foundAnyAttribute)
312         error("an 'attributeGroup' reference cannot appear after "
313             + "the definition of an 'anyAttribute' element");
314 
315       foundAttributes = true;
316       allowAnnotation = false;
317       unmarshaller = new AttributeGroupUnmarshaller(getSchemaContext(), _schema, atts);
318     }
319     // -- simpleContent
320     else if (SchemaNames.SIMPLE_CONTENT.equals(name)) {
321 
322       if (foundAttributes)
323         error("'simpleContent' and attribute definitions cannot both "
324             + "appear as children of 'complexType' at the same time.");
325       if (foundComplexContent)
326         error("'simpleContent' and 'complexContent' cannot both "
327             + "appear as children of 'complexType'.");
328       if (foundSimpleContent)
329         error("Only one (1) 'simpleContent' may appear as a child of " + "'complexType'.");
330       if (foundModelGroup)
331         error("'simpleContent' cannot appear as a child of "
332             + "'complexType' if 'all', 'sequence', 'choice' or " + "'group' also exist");
333 
334       foundSimpleContent = true;
335       allowAnnotation = false;
336       _complexType.setSimpleContent(true);
337       unmarshaller = new SimpleContentUnmarshaller(getSchemaContext(), _complexType, atts);
338     }
339     // -- complexContent
340     else if (SchemaNames.COMPLEX_CONTENT.equals(name)) {
341 
342       if (foundAttributes)
343         error("'complexContent' and attribute definitions cannot both "
344             + "appear as children of 'complexType' at the same time.");
345       if (foundSimpleContent)
346         error("'simpleContent' and 'complexContent' cannot both "
347             + "appear as children of 'complexType'.");
348       if (foundComplexContent)
349         error("Only one (1) 'complexContent' may appear as a child of " + "'complexType'.");
350       if (foundModelGroup)
351         error("'complexContent' cannot appear as a child of "
352             + "'complexType' if 'all', 'sequence', 'choice' or " + "'group' also exist");
353 
354       foundComplexContent = true;
355       allowAnnotation = false;
356 
357       _complexType.setComplexContent(true);
358       unmarshaller = new ComplexContentUnmarshaller(getSchemaContext(), _complexType, atts);
359     }
360     // --<group>
361     else if (name.equals(SchemaNames.GROUP)) {
362       if (foundAttributes)
363         error("'" + name + "' must appear before any attribute "
364             + "definitions when a child of 'complexType'.");
365       if (foundComplexContent)
366         error("'" + name + "' and 'complexContent' cannot both "
367             + "appear as children of 'complexType'.");
368       if (foundSimpleContent)
369         error("'" + name + "' and 'simpleContent' cannot both "
370             + "appear as children of 'complexType'.");
371       if (foundModelGroup)
372         error("'" + name + "' cannot appear as a child of 'complexType' "
373             + "if another 'all', 'sequence', 'choice' or " + "'group' also exists.");
374 
375       foundModelGroup = true;
376       allowAnnotation = false;
377       unmarshaller = new ModelGroupUnmarshaller(getSchemaContext(), _schema, atts);
378     }
379     // -- ModelGroup declarations (choice, all, sequence)
380     else if ((SchemaNames.isGroupName(name)) && (name != SchemaNames.GROUP)) {
381 
382       if (foundAttributes)
383         error("'" + name + "' must appear before any attribute "
384             + "definitions when a child of 'complexType'.");
385       if (foundComplexContent)
386         error("'" + name + "' and 'complexContent' cannot both "
387             + "appear as children of 'complexType'.");
388       if (foundSimpleContent)
389         error("'" + name + "' and 'simpleContent' cannot both "
390             + "appear as children of 'complexType'.");
391       if (foundModelGroup)
392         error("'" + name + "' cannot appear as a child of 'complexType' "
393             + "if another 'all', 'sequence', 'choice' or " + "'group' also exists.");
394 
395       foundModelGroup = true;
396       allowAnnotation = false;
397       unmarshaller = new GroupUnmarshaller(getSchemaContext(), _schema, name, atts);
398     } else if (name.equals(SchemaNames.ANNOTATION)) {
399       if (allowAnnotation) {
400         unmarshaller = new AnnotationUnmarshaller(getSchemaContext(), atts);
401         allowAnnotation = false;
402         foundAnnotation = true;
403       } else {
404         if (foundAnnotation) {
405           error("Only one (1) annotation may appear as a child of " + "'complexType' elements");
406         }
407         error("An annotation must appear as the first child of " + "'complexType' elements.");
408       }
409     } else
410       illegalElement(name);
411 
412   } // -- startElement
413 
414   /**
415    * Signals to end of the element with the given name.
416    *
417    * @param name the NCName of the element. It is an error if the name is a QName (ie. contains a
418    *        prefix).
419    * @param namespace the namespace of the element.
420    **/
421   public void endElement(String name, String namespace) throws XMLException {
422 
423     // -- Do delagation if necessary
424     if ((unmarshaller != null) && (depth > 0)) {
425       unmarshaller.endElement(name, namespace);
426       --depth;
427       return;
428     }
429     // -- have unmarshaller perform any necessary clean up
430     unmarshaller.finish();
431 
432     // -- <anyAttribute>
433     if (SchemaNames.ANY_ATTRIBUTE.equals(name)) {
434       Wildcard wildcard = ((WildcardUnmarshaller) unmarshaller).getWildcard();
435       try {
436         _complexType.setAnyAttribute(wildcard);
437       } catch (SchemaException e) {
438         throw new IllegalArgumentException(e.getMessage());
439       }
440     }
441     // -- attribute declarations
442     if (SchemaNames.ATTRIBUTE.equals(name)) {
443       AttributeDecl attrDecl = ((AttributeUnmarshaller) unmarshaller).getAttribute();
444 
445       _complexType.addAttributeDecl(attrDecl);
446     }
447     // -- attribute groups
448     else if (SchemaNames.ATTRIBUTE_GROUP.equals(name)) {
449       AttributeGroupReference attrGroupRef = (AttributeGroupReference) unmarshaller.getObject();
450       _complexType.addAttributeGroupReference(attrGroupRef);
451     }
452     // -- element declarations
453     else if (SchemaNames.ELEMENT.equals(name)) {
454 
455       ElementDecl element = ((ElementUnmarshaller) unmarshaller).getElement();
456       _complexType.addElementDecl(element);
457     }
458     // --group
459     else if (name.equals(SchemaNames.GROUP)) {
460       ModelGroup group = ((ModelGroupUnmarshaller) unmarshaller).getGroup();
461       _complexType.addGroup(group);
462     }
463 
464     // -- group declarations (all, choice, sequence)
465     else if ((SchemaNames.isGroupName(name)) && (name != SchemaNames.GROUP)) {
466       Group group = ((GroupUnmarshaller) unmarshaller).getGroup();
467       _complexType.addGroup(group);
468     }
469     // -- annotation
470     else if (SchemaNames.ANNOTATION.equals(name)) {
471       Annotation ann = ((AnnotationUnmarshaller) unmarshaller).getAnnotation();
472       _complexType.addAnnotation(ann);
473     }
474 
475     unmarshaller = null;
476   } // -- endElement
477 
478   public void characters(char[] ch, int start, int length) throws XMLException {
479     // -- Do delagation if necessary
480     if (unmarshaller != null) {
481       unmarshaller.characters(ch, start, length);
482     }
483   } // -- characters
484 
485 } // -- ComplexTypeUnmarshaller