001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2.model.impl;
038:
039: import java.io.IOException;
040: import java.lang.annotation.Annotation;
041: import java.lang.reflect.Field;
042: import java.lang.reflect.Method;
043: import java.lang.reflect.Modifier;
044: import java.lang.reflect.Type;
045: import java.util.List;
046: import java.util.Map;
047:
048: import javax.xml.bind.JAXBException;
049: import javax.xml.namespace.QName;
050: import javax.xml.stream.XMLStreamException;
051:
052: import com.sun.istack.NotNull;
053: import com.sun.xml.bind.AccessorFactory;
054: import com.sun.xml.bind.AccessorFactoryImpl;
055: import com.sun.xml.bind.XmlAccessorFactory;
056: import com.sun.xml.bind.annotation.XmlLocation;
057: import com.sun.xml.bind.api.AccessorException;
058: import com.sun.xml.bind.v2.ClassFactory;
059: import com.sun.xml.bind.v2.model.annotation.Locatable;
060: import com.sun.xml.bind.v2.model.core.PropertyKind;
061: import com.sun.xml.bind.v2.model.runtime.RuntimeClassInfo;
062: import com.sun.xml.bind.v2.model.runtime.RuntimeElement;
063: import com.sun.xml.bind.v2.model.runtime.RuntimePropertyInfo;
064: import com.sun.xml.bind.v2.model.runtime.RuntimeValuePropertyInfo;
065: import com.sun.xml.bind.v2.runtime.IllegalAnnotationException;
066: import com.sun.xml.bind.v2.runtime.Location;
067: import com.sun.xml.bind.v2.runtime.Name;
068: import com.sun.xml.bind.v2.runtime.Transducer;
069: import com.sun.xml.bind.v2.runtime.XMLSerializer;
070: import com.sun.xml.bind.v2.runtime.reflect.Accessor;
071: import com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor;
072: import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
073:
074: import org.xml.sax.Locator;
075: import org.xml.sax.SAXException;
076:
077: /**
078: * @author Kohsuke Kawaguchi (kk@kohsuke.org)
079: */
080: class RuntimeClassInfoImpl extends
081: ClassInfoImpl<Type, Class, Field, Method> implements
082: RuntimeClassInfo, RuntimeElement {
083:
084: /**
085: * If this class has a property annotated with {@link XmlLocation},
086: * this field will get the accessor for it.
087: *
088: * TODO: support method based XmlLocation
089: */
090: private Accessor<?, Locator> xmlLocationAccessor;
091:
092: private AccessorFactory accessorFactory;
093:
094: public RuntimeClassInfoImpl(RuntimeModelBuilder modelBuilder,
095: Locatable upstream, Class clazz) {
096: super (modelBuilder, upstream, clazz);
097: accessorFactory = createAccessorFactory(clazz);
098: }
099:
100: protected AccessorFactory createAccessorFactory(Class clazz) {
101: XmlAccessorFactory factoryAnn;
102: AccessorFactory accFactory = null;
103:
104: // user providing class to be used.
105: if (((RuntimeModelBuilder) builder).context.xmlAccessorFactorySupport) {
106: factoryAnn = findXmlAccessorFactoryAnnotation(clazz);
107:
108: if (factoryAnn != null) {
109: try {
110: accFactory = factoryAnn.value().newInstance();
111: } catch (InstantiationException e) {
112: builder
113: .reportError(new IllegalAnnotationException(
114: Messages.ACCESSORFACTORY_INSTANTIATION_EXCEPTION
115: .format(factoryAnn
116: .getClass()
117: .getName(),
118: nav().getClassName(
119: clazz)),
120: this ));
121: } catch (IllegalAccessException e) {
122: builder
123: .reportError(new IllegalAnnotationException(
124: Messages.ACCESSORFACTORY_ACCESS_EXCEPTION
125: .format(factoryAnn
126: .getClass()
127: .getName(),
128: nav().getClassName(
129: clazz)),
130: this ));
131: }
132: }
133: }
134:
135: // Fall back to local AccessorFactory when no
136: // user not providing one or as error recovery.
137: if (accFactory == null) {
138: accFactory = AccessorFactoryImpl.getInstance();
139: }
140: return accFactory;
141: }
142:
143: protected XmlAccessorFactory findXmlAccessorFactoryAnnotation(
144: Class clazz) {
145: XmlAccessorFactory factoryAnn = reader().getClassAnnotation(
146: XmlAccessorFactory.class, clazz, this );
147: if (factoryAnn == null) {
148: factoryAnn = reader().getPackageAnnotation(
149: XmlAccessorFactory.class, clazz, this );
150: }
151: return factoryAnn;
152: }
153:
154: public Method getFactoryMethod() {
155: return super .getFactoryMethod();
156: }
157:
158: public final RuntimeClassInfoImpl getBaseClass() {
159: return (RuntimeClassInfoImpl) super .getBaseClass();
160: }
161:
162: @Override
163: protected ReferencePropertyInfoImpl createReferenceProperty(
164: PropertySeed<Type, Class, Field, Method> seed) {
165: return new RuntimeReferencePropertyInfoImpl(this , seed);
166: }
167:
168: @Override
169: protected AttributePropertyInfoImpl createAttributeProperty(
170: PropertySeed<Type, Class, Field, Method> seed) {
171: return new RuntimeAttributePropertyInfoImpl(this , seed);
172: }
173:
174: @Override
175: protected ValuePropertyInfoImpl createValueProperty(
176: PropertySeed<Type, Class, Field, Method> seed) {
177: return new RuntimeValuePropertyInfoImpl(this , seed);
178: }
179:
180: @Override
181: protected ElementPropertyInfoImpl createElementProperty(
182: PropertySeed<Type, Class, Field, Method> seed) {
183: return new RuntimeElementPropertyInfoImpl(this , seed);
184: }
185:
186: @Override
187: protected MapPropertyInfoImpl createMapProperty(
188: PropertySeed<Type, Class, Field, Method> seed) {
189: return new RuntimeMapPropertyInfoImpl(this , seed);
190: }
191:
192: @Override
193: public List<? extends RuntimePropertyInfo> getProperties() {
194: return (List<? extends RuntimePropertyInfo>) super
195: .getProperties();
196: }
197:
198: @Override
199: public RuntimePropertyInfo getProperty(String name) {
200: return (RuntimePropertyInfo) super .getProperty(name);
201: }
202:
203: public void link() {
204: getTransducer(); // populate the transducer
205: super .link();
206: }
207:
208: private Accessor<?, Map<QName, String>> attributeWildcardAccessor;
209:
210: public <B> Accessor<B, Map<QName, String>> getAttributeWildcard() {
211: for (RuntimeClassInfoImpl c = this ; c != null; c = c
212: .getBaseClass()) {
213: if (c.attributeWildcard != null) {
214: if (c.attributeWildcardAccessor == null)
215: c.attributeWildcardAccessor = c
216: .createAttributeWildcardAccessor();
217: return (Accessor<B, Map<QName, String>>) c.attributeWildcardAccessor;
218: }
219: }
220: return null;
221: }
222:
223: private boolean computedTransducer = false;
224: private Transducer xducer = null;
225:
226: public Transducer getTransducer() {
227: if (!computedTransducer) {
228: computedTransducer = true;
229: xducer = calcTransducer();
230: }
231: return xducer;
232: }
233:
234: /**
235: * Creates a transducer if this class is bound to a text in XML.
236: */
237: private Transducer calcTransducer() {
238: RuntimeValuePropertyInfo valuep = null;
239: if (hasAttributeWildcard())
240: return null; // has attribute wildcard. Can't be handled as a leaf
241: for (RuntimeClassInfoImpl ci = this ; ci != null; ci = ci
242: .getBaseClass()) {
243: for (RuntimePropertyInfo pi : ci.getProperties())
244: if (pi.kind() == PropertyKind.VALUE) {
245: valuep = (RuntimeValuePropertyInfo) pi;
246: } else {
247: // this bean has something other than a value
248: return null;
249: }
250: }
251: if (valuep == null)
252: return null;
253: if (!valuep.getTarget().isSimpleType())
254: return null; // if there's an error, recover from it by returning null.
255:
256: return new TransducerImpl(getClazz(), TransducedAccessor.get(
257: ((RuntimeModelBuilder) builder).context, valuep));
258: }
259:
260: /**
261: * Creates
262: */
263: private Accessor<?, Map<QName, String>> createAttributeWildcardAccessor() {
264: assert attributeWildcard != null;
265: return ((RuntimePropertySeed) attributeWildcard).getAccessor();
266: }
267:
268: @Override
269: protected RuntimePropertySeed createFieldSeed(Field field) {
270: final boolean readOnly = Modifier
271: .isStatic(field.getModifiers());
272: Accessor acc;
273: try {
274: acc = accessorFactory.createFieldAccessor(clazz, field,
275: readOnly);
276: } catch (JAXBException e) {
277: builder.reportError(new IllegalAnnotationException(
278: Messages.CUSTOM_ACCESSORFACTORY_FIELD_ERROR.format(
279: nav().getClassName(clazz), e.toString()),
280: this ));
281: acc = Accessor.getErrorInstance(); // error recovery
282: }
283: return new RuntimePropertySeed(super .createFieldSeed(field),
284: acc);
285: }
286:
287: @Override
288: public RuntimePropertySeed createAccessorSeed(Method getter,
289: Method setter) {
290: Accessor acc;
291: try {
292: acc = accessorFactory.createPropertyAccessor(clazz, getter,
293: setter);
294: } catch (JAXBException e) {
295: builder.reportError(new IllegalAnnotationException(
296: Messages.CUSTOM_ACCESSORFACTORY_PROPERTY_ERROR
297: .format(nav().getClassName(clazz), e
298: .toString()), this ));
299: acc = Accessor.getErrorInstance(); // error recovery
300: }
301: return new RuntimePropertySeed(super .createAccessorSeed(getter,
302: setter), acc);
303: }
304:
305: @Override
306: protected void checkFieldXmlLocation(Field f) {
307: if (reader().hasFieldAnnotation(XmlLocation.class, f))
308: // TODO: check for XmlLocation signature
309: // TODO: check a collision with the super class
310: xmlLocationAccessor = new Accessor.FieldReflection<Object, Locator>(
311: f);
312: }
313:
314: public Accessor<?, Locator> getLocatorField() {
315: return xmlLocationAccessor;
316: }
317:
318: static final class RuntimePropertySeed implements
319: PropertySeed<Type, Class, Field, Method> {
320: /**
321: * @see #getAccessor()
322: */
323: private final Accessor acc;
324:
325: private final PropertySeed<Type, Class, Field, Method> core;
326:
327: public RuntimePropertySeed(
328: PropertySeed<Type, Class, Field, Method> core,
329: Accessor acc) {
330: this .core = core;
331: this .acc = acc;
332: }
333:
334: public String getName() {
335: return core.getName();
336: }
337:
338: public <A extends Annotation> A readAnnotation(
339: Class<A> annotationType) {
340: return core.readAnnotation(annotationType);
341: }
342:
343: public boolean hasAnnotation(
344: Class<? extends Annotation> annotationType) {
345: return core.hasAnnotation(annotationType);
346: }
347:
348: public Type getRawType() {
349: return core.getRawType();
350: }
351:
352: public Location getLocation() {
353: return core.getLocation();
354: }
355:
356: public Locatable getUpstream() {
357: return core.getUpstream();
358: }
359:
360: public Accessor getAccessor() {
361: return acc;
362: }
363: }
364:
365: /**
366: * {@link Transducer} implementation used when this class maps to PCDATA in XML.
367: *
368: * TODO: revisit the exception handling
369: */
370: private static final class TransducerImpl<BeanT> implements
371: Transducer<BeanT> {
372: private final TransducedAccessor<BeanT> xacc;
373: private final Class<BeanT> ownerClass;
374:
375: public TransducerImpl(Class<BeanT> ownerClass,
376: TransducedAccessor<BeanT> xacc) {
377: this .xacc = xacc;
378: this .ownerClass = ownerClass;
379: }
380:
381: public boolean useNamespace() {
382: return xacc.useNamespace();
383: }
384:
385: public boolean isDefault() {
386: return false;
387: }
388:
389: public void declareNamespace(BeanT bean, XMLSerializer w)
390: throws AccessorException {
391: try {
392: xacc.declareNamespace(bean, w);
393: } catch (SAXException e) {
394: throw new AccessorException(e);
395: }
396: }
397:
398: public @NotNull
399: CharSequence print(BeanT o) throws AccessorException {
400: try {
401: CharSequence value = xacc.print(o);
402: if (value == null)
403: throw new AccessorException(
404: Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE
405: .format(o));
406: return value;
407: } catch (SAXException e) {
408: throw new AccessorException(e);
409: }
410: }
411:
412: public BeanT parse(CharSequence lexical)
413: throws AccessorException, SAXException {
414: UnmarshallingContext ctxt = UnmarshallingContext
415: .getInstance();
416: BeanT inst;
417: if (ctxt != null)
418: inst = (BeanT) ctxt.createInstance(ownerClass);
419: else
420: // when this runs for parsing enum constants,
421: // there's no UnmarshallingContext.
422: inst = ClassFactory.create(ownerClass);
423:
424: xacc.parse(inst, lexical);
425: return inst;
426: }
427:
428: public void writeText(XMLSerializer w, BeanT o, String fieldName)
429: throws IOException, SAXException, XMLStreamException,
430: AccessorException {
431: if (!xacc.hasValue(o))
432: throw new AccessorException(
433: Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE
434: .format(o));
435: xacc.writeText(w, o, fieldName);
436: }
437:
438: public void writeLeafElement(XMLSerializer w, Name tagName,
439: BeanT o, String fieldName) throws IOException,
440: SAXException, XMLStreamException, AccessorException {
441: if (!xacc.hasValue(o))
442: throw new AccessorException(
443: Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE
444: .format(o));
445: xacc.writeLeafElement(w, tagName, o, fieldName);
446: }
447:
448: public QName getTypeName(BeanT instance) {
449: return null;
450: }
451: }
452: }
|