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-2003 (C) Intalio Inc. All Rights Reserved.
32   *
33   * $Id$
34   */
35  
36  package org.exolab.castor.xml.schema.reader;
37  
38  // -- imported classes and packages
39  import org.exolab.castor.xml.AttributeSet;
40  import org.exolab.castor.xml.Namespaces;
41  import org.exolab.castor.xml.XMLException;
42  import org.exolab.castor.xml.schema.Annotation;
43  import org.exolab.castor.xml.schema.ElementDecl;
44  import org.exolab.castor.xml.schema.Form;
45  import org.exolab.castor.xml.schema.IdentityConstraint;
46  import org.exolab.castor.xml.schema.Schema;
47  import org.exolab.castor.xml.schema.SchemaContext;
48  import org.exolab.castor.xml.schema.SchemaException;
49  import org.exolab.castor.xml.schema.SchemaNames;
50  import org.exolab.castor.xml.schema.XMLType;
51  
52  /**
53   * A class for Unmarshalling element definitions
54   * 
55   * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
56   *
57   * @version $Revision$ $Date: 2003-03-03 02:57:21 -0700 (Mon, 03 Mar 2003) $
58   */
59  public class ElementUnmarshaller extends ComponentReader {
60  
61    /**
62     * The value of the maximum occurance wild card
63     **/
64    private static final String MAX_OCCURS_WILDCARD = "unbounded";
65  
66    // --------------------/
67    // - Member Variables -/
68    // --------------------/
69  
70    /**
71     * The current ComponentReader
72     **/
73    private ComponentReader unmarshaller;
74  
75    /**
76     * The current branch depth
77     **/
78    private int depth = 0;
79  
80    /**
81     * The element reference for the element definition we are "unmarshalling".
82     **/
83    private ElementDecl _element = null;
84  
85  
86    private CharacterUnmarshaller charUnmarshaller = null;
87  
88    private Schema _schema = null;
89  
90    private boolean foundAnnotation = false;
91    private boolean foundIdentityConstraint = false;
92    private boolean foundSimpleType = false;
93    private boolean foundComplexType = false;
94    private boolean foundTypeReference = false;
95  
96    // ----------------/
97    // - Constructors -/
98    // ----------------/
99  
100   /**
101    * Creates a new ElementUnmarshaller.
102    * 
103    * @param schemaContext the {@link SchemaContext} to get some configuration settings from
104    * @param schema the Schema to which the Element belongs
105    * @param atts the AttributeList
106    **/
107   public ElementUnmarshaller(final SchemaContext schemaContext, final Schema schema,
108       final AttributeSet atts) throws XMLException {
109     super(schemaContext);
110 
111     this._schema = schema;
112 
113     _element = new ElementDecl(schema);
114 
115     String attValue = null;
116 
117     // -- @ref
118     attValue = atts.getValue(SchemaNames.REF_ATTR);
119     if (attValue != null) {
120       _element.setReferenceName(attValue);
121       // -- report error if name attr exists also
122       if (atts.getValue(SchemaNames.NAME_ATTR) != null) {
123         error("The attributes 'ref' and 'name' appearing on "
124             + "element declarations are mutually exclusive.");
125       }
126       validateRefAtts(atts);
127     }
128     // -- @name
129     else {
130       _element.setName(atts.getValue(SchemaNames.NAME_ATTR));
131     }
132 
133     // -- @abstract
134     attValue = atts.getValue(SchemaNames.ABSTRACT);
135     if (attValue != null) {
136       _element.setAbstract(Boolean.parseBoolean(attValue));
137     }
138 
139     // -- @block
140     _element.setBlock(atts.getValue(SchemaNames.BLOCK_ATTR));
141 
142     // -- @default
143     attValue = atts.getValue(SchemaNames.DEFAULT_ATTR);
144     if (attValue != null) {
145       if (_element.getFixedValue() != null)
146         error("'default' and 'fixed' must not both be present.");
147       _element.setDefaultValue(attValue);
148     }
149 
150     // -- @final
151     _element.setFinal(atts.getValue(SchemaNames.FINAL_ATTR));
152 
153     // -- @abstract
154     final boolean isAbstract = Boolean.parseBoolean(atts.getValue(SchemaNames.ABSTRACT));
155     if (isAbstract) {
156       _element.setAbstract(isAbstract);
157     }
158 
159     // -- @fixed
160     attValue = atts.getValue(SchemaNames.FIXED_ATTR);
161     if (attValue != null) {
162       if (_element.getDefaultValue() != null)
163         throw new IllegalArgumentException("'default' and 'fixed' must not both be present.");
164       _element.setFixedValue(attValue);
165     }
166 
167     // -- @form
168     attValue = atts.getValue(SchemaNames.FORM);
169     if (attValue != null) {
170       _element.setForm(Form.valueOf(attValue));
171     }
172 
173     // -- @id
174     _element.setId(atts.getValue(SchemaNames.ID_ATTR));
175 
176     // -- @substitutionGroup
177     attValue = atts.getValue(SchemaNames.SUBSTITUTION_GROUP_ATTR);
178     if (attValue != null) {
179       _element.setSubstitutionGroup(attValue);
180     }
181 
182     // -- @type
183     attValue = atts.getValue(SchemaNames.TYPE_ATTR);
184     if (attValue != null) {
185       foundTypeReference = true;
186       _element.setTypeReference(attValue);
187     }
188 
189 
190     // -- @nillable
191     attValue = atts.getValue(SchemaNames.NILLABLE_ATTR);
192     if (attValue != null) {
193       if (attValue.equals("true") || attValue.equals("1")) {
194         _element.setNillable(true);
195       } else if (!attValue.equals("false") && !attValue.equals("0")) {
196         String err =
197             "Invalid value for the 'nillable' attribute of " + "an element definition: " + attValue;
198         throw new IllegalArgumentException(err);
199       }
200     }
201 
202     /*
203      * @minOccurs if minOccurs is present the value is the int value of of the attribute, otherwise
204      * minOccurs is 1.
205      */
206     attValue = atts.getValue(SchemaNames.MIN_OCCURS_ATTR);
207     int minOccurs = 1;
208     if (attValue != null) {
209       minOccurs = toInt(attValue);
210       _element.setMinOccurs(minOccurs);
211     }
212 
213     /*
214      * @maxOccurs If maxOccurs is present, the value is either unbounded or the int value of the
215      * attribute, otherwise maxOccurs equals the minOccurs value.
216      */
217     attValue = atts.getValue(SchemaNames.MAX_OCCURS_ATTR);
218     if (attValue != null) {
219       if (MAX_OCCURS_WILDCARD.equals(attValue))
220         attValue = "-1";
221       int maxOccurs = toInt(attValue);
222       _element.setMaxOccurs(maxOccurs);
223     } else if (minOccurs > 1)
224       _element.setMaxOccurs(minOccurs);
225 
226     charUnmarshaller = new CharacterUnmarshaller(getSchemaContext());
227   } // -- ElementUnmarshaller
228 
229   // -----------/
230   // - Methods -/
231   // -----------/
232 
233   /**
234    * Returns the name of the element that this ComponentReader handles
235    * 
236    * @return the name of the element that this ComponentReader handles
237    **/
238   public String elementName() {
239     return SchemaNames.ELEMENT;
240   } // -- elementName
241 
242   /**
243    *
244   **/
245   public ElementDecl getElement() {
246     return _element;
247   } // -- getElement
248 
249   /**
250    * Returns the Object created by this ComponentReader
251    * 
252    * @return the Object created by this ComponentReader
253    **/
254   public Object getObject() {
255     return _element;
256   } // -- getObject
257 
258   /**
259    * Signals the start of an element with the given name.
260    *
261    * @param name the NCName of the element. It is an error if the name is a QName (ie. contains a
262    *        prefix).
263    * @param namespace the namespace of the element. This may be null. Note: A null namespace is not
264    *        the same as the default namespace unless the default namespace is also null.
265    * @param atts the AttributeSet containing the attributes associated with the element.
266    * @param nsDecls the namespace declarations being declared for this element. This may be null.
267    **/
268   public void startElement(String name, String namespace, AttributeSet atts, Namespaces nsDecls)
269       throws XMLException {
270 
271     // -- Do delagation if necessary
272     if (unmarshaller != null) {
273       unmarshaller.startElement(name, namespace, atts, nsDecls);
274       ++depth;
275       return;
276     }
277 
278     if (SchemaNames.ANNOTATION.equals(name)) {
279       if (foundSimpleType || foundIdentityConstraint || foundComplexType)
280         error("An annotation may only appear as the first child " + "of an element definition.");
281 
282 
283       if (foundAnnotation)
284         error("Only one (1) 'annotation' is allowed as a child of " + "element definitions.");
285 
286       foundAnnotation = true;
287       unmarshaller = new AnnotationUnmarshaller(getSchemaContext(), atts);
288     } else if (SchemaNames.COMPLEX_TYPE.equals(name)) {
289 
290       if (foundComplexType)
291         error("Only one (1) 'complexType' may appear in an " + "element definition.");
292       if (foundSimpleType)
293         error("Both 'simpleType' and 'complexType' cannot appear "
294             + "in the same element definition.");
295       if (foundTypeReference)
296         error("Both 'type' attribute and 'complexType' element "
297             + "cannot appear in the same element definition.");
298 
299 
300       if (foundIdentityConstraint)
301         error("A 'complexType' must appear before 'key', " + "'keyref' and 'unique' elements.");
302 
303       foundComplexType = true;
304       unmarshaller = new ComplexTypeUnmarshaller(getSchemaContext(), _schema, atts);
305     } else if (SchemaNames.SIMPLE_TYPE.equals(name)) {
306 
307       if (foundSimpleType)
308         error("Only one (1) 'simpleType' may appear in an " + "element definition.");
309       if (foundComplexType)
310         error("Both 'simpleType' and 'complexType' cannot appear "
311             + "in the same element definition.");
312       if (foundTypeReference)
313         error("Both 'type' attribute and 'simpleType' element "
314             + "cannot appear in the same element definition.");
315 
316       if (foundIdentityConstraint)
317         error("A 'simpleType' must appear before 'key', " + "'keyref' and 'unique' elements.");
318 
319       foundSimpleType = true;
320       unmarshaller = new SimpleTypeUnmarshaller(getSchemaContext(), _schema, atts);
321     } else if (SchemaNames.KEY.equals(name) || SchemaNames.KEYREF.equals(name)
322         || SchemaNames.UNIQUE.equals(name)) {
323       foundIdentityConstraint = true;
324       unmarshaller = new IdentityConstraintUnmarshaller(getSchemaContext(), name, atts);
325     } else
326       illegalElement(name);
327 
328   } // -- startElement
329 
330   /**
331    * Signals to end of the element with the given name.
332    *
333    * @param name the NCName of the element. It is an error if the name is a QName (ie. contains a
334    *        prefix).
335    * @param namespace the namespace of the element.
336    **/
337   public void endElement(String name, String namespace) throws XMLException {
338 
339     // -- Do delagation if necessary
340     if ((unmarshaller != null) && (depth > 0)) {
341       unmarshaller.endElement(name, namespace);
342       --depth;
343       return;
344     }
345 
346     // -- check for name mismatches
347     if ((unmarshaller != null) && (charUnmarshaller != unmarshaller)) {
348       if (!name.equals(unmarshaller.elementName())) {
349         String err = "missing end element for ";
350         err += unmarshaller.elementName();
351         throw new SchemaException(err);
352       }
353     }
354 
355     // -- call finish for any necessary cleanup
356     unmarshaller.finish();
357 
358     if (SchemaNames.ANNOTATION.equals(name)) {
359       Annotation ann = (Annotation) unmarshaller.getObject();
360       _element.addAnnotation(ann);
361     } else if (SchemaNames.COMPLEX_TYPE.equals(name)) {
362 
363       XMLType xmlType = ((ComplexTypeUnmarshaller) unmarshaller).getComplexType();
364 
365       _element.setType(xmlType);
366 
367     } else if (SchemaNames.SIMPLE_TYPE.equals(name)) {
368       XMLType xmlType = ((SimpleTypeUnmarshaller) unmarshaller).getSimpleType();
369       _element.setType(xmlType);
370     } else if (SchemaNames.KEY.equals(name) || SchemaNames.KEYREF.equals(name)
371         || SchemaNames.UNIQUE.equals(name)) {
372       IdentityConstraint constraint = (IdentityConstraint) unmarshaller.getObject();
373       _element.addIdentityConstraint(constraint);
374     }
375 
376     unmarshaller = null;
377 
378   } // -- endElement
379 
380   public void characters(char[] ch, int start, int length) throws XMLException {
381     // -- Do delagation if necessary
382     if (unmarshaller != null) {
383       unmarshaller.characters(ch, start, length);
384     }
385   } // -- characters
386 
387   /**
388    * Makes sure only minOccurs, maxOccurs, id, and ref occur for element references.
389    *
390    * @param atts the AttributeSet to process
391    */
392   private static void validateRefAtts(AttributeSet atts) throws XMLException {
393 
394     StringBuffer errors = null;
395 
396     for (int i = 0; i < atts.getSize(); i++) {
397       String name = atts.getName(i);
398       if (SchemaNames.REF_ATTR.equals(name))
399         continue;
400       else if (SchemaNames.MAX_OCCURS_ATTR.equals(name))
401         continue;
402       else if (SchemaNames.MIN_OCCURS_ATTR.equals(name))
403         continue;
404       else if (SchemaNames.ID_ATTR.equals(name))
405         continue;
406       else {
407         // -- check namespace
408         String namespace = atts.getNamespace(i);
409 
410         // -- If we have no namespace (ie no prefix) or we
411         // -- have the XSD Namespace then throw error
412         if ((namespace == null) || (namespace.length() == 0)
413             || namespace.equals(SchemaUnmarshaller.XSD_NAMESPACE)) {
414           // -- unprefixed attribute...assume XML Schema namespace
415           String error = "The attribute '" + name + "' must not appear on an element reference.";
416           if (errors == null)
417             errors = new StringBuffer(error);
418           else
419             errors.append(error);
420 
421           errors.append(System.getProperty("line.separator"));
422 
423 
424         }
425         // -- otherwise we have a namespaced attribute from a different
426         // -- namespace..this is valid...continue
427       }
428     }
429 
430     if (errors != null)
431       throw new XMLException(errors.toString());
432 
433   } // -- validateRefAtts
434 
435 } // -- ElementUnmarshaller