1 /*
   2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.internal.xjc.model;
  27 
  28 import java.util.Collection;
  29 import java.util.Collections;
  30 import java.util.HashSet;
  31 import java.util.List;
  32 import java.util.Set;
  33 
  34 import javax.xml.bind.JAXBElement;
  35 import javax.xml.bind.annotation.XmlElement;
  36 import javax.xml.namespace.QName;
  37 
  38 import com.sun.codemodel.internal.JPackage;
  39 import com.sun.codemodel.internal.JType;
  40 import com.sun.istack.internal.Nullable;
  41 import static com.sun.tools.internal.xjc.model.CElementPropertyInfo.CollectionMode.NOT_REPEATED;
  42 import static com.sun.tools.internal.xjc.model.CElementPropertyInfo.CollectionMode.REPEATED_VALUE;
  43 import com.sun.tools.internal.xjc.model.nav.NClass;
  44 import com.sun.tools.internal.xjc.model.nav.NType;
  45 import com.sun.tools.internal.xjc.model.nav.NavigatorImpl;
  46 import com.sun.tools.internal.xjc.outline.Aspect;
  47 import com.sun.tools.internal.xjc.outline.Outline;
  48 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIInlineBinaryData;
  49 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIFactoryMethod;
  50 import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder;
  51 import com.sun.tools.internal.xjc.reader.Ring;
  52 import com.sun.xml.internal.bind.v2.model.core.ElementInfo;
  53 import com.sun.xml.internal.xsom.XSElementDecl;
  54 import com.sun.xml.internal.xsom.XmlString;
  55 
  56 import org.xml.sax.Locator;
  57 
  58 /**
  59  * {@link ElementInfo} implementation for the compile-time model.
  60  *
  61  * <p>
  62  * As an NType, it represents the Java representation of this element
  63  * (either {@code JAXBElement<T>} or Foo).
  64  *
  65  * @author Kohsuke Kawaguchi
  66  */
  67 public final class CElementInfo extends AbstractCElement
  68     implements ElementInfo<NType,NClass>, NType, CClassInfoParent {
  69 
  70     private final QName tagName;
  71 
  72     /**
  73      * Represents {@code JAXBElement<ContentType>}.
  74      */
  75     private NType type;
  76 
  77     /**
  78      * If this element produces its own class, the short name of that class.
  79      * Otherwise null.
  80      */
  81     private String className;
  82 
  83     /**
  84      * If this element is global, the element info is considered to be
  85      * package-level, and this points to the package in which this element
  86      * lives in.
  87      *
  88      * <p>
  89      * For local elements, this points to the parent {@link CClassInfo}.
  90      */
  91     public final CClassInfoParent parent;
  92 
  93     private CElementInfo substitutionHead;
  94 
  95     /**
  96      * Lazily computed.
  97      */
  98     private Set<CElementInfo> substitutionMembers;
  99 
 100     /**
 101      * {@link Model} that owns this object.
 102      */
 103     private final Model model;
 104 
 105     private CElementPropertyInfo property;
 106 
 107     /**
 108      * Custom {@link #getSqueezedName() squeezed name}, if any.
 109      */
 110     private /*almost final*/ @Nullable String squeezedName;
 111 
 112     /**
 113      * Creates an element in the given parent.
 114      *
 115      * <p>
 116      * When using this construction, {@link #initContentType(TypeUse, XSElementDecl, XmlString)}
 117      * must not be invoked.
 118      */
 119     public CElementInfo(Model model,QName tagName, CClassInfoParent parent, TypeUse contentType, XmlString defaultValue, XSElementDecl source, CCustomizations customizations, Locator location ) {
 120         super(model,source,location,customizations);
 121         this.tagName = tagName;
 122         this.model = model;
 123         this.parent = parent;
 124         if(contentType!=null)
 125             initContentType(contentType, source, defaultValue);
 126 
 127         model.add(this);
 128     }
 129 
 130     /**
 131      * Creates an element with a class in the given parent.
 132      *
 133      * <p>
 134      * When using this construction, the caller must use
 135      * {@link #initContentType(TypeUse, XSElementDecl, XmlString)} to fill in the content type
 136      * later.
 137      *
 138      * This is to avoid a circular model construction dependency between buidling a type
 139      * inside an element and element itself. To build a content type, you need to have
 140      * {@link CElementInfo} for a parent, so we can't take it as a constructor parameter.
 141      */
 142     public CElementInfo(Model model,QName tagName, CClassInfoParent parent, String className, CCustomizations customizations, Locator location ) {
 143         this(model,tagName,parent,null,null,null,customizations,location);
 144         this.className = className;
 145     }
 146 
 147     public void initContentType(TypeUse contentType, @Nullable XSElementDecl source, XmlString defaultValue) {
 148         assert this.property==null; // must not be called twice
 149 
 150         this.property = new CElementPropertyInfo("Value",
 151                 contentType.isCollection()?REPEATED_VALUE:NOT_REPEATED,
 152                 contentType.idUse(),
 153                 contentType.getExpectedMimeType(),
 154                 source,null,getLocator(),true);
 155         this.property.setAdapter(contentType.getAdapterUse());
 156         BIInlineBinaryData.handle(source,property);
 157         property.getTypes().add(new CTypeRef(contentType.getInfo(),tagName,CTypeRef.getSimpleTypeName(source), true,defaultValue));
 158         this.type = NavigatorImpl.createParameterizedType(
 159             NavigatorImpl.theInstance.ref(JAXBElement.class),
 160             getContentInMemoryType() );
 161 
 162         BIFactoryMethod factoryMethod = Ring.get(BGMBuilder.class).getBindInfo(source).get(BIFactoryMethod.class);
 163         if(factoryMethod!=null) {
 164             factoryMethod.markAsAcknowledged();
 165             this.squeezedName = factoryMethod.name;
 166         }
 167 
 168     }
 169 
 170     public final String getDefaultValue() {
 171         return getProperty().getTypes().get(0).getDefaultValue();
 172     }
 173 
 174     public final JPackage _package() {
 175         return parent.getOwnerPackage();
 176     }
 177 
 178     public CNonElement getContentType() {
 179         return getProperty().ref().get(0);
 180     }
 181 
 182     public NType getContentInMemoryType() {
 183         if(getProperty().getAdapter()==null) {
 184             NType itemType = getContentType().getType();
 185             if(!property.isCollection())
 186                 return itemType;
 187 
 188             return NavigatorImpl.createParameterizedType(List.class,itemType);
 189         } else {
 190             return getProperty().getAdapter().customType;
 191         }
 192     }
 193 
 194     public CElementPropertyInfo getProperty() {
 195         return property;
 196     }
 197 
 198     public CClassInfo getScope() {
 199         if(parent instanceof CClassInfo)
 200             return (CClassInfo)parent;
 201         return null;
 202     }
 203 
 204     /**
 205      * @deprecated why are you calling a method that returns this?
 206      */
 207     public NType getType() {
 208         return this;
 209     }
 210 
 211     public QName getElementName() {
 212         return tagName;
 213     }
 214 
 215     public JType toType(Outline o, Aspect aspect) {
 216         if(className==null)
 217             return type.toType(o,aspect);
 218         else
 219             return o.getElement(this).implClass;
 220     }
 221 
 222     /**
 223      * Returns the "squeezed name" of this element.
 224      *
 225      * @see CClassInfo#getSqueezedName()
 226      */
 227     @XmlElement
 228     public String getSqueezedName() {
 229         if(squeezedName!=null)  return squeezedName;
 230 
 231         StringBuilder b = new StringBuilder();
 232         CClassInfo s = getScope();
 233         if(s!=null)
 234             b.append(s.getSqueezedName());
 235         if(className!=null)
 236             b.append(className);
 237         else
 238             b.append( model.getNameConverter().toClassName(tagName.getLocalPart()));
 239         return b.toString();
 240     }
 241 
 242     public CElementInfo getSubstitutionHead() {
 243         return substitutionHead;
 244     }
 245 
 246     public Collection<CElementInfo> getSubstitutionMembers() {
 247         if(substitutionMembers==null)
 248             return Collections.emptyList();
 249         else
 250             return substitutionMembers;
 251     }
 252 
 253     public void setSubstitutionHead(CElementInfo substitutionHead) {
 254         // don't set it twice
 255         assert this.substitutionHead==null;
 256         assert substitutionHead!=null;
 257         this.substitutionHead = substitutionHead;
 258 
 259         if(substitutionHead.substitutionMembers==null)
 260             substitutionHead.substitutionMembers = new HashSet<CElementInfo>();
 261         substitutionHead.substitutionMembers.add(this);
 262     }
 263 
 264     public boolean isBoxedType() {
 265         return false;
 266     }
 267 
 268     public String fullName() {
 269         if(className==null)
 270             return type.fullName();
 271         else {
 272             String r = parent.fullName();
 273             if(r.length()==0)   return className;
 274             else                return r+'.'+className;
 275         }
 276     }
 277 
 278     public <T> T accept(Visitor<T> visitor) {
 279         return visitor.onElement(this);
 280     }
 281 
 282     public JPackage getOwnerPackage() {
 283         return parent.getOwnerPackage();
 284     }
 285 
 286     public String shortName() {
 287         return className;
 288     }
 289 
 290     /**
 291      * True if this element has its own class
 292      * (as opposed to be represented as an instance of {@link JAXBElement}.
 293      */
 294     public boolean hasClass() {
 295         return className!=null;
 296     }
 297 }