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.runtime.property;
038:
039: import java.io.IOException;
040: import java.lang.reflect.Modifier;
041: import java.lang.reflect.Type;
042: import java.util.HashMap;
043: import java.util.Map;
044:
045: import javax.xml.namespace.QName;
046: import javax.xml.stream.XMLStreamException;
047:
048: import com.sun.xml.bind.api.AccessorException;
049: import com.sun.xml.bind.v2.model.core.PropertyKind;
050: import com.sun.xml.bind.v2.model.core.TypeRef;
051: import com.sun.xml.bind.v2.model.runtime.RuntimeElementPropertyInfo;
052: import com.sun.xml.bind.v2.model.runtime.RuntimeTypeInfo;
053: import com.sun.xml.bind.v2.model.runtime.RuntimeTypeRef;
054: import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
055: import com.sun.xml.bind.v2.runtime.JaxBeanInfo;
056: import com.sun.xml.bind.v2.runtime.Name;
057: import com.sun.xml.bind.v2.runtime.XMLSerializer;
058: import com.sun.xml.bind.v2.runtime.reflect.Accessor;
059: import com.sun.xml.bind.v2.runtime.unmarshaller.ChildLoader;
060: import com.sun.xml.bind.v2.runtime.unmarshaller.DefaultValueLoaderDecorator;
061: import com.sun.xml.bind.v2.runtime.unmarshaller.Loader;
062: import com.sun.xml.bind.v2.runtime.unmarshaller.XsiNilLoader;
063: import com.sun.xml.bind.v2.util.QNameMap;
064:
065: import org.xml.sax.SAXException;
066:
067: /**
068: * @author Kohsuke Kawaguchi (kk@kohsuke.org)
069: */
070: final class SingleElementNodeProperty<BeanT, ValueT> extends
071: PropertyImpl<BeanT> {
072:
073: private final Accessor<BeanT, ValueT> acc;
074:
075: private final boolean nillable;
076:
077: private final QName[] acceptedElements;
078:
079: private final Map<Class, TagAndType> typeNames = new HashMap<Class, TagAndType>();
080:
081: private RuntimeElementPropertyInfo prop;
082:
083: /**
084: * The tag name used to produce xsi:nil. The first one in the list.
085: */
086: private final Name nullTagName;
087:
088: public SingleElementNodeProperty(JAXBContextImpl context,
089: RuntimeElementPropertyInfo prop) {
090: super (context, prop);
091: acc = prop.getAccessor().optimize(context);
092: this .prop = prop;
093:
094: QName nt = null;
095: boolean nil = false;
096:
097: acceptedElements = new QName[prop.getTypes().size()];
098: for (int i = 0; i < acceptedElements.length; i++)
099: acceptedElements[i] = prop.getTypes().get(i).getTagName();
100:
101: for (RuntimeTypeRef e : prop.getTypes()) {
102: JaxBeanInfo beanInfo = context.getOrCreate(e.getTarget());
103: if (nt == null)
104: nt = e.getTagName();
105: typeNames.put(beanInfo.jaxbType, new TagAndType(
106: context.nameBuilder.createElementName(e
107: .getTagName()), beanInfo));
108: nil |= e.isNillable();
109: }
110:
111: nullTagName = context.nameBuilder.createElementName(nt);
112:
113: nillable = nil;
114: }
115:
116: public void wrapUp() {
117: super .wrapUp();
118: prop = null;
119: }
120:
121: public void reset(BeanT bean) throws AccessorException {
122: acc.set(bean, null);
123: }
124:
125: public String getIdValue(BeanT beanT) {
126: return null;
127: }
128:
129: public void serializeBody(BeanT o, XMLSerializer w, Object outerPeer)
130: throws SAXException, AccessorException, IOException,
131: XMLStreamException {
132: ValueT v = acc.get(o);
133: if (v != null) {
134: Class vtype = v.getClass();
135: TagAndType tt = typeNames.get(vtype); // quick way that usually works
136:
137: if (tt == null) {// slow way that always works
138: for (Map.Entry<Class, TagAndType> e : typeNames
139: .entrySet()) {
140: if (e.getKey().isAssignableFrom(vtype)) {
141: tt = e.getValue();
142: break;
143: }
144: }
145: }
146:
147: if (tt == null) {
148: // actually this is an error, because the actual type was not a sub-type
149: // of any of the types specified in the annotations,
150: // but for the purpose of experimenting with simple type substitution,
151: // it's convenient to marshal this anyway (for example so that classes
152: // generated from simple types like String can be marshalled as expected.)
153: w.startElement(
154: typeNames.values().iterator().next().tagName,
155: null);
156: w.childAsXsiType(v, fieldName, w.grammar
157: .getBeanInfo(Object.class));
158: } else {
159: w.startElement(tt.tagName, null);
160: w.childAsXsiType(v, fieldName, tt.beanInfo);
161: }
162: w.endElement();
163: } else if (nillable) {
164: w.startElement(nullTagName, null);
165: w.writeXsiNilTrue();
166: w.endElement();
167: }
168: }
169:
170: public void buildChildElementUnmarshallers(UnmarshallerChain chain,
171: QNameMap<ChildLoader> handlers) {
172: JAXBContextImpl context = chain.context;
173:
174: for (TypeRef<Type, Class> e : prop.getTypes()) {
175: JaxBeanInfo bi = context.getOrCreate((RuntimeTypeInfo) e
176: .getTarget());
177: // if the expected Java type is already final, type substitution won't really work anyway.
178: // this also traps cases like trying to substitute xsd:long element with xsi:type='xsd:int'
179: Loader l = bi.getLoader(context, !Modifier
180: .isFinal(bi.jaxbType.getModifiers()));
181: if (e.getDefaultValue() != null)
182: l = new DefaultValueLoaderDecorator(l, e
183: .getDefaultValue());
184: if (nillable || chain.context.allNillable)
185: l = new XsiNilLoader.Single(l, acc);
186: handlers.put(e.getTagName(), new ChildLoader(l, acc));
187: }
188: }
189:
190: public PropertyKind getKind() {
191: return PropertyKind.ELEMENT;
192: }
193:
194: @Override
195: public Accessor getElementPropertyAccessor(String nsUri,
196: String localName) {
197: for (QName n : acceptedElements) {
198: if (n.getNamespaceURI().equals(nsUri)
199: && n.getLocalPart().equals(localName))
200: return acc;
201: }
202: return null;
203: }
204:
205: }
|