All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.esotericsoftware.kryo.serializers.FieldSerializer Maven / Gradle / Ivy

/* Copyright (c) 2008, Nathan Sweet
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
 * conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the distribution.
 * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

package com.esotericsoftware.kryo.serializers;

import static com.esotericsoftware.minlog.Log.*;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.NotNull;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.util.IntArray;
import com.esotericsoftware.kryo.util.ObjectMap;
import com.esotericsoftware.kryo.util.Util;
import com.esotericsoftware.reflectasm.FieldAccess;

// BOZO - Make primitive serialization with ReflectASM configurable?

/** Serializes objects using direct field assignment. FieldSerializer is generic and can serialize most classes without any
 * configuration. It is efficient and writes only the field data, without any extra information. It does not support adding,
 * removing, or changing the type of fields without invalidating previously serialized bytes. This can be acceptable in many
 * situations, such as when sending data over a network, but may not be a good choice for long term data storage because the Java
 * classes cannot evolve. Because FieldSerializer attempts to read and write non-public fields by default, it is important to
 * evaluate each class that will be serialized. If fields are public, bytecode generation will be used instead of reflection.
 * @see Serializer
 * @see Kryo#register(Class, Serializer)
 * @see VersionFieldSerializer
 * @see TaggedFieldSerializer
 * @see CompatibleFieldSerializer
 * @author Nathan Sweet 
 * @author Roman Levenstein  */
public class FieldSerializer extends Serializer implements Comparator {

	final Kryo kryo;
	final Class type;
	/** type variables declared for this type */
	final TypeVariable[] typeParameters;
	final Class componentType;
	protected final FieldSerializerConfig config;
	private CachedField[] fields = new CachedField[0];
	private CachedField[] transientFields = new CachedField[0];
	protected HashSet removedFields = new HashSet();
	Object access;
	private FieldSerializerUnsafeUtil unsafeUtil;

	private FieldSerializerGenericsUtil genericsUtil;

	private FieldSerializerAnnotationsUtil annotationsUtil;

	/** Concrete classes passed as values for type variables */
	private Class[] generics;

	private Generics genericsScope;

	/** If set, this serializer tries to use a variable length encoding for int and long fields */
	private boolean varIntsEnabled;

	/** If set, adjacent primitive fields are written in bulk This flag may only work with Oracle JVMs, because they layout
	 * primitive fields in memory in such a way that primitive fields are grouped together. This option has effect only when used
	 * with Unsafe-based FieldSerializer.
	 * 

* FIXME: Not all versions of Sun/Oracle JDK properly work with this option. Disable it for now. Later add dynamic checks to * see if this feature is supported by a current JDK version. *

*/ private boolean useMemRegions = false; private boolean hasObjectFields = false; static CachedFieldFactory asmFieldFactory; static CachedFieldFactory objectFieldFactory; static CachedFieldFactory unsafeFieldFactory; static boolean unsafeAvailable; static Class unsafeUtilClass; static Method sortFieldsByOffsetMethod; static { try { unsafeUtilClass = FieldSerializer.class.getClassLoader().loadClass("com.esotericsoftware.kryo.util.UnsafeUtil"); Method unsafeMethod = unsafeUtilClass.getMethod("unsafe"); sortFieldsByOffsetMethod = unsafeUtilClass.getMethod("sortFieldsByOffset", List.class); Object unsafe = unsafeMethod.invoke(null); if (unsafe != null) unsafeAvailable = true; } catch (Throwable e) { if (TRACE) trace("kryo", "sun.misc.Unsafe is unavailable."); } } { varIntsEnabled = true; if (TRACE) trace("kryo", "Optimize ints: " + varIntsEnabled); } public FieldSerializer (Kryo kryo, Class type) { this(kryo, type, null); } public FieldSerializer (Kryo kryo, Class type, Class[] generics) { this(kryo, type, generics, kryo.getFieldSerializerConfig().clone()); } protected FieldSerializer (Kryo kryo, Class type, Class[] generics, FieldSerializerConfig config) { this.config = config; this.kryo = kryo; this.type = type; this.generics = generics; this.typeParameters = type.getTypeParameters(); if (this.typeParameters == null || this.typeParameters.length == 0) this.componentType = type.getComponentType(); else this.componentType = null; this.genericsUtil = new FieldSerializerGenericsUtil(this); this.unsafeUtil = FieldSerializerUnsafeUtil.Factory.getInstance(this); this.annotationsUtil = new FieldSerializerAnnotationsUtil(this); rebuildCachedFields(); } /** Called when the list of cached fields must be rebuilt. This is done any time settings are changed that affect which fields * will be used. It is called from the constructor for FieldSerializer, but not for subclasses. Subclasses must call this from * their constructor. */ protected void rebuildCachedFields () { rebuildCachedFields(false); } /** Rebuilds the list of cached fields. * @param minorRebuild if set, processing due to changes in generic type parameters will be optimized */ protected void rebuildCachedFields (boolean minorRebuild) { /** TODO: Optimize rebuildCachedFields invocations performed due to changes in generic type parameters */ if (TRACE && generics != null) trace("kryo", "Generic type parameters: " + Arrays.toString(generics)); if (type.isInterface()) { fields = new CachedField[0]; // No fields to serialize. return; } hasObjectFields = false; if (config.isOptimizedGenerics()) { // For generic classes, generate a mapping from type variable names to the concrete types // This mapping is the same for the whole class. Generics genScope = genericsUtil.buildGenericsScope(type, generics); genericsScope = genScope; // Push proper scopes at serializer construction time if (genericsScope != null) kryo.getGenericsResolver().pushScope(type, genericsScope); } List validFields; List validTransientFields; IntArray useAsm = new IntArray(); if (!minorRebuild) { // Collect all fields. List allFields = new ArrayList(); Class nextClass = type; while (nextClass != Object.class) { Field[] declaredFields = nextClass.getDeclaredFields(); if (declaredFields != null) { for (Field f : declaredFields) { if (Modifier.isStatic(f.getModifiers())) continue; allFields.add(f); } } nextClass = nextClass.getSuperclass(); } ObjectMap context = kryo.getContext(); // Sort fields by their offsets if (useMemRegions && !config.isUseAsm() && unsafeAvailable) { try { Field[] allFieldsArray = (Field[])sortFieldsByOffsetMethod.invoke(null, allFields); allFields = Arrays.asList(allFieldsArray); } catch (Exception e) { throw new RuntimeException("Cannot invoke UnsafeUtil.sortFieldsByOffset()", e); } } // TODO: useAsm is modified as a side effect, this should be pulled out of buildValidFields // Build a list of valid non-transient fields validFields = buildValidFields(false, allFields, context, useAsm); // Build a list of valid transient fields validTransientFields = buildValidFields(true, allFields, context, useAsm); // Use ReflectASM for any public fields. if (config.isUseAsm() && !Util.IS_ANDROID && Modifier.isPublic(type.getModifiers()) && useAsm.indexOf(1) != -1) { try { access = FieldAccess.get(type); } catch (RuntimeException ignored) { } } } else { // It is a minor rebuild validFields = buildValidFieldsFromCachedFields(fields, useAsm); // Build a list of valid transient fields validTransientFields = buildValidFieldsFromCachedFields(transientFields, useAsm); } List cachedFields = new ArrayList(validFields.size()); List cachedTransientFields = new ArrayList(validTransientFields.size()); // Process non-transient fields createCachedFields(useAsm, validFields, cachedFields, 0); // Process transient fields createCachedFields(useAsm, validTransientFields, cachedTransientFields, validFields.size()); Collections.sort(cachedFields, this); fields = cachedFields.toArray(new CachedField[cachedFields.size()]); Collections.sort(cachedTransientFields, this); transientFields = cachedTransientFields.toArray(new CachedField[cachedTransientFields.size()]); initializeCachedFields(); if (genericsScope != null) kryo.getGenericsResolver().popScope(); if (!minorRebuild) { for (CachedField field : removedFields) removeField(field); } annotationsUtil.processAnnotatedFields(this); } private List buildValidFieldsFromCachedFields (CachedField[] cachedFields, IntArray useAsm) { ArrayList fields = new ArrayList(cachedFields.length); for (CachedField f : cachedFields) { fields.add(f.field); useAsm.add((f.accessIndex > -1) ? 1 : 0); } return fields; } private List buildValidFields (boolean transientFields, List allFields, ObjectMap context, IntArray useAsm) { List result = new ArrayList(allFields.size()); for (int i = 0, n = allFields.size(); i < n; i++) { Field field = allFields.get(i); int modifiers = field.getModifiers(); if (Modifier.isTransient(modifiers) != transientFields) continue; if (Modifier.isStatic(modifiers)) continue; if (field.isSynthetic() && config.isIgnoreSyntheticFields()) continue; if (!field.isAccessible()) { if (!config.isSetFieldsAsAccessible()) continue; try { field.setAccessible(true); } catch (AccessControlException ex) { continue; } } Optional optional = field.getAnnotation(Optional.class); if (optional != null && !context.containsKey(optional.value())) continue; result.add(field); // BOZO - Must be public? useAsm .add(!Modifier.isFinal(modifiers) && Modifier.isPublic(modifiers) && Modifier.isPublic(field.getType().getModifiers()) ? 1 : 0); } return result; } private void createCachedFields (IntArray useAsm, List validFields, List cachedFields, int baseIndex) { if (config.isUseAsm() || !useMemRegions) { for (int i = 0, n = validFields.size(); i < n; i++) { Field field = validFields.get(i); int accessIndex = -1; if (access != null && useAsm.get(baseIndex + i) == 1) accessIndex = ((FieldAccess)access).getIndex(field.getName()); cachedFields.add(newCachedField(field, cachedFields.size(), accessIndex)); } } else { unsafeUtil.createUnsafeCacheFieldsAndRegions(validFields, cachedFields, baseIndex, useAsm); } } public void setGenerics (Kryo kryo, Class[] generics) { if (!config.isOptimizedGenerics()) return; this.generics = generics; if (typeParameters != null && typeParameters.length > 0) { // There is no need to rebuild all cached fields from scratch. // Generic parameter types do not affect the set of fields, offsets of fields, // transient and non-transient properties. They only affect the type of // fields and serializers selected for each field. rebuildCachedFields(true); } } /** Get generic type parameters of the class controlled by this serializer. * @return generic type parameters or null, if there are none. */ public Class[] getGenerics () { return generics; } protected void initializeCachedFields () { } CachedField newCachedField (Field field, int fieldIndex, int accessIndex) { Class[] fieldClass = new Class[] {field.getType()}; Type fieldGenericType = (config.isOptimizedGenerics()) ? field.getGenericType() : null; CachedField cachedField; if (!config.isOptimizedGenerics() || fieldGenericType == fieldClass[0]) { // For optimized generics this is a field without generic type parameters if (TRACE) trace("kryo", "Field " + field.getName() + ": " + fieldClass[0]); cachedField = newMatchingCachedField(field, accessIndex, fieldClass[0], fieldGenericType, null); } else { cachedField = genericsUtil.newCachedFieldOfGenericType(field, accessIndex, fieldClass, fieldGenericType); } if (cachedField instanceof ObjectField) { hasObjectFields = true; } cachedField.field = field; cachedField.varIntsEnabled = varIntsEnabled; if (!config.isUseAsm()) { cachedField.offset = unsafeUtil.getObjectFieldOffset(field); } cachedField.access = (FieldAccess)access; cachedField.accessIndex = accessIndex; cachedField.canBeNull = config.isFieldsCanBeNull() && !fieldClass[0].isPrimitive() && !field.isAnnotationPresent(NotNull.class); // Always use the same serializer for this field if the field's class is final. if (kryo.isFinal(fieldClass[0]) || config.isFixedFieldTypes()) cachedField.valueClass = fieldClass[0]; return cachedField; } CachedField newMatchingCachedField (Field field, int accessIndex, Class fieldClass, Type fieldGenericType, Class[] fieldGenerics) { CachedField cachedField; if (accessIndex != -1) { cachedField = getAsmFieldFactory().createCachedField(fieldClass, field, this); } else if (!config.isUseAsm()) { cachedField = getUnsafeFieldFactory().createCachedField(fieldClass, field, this); } else { cachedField = getObjectFieldFactory().createCachedField(fieldClass, field, this); if (config.isOptimizedGenerics()) { if (fieldGenerics != null) ((ObjectField)cachedField).generics = fieldGenerics; else if (fieldGenericType != null) { Class[] cachedFieldGenerics = FieldSerializerGenericsUtil.getGenerics(fieldGenericType, kryo); ((ObjectField)cachedField).generics = cachedFieldGenerics; if (TRACE) trace("kryo", "Field generics: " + Arrays.toString(cachedFieldGenerics)); } } } return cachedField; } private CachedFieldFactory getAsmFieldFactory () { if (asmFieldFactory == null) asmFieldFactory = new AsmCachedFieldFactory(); return asmFieldFactory; } private CachedFieldFactory getObjectFieldFactory () { if (objectFieldFactory == null) objectFieldFactory = new ObjectCachedFieldFactory(); return objectFieldFactory; } private CachedFieldFactory getUnsafeFieldFactory () { // Use reflection to load UnsafeFieldFactory, so that there is no explicit dependency // on anything using Unsafe. This is required to make FieldSerializer work on those // platforms that do not support sun.misc.Unsafe properly. if (unsafeFieldFactory == null) { try { unsafeFieldFactory = (CachedFieldFactory)this.getClass().getClassLoader() .loadClass("com.esotericsoftware.kryo.serializers.UnsafeCachedFieldFactory").newInstance(); } catch (Exception e) { throw new RuntimeException("Cannot create UnsafeFieldFactory", e); } } return unsafeFieldFactory; } public int compare (CachedField o1, CachedField o2) { // Fields are sorted by alpha so the order of the data is known. return getCachedFieldName(o1).compareTo(getCachedFieldName(o2)); } /** Sets the default value for {@link CachedField#setCanBeNull(boolean)}. Calling this method resets the {@link #getFields() * cached fields}. * @param fieldsCanBeNull False if none of the fields are null. Saves 0-1 byte per field. True if it is not known (default). */ public void setFieldsCanBeNull (boolean fieldsCanBeNull) { config.setFieldsCanBeNull(fieldsCanBeNull); rebuildCachedFields(); } /** Controls which fields are serialized. Calling this method resets the {@link #getFields() cached fields}. * @param setFieldsAsAccessible If true, all non-transient fields (inlcuding private fields) will be serialized and * {@link Field#setAccessible(boolean) set as accessible} if necessary (default). If false, only fields in the public * API will be serialized. */ public void setFieldsAsAccessible (boolean setFieldsAsAccessible) { config.setFieldsAsAccessible(setFieldsAsAccessible); rebuildCachedFields(); } /** Controls if synthetic fields are serialized. Default is true. Calling this method resets the {@link #getFields() cached * fields}. * @param ignoreSyntheticFields If true, only non-synthetic fields will be serialized. */ public void setIgnoreSyntheticFields (boolean ignoreSyntheticFields) { config.setIgnoreSyntheticFields(ignoreSyntheticFields); rebuildCachedFields(); } /** Sets the default value for {@link CachedField#setClass(Class)} to the field's declared type. This allows FieldSerializer to * be more efficient, since it knows field values will not be a subclass of their declared type. Default is false. Calling this * method resets the {@link #getFields() cached fields}. */ public void setFixedFieldTypes (boolean fixedFieldTypes) { config.setFixedFieldTypes(fixedFieldTypes); rebuildCachedFields(); } /** Controls whether ASM should be used. Calling this method resets the {@link #getFields() cached fields}. * @param setUseAsm If true, ASM will be used for fast serialization. If false, Unsafe will be used (default) */ public void setUseAsm (boolean setUseAsm) { config.setUseAsm(setUseAsm); rebuildCachedFields(); } // Enable/disable copying of transient fields public void setCopyTransient (boolean setCopyTransient) { config.setCopyTransient(setCopyTransient); } // Enable/disable serialization of transient fields public void setSerializeTransient (boolean setSerializeTransient) { config.setSerializeTransient(setSerializeTransient); } /** Controls if the serialization of generics should be optimized for smaller size. *

* Important: This setting changes the serialized representation, so that data can be deserialized only with * if this setting is the same as it was for serialization. *

* @param setOptimizedGenerics If true, the serialization of generics will be optimize for smaller size (default: false) */ public void setOptimizedGenerics (boolean setOptimizedGenerics) { config.setOptimizedGenerics(setOptimizedGenerics); rebuildCachedFields(); } /** This method can be called for different fields having the same type. Even though the raw type is the same, if the type is * generic, it could happen that different concrete classes are used to instantiate it. Therefore, in case of different * instantiation parameters, the fields analysis should be repeated. * * TODO: Cache serializer instances generated for a given set of generic parameters. Reuse it later instead of recomputing * every time. */ public void write (Kryo kryo, Output output, T object) { if (TRACE) trace("kryo", "FieldSerializer.write fields of class: " + object.getClass().getName()); if (config.isOptimizedGenerics()) { if (typeParameters != null && generics != null) { // Rebuild fields info. It may result in rebuilding the genericScope rebuildCachedFields(); } if (genericsScope != null) { // Push proper scopes at serializer usage time kryo.getGenericsResolver().pushScope(type, genericsScope); } } CachedField[] fields = this.fields; for (int i = 0, n = fields.length; i < n; i++) fields[i].write(output, object); // Serialize transient fields if (config.isSerializeTransient()) { for (int i = 0, n = transientFields.length; i < n; i++) transientFields[i].write(output, object); } if (config.isOptimizedGenerics() && genericsScope != null) { // Pop the scope for generics kryo.getGenericsResolver().popScope(); } } public T read (Kryo kryo, Input input, Class type) { try { if (config.isOptimizedGenerics()) { if (typeParameters != null && generics != null) { // Rebuild fields info. It may result in rebuilding the // genericScope rebuildCachedFields(); } if (genericsScope != null) { // Push a new scope for generics kryo.getGenericsResolver().pushScope(type, genericsScope); } } T object = create(kryo, input, type); kryo.reference(object); CachedField[] fields = this.fields; for (int i = 0, n = fields.length; i < n; i++) fields[i].read(input, object); // De-serialize transient fields if (config.isSerializeTransient()) { for (int i = 0, n = transientFields.length; i < n; i++) transientFields[i].read(input, object); } return object; } finally { if (config.isOptimizedGenerics() && genericsScope != null && kryo.getGenericsResolver() != null) { // Pop the scope for generics kryo.getGenericsResolver().popScope(); } } } /** Used by {@link #read(Kryo, Input, Class)} to create the new object. This can be overridden to customize object creation, eg * to call a constructor with arguments. The default implementation uses {@link Kryo#newInstance(Class)}. */ protected T create (Kryo kryo, Input input, Class type) { return kryo.newInstance(type); } /** Allows specific fields to be optimized. */ public CachedField getField (String fieldName) { for (CachedField cachedField : fields) if (getCachedFieldName(cachedField).equals(fieldName)) return cachedField; throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + type.getName()); } protected String getCachedFieldName (CachedField cachedField) { return config.getCachedFieldNameStrategy().getName(cachedField); } /** Removes a field so that it won't be serialized. */ public void removeField (String fieldName) { for (int i = 0; i < fields.length; i++) { CachedField cachedField = fields[i]; if (getCachedFieldName(cachedField).equals(fieldName)) { CachedField[] newFields = new CachedField[fields.length - 1]; System.arraycopy(fields, 0, newFields, 0, i); System.arraycopy(fields, i + 1, newFields, i, newFields.length - i); fields = newFields; removedFields.add(cachedField); return; } } for (int i = 0; i < transientFields.length; i++) { CachedField cachedField = transientFields[i]; if (getCachedFieldName(cachedField).equals(fieldName)) { CachedField[] newFields = new CachedField[transientFields.length - 1]; System.arraycopy(transientFields, 0, newFields, 0, i); System.arraycopy(transientFields, i + 1, newFields, i, newFields.length - i); transientFields = newFields; removedFields.add(cachedField); return; } } throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + type.getName()); } /** Removes a field so that it won't be serialized. */ public void removeField (CachedField removeField) { for (int i = 0; i < fields.length; i++) { CachedField cachedField = fields[i]; if (cachedField == removeField) { CachedField[] newFields = new CachedField[fields.length - 1]; System.arraycopy(fields, 0, newFields, 0, i); System.arraycopy(fields, i + 1, newFields, i, newFields.length - i); fields = newFields; removedFields.add(cachedField); return; } } for (int i = 0; i < transientFields.length; i++) { CachedField cachedField = transientFields[i]; if (cachedField == removeField) { CachedField[] newFields = new CachedField[transientFields.length - 1]; System.arraycopy(transientFields, 0, newFields, 0, i); System.arraycopy(transientFields, i + 1, newFields, i, newFields.length - i); transientFields = newFields; removedFields.add(cachedField); return; } } throw new IllegalArgumentException("Field \"" + removeField + "\" not found on class: " + type.getName()); } /** Get all fields controlled by this FieldSerializer * @return all fields controlled by this FieldSerializer */ public CachedField[] getFields () { return fields; } /** Get all transient fields controlled by this FieldSerializer * @return all transient fields controlled by this FieldSerializer */ public CachedField[] getTransientFields () { return transientFields; } public Class getType () { return type; } public Kryo getKryo () { return kryo; } public boolean getUseAsmEnabled () { return config.isUseAsm(); } public boolean getUseMemRegions () { return useMemRegions; } public boolean getCopyTransient () { return config.isCopyTransient(); } public boolean getSerializeTransient () { return config.isSerializeTransient(); } /** Used by {@link #copy(Kryo, Object)} to create the new object. This can be overridden to customize object creation, eg to * call a constructor with arguments. The default implementation uses {@link Kryo#newInstance(Class)}. */ protected T createCopy (Kryo kryo, T original) { return (T)kryo.newInstance(original.getClass()); } public T copy (Kryo kryo, T original) { T copy = createCopy(kryo, original); kryo.reference(copy); // Copy transient fields if (config.isCopyTransient()) { for (int i = 0, n = transientFields.length; i < n; i++) transientFields[i].copy(original, copy); } for (int i = 0, n = fields.length; i < n; i++) fields[i].copy(original, copy); return copy; } final Generics getGenericsScope () { return genericsScope; } /** Controls how a field will be serialized. */ public static abstract class CachedField { Field field; FieldAccess access; Class valueClass; Serializer serializer; boolean canBeNull; int accessIndex = -1; long offset = -1; boolean varIntsEnabled = true; /** @param valueClass The concrete class of the values for this field. This saves 1-2 bytes. The serializer registered for * the specified class will be used. Only set to a non-null value if the field type in the class definition is * final or the values for this field will not vary. */ public void setClass (Class valueClass) { this.valueClass = valueClass; this.serializer = null; } /** @param valueClass The concrete class of the values for this field. This saves 1-2 bytes. Only set to a non-null value if * the field type in the class definition is final or the values for this field will not vary. */ public void setClass (Class valueClass, Serializer serializer) { this.valueClass = valueClass; this.serializer = serializer; } public void setSerializer (Serializer serializer) { this.serializer = serializer; } public Serializer getSerializer () { return this.serializer; } public void setCanBeNull (boolean canBeNull) { this.canBeNull = canBeNull; } public Field getField () { return field; } public String toString () { return field.getName(); } abstract public void write (Output output, Object object); abstract public void read (Input input, Object object); abstract public void copy (Object original, Object copy); } public static interface CachedFieldFactory { public CachedField createCachedField (Class fieldClass, Field field, FieldSerializer ser); } public interface CachedFieldNameStrategy { CachedFieldNameStrategy DEFAULT = new CachedFieldNameStrategy() { @Override public String getName (CachedField cachedField) { return cachedField.field.getName(); } }; CachedFieldNameStrategy EXTENDED = new CachedFieldNameStrategy() { @Override public String getName (CachedField cachedField) { return cachedField.field.getDeclaringClass().getSimpleName() + "." + cachedField.field.getName(); } }; String getName (CachedField cachedField); } /** Indicates a field should be ignored when its declaring class is registered unless the {@link Kryo#getContext() context} has * a value set for the specified key. This can be useful when a field must be serialized for one purpose, but not for another. * Eg, a class for a networked application could have a field that should not be serialized and sent to clients, but should be * serialized when stored on the server. * @author Nathan Sweet */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) static public @interface Optional { public String value(); } /** Used to annotate fields with a specific Kryo serializer. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Bind { /** Value. * * @return the class used for this field */ @SuppressWarnings("rawtypes") Class value(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy