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