1/* 2 * Copyright (c) 1997, 2013, 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.runtime; 27 28import java.io.IOException; 29import java.lang.reflect.InvocationTargetException; 30import java.lang.reflect.Method; 31import java.lang.reflect.Modifier; 32import java.util.Collection; 33import java.util.Collections; 34import java.util.List; 35import java.util.Map; 36import java.util.logging.Level; 37import java.util.logging.Logger; 38 39import javax.xml.bind.ValidationEvent; 40import javax.xml.bind.annotation.XmlRootElement; 41import javax.xml.bind.helpers.ValidationEventImpl; 42import javax.xml.namespace.QName; 43import javax.xml.stream.XMLStreamException; 44 45import com.sun.istack.internal.FinalArrayList; 46import com.sun.xml.internal.bind.Util; 47import com.sun.xml.internal.bind.api.AccessorException; 48import com.sun.xml.internal.bind.v2.ClassFactory; 49import com.sun.xml.internal.bind.v2.WellKnownNamespace; 50import com.sun.xml.internal.bind.v2.model.core.ID; 51import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo; 52import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo; 53import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty; 54import com.sun.xml.internal.bind.v2.runtime.property.Property; 55import com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory; 56import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; 57import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; 58import com.sun.xml.internal.bind.v2.runtime.unmarshaller.StructureLoader; 59import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; 60import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiTypeLoader; 61 62import org.xml.sax.Locator; 63import org.xml.sax.SAXException; 64import org.xml.sax.helpers.LocatorImpl; 65 66/** 67 * {@link JaxBeanInfo} implementation for j2s bean. 68 * 69 * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) 70 */ 71public final class ClassBeanInfoImpl<BeanT> extends JaxBeanInfo<BeanT> implements AttributeAccessor<BeanT> { 72 73 /** 74 * Properties of this bean class but not its ancestor classes. 75 */ 76 public final Property<BeanT>[] properties; 77 78 /** 79 * Non-null if this bean has an ID property. 80 */ 81 private Property<? super BeanT> idProperty; 82 83 /** 84 * Immutable configured loader for this class. 85 * 86 * <p> 87 * Set from the link method, but considered final. 88 */ 89 private Loader loader; 90 private Loader loaderWithTypeSubst; 91 92 /** 93 * Set only until the link phase to avoid leaking memory. 94 */ 95 private RuntimeClassInfo ci; 96 97 private final Accessor<? super BeanT,Map<QName,String>> inheritedAttWildcard; 98 private final Transducer<BeanT> xducer; 99 100 /** 101 * {@link ClassBeanInfoImpl} that represents the super class of {@link #jaxbType}. 102 */ 103 public final ClassBeanInfoImpl<? super BeanT> superClazz; 104 105 private final Accessor<? super BeanT,Locator> xmlLocatorField; 106 107 private final Name tagName; 108 109 private boolean retainPropertyInfo = false; 110 111 /** 112 * The {@link AttributeProperty}s for this type and all its ancestors. 113 * If {@link JAXBContextImpl#c14nSupport} is true, this is sorted alphabetically. 114 */ 115 private /*final*/ AttributeProperty<BeanT>[] attributeProperties; 116 117 /** 118 * {@link Property}s that need to receive {@link Property#serializeURIs(Object, XMLSerializer)} callback. 119 */ 120 private /*final*/ Property<BeanT>[] uriProperties; 121 122 private final Method factoryMethod; 123 124 /*package*/ ClassBeanInfoImpl(JAXBContextImpl owner, RuntimeClassInfo ci) { 125 super(owner,ci,ci.getClazz(),ci.getTypeName(),ci.isElement(),false,true); 126 127 this.ci = ci; 128 this.inheritedAttWildcard = ci.getAttributeWildcard(); 129 this.xducer = ci.getTransducer(); 130 this.factoryMethod = ci.getFactoryMethod(); 131 this.retainPropertyInfo = owner.retainPropertyInfo; 132 133 // make the factory accessible 134 if(factoryMethod!=null) { 135 int classMod = factoryMethod.getDeclaringClass().getModifiers(); 136 137 if(!Modifier.isPublic(classMod) || !Modifier.isPublic(factoryMethod.getModifiers())) { 138 // attempt to make it work even if the constructor is not accessible 139 try { 140 factoryMethod.setAccessible(true); 141 } catch(SecurityException e) { 142 // but if we don't have a permission to do so, work gracefully. 143 logger.log(Level.FINE,"Unable to make the method of "+factoryMethod+" accessible",e); 144 throw e; 145 } 146 } 147 } 148 149 150 if(ci.getBaseClass()==null) 151 this.superClazz = null; 152 else 153 this.superClazz = owner.getOrCreate(ci.getBaseClass()); 154 155 if(superClazz!=null && superClazz.xmlLocatorField!=null) 156 xmlLocatorField = superClazz.xmlLocatorField; 157 else 158 xmlLocatorField = ci.getLocatorField(); 159 160 // create property objects 161 Collection<? extends RuntimePropertyInfo> ps = ci.getProperties(); 162 this.properties = new Property[ps.size()]; 163 int idx=0; 164 boolean elementOnly = true; 165 for( RuntimePropertyInfo info : ps ) { 166 Property p = PropertyFactory.create(owner,info); 167 if(info.id()==ID.ID) 168 idProperty = p; 169 properties[idx++] = p; 170 elementOnly &= info.elementOnlyContent(); 171 checkOverrideProperties(p); 172 } 173 // super class' idProperty might not be computed at this point, 174 // so check that later 175 176 hasElementOnlyContentModel( elementOnly ); 177 // again update this value later when we know that of the super class 178 179 if(ci.isElement()) 180 tagName = owner.nameBuilder.createElementName(ci.getElementName()); 181 else 182 tagName = null; 183 184 setLifecycleFlags(); 185 } 186 187 private void checkOverrideProperties(Property p) { 188 ClassBeanInfoImpl bi = this; 189 while ((bi = bi.superClazz) != null) { 190 Property[] props = bi.properties; 191 if (props == null) break; 192 for (Property superProperty : props) { 193 if (superProperty != null) { 194 String spName = superProperty.getFieldName(); 195 if ((spName != null) && (spName.equals(p.getFieldName()))) { 196 superProperty.setHiddenByOverride(true); 197 } 198 } 199 } 200 } 201 } 202 203 @Override 204 protected void link(JAXBContextImpl grammar) { 205 if(uriProperties!=null) 206 return; // avoid linking twice 207 208 super.link(grammar); 209 210 if(superClazz!=null) 211 superClazz.link(grammar); 212 213 getLoader(grammar,true); // make sure to build the loader if we haven't done so. 214 215 // propagate values from super class 216 if(superClazz!=null) { 217 if(idProperty==null) 218 idProperty = superClazz.idProperty; 219 220 if(!superClazz.hasElementOnlyContentModel()) 221 hasElementOnlyContentModel(false); 222 } 223 224 // create a list of attribute/URI handlers 225 List<AttributeProperty> attProps = new FinalArrayList<AttributeProperty>(); 226 List<Property> uriProps = new FinalArrayList<Property>(); 227 for (ClassBeanInfoImpl bi = this; bi != null; bi = bi.superClazz) { 228 for (int i = 0; i < bi.properties.length; i++) { 229 Property p = bi.properties[i]; 230 if(p instanceof AttributeProperty) 231 attProps.add((AttributeProperty) p); 232 if(p.hasSerializeURIAction()) 233 uriProps.add(p); 234 } 235 } 236 if(grammar.c14nSupport) 237 Collections.sort(attProps); 238 239 if(attProps.isEmpty()) 240 attributeProperties = EMPTY_PROPERTIES; 241 else 242 attributeProperties = attProps.toArray(new AttributeProperty[attProps.size()]); 243 244 if(uriProps.isEmpty()) 245 uriProperties = EMPTY_PROPERTIES; 246 else 247 uriProperties = uriProps.toArray(new Property[uriProps.size()]); 248 } 249 250 @Override 251 public void wrapUp() { 252 for (Property p : properties) 253 p.wrapUp(); 254 ci = null; 255 super.wrapUp(); 256 } 257 258 public String getElementNamespaceURI(BeanT bean) { 259 return tagName.nsUri; 260 } 261 262 public String getElementLocalName(BeanT bean) { 263 return tagName.localName; 264 } 265 266 public BeanT createInstance(UnmarshallingContext context) throws IllegalAccessException, InvocationTargetException, InstantiationException, SAXException { 267 268 BeanT bean = null; 269 if (factoryMethod == null){ 270 bean = ClassFactory.create0(jaxbType); 271 }else { 272 Object o = ClassFactory.create(factoryMethod); 273 if( jaxbType.isInstance(o) ){ 274 bean = (BeanT)o; 275 } else { 276 throw new InstantiationException("The factory method didn't return a correct object"); 277 } 278 } 279 280 if(xmlLocatorField!=null) 281 // need to copy because Locator is mutable 282 try { 283 xmlLocatorField.set(bean,new LocatorImpl(context.getLocator())); 284 } catch (AccessorException e) { 285 context.handleError(e); 286 } 287 return bean; 288 } 289 290 public boolean reset(BeanT bean, UnmarshallingContext context) throws SAXException { 291 try { 292 if(superClazz!=null) 293 superClazz.reset(bean,context); 294 for( Property<BeanT> p : properties ) 295 p.reset(bean); 296 return true; 297 } catch (AccessorException e) { 298 context.handleError(e); 299 return false; 300 } 301 } 302 303 public String getId(BeanT bean, XMLSerializer target) throws SAXException { 304 if(idProperty!=null) { 305 try { 306 return idProperty.getIdValue(bean); 307 } catch (AccessorException e) { 308 target.reportError(null,e); 309 } 310 } 311 return null; 312 } 313 314 public void serializeRoot(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { 315 if(tagName==null) { 316 Class beanClass = bean.getClass(); 317 String message; 318 if (beanClass.isAnnotationPresent(XmlRootElement.class)) { 319 message = Messages.UNABLE_TO_MARSHAL_UNBOUND_CLASS.format(beanClass.getName()); 320 } else { 321 message = Messages.UNABLE_TO_MARSHAL_NON_ELEMENT.format(beanClass.getName()); 322 } 323 target.reportError(new ValidationEventImpl(ValidationEvent.ERROR,message,null, null)); 324 } else { 325 target.startElement(tagName,bean); 326 target.childAsSoleContent(bean,null); 327 target.endElement(); 328 if (retainPropertyInfo) { 329 target.currentProperty.remove(); 330 } 331 } 332 } 333 334 public void serializeBody(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { 335 if (superClazz != null) { 336 superClazz.serializeBody(bean, target); 337 } 338 try { 339 for (Property<BeanT> p : properties) { 340 if (retainPropertyInfo) { 341 target.currentProperty.set(p); 342 } 343 boolean isThereAnOverridingProperty = p.isHiddenByOverride(); 344 if (!isThereAnOverridingProperty || bean.getClass().equals(jaxbType)) { 345 p.serializeBody(bean, target, null); 346 } else if (isThereAnOverridingProperty) { 347 // need to double check the override - it should be safe to do after the model has been created because it's targeted to override properties only 348 Class beanClass = bean.getClass(); 349 if (Utils.REFLECTION_NAVIGATOR.getDeclaredField(beanClass, p.getFieldName()) == null) { 350 p.serializeBody(bean, target, null); 351 } 352 } 353 } 354 } catch (AccessorException e) { 355 target.reportError(null, e); 356 } 357 } 358 359 public void serializeAttributes(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { 360 for( AttributeProperty<BeanT> p : attributeProperties ) 361 try { 362 if (retainPropertyInfo) { 363 final Property parentProperty = target.getCurrentProperty(); 364 target.currentProperty.set(p); 365 p.serializeAttributes(bean,target); 366 target.currentProperty.set(parentProperty); 367 } else { 368 p.serializeAttributes(bean,target); 369 } 370 if (p.attName.equals(WellKnownNamespace.XML_SCHEMA_INSTANCE, "nil")) { 371 isNilIncluded = true; 372 } 373 } catch (AccessorException e) { 374 target.reportError(null,e); 375 } 376 377 try { 378 if(inheritedAttWildcard!=null) { 379 Map<QName,String> map = inheritedAttWildcard.get(bean); 380 target.attWildcardAsAttributes(map,null); 381 } 382 } catch (AccessorException e) { 383 target.reportError(null,e); 384 } 385 } 386 387 public void serializeURIs(BeanT bean, XMLSerializer target) throws SAXException { 388 try { 389 if (retainPropertyInfo) { 390 final Property parentProperty = target.getCurrentProperty(); 391 for( Property<BeanT> p : uriProperties ) { 392 target.currentProperty.set(p); 393 p.serializeURIs(bean,target); 394 } 395 target.currentProperty.set(parentProperty); 396 } else { 397 for( Property<BeanT> p : uriProperties ) { 398 p.serializeURIs(bean,target); 399 } 400 } 401 if(inheritedAttWildcard!=null) { 402 Map<QName,String> map = inheritedAttWildcard.get(bean); 403 target.attWildcardAsURIs(map,null); 404 } 405 } catch (AccessorException e) { 406 target.reportError(null,e); 407 } 408 } 409 410 public Loader getLoader(JAXBContextImpl context, boolean typeSubstitutionCapable) { 411 if(loader==null) { 412 // these variables have to be set before they are initialized, 413 // because the initialization may build other loaders and they may refer to this. 414 StructureLoader sl = new StructureLoader(this); 415 loader = sl; 416 if(ci.hasSubClasses()) 417 loaderWithTypeSubst = new XsiTypeLoader(this); 418 else 419 // optimization. we know there can be no @xsi:type 420 loaderWithTypeSubst = loader; 421 422 423 sl.init(context,this,ci.getAttributeWildcard()); 424 } 425 if(typeSubstitutionCapable) 426 return loaderWithTypeSubst; 427 else 428 return loader; 429 } 430 431 public Transducer<BeanT> getTransducer() { 432 return xducer; 433 } 434 435 private static final AttributeProperty[] EMPTY_PROPERTIES = new AttributeProperty[0]; 436 437 private static final Logger logger = Util.getClassLogger(); 438 439} 440