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&ouml;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&ouml;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&ouml;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.
  • [get | view] (2011-06-13 10:36:36, 3.1 KB) [[attachment:dom4j-patches.tar.gz]]
  • [get | view] (2011-06-13 12:12:50, 2.9 KB) [[attachment:jenkins-htmlunit-core-js-patches.tar.gz]]
  • [get | view] (2011-06-13 13:35:24, 106.4 KB) [[attachment:jenkins-xstream.patch]]
  • [get | view] (2011-06-13 15:17:04, 21.2 KB) [[attachment:jexl-patches.tar.gz]]
 All files | Selected Files: delete move to page

You are not allowed to attach a file to this page.