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 (C) 2005 Keith Visco. All Rights Reserverd.
32   *
33   * $Id$
34   */
35  package org.exolab.castor.builder.factory;
36  
37  import org.exolab.castor.builder.BuilderConfiguration;
38  import org.exolab.castor.builder.info.ClassInfo;
39  import org.exolab.castor.builder.info.CollectionInfo;
40  import org.exolab.castor.builder.info.FieldInfo;
41  import org.exolab.castor.builder.info.NodeType;
42  import org.exolab.castor.builder.info.nature.XMLInfoNature;
43  import org.exolab.castor.builder.types.XSType;
44  import org.exolab.castor.mapping.xml.BindXml;
45  import org.exolab.castor.mapping.xml.ClassChoice;
46  import org.exolab.castor.mapping.xml.ClassMapping;
47  import org.exolab.castor.mapping.xml.FieldMapping;
48  import org.exolab.castor.mapping.xml.MapTo;
49  import org.exolab.castor.mapping.xml.types.BindXmlNodeType;
50  import org.exolab.javasource.JClass;
51  import org.exolab.javasource.JType;
52  
53  /**
54   * A factory for creating mapping files.
55   *
56   * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a>
57   * @version $Revision$ $Date: 2006-04-13 07:37:49 -0600 (Thu, 13 Apr 2006) $
58   */
59  public final class MappingFileSourceFactory {
60  
61    /**
62     * Creates a new MappingFileSourceFactory with the given configuration.
63     *
64     * @param config the BuilderConfiguration instance
65     */
66    public MappingFileSourceFactory(final BuilderConfiguration config) {
67      if (config == null) {
68        String err = "The argument 'config' must not be null.";
69        throw new IllegalArgumentException(err);
70      }
71    } // -- MappingFileSourceFactory
72  
73    /**
74     * Creates the class mapping for the given ClassInfo.
75     *
76     * @param classInfo the XML Schema element declaration
77     * @return the ClassMapping representing the ClassInfo
78     */
79    public ClassMapping createMapping(final ClassInfo classInfo) {
80      JClass jClass = classInfo.getJClass();
81      String className = jClass.getName();
82  
83      ClassMapping classMapping = new ClassMapping();
84      classMapping.setName(className);
85  
86      // -- Set namespace prefix
87      MapTo mapTo = new MapTo();
88      classMapping.setMapTo(mapTo);
89  
90      XMLInfoNature xmlNature = new XMLInfoNature(classInfo);
91  
92      String nsPrefix = xmlNature.getNamespacePrefix();
93      if ((nsPrefix != null) && (nsPrefix.length() > 0)) {
94        mapTo.setNsPrefix(nsPrefix);
95      }
96  
97      // -- Set namespace URI
98      String nsURI = xmlNature.getNamespaceURI();
99      if ((nsURI != null) && (nsURI.length() > 0)) {
100       mapTo.setNsUri(nsURI);
101     }
102 
103     // -- set XML Name
104     mapTo.setXml(xmlNature.getNodeName());
105 
106     // -- set Element Definition flag
107     mapTo.setElementDefinition(xmlNature.isElementDefinition());
108 
109     // -- set grouping compositor
110     if (xmlNature.isChoice()) {
111       // TODO need a way to specify choice in Mapping file
112     }
113 
114     boolean isAbstract = classInfo.isAbstract();
115     if (!isAbstract) {
116       isAbstract = jClass.getModifiers().isAbstract();
117     }
118     classInfo.setAbstract(isAbstract);
119 
120     // -- To prevent compiler warnings...make sure
121     // -- we don't declare temp variables if field count is 0;
122     if (classInfo.getFieldCount() == 0) {
123       return classMapping;
124     }
125 
126     // -- handle content
127     if (classInfo.allowContent()) {
128       createFieldMapping(classMapping, classInfo.getTextField(), null);
129     }
130 
131     ClassInfo base = classInfo.getBaseClass();
132     if (base != null) {
133       classMapping.setExtends(base.getJClass().getName());
134     }
135 
136     FieldInfo[] atts = classInfo.getAttributeFields();
137 
138     // -----------------------------/
139     // - Create attribute mappings -/
140     // -----------------------------/
141 
142     for (FieldInfo member : atts) {
143       // -- skip transient members
144       if (member.isTransient()) {
145         continue;
146       }
147 
148       // -- skip inherited fields
149       if (base != null && base.getAttributeField(xmlNature.getNodeName()) != null) {
150         continue;
151       }
152 
153       createFieldMapping(classMapping, member, nsURI);
154     }
155 
156     // ---------------------------/
157     // - Create element mappings -/
158     // ---------------------------/
159 
160     FieldInfo[] elements = classInfo.getElementFields();
161     for (FieldInfo member : elements) {
162       // -- skip transient members
163       if (member.isTransient()) {
164         continue;
165       }
166 
167       // -- skip inherited fields
168       if (base != null && base.getElementField(xmlNature.getNodeName()) != null) {
169         continue;
170       }
171 
172       createFieldMapping(classMapping, member, nsURI);
173     }
174 
175     return classMapping;
176   } // -- createClassMapping
177 
178   // -------------------/
179   // - Private Methods -/
180   // -------------------/
181 
182   /**
183    * Creates a FieldMapping for a given member and adds it to the given ClassMapping.
184    * 
185    * @param classMapping the ClassMapping we are adding a new FieldMapping to
186    * @param member member for which we are creating a FieldMapping
187    * @param nsURI name space URI
188    */
189   private void createFieldMapping(final ClassMapping classMapping, final FieldInfo member,
190       final String nsURI) {
191     XMLInfoNature xmlNature = new XMLInfoNature(member);
192 
193     XSType xsType = xmlNature.getSchemaType();
194 
195     boolean any = false;
196     NodeType nodeType = xmlNature.getNodeType();
197     boolean isAttribute = (nodeType == NodeType.ATTRIBUTE);
198     boolean isText = (nodeType == NodeType.TEXT);
199 
200     // -- a hack, I know, I will change later (kv)
201     if (member.getName().equals("_anyObject")) {
202       any = true;
203     }
204 
205     // Attributes can handle COLLECTION type for NMTOKENS or IDREFS for instance
206     if (xsType.isCollection()) {
207       xsType = new XMLInfoNature(((CollectionInfo) member).getContent()).getSchemaType();
208     }
209 
210     // -- create class choice on demand
211     ClassChoice classChoice = classMapping.getClassChoice();
212     if (classChoice == null) {
213       classChoice = new ClassChoice();
214       classMapping.setClassChoice(classChoice);
215     }
216 
217     // -- create field mapping
218     FieldMapping fieldMap = new FieldMapping();
219     classChoice.addFieldMapping(fieldMap);
220     String fieldName = member.getName();
221     if (fieldName.charAt(0) == '_') {
222       fieldName = fieldName.substring(1);
223     }
224     fieldMap.setName(fieldName);
225     String className = getClassName(xsType.getJType());
226     if (className.equals("byte[]")) {
227       className = "bytes";
228     }
229     fieldMap.setType(className);
230 
231     BindXml bindXml = new BindXml();
232     fieldMap.setBindXml(bindXml);
233 
234     String nodeName = xmlNature.getNodeName();
235     if ((nodeName != null) && (!isText)) {
236       bindXml.setName(nodeName);
237     }
238 
239     if (isAttribute) {
240       bindXml.setNode(BindXmlNodeType.ATTRIBUTE);
241     } else if (isText) {
242       bindXml.setNode(BindXmlNodeType.TEXT);
243     } else {
244       bindXml.setNode(BindXmlNodeType.ELEMENT);
245     }
246 
247     switch (xsType.getType()) {
248       case XSType.IDREF_TYPE:
249         bindXml.setReference(true);
250         break;
251       case XSType.ID_TYPE:
252         classMapping.addIdentity(member.getName());
253         break;
254       case XSType.QNAME_TYPE:
255         bindXml.setType("QName");
256       default:
257         break;
258     }
259 
260     // -- set any user-specified field handler
261     fieldMap.setHandler(member.getXMLFieldHandler());
262 
263     // -- container
264     if (member.isContainer()) {
265       fieldMap.setContainer(true);
266     }
267 
268     // Handle namespaces. FieldInfo namespace has higher priority than ClassInfo namespace.
269     // TODO Need to add better namespace support to bind-xml element,
270     // it's not very good at the moment
271     // nsURI = member.getNamespaceURI();
272     // if (nsURI != null) {
273     // jsc.add("desc.setNameSpaceURI(\"");
274     // jsc.append(nsURI);
275     // jsc.append("\");");
276     // }
277     //
278     // if (any && member.getNamespaceURI() == null) {
279     // nsURI = null;
280     // }
281 
282     // required
283     if (xmlNature.isRequired()) {
284       fieldMap.setRequired(true);
285     }
286 
287     // nillable
288     if (member.isNillable()) {
289       // TODO Mapping file needs nillable support!
290     }
291 
292     // if any it can match all the names
293     if (any) {
294       bindXml.setMatches("*");
295     }
296 
297     // Add Validation Code
298     // TODO mapping file has no validation support so users need to use xsi:schemaLocation
299     // in their XML instances and enable schema validation on the parser
300   }
301 
302   /**
303    * Returns the classname for the given JType. The class name may be the fully qualified classname
304    * or the mapping loader "short" name.
305    *
306    * @param jType the JType for which to return the class name
307    * @return the classname for the given XSType
308    */
309   private static String getClassName(final JType jType) {
310     // TODO Look up short names from: org.exolab.castor.mapping.loader.Types
311 
312     if (jType.isPrimitive()) {
313       return jType.getName();
314     }
315     return jType.toString();
316   }
317 }