Thu, 12 Oct 2017 19:44:07 +0800
merge
1 /*
2 * Copyright (c) 1997, 2010, 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.model.impl;
28 import java.io.IOException;
29 import java.lang.annotation.Annotation;
30 import java.lang.reflect.Field;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Modifier;
33 import java.lang.reflect.Type;
34 import java.util.List;
35 import java.util.Map;
37 import javax.xml.bind.JAXBException;
38 import javax.xml.namespace.QName;
39 import javax.xml.stream.XMLStreamException;
41 import com.sun.istack.internal.NotNull;
42 import com.sun.xml.internal.bind.AccessorFactory;
43 import com.sun.xml.internal.bind.AccessorFactoryImpl;
44 import com.sun.xml.internal.bind.InternalAccessorFactory;
45 import com.sun.xml.internal.bind.XmlAccessorFactory;
46 import com.sun.xml.internal.bind.annotation.XmlLocation;
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.model.annotation.Locatable;
50 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
51 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
52 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElement;
53 import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo;
54 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeValuePropertyInfo;
55 import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
56 import com.sun.xml.internal.bind.v2.runtime.Location;
57 import com.sun.xml.internal.bind.v2.runtime.Name;
58 import com.sun.xml.internal.bind.v2.runtime.Transducer;
59 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
60 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
61 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
62 import com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor;
63 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
65 import org.xml.sax.Locator;
66 import org.xml.sax.SAXException;
68 /**
69 * @author Kohsuke Kawaguchi (kk@kohsuke.org)
70 */
71 class RuntimeClassInfoImpl extends ClassInfoImpl<Type,Class,Field,Method>
72 implements RuntimeClassInfo, RuntimeElement {
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;
82 private AccessorFactory accessorFactory;
84 private boolean supressAccessorWarnings = false;
86 public RuntimeClassInfoImpl(RuntimeModelBuilder modelBuilder, Locatable upstream, Class clazz) {
87 super(modelBuilder, upstream, clazz);
88 accessorFactory = createAccessorFactory(clazz);
89 }
91 protected AccessorFactory createAccessorFactory(Class clazz) {
92 XmlAccessorFactory factoryAnn;
93 AccessorFactory accFactory = null;
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 }
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 }
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 }
135 public Method getFactoryMethod(){
136 return super.getFactoryMethod();
137 }
139 public final RuntimeClassInfoImpl getBaseClass() {
140 return (RuntimeClassInfoImpl)super.getBaseClass();
141 }
143 @Override
144 protected ReferencePropertyInfoImpl createReferenceProperty(PropertySeed<Type,Class,Field,Method> seed) {
145 return new RuntimeReferencePropertyInfoImpl(this,seed);
146 }
148 @Override
149 protected AttributePropertyInfoImpl createAttributeProperty(PropertySeed<Type,Class,Field,Method> seed) {
150 return new RuntimeAttributePropertyInfoImpl(this,seed);
151 }
153 @Override
154 protected ValuePropertyInfoImpl createValueProperty(PropertySeed<Type,Class,Field,Method> seed) {
155 return new RuntimeValuePropertyInfoImpl(this,seed);
156 }
158 @Override
159 protected ElementPropertyInfoImpl createElementProperty(PropertySeed<Type,Class,Field,Method> seed) {
160 return new RuntimeElementPropertyInfoImpl(this,seed);
161 }
163 @Override
164 protected MapPropertyInfoImpl createMapProperty(PropertySeed<Type,Class,Field,Method> seed) {
165 return new RuntimeMapPropertyInfoImpl(this,seed);
166 }
169 @Override
170 public List<? extends RuntimePropertyInfo> getProperties() {
171 return (List<? extends RuntimePropertyInfo>)super.getProperties();
172 }
174 @Override
175 public RuntimePropertyInfo getProperty(String name) {
176 return (RuntimePropertyInfo)super.getProperty(name);
177 }
180 public void link() {
181 getTransducer(); // populate the transducer
182 super.link();
183 }
185 private Accessor<?,Map<QName,String>> attributeWildcardAccessor;
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 }
198 private boolean computedTransducer = false;
199 private Transducer xducer = null;
201 public Transducer getTransducer() {
202 if(!computedTransducer) {
203 computedTransducer = true;
204 xducer = calcTransducer();
205 }
206 return xducer;
207 }
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.
230 return new TransducerImpl(getClazz(),TransducedAccessor.get(
231 ((RuntimeModelBuilder)builder).context,valuep));
232 }
234 /**
235 * Creates
236 */
237 private Accessor<?,Map<QName,String>> createAttributeWildcardAccessor() {
238 assert attributeWildcard!=null;
239 return ((RuntimePropertySeed)attributeWildcard).getAccessor();
240 }
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 }
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 }
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 }
284 public Accessor<?,Locator> getLocatorField() {
285 return xmlLocationAccessor;
286 }
288 static final class RuntimePropertySeed implements PropertySeed<Type,Class,Field,Method> {
289 /**
290 * @see #getAccessor()
291 */
292 private final Accessor acc;
294 private final PropertySeed<Type,Class,Field,Method> core;
296 public RuntimePropertySeed(PropertySeed<Type,Class,Field,Method> core, Accessor acc) {
297 this.core = core;
298 this.acc = acc;
299 }
301 public String getName() {
302 return core.getName();
303 }
305 public <A extends Annotation> A readAnnotation(Class<A> annotationType) {
306 return core.readAnnotation(annotationType);
307 }
309 public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
310 return core.hasAnnotation(annotationType);
311 }
313 public Type getRawType() {
314 return core.getRawType();
315 }
317 public Location getLocation() {
318 return core.getLocation();
319 }
321 public Locatable getUpstream() {
322 return core.getUpstream();
323 }
325 public Accessor getAccessor() {
326 return acc;
327 }
328 }
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;
341 public TransducerImpl(Class<BeanT> ownerClass,TransducedAccessor<BeanT> xacc) {
342 this.xacc = xacc;
343 this.ownerClass = ownerClass;
344 }
346 public boolean useNamespace() {
347 return xacc.useNamespace();
348 }
350 public boolean isDefault() {
351 return false;
352 }
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 }
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 }
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);
383 xacc.parse(inst,lexical);
384 return inst;
385 }
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 }
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 }
399 public QName getTypeName(BeanT instance) {
400 return null;
401 }
402 }
403 }