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.tools.internal.xjc.reader.xmlschema; aoqi@0: aoqi@0: import java.util.LinkedHashSet; aoqi@0: import java.util.Set; aoqi@0: aoqi@0: import javax.activation.MimeType; aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: import com.sun.tools.internal.xjc.model.CAdapter; aoqi@0: import com.sun.tools.internal.xjc.model.CClass; aoqi@0: import com.sun.tools.internal.xjc.model.CClassInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CCustomizations; aoqi@0: import com.sun.tools.internal.xjc.model.CElement; aoqi@0: import com.sun.tools.internal.xjc.model.CElementInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CElementPropertyInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CTypeRef; aoqi@0: import com.sun.tools.internal.xjc.model.Model; aoqi@0: import com.sun.tools.internal.xjc.model.Multiplicity; aoqi@0: import com.sun.tools.internal.xjc.model.TypeUse; aoqi@0: import com.sun.tools.internal.xjc.reader.RawTypeSet; aoqi@0: import com.sun.tools.internal.xjc.reader.Ring; aoqi@0: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDom; aoqi@0: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding; aoqi@0: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIXSubstitutable; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.ID; aoqi@0: import com.sun.xml.internal.bind.v2.model.core.WildcardMode; aoqi@0: import com.sun.xml.internal.xsom.XSElementDecl; aoqi@0: import com.sun.xml.internal.xsom.XSModelGroup; aoqi@0: import com.sun.xml.internal.xsom.XSModelGroupDecl; aoqi@0: import com.sun.xml.internal.xsom.XSParticle; aoqi@0: import com.sun.xml.internal.xsom.XSWildcard; aoqi@0: import com.sun.xml.internal.xsom.visitor.XSTermVisitor; aoqi@0: aoqi@0: /** aoqi@0: * Builds {@link RawTypeSet} for XML Schema. aoqi@0: * aoqi@0: * @author Kohsuke Kawaguchi aoqi@0: */ aoqi@0: public class RawTypeSetBuilder implements XSTermVisitor { aoqi@0: /** aoqi@0: * @param optional aoqi@0: * if this whole property is optional due to the aoqi@0: * occurrence constraints on ancestors, set this to true. aoqi@0: * this will prevent the primitive types to be generated. aoqi@0: */ aoqi@0: public static RawTypeSet build( XSParticle p, boolean optional ) { aoqi@0: RawTypeSetBuilder rtsb = new RawTypeSetBuilder(); aoqi@0: rtsb.particle(p); aoqi@0: Multiplicity mul = MultiplicityCounter.theInstance.particle(p); aoqi@0: aoqi@0: if(optional) aoqi@0: mul = mul.makeOptional(); aoqi@0: aoqi@0: return new RawTypeSet(rtsb.refs,mul); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * To avoid declaring the same element twice for a content model like aoqi@0: * (A,A), we keep track of element names here while we are building up aoqi@0: * this instance. aoqi@0: */ aoqi@0: private final Set elementNames = new LinkedHashSet(); aoqi@0: aoqi@0: private final Set refs = new LinkedHashSet(); aoqi@0: aoqi@0: protected final BGMBuilder builder = Ring.get(BGMBuilder.class); aoqi@0: aoqi@0: public RawTypeSetBuilder() {} aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Gets the {@link RawTypeSet.Ref}s that were built. aoqi@0: */ aoqi@0: public Set getRefs() { aoqi@0: return refs; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Build up {@link #refs} and compute the total multiplicity of this {@link RawTypeSet.Ref} set. aoqi@0: */ aoqi@0: private void particle( XSParticle p ) { aoqi@0: // if the DOM customization is present, bind it like a wildcard aoqi@0: BIDom dom = builder.getLocalDomCustomization(p); aoqi@0: if(dom!=null) { aoqi@0: dom.markAsAcknowledged(); aoqi@0: refs.add(new WildcardRef(WildcardMode.SKIP)); aoqi@0: } else { aoqi@0: p.getTerm().visit(this); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public void wildcard(XSWildcard wc) { aoqi@0: refs.add(new WildcardRef(wc)); aoqi@0: } aoqi@0: aoqi@0: public void modelGroupDecl(XSModelGroupDecl decl) { aoqi@0: modelGroup(decl.getModelGroup()); aoqi@0: } aoqi@0: aoqi@0: public void modelGroup(XSModelGroup group) { aoqi@0: for( XSParticle p : group.getChildren()) aoqi@0: particle(p); aoqi@0: } aoqi@0: aoqi@0: public void elementDecl(XSElementDecl decl) { aoqi@0: aoqi@0: QName n = BGMBuilder.getName(decl); aoqi@0: if(elementNames.add(n)) { aoqi@0: CElement elementBean = Ring.get(ClassSelector.class).bindToType(decl,null); aoqi@0: if(elementBean==null) aoqi@0: refs.add(new XmlTypeRef(decl)); aoqi@0: else { aoqi@0: // yikes! aoqi@0: if(elementBean instanceof CClass) aoqi@0: refs.add(new CClassRef(decl,(CClass)elementBean)); aoqi@0: else aoqi@0: refs.add(new CElementInfoRef(decl,(CElementInfo)elementBean)); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Reference to a wildcard. aoqi@0: */ aoqi@0: public static final class WildcardRef extends RawTypeSet.Ref { aoqi@0: private final WildcardMode mode; aoqi@0: aoqi@0: WildcardRef(XSWildcard wildcard) { aoqi@0: this.mode = getMode(wildcard); aoqi@0: } aoqi@0: WildcardRef(WildcardMode mode) { aoqi@0: this.mode = mode; aoqi@0: } aoqi@0: aoqi@0: private static WildcardMode getMode(XSWildcard wildcard) { aoqi@0: switch(wildcard.getMode()) { aoqi@0: case XSWildcard.LAX: aoqi@0: return WildcardMode.LAX; aoqi@0: case XSWildcard.STRTICT: aoqi@0: return WildcardMode.STRICT; aoqi@0: case XSWildcard.SKIP: aoqi@0: return WildcardMode.SKIP; aoqi@0: default: aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: protected CTypeRef toTypeRef(CElementPropertyInfo ep) { aoqi@0: // we don't allow a mapping to typeRef if the wildcard is present aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: aoqi@0: protected void toElementRef(CReferencePropertyInfo prop) { aoqi@0: prop.setWildcard(mode); aoqi@0: } aoqi@0: aoqi@0: protected RawTypeSet.Mode canBeType(RawTypeSet parent) { aoqi@0: return RawTypeSet.Mode.MUST_BE_REFERENCE; aoqi@0: } aoqi@0: aoqi@0: protected boolean isListOfValues() { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: protected ID id() { aoqi@0: return ID.NONE; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Reference to a class that maps from an element. aoqi@0: */ aoqi@0: public static final class CClassRef extends RawTypeSet.Ref { aoqi@0: public final CClass target; aoqi@0: public final XSElementDecl decl; aoqi@0: aoqi@0: CClassRef(XSElementDecl decl, CClass target) { aoqi@0: this.decl = decl; aoqi@0: this.target = target; aoqi@0: } aoqi@0: aoqi@0: protected CTypeRef toTypeRef(CElementPropertyInfo ep) { aoqi@0: return new CTypeRef(target,decl); aoqi@0: } aoqi@0: aoqi@0: protected void toElementRef(CReferencePropertyInfo prop) { aoqi@0: prop.getElements().add(target); aoqi@0: } aoqi@0: aoqi@0: protected RawTypeSet.Mode canBeType(RawTypeSet parent) { aoqi@0: // if element substitution can occur, no way it can be mapped to a list of types aoqi@0: if(decl.getSubstitutables().size()>1) aoqi@0: return RawTypeSet.Mode.MUST_BE_REFERENCE; aoqi@0: aoqi@0: return RawTypeSet.Mode.SHOULD_BE_TYPEREF; aoqi@0: } aoqi@0: aoqi@0: protected boolean isListOfValues() { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: protected ID id() { aoqi@0: return ID.NONE; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Reference to a class that maps from an element. aoqi@0: */ aoqi@0: public final class CElementInfoRef extends RawTypeSet.Ref { aoqi@0: public final CElementInfo target; aoqi@0: public final XSElementDecl decl; aoqi@0: aoqi@0: CElementInfoRef(XSElementDecl decl, CElementInfo target) { aoqi@0: this.decl = decl; aoqi@0: this.target = target; aoqi@0: } aoqi@0: aoqi@0: protected CTypeRef toTypeRef(CElementPropertyInfo ep) { aoqi@0: assert !target.isCollection(); aoqi@0: CAdapter a = target.getProperty().getAdapter(); aoqi@0: if(a!=null && ep!=null) ep.setAdapter(a); aoqi@0: aoqi@0: return new CTypeRef(target.getContentType(),decl); aoqi@0: } aoqi@0: aoqi@0: protected void toElementRef(CReferencePropertyInfo prop) { aoqi@0: prop.getElements().add(target); aoqi@0: } aoqi@0: aoqi@0: protected RawTypeSet.Mode canBeType(RawTypeSet parent) { aoqi@0: // if element substitution can occur, no way it can be mapped to a list of types aoqi@0: if(decl.getSubstitutables().size()>1) aoqi@0: return RawTypeSet.Mode.MUST_BE_REFERENCE; aoqi@0: // BIXSubstitutable also simulates this effect. Useful for separate compilation aoqi@0: BIXSubstitutable subst = builder.getBindInfo(decl).get(BIXSubstitutable.class); aoqi@0: if(subst!=null) { aoqi@0: subst.markAsAcknowledged(); aoqi@0: return RawTypeSet.Mode.MUST_BE_REFERENCE; aoqi@0: } aoqi@0: aoqi@0: // we have no place to put an adater if this thing maps to a type aoqi@0: CElementPropertyInfo p = target.getProperty(); aoqi@0: // if we have an adapter or IDness, which requires special aoqi@0: // annotation, and there's more than one element, aoqi@0: // we have no place to put the special annotation, so we need JAXBElement. aoqi@0: if((parent.refs.size()>1 || !parent.mul.isAtMostOnce()) && p.id()!=ID.NONE) aoqi@0: return RawTypeSet.Mode.MUST_BE_REFERENCE; aoqi@0: if(parent.refs.size() > 1 && p.getAdapter() != null) aoqi@0: return RawTypeSet.Mode.MUST_BE_REFERENCE; aoqi@0: aoqi@0: if(target.hasClass()) aoqi@0: // if the CElementInfo was explicitly bound to a class (which happen if and only if aoqi@0: // the user requested so, then map that to reference property so that the user sees a class aoqi@0: return RawTypeSet.Mode.CAN_BE_TYPEREF; aoqi@0: else aoqi@0: return RawTypeSet.Mode.SHOULD_BE_TYPEREF; aoqi@0: } aoqi@0: aoqi@0: protected boolean isListOfValues() { aoqi@0: return target.getProperty().isValueList(); aoqi@0: } aoqi@0: aoqi@0: protected ID id() { aoqi@0: return target.getProperty().id(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected MimeType getExpectedMimeType() { aoqi@0: return target.getProperty().getExpectedMimeType(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * References to a type. Could be global or local. aoqi@0: */ aoqi@0: public static final class XmlTypeRef extends RawTypeSet.Ref { aoqi@0: private final XSElementDecl decl; aoqi@0: private final TypeUse target; aoqi@0: aoqi@0: public XmlTypeRef(XSElementDecl decl) { aoqi@0: this.decl = decl; aoqi@0: SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class); aoqi@0: stb.refererStack.push(decl); aoqi@0: TypeUse r = Ring.get(ClassSelector.class).bindToType(decl.getType(),decl); aoqi@0: stb.refererStack.pop(); aoqi@0: target = r; aoqi@0: } aoqi@0: aoqi@0: protected CTypeRef toTypeRef(CElementPropertyInfo ep) { aoqi@0: if(ep!=null && target.getAdapterUse()!=null) aoqi@0: ep.setAdapter(target.getAdapterUse()); aoqi@0: return new CTypeRef(target.getInfo(),decl); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * The whole type set can be later bound to a reference property, aoqi@0: * in which case we need to generate additional code to wrap this aoqi@0: * type reference into an element class. aoqi@0: * aoqi@0: * This method generates such an element class and returns it. aoqi@0: */ aoqi@0: protected void toElementRef(CReferencePropertyInfo prop) { aoqi@0: CClassInfo scope = Ring.get(ClassSelector.class).getCurrentBean(); aoqi@0: Model model = Ring.get(Model.class); aoqi@0: aoqi@0: CCustomizations custs = Ring.get(BGMBuilder.class).getBindInfo(decl).toCustomizationList(); aoqi@0: aoqi@0: if(target instanceof CClassInfo && Ring.get(BIGlobalBinding.class).isSimpleMode()) { aoqi@0: CClassInfo bean = new CClassInfo(model,scope, aoqi@0: model.getNameConverter().toClassName(decl.getName()), aoqi@0: decl.getLocator(), null, BGMBuilder.getName(decl), decl, aoqi@0: custs); aoqi@0: bean.setBaseClass((CClassInfo)target); aoqi@0: prop.getElements().add(bean); aoqi@0: } else { aoqi@0: CElementInfo e = new CElementInfo(model,BGMBuilder.getName(decl),scope,target, aoqi@0: decl.getDefaultValue(), decl, custs, decl.getLocator()); aoqi@0: prop.getElements().add(e); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: protected RawTypeSet.Mode canBeType(RawTypeSet parent) { aoqi@0: // if we have an adapter or IDness, which requires special aoqi@0: // annotation, and there's more than one element, aoqi@0: // we have no place to put the special annotation, so we need JAXBElement. aoqi@0: if((parent.refs.size()>1 || !parent.mul.isAtMostOnce()) && target.idUse()!=ID.NONE) aoqi@0: return RawTypeSet.Mode.MUST_BE_REFERENCE; aoqi@0: if(parent.refs.size() > 1 && target.getAdapterUse() != null) aoqi@0: return RawTypeSet.Mode.MUST_BE_REFERENCE; aoqi@0: aoqi@0: // nillable and optional at the same time. needs an element wrapper to distinguish those aoqi@0: // two states. But this is not a hard requirement. aoqi@0: if(decl.isNillable() && parent.mul.isOptional()) aoqi@0: return RawTypeSet.Mode.CAN_BE_TYPEREF; aoqi@0: aoqi@0: return RawTypeSet.Mode.SHOULD_BE_TYPEREF; aoqi@0: } aoqi@0: aoqi@0: protected boolean isListOfValues() { aoqi@0: return target.isCollection(); aoqi@0: } aoqi@0: aoqi@0: protected ID id() { aoqi@0: return target.idUse(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected MimeType getExpectedMimeType() { aoqi@0: return target.getExpectedMimeType(); aoqi@0: } aoqi@0: } aoqi@0: }