Sun, 31 Aug 2014 16:14:36 +0400
8036981: JAXB not preserving formatting for xsd:any Mixed content
Reviewed-by: lancea, mkos
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 */
26 package com.sun.xml.internal.bind.v2.runtime;
28 import java.io.IOException;
29 import java.lang.reflect.InvocationTargetException;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.Modifier;
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
39 import javax.xml.bind.ValidationEvent;
40 import javax.xml.bind.annotation.XmlRootElement;
41 import javax.xml.bind.helpers.ValidationEventImpl;
42 import javax.xml.namespace.QName;
43 import javax.xml.stream.XMLStreamException;
45 import com.sun.istack.internal.FinalArrayList;
46 import com.sun.xml.internal.bind.Util;
47 import com.sun.xml.internal.bind.api.AccessorException;
48 import com.sun.xml.internal.bind.v2.ClassFactory;
49 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
50 import com.sun.xml.internal.bind.v2.model.core.ID;
51 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
52 import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo;
53 import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty;
54 import com.sun.xml.internal.bind.v2.runtime.property.Property;
55 import com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory;
56 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
57 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
58 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.StructureLoader;
59 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
60 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiTypeLoader;
62 import org.xml.sax.Locator;
63 import org.xml.sax.SAXException;
64 import org.xml.sax.helpers.LocatorImpl;
66 /**
67 * {@link JaxBeanInfo} implementation for j2s bean.
68 *
69 * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
70 */
71 public final class ClassBeanInfoImpl<BeanT> extends JaxBeanInfo<BeanT> implements AttributeAccessor<BeanT> {
73 /**
74 * Properties of this bean class but not its ancestor classes.
75 */
76 public final Property<BeanT>[] properties;
78 /**
79 * Non-null if this bean has an ID property.
80 */
81 private Property<? super BeanT> idProperty;
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;
92 /**
93 * Set only until the link phase to avoid leaking memory.
94 */
95 private RuntimeClassInfo ci;
97 private final Accessor<? super BeanT,Map<QName,String>> inheritedAttWildcard;
98 private final Transducer<BeanT> xducer;
100 /**
101 * {@link ClassBeanInfoImpl} that represents the super class of {@link #jaxbType}.
102 */
103 public final ClassBeanInfoImpl<? super BeanT> superClazz;
105 private final Accessor<? super BeanT,Locator> xmlLocatorField;
107 private final Name tagName;
109 private boolean retainPropertyInfo = false;
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;
117 /**
118 * {@link Property}s that need to receive {@link Property#serializeURIs(Object, XMLSerializer)} callback.
119 */
120 private /*final*/ Property<BeanT>[] uriProperties;
122 private final Method factoryMethod;
124 /*package*/ ClassBeanInfoImpl(JAXBContextImpl owner, RuntimeClassInfo ci) {
125 super(owner,ci,ci.getClazz(),ci.getTypeName(),ci.isElement(),false,true);
127 this.ci = ci;
128 this.inheritedAttWildcard = ci.getAttributeWildcard();
129 this.xducer = ci.getTransducer();
130 this.factoryMethod = ci.getFactoryMethod();
131 this.retainPropertyInfo = owner.retainPropertyInfo;
133 // make the factory accessible
134 if(factoryMethod!=null) {
135 int classMod = factoryMethod.getDeclaringClass().getModifiers();
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 }
150 if(ci.getBaseClass()==null)
151 this.superClazz = null;
152 else
153 this.superClazz = owner.getOrCreate(ci.getBaseClass());
155 if(superClazz!=null && superClazz.xmlLocatorField!=null)
156 xmlLocatorField = superClazz.xmlLocatorField;
157 else
158 xmlLocatorField = ci.getLocatorField();
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
176 hasElementOnlyContentModel( elementOnly );
177 // again update this value later when we know that of the super class
179 if(ci.isElement())
180 tagName = owner.nameBuilder.createElementName(ci.getElementName());
181 else
182 tagName = null;
184 setLifecycleFlags();
185 }
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 }
203 @Override
204 protected void link(JAXBContextImpl grammar) {
205 if(uriProperties!=null)
206 return; // avoid linking twice
208 super.link(grammar);
210 if(superClazz!=null)
211 superClazz.link(grammar);
213 getLoader(grammar,true); // make sure to build the loader if we haven't done so.
215 // propagate values from super class
216 if(superClazz!=null) {
217 if(idProperty==null)
218 idProperty = superClazz.idProperty;
220 if(!superClazz.hasElementOnlyContentModel())
221 hasElementOnlyContentModel(false);
222 }
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);
239 if(attProps.isEmpty())
240 attributeProperties = EMPTY_PROPERTIES;
241 else
242 attributeProperties = attProps.toArray(new AttributeProperty[attProps.size()]);
244 if(uriProps.isEmpty())
245 uriProperties = EMPTY_PROPERTIES;
246 else
247 uriProperties = uriProps.toArray(new Property[uriProps.size()]);
248 }
250 @Override
251 public void wrapUp() {
252 for (Property p : properties)
253 p.wrapUp();
254 ci = null;
255 super.wrapUp();
256 }
258 public String getElementNamespaceURI(BeanT bean) {
259 return tagName.nsUri;
260 }
262 public String getElementLocalName(BeanT bean) {
263 return tagName.localName;
264 }
266 public BeanT createInstance(UnmarshallingContext context) throws IllegalAccessException, InvocationTargetException, InstantiationException, SAXException {
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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;
423 sl.init(context,this,ci.getAttributeWildcard());
424 }
425 if(typeSubstitutionCapable)
426 return loaderWithTypeSubst;
427 else
428 return loader;
429 }
431 public Transducer<BeanT> getTransducer() {
432 return xducer;
433 }
435 private static final AttributeProperty[] EMPTY_PROPERTIES = new AttributeProperty[0];
437 private static final Logger logger = Util.getClassLogger();
439 }