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;
37  
38  import java.io.InputStream;
39  import java.io.PrintStream;
40  import java.io.PrintWriter;
41  import java.util.Hashtable;
42  import java.util.Map;
43  import java.util.Vector;
44  
45  import org.apache.commons.logging.Log;
46  import org.apache.commons.logging.LogFactory;
47  import org.castor.core.util.Messages;
48  import org.exolab.castor.mapping.Mapping;
49  import org.exolab.castor.xml.Unmarshaller;
50  import org.exolab.castor.xml.schema.simpletypes.AtomicType;
51  import org.exolab.castor.xml.schema.simpletypes.ListType;
52  import org.exolab.castor.xml.schema.simpletypes.RealType;
53  import org.exolab.castor.xml.schema.simpletypes.UrType;
54  import org.exolab.castor.xml.schema.simpletypes.factory.Type;
55  import org.exolab.castor.xml.schema.simpletypes.factory.TypeList;
56  import org.exolab.castor.xml.schema.simpletypes.factory.TypeProperty;
57  import org.xml.sax.InputSource;
58  
59  /**
60   * SimpleTypesFactory provides code constants for every built in type defined in
61   * www.w3.org/TR/xmlschma-2-20000407 USER_TYPE is used for user derived types.
62   *
63   * This factory can also create instances of classes derived from SimpleType that represent the
64   * simple types defined by xmlschema and those derived from them.
65   *
66   * @author <a href="mailto:berry@intalio.com">Arnaud Berry</a>
67   * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
68   **/
69  public class SimpleTypesFactory {
70    /** The Logger instance to use. */
71    private static final Log LOG = LogFactory.getLog(SimpleTypesFactory.class);
72  
73    // Type Codes:
74  
75    /** This code is for errors or uninitialized types. */
76    public static final int INVALID_TYPE = -1;
77  
78    /** Simple type defined by the user. */
79    public static final int USER_TYPE = 0;
80  
81    // Primitive types
82    public static final int STRING_TYPE = 1;
83    public static final int DURATION_TYPE = 2;
84    public static final int DATETIME_TYPE = 3;
85    public static final int TIME_TYPE = 4;
86    public static final int DATE_TYPE = 5;
87    public static final int GYEARMONTH_TYPE = 6;
88    public static final int GYEAR_TYPE = 7;
89    public static final int GMONTHDAY_TYPE = 8;
90    public static final int GDAY_TYPE = 9;
91    public static final int GMONTH_TYPE = 10;
92    public static final int BOOLEAN_TYPE = 11;
93    public static final int BASE64BINARY_TYPE = 12;
94    public static final int HEXBINARY_TYPE = 13;
95    public static final int FLOAT_TYPE = 14;
96    public static final int DOUBLE_TYPE = 15;
97    public static final int DECIMAL_TYPE = 16;
98    public static final int ANYURI_TYPE = 17;
99    public static final int QNAME_TYPE = 18;
100   public static final int NOTATION_TYPE = 19;
101   // Derived datatypes
102   public static final int NORMALIZEDSTRING_TYPE = 20;
103   public static final int TOKEN_TYPE = 21;
104   public static final int LANGUAGE_TYPE = 22;
105   public static final int NAME_TYPE = 23;
106   public static final int NCNAME_TYPE = 24;
107   public static final int ID_TYPE = 25;
108   public static final int IDREF_TYPE = 26;
109   public static final int IDREFS_TYPE = 27;
110   public static final int ENTITY_TYPE = 28;
111   public static final int ENTITIES_TYPE = 29;
112   public static final int NMTOKEN_TYPE = 30;
113   public static final int NMTOKENS_TYPE = 31;
114   public static final int INTEGER_TYPE = 32;
115   public static final int NON_POSITIVE_INTEGER_TYPE = 33;
116   public static final int NEGATIVE_INTEGER_TYPE = 34;
117   public static final int LONG_TYPE = 35;
118   public static final int INT_TYPE = 36;
119   public static final int SHORT_TYPE = 37;
120   public static final int BYTE_TYPE = 38;
121   public static final int NON_NEGATIVE_INTEGER_TYPE = 39;
122   public static final int UNSIGNED_LONG_TYPE = 40;
123   public static final int UNSIGNED_INT_TYPE = 41;
124   public static final int UNSIGNED_SHORT_TYPE = 42;
125   public static final int UNSIGNED_BYTE_TYPE = 43;
126   public static final int POSITIVE_INTEGER_TYPE = 44;
127 
128   public static final int ANYSIMPLETYPE_TYPE = 100;
129 
130   /** The resource location for the built-in types property files. */
131   static final String RESOURCE_LOCATION = "/org/exolab/castor/util/resources/";
132 
133   /** The resource for the mapping properties. */
134   static final String TYPE_MAPPINGS = RESOURCE_LOCATION + "SimpleTypesMapping.properties";
135 
136   /** The resource for the Simple types. */
137   static final String TYPE_DEFINITIONS = RESOURCE_LOCATION + "SimpleTypes.properties";
138 
139   /**
140    * Holds simpletypesfactory.Type instances that record information about XML schema built in
141    * types.
142    */
143   private static Map<String, Type> _typesByName = new Hashtable<String, Type>();
144 
145   /**
146    * Cross index for _typesByName to quickly get type information from its code.
147    */
148   private static Map<Integer, Type> _typesByCode = new Hashtable<Integer, Type>();
149 
150   /** The built-in schema, hopefully only temporary. */
151   private static final Schema BUILD_IN_SCHEMA = new Schema();
152 
153   /**
154    * Creates an instance of {@link SimpleTypesFactory}, loading type definition information from the
155    * relevant files.
156    */
157   public SimpleTypesFactory() {
158     loadTypesDefinitions();
159   }
160 
161   /**
162    * Indicates if a type code corresponds to an xml schema built in type.
163    * 
164    * @param codeType The type code to check.
165    * @return True if the given type code represents an XML schema built-in type.
166    */
167   public static boolean isBuiltInType(final int codeType) {
168     return USER_TYPE < codeType;
169   }
170 
171   /**
172    * Tells if a type code corresponds to an xml schema (built-in) primitive type.
173    * 
174    * @param codeType The type code to check.
175    * @return True if the given type code represents an XML schema built-in primitive type.
176    */
177   public static boolean isPrimitiveType(final int codeType) {
178     return (STRING_TYPE <= codeType) && (codeType <= NOTATION_TYPE);
179   }
180 
181   /**
182    * Tells if a type code corresponds to an xml schema (built-in) numeric type.
183    * 
184    * @param codeType The type code to check.
185    * @return True if the given type code represents an XML schema built-in numeric type.
186    */
187   public static boolean isNumericType(final int codeType) {
188     return ((FLOAT_TYPE <= codeType) && (codeType <= DECIMAL_TYPE))
189         || ((INTEGER_TYPE <= codeType) && (codeType <= POSITIVE_INTEGER_TYPE));
190   }
191 
192   /**
193    * Tells if a type code corresponds to an xml schema (built-in) date/time type.
194    * 
195    * @param codeType The type code to check.
196    * @return True if the given type code represents an XML schema built-in date/time type.
197    */
198   public static boolean isDateTimeType(final int codeType) {
199     return (DURATION_TYPE <= codeType) && (codeType <= GMONTH_TYPE);
200   }
201 
202 
203   /**
204    * Gets an instance of a class derived from {@link SimpleType} representing the built in type
205    * which name is given as a parameter.
206    * 
207    * @param typeName Name of the simple type.
208    * @return The {@link SimpleType} instance for the type name.
209    */
210   public SimpleType getBuiltInType(final String typeName) {
211     Type type = getType(typeName);
212     if (type == null) {
213       return null;
214     }
215     return type.getSimpleType();
216   }
217 
218   /**
219    * Gets a built in type's name given its code.
220    */
221   public String getBuiltInTypeName(final int builtInTypeCode) {
222     Type type = getType(builtInTypeCode);
223     if (type == null) {
224       return null;
225     }
226     return type.getName();
227   }
228 
229 
230   /**
231    * Creates an instance of a class derived from SimpleType, representing the user type defined by
232    * the given name, baseName and derivation method.
233    *
234    * Package private (used by Schema and DeferredSimpleType).
235    *
236    * The given schema is used as the owning Schema document, yet a call to schema.addSimpleType must
237    * till be made to add the SimpleType to the Schema.
238    *
239    * If the base type is not found in the schema, a DeferredSimpleType will be returned if
240    * createDeferredSimpleType is true, null otherwise.
241    *
242    * @param schema the owning schema
243    * @param name the name of the SimpleType
244    * @param baseName the name of the SimpleType's base type
245    * @param derivation the name of the derivation method (null/""/"list"/"restriction")
246    * @param createDeferredSimpleType should the type be deferred if it can't be created.
247    * @return the new SimpleType, or null if its parent could not be found.
248    **/
249   SimpleType createUserSimpleType(final Schema schema, final String name, final String baseName,
250       final String derivation, final boolean createDeferredSimpleType) {
251     if ((baseName == null) || (baseName.length() == 0)) {
252       // We need a base type name...
253       LOG.warn(Messages.format("schema.noBaseType", name));
254       return null;
255     }
256 
257     // Find the base type
258     SimpleType baseType = schema.getSimpleType(baseName);
259     if (baseType == null) {
260       // couldn't find the base type, must be forward declared
261       if (createDeferredSimpleType) { // => create a DeferredSimpleType
262         DeferredSimpleType result = new DeferredSimpleType();
263         result.setSchema(schema);
264         result.setName(name);
265         result.setBaseTypeName(baseName);
266         result.setDerivationMethod(derivation);
267         result.setTypeCode(USER_TYPE);
268         return result;
269       }
270       return null;
271     }
272 
273     return createUserSimpleType(schema, name, baseType, derivation);
274   }
275 
276   /**
277    * Creates an instance of a class derived from SimpleType, representing the user type defined by
278    * the given name, baseName and derivation method.
279    *
280    * Package private (used by Schema and DeferredSimpleType).
281    *
282    * The given schema is used as the owning Schema document, yet a call to schema#addSimpleType must
283    * still be made to add the SimpleType to the Schema if the SimpleType is not anonymous.
284    *
285    * If the base type is not found in the schema, a DeferredSimpleType will be returned if
286    * createDeferredSimpleType is true, null otherwise.
287    *
288    * @param schema the owning schema
289    * @param name the name of the SimpleType
290    * @param baseType the base type
291    * @param derivation the name of the derivation method (null/""/"list"/"restriction")
292    * @return the new SimpleType, or null if its parent could not be found.
293    **/
294   SimpleType createUserSimpleType(final Schema schema, final String name, final SimpleType baseType,
295       final String derivation) {
296     String internalName = name;
297     if (name == null) {
298       internalName = "anonymous-simple-type";
299     }
300 
301     if (baseType == null) {
302       // We need a base type
303       LOG.warn(Messages.format("schema.noBaseType", internalName));
304       return null;
305     }
306 
307     SimpleType result = null;
308 
309     if ((derivation != null) && (derivation.equals(SchemaNames.LIST))) {
310       // derive as list
311       /*
312        * This doesn't seem valid based on the XML Schema Recommendation, so I am commenting it out
313        * (kvisco) if ( !(baseType instanceof AtomicType) ) { //only lists of atomic values are
314        * allowed by the specification sendToLog( Messages.format("schema.deriveByListError",
315        * internalName, baseType.getName()) ); return null; }
316        */
317       try {
318         result = new ListType(schema);
319       } catch (SchemaException sx) {
320         LOG.warn(Messages.format("schema.deriveByListError", internalName, baseType.getName()));
321         return null;
322       }
323       ((ListType) result).setItemType(baseType);
324     } else {
325       // derive as restriction (only derivation allowed apart from list for simple types)
326 
327       if (baseType instanceof Union) {
328         // Stoil Valchkov (stoill@mail.bg) - this is my fix for union extension
329         // if we have union - handle it here. it wont be found as buildInBaseType
330         try {
331           result = new Union(schema);
332         } catch (SchemaException sx) {
333           LOG.warn(Messages.format("schema.deriveByListError", internalName, baseType.getName()));
334           return null;
335         }
336       } else {
337         // Find the built in ancestor type
338         SimpleType builtInBase = baseType.getBuiltInBaseType();
339         if (builtInBase == null) {
340           LOG.warn(Messages.format("schema.noBuiltInParent", internalName));
341           return null;
342         }
343         // creates the instance of a class derived from SimpleType representing the new type.
344         result = createInstance(schema, builtInBase.getName());
345         if (result == null) {
346           throw new SimpleTypesFactoryException(Messages.message("schema.cantLoadBuiltInTypes"));
347         }
348       }
349     }
350 
351     result.setSchema(schema);
352     result.setName(name);
353     result.setBaseType(baseType);
354     result.setDerivationMethod(derivation);
355     result.setTypeCode(USER_TYPE);
356     return result;
357   }
358 
359   /**
360    * Gets the informations about the built in type which name is provided as input parameter. Loads
361    * the types definitions if they were not yet loaded
362    */
363   private Type getType(final String typeName) {
364     return _typesByName.get(typeName);
365   }
366 
367   /**
368    * Gets the informations about the built in type which code is provided as input parameter. Loads
369    * the types definitions if they were not yet loaded
370    */
371   private Type getType(final int typeCode) {
372     return _typesByCode.get(Integer.valueOf(typeCode));
373   }
374 
375   /**
376    * Loads the built in type definitions from their xml file and its mapping file into the static
377    * fields typesByName and typeByCode. Loading is done only once.
378    */
379   private synchronized void loadTypesDefinitions() {
380     InputStream is = null;
381 
382     try { // Load the mapping file
383       Mapping mapping = new Mapping(getClass().getClassLoader());
384 
385       is = this.getClass().getResourceAsStream(TYPE_MAPPINGS);
386       mapping.loadMapping(new InputSource(is));
387 
388       // unmarshall the list of built in simple types
389       Unmarshaller unmarshaller = new Unmarshaller(TypeList.class);
390       unmarshaller.setMapping(mapping);
391       // -- turn off validation
392       unmarshaller.setValidation(false);
393 
394       is = this.getClass().getResourceAsStream(TYPE_DEFINITIONS);
395       TypeList typeList = (TypeList) unmarshaller.unmarshal(new org.xml.sax.InputSource(is));
396 
397       if (LOG.isTraceEnabled()) {
398         LOG.trace(typeList.toString());
399       }
400 
401       // Store the types by name in the typesByName and typesByCode hashtables
402       // and create for each its associated SimpleType instance.
403       for (Type type : typeList.getTypes()) {
404         _typesByName.put(type.getName(), type);
405         type.setSimpleType(createSimpleType(BUILD_IN_SCHEMA, type));
406         _typesByCode.put(Integer.valueOf(type.getSimpleType().getTypeCode()), type);
407       }
408     } catch (Exception except) {
409       // Of course, this should not happen if the config files are there.
410       String err = Messages.message("schema.cantLoadBuiltInTypes") + "; " + except;
411       throw new SimpleTypesFactoryException(except, err);
412     }
413   }
414 
415 
416   /**
417    * Creates a SimpleType, valid only for built in types.
418    */
419   private SimpleType createSimpleType(final Schema schema, final Type type) {
420     // Creates the instance of a class derived from SimpleType representing the type.
421     SimpleType result = createInstance(schema, type.getName());
422 
423     if (result == null) {
424       String err = Messages.message("schema.cantLoadBuiltInTypes");
425       throw new SimpleTypesFactoryException(err);
426     }
427 
428     result.setName(type.getName());
429 
430     // Load the result's typeCode
431     int intCode;
432     try {
433       intCode = getClass().getDeclaredField(type.getCode()).getInt(null);
434     } catch (Exception ex) {
435 
436       String error = Messages.message("schema.cantLoadBuiltInTypes") + ex;
437       throw new SimpleTypesFactoryException(ex, error);
438     }
439 
440     result.setTypeCode(intCode);
441 
442     // Find and set the result's SimpleType basetype (if any).
443     if (type.getBase() != null) {
444       result.setBaseType(getType(type.getBase()).getSimpleType());
445     }
446 
447     // Adds the facets to the result
448     Vector<TypeProperty> facets = type.getFacet();
449     FacetFactory facetFactory = FacetFactory.getInstance();
450     for (int index = 0; index < facets.size(); index++) {
451       TypeProperty prop = facets.elementAt(index);
452       if (!prop.getPseudo()) {
453         // adds a "real" facet (defined in the xml specs)
454         Facet facet = facetFactory.createFacet(prop.getName(), prop.getValue());
455         facet.setOwningType(result);
456         result.addFacet(facet);
457       } else {
458         // sets the information linked with the pseudo facet
459         if (RealType.class.isInstance(result)) {
460           RealType realResult = (RealType) result;
461           if (prop.getName().equals("minM")) {
462             realResult.setMinMantissa(Long.parseLong(prop.getValue()));
463           } else if (prop.getName().equals("maxM")) {
464             realResult.setMaxMantissa(Long.parseLong(prop.getValue()));
465           } else if (prop.getName().equals("minE")) {
466             realResult.setMinExponent(Long.parseLong(prop.getValue()));
467           } else if (prop.getName().equals("maxE")) {
468             realResult.setMaxExponent(Long.parseLong(prop.getValue()));
469           }
470         }
471       }
472     }
473     return result;
474   }
475 
476   /**
477    * Creates the correct instance for the given type name. Valid only for built in type names.
478    */
479   private SimpleType createInstance(final Schema schema, final String builtInTypeName) {
480     Type type = getType(builtInTypeName);
481 
482     // If the type is derived by list, return a new ListType.
483     String derivation = type.getDerivedBy();
484     ListType resultList = null;
485     if ((derivation != null) && (derivation.equals(SchemaNames.LIST))) {
486       try {
487         resultList = new ListType(schema);
488       } catch (SchemaException sx) {
489         // -- This should not happen...but who knows!
490         throw new SimpleTypesFactoryException(sx);
491       }
492     }
493 
494     // Finds the primitive ancestor (defines the class that represents it)
495     Class implClass = null;
496     while (type != null) {
497       if (type.getImplClass() != null) {
498         implClass = type.getImplClass();
499         break;
500       }
501       type = getType(type.getBase());
502     }
503 
504     if (implClass == null) {
505       return null;
506     }
507 
508     SimpleType result;
509     if (implClass.isAssignableFrom(UrType.class)) {
510       try {
511         result = (UrType) implClass.newInstance();
512         result.setSchema(schema);
513       } catch (Exception e) {
514         throw new SimpleTypesFactoryException(e);
515       }
516     } else {
517       try {
518         result = (AtomicType) implClass.newInstance();
519         result.setSchema(schema);
520       } catch (Exception except) {
521         except.printStackTrace();
522         result = null;
523       }
524       if (resultList != null) {
525         resultList.setItemType(result);
526         return resultList;
527       }
528     }
529     return result;
530   }
531 }
532 
533 
534 /**
535  * A RuntimeException which allows nested exceptions.
536  *
537  * @author <a href="arkin@intalio.com">Assaf Arkin</a>
538  * @author <a href="kvisco@intalio.com">Keith Visco</a>
539  * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
540  */
541 class SimpleTypesFactoryException extends RuntimeException {
542   /** SerialVersionUID. */
543   private static final long serialVersionUID = -7343397006284999081L;
544 
545   /** The exception which caused this Exception. */
546   private Throwable _exception;
547 
548 
549   /**
550    * Creates a new SimpleTypesFactoryException.
551    *
552    * @param message the error message
553    */
554   public SimpleTypesFactoryException(final String message) {
555     super(message);
556   }
557 
558 
559   /**
560    * Creates a new SimpleTypesFactoryException.
561    *
562    * @param exception the Exception which caused this Exception.
563    */
564   public SimpleTypesFactoryException(final Throwable exception) {
565     super(exception.toString());
566     _exception = exception;
567   }
568 
569   /**
570    * Creates a new SimpleTypesFactoryException.
571    *
572    * @param exception the Exception which caused this Exception.
573    * @param message the error message
574    */
575   public SimpleTypesFactoryException(final Throwable exception, final String message) {
576     super(message);
577     _exception = exception;
578   }
579 
580 
581   /**
582    * Returns the Exception which caused this Exception, or null if no nested exception exists.
583    *
584    * @return the nested Exception.
585    **/
586   public Throwable getException() {
587     return _exception;
588   }
589 
590 
591   public void printStackTrace() {
592     if (_exception == null) {
593       super.printStackTrace();
594     } else {
595       _exception.printStackTrace();
596     }
597   }
598 
599 
600   public void printStackTrace(final PrintStream print) {
601     if (_exception == null) {
602       super.printStackTrace(print);
603     } else {
604       _exception.printStackTrace(print);
605     }
606   }
607 
608 
609   public void printStackTrace(final PrintWriter print) {
610     if (_exception == null) {
611       super.printStackTrace(print);
612     } else {
613       _exception.printStackTrace(print);
614     }
615   }
616 }
617