View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright (C) 2005 Keith Visco. All Rights Reserverd.
42   *
43   * $Id$
44   */
45  package org.exolab.castor.builder.factory;
46  
47  import org.exolab.castor.builder.BuilderConfiguration;
48  import org.exolab.castor.builder.info.ClassInfo;
49  import org.exolab.castor.builder.info.CollectionInfo;
50  import org.exolab.castor.builder.info.FieldInfo;
51  import org.exolab.castor.builder.info.NodeType;
52  import org.exolab.castor.builder.info.nature.XMLInfoNature;
53  import org.exolab.castor.builder.types.XSType;
54  import org.exolab.castor.mapping.xml.BindXml;
55  import org.exolab.castor.mapping.xml.ClassChoice;
56  import org.exolab.castor.mapping.xml.ClassMapping;
57  import org.exolab.castor.mapping.xml.FieldMapping;
58  import org.exolab.castor.mapping.xml.MapTo;
59  import org.exolab.castor.mapping.xml.types.BindXmlNodeType;
60  import org.exolab.javasource.JClass;
61  import org.exolab.javasource.JType;
62  
63  /**
64   * A factory for creating mapping files.
65   *
66   * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a>
67   * @version $Revision$ $Date: 2006-04-13 07:37:49 -0600 (Thu, 13 Apr 2006) $
68   */
69  public final class MappingFileSourceFactory {
70  
71      /**
72       * Creates a new MappingFileSourceFactory with the given configuration.
73       *
74       * @param config the BuilderConfiguration instance
75       */
76      public MappingFileSourceFactory(final BuilderConfiguration config) {
77          if (config == null) {
78              String err = "The argument 'config' must not be null.";
79              throw new IllegalArgumentException(err);
80          }
81      } //-- MappingFileSourceFactory
82  
83      /**
84       * Creates the class mapping for the given ClassInfo.
85       *
86       * @param classInfo the XML Schema element declaration
87       * @return the ClassMapping representing the ClassInfo
88       */
89      public ClassMapping createMapping(final ClassInfo classInfo) {
90          JClass jClass    = classInfo.getJClass();
91          String className = jClass.getName();
92  
93          ClassMapping classMapping = new ClassMapping();
94          classMapping.setName(className);
95  
96          //-- Set namespace prefix
97          MapTo mapTo = new MapTo();
98          classMapping.setMapTo(mapTo);
99  
100         XMLInfoNature xmlNature = new XMLInfoNature(classInfo);
101 
102         String nsPrefix = xmlNature.getNamespacePrefix();
103         if ((nsPrefix != null) && (nsPrefix.length() > 0)) {
104             mapTo.setNsPrefix(nsPrefix);
105         }
106 
107         //-- Set namespace URI
108         String nsURI = xmlNature.getNamespaceURI();
109         if ((nsURI != null) && (nsURI.length() > 0)) {
110             mapTo.setNsUri(nsURI);
111         }
112 
113         //-- set XML Name
114         mapTo.setXml(xmlNature.getNodeName());
115 
116         //-- set Element Definition flag
117         mapTo.setElementDefinition(xmlNature.isElementDefinition());
118 
119         //-- set grouping compositor
120         if (xmlNature.isChoice()) {
121             // TODO need a way to specify choice in Mapping file
122         }
123 
124         boolean isAbstract = classInfo.isAbstract();
125         if (!isAbstract) {
126             isAbstract = jClass.getModifiers().isAbstract();
127         }
128         classInfo.setAbstract(isAbstract);
129 
130         //-- To prevent compiler warnings...make sure
131         //-- we don't declare temp variables if field count is 0;
132         if (classInfo.getFieldCount() == 0) {
133             return classMapping;
134         }
135 
136         //-- handle  content
137         if (classInfo.allowContent()) {
138             createFieldMapping(classMapping, classInfo.getTextField(), null);
139         }
140 
141         ClassInfo base = classInfo.getBaseClass();
142         if (base != null) {
143             classMapping.setExtends(base.getJClass().getName());
144         }
145 
146         FieldInfo[] atts = classInfo.getAttributeFields();
147 
148         //-----------------------------/
149         //- Create attribute mappings -/
150         //-----------------------------/
151 
152         for (int i = 0; i < atts.length; i++) {
153             FieldInfo member = atts[i];
154             //-- skip transient members
155             if (member.isTransient()) {
156                 continue;
157             }
158 
159             //-- skip inherited fields
160             if (base != null && base.getAttributeField(xmlNature.getNodeName()) != null) {
161                 continue;
162             }
163 
164             createFieldMapping(classMapping, member, nsURI);
165         }
166 
167         //---------------------------/
168         //- Create element mappings -/
169         //---------------------------/
170 
171         FieldInfo[] elements = classInfo.getElementFields();
172         for (int i = 0; i < elements.length; i++) {
173             FieldInfo member = elements[i];
174             //-- skip transient members
175             if (member.isTransient()) {
176                 continue;
177             }
178 
179             //-- skip inherited fields
180             if (base != null && base.getElementField(xmlNature.getNodeName()) != null) {
181                 continue;
182             }
183 
184             createFieldMapping(classMapping, member, nsURI);
185         }
186 
187         return classMapping;
188     } //-- createClassMapping
189 
190     //-------------------/
191     //- Private Methods -/
192     //-------------------/
193 
194     /**
195      * Creates a FieldMapping for a given member and adds it to the given
196      * ClassMapping.
197      * @param classMapping the ClassMapping we are adding a new FieldMapping to
198      * @param member member for which we are creating a FieldMapping
199      * @param nsURI name space URI
200      */
201     private void createFieldMapping(final ClassMapping classMapping, final FieldInfo member,
202                                     final String nsURI) {
203         XMLInfoNature xmlNature = new XMLInfoNature(member);
204         
205         XSType xsType = xmlNature.getSchemaType();
206 
207         boolean any = false;
208         NodeType nodeType = xmlNature.getNodeType();
209         boolean isAttribute = (nodeType == NodeType.ATTRIBUTE);
210         boolean isText = (nodeType == NodeType.TEXT);
211 
212         //-- a hack, I know, I will change later (kv)
213         if (member.getName().equals("_anyObject")) {
214             any = true;
215         }
216 
217         //Attributes can handle COLLECTION type for NMTOKENS or IDREFS for instance
218         if (xsType.isCollection()) {
219             xsType = new XMLInfoNature(((CollectionInfo) member).getContent()).getSchemaType();
220         }
221 
222         //-- create class choice on demand
223         ClassChoice classChoice = classMapping.getClassChoice();
224         if (classChoice == null) { 
225             classChoice = new ClassChoice();
226             classMapping.setClassChoice(classChoice);
227         }
228 
229         //-- create field mapping
230         FieldMapping fieldMap = new FieldMapping();
231         classChoice.addFieldMapping(fieldMap);
232         String fieldName = member.getName();
233         if (fieldName.charAt(0) == '_') {
234             fieldName = fieldName.substring(1);
235         }
236         fieldMap.setName(fieldName);
237         String className = getClassName(xsType.getJType());
238         if (className.equals("byte[]")) {
239             className = "bytes";
240         }
241         fieldMap.setType(className);
242 
243         BindXml bindXml = new BindXml();
244         fieldMap.setBindXml(bindXml);
245 
246         String nodeName = xmlNature.getNodeName();
247         if ((nodeName != null) && (!isText)) {
248             bindXml.setName(nodeName);
249         }
250 
251         if (isAttribute) {
252             bindXml.setNode(BindXmlNodeType.ATTRIBUTE);
253         } else if (isText) {
254             bindXml.setNode(BindXmlNodeType.TEXT);
255         } else {
256             bindXml.setNode(BindXmlNodeType.ELEMENT);
257         }
258 
259         switch (xsType.getType()) {
260             case XSType.IDREF_TYPE :
261                 bindXml.setReference(true);
262                 break;
263             case XSType.ID_TYPE :
264                 classMapping.addIdentity(member.getName());
265                 break;
266             case XSType.QNAME_TYPE :
267                 bindXml.setType("QName");
268             default:
269                 break;
270         }
271 
272         //-- set any user-specified field handler
273         fieldMap.setHandler(member.getXMLFieldHandler());
274 
275         //-- container
276         if (member.isContainer()) {
277             fieldMap.setContainer(true);
278         }
279 
280         // Handle namespaces.  FieldInfo namespace has higher priority than ClassInfo namespace.
281         // TODO Need to add better namespace support to bind-xml element,
282         //      it's not very good at the moment
283 //      nsURI = member.getNamespaceURI();
284 //      if (nsURI != null) {
285 //          jsc.add("desc.setNameSpaceURI(\"");
286 //          jsc.append(nsURI);
287 //          jsc.append("\");");
288 //      }
289 //
290 //      if (any && member.getNamespaceURI() == null) {
291 //          nsURI = null;
292 //      }
293 
294         // required
295         if (xmlNature.isRequired()) {
296             fieldMap.setRequired(true);
297         }
298 
299         // nillable
300         if (member.isNillable()) {
301             // TODO Mapping file needs nillable support!
302         }
303 
304         // if any it can match all the names
305         if (any) {
306             bindXml.setMatches("*");
307         }
308 
309         // Add Validation Code
310         // TODO mapping file has no validation support so users need to use xsi:schemaLocation
311         // in their XML instances and enable schema validation on the parser
312     }
313 
314     /**
315      * Returns the classname for the given JType. The class name may be the
316      * fully qualified classname or the mapping loader "short" name.
317      *
318      * @param jType
319      *            the JType for which to return the class name
320      * @return the classname for the given XSType
321      */
322     private static String getClassName(final JType jType) {
323         // TODO Look up short names from: org.exolab.castor.mapping.loader.Types
324 
325         if (jType.isPrimitive()) {
326             return jType.getName();
327         }
328         return jType.toString();
329     }
330 }