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 1999-2003 (C) Intalio Inc. All Rights Reserved.
042: *
043: * $Id: ElementUnmarshaller.java 6901 2007-03-18 19:20:02Z wguttmn $
044: */package org.exolab.castor.xml.schema.reader;
045:
046: //-- imported classes and packages
047: import org.exolab.castor.xml.AttributeSet;
048: import org.exolab.castor.xml.Namespaces;
049: import org.exolab.castor.xml.XMLException;
050: import org.exolab.castor.xml.schema.Annotation;
051: import org.exolab.castor.xml.schema.ElementDecl;
052: import org.exolab.castor.xml.schema.Form;
053: import org.exolab.castor.xml.schema.IdentityConstraint;
054: import org.exolab.castor.xml.schema.Resolver;
055: import org.exolab.castor.xml.schema.Schema;
056: import org.exolab.castor.xml.schema.SchemaException;
057: import org.exolab.castor.xml.schema.SchemaNames;
058: import org.exolab.castor.xml.schema.XMLType;
059:
060: /**
061: * A class for Unmarshalling element definitions
062: * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
063: *
064: * @version $Revision: 6901 $ $Date: 2003-03-03 02:57:21 -0700 (Mon, 03 Mar 2003) $
065: */
066: public class ElementUnmarshaller extends ComponentReader {
067:
068: /**
069: * The value of the maximum occurance wild card
070: **/
071: private static final String MAX_OCCURS_WILDCARD = "unbounded";
072:
073: //--------------------/
074: //- Member Variables -/
075: //--------------------/
076:
077: /**
078: * The current ComponentReader
079: **/
080: private ComponentReader unmarshaller;
081:
082: /**
083: * The current branch depth
084: **/
085: private int depth = 0;
086:
087: /**
088: * The element reference for the element definition we are "unmarshalling".
089: **/
090: private ElementDecl _element = null;
091:
092: private CharacterUnmarshaller charUnmarshaller = null;
093:
094: private Schema _schema = null;
095:
096: private boolean foundAnnotation = false;
097: private boolean foundIdentityConstraint = false;
098: private boolean foundSimpleType = false;
099: private boolean foundComplexType = false;
100: private boolean foundTypeReference = false;
101:
102: //----------------/
103: //- Constructors -/
104: //----------------/
105:
106: /**
107: * Creates a new ElementUnmarshaller
108: * @param schema the Schema to which the Element belongs
109: * @param atts the AttributeList
110: * @param resolver the resolver being used for reference resolving
111: **/
112: public ElementUnmarshaller(final Schema schema,
113: final AttributeSet atts, final Resolver resolver)
114: throws XMLException {
115: super ();
116: setResolver(resolver);
117:
118: this ._schema = schema;
119:
120: _element = new ElementDecl(schema);
121:
122: String attValue = null;
123:
124: //-- @ref
125: attValue = atts.getValue(SchemaNames.REF_ATTR);
126: if (attValue != null) {
127: _element.setReferenceName(attValue);
128: //-- report error if name attr exists also
129: if (atts.getValue(SchemaNames.NAME_ATTR) != null) {
130: error("The attributes 'ref' and 'name' appearing on "
131: + "element declarations are mutually exclusive.");
132: }
133: validateRefAtts(atts);
134: }
135: //-- @name
136: else {
137: _element.setName(atts.getValue(SchemaNames.NAME_ATTR));
138: }
139:
140: //-- @abstract
141: attValue = atts.getValue(SchemaNames.ABSTRACT);
142: if (attValue != null) {
143: _element
144: .setAbstract((new Boolean(attValue)).booleanValue());
145: }
146:
147: //-- @block
148: _element.setBlock(atts.getValue(SchemaNames.BLOCK_ATTR));
149:
150: //-- @default
151: attValue = atts.getValue(SchemaNames.DEFAULT_ATTR);
152: if (attValue != null) {
153: if (_element.getFixedValue() != null)
154: error("'default' and 'fixed' must not both be present.");
155: _element.setDefaultValue(attValue);
156: }
157:
158: //-- @final
159: _element.setFinal(atts.getValue(SchemaNames.FINAL_ATTR));
160:
161: //-- @abstract
162: final boolean isAbstract = new Boolean(atts
163: .getValue(SchemaNames.ABSTRACT)).booleanValue();
164: if (isAbstract) {
165: _element.setAbstract(isAbstract);
166: }
167:
168: //-- @fixed
169: attValue = atts.getValue(SchemaNames.FIXED_ATTR);
170: if (attValue != null) {
171: if (_element.getDefaultValue() != null)
172: throw new IllegalArgumentException(
173: "'default' and 'fixed' must not both be present.");
174: _element.setFixedValue(attValue);
175: }
176:
177: //-- @form
178: attValue = atts.getValue(SchemaNames.FORM);
179: if (attValue != null) {
180: _element.setForm(Form.valueOf(attValue));
181: }
182:
183: //-- @id
184: _element.setId(atts.getValue(SchemaNames.ID_ATTR));
185:
186: //-- @substitutionGroup
187: attValue = atts.getValue(SchemaNames.SUBSTITUTION_GROUP_ATTR);
188: if (attValue != null) {
189: _element.setSubstitutionGroup(attValue);
190: }
191:
192: //-- @type
193: attValue = atts.getValue(SchemaNames.TYPE_ATTR);
194: if (attValue != null) {
195: foundTypeReference = true;
196: _element.setTypeReference(attValue);
197: }
198:
199: //-- @nillable
200: attValue = atts.getValue(SchemaNames.NILLABLE_ATTR);
201: if (attValue != null) {
202: if (attValue.equals("true") || attValue.equals("1")) {
203: _element.setNillable(true);
204: } else if (!attValue.equals("false")
205: && !attValue.equals("0")) {
206: String err = "Invalid value for the 'nillable' attribute of "
207: + "an element definition: " + attValue;
208: throw new IllegalArgumentException(err);
209: }
210: }
211:
212: /*
213: * @minOccurs
214: * if minOccurs is present the value is the int value of
215: * of the attribute, otherwise minOccurs is 1.
216: */
217: attValue = atts.getValue(SchemaNames.MIN_OCCURS_ATTR);
218: int minOccurs = 1;
219: if (attValue != null) {
220: minOccurs = toInt(attValue);
221: _element.setMinOccurs(minOccurs);
222: }
223:
224: /*
225: * @maxOccurs
226: * If maxOccurs is present, the value is either unbounded
227: * or the int value of the attribute, otherwise maxOccurs
228: * equals the minOccurs value.
229: */
230: attValue = atts.getValue(SchemaNames.MAX_OCCURS_ATTR);
231: if (attValue != null) {
232: if (MAX_OCCURS_WILDCARD.equals(attValue))
233: attValue = "-1";
234: int maxOccurs = toInt(attValue);
235: _element.setMaxOccurs(maxOccurs);
236: } else if (minOccurs > 1)
237: _element.setMaxOccurs(minOccurs);
238:
239: charUnmarshaller = new CharacterUnmarshaller();
240: } //-- ElementUnmarshaller
241:
242: //-----------/
243: //- Methods -/
244: //-----------/
245:
246: /**
247: * Returns the name of the element that this ComponentReader
248: * handles
249: * @return the name of the element that this ComponentReader
250: * handles
251: **/
252: public String elementName() {
253: return SchemaNames.ELEMENT;
254: } //-- elementName
255:
256: /**
257: *
258: **/
259: public ElementDecl getElement() {
260: return _element;
261: } //-- getElement
262:
263: /**
264: * Returns the Object created by this ComponentReader
265: * @return the Object created by this ComponentReader
266: **/
267: public Object getObject() {
268: return _element;
269: } //-- getObject
270:
271: /**
272: * Signals the start of an element with the given name.
273: *
274: * @param name the NCName of the element. It is an error
275: * if the name is a QName (ie. contains a prefix).
276: * @param namespace the namespace of the element. This may be null.
277: * Note: A null namespace is not the same as the default namespace unless
278: * the default namespace is also null.
279: * @param atts the AttributeSet containing the attributes associated
280: * with the element.
281: * @param nsDecls the namespace declarations being declared for this
282: * element. This may be null.
283: **/
284: public void startElement(String name, String namespace,
285: AttributeSet atts, Namespaces nsDecls) throws XMLException {
286:
287: //-- Do delagation if necessary
288: if (unmarshaller != null) {
289: unmarshaller.startElement(name, namespace, atts, nsDecls);
290: ++depth;
291: return;
292: }
293:
294: if (SchemaNames.ANNOTATION.equals(name)) {
295: if (foundSimpleType || foundIdentityConstraint
296: || foundComplexType)
297: error("An annotation may only appear as the first child "
298: + "of an element definition.");
299:
300: if (foundAnnotation)
301: error("Only one (1) 'annotation' is allowed as a child of "
302: + "element definitions.");
303:
304: foundAnnotation = true;
305: unmarshaller = new AnnotationUnmarshaller(atts);
306: } else if (SchemaNames.COMPLEX_TYPE.equals(name)) {
307:
308: if (foundComplexType)
309: error("Only one (1) 'complexType' may appear in an "
310: + "element definition.");
311: if (foundSimpleType)
312: error("Both 'simpleType' and 'complexType' cannot appear "
313: + "in the same element definition.");
314: if (foundTypeReference)
315: error("Both 'type' attribute and 'complexType' element "
316: + "cannot appear in the same element definition.");
317:
318: if (foundIdentityConstraint)
319: error("A 'complexType' must appear before 'key', "
320: + "'keyref' and 'unique' elements.");
321:
322: foundComplexType = true;
323: unmarshaller = new ComplexTypeUnmarshaller(_schema, atts,
324: getResolver());
325: } else if (SchemaNames.SIMPLE_TYPE.equals(name)) {
326:
327: if (foundSimpleType)
328: error("Only one (1) 'simpleType' may appear in an "
329: + "element definition.");
330: if (foundComplexType)
331: error("Both 'simpleType' and 'complexType' cannot appear "
332: + "in the same element definition.");
333: if (foundTypeReference)
334: error("Both 'type' attribute and 'simpleType' element "
335: + "cannot appear in the same element definition.");
336:
337: if (foundIdentityConstraint)
338: error("A 'simpleType' must appear before 'key', "
339: + "'keyref' and 'unique' elements.");
340:
341: foundSimpleType = true;
342: unmarshaller = new SimpleTypeUnmarshaller(_schema, atts);
343: } else if (SchemaNames.KEY.equals(name)
344: || SchemaNames.KEYREF.equals(name)
345: || SchemaNames.UNIQUE.equals(name)) {
346: foundIdentityConstraint = true;
347: unmarshaller = new IdentityConstraintUnmarshaller(name,
348: atts);
349: } else
350: illegalElement(name);
351:
352: unmarshaller.setResolver(getResolver());
353: unmarshaller.setDocumentLocator(getDocumentLocator());
354:
355: } //-- startElement
356:
357: /**
358: * Signals to end of the element with the given name.
359: *
360: * @param name the NCName of the element. It is an error
361: * if the name is a QName (ie. contains a prefix).
362: * @param namespace the namespace of the element.
363: **/
364: public void endElement(String name, String namespace)
365: throws XMLException {
366:
367: //-- Do delagation if necessary
368: if ((unmarshaller != null) && (depth > 0)) {
369: unmarshaller.endElement(name, namespace);
370: --depth;
371: return;
372: }
373:
374: //-- check for name mismatches
375: if ((unmarshaller != null)
376: && (charUnmarshaller != unmarshaller)) {
377: if (!name.equals(unmarshaller.elementName())) {
378: String err = "missing end element for ";
379: err += unmarshaller.elementName();
380: throw new SchemaException(err);
381: }
382: }
383:
384: //-- call finish for any necessary cleanup
385: unmarshaller.finish();
386:
387: if (SchemaNames.ANNOTATION.equals(name)) {
388: Annotation ann = (Annotation) unmarshaller.getObject();
389: _element.addAnnotation(ann);
390: } else if (SchemaNames.COMPLEX_TYPE.equals(name)) {
391:
392: XMLType xmlType = ((ComplexTypeUnmarshaller) unmarshaller)
393: .getComplexType();
394:
395: _element.setType(xmlType);
396:
397: } else if (SchemaNames.SIMPLE_TYPE.equals(name)) {
398: XMLType xmlType = ((SimpleTypeUnmarshaller) unmarshaller)
399: .getSimpleType();
400: _element.setType(xmlType);
401: } else if (SchemaNames.KEY.equals(name)
402: || SchemaNames.KEYREF.equals(name)
403: || SchemaNames.UNIQUE.equals(name)) {
404: IdentityConstraint constraint = (IdentityConstraint) unmarshaller
405: .getObject();
406: _element.addIdentityConstraint(constraint);
407: }
408:
409: unmarshaller = null;
410:
411: } //-- endElement
412:
413: public void characters(char[] ch, int start, int length)
414: throws XMLException {
415: //-- Do delagation if necessary
416: if (unmarshaller != null) {
417: unmarshaller.characters(ch, start, length);
418: }
419: } //-- characters
420:
421: /**
422: * Makes sure only minOccurs, maxOccurs, id, and ref occur
423: * for element references.
424: *
425: * @param atts the AttributeSet to process
426: */
427: private static void validateRefAtts(AttributeSet atts)
428: throws XMLException {
429:
430: StringBuffer errors = null;
431:
432: for (int i = 0; i < atts.getSize(); i++) {
433: String name = atts.getName(i);
434: if (SchemaNames.REF_ATTR.equals(name))
435: continue;
436: else if (SchemaNames.MAX_OCCURS_ATTR.equals(name))
437: continue;
438: else if (SchemaNames.MIN_OCCURS_ATTR.equals(name))
439: continue;
440: else if (SchemaNames.ID_ATTR.equals(name))
441: continue;
442: else {
443: //-- check namespace
444: String namespace = atts.getNamespace(i);
445:
446: //-- If we have no namespace (ie no prefix) or we
447: //-- have the XSD Namespace then throw error
448: if ((namespace == null)
449: || (namespace.length() == 0)
450: || namespace
451: .equals(SchemaUnmarshaller.XSD_NAMESPACE)) {
452: //-- unprefixed attribute...assume XML Schema namespace
453: String error = "The attribute '"
454: + name
455: + "' must not appear on an element reference.";
456: if (errors == null)
457: errors = new StringBuffer(error);
458: else
459: errors.append(error);
460:
461: errors.append(System.getProperty("line.separator"));
462:
463: }
464: //-- otherwise we have a namespaced attribute from a different
465: //-- namespace..this is valid...continue
466: }
467: }
468:
469: if (errors != null)
470: throw new XMLException(errors.toString());
471:
472: } //-- validateRefAtts
473:
474: } //-- ElementUnmarshaller
|