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.lang.annotation.Annotation; 29import java.util.Collection; 30import java.util.Collections; 31import java.util.List; 32 33import javax.activation.MimeType; 34import javax.xml.bind.JAXBElement; 35import javax.xml.bind.annotation.XmlAttachmentRef; 36import javax.xml.bind.annotation.XmlElementDecl; 37import javax.xml.bind.annotation.XmlID; 38import javax.xml.bind.annotation.XmlIDREF; 39import javax.xml.bind.annotation.XmlInlineBinaryData; 40import javax.xml.bind.annotation.XmlSchema; 41import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 42import javax.xml.namespace.QName; 43 44import com.sun.istack.internal.FinalArrayList; 45import com.sun.xml.internal.bind.v2.TODO; 46import com.sun.xml.internal.bind.v2.model.annotation.AnnotationSource; 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.ClassInfo; 50import com.sun.xml.internal.bind.v2.model.core.ElementInfo; 51import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo; 52import com.sun.xml.internal.bind.v2.model.core.ID; 53import com.sun.xml.internal.bind.v2.model.core.NonElement; 54import com.sun.xml.internal.bind.v2.model.core.PropertyInfo; 55import com.sun.xml.internal.bind.v2.model.core.PropertyKind; 56import com.sun.xml.internal.bind.v2.model.core.TypeInfo; 57import com.sun.xml.internal.bind.v2.model.core.TypeRef; 58import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException; 59import com.sun.xml.internal.bind.v2.runtime.Location; 60import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter; 61 62/** 63 * {@link ElementInfo} implementation. 64 * 65 * @author Kohsuke Kawaguchi 66 */ 67class ElementInfoImpl<T,C,F,M> extends TypeInfoImpl<T,C,F,M> implements ElementInfo<T,C> { 68 69 private final QName tagName; 70 71 private final NonElement<T,C> contentType; 72 73 private final T tOfJAXBElementT; 74 75 private final T elementType; 76 77 private final ClassInfo<T,C> scope; 78 79 /** 80 * Annotation that controls the binding. 81 */ 82 private final XmlElementDecl anno; 83 84 /** 85 * If this element can substitute another element, the element name. 86 * @see #link() 87 */ 88 private ElementInfoImpl<T,C,F,M> substitutionHead; 89 90 /** 91 * Lazily constructed list of {@link ElementInfo}s that can substitute this element. 92 * This could be null. 93 * @see #link() 94 */ 95 private FinalArrayList<ElementInfoImpl<T,C,F,M>> substitutionMembers; 96 97 /** 98 * The factory method from which this mapping was created. 99 */ 100 private final M method; 101 102 /** 103 * If the content type is adapter, return that adapter. 104 */ 105 private final Adapter<T,C> adapter; 106 107 private final boolean isCollection; 108 109 private final ID id; 110 111 private final PropertyImpl property; 112 private final MimeType expectedMimeType; 113 private final boolean inlineBinary; 114 private final QName schemaType; 115 116 /** 117 * Singleton instance of {@link ElementPropertyInfo} for this element. 118 */ 119 protected class PropertyImpl implements 120 ElementPropertyInfo<T,C>, 121 TypeRef<T,C>, 122 AnnotationSource { 123 // 124 // TypeRef impl 125 // 126 public NonElement<T,C> getTarget() { 127 return contentType; 128 } 129 public QName getTagName() { 130 return tagName; 131 } 132 133 public List<? extends TypeRef<T,C>> getTypes() { 134 return Collections.singletonList(this); 135 } 136 137 public List<? extends NonElement<T,C>> ref() { 138 return Collections.singletonList(contentType); 139 } 140 141 public QName getXmlName() { 142 return tagName; 143 } 144 145 public boolean isCollectionRequired() { 146 return false; 147 } 148 149 public boolean isCollectionNillable() { 150 return true; 151 } 152 153 public boolean isNillable() { 154 return true; 155 } 156 157 public String getDefaultValue() { 158 String v = anno.defaultValue(); 159 if(v.equals("\u0000")) 160 return null; 161 else 162 return v; 163 } 164 165 public ElementInfoImpl<T,C,F,M> parent() { 166 return ElementInfoImpl.this; 167 } 168 169 public String getName() { 170 return "value"; 171 } 172 173 public String displayName() { 174 return "JAXBElement#value"; 175 } 176 177 public boolean isCollection() { 178 return isCollection; 179 } 180 181 /** 182 * For {@link ElementInfo}s, a collection always means a list of values. 183 */ 184 public boolean isValueList() { 185 return isCollection; 186 } 187 188 public boolean isRequired() { 189 return true; 190 } 191 192 public PropertyKind kind() { 193 return PropertyKind.ELEMENT; 194 } 195 196 public Adapter<T,C> getAdapter() { 197 return adapter; 198 } 199 200 public ID id() { 201 return id; 202 } 203 204 public MimeType getExpectedMimeType() { 205 return expectedMimeType; 206 } 207 208 public QName getSchemaType() { 209 return schemaType; 210 } 211 212 public boolean inlineBinaryData() { 213 return inlineBinary; 214 } 215 216 public PropertyInfo<T,C> getSource() { 217 return this; 218 } 219 220 // 221 // 222 // AnnotationSource impl 223 // 224 // 225 public <A extends Annotation> A readAnnotation(Class<A> annotationType) { 226 return reader().getMethodAnnotation(annotationType,method,ElementInfoImpl.this); 227 } 228 229 public boolean hasAnnotation(Class<? extends Annotation> annotationType) { 230 return reader().hasMethodAnnotation(annotationType,method); 231 } 232 } 233 234 /** 235 * @param m 236 * The factory method on ObjectFactory that comes with {@link XmlElementDecl}. 237 */ 238 public ElementInfoImpl(ModelBuilder<T,C,F,M> builder, 239 RegistryInfoImpl<T,C,F,M> registry, M m ) throws IllegalAnnotationException { 240 super(builder,registry); 241 242 this.method = m; 243 anno = reader().getMethodAnnotation( XmlElementDecl.class, m, this ); 244 assert anno!=null; // the caller should check this 245 assert anno instanceof Locatable; 246 247 elementType = nav().getReturnType(m); 248 T baseClass = nav().getBaseClass(elementType,nav().asDecl(JAXBElement.class)); 249 if(baseClass==null) 250 throw new IllegalAnnotationException( 251 Messages.XML_ELEMENT_MAPPING_ON_NON_IXMLELEMENT_METHOD.format(nav().getMethodName(m)), 252 anno ); 253 254 tagName = parseElementName(anno); 255 T[] methodParams = nav().getMethodParameters(m); 256 257 // adapter 258 Adapter<T,C> a = null; 259 if(methodParams.length>0) { 260 XmlJavaTypeAdapter adapter = reader().getMethodAnnotation(XmlJavaTypeAdapter.class,m,this); 261 if(adapter!=null) 262 a = new Adapter<T,C>(adapter,reader(),nav()); 263 else { 264 XmlAttachmentRef xsa = reader().getMethodAnnotation(XmlAttachmentRef.class,m,this); 265 if(xsa!=null) { 266 TODO.prototype("in Annotation Processing swaRefAdapter isn't avaialble, so this returns null"); 267 a = new Adapter<T,C>(owner.nav.asDecl(SwaRefAdapter.class),owner.nav); 268 } 269 } 270 } 271 this.adapter = a; 272 273 // T of JAXBElement<T> 274 tOfJAXBElementT = 275 methodParams.length>0 ? methodParams[0] // this is more reliable, as it works even for ObjectFactory that sometimes have to return public types 276 : nav().getTypeArgument(baseClass,0); // fall back to infer from the return type if no parameter. 277 278 if(adapter==null) { 279 T list = nav().getBaseClass(tOfJAXBElementT,nav().asDecl(List.class)); 280 if(list==null) { 281 isCollection = false; 282 contentType = builder.getTypeInfo(tOfJAXBElementT,this); // suck this type into the current set. 283 } else { 284 isCollection = true; 285 contentType = builder.getTypeInfo(nav().getTypeArgument(list,0),this); 286 } 287 } else { 288 // but if adapted, use the adapted type 289 contentType = builder.getTypeInfo(this.adapter.defaultType,this); 290 isCollection = false; 291 } 292 293 // scope 294 T s = reader().getClassValue(anno,"scope"); 295 if(nav().isSameType(s, nav().ref(XmlElementDecl.GLOBAL.class))) 296 scope = null; 297 else { 298 // TODO: what happens if there's an error? 299 NonElement<T,C> scp = builder.getClassInfo(nav().asDecl(s),this); 300 if(!(scp instanceof ClassInfo)) { 301 throw new IllegalAnnotationException( 302 Messages.SCOPE_IS_NOT_COMPLEXTYPE.format(nav().getTypeName(s)), 303 anno ); 304 } 305 scope = (ClassInfo<T,C>)scp; 306 } 307 308 id = calcId(); 309 310 property = createPropertyImpl(); 311 312 this.expectedMimeType = Util.calcExpectedMediaType(property,builder); 313 this.inlineBinary = reader().hasMethodAnnotation(XmlInlineBinaryData.class,method); 314 this.schemaType = Util.calcSchemaType(reader(),property,registry.registryClass, 315 getContentInMemoryType(),this); 316 } 317 318 final QName parseElementName(XmlElementDecl e) { 319 String local = e.name(); 320 String nsUri = e.namespace(); 321 if(nsUri.equals("##default")) { 322 // if defaulted ... 323 XmlSchema xs = reader().getPackageAnnotation(XmlSchema.class, 324 nav().getDeclaringClassForMethod(method),this); 325 if(xs!=null) 326 nsUri = xs.namespace(); 327 else { 328 nsUri = builder.defaultNsUri; 329 } 330 } 331 332 return new QName(nsUri.intern(),local.intern()); 333 } 334 335 protected PropertyImpl createPropertyImpl() { 336 return new PropertyImpl(); 337 } 338 339 public ElementPropertyInfo<T,C> getProperty() { 340 return property; 341 } 342 343 public NonElement<T,C> getContentType() { 344 return contentType; 345 } 346 347 public T getContentInMemoryType() { 348 if(adapter==null) { 349 return tOfJAXBElementT; 350 } else { 351 return adapter.customType; 352 } 353 } 354 355 public QName getElementName() { 356 return tagName; 357 } 358 359 public T getType() { 360 return elementType; 361 } 362 363 /** 364 * Leaf-type cannot be referenced from IDREF. 365 * 366 * @deprecated 367 * why are you calling a method whose return value is always known? 368 */ 369 public final boolean canBeReferencedByIDREF() { 370 return false; 371 } 372 373 private ID calcId() { 374 // TODO: share code with PropertyInfoImpl 375 if(reader().hasMethodAnnotation(XmlID.class,method)) { 376 return ID.ID; 377 } else 378 if(reader().hasMethodAnnotation(XmlIDREF.class,method)) { 379 return ID.IDREF; 380 } else { 381 return ID.NONE; 382 } 383 } 384 385 public ClassInfo<T, C> getScope() { 386 return scope; 387 } 388 389 public ElementInfo<T,C> getSubstitutionHead() { 390 return substitutionHead; 391 } 392 393 public Collection<? extends ElementInfoImpl<T,C,F,M>> getSubstitutionMembers() { 394 if(substitutionMembers==null) 395 return Collections.emptyList(); 396 else 397 return substitutionMembers; 398 } 399 400 /** 401 * Called after all the {@link TypeInfo}s are collected into the {@link #owner}. 402 */ 403 /*package*/ void link() { 404 // substitution head 405 if(anno.substitutionHeadName().length()!=0) { 406 QName name = new QName( 407 anno.substitutionHeadNamespace(), anno.substitutionHeadName() ); 408 substitutionHead = owner.getElementInfo(null,name); 409 if(substitutionHead==null) { 410 builder.reportError( 411 new IllegalAnnotationException(Messages.NON_EXISTENT_ELEMENT_MAPPING.format( 412 name.getNamespaceURI(),name.getLocalPart()), anno)); 413 // recover by ignoring this substitution declaration 414 } else 415 substitutionHead.addSubstitutionMember(this); 416 } else 417 substitutionHead = null; 418 super.link(); 419 } 420 421 private void addSubstitutionMember(ElementInfoImpl<T,C,F,M> child) { 422 if(substitutionMembers==null) 423 substitutionMembers = new FinalArrayList<ElementInfoImpl<T,C,F,M>>(); 424 substitutionMembers.add(child); 425 } 426 427 public Location getLocation() { 428 return nav().getMethodLocation(method); 429 } 430} 431