/*
 * Copyright (C) 2004, 2005 Joe Walnes.
 * Copyright (C) 2006, 2007, 2008, 2011, 2013, 2014 XStream Committers.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 * 
 * Created on 08. January 2014 by Joerg Schaible, renamed from Sun14ReflectionProvider
 */
package com.thoughtworks.xstream.converters.reflection;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;


/**
 * Instantiates a new object bypassing the constructor using undocumented internal JDK features.
 * <p>
 * The code in the constructor will never be executed and parameters do not have to be known. This is the same method
 * used by the internals of standard Java serialization, but relies on internal code (sun.misc.Unsafe) that may not be
 * present on all JVMs.
 * <p>
 * <p>
 * The implementation will use the same internals to write into fields. This is a lot faster and was additionally the
 * only possibility to set final fields prior to Java 5.
 * <p>
 * 
 * @author Joe Walnes
 * @author Brian Slesinsky
 * @author J&ouml;rg Schaible
 * @since 1.4.7
 */
public class SunUnsafeReflectionProvider extends SunLimitedUnsafeReflectionProvider {

    // references to the Field key are kept in the FieldDictionary
    private transient Map<Field, Long> fieldOffsetCache;

    /**
     * @since 1.4.7
     */
    public SunUnsafeReflectionProvider() {
        super();
    }

    /**
     * @since 1.4.7
     */
    public SunUnsafeReflectionProvider(final FieldDictionary dic) {
        super(dic);
    }

    @Override
    public void writeField(final Object object, final String fieldName, final Object value, final Class<?> definedIn) {
        write(fieldDictionary.field(object.getClass(), fieldName, definedIn), object, value);
    }

    private void write(final Field field, final Object object, final Object value) {
        if (exception != null) {
            throw new ObjectAccessException("Could not set field " + object.getClass() + "." + field.getName(),
                exception);
        }
        try {
            final long offset = getFieldOffset(field);
            final Class<?> type = field.getType();
            if (type.isPrimitive()) {
                if (type.equals(Integer.TYPE)) {
                    unsafe.putInt(object, offset, ((Integer)value).intValue());
                } else if (type.equals(Long.TYPE)) {
                    unsafe.putLong(object, offset, ((Long)value).longValue());
                } else if (type.equals(Short.TYPE)) {
                    unsafe.putShort(object, offset, ((Short)value).shortValue());
                } else if (type.equals(Character.TYPE)) {
                    unsafe.putChar(object, offset, ((Character)value).charValue());
                } else if (type.equals(Byte.TYPE)) {
                    unsafe.putByte(object, offset, ((Byte)value).byteValue());
                } else if (type.equals(Float.TYPE)) {
                    unsafe.putFloat(object, offset, ((Float)value).floatValue());
                } else if (type.equals(Double.TYPE)) {
                    unsafe.putDouble(object, offset, ((Double)value).doubleValue());
                } else if (type.equals(Boolean.TYPE)) {
                    unsafe.putBoolean(object, offset, ((Boolean)value).booleanValue());
                } else {
                    throw new ObjectAccessException("Could not set field "
                        + object.getClass()
                        + "."
                        + field.getName()
                        + ": Unknown type "
                        + type);
                }
            } else {
                unsafe.putObject(object, offset, value);
            }

        } catch (final IllegalArgumentException e) {
            throw new ObjectAccessException("Could not set field " + object.getClass() + "." + field.getName(), e);
        }
    }

    private synchronized long getFieldOffset(final Field f) {
        Long l = fieldOffsetCache.get(f);
        if (l == null) {
            l = Long.valueOf(unsafe.objectFieldOffset(f));
            fieldOffsetCache.put(f, l);
        }

        return l.longValue();
    }

    private Object readResolve() {
        init();
        return this;
    }

    @Override
    protected void init() {
        super.init();
        fieldOffsetCache = new HashMap<Field, Long>();
    }
}

@@ -87,16 +87,22 @@
 13, 2014
+, 2015
  XStream
@@ -114,16 +114,16 @@
 itters.%0A
-
  * All r
@@ -317,17 +317,16 @@
 file.%0A *
- 
 %0A * Crea
@@ -515,16 +515,37 @@
 va.util.
+concurrent.Concurrent
 HashMap;
@@ -562,16 +562,37 @@
 va.util.
+concurrent.Concurrent
 Map;%0A%0A%0A/
@@ -1157,17 +1157,16 @@
 * %3Cp%3E%0A *
- 
 %0A * @aut
@@ -1433,16 +1433,26 @@
 ansient 
+Concurrent
 Map%3CFiel
@@ -3589,16 +3589,20 @@
         
+    
 + object
@@ -3637,16 +3637,20 @@
         
+    
 + %22.%22%0A  
@@ -3671,16 +3671,20 @@
         
+  
+  
 + field.
@@ -3693,16 +3693,20 @@
 tName()%0A
+    
         
@@ -3741,16 +3741,20 @@
  type %22%0A
+    
         
@@ -4091,21 +4091,8 @@
 ate 
-synchronized 
 long
@@ -4203,19 +4203,47 @@
         
-l =
+fieldOffsetCache.putIfAbsent(f,
  Long.va
@@ -4276,16 +4276,17 @@
 fset(f))
+)
 ;%0A      
@@ -4282,32 +4282,36 @@
 )));%0A           
+ l =
  fieldOffsetCach
@@ -4316,16 +4316,13 @@
 che.
-pu
+ge
 t(f
-, l
 );%0A 
@@ -4503,32 +4503,32 @@
   super.init();%0A
-
         fieldOff
@@ -4542,16 +4542,26 @@
 e = new 
+Concurrent
 HashMap%3C

Use ConcurrentHashMap in SunUnsafeReflectionProvider (XSTR-727).