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 }