001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.tools.xjc.generator.bean;
038:
039: import java.util.Collection;
040: import java.util.HashMap;
041: import java.util.Map;
042:
043: import javax.xml.bind.JAXBException;
044: import javax.xml.bind.annotation.XmlInlineBinaryData;
045: import javax.xml.namespace.QName;
046:
047: import com.sun.codemodel.JClass;
048: import com.sun.codemodel.JCodeModel;
049: import com.sun.codemodel.JDefinedClass;
050: import com.sun.codemodel.JExpr;
051: import com.sun.codemodel.JExpression;
052: import com.sun.codemodel.JFieldVar;
053: import com.sun.codemodel.JInvocation;
054: import com.sun.codemodel.JMethod;
055: import com.sun.codemodel.JMod;
056: import com.sun.codemodel.JPackage;
057: import com.sun.codemodel.JType;
058: import com.sun.codemodel.JVar;
059: import com.sun.tools.xjc.generator.annotation.spec.XmlElementDeclWriter;
060: import com.sun.tools.xjc.generator.annotation.spec.XmlRegistryWriter;
061: import com.sun.tools.xjc.model.CElementInfo;
062: import com.sun.tools.xjc.model.CPropertyInfo;
063: import com.sun.tools.xjc.model.Constructor;
064: import com.sun.tools.xjc.model.Model;
065: import com.sun.tools.xjc.outline.Aspect;
066: import com.sun.tools.xjc.outline.FieldAccessor;
067: import com.sun.tools.xjc.outline.FieldOutline;
068: import com.sun.xml.bind.v2.TODO;
069:
070: /**
071: * Generates <code>ObjectFactory</code> then wraps it and provides
072: * access to it.
073: *
074: * <p>
075: * The ObjectFactory contains
076: * factory methods for each schema derived content class
077: *
078: * @author
079: * Ryan Shoemaker
080: */
081: abstract class ObjectFactoryGeneratorImpl extends
082: ObjectFactoryGenerator {
083:
084: private final BeanGenerator outline;
085: private final Model model;
086: private final JCodeModel codeModel;
087: /**
088: * Ref to {@link Class}.
089: */
090: private final JClass classRef;
091:
092: /**
093: * Reference to the generated ObjectFactory class.
094: */
095: private final JDefinedClass objectFactory;
096:
097: /** map of qname to the QName constant field. */
098: private final HashMap<QName, JFieldVar> qnameMap = new HashMap<QName, JFieldVar>();
099:
100: /**
101: * Names of the element factory methods that are created.
102: * Used to detect collisions.
103: *
104: * The value is used for reporting error locations.
105: */
106: private final Map<String, CElementInfo> elementFactoryNames = new HashMap<String, CElementInfo>();
107:
108: /**
109: * Names of the value factory methods that are created.
110: * Used to detect collisions.
111: *
112: * The value is used for reporting error locations.
113: */
114: private final Map<String, ClassOutlineImpl> valueFactoryNames = new HashMap<String, ClassOutlineImpl>();
115:
116: /**
117: * Returns a reference to the generated (public) ObjectFactory
118: */
119: public JDefinedClass getObjectFactory() {
120: return objectFactory;
121: }
122:
123: public ObjectFactoryGeneratorImpl(BeanGenerator outline,
124: Model model, JPackage targetPackage) {
125: this .outline = outline;
126: this .model = model;
127: this .codeModel = this .model.codeModel;
128: this .classRef = codeModel.ref(Class.class);
129:
130: // create the ObjectFactory class skeleton
131: objectFactory = this .outline.getClassFactory().createClass(
132: targetPackage, "ObjectFactory", null);
133: objectFactory.annotate2(XmlRegistryWriter.class);
134:
135: // generate the default constructor
136: //
137: // m1 result:
138: // public ObjectFactory() {}
139: JMethod m1 = objectFactory.constructor(JMod.PUBLIC);
140: m1
141: .javadoc()
142: .append(
143: "Create a new ObjectFactory that can be used to "
144: + "create new instances of schema derived classes "
145: + "for package: "
146: + targetPackage.name());
147:
148: // add some class javadoc
149: objectFactory
150: .javadoc()
151: .append(
152: "This object contains factory methods for each \n"
153: + "Java content interface and Java element interface \n"
154: + "generated in the "
155: + targetPackage.name()
156: + " package. \n"
157: + "<p>An ObjectFactory allows you to programatically \n"
158: + "construct new instances of the Java representation \n"
159: + "for XML content. The Java representation of XML \n"
160: + "content can consist of schema derived interfaces \n"
161: + "and classes representing the binding of schema \n"
162: + "type definitions, element declarations and model \n"
163: + "groups. Factory methods for each of these are \n"
164: + "provided in this class.");
165:
166: }
167:
168: /**
169: * Adds code for the given {@link CElementInfo} to ObjectFactory.
170: */
171: protected final void populate(CElementInfo ei, Aspect impl,
172: Aspect exposed) {
173: JType exposedElementType = ei.toType(outline, exposed);
174: JType exposedType = ei.getContentInMemoryType().toType(outline,
175: exposed);
176: JType implType = ei.getContentInMemoryType().toType(outline,
177: impl);
178: String namespaceURI = ei.getElementName().getNamespaceURI();
179: String localPart = ei.getElementName().getLocalPart();
180:
181: JClass scope = null;
182: if (ei.getScope() != null)
183: scope = outline.getClazz(ei.getScope()).implClass;
184:
185: JMethod m;
186:
187: if (ei.isAbstract()) {
188: // TODO: see the "Abstract elements and mighty IXmlElement" e-mail
189: // that I sent to jaxb-tech
190: TODO.checkSpec();
191: }
192:
193: {// collision check
194: CElementInfo existing = elementFactoryNames.put(ei
195: .getSqueezedName(), ei);
196: if (existing != null) {
197: outline.getErrorReceiver().error(
198: existing.getLocator(),
199: Messages.OBJECT_FACTORY_CONFLICT.format(ei
200: .getSqueezedName()));
201: outline.getErrorReceiver().error(
202: ei.getLocator(),
203: Messages.OBJECT_FACTORY_CONFLICT_RELATED
204: .format());
205: return;
206: }
207: }
208:
209: // no arg constructor
210: // [RESULT] if the element doesn't have its own class, something like:
211: //
212: // @XmlElementMapping(uri = "", name = "foo")
213: // public JAXBElement<Foo> createFoo( Foo value ) {
214: // return new JAXBElement<Foo>(
215: // new QName("","foo"),(Class)FooImpl.class,scope,(FooImpl)value);
216: // }
217: // NOTE: when we generate value classes Foo==FooImpl
218: //
219: // [RESULT] otherwise
220: //
221: // @XmlElementMapping(uri = "", name = "foo")
222: // public Foo createFoo( FooType value ) {
223: // return new Foo((FooTypeImpl)value);
224: // }
225: // NOTE: when we generate value classes FooType==FooTypeImpl
226: //
227: // to deal with
228: // new JAXBElement<List<String>>( ..., List.class, ... );
229: // we sometimes have to produce (Class)List.class instead of just List.class
230:
231: m = objectFactory.method(JMod.PUBLIC, exposedElementType,
232: "create" + ei.getSqueezedName());
233: JVar $value = m.param(exposedType, "value");
234:
235: JExpression declaredType;
236: if (implType.boxify().isParameterized()
237: || !exposedType.equals(implType))
238: declaredType = JExpr.cast(classRef, implType.boxify()
239: .dotclass());
240: else
241: declaredType = implType.boxify().dotclass();
242: JExpression scopeClass = scope == null ? JExpr._null() : scope
243: .dotclass();
244:
245: // build up the return extpression
246: JInvocation exp = JExpr._new(exposedElementType);
247: if (!ei.hasClass()) {
248: exp.arg(getQNameInvocation(ei));
249: exp.arg(declaredType);
250: exp.arg(scopeClass);
251: }
252: if (implType == exposedType)
253: exp.arg($value);
254: else
255: exp.arg(JExpr.cast(implType, $value));
256:
257: m.body()._return(exp);
258:
259: m.javadoc().append("Create an instance of ").append(
260: exposedElementType).append("}");
261:
262: XmlElementDeclWriter xemw = m
263: .annotate2(XmlElementDeclWriter.class);
264: xemw.namespace(namespaceURI).name(localPart);
265: if (scope != null)
266: xemw.scope(scope);
267:
268: if (ei.getSubstitutionHead() != null) {
269: QName n = ei.getSubstitutionHead().getElementName();
270: xemw.substitutionHeadNamespace(n.getNamespaceURI());
271: xemw.substitutionHeadName(n.getLocalPart());
272: }
273:
274: if (ei.getDefaultValue() != null)
275: xemw.defaultValue(ei.getDefaultValue());
276:
277: if (ei.getProperty().inlineBinaryData())
278: m.annotate(XmlInlineBinaryData.class);
279:
280: // if the element is adapter, put that annotation on the factory method
281: outline.generateAdapterIfNecessary(ei.getProperty(), m);
282: }
283:
284: /**
285: * return a JFieldVar that represents the QName field for the given information.
286: *
287: * if it doesn't exist, create a static field in the class and store a new JFieldVar.
288: */
289: private JExpression getQNameInvocation(CElementInfo ei) {
290: QName name = ei.getElementName();
291: if (qnameMap.containsKey(name)) {
292: return qnameMap.get(name);
293: }
294:
295: if (qnameMap.size() > 1024)
296: // stop gap measure to avoid 'code too large' error in javac.
297: return createQName(name);
298:
299: // [RESULT]
300: // private static final QName _XYZ_NAME = new QName("uri", "local");
301: JFieldVar qnameField = objectFactory.field(JMod.PRIVATE
302: | JMod.STATIC | JMod.FINAL, QName.class, '_'
303: + ei.getSqueezedName() + "_QNAME", createQName(name));
304:
305: qnameMap.put(name, qnameField);
306:
307: return qnameField;
308: }
309:
310: /**
311: * Generates an expression that evaluates to "new QName(...)"
312: */
313: private JInvocation createQName(QName name) {
314: return JExpr._new(codeModel.ref(QName.class)).arg(
315: name.getNamespaceURI()).arg(name.getLocalPart());
316: }
317:
318: protected final void populate(ClassOutlineImpl cc, JClass sigType) {
319: // add static factory method for this class to JAXBContext.
320: //
321: // generate methods like:
322: // public static final SIGTYPE createFoo() {
323: // return new FooImpl();
324: // }
325:
326: if (!cc.target.isAbstract()) {
327: JMethod m = objectFactory.method(JMod.PUBLIC, sigType,
328: "create" + cc.target.getSqueezedName());
329: m.body()._return(JExpr._new(cc.implRef));
330:
331: // add some jdoc to avoid javadoc warnings in jdk1.4
332: m.javadoc().append("Create an instance of ").append(cc.ref);
333: }
334:
335: // add static factory methods for all the other constructors.
336: Collection<? extends Constructor> consl = cc.target
337: .getConstructors();
338: if (consl.size() != 0) {
339: // if we are going to add constructors with parameters,
340: // first we need to have a default constructor.
341: cc.implClass.constructor(JMod.PUBLIC);
342: }
343:
344: {// collision check
345: String name = cc.target.getSqueezedName();
346: ClassOutlineImpl existing = valueFactoryNames.put(name, cc);
347: if (existing != null) {
348: outline.getErrorReceiver().error(
349: existing.target.getLocator(),
350: Messages.OBJECT_FACTORY_CONFLICT.format(name));
351: outline.getErrorReceiver().error(
352: cc.target.getLocator(),
353: Messages.OBJECT_FACTORY_CONFLICT_RELATED
354: .format());
355: return;
356: }
357: }
358:
359: for (Constructor cons : consl) {
360: // method on ObjectFactory
361: // [RESULT]
362: // Foo createFoo( T1 a, T2 b, T3 c, ... ) throws JAXBException {
363: // return new FooImpl(a,b,c,...);
364: // }
365: JMethod m = objectFactory.method(JMod.PUBLIC, cc.ref,
366: "create" + cc.target.getSqueezedName());
367: JInvocation inv = JExpr._new(cc.implRef);
368: m.body()._return(inv);
369:
370: // let's not throw this exception.
371: // m._throws(codeModel.ref(JAXBException.class));
372:
373: // add some jdoc to avoid javadoc warnings in jdk1.4
374: m.javadoc().append("Create an instance of ").append(cc.ref)
375: .addThrows(JAXBException.class).append(
376: "if an error occurs");
377:
378: // constructor
379: // [RESULT]
380: // FooImpl( T1 a, T2 b, T3 c, ... ) {
381: // }
382: JMethod c = cc.implClass.constructor(JMod.PUBLIC);
383:
384: for (String fieldName : cons.fields) {
385: CPropertyInfo field = cc.target.getProperty(fieldName);
386: if (field == null) {
387: outline.getErrorReceiver().error(
388: cc.target.getLocator(),
389: Messages.ILLEGAL_CONSTRUCTOR_PARAM
390: .format(fieldName));
391: continue;
392: }
393:
394: fieldName = camelize(fieldName);
395:
396: FieldOutline fo = outline.getField(field);
397: FieldAccessor accessor = fo.create(JExpr._this ());
398:
399: // declare a parameter on this factory method and set
400: // it to the field
401: inv.arg(m.param(fo.getRawType(), fieldName));
402:
403: JVar $var = c.param(fo.getRawType(), fieldName);
404: accessor.fromRawValue(c.body(), '_' + fieldName, $var);
405: }
406: }
407: }
408:
409: /** Change the first character to the lower case. */
410: private static String camelize(String s) {
411: return Character.toLowerCase(s.charAt(0)) + s.substring(1);
412: }
413: }
|