1 /* 2 * Copyright (c) 1997, 2012, 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.lang.annotation.Annotation; 29 import java.util.Collection; 30 import java.util.Map; 31 32 import javax.xml.XMLConstants; 33 import javax.xml.bind.annotation.XmlTransient; 34 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 35 import javax.xml.namespace.QName; 36 37 import com.sun.codemodel.internal.JClass; 38 import com.sun.codemodel.internal.JJavaName; 39 import com.sun.codemodel.internal.JType; 40 import com.sun.tools.internal.xjc.generator.bean.field.FieldRenderer; 41 import com.sun.tools.internal.xjc.model.nav.NClass; 42 import com.sun.tools.internal.xjc.model.nav.NType; 43 import com.sun.tools.internal.xjc.reader.Ring; 44 import com.sun.xml.internal.bind.api.impl.NameConverter; 45 import com.sun.xml.internal.bind.v2.WellKnownNamespace; 46 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo; 47 import com.sun.xml.internal.bind.v2.runtime.RuntimeUtil; 48 import com.sun.xml.internal.xsom.XSComponent; 49 50 import org.xml.sax.Locator; 51 52 /** 53 * Model of a property to be generated. 54 * 55 * @author Kohsuke Kawaguchi 56 */ 57 public abstract class CPropertyInfo implements PropertyInfo<NType,NClass>, CCustomizable { 58 59 @XmlTransient 60 private CClassInfo parent; 61 private String privateName; 62 private String publicName; 63 private final boolean isCollection; 64 65 @XmlTransient 66 public final Locator locator; 67 68 /** 69 * @see #getSchemaComponent() 70 */ 71 private final XSComponent source; 72 73 /** 74 * If the base type of the property is overriden, 75 * this field is set to non-null. 76 */ 77 public JType baseType; 78 79 /** 80 * Javadoc for this property. Must not be null. 81 */ 82 public String javadoc=""; 83 84 /** 85 * Property with {@link @XmlInlineBinaryData}. 86 */ 87 public boolean inlineBinaryData; 88 89 /** 90 * Specifies how the field is generated by the backend. 91 */ 92 @XmlJavaTypeAdapter(RuntimeUtil.ToStringAdapter.class) 93 public FieldRenderer realization; 94 95 /** 96 * If non-null, keeps the default value in Java representation. 97 * 98 * If {@link #isCollection} is true, this field is always null, 99 * for we don't handle default values for a list. 100 */ 101 public CDefaultValue defaultValue; 102 103 private final CCustomizations customizations; 104 105 protected CPropertyInfo(String name, boolean collection, XSComponent source, 106 CCustomizations customizations, Locator locator) { 107 this.publicName = name; 108 String n = null; 109 110 Model m = Ring.get(Model.class); 111 if (m != null) { 112 n = m.getNameConverter().toVariableName(name); 113 } else { 114 n = NameConverter.standard.toVariableName(name); 115 } 116 117 if(!JJavaName.isJavaIdentifier(n)) 118 n = '_'+n; // avoid colliding with the reserved names like 'abstract'. 119 this.privateName = n; 120 121 this.isCollection = collection; 122 this.locator = locator; 123 if(customizations==null) 124 this.customizations = CCustomizations.EMPTY; 125 else 126 this.customizations = customizations; 127 this.source = source; 128 } 129 130 // called from CClassInfo when added 131 final void setParent( CClassInfo parent ) { 132 assert this.parent==null; 133 assert parent!=null; 134 this.parent = parent; 135 customizations.setParent(parent.model,this); 136 } 137 138 public CTypeInfo parent() { 139 return parent; 140 } 141 142 public Locator getLocator() { 143 return locator; 144 } 145 146 /** 147 * If this model object is built from XML Schema, 148 * this property returns a schema component from which the model is built. 149 * 150 * @return 151 * null if the model is built from sources other than XML Schema 152 * (such as DTD.) 153 */ 154 public final XSComponent getSchemaComponent() { 155 return source; 156 } 157 158 public abstract CAdapter getAdapter(); 159 160 /** 161 * Name of the property. 162 * 163 * <p> 164 * This method is implemented to follow the contract of 165 * {@link PropertyInfo#getName()}, and therefore it always 166 * returns the name of the annotated field. 167 * <p> 168 * This name is normally not useful for the rest of XJC, 169 * which usually wants to access the "public name" of the property. 170 * A "public name" of the property is a name like "FooBar" which 171 * is used as a seed for generating the accessor methods. 172 * This is the name controlled by the schema customization via users. 173 * 174 * <p> 175 * If the caller is calling this method statically, it's usually 176 * the sign of a mistake. Use {@link #getName(boolean)} method instead, 177 * which forces you to think about which name you want to get. 178 * 179 * @deprecated 180 * marked as deprecated so that we can spot the use of this method. 181 * 182 * @see #getName(boolean) 183 */ 184 public String getName() { 185 return getName(false); 186 } 187 188 /** 189 * Gets the name of the property. 190 * 191 * @param isPublic 192 * if true, this method returns a name like "FooBar", which 193 * should be used as a seed for generating user-visible names 194 * (such as accessors like "getFooBar".) 195 * 196 * <p> 197 * if false, this method returns the "name of the property" 198 * as defined in the j2s side of the spec. This name is usually 199 * something like "fooBar", which often corresponds to the XML 200 * element/attribute name of this property (for taking advantage 201 * of annotation defaulting as much as possible) 202 */ 203 public String getName(boolean isPublic) { 204 return isPublic?publicName:privateName; 205 } 206 207 /** 208 * Overrides the name of the property. 209 * 210 * This method can be used from {@link Plugin#postProcessModel(Model, ErrorHandler)}. 211 * But the caller should do so with the understanding that this is inherently 212 * dangerous method. 213 */ 214 public void setName(boolean isPublic, String newName) { 215 if(isPublic) 216 publicName = newName; 217 else 218 privateName = newName; 219 } 220 221 public String displayName() { 222 return parent.toString()+'#'+getName(false); 223 } 224 225 public boolean isCollection() { 226 return isCollection; 227 } 228 229 230 public abstract Collection<? extends CTypeInfo> ref(); 231 232 /** 233 * Returns true if this property is "unboxable". 234 * 235 * <p> 236 * In general, a property often has to be capable of representing null 237 * to indicate the absence of the value. This requires properties 238 * to be generated as <tt>@XmlElement Float f</tt>, not as 239 * <tt>@XmlElement float f;</tt>. But this is slow. 240 * 241 * <p> 242 * Fortunately, there are cases where we know that the property can 243 * never legally be absent. When this condition holds we can generate 244 * the optimized "unboxed form". 245 * 246 * <p> 247 * The exact such conditions depend 248 * on the kind of properties, so refer to the implementation code 249 * for the details. 250 * 251 * <p> 252 * This method returns true when the property can be generated 253 * as "unboxed form", false otherwise. 254 * 255 * <p> 256 * When this property is a collection, this method returns true 257 * if items in the collection is unboxable. Obviously, the collection 258 * itself is always a reference type. 259 */ 260 public boolean isUnboxable() { 261 Collection<? extends CTypeInfo> ts = ref(); 262 if(ts.size()!=1) 263 // if the property is heterogeneous, no way. 264 // ts.size()==0 is a special case that can happen for wildcards. 265 return false; 266 267 if(baseType!=null && (baseType instanceof JClass)) 268 return false; 269 270 CTypeInfo t = ts.iterator().next(); 271 // only a primitive type is eligible. 272 return t.getType().isBoxedType(); 273 } 274 275 /** 276 * Returns true if this property needs to represent null 277 * just for the purpose of representing an absence of the property. 278 */ 279 public boolean isOptionalPrimitive() { 280 return false; 281 } 282 283 public CCustomizations getCustomizations() { 284 return customizations; 285 } 286 287 public boolean inlineBinaryData() { 288 return inlineBinaryData; 289 } 290 291 public abstract <V> V accept( CPropertyVisitor<V> visitor ); 292 293 /** 294 * Checks if the given {@link TypeUse} would need an explicit {@link XmlSchemaType} 295 * annotation with the given type name. 296 */ 297 protected static boolean needsExplicitTypeName(TypeUse type, QName typeName) { 298 if(typeName==null) 299 // this is anonymous type. can't have @XmlSchemaType 300 return false; 301 302 if(!XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(typeName.getNamespaceURI())) 303 // if we put application-defined type name, it will be undefined 304 // by the time we generate a schema. 305 return false; 306 307 if(type.isCollection()) 308 // there's no built-in binding for a list simple type, 309 // so any collection type always need @XmlSchemaType 310 return true; 311 312 QName itemType = type.getInfo().getTypeName(); 313 if(itemType==null) 314 // this is somewhat strange case, as it means the bound type is anonymous 315 // but it's eventually derived by a named type and used. 316 // but we can certainly use typeName as @XmlSchemaType value here 317 return true; 318 319 // if it's the default type name for this item, then no need 320 return !itemType.equals(typeName); 321 } 322 323 /** 324 * Puts the element names that this property possesses to the map, 325 * so that we can find two properties that own the same element name, 326 * which is an error. 327 * 328 * @return 329 * null if no conflict was found. Otherwise return the QName that has the collision. 330 */ 331 public QName collectElementNames(Map<QName,CPropertyInfo> table) { 332 return null; 333 } 334 335 public final <A extends Annotation> A readAnnotation(Class<A> annotationType) { 336 throw new UnsupportedOperationException(); 337 } 338 339 public final boolean hasAnnotation(Class<? extends Annotation> annotationType) { 340 throw new UnsupportedOperationException(); 341 } 342 }