Sun, 31 Aug 2014 16:14:36 +0400
8036981: JAXB not preserving formatting for xsd:any Mixed content
Reviewed-by: lancea, mkos
1 /*
2 * Copyright (c) 1997, 2013, 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.runtime.reflect;
28 import java.lang.ref.WeakReference;
29 import java.lang.reflect.Array;
30 import java.lang.reflect.ParameterizedType;
31 import java.lang.reflect.Type;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.WeakHashMap;
40 import java.util.LinkedList;
41 import java.util.HashSet;
42 import java.util.TreeSet;
43 import java.util.Stack;
44 import java.util.concurrent.Callable;
46 import javax.xml.bind.JAXBException;
48 import com.sun.istack.internal.SAXException2;
49 import com.sun.xml.internal.bind.api.AccessorException;
50 import com.sun.xml.internal.bind.v2.ClassFactory;
51 import com.sun.xml.internal.bind.v2.TODO;
52 import com.sun.xml.internal.bind.v2.model.core.Adapter;
53 import com.sun.xml.internal.bind.v2.model.core.ID;
54 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
55 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Patcher;
56 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
57 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.LocatorEx;
59 import org.xml.sax.SAXException;
61 /**
62 * Used to list individual values of a multi-value property, and
63 * to pack individual values into a multi-value property.
64 *
65 * @author Kohsuke Kawaguchi (kk@kohsuke.org)
66 */
67 public abstract class Lister<BeanT,PropT,ItemT,PackT> {
69 protected Lister() {}
71 /**
72 * Iterates values of a multi-value property.
73 *
74 * @param context
75 * This parameter is used to support ID/IDREF handling.
76 */
77 public abstract ListIterator<ItemT> iterator(PropT multiValueProp, XMLSerializer context);
79 /**
80 * Setting values to a multi-value property starts by creating
81 * a transient object called "pack" from the current field.
82 */
83 public abstract PackT startPacking(BeanT bean, Accessor<BeanT, PropT> acc) throws AccessorException;
85 /**
86 * Once the {@link #startPacking} is called, you can
87 * add values to the pack by using this method.
88 */
89 public abstract void addToPack( PackT pack, ItemT newValue ) throws AccessorException;
91 /**
92 * Finally, call this method to
93 * wraps up the {@code pack}. This method may update the field of
94 * the given bean.
95 */
96 public abstract void endPacking( PackT pack, BeanT bean, Accessor<BeanT,PropT> acc ) throws AccessorException;
98 /**
99 * Clears the values of the property.
100 */
101 public abstract void reset(BeanT o,Accessor<BeanT,PropT> acc) throws AccessorException;
104 /**
105 * Gets a reference to the appropriate {@link Lister} object
106 * if the field is a multi-value field. Otherwise null.
107 *
108 * @param fieldType
109 * the type of the field that stores the collection
110 * @param idness
111 * ID-ness of the property.
112 * @param adapter
113 * adapter to be used for individual items. can be null.
114 */
115 public static <BeanT,PropT,ItemT,PackT>
116 Lister<BeanT,PropT,ItemT,PackT> create(Type fieldType,ID idness, Adapter<Type,Class> adapter) {
118 Class rawType = (Class) Utils.REFLECTION_NAVIGATOR.erasure(fieldType);
119 Class itemType;
121 Lister l;
122 if( rawType.isArray() ) {
123 itemType = rawType.getComponentType();
124 l = getArrayLister(itemType);
125 } else
126 if( Collection.class.isAssignableFrom(rawType) ) {
127 Type bt = Utils.REFLECTION_NAVIGATOR.getBaseClass(fieldType,Collection.class);
128 if(bt instanceof ParameterizedType)
129 itemType = (Class) Utils.REFLECTION_NAVIGATOR.erasure(((ParameterizedType)bt).getActualTypeArguments()[0]);
130 else
131 itemType = Object.class;
132 l = new CollectionLister(getImplClass(rawType));
133 } else
134 return null;
136 if(idness==ID.IDREF)
137 l = new IDREFS(l,itemType);
139 if(adapter!=null)
140 l = new AdaptedLister(l,adapter.adapterType);
142 return l;
143 }
145 private static Class getImplClass(Class<?> fieldType) {
146 return ClassFactory.inferImplClass(fieldType,COLLECTION_IMPL_CLASSES);
147 }
149 /**
150 * Cache instances of {@link ArrayLister}s.
151 */
152 private static final Map<Class,WeakReference<Lister>> arrayListerCache =
153 Collections.synchronizedMap(new WeakHashMap<Class,WeakReference<Lister>>());
155 /**
156 * Creates a lister for array type.
157 */
158 private static Lister getArrayLister( Class componentType ) {
159 Lister l=null;
160 if(componentType.isPrimitive())
161 l = primitiveArrayListers.get(componentType);
162 else {
163 WeakReference<Lister> wr = arrayListerCache.get(componentType);
164 if(wr!=null)
165 l = wr.get();
166 if(l==null) {
167 l = new ArrayLister(componentType);
168 arrayListerCache.put(componentType,new WeakReference<Lister>(l));
169 }
170 }
171 assert l!=null;
172 return l;
173 }
175 /**
176 * {@link Lister} for an array.
177 *
178 * <p>
179 * Array packing is slower, but we expect this to be used less frequently than
180 * the {@link CollectionLister}.
181 */
182 private static final class ArrayLister<BeanT,ItemT> extends Lister<BeanT,ItemT[],ItemT,Pack<ItemT>> {
184 private final Class<ItemT> itemType;
186 public ArrayLister(Class<ItemT> itemType) {
187 this.itemType = itemType;
188 }
190 public ListIterator<ItemT> iterator(final ItemT[] objects, XMLSerializer context) {
191 return new ListIterator<ItemT>() {
192 int idx=0;
193 public boolean hasNext() {
194 return idx<objects.length;
195 }
197 public ItemT next() {
198 return objects[idx++];
199 }
200 };
201 }
203 public Pack startPacking(BeanT current, Accessor<BeanT, ItemT[]> acc) {
204 return new Pack<ItemT>(itemType);
205 }
207 public void addToPack(Pack<ItemT> objects, ItemT o) {
208 objects.add(o);
209 }
211 public void endPacking( Pack<ItemT> pack, BeanT bean, Accessor<BeanT,ItemT[]> acc ) throws AccessorException {
212 acc.set(bean,pack.build());
213 }
215 public void reset(BeanT o,Accessor<BeanT,ItemT[]> acc) throws AccessorException {
216 acc.set(o,(ItemT[])Array.newInstance(itemType,0));
217 }
219 }
221 public static final class Pack<ItemT> extends ArrayList<ItemT> {
222 private final Class<ItemT> itemType;
224 public Pack(Class<ItemT> itemType) {
225 this.itemType = itemType;
226 }
228 public ItemT[] build() {
229 return super.toArray( (ItemT[])Array.newInstance(itemType,size()) );
230 }
231 }
233 /**
234 * Listers for the primitive type arrays, keyed by their primitive Class object.
235 */
236 /*package*/ static final Map<Class,Lister> primitiveArrayListers = new HashMap<Class,Lister>();
238 static {
239 // register primitive array listers
240 PrimitiveArrayListerBoolean.register();
241 PrimitiveArrayListerByte.register();
242 PrimitiveArrayListerCharacter.register();
243 PrimitiveArrayListerDouble.register();
244 PrimitiveArrayListerFloat.register();
245 PrimitiveArrayListerInteger.register();
246 PrimitiveArrayListerLong.register();
247 PrimitiveArrayListerShort.register();
248 }
250 /**
251 * {@link Lister} for a collection
252 */
253 public static final class CollectionLister<BeanT,T extends Collection> extends Lister<BeanT,T,Object,T> {
255 /**
256 * Sometimes we need to create a new instance of a collection.
257 * This is such an implementation class.
258 */
259 private final Class<? extends T> implClass;
261 public CollectionLister(Class<? extends T> implClass) {
262 this.implClass = implClass;
263 }
265 public ListIterator iterator(T collection, XMLSerializer context) {
266 final Iterator itr = collection.iterator();
267 return new ListIterator() {
268 public boolean hasNext() {
269 return itr.hasNext();
270 }
271 public Object next() {
272 return itr.next();
273 }
274 };
275 }
277 public T startPacking(BeanT bean, Accessor<BeanT, T> acc) throws AccessorException {
278 T collection = acc.get(bean);
279 if(collection==null) {
280 collection = ClassFactory.create(implClass);
281 if(!acc.isAdapted())
282 acc.set(bean,collection);
283 }
284 collection.clear();
285 return collection;
286 }
288 public void addToPack(T collection, Object o) {
289 collection.add(o);
290 }
292 public void endPacking( T collection, BeanT bean, Accessor<BeanT,T> acc ) throws AccessorException {
293 // this needs to be done in the endPacking, because
294 // sometimes the accessor uses an adapter, and the adapter needs to see
295 // the whole thing.
297 // but always doing so causes a problem when this collection property
298 // is getter-only
300 // invoke set when possible (see Issue 488)
301 try {
302 if (acc.isAdapted()) {
303 acc.set(bean,collection);
304 }
305 } catch (AccessorException ae) {
306 if(acc.isAdapted()) throw ae;
307 }
308 }
310 public void reset(BeanT bean, Accessor<BeanT, T> acc) throws AccessorException {
311 T collection = acc.get(bean);
312 if(collection == null) {
313 return;
314 }
315 collection.clear();
316 }
317 }
319 /**
320 * {@link Lister} for IDREFS.
321 */
322 private static final class IDREFS<BeanT,PropT> extends Lister<BeanT,PropT,String,IDREFS<BeanT,PropT>.Pack> {
323 private final Lister<BeanT,PropT,Object,Object> core;
324 /**
325 * Expected type to which IDREF resolves to.
326 */
327 private final Class itemType;
329 public IDREFS(Lister core, Class itemType) {
330 this.core = core;
331 this.itemType = itemType;
332 }
334 public ListIterator<String> iterator(PropT prop, XMLSerializer context) {
335 final ListIterator i = core.iterator(prop,context);
337 return new IDREFSIterator(i, context);
338 }
340 public Pack startPacking(BeanT bean, Accessor<BeanT, PropT> acc) {
341 return new Pack(bean,acc);
342 }
344 public void addToPack(Pack pack, String item) {
345 pack.add(item);
346 }
348 public void endPacking(Pack pack, BeanT bean, Accessor<BeanT, PropT> acc) {
349 }
351 public void reset(BeanT bean, Accessor<BeanT, PropT> acc) throws AccessorException {
352 core.reset(bean,acc);
353 }
355 /**
356 * PackT for this lister.
357 */
358 private class Pack implements Patcher {
359 private final BeanT bean;
360 private final List<String> idrefs = new ArrayList<String>();
361 private final UnmarshallingContext context;
362 private final Accessor<BeanT,PropT> acc;
363 private final LocatorEx location;
365 public Pack(BeanT bean, Accessor<BeanT,PropT> acc) {
366 this.bean = bean;
367 this.acc = acc;
368 this.context = UnmarshallingContext.getInstance();
369 this.location = new LocatorEx.Snapshot(context.getLocator());
370 context.addPatcher(this);
371 }
373 public void add(String item) {
374 idrefs.add(item);
375 }
377 /**
378 * Resolves IDREFS and fill in the actual array.
379 */
380 public void run() throws SAXException {
381 try {
382 Object pack = core.startPacking(bean,acc);
384 for( String id : idrefs ) {
385 Callable callable = context.getObjectFromId(id,itemType);
386 Object t;
388 try {
389 t = (callable!=null) ? callable.call() : null;
390 } catch (SAXException e) {
391 throw e;
392 } catch (Exception e) {
393 throw new SAXException2(e);
394 }
396 if(t==null) {
397 context.errorUnresolvedIDREF(bean,id,location);
398 } else {
399 TODO.prototype(); // TODO: check if the type of t is proper.
400 core.addToPack(pack,t);
401 }
402 }
404 core.endPacking(pack,bean,acc);
405 } catch (AccessorException e) {
406 context.handleError(e);
407 }
408 }
409 }
410 }
412 /**
413 * {@link Iterator} for IDREFS lister.
414 *
415 * <p>
416 * Only in ArrayElementProperty we need to get the actual
417 * referenced object. This is a kind of ugly way to make that work.
418 */
419 public static final class IDREFSIterator implements ListIterator<String> {
420 private final ListIterator i;
421 private final XMLSerializer context;
422 private Object last;
424 private IDREFSIterator(ListIterator i, XMLSerializer context) {
425 this.i = i;
426 this.context = context;
427 }
429 public boolean hasNext() {
430 return i.hasNext();
431 }
433 /**
434 * Returns the last referenced object (not just its ID)
435 */
436 public Object last() {
437 return last;
438 }
440 public String next() throws SAXException, JAXBException {
441 last = i.next();
442 String id = context.grammar.getBeanInfo(last,true).getId(last,context);
443 if(id==null) {
444 context.errorMissingId(last);
445 }
446 return id;
447 }
448 }
450 /**
451 * Gets the special {@link Lister} used to recover from an error.
452 */
453 @SuppressWarnings("unchecked")
454 public static <A,B,C,D> Lister<A,B,C,D> getErrorInstance() {
455 return ERROR;
456 }
458 public static final Lister ERROR = new Lister() {
459 public ListIterator iterator(Object o, XMLSerializer context) {
460 return EMPTY_ITERATOR;
461 }
463 public Object startPacking(Object o, Accessor accessor) {
464 return null;
465 }
467 public void addToPack(Object o, Object o1) {
468 }
470 public void endPacking(Object o, Object o1, Accessor accessor) {
471 }
473 public void reset(Object o, Accessor accessor) {
474 }
475 };
477 private static final ListIterator EMPTY_ITERATOR = new ListIterator() {
478 public boolean hasNext() {
479 return false;
480 }
482 public Object next() {
483 throw new IllegalStateException();
484 }
485 };
487 private static final Class[] COLLECTION_IMPL_CLASSES = new Class[] {
488 ArrayList.class,
489 LinkedList.class,
490 HashSet.class,
491 TreeSet.class,
492 Stack.class
493 };
494 }