/* * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.internal.xjc.reader.xmlschema; import java.util.HashSet; import java.util.Set; import javax.activation.MimeType; import javax.xml.namespace.QName; import com.sun.tools.internal.xjc.model.CAdapter; import com.sun.tools.internal.xjc.model.CClass; import com.sun.tools.internal.xjc.model.CClassInfo; import com.sun.tools.internal.xjc.model.CCustomizations; import com.sun.tools.internal.xjc.model.CElement; import com.sun.tools.internal.xjc.model.CElementInfo; import com.sun.tools.internal.xjc.model.CElementPropertyInfo; import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; import com.sun.tools.internal.xjc.model.CTypeRef; import com.sun.tools.internal.xjc.model.Model; import com.sun.tools.internal.xjc.model.Multiplicity; import com.sun.tools.internal.xjc.model.TypeUse; import com.sun.tools.internal.xjc.reader.RawTypeSet; import com.sun.tools.internal.xjc.reader.Ring; import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDom; import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding; import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIXSubstitutable; import com.sun.xml.internal.bind.v2.model.core.ID; import com.sun.xml.internal.bind.v2.model.core.WildcardMode; import com.sun.xml.internal.xsom.XSElementDecl; import com.sun.xml.internal.xsom.XSModelGroup; import com.sun.xml.internal.xsom.XSModelGroupDecl; import com.sun.xml.internal.xsom.XSParticle; import com.sun.xml.internal.xsom.XSWildcard; import com.sun.xml.internal.xsom.visitor.XSTermVisitor; /** * Builds {@link RawTypeSet} for XML Schema. * * @author Kohsuke Kawaguchi */ public class RawTypeSetBuilder implements XSTermVisitor { /** * @param optional * if this whole property is optional due to the * occurence constraints on ancestors, set this to true. * this will prevent the primitive types to be generated. */ public static RawTypeSet build( XSParticle p, boolean optional ) { RawTypeSetBuilder rtsb = new RawTypeSetBuilder(); rtsb.particle(p); Multiplicity mul = MultiplicityCounter.theInstance.particle(p); if(optional) mul = mul.makeOptional(); return new RawTypeSet(rtsb.refs,mul); } /** * To avoid declaring the same element twice for a content model like * (A,A), we keep track of element names here while we are building up * this instance. */ private final Set<QName> elementNames = new HashSet<QName>(); private final Set<RawTypeSet.Ref> refs = new HashSet<RawTypeSet.Ref>(); protected final BGMBuilder builder = Ring.get(BGMBuilder.class); public RawTypeSetBuilder() {} /** * Gets the {@link RawTypeSet.Ref}s that were built. */ public Set<RawTypeSet.Ref> getRefs() { return refs; } /** * Build up {@link #refs} and compute the total multiplicity of this {@link RawTypeSet.Ref} set. */ private void particle( XSParticle p ) { // if the DOM customization is present, bind it like a wildcard BIDom dom = builder.getLocalDomCustomization(p); if(dom!=null) { dom.markAsAcknowledged(); refs.add(new WildcardRef(WildcardMode.SKIP)); } else { p.getTerm().visit(this); } } public void wildcard(XSWildcard wc) { refs.add(new WildcardRef(wc)); } public void modelGroupDecl(XSModelGroupDecl decl) { modelGroup(decl.getModelGroup()); } public void modelGroup(XSModelGroup group) { for( XSParticle p : group.getChildren()) particle(p); } public void elementDecl(XSElementDecl decl) { QName n = BGMBuilder.getName(decl); if(elementNames.add(n)) { CElement elementBean = Ring.get(ClassSelector.class).bindToType(decl,null); if(elementBean==null) refs.add(new XmlTypeRef(decl)); else { // yikes! if(elementBean instanceof CClass) refs.add(new CClassRef(decl,(CClass)elementBean)); else refs.add(new CElementInfoRef(decl,(CElementInfo)elementBean)); } } } /** * Reference to a wildcard. */ public static final class WildcardRef extends RawTypeSet.Ref { private final WildcardMode mode; WildcardRef(XSWildcard wildcard) { this.mode = getMode(wildcard); } WildcardRef(WildcardMode mode) { this.mode = mode; } private static WildcardMode getMode(XSWildcard wildcard) { switch(wildcard.getMode()) { case XSWildcard.LAX: return WildcardMode.LAX; case XSWildcard.STRTICT: return WildcardMode.STRICT; case XSWildcard.SKIP: return WildcardMode.SKIP; default: throw new IllegalStateException(); } } protected CTypeRef toTypeRef(CElementPropertyInfo ep) { // we don't allow a mapping to typeRef if the wildcard is present throw new IllegalStateException(); } protected void toElementRef(CReferencePropertyInfo prop) { prop.setWildcard(mode); } protected RawTypeSet.Mode canBeType(RawTypeSet parent) { return RawTypeSet.Mode.MUST_BE_REFERENCE; } protected boolean isListOfValues() { return false; } protected ID id() { return ID.NONE; } } /** * Reference to a class that maps from an element. */ public static final class CClassRef extends RawTypeSet.Ref { public final CClass target; public final XSElementDecl decl; CClassRef(XSElementDecl decl, CClass target) { this.decl = decl; this.target = target; } protected CTypeRef toTypeRef(CElementPropertyInfo ep) { return new CTypeRef(target,decl); } protected void toElementRef(CReferencePropertyInfo prop) { prop.getElements().add(target); } protected RawTypeSet.Mode canBeType(RawTypeSet parent) { // if element substitution can occur, no way it can be mapped to a list of types if(decl.getSubstitutables().size()>1) return RawTypeSet.Mode.MUST_BE_REFERENCE; return RawTypeSet.Mode.SHOULD_BE_TYPEREF; } protected boolean isListOfValues() { return false; } protected ID id() { return ID.NONE; } } /** * Reference to a class that maps from an element. */ public final class CElementInfoRef extends RawTypeSet.Ref { public final CElementInfo target; public final XSElementDecl decl; CElementInfoRef(XSElementDecl decl, CElementInfo target) { this.decl = decl; this.target = target; } protected CTypeRef toTypeRef(CElementPropertyInfo ep) { assert !target.isCollection(); CAdapter a = target.getProperty().getAdapter(); if(a!=null && ep!=null) ep.setAdapter(a); return new CTypeRef(target.getContentType(),decl); } protected void toElementRef(CReferencePropertyInfo prop) { prop.getElements().add(target); } protected RawTypeSet.Mode canBeType(RawTypeSet parent) { // if element substitution can occur, no way it can be mapped to a list of types if(decl.getSubstitutables().size()>1) return RawTypeSet.Mode.MUST_BE_REFERENCE; // BIXSubstitutable also simulates this effect. Useful for separate compilation BIXSubstitutable subst = builder.getBindInfo(decl).get(BIXSubstitutable.class); if(subst!=null) { subst.markAsAcknowledged(); return RawTypeSet.Mode.MUST_BE_REFERENCE; } // we have no place to put an adater if this thing maps to a type CElementPropertyInfo p = target.getProperty(); // if we have an adapter or IDness, which requires special // annotation, and there's more than one element, // we have no place to put the special annotation, so we need JAXBElement. if((parent.refs.size()>1 || !parent.mul.isAtMostOnce()) && p.id()!=ID.NONE) return RawTypeSet.Mode.MUST_BE_REFERENCE; if(parent.refs.size() > 1 && p.getAdapter() != null) return RawTypeSet.Mode.MUST_BE_REFERENCE; if(target.hasClass()) // if the CElementInfo was explicitly bound to a class (which happen if and only if // the user requested so, then map that to reference property so that the user sees a class return RawTypeSet.Mode.CAN_BE_TYPEREF; else return RawTypeSet.Mode.SHOULD_BE_TYPEREF; } protected boolean isListOfValues() { return target.getProperty().isValueList(); } protected ID id() { return target.getProperty().id(); } protected MimeType getExpectedMimeType() { return target.getProperty().getExpectedMimeType(); } } /** * References to a type. Could be global or local. */ public static final class XmlTypeRef extends RawTypeSet.Ref { private final XSElementDecl decl; private final TypeUse target; public XmlTypeRef(XSElementDecl decl) { this.decl = decl; SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class); stb.refererStack.push(decl); TypeUse r = Ring.get(ClassSelector.class).bindToType(decl.getType(),decl); stb.refererStack.pop(); target = r; } protected CTypeRef toTypeRef(CElementPropertyInfo ep) { if(ep!=null && target.getAdapterUse()!=null) ep.setAdapter(target.getAdapterUse()); return new CTypeRef(target.getInfo(),decl); } /** * The whole type set can be later bound to a reference property, * in which case we need to generate additional code to wrap this * type reference into an element class. * * This method generates such an element class and returns it. */ protected void toElementRef(CReferencePropertyInfo prop) { CClassInfo scope = Ring.get(ClassSelector.class).getCurrentBean(); Model model = Ring.get(Model.class); CCustomizations custs = Ring.get(BGMBuilder.class).getBindInfo(decl).toCustomizationList(); if(target instanceof CClassInfo && Ring.get(BIGlobalBinding.class).isSimpleMode()) { CClassInfo bean = new CClassInfo(model,scope, model.getNameConverter().toClassName(decl.getName()), decl.getLocator(), null, BGMBuilder.getName(decl), decl, custs); bean.setBaseClass((CClassInfo)target); prop.getElements().add(bean); } else { CElementInfo e = new CElementInfo(model,BGMBuilder.getName(decl),scope,target, decl.getDefaultValue(), decl, custs, decl.getLocator()); prop.getElements().add(e); } } protected RawTypeSet.Mode canBeType(RawTypeSet parent) { // if we have an adapter or IDness, which requires special // annotation, and there's more than one element, // we have no place to put the special annotation, so we need JAXBElement. if((parent.refs.size()>1 || !parent.mul.isAtMostOnce()) && target.idUse()!=ID.NONE) return RawTypeSet.Mode.MUST_BE_REFERENCE; if(parent.refs.size() > 1 && target.getAdapterUse() != null) return RawTypeSet.Mode.MUST_BE_REFERENCE; // nillable and optional at the same time. needs an element wrapper to distinguish those // two states. But this is not a hard requirement. if(decl.isNillable() && parent.mul.isOptional()) return RawTypeSet.Mode.CAN_BE_TYPEREF; return RawTypeSet.Mode.SHOULD_BE_TYPEREF; } protected boolean isListOfValues() { return target.isCollection(); } protected ID id() { return target.idUse(); } protected MimeType getExpectedMimeType() { return target.getExpectedMimeType(); } } }