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.xml.bind.v2.model.impl;
038:
039: import java.util.HashMap;
040: import java.util.Map;
041:
042: import javax.xml.bind.annotation.XmlAttachmentRef;
043: import javax.xml.bind.annotation.XmlRegistry;
044: import javax.xml.bind.annotation.XmlSchema;
045: import javax.xml.bind.annotation.XmlSeeAlso;
046: import javax.xml.bind.annotation.XmlTransient;
047: import javax.xml.namespace.QName;
048:
049: import com.sun.xml.bind.util.Which;
050: import com.sun.xml.bind.v2.model.annotation.AnnotationReader;
051: import com.sun.xml.bind.v2.model.annotation.ClassLocatable;
052: import com.sun.xml.bind.v2.model.annotation.Locatable;
053: import com.sun.xml.bind.v2.model.core.ClassInfo;
054: import com.sun.xml.bind.v2.model.core.ErrorHandler;
055: import com.sun.xml.bind.v2.model.core.LeafInfo;
056: import com.sun.xml.bind.v2.model.core.NonElement;
057: import com.sun.xml.bind.v2.model.core.PropertyInfo;
058: import com.sun.xml.bind.v2.model.core.PropertyKind;
059: import com.sun.xml.bind.v2.model.core.Ref;
060: import com.sun.xml.bind.v2.model.core.RegistryInfo;
061: import com.sun.xml.bind.v2.model.core.TypeInfo;
062: import com.sun.xml.bind.v2.model.core.TypeInfoSet;
063: import com.sun.xml.bind.v2.model.nav.Navigator;
064: import com.sun.xml.bind.v2.runtime.IllegalAnnotationException;
065: import com.sun.xml.bind.WhiteSpaceProcessor;
066:
067: /**
068: * Builds a {@link TypeInfoSet} (a set of JAXB properties)
069: * by using {@link ElementInfoImpl} and {@link ClassInfoImpl}.
070: * from annotated Java classes.
071: *
072: * <p>
073: * This class uses {@link Navigator} and {@link AnnotationReader} to
074: * work with arbitrary annotation source and arbitrary Java model.
075: * For this purpose this class is parameterized.
076: *
077: * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
078: */
079: public class ModelBuilder<T, C, F, M> {
080:
081: /**
082: * {@link TypeInfo}s that are built will go into this set.
083: */
084: final TypeInfoSetImpl<T, C, F, M> typeInfoSet;
085:
086: public final AnnotationReader<T, C, F, M> reader;
087:
088: public final Navigator<T, C, F, M> nav;
089:
090: /**
091: * Used to detect collisions among global type names.
092: */
093: private final Map<QName, TypeInfo> typeNames = new HashMap<QName, TypeInfo>();
094:
095: /**
096: * JAXB doesn't want to use namespaces unless we are told to, but WS-I BP
097: * conformace requires JAX-RPC to always use a non-empty namespace URI.
098: * (see http://www.ws-i.org/Profiles/BasicProfile-1.0-2004-04-16.html#WSDLTYPES R2105)
099: *
100: * <p>
101: * To work around this issue, we allow the use of the empty namespaces to be
102: * replaced by a particular designated namespace URI.
103: *
104: * <p>
105: * This field keeps the value of that replacing namespace URI.
106: * When there's no replacement, this field is set to "".
107: */
108: public final String defaultNsUri;
109:
110: /**
111: * Packages whose registries are already added.
112: */
113: /*package*/final Map<String, RegistryInfoImpl<T, C, F, M>> registries = new HashMap<String, RegistryInfoImpl<T, C, F, M>>();
114:
115: private final Map<C, C> subclassReplacements;
116:
117: /**
118: * @see #setErrorHandler
119: */
120: private ErrorHandler errorHandler;
121: private boolean hadError;
122:
123: /**
124: * Set to true if the model includes {@link XmlAttachmentRef}. JAX-WS
125: * needs to know this information.
126: */
127: public boolean hasSwaRef;
128:
129: private final ErrorHandler proxyErrorHandler = new ErrorHandler() {
130: public void error(IllegalAnnotationException e) {
131: reportError(e);
132: }
133: };
134:
135: public ModelBuilder(AnnotationReader<T, C, F, M> reader,
136: Navigator<T, C, F, M> navigator,
137: Map<C, C> subclassReplacements, String defaultNamespaceRemap) {
138:
139: this .reader = reader;
140: this .nav = navigator;
141: this .subclassReplacements = subclassReplacements;
142: if (defaultNamespaceRemap == null)
143: defaultNamespaceRemap = "";
144: this .defaultNsUri = defaultNamespaceRemap;
145: reader.setErrorHandler(proxyErrorHandler);
146: typeInfoSet = createTypeInfoSet();
147: }
148:
149: /**
150: * Makes sure that we are running with 2.1 JAXB API,
151: * and report an error if not.
152: */
153: static {
154: try {
155: XmlSchema s = null;
156: s.location();
157: } catch (NullPointerException e) {
158: // as epxected
159: } catch (NoSuchMethodError e) {
160: // this is not a 2.1 API. Where is it being loaded from?
161: Messages res;
162: if (XmlSchema.class.getClassLoader() == null)
163: res = Messages.INCOMPATIBLE_API_VERSION_MUSTANG;
164: else
165: res = Messages.INCOMPATIBLE_API_VERSION;
166:
167: throw new LinkageError(res.format(Which
168: .which(XmlSchema.class), Which
169: .which(ModelBuilder.class)));
170: }
171: }
172:
173: /**
174: * Makes sure that we don't have conflicting 1.0 runtime,
175: * and report an error if we do.
176: */
177: static {
178: try {
179: WhiteSpaceProcessor.isWhiteSpace("xyz");
180: } catch (NoSuchMethodError e) {
181: // we seem to be getting 1.0 runtime
182: throw new LinkageError(Messages.RUNNING_WITH_1_0_RUNTIME
183: .format(Which.which(WhiteSpaceProcessor.class),
184: Which.which(ModelBuilder.class)));
185: }
186: }
187:
188: protected TypeInfoSetImpl<T, C, F, M> createTypeInfoSet() {
189: return new TypeInfoSetImpl<T, C, F, M>(nav, reader,
190: BuiltinLeafInfoImpl.createLeaves(nav));
191: }
192:
193: /**
194: * Builds a JAXB {@link ClassInfo} model from a given class declaration
195: * and adds that to this model owner.
196: *
197: * <p>
198: * Return type is either {@link ClassInfo} or {@link LeafInfo} (for types like
199: * {@link String} or {@link Enum}-derived ones)
200: */
201: public NonElement<T, C> getClassInfo(C clazz, Locatable upstream) {
202: return getClassInfo(clazz, false, upstream);
203: }
204:
205: /**
206: * For limited cases where the caller needs to search for a super class.
207: * This is necessary because we don't want {@link #subclassReplacements}
208: * to kick in for the super class search, which will cause infinite recursion.
209: */
210: public NonElement<T, C> getClassInfo(C clazz,
211: boolean searchForSuperClass, Locatable upstream) {
212: assert clazz != null;
213: NonElement<T, C> r = typeInfoSet.getClassInfo(clazz);
214: if (r != null)
215: return r;
216:
217: if (nav.isEnum(clazz)) {
218: EnumLeafInfoImpl<T, C, F, M> li = createEnumLeafInfo(clazz,
219: upstream);
220: typeInfoSet.add(li);
221: r = li;
222: addTypeName(r);
223: } else {
224: boolean isReplaced = subclassReplacements
225: .containsKey(clazz);
226: if (isReplaced && !searchForSuperClass) {
227: // handle it as if the replacement was specified
228: r = getClassInfo(subclassReplacements.get(clazz),
229: upstream);
230: } else if (reader.hasClassAnnotation(clazz,
231: XmlTransient.class)
232: || isReplaced) {
233: // handle it as if the base class was specified
234: r = getClassInfo(nav.getSuperClass(clazz),
235: searchForSuperClass, new ClassLocatable<C>(
236: upstream, clazz, nav));
237: } else {
238: ClassInfoImpl<T, C, F, M> ci = createClassInfo(clazz,
239: upstream);
240: typeInfoSet.add(ci);
241:
242: // compute the closure by eagerly expanding references
243: for (PropertyInfo<T, C> p : ci.getProperties()) {
244: if (p.kind() == PropertyKind.REFERENCE) {
245: // make sure that we have a registry for this package
246: String pkg = nav.getPackageName(ci.getClazz());
247: if (!registries.containsKey(pkg)) {
248: // insert the package's object factory
249: C c = nav.findClass(pkg + ".ObjectFactory",
250: ci.getClazz());
251: if (c != null)
252: addRegistry(c, (Locatable) p);
253: }
254: }
255:
256: for (TypeInfo<T, C> t : p.ref())
257: ; // just compute a reference should be suffice
258: }
259: ci.getBaseClass(); // same as above.
260:
261: r = ci;
262: addTypeName(r);
263: }
264: }
265:
266: // more reference closure expansion. @XmlSeeAlso
267: XmlSeeAlso sa = reader.getClassAnnotation(XmlSeeAlso.class,
268: clazz, upstream);
269: if (sa != null) {
270: for (T t : reader.getClassArrayValue(sa, "value")) {
271: getTypeInfo(t, (Locatable) sa);
272: }
273: }
274:
275: return r;
276: }
277:
278: /**
279: * Checks the uniqueness of the type name.
280: */
281: private void addTypeName(NonElement<T, C> r) {
282: QName t = r.getTypeName();
283: if (t == null)
284: return;
285:
286: TypeInfo old = typeNames.put(t, r);
287: if (old != null) {
288: // collision
289: reportError(new IllegalAnnotationException(
290: Messages.CONFLICTING_XML_TYPE_MAPPING.format(r
291: .getTypeName()), old, r));
292: }
293: }
294:
295: /**
296: * Have the builder recognize the type (if it hasn't done so yet),
297: * and returns a {@link NonElement} that represents it.
298: *
299: * @return
300: * always non-null.
301: */
302: public NonElement<T, C> getTypeInfo(T t, Locatable upstream) {
303: NonElement<T, C> r = typeInfoSet.getTypeInfo(t);
304: if (r != null)
305: return r;
306:
307: if (nav.isArray(t)) { // no need for checking byte[], because above typeInfoset.getTypeInfo() would return non-null
308: ArrayInfoImpl<T, C, F, M> ai = createArrayInfo(upstream, t);
309: addTypeName(ai);
310: typeInfoSet.add(ai);
311: return ai;
312: }
313:
314: C c = nav.asDecl(t);
315: assert c != null : t.toString()
316: + " must be a leaf, but we failed to recognize it.";
317: return getClassInfo(c, upstream);
318: }
319:
320: /**
321: * This method is used to add a root reference to a model.
322: */
323: public NonElement<T, C> getTypeInfo(Ref<T, C> ref) {
324: // TODO: handle XmlValueList
325: assert !ref.valueList;
326: C c = nav.asDecl(ref.type);
327: if (c != null
328: && reader
329: .getClassAnnotation(XmlRegistry.class, c, null/*TODO: is this right?*/) != null) {
330: if (!registries.containsKey(nav.getPackageName(c)))
331: addRegistry(c, null);
332: return null; // TODO: is this correct?
333: } else
334: return getTypeInfo(ref.type, null);
335: }
336:
337: protected EnumLeafInfoImpl<T, C, F, M> createEnumLeafInfo(C clazz,
338: Locatable upstream) {
339: return new EnumLeafInfoImpl<T, C, F, M>(this , upstream, clazz,
340: nav.use(clazz));
341: }
342:
343: protected ClassInfoImpl<T, C, F, M> createClassInfo(C clazz,
344: Locatable upstream) {
345: return new ClassInfoImpl<T, C, F, M>(this , upstream, clazz);
346: }
347:
348: protected ElementInfoImpl<T, C, F, M> createElementInfo(
349: RegistryInfoImpl<T, C, F, M> registryInfo, M m)
350: throws IllegalAnnotationException {
351: return new ElementInfoImpl<T, C, F, M>(this , registryInfo, m);
352: }
353:
354: protected ArrayInfoImpl<T, C, F, M> createArrayInfo(
355: Locatable upstream, T arrayType) {
356: return new ArrayInfoImpl<T, C, F, M>(this , upstream, arrayType);
357: }
358:
359: /**
360: * Visits a class with {@link XmlRegistry} and records all the element mappings
361: * in it.
362: */
363: public RegistryInfo<T, C> addRegistry(C registryClass,
364: Locatable upstream) {
365: return new RegistryInfoImpl<T, C, F, M>(this , upstream,
366: registryClass);
367: }
368:
369: /**
370: * Gets a {@link RegistryInfo} for the given package.
371: *
372: * @return
373: * null if no registry exists for the package.
374: * unlike other getXXX methods on this class,
375: * this method is side-effect free.
376: */
377: public RegistryInfo<T, C> getRegistry(String packageName) {
378: return registries.get(packageName);
379: }
380:
381: private boolean linked;
382:
383: /**
384: * Called after all the classes are added to the type set
385: * to "link" them together.
386: *
387: * <p>
388: * Don't expose implementation classes in the signature.
389: *
390: * @return
391: * fully built {@link TypeInfoSet} that represents the model,
392: * or null if there was an error.
393: */
394: public TypeInfoSet<T, C, F, M> link() {
395:
396: assert !linked;
397: linked = true;
398:
399: for (ElementInfoImpl ei : typeInfoSet.getAllElements())
400: ei.link();
401:
402: for (ClassInfoImpl ci : typeInfoSet.beans().values())
403: ci.link();
404:
405: for (EnumLeafInfoImpl li : typeInfoSet.enums().values())
406: li.link();
407:
408: if (hadError)
409: return null;
410: else
411: return typeInfoSet;
412: }
413:
414: //
415: //
416: // error handling
417: //
418: //
419:
420: /**
421: * Sets the error handler that receives errors discovered during the model building.
422: *
423: * @param errorHandler
424: * can be null.
425: */
426: public void setErrorHandler(ErrorHandler errorHandler) {
427: this .errorHandler = errorHandler;
428: }
429:
430: public final void reportError(IllegalAnnotationException e) {
431: hadError = true;
432: if (errorHandler != null)
433: errorHandler.error(e);
434: }
435:
436: public boolean isReplaced(C sc) {
437: return subclassReplacements.containsKey(sc);
438: }
439: }
|