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.Collections;
029: import java.util.HashMap;
030: import java.util.Iterator;
031: import java.util.LinkedHashMap;
032: import java.util.Map;
033:
034: import javax.xml.bind.JAXBContext;
035: import javax.xml.bind.JAXBException;
036: import javax.xml.bind.Marshaller;
037: import javax.xml.bind.annotation.XmlNs;
038: import javax.xml.bind.annotation.XmlNsForm;
039: import javax.xml.bind.annotation.XmlRegistry;
040: import javax.xml.bind.annotation.XmlSchema;
041: import javax.xml.bind.annotation.XmlTransient;
042: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
043: import javax.xml.namespace.QName;
044: import javax.xml.transform.Result;
045:
046: import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader;
047: import com.sun.xml.internal.bind.v2.model.core.BuiltinLeafInfo;
048: import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
049: import com.sun.xml.internal.bind.v2.model.core.LeafInfo;
050: import com.sun.xml.internal.bind.v2.model.core.NonElement;
051: import com.sun.xml.internal.bind.v2.model.core.Ref;
052: import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
053: import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
054: import com.sun.xml.internal.bind.v2.model.nav.Navigator;
055: import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
056: import com.sun.xml.internal.bind.v2.runtime.RuntimeUtil;
057: import com.sun.xml.internal.bind.v2.util.FlattenIterator;
058:
059: /**
060: * Set of {@link TypeInfo}s.
061: *
062: * <p>
063: * This contains a fixed set of {@link LeafInfo}s and arbitrary set of {@link ClassInfo}s.
064: *
065: * <p>
066: * Members are annotated with JAXB annotations so that we can dump it easily.
067: *
068: * @author Kohsuke Kawaguchi
069: */
070: class TypeInfoSetImpl<TypeT, ClassDeclT, FieldT, MethodT> implements
071: TypeInfoSet<TypeT, ClassDeclT, FieldT, MethodT> {
072:
073: @XmlTransient
074: public final Navigator<TypeT, ClassDeclT, FieldT, MethodT> nav;
075:
076: @XmlTransient
077: public final AnnotationReader<TypeT, ClassDeclT, FieldT, MethodT> reader;
078:
079: /**
080: * All the leaves.
081: */
082: private final Map<TypeT, BuiltinLeafInfo<TypeT, ClassDeclT>> builtins = new LinkedHashMap<TypeT, BuiltinLeafInfo<TypeT, ClassDeclT>>();
083:
084: /** All {@link EnumLeafInfoImpl}s. */
085: private final Map<ClassDeclT, EnumLeafInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> enums = new LinkedHashMap<ClassDeclT, EnumLeafInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>();
086:
087: /** All {@link ArrayInfoImpl}s. */
088: private final Map<TypeT, ArrayInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> arrays = new LinkedHashMap<TypeT, ArrayInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>();
089:
090: /**
091: * All the user-defined classes.
092: *
093: * Using {@link LinkedHashMap} allows us to process classes
094: * in the order they are given to us. When the user incorrectly
095: * puts an unexpected class into a reference graph, this causes
096: * an error to be reported on a class closer to the user's code.
097: */
098: @XmlJavaTypeAdapter(RuntimeUtil.ToStringAdapter.class)
099: private final Map<ClassDeclT, ClassInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> beans = new LinkedHashMap<ClassDeclT, ClassInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>();
100:
101: @XmlTransient
102: private final Map<ClassDeclT, ClassInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> beansView = Collections
103: .unmodifiableMap(beans);
104:
105: /**
106: * The element mapping.
107: */
108: private final Map<ClassDeclT, Map<QName, ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>> elementMappings = new LinkedHashMap<ClassDeclT, Map<QName, ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>>();
109:
110: private final Iterable<? extends ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> allElements = new Iterable<ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>() {
111: public Iterator<ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> iterator() {
112: return new FlattenIterator<ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>(
113: elementMappings.values());
114: }
115: };
116:
117: /**
118: * {@link TypeInfo} for <tt>xs:anyType</tt>.
119: *
120: * anyType is the only {@link TypeInfo} that works with an interface,
121: * and accordingly it requires a lot of special casing.
122: */
123: private final NonElement<TypeT, ClassDeclT> anyType;
124:
125: /**
126: * Lazily parsed set of {@link XmlNs}s.
127: *
128: * @see #getXmlNs(String)
129: */
130: private Map<String, Map<String, String>> xmlNsCache;
131:
132: public TypeInfoSetImpl(
133: Navigator<TypeT, ClassDeclT, FieldT, MethodT> nav,
134: AnnotationReader<TypeT, ClassDeclT, FieldT, MethodT> reader,
135: Map<TypeT, ? extends BuiltinLeafInfoImpl<TypeT, ClassDeclT>> leaves) {
136: this .nav = nav;
137: this .reader = reader;
138: this .builtins.putAll(leaves);
139:
140: this .anyType = createAnyType();
141:
142: // register primitive types.
143: for (Map.Entry<Class, Class> e : RuntimeUtil.primitiveToBox
144: .entrySet()) {
145: this .builtins.put(nav.getPrimitive(e.getKey()), leaves
146: .get(nav.ref(e.getValue())));
147: }
148:
149: // make sure at lease we got a map for global ones.
150: elementMappings
151: .put(
152: null,
153: new LinkedHashMap<QName, ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>());
154: }
155:
156: protected NonElement<TypeT, ClassDeclT> createAnyType() {
157: return new AnyTypeImpl<TypeT, ClassDeclT>(nav);
158: }
159:
160: public Navigator<TypeT, ClassDeclT, FieldT, MethodT> getNavigator() {
161: return nav;
162: }
163:
164: /**
165: * Adds a new {@link ClassInfo} to the set.
166: */
167: public void add(ClassInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> ci) {
168: beans.put(ci.getClazz(), ci);
169: }
170:
171: /**
172: * Adds a new {@link LeafInfo} to the set.
173: */
174: public void add(
175: EnumLeafInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> li) {
176: enums.put(li.clazz, li);
177: }
178:
179: public void add(ArrayInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> ai) {
180: arrays.put(ai.getType(), ai);
181: }
182:
183: /**
184: * Returns a {@link TypeInfo} for the given type.
185: *
186: * @return
187: * null if the specified type cannot be bound by JAXB, or
188: * not known to this set.
189: */
190: public NonElement<TypeT, ClassDeclT> getTypeInfo(TypeT type) {
191: type = nav.erasure(type); // replace type variables by their bounds
192:
193: LeafInfo<TypeT, ClassDeclT> l = builtins.get(type);
194: if (l != null)
195: return l;
196:
197: if (nav.isArray(type)) {
198: return arrays.get(type);
199: }
200:
201: ClassDeclT d = nav.asDecl(type);
202: if (d == null)
203: return null;
204: return getClassInfo(d);
205: }
206:
207: public NonElement<TypeT, ClassDeclT> getAnyTypeInfo() {
208: return anyType;
209: }
210:
211: /**
212: * This method is used to add a root reference to a model.
213: */
214: public NonElement<TypeT, ClassDeclT> getTypeInfo(
215: Ref<TypeT, ClassDeclT> ref) {
216: // TODO: handle XmlValueList
217: assert !ref.valueList;
218: ClassDeclT c = nav.asDecl(ref.type);
219: if (c != null
220: && reader
221: .getClassAnnotation(XmlRegistry.class, c, null/*TODO: is this right?*/) != null) {
222: return null; // TODO: is this correct?
223: } else
224: return getTypeInfo(ref.type);
225: }
226:
227: /**
228: * Returns all the {@link ClassInfo}s known to this set.
229: */
230: public Map<ClassDeclT, ? extends ClassInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> beans() {
231: return beansView;
232: }
233:
234: public Map<TypeT, ? extends BuiltinLeafInfo<TypeT, ClassDeclT>> builtins() {
235: return builtins;
236: }
237:
238: public Map<ClassDeclT, ? extends EnumLeafInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> enums() {
239: return enums;
240: }
241:
242: public Map<? extends TypeT, ? extends ArrayInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> arrays() {
243: return arrays;
244: }
245:
246: /**
247: * Returns a {@link ClassInfo} for the given bean.
248: *
249: * <p>
250: * This method is almost like refinement of {@link #getTypeInfo(Object)} except
251: * our C cannot derive from T.
252: *
253: * @return
254: * null if the specified type is not bound by JAXB or otherwise
255: * unknown to this set.
256: */
257: public NonElement<TypeT, ClassDeclT> getClassInfo(ClassDeclT type) {
258: LeafInfo<TypeT, ClassDeclT> l = builtins.get(nav.use(type));
259: if (l != null)
260: return l;
261:
262: l = enums.get(type);
263: if (l != null)
264: return l;
265:
266: if (nav.asDecl(Object.class).equals(type))
267: return anyType;
268:
269: return beans.get(type);
270: }
271:
272: public ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> getElementInfo(
273: ClassDeclT scope, QName name) {
274: while (scope != null) {
275: Map<QName, ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> m = elementMappings
276: .get(scope);
277: if (m != null) {
278: ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> r = m
279: .get(name);
280: if (r != null)
281: return r;
282: }
283: scope = nav.getSuperClass(scope);
284: }
285: return elementMappings.get(null).get(name);
286: }
287:
288: /**
289: * @param builder
290: * used for reporting errors.
291: */
292: public final void add(
293: ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> ei,
294: ModelBuilder<TypeT, ClassDeclT, FieldT, MethodT> builder) {
295: ClassDeclT scope = null;
296: if (ei.getScope() != null)
297: scope = ei.getScope().getClazz();
298:
299: Map<QName, ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> m = elementMappings
300: .get(scope);
301: if (m == null)
302: elementMappings
303: .put(
304: scope,
305: m = new LinkedHashMap<QName, ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>>());
306:
307: ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> existing = m
308: .put(ei.getElementName(), ei);
309:
310: if (existing != null) {
311: QName en = ei.getElementName();
312: builder.reportError(new IllegalAnnotationException(
313: Messages.CONFLICTING_XML_ELEMENT_MAPPING.format(en
314: .getNamespaceURI(), en.getLocalPart()), ei,
315: existing));
316: }
317: }
318:
319: public Map<QName, ? extends ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> getElementMappings(
320: ClassDeclT scope) {
321: return elementMappings.get(scope);
322: }
323:
324: public Iterable<? extends ElementInfoImpl<TypeT, ClassDeclT, FieldT, MethodT>> getAllElements() {
325: return allElements;
326: }
327:
328: public Map<String, String> getXmlNs(String namespaceUri) {
329: if (xmlNsCache == null) {
330: xmlNsCache = new HashMap<String, Map<String, String>>();
331:
332: for (ClassInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> ci : beans()
333: .values()) {
334: XmlSchema xs = reader.getPackageAnnotation(
335: XmlSchema.class, ci.getClazz(), null);
336: if (xs == null)
337: continue;
338:
339: String uri = xs.namespace();
340: Map<String, String> m = xmlNsCache.get(uri);
341: if (m == null)
342: xmlNsCache.put(uri,
343: m = new HashMap<String, String>());
344:
345: for (XmlNs xns : xs.xmlns()) {
346: m.put(xns.prefix(), xns.namespaceURI());
347: }
348: }
349: }
350:
351: Map<String, String> r = xmlNsCache.get(namespaceUri);
352: if (r != null)
353: return r;
354: else
355: return Collections.emptyMap();
356: }
357:
358: public final XmlNsForm getElementFormDefault(String nsUri) {
359: for (ClassInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> ci : beans()
360: .values()) {
361: XmlSchema xs = reader.getPackageAnnotation(XmlSchema.class,
362: ci.getClazz(), null);
363: if (xs == null)
364: continue;
365:
366: if (!xs.namespace().equals(nsUri))
367: continue;
368:
369: XmlNsForm xnf = xs.elementFormDefault();
370: if (xnf != XmlNsForm.UNSET)
371: return xnf;
372: }
373: return XmlNsForm.UNSET;
374: }
375:
376: public final XmlNsForm getAttributeFormDefault(String nsUri) {
377: for (ClassInfoImpl<TypeT, ClassDeclT, FieldT, MethodT> ci : beans()
378: .values()) {
379: XmlSchema xs = reader.getPackageAnnotation(XmlSchema.class,
380: ci.getClazz(), null);
381: if (xs == null)
382: continue;
383:
384: if (!xs.namespace().equals(nsUri))
385: continue;
386:
387: XmlNsForm xnf = xs.attributeFormDefault();
388: if (xnf != XmlNsForm.UNSET)
389: return xnf;
390: }
391: return XmlNsForm.UNSET;
392: }
393:
394: /**
395: * Dumps this model into XML.
396: *
397: * For debug only.
398: *
399: * TODO: not sure if this actually works. We don't really know what are T,C.
400: */
401: public void dump(Result out) throws JAXBException {
402: JAXBContext context = JAXBContext.newInstance(this .getClass());
403: Marshaller m = context.createMarshaller();
404: m.marshal(this, out);
405: }
406: }
|