|
1 /* |
|
2 * Copyright (c) 1997, 2011, 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 |
|
26 package com.sun.xml.internal.bind.v2.runtime; |
|
27 |
|
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; |
|
38 |
|
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; |
|
44 |
|
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; |
|
61 |
|
62 import org.xml.sax.Locator; |
|
63 import org.xml.sax.SAXException; |
|
64 import org.xml.sax.helpers.LocatorImpl; |
|
65 |
|
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> { |
|
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) break; |
|
194 String spName = superProperty.getFieldName(); |
|
195 if ((spName != null) && (spName.equals(p.getFieldName()))) { |
|
196 superProperty.setHiddenByOverride(true); |
|
197 } |
|
198 } |
|
199 } |
|
200 } |
|
201 |
|
202 @Override |
|
203 protected void link(JAXBContextImpl grammar) { |
|
204 if(uriProperties!=null) |
|
205 return; // avoid linking twice |
|
206 |
|
207 super.link(grammar); |
|
208 |
|
209 if(superClazz!=null) |
|
210 superClazz.link(grammar); |
|
211 |
|
212 getLoader(grammar,true); // make sure to build the loader if we haven't done so. |
|
213 |
|
214 // propagate values from super class |
|
215 if(superClazz!=null) { |
|
216 if(idProperty==null) |
|
217 idProperty = superClazz.idProperty; |
|
218 |
|
219 if(!superClazz.hasElementOnlyContentModel()) |
|
220 hasElementOnlyContentModel(false); |
|
221 } |
|
222 |
|
223 // create a list of attribute/URI handlers |
|
224 List<AttributeProperty> attProps = new FinalArrayList<AttributeProperty>(); |
|
225 List<Property> uriProps = new FinalArrayList<Property>(); |
|
226 for (ClassBeanInfoImpl bi = this; bi != null; bi = bi.superClazz) { |
|
227 for (int i = 0; i < bi.properties.length; i++) { |
|
228 Property p = bi.properties[i]; |
|
229 if(p instanceof AttributeProperty) |
|
230 attProps.add((AttributeProperty) p); |
|
231 if(p.hasSerializeURIAction()) |
|
232 uriProps.add(p); |
|
233 } |
|
234 } |
|
235 if(grammar.c14nSupport) |
|
236 Collections.sort(attProps); |
|
237 |
|
238 if(attProps.isEmpty()) |
|
239 attributeProperties = EMPTY_PROPERTIES; |
|
240 else |
|
241 attributeProperties = attProps.toArray(new AttributeProperty[attProps.size()]); |
|
242 |
|
243 if(uriProps.isEmpty()) |
|
244 uriProperties = EMPTY_PROPERTIES; |
|
245 else |
|
246 uriProperties = uriProps.toArray(new Property[uriProps.size()]); |
|
247 } |
|
248 |
|
249 @Override |
|
250 public void wrapUp() { |
|
251 for (Property p : properties) |
|
252 p.wrapUp(); |
|
253 ci = null; |
|
254 super.wrapUp(); |
|
255 } |
|
256 |
|
257 public String getElementNamespaceURI(BeanT bean) { |
|
258 return tagName.nsUri; |
|
259 } |
|
260 |
|
261 public String getElementLocalName(BeanT bean) { |
|
262 return tagName.localName; |
|
263 } |
|
264 |
|
265 public BeanT createInstance(UnmarshallingContext context) throws IllegalAccessException, InvocationTargetException, InstantiationException, SAXException { |
|
266 |
|
267 BeanT bean = null; |
|
268 if (factoryMethod == null){ |
|
269 bean = ClassFactory.create0(jaxbType); |
|
270 }else { |
|
271 Object o = ClassFactory.create(factoryMethod); |
|
272 if( jaxbType.isInstance(o) ){ |
|
273 bean = (BeanT)o; |
|
274 } else { |
|
275 throw new InstantiationException("The factory method didn't return a correct object"); |
|
276 } |
|
277 } |
|
278 |
|
279 if(xmlLocatorField!=null) |
|
280 // need to copy because Locator is mutable |
|
281 try { |
|
282 xmlLocatorField.set(bean,new LocatorImpl(context.getLocator())); |
|
283 } catch (AccessorException e) { |
|
284 context.handleError(e); |
|
285 } |
|
286 return bean; |
|
287 } |
|
288 |
|
289 public boolean reset(BeanT bean, UnmarshallingContext context) throws SAXException { |
|
290 try { |
|
291 if(superClazz!=null) |
|
292 superClazz.reset(bean,context); |
|
293 for( Property<BeanT> p : properties ) |
|
294 p.reset(bean); |
|
295 return true; |
|
296 } catch (AccessorException e) { |
|
297 context.handleError(e); |
|
298 return false; |
|
299 } |
|
300 } |
|
301 |
|
302 public String getId(BeanT bean, XMLSerializer target) throws SAXException { |
|
303 if(idProperty!=null) { |
|
304 try { |
|
305 return idProperty.getIdValue(bean); |
|
306 } catch (AccessorException e) { |
|
307 target.reportError(null,e); |
|
308 } |
|
309 } |
|
310 return null; |
|
311 } |
|
312 |
|
313 public void serializeRoot(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { |
|
314 if(tagName==null) { |
|
315 Class beanClass = bean.getClass(); |
|
316 String message; |
|
317 if (beanClass.isAnnotationPresent(XmlRootElement.class)) { |
|
318 message = Messages.UNABLE_TO_MARSHAL_UNBOUND_CLASS.format(beanClass.getName()); |
|
319 } else { |
|
320 message = Messages.UNABLE_TO_MARSHAL_NON_ELEMENT.format(beanClass.getName()); |
|
321 } |
|
322 target.reportError(new ValidationEventImpl(ValidationEvent.ERROR,message,null, null)); |
|
323 } else { |
|
324 target.startElement(tagName,bean); |
|
325 target.childAsSoleContent(bean,null); |
|
326 target.endElement(); |
|
327 if (retainPropertyInfo) { |
|
328 target.currentProperty.remove(); |
|
329 } |
|
330 } |
|
331 } |
|
332 |
|
333 public void serializeBody(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { |
|
334 if (superClazz != null) { |
|
335 superClazz.serializeBody(bean, target); |
|
336 } |
|
337 try { |
|
338 for (Property<BeanT> p : properties) { |
|
339 if (retainPropertyInfo) { |
|
340 target.currentProperty.set(p); |
|
341 } |
|
342 if (!(p.isHiddenByOverride() && !bean.getClass().equals(jaxbType))) { |
|
343 p.serializeBody(bean, target, null); |
|
344 } |
|
345 } |
|
346 } catch (AccessorException e) { |
|
347 target.reportError(null, e); |
|
348 } |
|
349 } |
|
350 |
|
351 public void serializeAttributes(BeanT bean, XMLSerializer target) throws SAXException, IOException, XMLStreamException { |
|
352 for( AttributeProperty<BeanT> p : attributeProperties ) |
|
353 try { |
|
354 if (retainPropertyInfo) { |
|
355 final Property parentProperty = target.getCurrentProperty(); |
|
356 target.currentProperty.set(p); |
|
357 p.serializeAttributes(bean,target); |
|
358 target.currentProperty.set(parentProperty); |
|
359 } else { |
|
360 p.serializeAttributes(bean,target); |
|
361 } |
|
362 if (p.attName.equals(WellKnownNamespace.XML_SCHEMA_INSTANCE, "nil")) { |
|
363 isNilIncluded = true; |
|
364 } |
|
365 } catch (AccessorException e) { |
|
366 target.reportError(null,e); |
|
367 } |
|
368 |
|
369 try { |
|
370 if(inheritedAttWildcard!=null) { |
|
371 Map<QName,String> map = inheritedAttWildcard.get(bean); |
|
372 target.attWildcardAsAttributes(map,null); |
|
373 } |
|
374 } catch (AccessorException e) { |
|
375 target.reportError(null,e); |
|
376 } |
|
377 } |
|
378 |
|
379 public void serializeURIs(BeanT bean, XMLSerializer target) throws SAXException { |
|
380 try { |
|
381 if (retainPropertyInfo) { |
|
382 final Property parentProperty = target.getCurrentProperty(); |
|
383 for( Property<BeanT> p : uriProperties ) { |
|
384 target.currentProperty.set(p); |
|
385 p.serializeURIs(bean,target); |
|
386 } |
|
387 target.currentProperty.set(parentProperty); |
|
388 } else { |
|
389 for( Property<BeanT> p : uriProperties ) { |
|
390 p.serializeURIs(bean,target); |
|
391 } |
|
392 } |
|
393 if(inheritedAttWildcard!=null) { |
|
394 Map<QName,String> map = inheritedAttWildcard.get(bean); |
|
395 target.attWildcardAsURIs(map,null); |
|
396 } |
|
397 } catch (AccessorException e) { |
|
398 target.reportError(null,e); |
|
399 } |
|
400 } |
|
401 |
|
402 public Loader getLoader(JAXBContextImpl context, boolean typeSubstitutionCapable) { |
|
403 if(loader==null) { |
|
404 // these variables have to be set before they are initialized, |
|
405 // because the initialization may build other loaders and they may refer to this. |
|
406 StructureLoader sl = new StructureLoader(this); |
|
407 loader = sl; |
|
408 if(ci.hasSubClasses()) |
|
409 loaderWithTypeSubst = new XsiTypeLoader(this); |
|
410 else |
|
411 // optimization. we know there can be no @xsi:type |
|
412 loaderWithTypeSubst = loader; |
|
413 |
|
414 |
|
415 sl.init(context,this,ci.getAttributeWildcard()); |
|
416 } |
|
417 if(typeSubstitutionCapable) |
|
418 return loaderWithTypeSubst; |
|
419 else |
|
420 return loader; |
|
421 } |
|
422 |
|
423 public Transducer<BeanT> getTransducer() { |
|
424 return xducer; |
|
425 } |
|
426 |
|
427 private static final AttributeProperty[] EMPTY_PROPERTIES = new AttributeProperty[0]; |
|
428 |
|
429 private static final Logger logger = Util.getClassLogger(); |
|
430 |
|
431 } |