PropertyInfoImpl.java revision 524:dcaa586ab756
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 26package com.sun.xml.internal.bind.v2.model.impl; 27 28import java.util.Collection; 29import java.lang.annotation.Annotation; 30 31import javax.activation.MimeType; 32import javax.xml.bind.annotation.XmlAttachmentRef; 33import javax.xml.bind.annotation.XmlElement; 34import javax.xml.bind.annotation.XmlElementWrapper; 35import javax.xml.bind.annotation.XmlID; 36import javax.xml.bind.annotation.XmlIDREF; 37import javax.xml.bind.annotation.XmlInlineBinaryData; 38import javax.xml.bind.annotation.XmlMimeType; 39import javax.xml.bind.annotation.XmlSchema; 40import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 41import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; 42import javax.xml.bind.annotation.adapters.XmlAdapter; 43import javax.xml.namespace.QName; 44 45import com.sun.xml.internal.bind.v2.TODO; 46import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader; 47import com.sun.xml.internal.bind.v2.model.annotation.Locatable; 48import com.sun.xml.internal.bind.v2.model.core.Adapter; 49import com.sun.xml.internal.bind.v2.model.core.ID; 50import com.sun.xml.internal.bind.v2.model.core.PropertyInfo; 51import com.sun.xml.internal.bind.v2.model.core.TypeInfo; 52import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet; 53import com.sun.xml.internal.bind.v2.model.nav.Navigator; 54import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException; 55import com.sun.xml.internal.bind.v2.runtime.Location; 56import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter; 57 58/** 59 * Default partial implementation for {@link PropertyInfo}. 60 * 61 * @author Kohsuke Kawaguchi 62 */ 63abstract class PropertyInfoImpl<T,C,F,M> 64 implements PropertyInfo<T,C>, Locatable, Comparable<PropertyInfoImpl> /*by their names*/ { 65 66 /** 67 * Object that reads annotations. 68 */ 69 protected final PropertySeed<T,C,F,M> seed; 70 71 private final boolean isCollection; 72 73 private final ID id; 74 75 private final MimeType expectedMimeType; 76 private final boolean inlineBinary; 77 private final QName schemaType; 78 79 protected final ClassInfoImpl<T,C,F,M> parent; 80 81 private final Adapter<T,C> adapter; 82 83 protected PropertyInfoImpl(ClassInfoImpl<T,C,F,M> parent, PropertySeed<T,C,F,M> spi) { 84 this.seed = spi; 85 this.parent = parent; 86 87 if(parent==null) 88 /* 89 Various people reported a bug where this parameter is somehow null. 90 In an attempt to catch the error better, let's do an explicit check here. 91 92 http://forums.java.net/jive/thread.jspa?threadID=18479 93 http://forums.java.net/jive/thread.jspa?messageID=165946 94 */ 95 throw new AssertionError(); 96 97 MimeType mt = Util.calcExpectedMediaType(seed,parent.builder); 98 if(mt!=null && !kind().canHaveXmlMimeType) { 99 parent.builder.reportError(new IllegalAnnotationException( 100 Messages.ILLEGAL_ANNOTATION.format(XmlMimeType.class.getName()), 101 seed.readAnnotation(XmlMimeType.class) 102 )); 103 mt = null; 104 } 105 this.expectedMimeType = mt; 106 this.inlineBinary = seed.hasAnnotation(XmlInlineBinaryData.class); 107 108 T t = seed.getRawType(); 109 110 // check if there's an adapter applicable to the whole property 111 XmlJavaTypeAdapter xjta = getApplicableAdapter(t); 112 if(xjta!=null) { 113 isCollection = false; 114 adapter = new Adapter<T,C>(xjta,reader(),nav()); 115 } else { 116 // check if the adapter is applicable to the individual item in the property 117 118 this.isCollection = nav().isSubClassOf(t, nav().ref(Collection.class)) 119 || nav().isArrayButNotByteArray(t); 120 121 xjta = getApplicableAdapter(getIndividualType()); 122 if(xjta==null) { 123 // ugly ugly hack, but we implement swaRef as adapter 124 XmlAttachmentRef xsa = seed.readAnnotation(XmlAttachmentRef.class); 125 if(xsa!=null) { 126 parent.builder.hasSwaRef = true; 127 adapter = new Adapter<T,C>(nav().asDecl(SwaRefAdapter.class),nav()); 128 } else { 129 adapter = null; 130 131 // if this field has adapter annotation but not applicable, 132 // that must be an error of the user 133 xjta = seed.readAnnotation(XmlJavaTypeAdapter.class); 134 if(xjta!=null) { 135 T ad = reader().getClassValue(xjta,"value"); 136 parent.builder.reportError(new IllegalAnnotationException( 137 Messages.UNMATCHABLE_ADAPTER.format( 138 nav().getTypeName(ad), nav().getTypeName(t)), 139 xjta 140 )); 141 } 142 } 143 } else { 144 adapter = new Adapter<T,C>(xjta,reader(),nav()); 145 } 146 } 147 148 this.id = calcId(); 149 this.schemaType = Util.calcSchemaType(reader(),seed,parent.clazz, 150 getIndividualType(),this); 151 } 152 153 154 public ClassInfoImpl<T,C,F,M> parent() { 155 return parent; 156 } 157 158 protected final Navigator<T,C,F,M> nav() { 159 return parent.nav(); 160 } 161 protected final AnnotationReader<T,C,F,M> reader() { 162 return parent.reader(); 163 } 164 165 public T getRawType() { 166 return seed.getRawType(); 167 } 168 169 public T getIndividualType() { 170 if(adapter!=null) 171 return adapter.defaultType; 172 T raw = getRawType(); 173 if(!isCollection()) { 174 return raw; 175 } else { 176 if(nav().isArrayButNotByteArray(raw)) 177 return nav().getComponentType(raw); 178 179 T bt = nav().getBaseClass(raw, nav().asDecl(Collection.class) ); 180 if(nav().isParameterizedType(bt)) 181 return nav().getTypeArgument(bt,0); 182 else 183 return nav().ref(Object.class); 184 } 185 } 186 187 public final String getName() { 188 return seed.getName(); 189 } 190 191 /** 192 * Checks if the given adapter is applicable to the declared property type. 193 */ 194 private boolean isApplicable(XmlJavaTypeAdapter jta, T declaredType ) { 195 if(jta==null) return false; 196 197 T type = reader().getClassValue(jta,"type"); 198 if(nav().isSameType(declaredType, type)) 199 return true; // for types explicitly marked in XmlJavaTypeAdapter.type() 200 201 T ad = reader().getClassValue(jta,"value"); 202 T ba = nav().getBaseClass(ad, nav().asDecl(XmlAdapter.class)); 203 if(!nav().isParameterizedType(ba)) 204 return true; // can't check type applicability. assume Object, which means applicable to any. 205 T inMemType = nav().getTypeArgument(ba, 1); 206 207 return nav().isSubClassOf(declaredType,inMemType); 208 } 209 210 private XmlJavaTypeAdapter getApplicableAdapter(T type) { 211 XmlJavaTypeAdapter jta = seed.readAnnotation(XmlJavaTypeAdapter.class); 212 if(jta!=null && isApplicable(jta,type)) 213 return jta; 214 215 // check the applicable adapters on the package 216 XmlJavaTypeAdapters jtas = reader().getPackageAnnotation(XmlJavaTypeAdapters.class, parent.clazz, seed ); 217 if(jtas!=null) { 218 for (XmlJavaTypeAdapter xjta : jtas.value()) { 219 if(isApplicable(xjta,type)) 220 return xjta; 221 } 222 } 223 jta = reader().getPackageAnnotation(XmlJavaTypeAdapter.class, parent.clazz, seed ); 224 if(isApplicable(jta,type)) 225 return jta; 226 227 // then on the target class 228 C refType = nav().asDecl(type); 229 if(refType!=null) { 230 jta = reader().getClassAnnotation(XmlJavaTypeAdapter.class, refType, seed ); 231 if(jta!=null && isApplicable(jta,type)) // the one on the type always apply. 232 return jta; 233 } 234 235 return null; 236 } 237 238 /** 239 * This is the default implementation of the getAdapter method 240 * defined on many of the {@link PropertyInfo}-derived classes. 241 */ 242 public Adapter<T,C> getAdapter() { 243 return adapter; 244 } 245 246 247 public final String displayName() { 248 return nav().getClassName(parent.getClazz())+'#'+getName(); 249 } 250 251 public final ID id() { 252 return id; 253 } 254 255 private ID calcId() { 256 if(seed.hasAnnotation(XmlID.class)) { 257 // check the type 258 if(!nav().isSameType(getIndividualType(), nav().ref(String.class))) 259 parent.builder.reportError(new IllegalAnnotationException( 260 Messages.ID_MUST_BE_STRING.format(getName()), seed ) 261 ); 262 return ID.ID; 263 } else 264 if(seed.hasAnnotation(XmlIDREF.class)) { 265 return ID.IDREF; 266 } else { 267 return ID.NONE; 268 } 269 } 270 271 public final MimeType getExpectedMimeType() { 272 return expectedMimeType; 273 } 274 275 public final boolean inlineBinaryData() { 276 return inlineBinary; 277 } 278 279 public final QName getSchemaType() { 280 return schemaType; 281 } 282 283 public final boolean isCollection() { 284 return isCollection; 285 } 286 287 /** 288 * Called after all the {@link TypeInfo}s are collected into the governing {@link TypeInfoSet}. 289 * 290 * Derived class can do additional actions to complete the model. 291 */ 292 protected void link() { 293 if(id==ID.IDREF) { 294 // make sure that the refereced type has ID 295 for (TypeInfo<T,C> ti : ref()) { 296 if(!ti.canBeReferencedByIDREF()) 297 parent.builder.reportError(new IllegalAnnotationException( 298 Messages.INVALID_IDREF.format( 299 parent.builder.nav.getTypeName(ti.getType())), this )); 300 } 301 } 302 } 303 304 /** 305 * A {@link PropertyInfoImpl} is always referenced by its enclosing class, 306 * so return that as the upstream. 307 */ 308 public Locatable getUpstream() { 309 return parent; 310 } 311 312 public Location getLocation() { 313 return seed.getLocation(); 314 } 315 316 317// 318// 319// convenience methods for derived classes 320// 321// 322 323 324 /** 325 * Computes the tag name from a {@link XmlElement} by taking the defaulting into account. 326 */ 327 protected final QName calcXmlName(XmlElement e) { 328 if(e!=null) 329 return calcXmlName(e.namespace(),e.name()); 330 else 331 return calcXmlName("##default","##default"); 332 } 333 334 /** 335 * Computes the tag name from a {@link XmlElementWrapper} by taking the defaulting into account. 336 */ 337 protected final QName calcXmlName(XmlElementWrapper e) { 338 if(e!=null) 339 return calcXmlName(e.namespace(),e.name()); 340 else 341 return calcXmlName("##default","##default"); 342 } 343 344 private QName calcXmlName(String uri,String local) { 345 // compute the default 346 TODO.checkSpec(); 347 if(local.length()==0 || local.equals("##default")) 348 local = seed.getName(); 349 if(uri.equals("##default")) { 350 XmlSchema xs = reader().getPackageAnnotation( XmlSchema.class, parent.getClazz(), this ); 351 // JAX-RPC doesn't want the default namespace URI swapping to take effect to 352 // local "unqualified" elements. UGLY. 353 if(xs!=null) { 354 switch(xs.elementFormDefault()) { 355 case QUALIFIED: 356 QName typeName = parent.getTypeName(); 357 if(typeName!=null) 358 uri = typeName.getNamespaceURI(); 359 else 360 uri = xs.namespace(); 361 if(uri.length()==0) 362 uri = parent.builder.defaultNsUri; 363 break; 364 case UNQUALIFIED: 365 case UNSET: 366 uri = ""; 367 } 368 } else { 369 uri = ""; 370 } 371 } 372 return new QName(uri.intern(),local.intern()); 373 } 374 375 public int compareTo(PropertyInfoImpl that) { 376 return this.getName().compareTo(that.getName()); 377 } 378 379 public final <A extends Annotation> A readAnnotation(Class<A> annotationType) { 380 return seed.readAnnotation(annotationType); 381 } 382 383 public final boolean hasAnnotation(Class<? extends Annotation> annotationType) { 384 return seed.hasAnnotation(annotationType); 385 } 386} 387