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