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