View Javadoc
1   /*
2    * Copyright 2006 Werner Guttmann
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.exolab.castor.builder.factory;
15  
16  import org.apache.commons.lang3.StringUtils;
17  import org.exolab.castor.builder.BuilderConfiguration;
18  import org.exolab.castor.builder.info.CollectionInfo;
19  import org.exolab.castor.builder.info.FieldInfo;
20  import org.exolab.castor.builder.info.NodeType;
21  import org.exolab.castor.builder.info.nature.XMLInfoNature;
22  import org.exolab.castor.builder.types.XSClass;
23  import org.exolab.castor.builder.types.XSListType;
24  import org.exolab.castor.builder.types.XSType;
25  import org.exolab.javasource.JClass;
26  import org.exolab.javasource.JSourceCode;
27  
28  /**
29   * A factory for creating XMLFieldHandler instances as embedded in descriptors classes generated
30   * throughout code generation.
31   *
32   * @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a>
33   * @version $Revision: 6469 $ $Date: 2006-04-13 07:37:49 -0600 (Thu, 13 Apr 2006) $
34   */
35  public final class XMLFieldHandlerFactory {
36  
37    /**
38     * The XML code generator configuration.
39     */
40    private BuilderConfiguration _config;
41  
42    /**
43     * Creates an instance of this factory.
44     * 
45     * @param config The XML code generator configuration.
46     */
47    public XMLFieldHandlerFactory(final BuilderConfiguration config) {
48      _config = config;
49    }
50  
51    /**
52     * Creates the XMLFieldHandler for the given FieldInfo.
53     *
54     * @param member the member for which to create an XMLFieldHandler
55     * @param xsType the XSType (XML Schema Type) of this field
56     * @param localClassName unqualified (no package) name of this class
57     * @param jsc the source code to which we'll add this XMLFieldHandler
58     * @param forGeneralizedHandler Whether to generate a generalized field handler
59     */
60    public void createXMLFieldHandler(final FieldInfo member, final XSType xsType,
61        final String localClassName, final JSourceCode jsc, final boolean forGeneralizedHandler) {
62  
63      XMLInfoNature xmlNature = new XMLInfoNature(member);
64  
65      boolean any = false;
66      boolean isEnumerated = false;
67  
68      // -- a hack, I know, I will change later (kv)
69      if (member.getName().equals("_anyObject")) {
70        any = true;
71      }
72  
73      if (xsType.getType() == XSType.CLASS) {
74        isEnumerated = ((XSClass) xsType).isEnumerated();
75      }
76  
77      jsc.add("handler = new org.exolab.castor.xml.XMLFieldHandler() {");
78      jsc.indent();
79  
80      createGetValueMethod(member, xsType, localClassName, jsc);
81  
82      NodeType nodeType = xmlNature.getNodeType();
83      boolean isAttribute = (nodeType == NodeType.ATTRIBUTE);
84      boolean isContent = (nodeType == NodeType.TEXT);
85  
86      createSetValueMethod(member, xsType, localClassName, jsc, any, isAttribute, isContent);
87      createResetMethod(member, localClassName, jsc);
88      createNewInstanceMethod(member, xsType, jsc, forGeneralizedHandler, any, isEnumerated);
89  
90      jsc.unindent();
91      jsc.add("};");
92    } // --end of XMLFieldHandler
93  
94    /**
95     * Creates the getValue() method of the corresponsing XMLFieldHandler.
96     *
97     * @param member The member element.
98     * @param xsType The XSType instance
99     * @param jsc The source code to which to append the 'getValue' method.
100    * @param localClassName Name of the object instance as used locally
101    */
102   private void createGetValueMethod(final FieldInfo member, final XSType xsType,
103       final String localClassName, final JSourceCode jsc) {
104     // -- getValue(Object) method
105 
106     XMLInfoNature xmlNature = new XMLInfoNature(member);
107 
108     if (_config.useJava50()) {
109       jsc.add("@Override");
110     }
111     jsc.add("public java.lang.Object getValue( java.lang.Object object ) ");
112     jsc.indent();
113     jsc.add("throws IllegalStateException");
114     jsc.unindent();
115     jsc.add("{");
116     jsc.indent();
117     jsc.add(localClassName);
118     jsc.append(" target = (");
119     jsc.append(localClassName);
120     jsc.append(") object;");
121     // -- handle primitives
122     if ((!xsType.isEnumerated()) && (xsType.getJType().isPrimitive())
123         && (!xmlNature.isMultivalued())) {
124       jsc.add("if (!target." + member.getHasMethodName() + "()) { return null; }");
125     }
126     // -- Return field value
127     jsc.add("return ");
128     String value = "target." + member.getReadMethodName() + "()";
129     if (xmlNature.isMultivalued()) {
130       jsc.append(value); // --Be careful : different for attributes
131     } else {
132       jsc.append(xsType.createToJavaObjectCode(value));
133     }
134     jsc.append(";");
135     jsc.unindent();
136     jsc.add("}");
137     // --end of getValue(Object) method
138   }
139 
140   /**
141    * Creates the setValue() method of the corresponsing XMLFieldHandler.
142    *
143    * @param member The member element.
144    * @param xsType The XSType instance
145    * @param localClassName Name of the object instance as used locally
146    * @param jsc The source code to which to append the 'setValue' method.
147    * @param any Whether to create a setValue() method for &lt;xs:any>
148    * @param isAttribute Whether to create a setValue() method for an attribute.
149    * @param isContent Whether to create a setValue() method for XML content.
150    */
151   private void createSetValueMethod(final FieldInfo member, final XSType xsType,
152       final String localClassName, final JSourceCode jsc, final boolean any,
153       final boolean isAttribute, final boolean isContent) {
154     if (_config.useJava50()) {
155       jsc.add("@Override");
156     }
157     jsc.add("public void setValue( java.lang.Object object, java.lang.Object value) ");
158     jsc.indent();
159     jsc.add("throws IllegalStateException, IllegalArgumentException");
160     jsc.unindent();
161     jsc.add("{");
162     jsc.indent();
163     jsc.add("try {");
164     jsc.indent();
165     jsc.add(localClassName);
166     jsc.append(" target = (");
167     jsc.append(localClassName);
168     jsc.append(") object;");
169     // -- check for null primitives
170 
171     XMLInfoNature xmlNature = new XMLInfoNature(member);
172 
173     if (xsType.isPrimitive() && !_config.usePrimitiveWrapper()) {
174       if ((!xmlNature.isRequired()) && (!xsType.isEnumerated()) && (!xmlNature.isMultivalued())) {
175         jsc.add("// if null, use delete method for optional primitives ");
176         jsc.add("if (value == null) {");
177         jsc.indent();
178         jsc.add("target.");
179         jsc.append(member.getDeleteMethodName());
180         jsc.append("();");
181         jsc.add("return;");
182         jsc.unindent();
183         jsc.add("}");
184       } else {
185         jsc.add("// ignore null values for non optional primitives");
186         jsc.add("if (value == null) { return; }");
187         jsc.add("");
188       }
189     } // if primitive
190 
191     if (xsType.isEnumerated() && StringUtils.isNotEmpty(member.getDefaultValue())) {
192       jsc.add("// default value supplied; as such, do not inject null values");
193       jsc.add("if (value == null) {");
194       jsc.addIndented("return;");
195       jsc.add("}");
196       jsc.add("");
197 
198     }
199     jsc.add("target.");
200     jsc.append(member.getWriteMethodName());
201     jsc.append("( ");
202     if (xsType.isPrimitive() && !_config.usePrimitiveWrapper()) {
203       jsc.append(xsType.createFromJavaObjectCode("value"));
204     } else if (any) {
205       jsc.append(" value ");
206     } else {
207       jsc.append("(");
208       jsc.append(xsType.getJType().toString());
209       // special handling for the type package
210       // when we are dealing with attributes
211       // This is a temporary solution since we need to handle
212       // the 'types' in specific handlers in the future
213       // i.e add specific FieldHandler in org.exolab.castor.xml.handlers
214       // dateTime is not concerned by the following since it is directly
215       // handle by DateFieldHandler
216       if ((isAttribute | isContent) && xsType.isDateTime()
217           && xsType.getType() != XSType.DATETIME_TYPE) {
218         jsc.append(".parse");
219         jsc.append(_config.getJavaNaming().toJavaClassName(xsType.getName()));
220         jsc.append("((java.lang.String) value))");
221       } else {
222         jsc.append(") value");
223       }
224     }
225     jsc.append(");");
226 
227     jsc.unindent();
228     jsc.add("} catch (java.lang.Exception ex) {");
229     jsc.indent();
230     jsc.add("throw new IllegalStateException(ex.toString());");
231     jsc.unindent();
232     jsc.add("}");
233     jsc.unindent();
234     jsc.add("}");
235     // --end of setValue(Object, Object) method
236   }
237 
238   /**
239    * Creates the resetValue() method of the corresponsing XMLFieldHandler.
240    *
241    * @param member The member element.
242    * @param jsc The source code to which to append the 'resetValue' method.
243    * @param localClassName Name of the object instance as used locally
244    */
245   private void createResetMethod(final FieldInfo member, final String localClassName,
246       final JSourceCode jsc) {
247     // -- reset method (handle collections only)
248     if (new XMLInfoNature(member).isMultivalued()) {
249       CollectionInfo cInfo = (CollectionInfo) member;
250       // FieldInfo content = cInfo.getContent();
251       jsc.add("public void resetValue(Object object)"
252           + " throws IllegalStateException, IllegalArgumentException {");
253       jsc.indent();
254       jsc.add("try {");
255       jsc.indent();
256       jsc.add(localClassName);
257       jsc.append(" target = (");
258       jsc.append(localClassName);
259       jsc.append(") object;");
260       String cName = _config.getJavaNaming().toJavaClassName(cInfo.getElementName());
261       // if (cInfo instanceof CollectionInfoJ2) {
262       // jsc.add("target.clear" + cName + "();");
263       // } else {
264       jsc.add("target.removeAll" + cName + "();");
265       // }
266       jsc.unindent();
267       jsc.add("} catch (java.lang.Exception ex) {");
268       jsc.indent();
269       jsc.add("throw new IllegalStateException(ex.toString());");
270       jsc.unindent();
271       jsc.add("}");
272       jsc.unindent();
273       jsc.add("}");
274     }
275     // -- end of reset method
276   }
277 
278   /**
279    * Creates the newInstance() method of the corresponsing XMLFieldHandler.
280    *
281    * @param member The member element.
282    * @param xsType The XSType instance
283    * @param jsc The source code to which to append the 'newInstance' method.
284    * @param forGeneralizedHandler Whether to generate a generalized field handler
285    * @param any Whether to create a newInstance() method for &lt;xs:any>
286    * @param isEnumerated Whether to create a newInstance() method for an enumeration.
287    */
288   private void createNewInstanceMethod(final FieldInfo member, final XSType xsType,
289       final JSourceCode jsc, final boolean forGeneralizedHandler, final boolean any,
290       final boolean isEnumerated) {
291     boolean isAbstract = false;
292 
293     // Commented out according to CASTOR-1340
294     // if (member.getDeclaringClassInfo() != null) {
295     // isAbstract = member.getDeclaringClassInfo().isAbstract();
296     // }
297 
298     // check whether class of member is declared as abstract
299     XMLInfoNature xmlNature = new XMLInfoNature(member);
300 
301     if (xmlNature.getSchemaType() != null
302         && xmlNature.getSchemaType().getJType() instanceof JClass) {
303       JClass jClass = (JClass) xmlNature.getSchemaType().getJType();
304       isAbstract = jClass.getModifiers().isAbstract();
305     }
306 
307     if (!isAbstract && xsType.getJType() instanceof JClass) {
308       JClass jClass = (JClass) xsType.getJType();
309       isAbstract = jClass.getModifiers().isAbstract();
310     }
311 
312     if (!isAbstract && xmlNature.getSchemaType() instanceof XSListType) {
313       XSListType xsList = (XSListType) xmlNature.getSchemaType();
314       if (xsList.getContentType().getJType() instanceof JClass) {
315         JClass componentType = (JClass) xsList.getContentType().getJType();
316         if (componentType.getModifiers().isAbstract()) {
317           isAbstract = componentType.getModifiers().isAbstract();
318         }
319       }
320     }
321 
322     if (_config.useJava50()) {
323       jsc.add("@Override");
324       jsc.add("@SuppressWarnings(\"unused\")");
325     }
326 
327     jsc.add("public java.lang.Object newInstance(java.lang.Object parent) {");
328     jsc.indent();
329     jsc.add("return ");
330 
331     if (any || forGeneralizedHandler || isEnumerated || xsType.isPrimitive()
332         || xsType.getJType().isArray() || (xsType.getType() == XSType.STRING_TYPE) || isAbstract) {
333       jsc.append("null;");
334     } else {
335       jsc.append(xsType.newInstanceCode());
336     }
337 
338     jsc.unindent();
339     jsc.add("}");
340   }
341 
342 }