JenkinsForks
Attachment 'jenkins-xstream.patch'
Download 1 Description: Cumulative patch from Jenkins fork of xstream for application
2 to stock xstream.
3 Author: James Page <james.page@canonical.com>
4 Origin: https://github.com/jenkinsci/xstream/commits/1.3.1-hudson-8
5
6 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/XStream.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/XStream.java
7 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/XStream.java 2008-12-06 15:47:56.000000000 +0000
8 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/XStream.java 2010-10-09 18:20:48.000000000 +0100
9 @@ -202,7 +202,7 @@ import java.util.Vector;
10 * no other registered converter is suitable, can be registered with priority
11 * XStream.PRIORITY_VERY_LOW. XStream uses by default the
12 * {@link com.thoughtworks.xstream.converters.reflection.ReflectionConverter} as the fallback
13 - * converter.
14 + * converter (override createDefaultConverter() to change this).
15 * </p>
16 * <p/>
17 * <p>
18 @@ -612,10 +612,13 @@ public class XStream {
19 addDefaultImplementation(GregorianCalendar.class, Calendar.class);
20 }
21
22 + protected Converter createDefaultConverter() {
23 + return new ReflectionConverter(mapper, reflectionProvider);
24 + }
25 +
26 protected void setupConverters() {
27 - final ReflectionConverter reflectionConverter =
28 - new ReflectionConverter(mapper, reflectionProvider);
29 - registerConverter(reflectionConverter, PRIORITY_VERY_LOW);
30 + final Converter defaultConverter = createDefaultConverter();
31 + registerConverter(defaultConverter, PRIORITY_VERY_LOW);
32
33 registerConverter(new SerializableConverter(mapper, reflectionProvider), PRIORITY_LOW);
34 registerConverter(new ExternalizableConverter(mapper), PRIORITY_LOW);
35 @@ -675,7 +678,7 @@ public class XStream {
36 dynamicallyRegisterConverter(
37 "com.thoughtworks.xstream.converters.extended.ThrowableConverter",
38 PRIORITY_NORMAL, new Class[]{Converter.class},
39 - new Object[]{reflectionConverter});
40 + new Object[]{defaultConverter});
41 dynamicallyRegisterConverter(
42 "com.thoughtworks.xstream.converters.extended.StackTraceElementConverter",
43 PRIORITY_NORMAL, null, null);
44 @@ -685,7 +688,7 @@ public class XStream {
45 dynamicallyRegisterConverter(
46 "com.thoughtworks.xstream.converters.extended.RegexPatternConverter",
47 PRIORITY_NORMAL, new Class[]{Converter.class},
48 - new Object[]{reflectionConverter});
49 + new Object[]{defaultConverter});
50 dynamicallyRegisterConverter(
51 "com.thoughtworks.xstream.converters.extended.CharsetConverter",
52 PRIORITY_NORMAL, null, null);
53 @@ -713,7 +716,7 @@ public class XStream {
54 null, null);
55 }
56
57 - registerConverter(new SelfStreamingInstanceChecker(reflectionConverter, this), PRIORITY_NORMAL);
58 + registerConverter(new SelfStreamingInstanceChecker(defaultConverter, this), PRIORITY_NORMAL);
59 }
60
61 private void dynamicallyRegisterConverter(
62 @@ -1221,6 +1224,14 @@ public class XStream {
63 return converterLookup;
64 }
65
66 + public ConverterRegistry getConverterRegistry() {
67 + return converterRegistry;
68 + }
69 +
70 + public JVM getJvm() {
71 + return jvm;
72 + }
73 +
74 /**
75 * Change mode for dealing with duplicate references. Valid values are
76 * <code>XPATH_ABSOLUTE_REFERENCES</code>, <code>XPATH_RELATIVE_REFERENCES</code>,
77 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/annotations/XStreamSerializable.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/annotations/XStreamSerializable.java
78 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/annotations/XStreamSerializable.java 1970-01-01 01:00:00.000000000 +0100
79 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/annotations/XStreamSerializable.java 2010-10-09 18:20:48.000000000 +0100
80 @@ -0,0 +1,14 @@
81 +package com.thoughtworks.xstream.annotations;
82 +
83 +import static java.lang.annotation.ElementType.FIELD;
84 +import java.lang.annotation.Retention;
85 +import static java.lang.annotation.RetentionPolicy.RUNTIME;
86 +import java.lang.annotation.Target;
87 +
88 +/**
89 + * @author Kohsuke Kawaguchi
90 + */
91 +@Retention(RUNTIME)
92 +@Target(FIELD)
93 +public @interface XStreamSerializable {
94 +}
95 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/annotations/XStreamSerializeAs.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/annotations/XStreamSerializeAs.java
96 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/annotations/XStreamSerializeAs.java 1970-01-01 01:00:00.000000000 +0100
97 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/annotations/XStreamSerializeAs.java 2010-10-09 18:20:48.000000000 +0100
98 @@ -0,0 +1,25 @@
99 +package com.thoughtworks.xstream.annotations;
100 +
101 +import java.lang.annotation.Inherited;
102 +import java.lang.annotation.Retention;
103 +import java.lang.annotation.Target;
104 +
105 +import static java.lang.annotation.ElementType.*;
106 +import static java.lang.annotation.RetentionPolicy.*;
107 +
108 +/**
109 + * Designates that the type and its derived types will serialize
110 + * as the specified type. This is useful if you want an entire
111 + * family of types serialize as the base type with its special converter.
112 + *
113 + * @author Kohsuke Kawaguchi
114 + */
115 +@Retention(RUNTIME)
116 +@Target(TYPE)
117 +@Inherited
118 +public @interface XStreamSerializeAs {
119 + /**
120 + * Use {@code void.class} to cancel out the annotation defined in the super type.
121 + */
122 + Class<?> value();
123 +}
124 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/basic/StringConverter.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/basic/StringConverter.java
125 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/basic/StringConverter.java 2008-12-06 15:47:52.000000000 +0000
126 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/basic/StringConverter.java 2010-10-09 18:20:48.000000000 +0100
127 @@ -11,6 +11,8 @@
128 */
129 package com.thoughtworks.xstream.converters.basic;
130
131 +import com.thoughtworks.xstream.core.util.ConcurrentWeakHashMap;
132 +
133 import java.lang.ref.WeakReference;
134 import java.util.Collections;
135 import java.util.Map;
136 @@ -42,7 +44,7 @@ public class StringConverter extends Abs
137 }
138
139 public StringConverter() {
140 - this(Collections.synchronizedMap(new WeakHashMap()));
141 + this(new ConcurrentWeakHashMap());
142 }
143
144 public boolean canConvert(final Class type) {
145 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/enums/EnumConverter.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/enums/EnumConverter.java
146 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/enums/EnumConverter.java 2008-12-06 15:47:52.000000000 +0000
147 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/enums/EnumConverter.java 2010-10-09 18:20:48.000000000 +0100
148 @@ -44,7 +44,17 @@ public class EnumConverter implements Co
149 if (type.getSuperclass() != Enum.class) {
150 type = type.getSuperclass(); // polymorphic enums
151 }
152 - return Enum.valueOf(type, reader.getValue());
153 + String name = reader.getValue();
154 + try {
155 + return Enum.valueOf(type, name);
156 + } catch (IllegalArgumentException e) {
157 + // failed to find it, do a case insensitive match
158 + for (Enum c : (Enum[])type.getEnumConstants())
159 + if (c.name().equalsIgnoreCase(name))
160 + return c;
161 + // all else failed
162 + throw e;
163 + }
164 }
165
166 }
167 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java
168 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java 2008-12-06 15:47:52.000000000 +0000
169 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/FieldDictionary.java 2010-10-09 18:20:48.000000000 +0100
170 @@ -23,6 +23,7 @@ import java.util.WeakHashMap;
171
172 import com.thoughtworks.xstream.core.JVM;
173 import com.thoughtworks.xstream.core.util.OrderRetainingMap;
174 +import com.thoughtworks.xstream.core.util.ConcurrentWeakHashMap;
175
176
177 /**
178 @@ -34,8 +35,19 @@ import com.thoughtworks.xstream.core.uti
179 */
180 public class FieldDictionary {
181
182 - private transient Map keyedByFieldNameCache;
183 - private transient Map keyedByFieldKeyCache;
184 + static final class Entry {
185 + final Map<String,Field> keyedByFieldName;
186 + final Map<FieldKey,Field> keyedByFieldKey;
187 +
188 + Entry(Map keyedByFieldName, Map keyedByFieldKey) {
189 + this.keyedByFieldName = keyedByFieldName;
190 + this.keyedByFieldKey = keyedByFieldKey;
191 + }
192 + }
193 +
194 + private transient Map<Class,Entry> cache;
195 +// private transient Map keyedByFieldNameCache;
196 +// private transient Map keyedByFieldKeyCache;
197 private final FieldKeySorter sorter;
198
199 public FieldDictionary() {
200 @@ -48,10 +60,12 @@ public class FieldDictionary {
201 }
202
203 private void init() {
204 - keyedByFieldNameCache = new WeakHashMap();
205 - keyedByFieldKeyCache = new WeakHashMap();
206 - keyedByFieldNameCache.put(Object.class, Collections.EMPTY_MAP);
207 - keyedByFieldKeyCache.put(Object.class, Collections.EMPTY_MAP);
208 +// keyedByFieldNameCache = new WeakHashMap();
209 +// keyedByFieldKeyCache = new WeakHashMap();
210 +// keyedByFieldNameCache.put(Object.class, Collections.EMPTY_MAP);
211 +// keyedByFieldKeyCache.put(Object.class, Collections.EMPTY_MAP);
212 + cache = new ConcurrentWeakHashMap<Class,Entry>();
213 + cache.put(Object.class, new Entry(Collections.EMPTY_MAP,Collections.EMPTY_MAP));
214 }
215
216 /**
217 @@ -87,20 +101,28 @@ public class FieldDictionary {
218 * @return the field itself
219 */
220 public Field field(Class cls, String name, Class definedIn) {
221 - Map fields = buildMap(cls, definedIn != null);
222 - Field field = (Field)fields.get(definedIn != null ? (Object)new FieldKey(
223 - name, definedIn, 0) : (Object)name);
224 + Field field = fieldOrNull(cls,name,definedIn);
225 if (field == null) {
226 - throw new ObjectAccessException("No such field " + cls.getName() + "." + name);
227 + throw new NonExistentFieldException("No such field " + cls.getName() + "." + name,name);
228 } else {
229 return field;
230 }
231 }
232
233 + /**
234 + * Works like {@link #field(Class, String, Class)} but returns null instead of throwing exception.
235 + */
236 + public Field fieldOrNull(Class cls, String name, Class definedIn) {
237 + Map fields = buildMap(cls, definedIn != null);
238 + Field field = (Field)fields.get(definedIn != null ? (Object)new FieldKey(
239 + name, definedIn, 0) : (Object)name);
240 + return field;
241 + }
242 +
243 private Map buildMap(final Class type, boolean tupleKeyed) {
244 Class cls = type;
245 - synchronized (this) {
246 - if (!keyedByFieldNameCache.containsKey(type)) {
247 +// synchronized (this) {
248 + if (!cache.containsKey(type)) {
249 final List superClasses = new ArrayList();
250 while (!Object.class.equals(cls)) {
251 superClasses.add(0, cls);
252 @@ -110,7 +132,7 @@ public class FieldDictionary {
253 Map lastKeyedByFieldKey = Collections.EMPTY_MAP;
254 for (final Iterator iter = superClasses.iterator(); iter.hasNext();) {
255 cls = (Class)iter.next();
256 - if (!keyedByFieldNameCache.containsKey(cls)) {
257 + if (!cache.containsKey(cls)) {
258 final Map keyedByFieldName = new HashMap(lastKeyedByFieldName);
259 final Map keyedByFieldKey = new OrderRetainingMap(lastKeyedByFieldKey);
260 Field[] fields = cls.getDeclaredFields();
261 @@ -137,16 +159,20 @@ public class FieldDictionary {
262 }
263 keyedByFieldKey.put(fieldKey, field);
264 }
265 - keyedByFieldNameCache.put(cls, keyedByFieldName);
266 - keyedByFieldKeyCache.put(cls, sorter.sort(type, keyedByFieldKey));
267 + cache.put(cls, new Entry(keyedByFieldName,sorter.sort(type, keyedByFieldKey)));
268 }
269 - lastKeyedByFieldName = (Map)keyedByFieldNameCache.get(cls);
270 - lastKeyedByFieldKey = (Map)keyedByFieldKeyCache.get(cls);
271 + Entry e = cache.get(cls);
272 + lastKeyedByFieldName = e.keyedByFieldName;
273 + lastKeyedByFieldKey = e.keyedByFieldKey;
274 }
275 }
276 - }
277 - return (Map)(tupleKeyed ? keyedByFieldKeyCache.get(type) : keyedByFieldNameCache
278 - .get(type));
279 +// }
280 +
281 + Entry e = cache.get(type);
282 + return tupleKeyed ? e.keyedByFieldKey : e.keyedByFieldName;
283 +
284 +// return (Map)(tupleKeyed ? keyedByFieldKeyCache.get(type) : keyedByFieldNameCache
285 +// .get(type));
286 }
287
288 protected Object readResolve() {
289 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/HarmonyReflectionProvider.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/HarmonyReflectionProvider.java
290 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/HarmonyReflectionProvider.java 2008-12-06 15:47:52.000000000 +0000
291 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/HarmonyReflectionProvider.java 1970-01-01 01:00:00.000000000 +0100
292 @@ -1,131 +0,0 @@
293 -/*
294 - * Copyright (C) 2008 XStream Committers.
295 - * All rights reserved.
296 - *
297 - * The software in this package is published under the terms of the BSD
298 - * style license a copy of which has been included with this distribution in
299 - * the LICENSE.txt file.
300 - *
301 - * Created on 09. January 2008 by Joerg Schaible
302 - */
303 -package com.thoughtworks.xstream.converters.reflection;
304 -
305 -import org.apache.harmony.misc.accessors.ObjectAccessor;
306 -
307 -import java.lang.reflect.Field;
308 -import java.lang.reflect.InvocationTargetException;
309 -import java.lang.reflect.Method;
310 -
311 -
312 -/**
313 - * Instantiates a new object on the Harmony JVM by bypassing the constructor (meaning code in
314 - * the constructor will never be executed and parameters do not have to be known). This is the
315 - * same method used by the internals of standard Java serialization, but relies on internal Harmony
316 - * code.
317 - *
318 - * Note, this is work in progress. Harmony 5.0M4 crashes instantiating a class derived from
319 - * {@link com.thoughtworks.acceptance.objects.StandardObject}, of type
320 - * {@link javax.swing.JTable} or {@link java.awt.Font}. Additionally it fails with a NPE processing the
321 - * annotations and has a wrong offset dealing with time zone. Same problems apply to 5.0M5.
322 - *
323 - * @author Jörg Schaible
324 - * @author Joe Walnes
325 - */
326 -public class HarmonyReflectionProvider extends PureJavaReflectionProvider {
327 - private final static ObjectAccessor objectAccess;
328 - private final static Exception exception;
329 - static {
330 - ObjectAccessor accessor = null;
331 - Exception ex = null;
332 - Method method;
333 - try {
334 - method = ObjectAccessor.class.getDeclaredMethod("getInstance");
335 - method.setAccessible(true);
336 - accessor = (ObjectAccessor)method.invoke(null, null);
337 - } catch (NoSuchMethodException e) {
338 - ex = e;
339 - } catch (IllegalAccessException e) {
340 - ex = e;
341 - } catch (InvocationTargetException e) {
342 - ex = e;
343 - }
344 - objectAccess = accessor;
345 - exception = ex;
346 - }
347 -
348 - public HarmonyReflectionProvider() {
349 - super();
350 - }
351 -
352 - public HarmonyReflectionProvider(FieldDictionary dic) {
353 - super(dic);
354 - }
355 -
356 - public Object newInstance(Class type) {
357 - if (exception != null) {
358 - throw new ObjectAccessException("Cannot construct " + type.getName(), exception);
359 - }
360 - try {
361 - return objectAccess.newInstance(type);
362 - } catch (SecurityException e) {
363 - throw new ObjectAccessException("Cannot construct " + type.getName(), e);
364 - } catch (IllegalArgumentException e) {
365 - throw new ObjectAccessException("Cannot construct " + type.getName(), e);
366 - }
367 - }
368 -
369 - public void writeField(Object object, String fieldName, Object value, Class definedIn) {
370 - write(fieldDictionary.field(object.getClass(), fieldName, definedIn), object, value);
371 - }
372 -
373 - private void write(Field field, Object object, Object value) {
374 - if (exception != null) {
375 - throw new ObjectAccessException("Could not set field "
376 - + object.getClass()
377 - + "."
378 - + field.getName(), exception);
379 - }
380 - try {
381 - long offset = objectAccess.getFieldID(field);
382 - Class type = field.getType();
383 - if (type.isPrimitive()) {
384 - if (type.equals(Integer.TYPE)) {
385 - objectAccess.setInt(object, offset, ((Integer)value).intValue());
386 - } else if (type.equals(Long.TYPE)) {
387 - objectAccess.setLong(object, offset, ((Long)value).longValue());
388 - } else if (type.equals(Short.TYPE)) {
389 - objectAccess.setShort(object, offset, ((Short)value).shortValue());
390 - } else if (type.equals(Character.TYPE)) {
391 - objectAccess.setChar(object, offset, ((Character)value).charValue());
392 - } else if (type.equals(Byte.TYPE)) {
393 - objectAccess.setByte(object, offset, ((Byte)value).byteValue());
394 - } else if (type.equals(Float.TYPE)) {
395 - objectAccess.setFloat(object, offset, ((Float)value).floatValue());
396 - } else if (type.equals(Double.TYPE)) {
397 - objectAccess.setDouble(object, offset, ((Double)value).doubleValue());
398 - } else if (type.equals(Boolean.TYPE)) {
399 - objectAccess.setBoolean(object, offset, ((Boolean)value).booleanValue());
400 - } else {
401 - throw new ObjectAccessException("Could not set field "
402 - + object.getClass()
403 - + "."
404 - + field.getName()
405 - + ": Unknown type "
406 - + type);
407 - }
408 - } else {
409 - objectAccess.setObject(object, offset, value);
410 - }
411 -
412 - } catch (IllegalArgumentException e) {
413 - throw new ObjectAccessException("Could not set field "
414 - + object.getClass()
415 - + "."
416 - + field.getName(), e);
417 - }
418 - }
419 -
420 - protected void validateFieldAccess(Field field) {
421 - // (overriden) don't mind final fields.
422 - }
423 -}
424 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/NonExistentFieldException.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/NonExistentFieldException.java
425 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/NonExistentFieldException.java 1970-01-01 01:00:00.000000000 +0100
426 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/NonExistentFieldException.java 2010-10-09 18:20:48.000000000 +0100
427 @@ -0,0 +1,24 @@
428 +package com.thoughtworks.xstream.converters.reflection;
429 +
430 +/**
431 + * Indicates that field/property didn't exist in classes (as opposed to what's implied in XML.)
432 + *
433 + * @author Kohsuke Kawaguchi
434 + */
435 +public class NonExistentFieldException extends ObjectAccessException {
436 + private final String fieldName;
437 +
438 + public NonExistentFieldException(String message, String fieldName) {
439 + super(message);
440 + this.fieldName = fieldName;
441 + }
442 +
443 + public NonExistentFieldException(String message, Throwable cause, String fieldName) {
444 + super(message, cause);
445 + this.fieldName = fieldName;
446 + }
447 +
448 + public String getFieldName() {
449 + return fieldName;
450 + }
451 +}
452 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java
453 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java 2008-12-06 15:47:52.000000000 +0000
454 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/PureJavaReflectionProvider.java 2010-10-09 18:20:48.000000000 +0100
455 @@ -12,6 +12,7 @@
456 package com.thoughtworks.xstream.converters.reflection;
457
458 import com.thoughtworks.xstream.core.JVM;
459 +import com.thoughtworks.xstream.annotations.XStreamSerializable;
460
461 import java.io.ByteArrayInputStream;
462 import java.io.ByteArrayOutputStream;
463 @@ -152,17 +153,14 @@ public class PureJavaReflectionProvider
464 }
465
466 public boolean fieldDefinedInClass(String fieldName, Class type) {
467 - try {
468 - Field field = fieldDictionary.field(type, fieldName, null);
469 - return fieldModifiersSupported(field) || Modifier.isTransient(field.getModifiers());
470 - } catch (ObjectAccessException e) {
471 - return false;
472 - }
473 + Field field = fieldDictionary.fieldOrNull(type, fieldName, null);
474 + if(field==null) return false;
475 + return fieldModifiersSupported(field) || Modifier.isTransient(field.getModifiers());
476 }
477
478 protected boolean fieldModifiersSupported(Field field) {
479 return !(Modifier.isStatic(field.getModifiers())
480 - || Modifier.isTransient(field.getModifiers()));
481 + || (Modifier.isTransient(field.getModifiers()) && field.getAnnotation(XStreamSerializable.class)==null));
482 }
483
484 protected void validateFieldAccess(Field field) {
485 @@ -180,6 +178,10 @@ public class PureJavaReflectionProvider
486 return fieldDictionary.field(definedIn, fieldName, null);
487 }
488
489 + public Field getFieldOrNull(Class definedIn, String fieldName) {
490 + return fieldDictionary.fieldOrNull(definedIn, fieldName, null);
491 + }
492 +
493 public void setFieldDictionary(FieldDictionary dictionary) {
494 this.fieldDictionary = dictionary;
495 }
496 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProvider.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProvider.java
497 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProvider.java 2008-12-06 15:47:52.000000000 +0000
498 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProvider.java 2010-10-09 18:20:48.000000000 +0100
499 @@ -60,4 +60,10 @@ public interface ReflectionProvider {
500 */
501 Field getField(Class definedIn, String fieldName);
502
503 + /**
504 + * Works like {@link #getField(Class, String)} but returns null
505 + * instead of throwing {@link ObjectAccessException}.
506 + */
507 + Field getFieldOrNull(Class definedIn, String fieldName);
508 +
509 }
510 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProviderWrapper.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProviderWrapper.java
511 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProviderWrapper.java 2008-12-06 15:47:52.000000000 +0000
512 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/ReflectionProviderWrapper.java 2010-10-09 18:20:48.000000000 +0100
513 @@ -50,4 +50,7 @@ public class ReflectionProviderWrapper i
514 this.wrapped.writeField(object, fieldName, value, definedIn);
515 }
516
517 + public Field getFieldOrNull(Class definedIn, String fieldName) {
518 + return this.wrapped.getFieldOrNull(definedIn,fieldName);
519 + }
520 }
521 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java
522 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java 2008-12-06 15:47:52.000000000 +0000
523 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java 2010-10-09 18:20:48.000000000 +0100
524 @@ -320,8 +320,8 @@ public class SerializableConverter exten
525 } else {
526 ObjectStreamField field = objectStreamClass.getField(name);
527 if (field == null) {
528 - throw new ObjectAccessException("Class " + currentType[0]
529 - + " does not contain a field named '" + name + "'");
530 + throw new NonExistentFieldException("Class " + currentType[0]
531 + + " does not contain a field named '" + name + "'",name);
532 }
533 type = field.getType();
534 }
535 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializationMethodInvoker.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializationMethodInvoker.java
536 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializationMethodInvoker.java 2008-12-06 15:47:52.000000000 +0000
537 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializationMethodInvoker.java 2010-10-09 18:20:48.000000000 +0100
538 @@ -20,6 +20,7 @@ import java.lang.reflect.Method;
539 import java.util.Collections;
540 import java.util.HashMap;
541 import java.util.Map;
542 +import java.util.concurrent.ConcurrentHashMap;
543
544 /**
545 * Convenience wrapper to invoke special serialization methods on objects (and perform reflection caching).
546 @@ -28,7 +29,7 @@ import java.util.Map;
547 */
548 public class SerializationMethodInvoker {
549
550 - private Map cache = Collections.synchronizedMap(new HashMap());
551 + private Map cache = new ConcurrentHashMap();
552 private static final Object NO_METHOD = new Object();
553 private static final Object[] EMPTY_ARGS = new Object[0];
554
555 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/Sun14ReflectionProvider.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/Sun14ReflectionProvider.java
556 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/converters/reflection/Sun14ReflectionProvider.java 2008-12-06 15:47:52.000000000 +0000
557 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/converters/reflection/Sun14ReflectionProvider.java 2010-10-09 18:20:48.000000000 +0100
558 @@ -22,6 +22,8 @@ import java.util.Collections;
559 import java.util.Map;
560 import java.util.WeakHashMap;
561
562 +import com.thoughtworks.xstream.core.util.ConcurrentWeakHashMap;
563 +
564 /**
565 * Instantiates a new object on the Sun JVM by bypassing the constructor (meaning code in the constructor
566 * will never be executed and parameters do not have to be known). This is the same method used by the internals of
567 @@ -58,7 +60,7 @@ public class Sun14ReflectionProvider ext
568 }
569
570 private transient ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
571 - private transient Map constructorCache = Collections.synchronizedMap(new WeakHashMap());
572 + private transient Map constructorCache = new ConcurrentWeakHashMap();
573
574 public Sun14ReflectionProvider() {
575 super();
576 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java
577 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java 2008-12-06 15:47:52.000000000 +0000
578 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/core/DefaultConverterLookup.java 2010-10-09 18:20:48.000000000 +0100
579 @@ -16,6 +16,7 @@ import com.thoughtworks.xstream.converte
580 import com.thoughtworks.xstream.converters.Converter;
581 import com.thoughtworks.xstream.converters.ConverterLookup;
582 import com.thoughtworks.xstream.converters.ConverterRegistry;
583 +import com.thoughtworks.xstream.converters.basic.NullConverter;
584 import com.thoughtworks.xstream.core.util.PrioritizedList;
585 import com.thoughtworks.xstream.mapper.Mapper;
586
587 @@ -23,6 +24,7 @@ import java.util.Collections;
588 import java.util.HashMap;
589 import java.util.Iterator;
590 import java.util.Map;
591 +import java.util.concurrent.ConcurrentHashMap;
592
593 /**
594 * The default implementation of converters lookup.
595 @@ -34,7 +36,7 @@ import java.util.Map;
596 public class DefaultConverterLookup implements ConverterLookup, ConverterRegistry {
597
598 private final PrioritizedList converters = new PrioritizedList();
599 - private transient Map typeToConverterMap = Collections.synchronizedMap(new HashMap());
600 + private transient Map typeToConverterMap = new ConcurrentHashMap();
601
602 public DefaultConverterLookup() {
603 }
604 @@ -52,6 +54,7 @@ public class DefaultConverterLookup impl
605 }
606
607 public Converter lookupConverterForType(Class type) {
608 + if(type==null) return NULL;
609 Converter cachedConverter = (Converter) typeToConverterMap.get(type);
610 if (cachedConverter != null) return cachedConverter;
611 Iterator iterator = converters.iterator();
612 @@ -76,8 +79,9 @@ public class DefaultConverterLookup impl
613 }
614
615 private Object readResolve() {
616 - typeToConverterMap = Collections.synchronizedMap(new HashMap());
617 + typeToConverterMap = new ConcurrentHashMap();
618 return this;
619 }
620
621 + private static final NullConverter NULL = new NullConverter();
622 }
623 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/core/JVM.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/core/JVM.java
624 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/core/JVM.java 2008-12-06 15:47:52.000000000 +0000
625 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/core/JVM.java 2010-10-09 18:20:48.000000000 +0100
626 @@ -130,6 +130,10 @@ public class JVM {
627 return false;
628 }
629
630 + private static boolean isOracle() {
631 + return vendor.indexOf("Oracle") != -1;
632 + }
633 +
634 private static boolean isHitachi() {
635 return vendor.indexOf("Hitachi") != -1;
636 }
637 @@ -188,6 +192,7 @@ public class JVM {
638 || isIBM()
639 || isBlackdown()
640 || isBEAWithUnsafeSupport()
641 + || isOracle()
642 || isHitachi()
643 || isSAP()
644 || isDiablo())
645 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/core/util/ConcurrentWeakHashMap.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/core/util/ConcurrentWeakHashMap.java
646 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/core/util/ConcurrentWeakHashMap.java 1970-01-01 01:00:00.000000000 +0100
647 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/core/util/ConcurrentWeakHashMap.java 2010-10-09 18:20:48.000000000 +0100
648 @@ -0,0 +1,1450 @@
649 +/*
650 + * Written by Doug Lea with assistance from members of JCP JSR-166
651 + * Expert Group and released to the public domain, as explained at
652 + * http://creativecommons.org/licenses/publicdomain
653 + */
654 +package com.thoughtworks.xstream.core.util;
655 +
656 +import java.io.IOException;
657 +import java.io.Serializable;
658 +import java.lang.ref.ReferenceQueue;
659 +import java.lang.ref.WeakReference;
660 +import java.util.AbstractCollection;
661 +import java.util.AbstractMap;
662 +import java.util.AbstractSet;
663 +import java.util.Collection;
664 +import java.util.ConcurrentModificationException;
665 +import java.util.Enumeration;
666 +import java.util.HashMap;
667 +import java.util.Hashtable;
668 +import java.util.Iterator;
669 +import java.util.Map;
670 +import java.util.NoSuchElementException;
671 +import java.util.Set;
672 +import java.util.concurrent.locks.ReentrantLock;
673 +
674 +/**
675 + * A hash table with <em>weak keys</em>, full concurrency of retrievals, and
676 + * adjustable expected concurrency for updates. Similar to
677 + * {@link java.util.WeakHashMap}, entries of this table are periodically
678 + * removed once their corresponding keys are no longer referenced outside of
679 + * this table. In other words, this table will not prevent a key from being
680 + * discarded by the garbage collector. Once a key has been discarded by the
681 + * collector, the corresponding entry is no longer visible to this table;
682 + * however, the entry may occupy space until a future table operation decides to
683 + * reclaim it. For this reason, summary functions such as <tt>size</tt> and
684 + * <tt>isEmpty</tt> might return a value greater than the observed number of
685 + * entries. In order to support a high level of concurrency, stale entries are
686 + * only reclaimed during blocking (usually mutating) operations.
687 + *
688 + * While keys in this table are only held using a weak reference, values are
689 + * held using a normal strong reference. This provides the guarantee that a
690 + * value will always have at least the same life-span as it's key. For this
691 + * reason, care should be taken to ensure that a value never refers, either
692 + * directly or indirectly, to its key, thereby preventing reclamation. If weak
693 + * values are desired, one can simply use a {@link WeakReference} for the value
694 + * type.
695 + *
696 + * Just like {@link java.util.ConcurrentHashMap}, this class obeys the same
697 + * functional specification as {@link java.util.Hashtable}, and includes
698 + * versions of methods corresponding to each method of <tt>Hashtable</tt>.
699 + * However, even though all operations are thread-safe, retrieval operations do
700 + * <em>not</em> entail locking, and there is <em>not</em> any support for
701 + * locking the entire table in a way that prevents all access. This class is
702 + * fully interoperable with <tt>Hashtable</tt> in programs that rely on its
703 + * thread safety but not on its synchronization details.
704 + *
705 + * <p>
706 + * Retrieval operations (including <tt>get</tt>) generally do not block, so
707 + * may overlap with update operations (including <tt>put</tt> and
708 + * <tt>remove</tt>). Retrievals reflect the results of the most recently
709 + * <em>completed</em> update operations holding upon their onset. For
710 + * aggregate operations such as <tt>putAll</tt> and <tt>clear</tt>,
711 + * concurrent retrievals may reflect insertion or removal of only some entries.
712 + * Similarly, Iterators and Enumerations return elements reflecting the state of
713 + * the hash table at some point at or since the creation of the
714 + * iterator/enumeration. They do <em>not</em> throw
715 + * {@link ConcurrentModificationException}. However, iterators are designed to
716 + * be used by only one thread at a time.
717 + *
718 + * <p>
719 + * The allowed concurrency among update operations is guided by the optional
720 + * <tt>concurrencyLevel</tt> constructor argument (default <tt>16</tt>),
721 + * which is used as a hint for internal sizing. The table is internally
722 + * partitioned to try to permit the indicated number of concurrent updates
723 + * without contention. Because placement in hash tables is essentially random,
724 + * the actual concurrency will vary. Ideally, you should choose a value to
725 + * accommodate as many threads as will ever concurrently modify the table. Using
726 + * a significantly higher value than you need can waste space and time, and a
727 + * significantly lower value can lead to thread contention. But overestimates
728 + * and underestimates within an order of magnitude do not usually have much
729 + * noticeable impact. A value of one is appropriate when it is known that only
730 + * one thread will modify and all others will only read. Also, resizing this or
731 + * any other kind of hash table is a relatively slow operation, so, when
732 + * possible, it is a good idea to provide estimates of expected table sizes in
733 + * constructors.
734 + *
735 + * <p>
736 + * This class and its views and iterators implement all of the <em>optional</em>
737 + * methods of the {@link Map} and {@link Iterator} interfaces.
738 + *
739 + * <p>
740 + * Like {@link Hashtable} but unlike {@link HashMap}, this class does
741 + * <em>not</em> allow <tt>null</tt> to be used as a key or value.
742 + *
743 + * <p>
744 + * This class is a member of the <a href="{@docRoot}/../technotes/guides/collections/index.html">
745 + * Java Collections Framework</a>.
746 + *
747 + * @author Doug Lea
748 + * @author Jason T. Greene
749 + * @param <K> the type of keys maintained by this map
750 + * @param <V> the type of mapped values
751 + */
752 +public class ConcurrentWeakHashMap<K, V> extends AbstractMap<K, V>
753 + implements java.util.concurrent.ConcurrentMap<K, V>, Serializable {
754 + private static final long serialVersionUID = 7249069246763182397L;
755 +
756 + /*
757 + * The basic strategy is to subdivide the table among Segments,
758 + * each of which itself is a concurrently readable hash table.
759 + */
760 +
761 + /* ---------------- Constants -------------- */
762 +
763 + /**
764 + * The default initial capacity for this table,
765 + * used when not otherwise specified in a constructor.
766 + */
767 + static final int DEFAULT_INITIAL_CAPACITY = 16;
768 +
769 + /**
770 + * The default load factor for this table, used when not
771 + * otherwise specified in a constructor.
772 + */
773 + static final float DEFAULT_LOAD_FACTOR = 0.75f;
774 +
775 + /**
776 + * The default concurrency level for this table, used when not
777 + * otherwise specified in a constructor.
778 + */
779 + static final int DEFAULT_CONCURRENCY_LEVEL = 16;
780 +
781 + /**
782 + * The maximum capacity, used if a higher value is implicitly
783 + * specified by either of the constructors with arguments. MUST
784 + * be a power of two <= 1<<30 to ensure that entries are indexable
785 + * using ints.
786 + */
787 + static final int MAXIMUM_CAPACITY = 1 << 30;
788 +
789 + /**
790 + * The maximum number of segments to allow; used to bound
791 + * constructor arguments.
792 + */
793 + static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
794 +
795 + /**
796 + * Number of unsynchronized retries in size and containsValue
797 + * methods before resorting to locking. This is used to avoid
798 + * unbounded retries if tables undergo continuous modification
799 + * which would make it impossible to obtain an accurate result.
800 + */
801 + static final int RETRIES_BEFORE_LOCK = 2;
802 +
803 + /* ---------------- Fields -------------- */
804 +
805 + /**
806 + * Mask value for indexing into segments. The upper bits of a
807 + * key's hash code are used to choose the segment.
808 + */
809 + final int segmentMask;
810 +
811 + /**
812 + * Shift value for indexing within segments.
813 + */
814 + final int segmentShift;
815 +
816 + /**
817 + * The segments, each of which is a specialized hash table
818 + */
819 + final Segment<K,V>[] segments;
820 +
821 + transient Set<K> keySet;
822 + transient Set<Map.Entry<K,V>> entrySet;
823 + transient Collection<V> values;
824 +
825 + /* ---------------- Small Utilities -------------- */
826 +
827 + /**
828 + * Applies a supplemental hash function to a given hashCode, which
829 + * defends against poor quality hash functions. This is critical
830 + * because ConcurrentWeakHashMap uses power-of-two length hash tables,
831 + * that otherwise encounter collisions for hashCodes that do not
832 + * differ in lower or upper bits.
833 + */
834 + private static int hash(int h) {
835 + // Spread bits to regularize both segment and index locations,
836 + // using variant of single-word Wang/Jenkins hash.
837 + h += (h << 15) ^ 0xffffcd7d;
838 + h ^= (h >>> 10);
839 + h += (h << 3);
840 + h ^= (h >>> 6);
841 + h += (h << 2) + (h << 14);
842 + return h ^ (h >>> 16);
843 + }
844 +
845 + /**
846 + * Returns the segment that should be used for key with given hash
847 + * @param hash the hash code for the key
848 + * @return the segment
849 + */
850 + final Segment<K,V> segmentFor(int hash) {
851 + return segments[(hash >>> segmentShift) & segmentMask];
852 + }
853 +
854 + /* ---------------- Inner Classes -------------- */
855 +
856 + /**
857 + * A weak-key reference which stores the key hash needed for reclamation.
858 + */
859 + static final class WeakKeyReference<K> extends WeakReference<K> {
860 + final int hash;
861 + WeakKeyReference(K key, int hash, ReferenceQueue<K> refQueue) {
862 + super(key, refQueue);
863 + this.hash = hash;
864 + }
865 + }
866 +
867 + /**
868 + * ConcurrentWeakHashMap list entry. Note that this is never exported
869 + * out as a user-visible Map.Entry.
870 + *
871 + * Because the value field is volatile, not final, it is legal wrt
872 + * the Java Memory Model for an unsynchronized reader to see null
873 + * instead of initial value when read via a data race. Although a
874 + * reordering leading to this is not likely to ever actually
875 + * occur, the Segment.readValueUnderLock method is used as a
876 + * backup in case a null (pre-initialized) value is ever seen in
877 + * an unsynchronized access method.
878 + */
879 + static final class HashEntry<K,V> {
880 + final WeakReference<K> keyRef;
881 + final int hash;
882 + volatile V value;
883 + final HashEntry<K,V> next;
884 +
885 + HashEntry(K key, int hash, HashEntry<K,V> next, V value, ReferenceQueue<K> refQueue) {
886 + this.keyRef = new WeakKeyReference<K>(key, hash, refQueue);
887 + this.hash = hash;
888 + this.next = next;
889 + this.value = value;
890 + }
891 +
892 + @SuppressWarnings("unchecked")
893 + static final <K,V> HashEntry<K,V>[] newArray(int i) {
894 + return new HashEntry[i];
895 + }
896 + }
897 +
898 + /**
899 + * Segments are specialized versions of hash tables. This
900 + * subclasses from ReentrantLock opportunistically, just to
901 + * simplify some locking and avoid separate construction.
902 + */
903 + static final class Segment<K,V> extends ReentrantLock implements Serializable {
904 + /*
905 + * Segments maintain a table of entry lists that are ALWAYS
906 + * kept in a consistent state, so can be read without locking.
907 + * Next fields of nodes are immutable (final). All list
908 + * additions are performed at the front of each bin. This
909 + * makes it easy to check changes, and also fast to traverse.
910 + * When nodes would otherwise be changed, new nodes are
911 + * created to replace them. This works well for hash tables
912 + * since the bin lists tend to be short. (The average length
913 + * is less than two for the default load factor threshold.)
914 + *
915 + * Read operations can thus proceed without locking, but rely
916 + * on selected uses of volatiles to ensure that completed
917 + * write operations performed by other threads are
918 + * noticed. For most purposes, the "count" field, tracking the
919 + * number of elements, serves as that volatile variable
920 + * ensuring visibility. This is convenient because this field
921 + * needs to be read in many read operations anyway:
922 + *
923 + * - All (unsynchronized) read operations must first read the
924 + * "count" field, and should not look at table entries if
925 + * it is 0.
926 + *
927 + * - All (synchronized) write operations should write to
928 + * the "count" field after structurally changing any bin.
929 + * The operations must not take any action that could even
930 + * momentarily cause a concurrent read operation to see
931 + * inconsistent data. This is made easier by the nature of
932 + * the read operations in Map. For example, no operation
933 + * can reveal that the table has grown but the threshold
934 + * has not yet been updated, so there are no atomicity
935 + * requirements for this with respect to reads.
936 + *
937 + * As a guide, all critical volatile reads and writes to the
938 + * count field are marked in code comments.
939 + */
940 +
941 + private static final long serialVersionUID = 2249069246763182397L;
942 +
943 + /**
944 + * The number of elements in this segment's region.
945 + */
946 + transient volatile int count;
947 +
948 + /**
949 + * Number of updates that alter the size of the table. This is
950 + * used during bulk-read methods to make sure they see a
951 + * consistent snapshot: If modCounts change during a traversal
952 + * of segments computing size or checking containsValue, then
953 + * we might have an inconsistent view of state so (usually)
954 + * must retry.
955 + */
956 + transient int modCount;
957 +
958 + /**
959 + * The table is rehashed when its size exceeds this threshold.
960 + * (The value of this field is always <tt>(int)(capacity *
961 + * loadFactor)</tt>.)
962 + */
963 + transient int threshold;
964 +
965 + /**
966 + * The per-segment table.
967 + */
968 + transient volatile HashEntry<K,V>[] table;
969 +
970 + /**
971 + * The load factor for the hash table. Even though this value
972 + * is same for all segments, it is replicated to avoid needing
973 + * links to outer object.
974 + * @serial
975 + */
976 + final float loadFactor;
977 +
978 + /**
979 + * The collected weak-key reference queue for this segment.
980 + * This should be (re)initialized whenever table is assigned,
981 + */
982 + transient volatile ReferenceQueue<K> refQueue;
983 +
984 + Segment(int initialCapacity, float lf) {
985 + loadFactor = lf;
986 + setTable(HashEntry.<K,V>newArray(initialCapacity));
987 + }
988 +
989 + @SuppressWarnings("unchecked")
990 + static final <K,V> Segment<K,V>[] newArray(int i) {
991 + return new Segment[i];
992 + }
993 +
994 + /**
995 + * Sets table to new HashEntry array.
996 + * Call only while holding lock or in constructor.
997 + */
998 + void setTable(HashEntry<K,V>[] newTable) {
999 + threshold = (int)(newTable.length * loadFactor);
1000 + table = newTable;
1001 + refQueue = new ReferenceQueue<K>();
1002 + }
1003 +
1004 + /**
1005 + * Returns properly casted first entry of bin for given hash.
1006 + */
1007 + HashEntry<K,V> getFirst(int hash) {
1008 + HashEntry<K,V>[] tab = table;
1009 + return tab[hash & (tab.length - 1)];
1010 + }
1011 +
1012 + /**
1013 + * Reads value field of an entry under lock. Called if value
1014 + * field ever appears to be null. This is possible only if a
1015 + * compiler happens to reorder a HashEntry initialization with
1016 + * its table assignment, which is legal under memory model
1017 + * but is not known to ever occur.
1018 + */
1019 + V readValueUnderLock(HashEntry<K,V> e) {
1020 + lock();
1021 + try {
1022 + removeStale();
1023 + return e.value;
1024 + } finally {
1025 + unlock();
1026 + }
1027 + }
1028 +
1029 + /* Specialized implementations of map methods */
1030 +
1031 + V get(Object key, int hash) {
1032 + if (count != 0) { // read-volatile
1033 + HashEntry<K,V> e = getFirst(hash);
1034 + while (e != null) {
1035 + if (e.hash == hash && key.equals(e.keyRef.get())) {
1036 + V v = e.value;
1037 + if (v != null)
1038 + return v;
1039 + return readValueUnderLock(e); // recheck
1040 + }
1041 + e = e.next;
1042 + }
1043 + }
1044 + return null;
1045 + }
1046 +
1047 + boolean containsKey(Object key, int hash) {
1048 + if (count != 0) { // read-volatile
1049 + HashEntry<K,V> e = getFirst(hash);
1050 + while (e != null) {
1051 + if (e.hash == hash && key.equals(e.keyRef.get()))
1052 + return true;
1053 + e = e.next;
1054 + }
1055 + }
1056 + return false;
1057 + }
1058 +
1059 + boolean containsValue(Object value) {
1060 + if (count != 0) { // read-volatile
1061 + HashEntry<K,V>[] tab = table;
1062 + int len = tab.length;
1063 + for (int i = 0 ; i < len; i++) {
1064 + for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
1065 + V v = e.value;
1066 + if (v == null) // recheck
1067 + v = readValueUnderLock(e);
1068 + if (value.equals(v))
1069 + return true;
1070 + }
1071 + }
1072 + }
1073 + return false;
1074 + }
1075 +
1076 + boolean replace(K key, int hash, V oldValue, V newValue) {
1077 + lock();
1078 + try {
1079 + removeStale();
1080 + HashEntry<K,V> e = getFirst(hash);
1081 + while (e != null && (e.hash != hash || !key.equals(e.keyRef.get())))
1082 + e = e.next;
1083 +
1084 + boolean replaced = false;
1085 + if (e != null && oldValue.equals(e.value)) {
1086 + replaced = true;
1087 + e.value = newValue;
1088 + }
1089 + return replaced;
1090 + } finally {
1091 + unlock();
1092 + }
1093 + }
1094 +
1095 + V replace(K key, int hash, V newValue) {
1096 + lock();
1097 + try {
1098 + removeStale();
1099 + HashEntry<K,V> e = getFirst(hash);
1100 + while (e != null && (e.hash != hash || !key.equals(e.keyRef.get())))
1101 + e = e.next;
1102 +
1103 + V oldValue = null;
1104 + if (e != null) {
1105 + oldValue = e.value;
1106 + e.value = newValue;
1107 + }
1108 + return oldValue;
1109 + } finally {
1110 + unlock();
1111 + }
1112 + }
1113 +
1114 +
1115 + V put(K key, int hash, V value, boolean onlyIfAbsent) {
1116 + lock();
1117 + try {
1118 + removeStale();
1119 + int c = count;
1120 + if (c++ > threshold) {// ensure capacity
1121 + int reduced = rehash();
1122 + if (reduced > 0) // adjust from possible weak cleanups
1123 + count = (c -= reduced) - 1; // write-volatile
1124 + }
1125 +
1126 + HashEntry<K,V>[] tab = table;
1127 + int index = hash & (tab.length - 1);
1128 + HashEntry<K,V> first = tab[index];
1129 + HashEntry<K,V> e = first;
1130 + while (e != null && (e.hash != hash || !key.equals(e.keyRef.get())))
1131 + e = e.next;
1132 +
1133 + V oldValue;
1134 + if (e != null) {
1135 + oldValue = e.value;
1136 + if (!onlyIfAbsent)
1137 + e.value = value;
1138 + }
1139 + else {
1140 + oldValue = null;
1141 + ++modCount;
1142 + tab[index] = new HashEntry<K,V>(key, hash, first, value, refQueue);
1143 + count = c; // write-volatile
1144 + }
1145 + return oldValue;
1146 + } finally {
1147 + unlock();
1148 + }
1149 + }
1150 +
1151 + int rehash() {
1152 + HashEntry<K,V>[] oldTable = table;
1153 + int oldCapacity = oldTable.length;
1154 + if (oldCapacity >= MAXIMUM_CAPACITY)
1155 + return 0;
1156 +
1157 + /*
1158 + * Reclassify nodes in each list to new Map. Because we are
1159 + * using power-of-two expansion, the elements from each bin
1160 + * must either stay at same index, or move with a power of two
1161 + * offset. We eliminate unnecessary node creation by catching
1162 + * cases where old nodes can be reused because their next
1163 + * fields won't change. Statistically, at the default
1164 + * threshold, only about one-sixth of them need cloning when
1165 + * a table doubles. The nodes they replace will be garbage
1166 + * collectable as soon as they are no longer referenced by any
1167 + * reader thread that may be in the midst of traversing table
1168 + * right now.
1169 + */
1170 +
1171 + HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1);
1172 + threshold = (int)(newTable.length * loadFactor);
1173 + int sizeMask = newTable.length - 1;
1174 + int reduce = 0;
1175 + for (int i = 0; i < oldCapacity ; i++) {
1176 + // We need to guarantee that any existing reads of old Map can
1177 + // proceed. So we cannot yet null out each bin.
1178 + HashEntry<K,V> e = oldTable[i];
1179 +
1180 + if (e != null) {
1181 + HashEntry<K,V> next = e.next;
1182 + int idx = e.hash & sizeMask;
1183 +
1184 + // Single node on list
1185 + if (next == null)
1186 + newTable[idx] = e;
1187 +
1188 + else {
1189 + // Reuse trailing consecutive sequence at same slot
1190 + HashEntry<K,V> lastRun = e;
1191 + int lastIdx = idx;
1192 + for (HashEntry<K,V> last = next;
1193 + last != null;
1194 + last = last.next) {
1195 + int k = last.hash & sizeMask;
1196 + if (k != lastIdx) {
1197 + lastIdx = k;
1198 + lastRun = last;
1199 + }
1200 + }
1201 + newTable[lastIdx] = lastRun;
1202 + // Clone all remaining nodes
1203 + for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
1204 + // Skip GC'd weak refs
1205 + K key = p.keyRef.get();
1206 + if (key == null) {
1207 + reduce++;
1208 + continue;
1209 + }
1210 + int k = p.hash & sizeMask;
1211 + HashEntry<K,V> n = newTable[k];
1212 + newTable[k] = new HashEntry<K,V>(key, p.hash, n, p.value, refQueue);
1213 + }
1214 + }
1215 + }
1216 + }
1217 + table = newTable;
1218 + return reduce;
1219 + }
1220 +
1221 + /**
1222 + * Remove; match on key only if value null, else match both.
1223 + */
1224 + V remove(Object key, int hash, Object value, boolean weakRemove) {
1225 + lock();
1226 + try {
1227 + if (!weakRemove)
1228 + removeStale();
1229 + int c = count - 1;
1230 + HashEntry<K,V>[] tab = table;
1231 + int index = hash & (tab.length - 1);
1232 + HashEntry<K,V> first = tab[index];
1233 + HashEntry<K,V> e = first;
1234 + // a weak remove operation compares the WeakReference instance
1235 + while (e != null && (!weakRemove || key != e.keyRef)
1236 + && (e.hash != hash || !key.equals(e.keyRef.get())))
1237 + e = e.next;
1238 +
1239 + V oldValue = null;
1240 + if (e != null) {
1241 + V v = e.value;
1242 + if (value == null || value.equals(v)) {
1243 + oldValue = v;
1244 + // All entries following removed node can stay
1245 + // in list, but all preceding ones need to be
1246 + // cloned.
1247 + ++modCount;
1248 + HashEntry<K,V> newFirst = e.next;
1249 + for (HashEntry<K,V> p = first; p != e; p = p.next) {
1250 + K pKey = p.keyRef.get();
1251 + if (pKey == null) { // Skip GC'd keys
1252 + c--;
1253 + continue;
1254 + }
1255 +
1256 + newFirst = new HashEntry<K,V>(pKey, p.hash,
1257 + newFirst, p.value, refQueue);
1258 + }
1259 + tab[index] = newFirst;
1260 + count = c; // write-volatile
1261 + }
1262 + }
1263 + return oldValue;
1264 + } finally {
1265 + unlock();
1266 + }
1267 + }
1268 +
1269 + @SuppressWarnings("unchecked")
1270 + void removeStale() {
1271 + WeakKeyReference<K> ref;
1272 + while ((ref = (WeakKeyReference<K>) refQueue.poll()) != null) {
1273 + remove(ref, ref.hash, null, true);
1274 + }
1275 + }
1276 +
1277 + void clear() {
1278 + if (count != 0) {
1279 + lock();
1280 + try {
1281 + HashEntry<K,V>[] tab = table;
1282 + for (int i = 0; i < tab.length ; i++)
1283 + tab[i] = null;
1284 + ++modCount;
1285 + // replace the reference queue to avoid unnecessary stale cleanups
1286 + refQueue = new ReferenceQueue<K>();
1287 + count = 0; // write-volatile
1288 + } finally {
1289 + unlock();
1290 + }
1291 + }
1292 + }
1293 + }
1294 +
1295 +
1296 +
1297 + /* ---------------- Public operations -------------- */
1298 +
1299 + /**
1300 + * Creates a new, empty map with the specified initial
1301 + * capacity, load factor and concurrency level.
1302 + *
1303 + * @param initialCapacity the initial capacity. The implementation
1304 + * performs internal sizing to accommodate this many elements.
1305 + * @param loadFactor the load factor threshold, used to control resizing.
1306 + * Resizing may be performed when the average number of elements per
1307 + * bin exceeds this threshold.
1308 + * @param concurrencyLevel the estimated number of concurrently
1309 + * updating threads. The implementation performs internal sizing
1310 + * to try to accommodate this many threads.
1311 + * @throws IllegalArgumentException if the initial capacity is
1312 + * negative or the load factor or concurrencyLevel are
1313 + * nonpositive.
1314 + */
1315 + public ConcurrentWeakHashMap(int initialCapacity,
1316 + float loadFactor, int concurrencyLevel) {
1317 + if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
1318 + throw new IllegalArgumentException();
1319 +
1320 + if (concurrencyLevel > MAX_SEGMENTS)
1321 + concurrencyLevel = MAX_SEGMENTS;
1322 +
1323 + // Find power-of-two sizes best matching arguments
1324 + int sshift = 0;
1325 + int ssize = 1;
1326 + while (ssize < concurrencyLevel) {
1327 + ++sshift;
1328 + ssize <<= 1;
1329 + }
1330 + segmentShift = 32 - sshift;
1331 + segmentMask = ssize - 1;
1332 + this.segments = Segment.newArray(ssize);
1333 +
1334 + if (initialCapacity > MAXIMUM_CAPACITY)
1335 + initialCapacity = MAXIMUM_CAPACITY;
1336 + int c = initialCapacity / ssize;
1337 + if (c * ssize < initialCapacity)
1338 + ++c;
1339 + int cap = 1;
1340 + while (cap < c)
1341 + cap <<= 1;
1342 +
1343 + for (int i = 0; i < this.segments.length; ++i)
1344 + this.segments[i] = new Segment<K,V>(cap, loadFactor);
1345 + }
1346 +
1347 + /**
1348 + * Creates a new, empty map with the specified initial capacity
1349 + * and load factor and with the default concurrencyLevel (16).
1350 + *
1351 + * @param initialCapacity The implementation performs internal
1352 + * sizing to accommodate this many elements.
1353 + * @param loadFactor the load factor threshold, used to control resizing.
1354 + * Resizing may be performed when the average number of elements per
1355 + * bin exceeds this threshold.
1356 + * @throws IllegalArgumentException if the initial capacity of
1357 + * elements is negative or the load factor is nonpositive
1358 + *
1359 + * @since 1.6
1360 + */
1361 + public ConcurrentWeakHashMap(int initialCapacity, float loadFactor) {
1362 + this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
1363 + }
1364 +
1365 + /**
1366 + * Creates a new, empty map with the specified initial capacity,
1367 + * and with default load factor (0.75) and concurrencyLevel (16).
1368 + *
1369 + * @param initialCapacity the initial capacity. The implementation
1370 + * performs internal sizing to accommodate this many elements.
1371 + * @throws IllegalArgumentException if the initial capacity of
1372 + * elements is negative.
1373 + */
1374 + public ConcurrentWeakHashMap(int initialCapacity) {
1375 + this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
1376 + }
1377 +
1378 + /**
1379 + * Creates a new, empty map with a default initial capacity (16),
1380 + * load factor (0.75) and concurrencyLevel (16).
1381 + */
1382 + public ConcurrentWeakHashMap() {
1383 + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
1384 + }
1385 +
1386 + /**
1387 + * Creates a new map with the same mappings as the given map.
1388 + * The map is created with a capacity of 1.5 times the number
1389 + * of mappings in the given map or 16 (whichever is greater),
1390 + * and a default load factor (0.75) and concurrencyLevel (16).
1391 + *
1392 + * @param m the map
1393 + */
1394 + public ConcurrentWeakHashMap(Map<? extends K, ? extends V> m) {
1395 + this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
1396 + DEFAULT_INITIAL_CAPACITY),
1397 + DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
1398 + putAll(m);
1399 + }
1400 +
1401 + /**
1402 + * Returns <tt>true</tt> if this map contains no key-value mappings.
1403 + *
1404 + * @return <tt>true</tt> if this map contains no key-value mappings
1405 + */
1406 + public boolean isEmpty() {
1407 + final Segment<K,V>[] segments = this.segments;
1408 + /*
1409 + * We keep track of per-segment modCounts to avoid ABA
1410 + * problems in which an element in one segment was added and
1411 + * in another removed during traversal, in which case the
1412 + * table was never actually empty at any point. Note the
1413 + * similar use of modCounts in the size() and containsValue()
1414 + * methods, which are the only other methods also susceptible
1415 + * to ABA problems.
1416 + */
1417 + int[] mc = new int[segments.length];
1418 + int mcsum = 0;
1419 + for (int i = 0; i < segments.length; ++i) {
1420 + if (segments[i].count != 0)
1421 + return false;
1422 + else
1423 + mcsum += mc[i] = segments[i].modCount;
1424 + }
1425 + // If mcsum happens to be zero, then we know we got a snapshot
1426 + // before any modifications at all were made. This is
1427 + // probably common enough to bother tracking.
1428 + if (mcsum != 0) {
1429 + for (int i = 0; i < segments.length; ++i) {
1430 + if (segments[i].count != 0 ||
1431 + mc[i] != segments[i].modCount)
1432 + return false;
1433 + }
1434 + }
1435 + return true;
1436 + }
1437 +
1438 + /**
1439 + * Returns the number of key-value mappings in this map. If the
1440 + * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
1441 + * <tt>Integer.MAX_VALUE</tt>.
1442 + *
1443 + * @return the number of key-value mappings in this map
1444 + */
1445 + public int size() {
1446 + final Segment<K,V>[] segments = this.segments;
1447 + long sum = 0;
1448 + long check = 0;
1449 + int[] mc = new int[segments.length];
1450 + // Try a few times to get accurate count. On failure due to
1451 + // continuous async changes in table, resort to locking.
1452 + for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
1453 + check = 0;
1454 + sum = 0;
1455 + int mcsum = 0;
1456 + for (int i = 0; i < segments.length; ++i) {
1457 + sum += segments[i].count;
1458 + mcsum += mc[i] = segments[i].modCount;
1459 + }
1460 + if (mcsum != 0) {
1461 + for (int i = 0; i < segments.length; ++i) {
1462 + check += segments[i].count;
1463 + if (mc[i] != segments[i].modCount) {
1464 + check = -1; // force retry
1465 + break;
1466 + }
1467 + }
1468 + }
1469 + if (check == sum)
1470 + break;
1471 + }
1472 + if (check != sum) { // Resort to locking all segments
1473 + sum = 0;
1474 + for (int i = 0; i < segments.length; ++i)
1475 + segments[i].lock();
1476 + for (int i = 0; i < segments.length; ++i)
1477 + sum += segments[i].count;
1478 + for (int i = 0; i < segments.length; ++i)
1479 + segments[i].unlock();
1480 + }
1481 + if (sum > Integer.MAX_VALUE)
1482 + return Integer.MAX_VALUE;
1483 + else
1484 + return (int)sum;
1485 + }
1486 +
1487 + /**
1488 + * Returns the value to which the specified key is mapped,
1489 + * or {@code null} if this map contains no mapping for the key.
1490 + *
1491 + * <p>More formally, if this map contains a mapping from a key
1492 + * {@code k} to a value {@code v} such that {@code key.equals(k)},
1493 + * then this method returns {@code v}; otherwise it returns
1494 + * {@code null}. (There can be at most one such mapping.)
1495 + *
1496 + * @throws NullPointerException if the specified key is null
1497 + */
1498 + public V get(Object key) {
1499 + int hash = hash(key.hashCode());
1500 + return segmentFor(hash).get(key, hash);
1501 + }
1502 +
1503 + /**
1504 + * Tests if the specified object is a key in this table.
1505 + *
1506 + * @param key possible key
1507 + * @return <tt>true</tt> if and only if the specified object
1508 + * is a key in this table, as determined by the
1509 + * <tt>equals</tt> method; <tt>false</tt> otherwise.
1510 + * @throws NullPointerException if the specified key is null
1511 + */
1512 + public boolean containsKey(Object key) {
1513 + int hash = hash(key.hashCode());
1514 + return segmentFor(hash).containsKey(key, hash);
1515 + }
1516 +
1517 + /**
1518 + * Returns <tt>true</tt> if this map maps one or more keys to the
1519 + * specified value. Note: This method requires a full internal
1520 + * traversal of the hash table, and so is much slower than
1521 + * method <tt>containsKey</tt>.
1522 + *
1523 + * @param value value whose presence in this map is to be tested
1524 + * @return <tt>true</tt> if this map maps one or more keys to the
1525 + * specified value
1526 + * @throws NullPointerException if the specified value is null
1527 + */
1528 + public boolean containsValue(Object value) {
1529 + if (value == null)
1530 + throw new NullPointerException();
1531 +
1532 + // See explanation of modCount use above
1533 +
1534 + final Segment<K,V>[] segments = this.segments;
1535 + int[] mc = new int[segments.length];
1536 +
1537 + // Try a few times without locking
1538 + for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
1539 + int sum = 0;
1540 + int mcsum = 0;
1541 + for (int i = 0; i < segments.length; ++i) {
1542 + int c = segments[i].count;
1543 + mcsum += mc[i] = segments[i].modCount;
1544 + if (segments[i].containsValue(value))
1545 + return true;
1546 + }
1547 + boolean cleanSweep = true;
1548 + if (mcsum != 0) {
1549 + for (int i = 0; i < segments.length; ++i) {
1550 + int c = segments[i].count;
1551 + if (mc[i] != segments[i].modCount) {
1552 + cleanSweep = false;
1553 + break;
1554 + }
1555 + }
1556 + }
1557 + if (cleanSweep)
1558 + return false;
1559 + }
1560 + // Resort to locking all segments
1561 + for (int i = 0; i < segments.length; ++i)
1562 + segments[i].lock();
1563 + boolean found = false;
1564 + try {
1565 + for (int i = 0; i < segments.length; ++i) {
1566 + if (segments[i].containsValue(value)) {
1567 + found = true;
1568 + break;
1569 + }
1570 + }
1571 + } finally {
1572 + for (int i = 0; i < segments.length; ++i)
1573 + segments[i].unlock();
1574 + }
1575 + return found;
1576 + }
1577 +
1578 + /**
1579 + * Legacy method testing if some key maps into the specified value
1580 + * in this table. This method is identical in functionality to
1581 + * {@link #containsValue}, and exists solely to ensure
1582 + * full compatibility with class {@link java.util.Hashtable},
1583 + * which supported this method prior to introduction of the
1584 + * Java Collections framework.
1585 +
1586 + * @param value a value to search for
1587 + * @return <tt>true</tt> if and only if some key maps to the
1588 + * <tt>value</tt> argument in this table as
1589 + * determined by the <tt>equals</tt> method;
1590 + * <tt>false</tt> otherwise
1591 + * @throws NullPointerException if the specified value is null
1592 + */
1593 + public boolean contains(Object value) {
1594 + return containsValue(value);
1595 + }
1596 +
1597 + /**
1598 + * Maps the specified key to the specified value in this table.
1599 + * Neither the key nor the value can be null.
1600 + *
1601 + * <p> The value can be retrieved by calling the <tt>get</tt> method
1602 + * with a key that is equal to the original key.
1603 + *
1604 + * @param key key with which the specified value is to be associated
1605 + * @param value value to be associated with the specified key
1606 + * @return the previous value associated with <tt>key</tt>, or
1607 + * <tt>null</tt> if there was no mapping for <tt>key</tt>
1608 + * @throws NullPointerException if the specified key or value is null
1609 + */
1610 + public V put(K key, V value) {
1611 + if (value == null)
1612 + throw new NullPointerException();
1613 + int hash = hash(key.hashCode());
1614 + return segmentFor(hash).put(key, hash, value, false);
1615 + }
1616 +
1617 + /**
1618 + * {@inheritDoc}
1619 + *
1620 + * @return the previous value associated with the specified key,
1621 + * or <tt>null</tt> if there was no mapping for the key
1622 + * @throws NullPointerException if the specified key or value is null
1623 + */
1624 + public V putIfAbsent(K key, V value) {
1625 + if (value == null)
1626 + throw new NullPointerException();
1627 + int hash = hash(key.hashCode());
1628 + return segmentFor(hash).put(key, hash, value, true);
1629 + }
1630 +
1631 + /**
1632 + * Copies all of the mappings from the specified map to this one.
1633 + * These mappings replace any mappings that this map had for any of the
1634 + * keys currently in the specified map.
1635 + *
1636 + * @param m mappings to be stored in this map
1637 + */
1638 + public void putAll(Map<? extends K, ? extends V> m) {
1639 + for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
1640 + put(e.getKey(), e.getValue());
1641 + }
1642 +
1643 + /**
1644 + * Removes the key (and its corresponding value) from this map.
1645 + * This method does nothing if the key is not in the map.
1646 + *
1647 + * @param key the key that needs to be removed
1648 + * @return the previous value associated with <tt>key</tt>, or
1649 + * <tt>null</tt> if there was no mapping for <tt>key</tt>
1650 + * @throws NullPointerException if the specified key is null
1651 + */
1652 + public V remove(Object key) {
1653 + int hash = hash(key.hashCode());
1654 + return segmentFor(hash).remove(key, hash, null, false);
1655 + }
1656 +
1657 + /**
1658 + * {@inheritDoc}
1659 + *
1660 + * @throws NullPointerException if the specified key is null
1661 + */
1662 + public boolean remove(Object key, Object value) {
1663 + int hash = hash(key.hashCode());
1664 + if (value == null)
1665 + return false;
1666 + return segmentFor(hash).remove(key, hash, value, false) != null;
1667 + }
1668 +
1669 + /**
1670 + * {@inheritDoc}
1671 + *
1672 + * @throws NullPointerException if any of the arguments are null
1673 + */
1674 + public boolean replace(K key, V oldValue, V newValue) {
1675 + if (oldValue == null || newValue == null)
1676 + throw new NullPointerException();
1677 + int hash = hash(key.hashCode());
1678 + return segmentFor(hash).replace(key, hash, oldValue, newValue);
1679 + }
1680 +
1681 + /**
1682 + * {@inheritDoc}
1683 + *
1684 + * @return the previous value associated with the specified key,
1685 + * or <tt>null</tt> if there was no mapping for the key
1686 + * @throws NullPointerException if the specified key or value is null
1687 + */
1688 + public V replace(K key, V value) {
1689 + if (value == null)
1690 + throw new NullPointerException();
1691 + int hash = hash(key.hashCode());
1692 + return segmentFor(hash).replace(key, hash, value);
1693 + }
1694 +
1695 + /**
1696 + * Removes all of the mappings from this map.
1697 + */
1698 + public void clear() {
1699 + for (int i = 0; i < segments.length; ++i)
1700 + segments[i].clear();
1701 + }
1702 +
1703 + /**
1704 + * Returns a {@link Set} view of the keys contained in this map.
1705 + * The set is backed by the map, so changes to the map are
1706 + * reflected in the set, and vice-versa. The set supports element
1707 + * removal, which removes the corresponding mapping from this map,
1708 + * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
1709 + * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
1710 + * operations. It does not support the <tt>add</tt> or
1711 + * <tt>addAll</tt> operations.
1712 + *
1713 + * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
1714 + * that will never throw {@link ConcurrentModificationException},
1715 + * and guarantees to traverse elements as they existed upon
1716 + * construction of the iterator, and may (but is not guaranteed to)
1717 + * reflect any modifications subsequent to construction.
1718 + */
1719 + public Set<K> keySet() {
1720 + Set<K> ks = keySet;
1721 + return (ks != null) ? ks : (keySet = new KeySet());
1722 + }
1723 +
1724 + /**
1725 + * Returns a {@link Collection} view of the values contained in this map.
1726 + * The collection is backed by the map, so changes to the map are
1727 + * reflected in the collection, and vice-versa. The collection
1728 + * supports element removal, which removes the corresponding
1729 + * mapping from this map, via the <tt>Iterator.remove</tt>,
1730 + * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
1731 + * <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not
1732 + * support the <tt>add</tt> or <tt>addAll</tt> operations.
1733 + *
1734 + * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
1735 + * that will never throw {@link ConcurrentModificationException},
1736 + * and guarantees to traverse elements as they existed upon
1737 + * construction of the iterator, and may (but is not guaranteed to)
1738 + * reflect any modifications subsequent to construction.
1739 + */
1740 + public Collection<V> values() {
1741 + Collection<V> vs = values;
1742 + return (vs != null) ? vs : (values = new Values());
1743 + }
1744 +
1745 + /**
1746 + * Returns a {@link Set} view of the mappings contained in this map.
1747 + * The set is backed by the map, so changes to the map are
1748 + * reflected in the set, and vice-versa. The set supports element
1749 + * removal, which removes the corresponding mapping from the map,
1750 + * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
1751 + * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
1752 + * operations. It does not support the <tt>add</tt> or
1753 + * <tt>addAll</tt> operations.
1754 + *
1755 + * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
1756 + * that will never throw {@link ConcurrentModificationException},
1757 + * and guarantees to traverse elements as they existed upon
1758 + * construction of the iterator, and may (but is not guaranteed to)
1759 + * reflect any modifications subsequent to construction.
1760 + */
1761 + public Set<Map.Entry<K,V>> entrySet() {
1762 + Set<Map.Entry<K,V>> es = entrySet;
1763 + return (es != null) ? es : (entrySet = new EntrySet());
1764 + }
1765 +
1766 + /**
1767 + * Returns an enumeration of the keys in this table.
1768 + *
1769 + * @return an enumeration of the keys in this table
1770 + * @see #keySet()
1771 + */
1772 + public Enumeration<K> keys() {
1773 + return new KeyIterator();
1774 + }
1775 +
1776 + /**
1777 + * Returns an enumeration of the values in this table.
1778 + *
1779 + * @return an enumeration of the values in this table
1780 + * @see #values()
1781 + */
1782 + public Enumeration<V> elements() {
1783 + return new ValueIterator();
1784 + }
1785 +
1786 + /* ---------------- Iterator Support -------------- */
1787 +
1788 + abstract class HashIterator {
1789 + int nextSegmentIndex;
1790 + int nextTableIndex;
1791 + HashEntry<K,V>[] currentTable;
1792 + HashEntry<K, V> nextEntry;
1793 + HashEntry<K, V> lastReturned;
1794 + K currentKey; // Strong reference to weak key (prevents gc)
1795 +
1796 + HashIterator() {
1797 + nextSegmentIndex = segments.length - 1;
1798 + nextTableIndex = -1;
1799 + advance();
1800 + }
1801 +
1802 + public boolean hasMoreElements() { return hasNext(); }
1803 +
1804 + final void advance() {
1805 + if (nextEntry != null && (nextEntry = nextEntry.next) != null)
1806 + return;
1807 +
1808 + while (nextTableIndex >= 0) {
1809 + if ( (nextEntry = currentTable[nextTableIndex--]) != null)
1810 + return;
1811 + }
1812 +
1813 + while (nextSegmentIndex >= 0) {
1814 + Segment<K,V> seg = segments[nextSegmentIndex--];
1815 + if (seg.count != 0) {
1816 + currentTable = seg.table;
1817 + for (int j = currentTable.length - 1; j >= 0; --j) {
1818 + if ( (nextEntry = currentTable[j]) != null) {
1819 + nextTableIndex = j - 1;
1820 + return;
1821 + }
1822 + }
1823 + }
1824 + }
1825 + }
1826 +
1827 + public boolean hasNext() {
1828 + while (nextEntry != null) {
1829 + if (nextEntry.keyRef.get() != null)
1830 + return true;
1831 + advance();
1832 + }
1833 +
1834 + return false;
1835 + }
1836 +
1837 + HashEntry<K,V> nextEntry() {
1838 + do {
1839 + if (nextEntry == null)
1840 + throw new NoSuchElementException();
1841 +
1842 + lastReturned = nextEntry;
1843 + currentKey = lastReturned.keyRef.get();
1844 + advance();
1845 + } while (currentKey == null); // Skip GC'd keys
1846 +
1847 + return lastReturned;
1848 + }
1849 +
1850 + public void remove() {
1851 + if (lastReturned == null)
1852 + throw new IllegalStateException();
1853 + ConcurrentWeakHashMap.this.remove(currentKey);
1854 + lastReturned = null;
1855 + }
1856 + }
1857 +
1858 + final class KeyIterator
1859 + extends HashIterator
1860 + implements Iterator<K>, Enumeration<K>
1861 + {
1862 + public K next() { return super.nextEntry().keyRef.get(); }
1863 + public K nextElement() { return super.nextEntry().keyRef.get(); }
1864 + }
1865 +
1866 + final class ValueIterator
1867 + extends HashIterator
1868 + implements Iterator<V>, Enumeration<V>
1869 + {
1870 + public V next() { return super.nextEntry().value; }
1871 + public V nextElement() { return super.nextEntry().value; }
1872 + }
1873 +
1874 + /*
1875 + * This class is needed for JDK5 compatibility.
1876 + */
1877 + static class SimpleEntry<K, V> implements Entry<K, V>,
1878 + java.io.Serializable {
1879 + private static final long serialVersionUID = -8499721149061103585L;
1880 +
1881 + private final K key;
1882 + private V value;
1883 +
1884 + public SimpleEntry(K key, V value) {
1885 + this.key = key;
1886 + this.value = value;
1887 + }
1888 +
1889 + public SimpleEntry(Entry<? extends K, ? extends V> entry) {
1890 + this.key = entry.getKey();
1891 + this.value = entry.getValue();
1892 + }
1893 +
1894 + public K getKey() {
1895 + return key;
1896 + }
1897 +
1898 + public V getValue() {
1899 + return value;
1900 + }
1901 +
1902 + public V setValue(V value) {
1903 + V oldValue = this.value;
1904 + this.value = value;
1905 + return oldValue;
1906 + }
1907 +
1908 + public boolean equals(Object o) {
1909 + if (!(o instanceof Map.Entry))
1910 + return false;
1911 + @SuppressWarnings("unchecked")
1912 + Map.Entry e = (Map.Entry) o;
1913 + return eq(key, e.getKey()) && eq(value, e.getValue());
1914 + }
1915 +
1916 + public int hashCode() {
1917 + return (key == null ? 0 : key.hashCode())
1918 + ^ (value == null ? 0 : value.hashCode());
1919 + }
1920 +
1921 + public String toString() {
1922 + return key + "=" + value;
1923 + }
1924 +
1925 + private static boolean eq(Object o1, Object o2) {
1926 + return o1 == null ? o2 == null : o1.equals(o2);
1927 + }
1928 + }
1929 +
1930 +
1931 + /**
1932 + * Custom Entry class used by EntryIterator.next(), that relays setValue
1933 + * changes to the underlying map.
1934 + */
1935 + final class WriteThroughEntry extends SimpleEntry<K,V>
1936 + {
1937 + private static final long serialVersionUID = -7900634345345313646L;
1938 +
1939 + WriteThroughEntry(K k, V v) {
1940 + super(k,v);
1941 + }
1942 +
1943 + /**
1944 + * Set our entry's value and write through to the map. The
1945 + * value to return is somewhat arbitrary here. Since a
1946 + * WriteThroughEntry does not necessarily track asynchronous
1947 + * changes, the most recent "previous" value could be
1948 + * different from what we return (or could even have been
1949 + * removed in which case the put will re-establish). We do not
1950 + * and cannot guarantee more.
1951 + */
1952 + public V setValue(V value) {
1953 + if (value == null) throw new NullPointerException();
1954 + V v = super.setValue(value);
1955 + ConcurrentWeakHashMap.this.put(getKey(), value);
1956 + return v;
1957 + }
1958 + }
1959 +
1960 + final class EntryIterator
1961 + extends HashIterator
1962 + implements Iterator<Entry<K,V>>
1963 + {
1964 + public Map.Entry<K,V> next() {
1965 + HashEntry<K,V> e = super.nextEntry();
1966 + return new WriteThroughEntry(e.keyRef.get(), e.value);
1967 + }
1968 + }
1969 +
1970 + final class KeySet extends AbstractSet<K> {
1971 + public Iterator<K> iterator() {
1972 + return new KeyIterator();
1973 + }
1974 + public int size() {
1975 + return ConcurrentWeakHashMap.this.size();
1976 + }
1977 + public boolean isEmpty() {
1978 + return ConcurrentWeakHashMap.this.isEmpty();
1979 + }
1980 + public boolean contains(Object o) {
1981 + return ConcurrentWeakHashMap.this.containsKey(o);
1982 + }
1983 + public boolean remove(Object o) {
1984 + return ConcurrentWeakHashMap.this.remove(o) != null;
1985 + }
1986 + public void clear() {
1987 + ConcurrentWeakHashMap.this.clear();
1988 + }
1989 + }
1990 +
1991 + final class Values extends AbstractCollection<V> {
1992 + public Iterator<V> iterator() {
1993 + return new ValueIterator();
1994 + }
1995 + public int size() {
1996 + return ConcurrentWeakHashMap.this.size();
1997 + }
1998 + public boolean isEmpty() {
1999 + return ConcurrentWeakHashMap.this.isEmpty();
2000 + }
2001 + public boolean contains(Object o) {
2002 + return ConcurrentWeakHashMap.this.containsValue(o);
2003 + }
2004 + public void clear() {
2005 + ConcurrentWeakHashMap.this.clear();
2006 + }
2007 + }
2008 +
2009 + final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
2010 + public Iterator<Map.Entry<K,V>> iterator() {
2011 + return new EntryIterator();
2012 + }
2013 + public boolean contains(Object o) {
2014 + if (!(o instanceof Map.Entry))
2015 + return false;
2016 + Map.Entry<?,?> e = (Map.Entry<?,?>)o;
2017 + V v = ConcurrentWeakHashMap.this.get(e.getKey());
2018 + return v != null && v.equals(e.getValue());
2019 + }
2020 + public boolean remove(Object o) {
2021 + if (!(o instanceof Map.Entry))
2022 + return false;
2023 + Map.Entry<?,?> e = (Map.Entry<?,?>)o;
2024 + return ConcurrentWeakHashMap.this.remove(e.getKey(), e.getValue());
2025 + }
2026 + public int size() {
2027 + return ConcurrentWeakHashMap.this.size();
2028 + }
2029 + public boolean isEmpty() {
2030 + return ConcurrentWeakHashMap.this.isEmpty();
2031 + }
2032 + public void clear() {
2033 + ConcurrentWeakHashMap.this.clear();
2034 + }
2035 + }
2036 +
2037 + /* ---------------- Serialization Support -------------- */
2038 +
2039 + /**
2040 + * Save the state of the <tt>ConcurrentWeakHashMap</tt> instance to a
2041 + * stream (i.e., serialize it).
2042 + * @param s the stream
2043 + * @serialData
2044 + * the key (Object) and value (Object)
2045 + * for each key-value mapping, followed by a null pair.
2046 + * The key-value mappings are emitted in no particular order.
2047 + */
2048 + private void writeObject(java.io.ObjectOutputStream s) throws IOException {
2049 + s.defaultWriteObject();
2050 +
2051 + for (int k = 0; k < segments.length; ++k) {
2052 + Segment<K,V> seg = segments[k];
2053 + seg.lock();
2054 + try {
2055 + HashEntry<K,V>[] tab = seg.table;
2056 + for (int i = 0; i < tab.length; ++i) {
2057 + for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
2058 + K key = e.keyRef.get();
2059 + if (key == null) // Skip GC'd keys
2060 + continue;
2061 +
2062 + s.writeObject(key);
2063 + s.writeObject(e.value);
2064 + }
2065 + }
2066 + } finally {
2067 + seg.unlock();
2068 + }
2069 + }
2070 + s.writeObject(null);
2071 + s.writeObject(null);
2072 + }
2073 +
2074 + /**
2075 + * Reconstitute the <tt>ConcurrentWeakHashMap</tt> instance from a
2076 + * stream (i.e., deserialize it).
2077 + * @param s the stream
2078 + */
2079 + @SuppressWarnings("unchecked")
2080 + private void readObject(java.io.ObjectInputStream s)
2081 + throws IOException, ClassNotFoundException {
2082 + s.defaultReadObject();
2083 +
2084 + // Initialize each segment to be minimally sized, and let grow.
2085 + for (int i = 0; i < segments.length; ++i) {
2086 + segments[i].setTable(new HashEntry[1]);
2087 + }
2088 +
2089 + // Read the keys and values, and put the mappings in the table
2090 + for (;;) {
2091 + K key = (K) s.readObject();
2092 + V value = (V) s.readObject();
2093 + if (key == null)
2094 + break;
2095 + put(key, value);
2096 + }
2097 + }
2098 +}
2099 \ No newline at end of file
2100 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/HierarchicalStreamReader.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/HierarchicalStreamReader.java
2101 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/HierarchicalStreamReader.java 2008-12-06 15:47:50.000000000 +0000
2102 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/HierarchicalStreamReader.java 2010-10-09 18:20:48.000000000 +0100
2103 @@ -26,6 +26,12 @@ public interface HierarchicalStreamReade
2104 boolean hasMoreChildren();
2105
2106 /**
2107 + * In situation where {@link #hasMoreChildren()} returns true, peek the tag name
2108 + * of the child.
2109 + */
2110 + String peekNextChild();
2111 +
2112 + /**
2113 * Select the current child as current node.
2114 * A call to this function must be balanced with a call to {@link #moveUp()}.
2115 */
2116 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/ReaderWrapper.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/ReaderWrapper.java
2117 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/ReaderWrapper.java 2008-12-06 15:47:50.000000000 +0000
2118 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/ReaderWrapper.java 2010-10-09 18:20:48.000000000 +0100
2119 @@ -32,6 +32,10 @@ public abstract class ReaderWrapper impl
2120 return wrapped.hasMoreChildren();
2121 }
2122
2123 + public String peekNextChild() {
2124 + return wrapped.peekNextChild();
2125 + }
2126 +
2127 public void moveDown() {
2128 wrapped.moveDown();
2129 }
2130 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/binary/BinaryStreamReader.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/binary/BinaryStreamReader.java
2131 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/binary/BinaryStreamReader.java 2008-12-06 15:47:50.000000000 +0000
2132 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/binary/BinaryStreamReader.java 2010-10-09 18:20:48.000000000 +0100
2133 @@ -50,6 +50,21 @@ public class BinaryStreamReader implemen
2134 return depthState.hasMoreChildren();
2135 }
2136
2137 + public String peekNextChild() {
2138 + Token nextToken = readToken();
2139 + switch (nextToken.getType()) {
2140 + case Token.TYPE_VALUE:
2141 + case Token.TYPE_END_NODE:
2142 + pushBack(nextToken);
2143 + return null;
2144 + case Token.TYPE_START_NODE:
2145 + pushBack(nextToken);
2146 + return idRegistry.get(nextToken.getId());
2147 + default:
2148 + throw new StreamException("Unexpected token " + nextToken);
2149 + }
2150 + }
2151 +
2152 public String getNodeName() {
2153 return depthState.getName();
2154 }
2155 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/AbstractPullReader.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/AbstractPullReader.java
2156 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/AbstractPullReader.java 2008-12-06 15:47:50.000000000 +0000
2157 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/AbstractPullReader.java 2010-10-09 18:20:48.000000000 +0100
2158 @@ -88,6 +88,23 @@ public abstract class AbstractPullReader
2159 }
2160 }
2161
2162 + public String peekNextChild() {
2163 + mark();
2164 + while (true) {
2165 + Event ev = readEvent();
2166 + switch (ev.type) {
2167 + case START_NODE:
2168 + reset();
2169 + return ev.value;
2170 + case END_NODE:
2171 + reset();
2172 + return null;
2173 + default:
2174 + continue;
2175 + }
2176 + }
2177 + }
2178 +
2179 public void moveDown() {
2180 int currentDepth = elementStack.size();
2181 while (elementStack.size() <= currentDepth) {
2182 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/Dom4JReader.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/Dom4JReader.java
2183 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/Dom4JReader.java 2008-12-06 15:47:50.000000000 +0000
2184 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/Dom4JReader.java 2010-10-09 18:20:48.000000000 +0100
2185 @@ -16,6 +16,8 @@ import com.thoughtworks.xstream.converte
2186 import org.dom4j.Document;
2187 import org.dom4j.Element;
2188
2189 +import java.util.List;
2190 +
2191 public class Dom4JReader extends AbstractDocumentReader {
2192
2193 private Element currentElement;
2194 @@ -41,7 +43,13 @@ public class Dom4JReader extends Abstrac
2195 public Dom4JReader(Document document, XmlFriendlyReplacer replacer) {
2196 this(document.getRootElement(), replacer);
2197 }
2198 -
2199 +
2200 + public String peekNextChild() {
2201 + List list = currentElement.elements();
2202 + if(list.isEmpty()) return null;
2203 + return unescapeXmlName(((Element)list.get(0)).getName());
2204 + }
2205 +
2206 public String getNodeName() {
2207 return unescapeXmlName(currentElement.getName());
2208 }
2209 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/DomReader.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/DomReader.java
2210 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/DomReader.java 2008-12-06 15:47:50.000000000 +0000
2211 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/DomReader.java 2010-10-09 18:20:48.000000000 +0100
2212 @@ -49,7 +49,19 @@ public class DomReader extends AbstractD
2213 public DomReader(Document document, XmlFriendlyReplacer replacer) {
2214 this(document.getDocumentElement(), replacer);
2215 }
2216 -
2217 +
2218 + public String peekNextChild() {
2219 + NodeList childNodes = currentElement.getChildNodes();
2220 + childElements = new ArrayList();
2221 + for (int i = 0; i < childNodes.getLength(); i++) {
2222 + Node node = childNodes.item(i);
2223 + if (node instanceof Element) {
2224 + return unescapeXmlName(((Element)node).getTagName());
2225 + }
2226 + }
2227 + return null;
2228 + }
2229 +
2230 public String getNodeName() {
2231 return unescapeXmlName(currentElement.getTagName());
2232 }
2233 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/JDomReader.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/JDomReader.java
2234 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/JDomReader.java 2008-12-06 15:47:50.000000000 +0000
2235 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/JDomReader.java 2010-10-09 18:20:48.000000000 +0100
2236 @@ -15,6 +15,8 @@ import org.jdom.Attribute;
2237 import org.jdom.Document;
2238 import org.jdom.Element;
2239
2240 +import java.util.List;
2241 +
2242 /**
2243 * @author Laurent Bihanic
2244 */
2245 @@ -60,6 +62,12 @@ public class JDomReader extends Abstract
2246 // return currentElement.getParent();
2247 }
2248
2249 + public String peekNextChild() {
2250 + List list = currentElement.getChildren();
2251 + if(list.isEmpty()) return null;
2252 + return unescapeXmlName(((Element)list.get(0)).getName());
2253 + }
2254 +
2255 protected Object getChild(int index) {
2256 return currentElement.getChildren().get(index);
2257 }
2258 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/XmlFriendlyReplacer.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/XmlFriendlyReplacer.java
2259 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/XmlFriendlyReplacer.java 2008-12-06 15:47:50.000000000 +0000
2260 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/XmlFriendlyReplacer.java 2010-10-09 18:20:48.000000000 +0100
2261 @@ -115,6 +115,8 @@ public class XmlFriendlyReplacer {
2262 * @return The String with unescaped name
2263 */
2264 public String unescapeName(String name) {
2265 + // common path: there's nothing that was escaped
2266 + if(name.indexOf(underscoreReplacement)<0 && name.indexOf(dollarReplacement)<0) return name;
2267 final WeakReference ref = (WeakReference)unescapeCache.get(name);
2268 String s = (String)(ref == null ? null : ref.get());
2269
2270 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/XomReader.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/XomReader.java
2271 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/XomReader.java 2008-12-06 15:47:50.000000000 +0000
2272 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/XomReader.java 2010-10-09 18:20:48.000000000 +0100
2273 @@ -15,6 +15,7 @@ import nu.xom.Document;
2274 import nu.xom.Element;
2275 import nu.xom.Node;
2276 import nu.xom.Text;
2277 +import nu.xom.Elements;
2278
2279 public class XomReader extends AbstractDocumentReader {
2280
2281 @@ -46,6 +47,12 @@ public class XomReader extends AbstractD
2282 return unescapeXmlName(currentElement.getLocalName());
2283 }
2284
2285 + public String peekNextChild() {
2286 + Elements children = currentElement.getChildElements();
2287 + if(children.size()==0) return null;
2288 + return unescapeXmlName(children.get(0).getLocalName());
2289 + }
2290 +
2291 public String getValue() {
2292 // currentElement.getValue() not used as this includes text of child elements, which we don't want.
2293 StringBuffer result = new StringBuffer();
2294 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/XppDomReader.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/XppDomReader.java
2295 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/io/xml/XppDomReader.java 2008-12-06 15:47:50.000000000 +0000
2296 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/io/xml/XppDomReader.java 2010-10-09 18:20:48.000000000 +0100
2297 @@ -36,6 +36,11 @@ public class XppDomReader extends Abstra
2298 return unescapeXmlName(currentElement.getName());
2299 }
2300
2301 + public String peekNextChild() {
2302 + if(currentElement.getChildCount()==0) return null;
2303 + return unescapeXmlName(currentElement.getChild(0).getName());
2304 + }
2305 +
2306 public String getValue() {
2307 String text = null;
2308
2309 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/mapper/AnnotationMapper.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/mapper/AnnotationMapper.java
2310 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/mapper/AnnotationMapper.java 2008-12-06 15:47:50.000000000 +0000
2311 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/mapper/AnnotationMapper.java 2010-10-09 18:20:48.000000000 +0100
2312 @@ -20,6 +20,7 @@ import com.thoughtworks.xstream.annotati
2313 import com.thoughtworks.xstream.annotations.XStreamImplicitCollection;
2314 import com.thoughtworks.xstream.annotations.XStreamInclude;
2315 import com.thoughtworks.xstream.annotations.XStreamOmitField;
2316 +import com.thoughtworks.xstream.annotations.XStreamSerializeAs;
2317 import com.thoughtworks.xstream.converters.Converter;
2318 import com.thoughtworks.xstream.converters.ConverterMatcher;
2319 import com.thoughtworks.xstream.converters.ConverterRegistry;
2320 @@ -68,6 +69,8 @@ public class AnnotationMapper extends Ma
2321 private final Map<Class<?>, Converter> converterCache = new HashMap<Class<?>, Converter>();
2322 private final Set<Class<?>> annotatedTypes = new WeakHashSet<Class<?>>();
2323
2324 + private final Map<Class,String> serializedClass = new WeakHashMap<Class, String>();
2325 +
2326 /**
2327 * Construct an AnnotationMapper.
2328 *
2329 @@ -104,6 +107,8 @@ public class AnnotationMapper extends Ma
2330 if (!locked) {
2331 processAnnotations(type);
2332 }
2333 + String name = serializedClass.get(type);
2334 + if (name!=null) return name;
2335 return super.serializedClass(type);
2336 }
2337
2338 @@ -150,6 +155,7 @@ public class AnnotationMapper extends Ma
2339 return;
2340 }
2341 synchronized (annotatedTypes) {
2342 + if (annotatedTypes.contains(initialType)) return;
2343 final Set<Class<?>> types = new UnprocessedTypesSet();
2344 types.add(initialType);
2345 processTypes(types);
2346 @@ -167,6 +173,10 @@ public class AnnotationMapper extends Ma
2347 continue;
2348 }
2349
2350 + XStreamSerializeAs a = type.getAnnotation(XStreamSerializeAs.class);
2351 + if (a!=null && a.value()!=void.class)
2352 + serializedClass.put(type,a.value().getName());
2353 +
2354 addParametrizedTypes(type, types);
2355
2356 processConverterAnnotations(type);
2357 diff -u -rupN xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/mapper/CachingMapper.java jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/mapper/CachingMapper.java
2358 --- xstream-1.3.1//xstream/src/java/com/thoughtworks/xstream/mapper/CachingMapper.java 2008-12-06 15:47:50.000000000 +0000
2359 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/java/com/thoughtworks/xstream/mapper/CachingMapper.java 2010-10-09 18:20:48.000000000 +0100
2360 @@ -17,6 +17,7 @@ import java.lang.ref.WeakReference;
2361 import java.util.Collections;
2362 import java.util.HashMap;
2363 import java.util.Map;
2364 +import java.util.concurrent.ConcurrentHashMap;
2365
2366 /**
2367 * Mapper that caches which names map to which classes. Prevents repetitive searching and class loading.
2368 @@ -43,20 +44,26 @@ public class CachingMapper extends Mappe
2369 public Class realClass(String elementName) {
2370 WeakReference reference = (WeakReference) realClassCache.get(elementName);
2371 if (reference != null) {
2372 - Class cached = (Class) reference.get();
2373 + Object cached = reference.get();
2374 + if (cached instanceof CannotResolveClassException)
2375 + throw (CannotResolveClassException) cached;
2376 if (cached != null) {
2377 - return cached;
2378 + return (Class)cached;
2379 }
2380 }
2381 -
2382 - Class result = super.realClass(elementName);
2383 - realClassCache.put(elementName, new WeakReference(result));
2384 - return result;
2385 +
2386 + try {
2387 + Class result = super.realClass(elementName);
2388 + realClassCache.put(elementName, new WeakReference(result));
2389 + return result;
2390 + } catch (CannotResolveClassException e) {
2391 + realClassCache.put(elementName,new WeakReference(e));
2392 + throw e;
2393 + }
2394 }
2395
2396 private Object readResolve() {
2397 - realClassCache = Collections.synchronizedMap(new HashMap(128));
2398 + realClassCache = new ConcurrentHashMap();
2399 return this;
2400 }
2401 -
2402 }
2403 diff -u -rupN xstream-1.3.1//xstream/src/test/com/thoughtworks/xstream/converters/enums/EnumConverterTest.java jenkins-xstream-1.3.1-hudson-8//xstream/src/test/com/thoughtworks/xstream/converters/enums/EnumConverterTest.java
2404 --- xstream-1.3.1//xstream/src/test/com/thoughtworks/xstream/converters/enums/EnumConverterTest.java 2008-12-06 15:47:48.000000000 +0000
2405 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/test/com/thoughtworks/xstream/converters/enums/EnumConverterTest.java 2010-10-09 18:20:48.000000000 +0100
2406 @@ -40,6 +40,12 @@ public class EnumConverterTest extends T
2407 assertEquals(in, xstream.fromXML(expectedXml));
2408 }
2409
2410 + public void testRelaxedNameMatch() {
2411 + String expectedXml = "<simple>green</simple>";
2412 + SimpleEnum in = SimpleEnum.GREEN;
2413 + assertEquals(in, xstream.fromXML(expectedXml));
2414 + }
2415 +
2416 public void testRepresentsPolymorphicEnumAsSingleStringValue() {
2417 String expectedXml = "<polymorphic>B</polymorphic>";
2418 PolymorphicEnum in = PolymorphicEnum.B;
2419 diff -u -rupN xstream-1.3.1//xstream/src/test/com/thoughtworks/xstream/core/DefaultConverterLookupTest.java jenkins-xstream-1.3.1-hudson-8//xstream/src/test/com/thoughtworks/xstream/core/DefaultConverterLookupTest.java
2420 --- xstream-1.3.1//xstream/src/test/com/thoughtworks/xstream/core/DefaultConverterLookupTest.java 2008-12-06 15:47:46.000000000 +0000
2421 +++ jenkins-xstream-1.3.1-hudson-8//xstream/src/test/com/thoughtworks/xstream/core/DefaultConverterLookupTest.java 2010-10-09 18:20:48.000000000 +0100
2422 @@ -12,12 +12,15 @@
2423 package com.thoughtworks.xstream.core;
2424
2425 import java.util.BitSet;
2426 +import java.io.ByteArrayInputStream;
2427
2428 import junit.framework.TestCase;
2429
2430 import com.thoughtworks.xstream.XStream;
2431 +import com.thoughtworks.xstream.mapper.CannotResolveClassException;
2432 import com.thoughtworks.xstream.converters.Converter;
2433 import com.thoughtworks.xstream.converters.SingleValueConverterWrapper;
2434 +import com.thoughtworks.xstream.converters.ConversionException;
2435 import com.thoughtworks.xstream.converters.basic.StringConverter;
2436 import com.thoughtworks.xstream.converters.collections.BitSetConverter;
2437
2438 @@ -41,4 +44,25 @@ public class DefaultConverterLookupTest
2439 assertEquals(lookup.lookupConverterForType(String.class), newConverter);
2440 }
2441
2442 + /**
2443 + * Caching the failure should function correctly.
2444 + *
2445 + */
2446 + public void testFailureCache() {
2447 + String xml = "<root><field class='NoSuchClass'/></root>";
2448 + XStream xs = new XStream();
2449 + xs.alias("root",Root.class);
2450 + for (int i=0; i<3; i++) {
2451 + try {
2452 + xs.fromXML(new ByteArrayInputStream(xml.getBytes()));
2453 + fail(); // should fail to unmarshal
2454 + } catch (ConversionException e) {
2455 + assertTrue(e.getCause() instanceof CannotResolveClassException);
2456 + }
2457 + }
2458 + }
2459 +
2460 + public static class Root {
2461 + public Object field;
2462 + }
2463 }
2464 diff -u -rupN xstream-1.3.1//xstream-benchmark/src/test/com/thoughtworks/xstream/benchmark/xmlfriendly/target/Field$Reflection.java jenkins-xstream-1.3.1-hudson-8//xstream-benchmark/src/test/com/thoughtworks/xstream/benchmark/xmlfriendly/target/Field$Reflection.java
2465 --- xstream-1.3.1//xstream-benchmark/src/test/com/thoughtworks/xstream/benchmark/xmlfriendly/target/Field$Reflection.java 1970-01-01 01:00:00.000000000 +0100
2466 +++ jenkins-xstream-1.3.1-hudson-8//xstream-benchmark/src/test/com/thoughtworks/xstream/benchmark/xmlfriendly/target/Field$Reflection.java 2010-10-09 18:20:48.000000000 +0100
2467 @@ -0,0 +1,43 @@
2468 +/*
2469 + * Copyright (C) 2007 XStream Committers.
2470 + * All rights reserved.
2471 + *
2472 + * The software in this package is published under the terms of the BSD
2473 + * style license a copy of which has been included with this distribution in
2474 + * the LICENSE.txt file.
2475 + *
2476 + * Created on 13. September 2007 by Joerg Schaible
2477 + */
2478 +package com.thoughtworks.xstream.benchmark.xmlfriendly.target;
2479 +
2480 +import com.thoughtworks.xstream.benchmark.reflection.targets.AbstractReflectionTarget;
2481 +import com.thoughtworks.xstream.benchmark.xmlfriendly.model.A100$Fields;
2482 +import com.thoughtworks.xstream.tools.benchmark.Target;
2483 +
2484 +import java.util.ArrayList;
2485 +import java.util.List;
2486 +
2487 +/**
2488 + * A Target for a 100 fields class with each field name containing 5 dollars.
2489 + *
2490 + * @author Jörg Schaible
2491 + * @see com.thoughtworks.xstream.tools.benchmark.Harness
2492 + * @see Target
2493 + */
2494 +public class Field$Reflection extends AbstractReflectionTarget {
2495 +
2496 + public Field$Reflection() {
2497 + super(new ArrayList());
2498 + List list = (List)target();
2499 + for(int i = 0; i < 100; ++i) {
2500 + Object o = new A100$Fields();
2501 + fill(o);
2502 + list.add(o);
2503 + }
2504 + }
2505 +
2506 + public String toString() {
2507 + return "Field with dollars Target";
2508 + }
2509 +
2510 +}
2511 diff -u -rupN xstream-1.3.1//xstream-benchmark/src/test/com/thoughtworks/xstream/benchmark/xmlfriendly/target/Field_Reflection.java jenkins-xstream-1.3.1-hudson-8//xstream-benchmark/src/test/com/thoughtworks/xstream/benchmark/xmlfriendly/target/Field_Reflection.java
2512 --- xstream-1.3.1//xstream-benchmark/src/test/com/thoughtworks/xstream/benchmark/xmlfriendly/target/Field_Reflection.java 1970-01-01 01:00:00.000000000 +0100
2513 +++ jenkins-xstream-1.3.1-hudson-8//xstream-benchmark/src/test/com/thoughtworks/xstream/benchmark/xmlfriendly/target/Field_Reflection.java 2010-10-09 18:20:48.000000000 +0100
2514 @@ -0,0 +1,43 @@
2515 +/*
2516 + * Copyright (C) 2007 XStream Committers.
2517 + * All rights reserved.
2518 + *
2519 + * The software in this package is published under the terms of the BSD
2520 + * style license a copy of which has been included with this distribution in
2521 + * the LICENSE.txt file.
2522 + *
2523 + * Created on 13. September 2007 by Joerg Schaible
2524 + */
2525 +package com.thoughtworks.xstream.benchmark.xmlfriendly.target;
2526 +
2527 +import com.thoughtworks.xstream.benchmark.reflection.targets.AbstractReflectionTarget;
2528 +import com.thoughtworks.xstream.benchmark.xmlfriendly.model.A100_Fields;
2529 +import com.thoughtworks.xstream.tools.benchmark.Target;
2530 +
2531 +import java.util.ArrayList;
2532 +import java.util.List;
2533 +
2534 +/**
2535 + * A Target for a 100 fields class with each field name containing 5 underscores.
2536 + *
2537 + * @author Jörg Schaible
2538 + * @see com.thoughtworks.xstream.tools.benchmark.Harness
2539 + * @see Target
2540 + */
2541 +public class Field_Reflection extends AbstractReflectionTarget {
2542 +
2543 + public Field_Reflection() {
2544 + super(new ArrayList());
2545 + List list = (List)target();
2546 + for(int i = 0; i < 100; ++i) {
2547 + Object o = new A100_Fields();
2548 + fill(o);
2549 + list.add(o);
2550 + }
2551 + }
2552 +
2553 + public String toString() {
2554 + return "Field with underscores Target";
2555 + }
2556 +
2557 +}
2558
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.