aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.bind.v2.model.impl; aoqi@0: aoqi@0: import java.util.Collections; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.Iterator; aoqi@0: import java.util.LinkedHashMap; aoqi@0: import java.util.Map; aoqi@0: aoqi@0: import javax.xml.bind.JAXBContext; aoqi@0: import javax.xml.bind.JAXBException; aoqi@0: import javax.xml.bind.Marshaller; aoqi@0: import javax.xml.bind.annotation.XmlNs; aoqi@0: import javax.xml.bind.annotation.XmlNsForm; aoqi@0: import javax.xml.bind.annotation.XmlRegistry; aoqi@0: import javax.xml.bind.annotation.XmlSchema; aoqi@0: import javax.xml.bind.annotation.XmlTransient; aoqi@0: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; aoqi@0: import javax.xml.namespace.QName; aoqi@0: import javax.xml.transform.Result; aoqi@0: aoqi@0: import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.BuiltinLeafInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ClassInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.LeafInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.NonElement; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.Ref; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.TypeInfo; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet; aoqi@0: import com.sun.xml.internal.bind.v2.model.nav.Navigator; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.RuntimeUtil; aoqi@0: import com.sun.xml.internal.bind.v2.util.FlattenIterator; aoqi@0: aoqi@0: /** aoqi@0: * Set of {@link TypeInfo}s. aoqi@0: * aoqi@0: *

aoqi@0: * This contains a fixed set of {@link LeafInfo}s and arbitrary set of {@link ClassInfo}s. aoqi@0: * aoqi@0: *

aoqi@0: * Members are annotated with JAXB annotations so that we can dump it easily. aoqi@0: * aoqi@0: * @author Kohsuke Kawaguchi aoqi@0: */ aoqi@0: class TypeInfoSetImpl implements TypeInfoSet { aoqi@0: aoqi@0: @XmlTransient aoqi@0: public final Navigator nav; aoqi@0: aoqi@0: @XmlTransient aoqi@0: public final AnnotationReader reader; aoqi@0: aoqi@0: /** aoqi@0: * All the leaves. aoqi@0: */ aoqi@0: private final Map> builtins = aoqi@0: new LinkedHashMap>(); aoqi@0: aoqi@0: /** All {@link EnumLeafInfoImpl}s. */ aoqi@0: private final Map> enums = aoqi@0: new LinkedHashMap>(); aoqi@0: aoqi@0: /** All {@link ArrayInfoImpl}s. */ aoqi@0: private final Map> arrays = aoqi@0: new LinkedHashMap>(); aoqi@0: aoqi@0: /** aoqi@0: * All the user-defined classes. aoqi@0: * aoqi@0: * Using {@link LinkedHashMap} allows us to process classes aoqi@0: * in the order they are given to us. When the user incorrectly aoqi@0: * puts an unexpected class into a reference graph, this causes aoqi@0: * an error to be reported on a class closer to the user's code. aoqi@0: */ aoqi@0: @XmlJavaTypeAdapter(RuntimeUtil.ToStringAdapter.class) aoqi@0: private final Map> beans aoqi@0: = new LinkedHashMap>(); aoqi@0: aoqi@0: @XmlTransient aoqi@0: private final Map> beansView = aoqi@0: Collections.unmodifiableMap(beans); aoqi@0: aoqi@0: /** aoqi@0: * The element mapping. aoqi@0: */ aoqi@0: private final Map>> elementMappings = aoqi@0: new LinkedHashMap>>(); aoqi@0: aoqi@0: private final Iterable> allElements = aoqi@0: new Iterable>() { aoqi@0: public Iterator> iterator() { aoqi@0: return new FlattenIterator>(elementMappings.values()); aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: /** aoqi@0: * {@link TypeInfo} for xs:anyType. aoqi@0: * aoqi@0: * anyType is the only {@link TypeInfo} that works with an interface, aoqi@0: * and accordingly it requires a lot of special casing. aoqi@0: */ aoqi@0: private final NonElement anyType; aoqi@0: aoqi@0: /** aoqi@0: * Lazily parsed set of {@link XmlNs}s. aoqi@0: * aoqi@0: * @see #getXmlNs(String) aoqi@0: */ aoqi@0: private Map> xmlNsCache; aoqi@0: aoqi@0: public TypeInfoSetImpl(Navigator nav, aoqi@0: AnnotationReader reader, aoqi@0: Map> leaves) { aoqi@0: this.nav = nav; aoqi@0: this.reader = reader; aoqi@0: this.builtins.putAll(leaves); aoqi@0: aoqi@0: this.anyType = createAnyType(); aoqi@0: aoqi@0: // register primitive types. aoqi@0: for (Map.Entry e : RuntimeUtil.primitiveToBox.entrySet()) { aoqi@0: this.builtins.put( nav.getPrimitive(e.getKey()), leaves.get(nav.ref(e.getValue())) ); aoqi@0: } aoqi@0: aoqi@0: // make sure at lease we got a map for global ones. aoqi@0: elementMappings.put(null,new LinkedHashMap>()); aoqi@0: } aoqi@0: aoqi@0: protected NonElement createAnyType() { aoqi@0: return new AnyTypeImpl(nav); aoqi@0: } aoqi@0: aoqi@0: public Navigator getNavigator() { aoqi@0: return nav; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Adds a new {@link ClassInfo} to the set. aoqi@0: */ aoqi@0: public void add( ClassInfoImpl ci ) { aoqi@0: beans.put( ci.getClazz(), ci ); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Adds a new {@link LeafInfo} to the set. aoqi@0: */ aoqi@0: public void add( EnumLeafInfoImpl li ) { aoqi@0: enums.put( li.clazz, li ); aoqi@0: } aoqi@0: aoqi@0: public void add(ArrayInfoImpl ai) { aoqi@0: arrays.put( ai.getType(), ai ); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns a {@link TypeInfo} for the given type. aoqi@0: * aoqi@0: * @return aoqi@0: * null if the specified type cannot be bound by JAXB, or aoqi@0: * not known to this set. aoqi@0: */ aoqi@0: public NonElement getTypeInfo( T type ) { aoqi@0: type = nav.erasure(type); // replace type variables by their bounds aoqi@0: aoqi@0: LeafInfo l = builtins.get(type); aoqi@0: if(l!=null) return l; aoqi@0: aoqi@0: if( nav.isArray(type) ) { aoqi@0: return arrays.get(type); aoqi@0: } aoqi@0: aoqi@0: C d = nav.asDecl(type); aoqi@0: if(d==null) return null; aoqi@0: return getClassInfo(d); aoqi@0: } aoqi@0: aoqi@0: public NonElement getAnyTypeInfo() { aoqi@0: return anyType; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * This method is used to add a root reference to a model. aoqi@0: */ aoqi@0: public NonElement getTypeInfo(Ref ref) { aoqi@0: // TODO: handle XmlValueList aoqi@0: assert !ref.valueList; aoqi@0: C c = nav.asDecl(ref.type); aoqi@0: if(c!=null && reader.getClassAnnotation(XmlRegistry.class,c,null/*TODO: is this right?*/)!=null) { aoqi@0: return null; // TODO: is this correct? aoqi@0: } else aoqi@0: return getTypeInfo(ref.type); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns all the {@link ClassInfo}s known to this set. aoqi@0: */ aoqi@0: public Map> beans() { aoqi@0: return beansView; aoqi@0: } aoqi@0: aoqi@0: public Map> builtins() { aoqi@0: return builtins; aoqi@0: } aoqi@0: aoqi@0: public Map> enums() { aoqi@0: return enums; aoqi@0: } aoqi@0: aoqi@0: public Map> arrays() { aoqi@0: return arrays; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns a {@link ClassInfo} for the given bean. aoqi@0: * aoqi@0: *

aoqi@0: * This method is almost like refinement of {@link #getTypeInfo(Object)} except aoqi@0: * our C cannot derive from T. aoqi@0: * aoqi@0: * @return aoqi@0: * null if the specified type is not bound by JAXB or otherwise aoqi@0: * unknown to this set. aoqi@0: */ aoqi@0: public NonElement getClassInfo( C type ) { aoqi@0: LeafInfo l = builtins.get(nav.use(type)); aoqi@0: if(l!=null) return l; aoqi@0: aoqi@0: l = enums.get(type); aoqi@0: if(l!=null) return l; aoqi@0: aoqi@0: if(nav.asDecl(Object.class).equals(type)) aoqi@0: return anyType; aoqi@0: aoqi@0: return beans.get(type); aoqi@0: } aoqi@0: aoqi@0: public ElementInfoImpl getElementInfo( C scope, QName name ) { aoqi@0: while(scope!=null) { aoqi@0: Map> m = elementMappings.get(scope); aoqi@0: if(m!=null) { aoqi@0: ElementInfoImpl r = m.get(name); aoqi@0: if(r!=null) return r; aoqi@0: } aoqi@0: scope = nav.getSuperClass(scope); aoqi@0: } aoqi@0: return elementMappings.get(null).get(name); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * @param builder aoqi@0: * used for reporting errors. aoqi@0: */ aoqi@0: public final void add( ElementInfoImpl ei, ModelBuilder builder ) { aoqi@0: C scope = null; aoqi@0: if(ei.getScope()!=null) aoqi@0: scope = ei.getScope().getClazz(); aoqi@0: aoqi@0: Map> m = elementMappings.get(scope); aoqi@0: if(m==null) aoqi@0: elementMappings.put(scope,m=new LinkedHashMap>()); aoqi@0: aoqi@0: ElementInfoImpl existing = m.put(ei.getElementName(),ei); aoqi@0: aoqi@0: if(existing!=null) { aoqi@0: QName en = ei.getElementName(); aoqi@0: builder.reportError( aoqi@0: new IllegalAnnotationException( aoqi@0: Messages.CONFLICTING_XML_ELEMENT_MAPPING.format(en.getNamespaceURI(),en.getLocalPart()), aoqi@0: ei, existing )); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public Map> getElementMappings( C scope ) { aoqi@0: return elementMappings.get(scope); aoqi@0: } aoqi@0: aoqi@0: public Iterable> getAllElements() { aoqi@0: return allElements; aoqi@0: } aoqi@0: aoqi@0: public Map getXmlNs(String namespaceUri) { aoqi@0: if(xmlNsCache==null) { aoqi@0: xmlNsCache = new HashMap>(); aoqi@0: aoqi@0: for (ClassInfoImpl ci : beans().values()) { aoqi@0: XmlSchema xs = reader.getPackageAnnotation( XmlSchema.class, ci.getClazz(), null ); aoqi@0: if(xs==null) aoqi@0: continue; aoqi@0: aoqi@0: String uri = xs.namespace(); aoqi@0: Map m = xmlNsCache.get(uri); aoqi@0: if(m==null) aoqi@0: xmlNsCache.put(uri,m=new HashMap()); aoqi@0: aoqi@0: for( XmlNs xns : xs.xmlns() ) { aoqi@0: m.put(xns.prefix(),xns.namespaceURI()); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: Map r = xmlNsCache.get(namespaceUri); aoqi@0: if(r!=null) return r; aoqi@0: else return Collections.emptyMap(); aoqi@0: } aoqi@0: aoqi@0: public Map getSchemaLocations() { aoqi@0: Map r = new HashMap(); aoqi@0: for (ClassInfoImpl ci : beans().values()) { aoqi@0: XmlSchema xs = reader.getPackageAnnotation( XmlSchema.class, ci.getClazz(), null ); aoqi@0: if(xs==null) aoqi@0: continue; aoqi@0: aoqi@0: String loc = xs.location(); aoqi@0: if(loc.equals(XmlSchema.NO_LOCATION)) aoqi@0: continue; // unspecified aoqi@0: aoqi@0: r.put(xs.namespace(),loc); aoqi@0: } aoqi@0: return r; aoqi@0: } aoqi@0: aoqi@0: public final XmlNsForm getElementFormDefault(String nsUri) { aoqi@0: for (ClassInfoImpl ci : beans().values()) { aoqi@0: XmlSchema xs = reader.getPackageAnnotation( XmlSchema.class, ci.getClazz(), null ); aoqi@0: if(xs==null) aoqi@0: continue; aoqi@0: aoqi@0: if(!xs.namespace().equals(nsUri)) aoqi@0: continue; aoqi@0: aoqi@0: XmlNsForm xnf = xs.elementFormDefault(); aoqi@0: if(xnf!=XmlNsForm.UNSET) aoqi@0: return xnf; aoqi@0: } aoqi@0: return XmlNsForm.UNSET; aoqi@0: } aoqi@0: aoqi@0: public final XmlNsForm getAttributeFormDefault(String nsUri) { aoqi@0: for (ClassInfoImpl ci : beans().values()) { aoqi@0: XmlSchema xs = reader.getPackageAnnotation( XmlSchema.class, ci.getClazz(), null ); aoqi@0: if(xs==null) aoqi@0: continue; aoqi@0: aoqi@0: if(!xs.namespace().equals(nsUri)) aoqi@0: continue; aoqi@0: aoqi@0: XmlNsForm xnf = xs.attributeFormDefault(); aoqi@0: if(xnf!=XmlNsForm.UNSET) aoqi@0: return xnf; aoqi@0: } aoqi@0: return XmlNsForm.UNSET; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Dumps this model into XML. aoqi@0: * aoqi@0: * For debug only. aoqi@0: * aoqi@0: * TODO: not sure if this actually works. We don't really know what are T,C. aoqi@0: */ aoqi@0: public void dump( Result out ) throws JAXBException { aoqi@0: JAXBContext context = JAXBContext.newInstance(this.getClass()); aoqi@0: Marshaller m = context.createMarshaller(); aoqi@0: m.marshal(this,out); aoqi@0: } aoqi@0: }