001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.model.impl;
027:
028: import java.util.HashMap;
029: import java.util.Map;
030:
031: import javax.xml.bind.annotation.XmlRegistry;
032: import javax.xml.namespace.QName;
033:
034: import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader;
035: import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
036: import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
037: import com.sun.xml.internal.bind.v2.model.core.ErrorHandler;
038: import com.sun.xml.internal.bind.v2.model.core.LeafInfo;
039: import com.sun.xml.internal.bind.v2.model.core.NonElement;
040: import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
041: import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
042: import com.sun.xml.internal.bind.v2.model.core.Ref;
043: import com.sun.xml.internal.bind.v2.model.core.RegistryInfo;
044: import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
045: import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
046: import com.sun.xml.internal.bind.v2.model.nav.Navigator;
047: import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
048:
049: /**
050: * Builds a {@link TypeInfoSet} (a set of JAXB properties)
051: * by using {@link ElementInfoImpl} and {@link ClassInfoImpl}.
052: * from annotated Java classes.
053: *
054: * <p>
055: * This class uses {@link Navigator} and {@link AnnotationReader} to
056: * work with arbitrary annotation source and arbitrary Java model.
057: * For this purpose this class is parameterized.
058: *
059: * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
060: */
061: public class ModelBuilder<T, C, F, M> {
062:
063: /**
064: * {@link TypeInfo}s that are built will go into this set.
065: */
066: final TypeInfoSetImpl<T, C, F, M> typeInfoSet;
067:
068: public final AnnotationReader<T, C, F, M> reader;
069:
070: public final Navigator<T, C, F, M> nav;
071:
072: /**
073: * Used to detect collisions among global type names.
074: */
075: private final Map<QName, TypeInfo> typeNames = new HashMap<QName, TypeInfo>();
076:
077: /**
078: * JAXB doesn't want to use namespaces unless we are told to, but WS-I BP
079: * conformace requires JAX-RPC to always use a non-empty namespace URI.
080: * (see http://www.ws-i.org/Profiles/BasicProfile-1.0-2004-04-16.html#WSDLTYPES R2105)
081: *
082: * <p>
083: * To work around this issue, we allow the use of the empty namespaces to be
084: * replaced by a particular designated namespace URI.
085: *
086: * <p>
087: * This field keeps the value of that replacing namespace URI.
088: * When there's no replacement, this field is set to "".
089: */
090: public final String defaultNsUri;
091:
092: /**
093: * Packages whose registries are already added.
094: */
095: /*package*/final Map<String, RegistryInfoImpl> registries = new HashMap<String, RegistryInfoImpl>();
096:
097: /**
098: * @see #setErrorHandler
099: */
100: private ErrorHandler errorHandler;
101: private boolean hadError;
102:
103: private final ErrorHandler proxyErrorHandler = new ErrorHandler() {
104: public void error(IllegalAnnotationException e) {
105: reportError(e);
106: }
107: };
108:
109: public ModelBuilder(AnnotationReader<T, C, F, M> reader,
110: Navigator<T, C, F, M> navigator,
111: String defaultNamespaceRemap) {
112:
113: this .reader = reader;
114: this .nav = navigator;
115: if (defaultNamespaceRemap == null)
116: defaultNamespaceRemap = "";
117: this .defaultNsUri = defaultNamespaceRemap;
118: reader.setErrorHandler(proxyErrorHandler);
119: typeInfoSet = createTypeInfoSet();
120: }
121:
122: protected TypeInfoSetImpl<T, C, F, M> createTypeInfoSet() {
123: return new TypeInfoSetImpl(nav, reader, BuiltinLeafInfoImpl
124: .createLeaves(nav));
125: }
126:
127: /**
128: * Builds a JAXB {@link ClassInfo} model from a given class declaration
129: * and adds that to this model owner.
130: *
131: * <p>
132: * Return type is either {@link ClassInfo} or {@link LeafInfo} (for types like
133: * {@link String} or {@link Enum}-derived ones)
134: */
135: public NonElement<T, C> getClassInfo(C clazz, Locatable upstream) {
136: assert clazz != null;
137: NonElement<T, C> r = typeInfoSet.getClassInfo(clazz);
138: if (r != null)
139: return r;
140:
141: if (nav.isEnum(clazz)) {
142: EnumLeafInfoImpl<T, C, F, M> li = createEnumLeafInfo(clazz,
143: upstream);
144: typeInfoSet.add(li);
145: r = li;
146: } else {
147: ClassInfoImpl<T, C, F, M> ci = createClassInfo(clazz,
148: upstream);
149: typeInfoSet.add(ci);
150:
151: // compute the closure by eagerly expanding references
152: for (PropertyInfo<T, C> p : ci.getProperties()) {
153: if (p.kind() == PropertyKind.REFERENCE) {
154: // make sure that we have a registry for this package
155: String pkg = nav.getPackageName(ci.getClazz());
156: if (!registries.containsKey(pkg)) {
157: // insert the package's object factory
158: C c = nav.findClass(pkg + ".ObjectFactory", ci
159: .getClazz());
160: if (c != null)
161: addRegistry(c, (Locatable) p);
162: }
163: }
164:
165: for (TypeInfo<T, C> t : p.ref())
166: ; // just compute a reference should be suffice
167: }
168: ci.getBaseClass();
169:
170: r = ci;
171: }
172:
173: addTypeName(r);
174:
175: return r;
176: }
177:
178: /**
179: * Checks the uniqueness of the type name.
180: */
181: private void addTypeName(NonElement<T, C> r) {
182: QName t = r.getTypeName();
183: if (t == null)
184: return;
185:
186: TypeInfo old = typeNames.put(t, r);
187: if (old != null) {
188: // collision
189: reportError(new IllegalAnnotationException(
190: Messages.CONFLICTING_XML_TYPE_MAPPING.format(r
191: .getTypeName()), old, r));
192: }
193: }
194:
195: /**
196: * Have the builder recognize the type (if it hasn't done so yet),
197: * and returns a {@link NonElement} that represents it.
198: *
199: * @return
200: * always non-null.
201: */
202: public NonElement<T, C> getTypeInfo(T t, Locatable upstream) {
203: NonElement<T, C> r = typeInfoSet.getTypeInfo(t);
204: if (r != null)
205: return r;
206:
207: if (nav.isArray(t)) { // no need for checking byte[], because above typeInfoset.getTypeInfo() would return non-null
208: ArrayInfoImpl<T, C, F, M> ai = createArrayInfo(upstream, t);
209: addTypeName(ai);
210: typeInfoSet.add(ai);
211: return ai;
212: }
213:
214: C c = nav.asDecl(t);
215: assert c != null : t.toString()
216: + " must be a leaf, but we failed to recognize it.";
217: return getClassInfo(c, upstream);
218: }
219:
220: /**
221: * This method is used to add a root reference to a model.
222: */
223: public NonElement<T, C> getTypeInfo(Ref<T, C> ref) {
224: // TODO: handle XmlValueList
225: assert !ref.valueList;
226: C c = nav.asDecl(ref.type);
227: if (c != null
228: && reader
229: .getClassAnnotation(XmlRegistry.class, c, null/*TODO: is this right?*/) != null) {
230: if (!registries.containsKey(nav.getPackageName(c)))
231: addRegistry(c, null);
232: return null; // TODO: is this correct?
233: } else
234: return getTypeInfo(ref.type, null);
235: }
236:
237: protected EnumLeafInfoImpl<T, C, F, M> createEnumLeafInfo(C clazz,
238: Locatable upstream) {
239: return new EnumLeafInfoImpl<T, C, F, M>(this , upstream, clazz,
240: nav.use(clazz));
241: }
242:
243: protected ClassInfoImpl<T, C, F, M> createClassInfo(C clazz,
244: Locatable upstream) {
245: return new ClassInfoImpl<T, C, F, M>(this , upstream, clazz);
246: }
247:
248: protected ElementInfoImpl<T, C, F, M> createElementInfo(
249: RegistryInfoImpl<T, C, F, M> registryInfo, M m)
250: throws IllegalAnnotationException {
251: return new ElementInfoImpl<T, C, F, M>(this , registryInfo, m);
252: }
253:
254: protected ArrayInfoImpl<T, C, F, M> createArrayInfo(
255: Locatable upstream, T arrayType) {
256: return new ArrayInfoImpl<T, C, F, M>(this , upstream, arrayType);
257: }
258:
259: /**
260: * Visits a class with {@link XmlRegistry} and records all the element mappings
261: * in it.
262: */
263: public RegistryInfo<T, C> addRegistry(C registryClass,
264: Locatable upstream) {
265: RegistryInfoImpl<T, C, F, M> r = new RegistryInfoImpl<T, C, F, M>(
266: this , upstream, registryClass);
267: return r;
268: }
269:
270: /**
271: * Gets a {@link RegistryInfo} for the given package.
272: *
273: * @return
274: * null if no registry exists for the package.
275: * unlike other getXXX methods on this class,
276: * this method is side-effect free.
277: */
278: public RegistryInfo<T, C> getRegistry(String packageName) {
279: return registries.get(packageName);
280: }
281:
282: private boolean linked;
283:
284: /**
285: * Called after all the classes are added to the type set
286: * to "link" them together.
287: *
288: * <p>
289: * Don't expose implementation classes in the signature.
290: *
291: * @return
292: * fully built {@link TypeInfoSet} that represents the model,
293: * or null if there was an error.
294: */
295: public TypeInfoSet<T, C, F, M> link() {
296:
297: assert !linked;
298: linked = true;
299:
300: for (ElementInfoImpl ei : typeInfoSet.getAllElements())
301: ei.link();
302:
303: for (ClassInfoImpl ci : typeInfoSet.beans().values())
304: ci.link();
305:
306: for (EnumLeafInfoImpl li : typeInfoSet.enums().values())
307: li.link();
308:
309: if (hadError)
310: return null;
311: else
312: return typeInfoSet;
313: }
314:
315: //
316: //
317: // error handling
318: //
319: //
320:
321: /**
322: * Sets the error handler that receives errors discovered during the model building.
323: *
324: * @param errorHandler
325: * can be null.
326: */
327: public void setErrorHandler(ErrorHandler errorHandler) {
328: this .errorHandler = errorHandler;
329: }
330:
331: public final void reportError(IllegalAnnotationException e) {
332: hadError = true;
333: if (errorHandler != null)
334: errorHandler.error(e);
335: }
336: }
|