001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.model.impl;
027:
028: import java.io.IOException;
029: import java.lang.annotation.Annotation;
030: import java.lang.reflect.Field;
031: import java.lang.reflect.Method;
032: import java.lang.reflect.Modifier;
033: import java.lang.reflect.Type;
034: import java.util.List;
035: import java.util.Map;
036:
037: import javax.xml.namespace.QName;
038: import javax.xml.stream.XMLStreamException;
039:
040: import com.sun.istack.internal.NotNull;
041: import com.sun.xml.internal.bind.annotation.XmlLocation;
042: import com.sun.xml.internal.bind.api.AccessorException;
043: import com.sun.xml.internal.bind.v2.ClassFactory;
044: import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
045: import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
046: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo;
047: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElement;
048: import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo;
049: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeValuePropertyInfo;
050: import com.sun.xml.internal.bind.v2.runtime.Location;
051: import com.sun.xml.internal.bind.v2.runtime.Name;
052: import com.sun.xml.internal.bind.v2.runtime.Transducer;
053: import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
054: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
055: import com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor;
056: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
057:
058: import org.xml.sax.Locator;
059: import org.xml.sax.SAXException;
060:
061: /**
062: * @author Kohsuke Kawaguchi (kk@kohsuke.org)
063: */
064: class RuntimeClassInfoImpl extends
065: ClassInfoImpl<Type, Class, Field, Method> implements
066: RuntimeClassInfo, RuntimeElement {
067:
068: /**
069: * If this class has a property annotated with {@link XmlLocation},
070: * this field will get the accessor for it.
071: *
072: * TODO: support method based XmlLocation
073: */
074: private Accessor<?, Locator> xmlLocationAccessor;
075:
076: public RuntimeClassInfoImpl(RuntimeModelBuilder modelBuilder,
077: Locatable upstream, Class clazz) {
078: super (modelBuilder, upstream, clazz);
079: }
080:
081: public Method getFactoryMethod() {
082: return super .getFactoryMethod();
083: }
084:
085: public final RuntimeClassInfoImpl getBaseClass() {
086: return (RuntimeClassInfoImpl) super .getBaseClass();
087: }
088:
089: @Override
090: protected ReferencePropertyInfoImpl createReferenceProperty(
091: PropertySeed<Type, Class, Field, Method> seed) {
092: return new RuntimeReferencePropertyInfoImpl(this , seed);
093: }
094:
095: @Override
096: protected AttributePropertyInfoImpl createAttributeProperty(
097: PropertySeed<Type, Class, Field, Method> seed) {
098: return new RuntimeAttributePropertyInfoImpl(this , seed);
099: }
100:
101: @Override
102: protected ValuePropertyInfoImpl createValueProperty(
103: PropertySeed<Type, Class, Field, Method> seed) {
104: return new RuntimeValuePropertyInfoImpl(this , seed);
105: }
106:
107: @Override
108: protected ElementPropertyInfoImpl createElementProperty(
109: PropertySeed<Type, Class, Field, Method> seed) {
110: return new RuntimeElementPropertyInfoImpl(this , seed);
111: }
112:
113: @Override
114: protected MapPropertyInfoImpl createMapProperty(
115: PropertySeed<Type, Class, Field, Method> seed) {
116: return new RuntimeMapPropertyInfoImpl(this , seed);
117: }
118:
119: @Override
120: public List<? extends RuntimePropertyInfo> getProperties() {
121: return (List<? extends RuntimePropertyInfo>) super
122: .getProperties();
123: }
124:
125: @Override
126: public RuntimePropertyInfo getProperty(String name) {
127: return (RuntimePropertyInfo) super .getProperty(name);
128: }
129:
130: public void link() {
131: super .link();
132: getTransducer(); // populate the transducer
133: }
134:
135: private Accessor<?, Map<QName, String>> attributeWildcardAccessor;
136:
137: public <B> Accessor<B, Map<QName, String>> getAttributeWildcard() {
138: for (RuntimeClassInfoImpl c = this ; c != null; c = c
139: .getBaseClass()) {
140: if (c.attributeWildcard != null) {
141: if (c.attributeWildcardAccessor == null)
142: c.attributeWildcardAccessor = c
143: .createAttributeWildcardAccessor();
144: return (Accessor<B, Map<QName, String>>) c.attributeWildcardAccessor;
145: }
146: }
147: return null;
148: }
149:
150: private boolean computedTransducer = false;
151: private Transducer xducer = null;
152:
153: public Transducer getTransducer() {
154: if (!computedTransducer) {
155: computedTransducer = true;
156: xducer = calcTransducer();
157: }
158: return xducer;
159: }
160:
161: /**
162: * Creates a transducer if this class is bound to a text in XML.
163: */
164: private Transducer calcTransducer() {
165: RuntimeValuePropertyInfo valuep = null;
166: if (hasAttributeWildcard())
167: return null; // has attribute wildcard. Can't be handled as a leaf
168: for (RuntimeClassInfoImpl ci = this ; ci != null; ci = ci
169: .getBaseClass()) {
170: for (RuntimePropertyInfo pi : ci.getProperties())
171: if (pi.kind() == PropertyKind.VALUE) {
172: valuep = (RuntimeValuePropertyInfo) pi;
173: } else {
174: // this bean has something other than a value
175: return null;
176: }
177: }
178: if (valuep == null)
179: return null;
180: if (!valuep.getTarget().isSimpleType())
181: return null; // if there's an error, recover from it by returning null.
182:
183: return new TransducerImpl(getClazz(), TransducedAccessor
184: .get(valuep));
185: }
186:
187: /**
188: * Creates
189: */
190: private Accessor<?, Map<QName, String>> createAttributeWildcardAccessor() {
191: assert attributeWildcard != null;
192: return ((RuntimePropertySeed) attributeWildcard).getAccessor();
193: }
194:
195: @Override
196: protected RuntimePropertySeed createFieldSeed(Field field) {
197: Accessor.FieldReflection acc;
198: if (Modifier.isStatic(field.getModifiers()))
199: acc = new Accessor.ReadOnlyFieldReflection(field);
200: else
201: acc = new Accessor.FieldReflection(field);
202:
203: return new RuntimePropertySeed(super .createFieldSeed(field),
204: acc);
205: }
206:
207: @Override
208: public RuntimePropertySeed createAccessorSeed(Method getter,
209: Method setter) {
210: Accessor acc;
211: if (getter == null)
212: acc = new Accessor.SetterOnlyReflection(setter);
213: else if (setter == null)
214: acc = new Accessor.GetterOnlyReflection(getter);
215: else
216: acc = new Accessor.GetterSetterReflection(getter, setter);
217:
218: return new RuntimePropertySeed(super .createAccessorSeed(getter,
219: setter), acc);
220: }
221:
222: @Override
223: protected void checkFieldXmlLocation(Field f) {
224: if (reader().hasFieldAnnotation(XmlLocation.class, f))
225: // TODO: check for XmlLocation signature
226: // TODO: check a collision with the super class
227: xmlLocationAccessor = new Accessor.FieldReflection<Object, Locator>(
228: f);
229: }
230:
231: public Accessor<?, Locator> getLocatorField() {
232: return xmlLocationAccessor;
233: }
234:
235: static final class RuntimePropertySeed implements
236: PropertySeed<Type, Class, Field, Method> {
237: /**
238: * @see #getAccessor()
239: */
240: private final Accessor acc;
241:
242: private final PropertySeed<Type, Class, Field, Method> core;
243:
244: public RuntimePropertySeed(
245: PropertySeed<Type, Class, Field, Method> core,
246: Accessor acc) {
247: this .core = core;
248: this .acc = acc;
249: }
250:
251: public String getName() {
252: return core.getName();
253: }
254:
255: public <A extends Annotation> A readAnnotation(
256: Class<A> annotationType) {
257: return core.readAnnotation(annotationType);
258: }
259:
260: public boolean hasAnnotation(
261: Class<? extends Annotation> annotationType) {
262: return core.hasAnnotation(annotationType);
263: }
264:
265: public Type getRawType() {
266: return core.getRawType();
267: }
268:
269: public Location getLocation() {
270: return core.getLocation();
271: }
272:
273: public Locatable getUpstream() {
274: return core.getUpstream();
275: }
276:
277: public Accessor getAccessor() {
278: return acc;
279: }
280: }
281:
282: /**
283: * {@link Transducer} implementation used when this class maps to PCDATA in XML.
284: *
285: * TODO: revisit the exception handling
286: */
287: private static final class TransducerImpl<BeanT> implements
288: Transducer<BeanT> {
289: private final TransducedAccessor<BeanT> xacc;
290: private final Class<BeanT> ownerClass;
291:
292: public TransducerImpl(Class<BeanT> ownerClass,
293: TransducedAccessor<BeanT> xacc) {
294: this .xacc = xacc;
295: this .ownerClass = ownerClass;
296: }
297:
298: public boolean useNamespace() {
299: return xacc.useNamespace();
300: }
301:
302: public boolean isDefault() {
303: return false;
304: }
305:
306: public void declareNamespace(BeanT bean, XMLSerializer w)
307: throws AccessorException {
308: try {
309: xacc.declareNamespace(bean, w);
310: } catch (SAXException e) {
311: throw new AccessorException(e);
312: }
313: }
314:
315: public @NotNull
316: CharSequence print(BeanT o) throws AccessorException {
317: try {
318: CharSequence value = xacc.print(o);
319: if (value == null)
320: throw new AccessorException(
321: Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE
322: .format(o));
323: return value;
324: } catch (SAXException e) {
325: throw new AccessorException(e);
326: }
327: }
328:
329: public BeanT parse(CharSequence lexical)
330: throws AccessorException, SAXException {
331: UnmarshallingContext ctxt = UnmarshallingContext
332: .getInstance();
333: BeanT inst;
334: if (ctxt != null)
335: inst = (BeanT) ctxt.createInstance(ownerClass);
336: else
337: // when this runs for parsing enum constants,
338: // there's no UnmarshallingContext.
339: inst = ClassFactory.create(ownerClass);
340:
341: xacc.parse(inst, lexical);
342: return inst;
343: }
344:
345: public void writeText(XMLSerializer w, BeanT o, String fieldName)
346: throws IOException, SAXException, XMLStreamException,
347: AccessorException {
348: if (!xacc.hasValue(o))
349: throw new AccessorException(
350: Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE
351: .format(o));
352: xacc.writeText(w, o, fieldName);
353: }
354:
355: public void writeLeafElement(XMLSerializer w, Name tagName,
356: BeanT o, String fieldName) throws IOException,
357: SAXException, XMLStreamException, AccessorException {
358: if (!xacc.hasValue(o))
359: throw new AccessorException(
360: Messages.THERE_MUST_BE_VALUE_IN_XMLVALUE
361: .format(o));
362: xacc.writeLeafElement(w, tagName, o, fieldName);
363: }
364:
365: public QName getTypeName(BeanT instance) {
366: return null;
367: }
368: }
369: }
|