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.io.IOException; 29import java.lang.annotation.Annotation; 30import java.lang.reflect.Field; 31import java.lang.reflect.Method; 32import java.lang.reflect.Modifier; 33import java.lang.reflect.Type; 34import java.util.List; 35import java.util.Map; 36 37import javax.xml.bind.JAXBException; 38import javax.xml.namespace.QName; 39import javax.xml.stream.XMLStreamException; 40 41import com.sun.istack.internal.NotNull; 42import com.sun.xml.internal.bind.AccessorFactory; 43import com.sun.xml.internal.bind.AccessorFactoryImpl; 44import com.sun.xml.internal.bind.InternalAccessorFactory; 45import com.sun.xml.internal.bind.XmlAccessorFactory; 46import com.sun.xml.internal.bind.annotation.XmlLocation; 47import com.sun.xml.internal.bind.api.AccessorException; 48import com.sun.xml.internal.bind.v2.ClassFactory; 49import com.sun.xml.internal.bind.v2.model.annotation.Locatable; 50import com.sun.xml.internal.bind.v2.model.core.PropertyKind; 51import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo; 52import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElement; 53import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo; 54import com.sun.xml.internal.bind.v2.model.runtime.RuntimeValuePropertyInfo; 55import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException; 56import com.sun.xml.internal.bind.v2.runtime.Location; 57import com.sun.xml.internal.bind.v2.runtime.Name; 58import com.sun.xml.internal.bind.v2.runtime.Transducer; 59import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; 60import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; 61import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; 62import com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor; 63import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; 64 65import org.xml.sax.Locator; 66import org.xml.sax.SAXException; 67 68/** 69 * @author Kohsuke Kawaguchi (kk@kohsuke.org) 70 */ 71class RuntimeClassInfoImpl extends ClassInfoImpl<Type,Class,Field,Method> 72 implements RuntimeClassInfo, RuntimeElement { 73 74 /** 75 * If this class has a property annotated with {@link XmlLocation}, 76 * this field will get the accessor for it. 77 * 78 * TODO: support method based XmlLocation 79 */ 80 private Accessor<?,Locator> xmlLocationAccessor; 81 82 private AccessorFactory accessorFactory; 83 84 private boolean supressAccessorWarnings = false; 85 86 public RuntimeClassInfoImpl(RuntimeModelBuilder modelBuilder, Locatable upstream, Class clazz) { 87 super(modelBuilder, upstream, clazz); 88 accessorFactory = createAccessorFactory(clazz); 89 } 90 91 protected AccessorFactory createAccessorFactory(Class clazz) { 92 XmlAccessorFactory factoryAnn; 93 AccessorFactory accFactory = null; 94 95 // user providing class to be used. 96 JAXBContextImpl context = ((RuntimeModelBuilder) builder).context; 97 if (context!=null) { 98 this.supressAccessorWarnings = context.supressAccessorWarnings; 99 if (context.xmlAccessorFactorySupport) { 100 factoryAnn = findXmlAccessorFactoryAnnotation(clazz); 101 if (factoryAnn != null) { 102 try { 103 accFactory = factoryAnn.value().newInstance(); 104 } catch (InstantiationException e) { 105 builder.reportError(new IllegalAnnotationException( 106 Messages.ACCESSORFACTORY_INSTANTIATION_EXCEPTION.format( 107 factoryAnn.getClass().getName(), nav().getClassName(clazz)), this)); 108 } catch (IllegalAccessException e) { 109 builder.reportError(new IllegalAnnotationException( 110 Messages.ACCESSORFACTORY_ACCESS_EXCEPTION.format( 111 factoryAnn.getClass().getName(), nav().getClassName(clazz)),this)); 112 } 113 } 114 } 115 } 116 117 118 // Fall back to local AccessorFactory when no 119 // user not providing one or as error recovery. 120 if (accFactory == null){ 121 accFactory = AccessorFactoryImpl.getInstance(); 122 } 123 return accFactory; 124 } 125 126 protected XmlAccessorFactory findXmlAccessorFactoryAnnotation(Class clazz) { 127 XmlAccessorFactory factoryAnn = reader().getClassAnnotation(XmlAccessorFactory.class,clazz,this); 128 if (factoryAnn == null) { 129 factoryAnn = reader().getPackageAnnotation(XmlAccessorFactory.class,clazz,this); 130 } 131 return factoryAnn; 132 } 133 134 135 public Method getFactoryMethod(){ 136 return super.getFactoryMethod(); 137 } 138 139 public final RuntimeClassInfoImpl getBaseClass() { 140 return (RuntimeClassInfoImpl)super.getBaseClass(); 141 } 142 143 @Override 144 protected ReferencePropertyInfoImpl createReferenceProperty(PropertySeed<Type,Class,Field,Method> seed) { 145 return new RuntimeReferencePropertyInfoImpl(this,seed); 146 } 147 148 @Override 149 protected AttributePropertyInfoImpl createAttributeProperty(PropertySeed<Type,Class,Field,Method> seed) { 150 return new RuntimeAttributePropertyInfoImpl(this,seed); 151 } 152 153 @Override 154 protected ValuePropertyInfoImpl createValueProperty(PropertySeed<Type,Class,Field,Method> seed) { 155 return new RuntimeValuePropertyInfoImpl(this,seed); 156 } 157 158 @Override 159 protected ElementPropertyInfoImpl createElementProperty(PropertySeed<Type,Class,Field,Method> seed) { 160 return new RuntimeElementPropertyInfoImpl(this,seed); 161 } 162 163 @Override 164 protected MapPropertyInfoImpl createMapProperty(PropertySeed<Type,Class,Field,Method> seed) { 165 return new RuntimeMapPropertyInfoImpl(this,seed); 166 } 167 168 169 @Override 170 public List<? extends RuntimePropertyInfo> getProperties() { 171 return (List<? extends RuntimePropertyInfo>)super.getProperties(); 172 } 173 174 @Override 175 public RuntimePropertyInfo getProperty(String name) { 176 return (RuntimePropertyInfo)super.getProperty(name); 177 } 178 179 180 public void link() { 181 getTransducer(); // populate the transducer 182 super.link(); 183 } 184 185 private Accessor<?,Map<QName,String>> attributeWildcardAccessor; 186 187 public <B> Accessor<B,Map<QName,String>> getAttributeWildcard() { 188 for( RuntimeClassInfoImpl c=this; c!=null; c=c.getBaseClass() ) { 189 if(c.attributeWildcard!=null) { 190 if(c.attributeWildcardAccessor==null) 191 c.attributeWildcardAccessor = c.createAttributeWildcardAccessor(); 192 return (Accessor<B,Map<QName,String>>)c.attributeWildcardAccessor; 193 } 194 } 195 return null; 196 } 197 198 private boolean computedTransducer = false; 199 private Transducer xducer = null; 200 201 public Transducer getTransducer() { 202 if(!computedTransducer) { 203 computedTransducer = true; 204 xducer = calcTransducer(); 205 } 206 return xducer; 207 } 208 209 /** 210 * Creates a transducer if this class is bound to a text in XML. 211 */ 212 private Transducer calcTransducer() { 213 RuntimeValuePropertyInfo valuep=null; 214 if(hasAttributeWildcard()) 215 return null; // has attribute wildcard. Can't be handled as a leaf 216 for (RuntimeClassInfoImpl ci = this; ci != null; ci = ci.getBaseClass()) { 217 for( RuntimePropertyInfo pi : ci.getProperties() ) 218 if(pi.kind()==PropertyKind.VALUE) { 219 valuep = (RuntimeValuePropertyInfo)pi; 220 } else { 221 // this bean has something other than a value 222 return null; 223 } 224 } 225 if(valuep==null) 226 return null; 227 if( !valuep.getTarget().isSimpleType() ) 228 return null; // if there's an error, recover from it by returning null. 229 230 return new TransducerImpl(getClazz(),TransducedAccessor.get( 231 ((RuntimeModelBuilder)builder).context,valuep)); 232 } 233 234 /** 235 * Creates 236 */ 237 private Accessor<?,Map<QName,String>> createAttributeWildcardAccessor() { 238 assert attributeWildcard!=null; 239 return ((RuntimePropertySeed)attributeWildcard).getAccessor(); 240 } 241 242 @Override 243 protected RuntimePropertySeed createFieldSeed(Field field) { 244 final boolean readOnly = Modifier.isStatic(field.getModifiers()); 245 Accessor acc; 246 try { 247 if (supressAccessorWarnings) { 248 acc = ((InternalAccessorFactory)accessorFactory).createFieldAccessor(clazz, field, readOnly, supressAccessorWarnings); 249 } else { 250 acc = accessorFactory.createFieldAccessor(clazz, field, readOnly); 251 } 252 } catch(JAXBException e) { 253 builder.reportError(new IllegalAnnotationException( 254 Messages.CUSTOM_ACCESSORFACTORY_FIELD_ERROR.format( 255 nav().getClassName(clazz), e.toString()), this )); 256 acc = Accessor.getErrorInstance(); // error recovery 257 } 258 return new RuntimePropertySeed(super.createFieldSeed(field), acc ); 259 } 260 261 @Override 262 public RuntimePropertySeed createAccessorSeed(Method getter, Method setter) { 263 Accessor acc; 264 try { 265 acc = accessorFactory.createPropertyAccessor(clazz, getter, setter); 266 } catch(JAXBException e) { 267 builder.reportError(new IllegalAnnotationException( 268 Messages.CUSTOM_ACCESSORFACTORY_PROPERTY_ERROR.format( 269 nav().getClassName(clazz), e.toString()), this )); 270 acc = Accessor.getErrorInstance(); // error recovery 271 } 272 return new RuntimePropertySeed( super.createAccessorSeed(getter,setter), 273 acc ); 274 } 275 276 @Override 277 protected void checkFieldXmlLocation(Field f) { 278 if(reader().hasFieldAnnotation(XmlLocation.class,f)) 279 // TODO: check for XmlLocation signature 280 // TODO: check a collision with the super class 281 xmlLocationAccessor = new Accessor.FieldReflection<Object,Locator>(f); 282 } 283 284 public Accessor<?,Locator> getLocatorField() { 285 return xmlLocationAccessor; 286 } 287 288 static final class RuntimePropertySeed implements PropertySeed<Type,Class,Field,Method> { 289 /** 290 * @see #getAccessor() 291 */ 292 private final Accessor acc; 293 294 private final PropertySeed<Type,Class,Field,Method> core; 295 296 public RuntimePropertySeed(PropertySeed<Type,Class,Field,Method> core, Accessor acc) { 297 this.core = core; 298 this.acc = acc; 299 } 300 301 public String getName() { 302 return core.getName(); 303 } 304 305 public <A extends Annotation> A readAnnotation(Class<A> annotationType) { 306 return core.readAnnotation(annotationType); 307 } 308 309 public boolean hasAnnotation(Class<? extends Annotation> annotationType) { 310 return core.hasAnnotation(annotationType); 311 } 312 313 public Type getRawType() { 314 return core.getRawType(); 315 } 316 317 public Location getLocation() { 318 return core.getLocation(); 319 } 320 321 public Locatable getUpstream() { 322 return core.getUpstream(); 323 } 324 325 public Accessor getAccessor() { 326 return acc; 327 } 328 } 329 330 331 332 /** 333 * {@link Transducer} implementation used when this class maps to PCDATA in XML. 334 * 335 * TODO: revisit the exception handling 336 */ 337 private static final class TransducerImpl<BeanT> implements Transducer<BeanT> { 338 private final TransducedAccessor<BeanT> xacc; 339 private final Class<BeanT> ownerClass; 340 341 public TransducerImpl(Class<BeanT> ownerClass,TransducedAccessor<BeanT> xacc) { 342 this.xacc = xacc; 343 this.ownerClass = ownerClass; 344 } 345 346 public boolean useNamespace() { 347 return xacc.useNamespace(); 348 } 349 350 public boolean isDefault() { 351 return false; 352 } 353 354 public void declareNamespace(BeanT bean, XMLSerializer w) throws AccessorException { 355 try { 356 xacc.declareNamespace(bean,w); 357 } catch (SAXException e) { 358 throw new AccessorException(e); 359 } 360 } 361 362 public @NotNull CharSequence print(BeanT o) throws AccessorException { 363 try { 364 CharSequence value = xacc.print(o); 365 if(value==null) 366 throw new AccessorException(Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE.format(o)); 367 return value; 368 } catch (SAXException e) { 369 throw new AccessorException(e); 370 } 371 } 372 373 public BeanT parse(CharSequence lexical) throws AccessorException, SAXException { 374 UnmarshallingContext ctxt = UnmarshallingContext.getInstance(); 375 BeanT inst; 376 if(ctxt!=null) 377 inst = (BeanT)ctxt.createInstance(ownerClass); 378 else 379 // when this runs for parsing enum constants, 380 // there's no UnmarshallingContext. 381 inst = ClassFactory.create(ownerClass); 382 383 xacc.parse(inst,lexical); 384 return inst; 385 } 386 387 public void writeText(XMLSerializer w, BeanT o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException { 388 if(!xacc.hasValue(o)) 389 throw new AccessorException(Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE.format(o)); 390 xacc.writeText(w,o,fieldName); 391 } 392 393 public void writeLeafElement(XMLSerializer w, Name tagName, BeanT o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException { 394 if(!xacc.hasValue(o)) 395 throw new AccessorException(Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE.format(o)); 396 xacc.writeLeafElement(w,tagName,o,fieldName); 397 } 398 399 public QName getTypeName(BeanT instance) { 400 return null; 401 } 402 } 403} 404