Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /* 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.util.List;
 
 
 // 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.

 
 public class FieldSerializer<T> extends Serializer<T> implements Comparator<FieldSerializer.CachedField> {
 	final Kryo kryo;
 	final Class type;
type variables declared for this type
 
 	private CachedField[] fields = new CachedField[0];
 	private CachedField[] transientFields = new CachedField[0];
 	protected HashSet<CachedFieldremovedFields = new HashSet();
 	private boolean fieldsCanBeNull = truesetFieldsAsAccessible = true;
 	private boolean ignoreSyntheticFields = true;
 	private boolean fixedFieldTypes;
If set, ASM-backend is used. Otherwise Unsafe-based backend or reflection is used
 
 	private boolean useAsmEnabled;
 
 
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;

If set, transient fields will be copied
	private boolean copyTransient = true;

If set, transient fields will be serialized
	private final boolean serializeTransient = false;
	private boolean hasObjectFields = false;
	static boolean unsafeAvailable;
	static {
		try {
			 = FieldSerializer.class.getClassLoader().loadClass("com.esotericsoftware.kryo.util.UnsafeUtil");
			Method unsafeMethod = .getMethod("unsafe");
			 = .getMethod("sortFieldsByOffset"List.class);
			Object unsafe = unsafeMethod.invoke(null);
			if (unsafe != null = true;
catch (Throwable e) {
			if (trace("kryo""sun.misc.Unsafe is unavailable.");
		}
	}
	{
		if (trace("kryo""Optimize ints: " + );
	}
	public FieldSerializer (Kryo kryoClass type) {
		this. = kryo;
		this. = type;
		if (this. == null || this..length == 0)
		else
			this. = null;
		if (!this. && !) {
			this. = true;
			if (trace("kryo""sun.misc.Unsafe is unavailable, using ASM.");
		}
		this. = FieldSerializerUnsafeUtil.Factory.getInstance(this);
	}
	public FieldSerializer (Kryo kryoClass typeClass[] generics) {
		this. = kryo;
		this. = type;
		this. = generics;
		if (this. == null || this..length == 0)
		else
			this. = null;
		if (!this. && !) {
			this. = true;
			if (trace("kryo""sun.misc.Unsafe is unavailable, using ASM.");
		}
		this. = FieldSerializerUnsafeUtil.Factory.getInstance(this);
	}

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 () {
	}

Rebuilds the list of cached fields.

Parameters:
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 ( &&  != nulltrace("kryo""Generic type parameters: " + Arrays.toString());
		if (.isInterface()) {
			 = new CachedField[0]; // No fields to serialize.
			return;
		}
		 = false;
		// For generic classes, generate a mapping from type variable names to the concrete types
		// This mapping is the same for the whole class.
		 = genScope;
		// Push proper scopes at serializer construction time
		List<FieldvalidFields;
		List<FieldvalidTransientFields;
		IntArray useAsm = new IntArray();
		if (!minorRebuild) {
			// Collect all fields.
			List<FieldallFields = new ArrayList();
			Class nextClass = ;
			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 = .getContext();
			// Sort fields by their offsets
				try {
					Field[] allFieldsArray = (Field[]).invoke(nullallFields);
					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(falseallFieldscontextuseAsm);
			// Build a list of valid transient fields
			validTransientFields = buildValidFields(trueallFieldscontextuseAsm);
			// Use ReflectASM for any public fields.
			if ( && !. && Modifier.isPublic(.getModifiers()) && useAsm.indexOf(1) != -1) {
				try {
					 = FieldAccess.get();
catch (RuntimeException ignored) {
				}
			}
else {
			// It is a minor rebuild
			validFields = buildValidFieldsFromCachedFields(useAsm);
			// Build a list of valid transient fields
			validTransientFields = buildValidFieldsFromCachedFields(useAsm);
		}
		List<CachedFieldcachedFields = new ArrayList(validFields.size());
		List<CachedFieldcachedTransientFields = new ArrayList(validTransientFields.size());
		// Process non-transient fields
		createCachedFields(useAsmvalidFieldscachedFields, 0);
		// Process transient fields
		createCachedFields(useAsmvalidTransientFieldscachedTransientFieldsvalidFields.size());
		Collections.sort(cachedFieldsthis);
		 = cachedFields.toArray(new CachedField[cachedFields.size()]);
		Collections.sort(cachedTransientFieldsthis);
		 = cachedTransientFields.toArray(new CachedField[cachedTransientFields.size()]);
		if (!minorRebuild) {
			for (CachedField field : )
		}
	}
	private List<FieldbuildValidFieldsFromCachedFields (CachedField[] cachedFieldsIntArray useAsm) {
		ArrayList<Fieldfields = new ArrayList<Field>(cachedFields.length);
		for (CachedField f : cachedFields) {
			fields.add(f.field);
			useAsm.add((f.accessIndex > -1) ? 1 : 0);
		}
		return fields;
	}
	private List<FieldbuildValidFields (boolean transientFieldsList<FieldallFieldsObjectMap contextIntArray useAsm) {
		List<Fieldresult = new ArrayList(allFields.size());
		for (int i = 0, n = allFields.size(); i < ni++) {
			Field field = allFields.get(i);
			int modifiers = field.getModifiers();
			if (Modifier.isTransient(modifiers) != transientFieldscontinue;
			if (Modifier.isStatic(modifiers)) continue;
			if (field.isSynthetic() && continue;
			if (!field.isAccessible()) {
				if (!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 useAsmList<FieldvalidFieldsList<CachedFieldcachedFieldsint baseIndex) {
			for (int i = 0, n = validFields.size(); i < ni++) {
				Field field = validFields.get(i);
				int accessIndex = -1;
				if ( != null && useAsm.get(baseIndex + i) == 1) accessIndex = ((FieldAccess)).getIndex(field.getName());
				cachedFields.add(newCachedField(fieldcachedFields.size(), accessIndex));
			}
else {
			.createUnsafeCacheFieldsAndRegions(validFieldscachedFieldsbaseIndexuseAsm);
		}
	}
	public void setGenerics (Kryo kryoClass[] generics) {
		this. = generics;
		if ( != null && . > 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.
		}
	}

Get generic type parameters of the class controlled by this serializer.

Returns:
generic type parameters or null, if there are none.
	public Class[] getGenerics () {
		return ;
	}
	protected void initializeCachedFields () {
	}
	CachedField newCachedField (Field fieldint fieldIndexint accessIndex) {
		Class[] fieldClass = new Class[] {field.getType()};
		Type fieldGenericType = field.getGenericType();
		CachedField cachedField;
		if (fieldGenericType == fieldClass[0]) {
			// This is a field without generic type parameters
			if (trace("kryo""Field " + field.getName() + ": " + fieldClass[0]);
			cachedField = newMatchingCachedField(fieldaccessIndexfieldClass[0], fieldGenericTypenull);
else {
			cachedField = .newCachedFieldOfGenericType(fieldaccessIndexfieldClassfieldGenericType);
		}
		if (cachedField instanceof ObjectField) {
		}
		cachedField.field = field;
		cachedField.varIntsEnabled = ;
		if (!) {
			cachedField.offset = .getObjectFieldOffset(field);
		}
		cachedField.access = (FieldAccess);
		cachedField.accessIndex = accessIndex;
		cachedField.canBeNull =  && !fieldClass[0].isPrimitive() && !field.isAnnotationPresent(NotNull.class);
		// Always use the same serializer for this field if the field's class is final.
		if (.isFinal(fieldClass[0]) || cachedField.valueClass = fieldClass[0];
		return cachedField;
	}
	CachedField newMatchingCachedField (Field fieldint accessIndexClass fieldClassType fieldGenericType,
		Class[] fieldGenerics) {
		CachedField cachedField;
		if (accessIndex != -1) {
			cachedField = getAsmFieldFactory().createCachedField(fieldClassfieldthis);
else if (!) {
			cachedField = getUnsafeFieldFactory().createCachedField(fieldClassfieldthis);
else {
			cachedField = getObjectFieldFactory().createCachedField(fieldClassfieldthis);
			if (fieldGenerics != null)
				((ObjectField)cachedField). = fieldGenerics;
			else {
				Class[] cachedFieldGenerics = FieldSerializerGenericsUtil.getGenerics(fieldGenericType);
				((ObjectField)cachedField). = cachedFieldGenerics;
				if (trace("kryo""Field generics: " + Arrays.toString(cachedFieldGenerics));
			}
		}
		return cachedField;
	}
	}
	}
		// 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 ( == null) {
			try {
					.loadClass("com.esotericsoftware.kryo.serializers.UnsafeCachedFieldFactory").newInstance();
catch (Exception e) {
				throw new RuntimeException("Cannot create UnsafeFieldFactory"e);
			}
		}
	}
	public int compare (CachedField o1CachedField o2) {
		// Fields are sorted by alpha so the order of the data is known.
		return o1.field.getName().compareTo(o2.field.getName());
	}

Sets the default value for FieldSerializer.CachedField.setCanBeNull(boolean). Calling this method resets the cached fields.

Parameters:
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) {
		this. = fieldsCanBeNull;
		if (trace("kryo""setFieldsCanBeNull: " + fieldsCanBeNull);
	}

Controls which fields are serialized. Calling this method resets the cached fields.

Parameters:
setFieldsAsAccessible If true, all non-transient fields (inlcuding private fields) will be serialized and set as accessible if necessary (default). If false, only fields in the public API will be serialized.
	public void setFieldsAsAccessible (boolean setFieldsAsAccessible) {
		this. = setFieldsAsAccessible;
		if (trace("kryo""setFieldsAsAccessible: " + setFieldsAsAccessible);
	}

Controls if synthetic fields are serialized. Default is true. Calling this method resets the cached fields.

Parameters:
ignoreSyntheticFields If true, only non-synthetic fields will be serialized.
	public void setIgnoreSyntheticFields (boolean ignoreSyntheticFields) {
		this. = ignoreSyntheticFields;
		if (trace("kryo""setIgnoreSyntheticFields: " + ignoreSyntheticFields);
	}

Sets the default value for FieldSerializer.CachedField.setClass(java.lang.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 cached fields.
	public void setFixedFieldTypes (boolean fixedFieldTypes) {
		this. = fixedFieldTypes;
		if (trace("kryo""setFixedFieldTypes: " + fixedFieldTypes);
	}

Controls whether ASM should be used. Calling this method resets the cached fields.

Parameters:
setUseAsm If true, ASM will be used for fast serialization. If false, Unsafe will be used (default)
	public void setUseAsm (boolean setUseAsm) {
		 = setUseAsm;
			 = true;
			if (trace("kryo""sun.misc.Unsafe is unavailable, using ASM.");
		}
		// optimizeInts = useAsmBackend;
		if (trace("kryo""setUseAsm: " + setUseAsm);
	}
	// Enable/disable copying of transient fields
	public void setCopyTransient (boolean setCopyTransient) {
		 = setCopyTransient;
	}

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 kryoOutput output, T object) {
		if (trace("kryo""FieldSerializer.write fields of class: " + object.getClass().getName());
		if ( != null &&  != null) {
			// Rebuild fields info. It may result in rebuilding the genericScope
		}
		if ( != null) {
			// Push proper scopes at serializer usage time
		}
		CachedField[] fields = this.;
		for (int i = 0, n = fields.lengthi < ni++)
			fields[i].write(outputobject);
		// Serialize transient fields
			for (int i = 0, n = .i < ni++)
				[i].write(outputobject);
		}
		if ( != null) {
			// Pop the scope for generics
		}
	}
	public T read (Kryo kryoInput inputClass<T> type) {
		try {
			if ( != null &&  != null) {
				// Rebuild fields info. It may result in rebuilding the
				// genericScope
			}
			if ( != null) {
				// Push a new scope for generics
			}
object = create(kryoinputtype);
			kryo.reference(object);
			CachedField[] fields = this.;
			for (int i = 0, n = fields.lengthi < ni++)
				fields[i].read(inputobject);
			// De-serialize transient fields
				for (int i = 0, n = .i < ni++)
					[i].read(inputobject);
			}
			return object;
finally {
			if ( != null && kryo.getGenericsScope() != null) {
				// Pop the scope for generics
			}
		}
	}

Used by read(com.esotericsoftware.kryo.Kryo,com.esotericsoftware.kryo.io.Input,java.lang.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 com.esotericsoftware.kryo.Kryo.newInstance(java.lang.Class).
	protected T create (Kryo kryoInput inputClass<T> type) {
		return kryo.newInstance(type);
	}

Allows specific fields to be optimized.
	public CachedField getField (String fieldName) {
		for (CachedField cachedField : )
			if (cachedField.field.getName().equals(fieldName)) return cachedField;
		throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + .getName());
	}

Removes a field so that it won't be serialized.
	public void removeField (String fieldName) {
		for (int i = 0; i < .i++) {
			CachedField cachedField = [i];
			if (cachedField.field.getName().equals(fieldName)) {
				CachedField[] newFields = new CachedField[. - 1];
				System.arraycopy(, 0, newFields, 0, i);
				System.arraycopy(i + 1, newFieldsinewFields.length - i);
				 = newFields;
				.add(cachedField);
				return;
			}
		}
		for (int i = 0; i < .i++) {
			CachedField cachedField = [i];
			if (cachedField.field.getName().equals(fieldName)) {
				CachedField[] newFields = new CachedField[. - 1];
				System.arraycopy(, 0, newFields, 0, i);
				System.arraycopy(i + 1, newFieldsinewFields.length - i);
				 = newFields;
				.add(cachedField);
				return;
			}
		}
		throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + .getName());
	}

Removes a field so that it won't be serialized.
	public void removeField (CachedField removeField) {
		for (int i = 0; i < .i++) {
			CachedField cachedField = [i];
			if (cachedField == removeField) {
				CachedField[] newFields = new CachedField[. - 1];
				System.arraycopy(, 0, newFields, 0, i);
				System.arraycopy(i + 1, newFieldsinewFields.length - i);
				 = newFields;
				.add(cachedField);
				return;
			}
		}
		for (int i = 0; i < .i++) {
			CachedField cachedField = [i];
			if (cachedField == removeField) {
				CachedField[] newFields = new CachedField[. - 1];
				System.arraycopy(, 0, newFields, 0, i);
				System.arraycopy(i + 1, newFieldsinewFields.length - i);
				 = newFields;
				.add(cachedField);
				return;
			}
		}
		throw new IllegalArgumentException("Field \"" + removeField + "\" not found on class: " + .getName());
	}

Get all fields controlled by this FieldSerializer

Returns:
all fields controlled by this FieldSerializer
	public CachedField[] getFields () {
		return ;
	}
	public Class getType () {
		return ;
	}
	public Kryo getKryo () {
		return ;
	}
	public boolean getUseAsmEnabled () {
	}
	public boolean getUseMemRegions () {
	}
	public boolean getCopyTransient () {
	}

Used by copy(com.esotericsoftware.kryo.Kryo,java.lang.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 com.esotericsoftware.kryo.Kryo.newInstance(java.lang.Class).
	protected T createCopy (Kryo kryo, T original) {
		return (T)kryo.newInstance(original.getClass());
	}
	public T copy (Kryo kryo, T original) {
copy = createCopy(kryooriginal);
		kryo.reference(copy);
		// Copy transient fields
			for (int i = 0, n = .i < ni++)
				[i].copy(originalcopy);
		}
		for (int i = 0, n = .i < ni++)
			[i].copy(originalcopy);
		return copy;
	}
	public final Generics getGenericsScope () {
	}

Controls how a field will be serialized.
	public static abstract class CachedField<X> {
		boolean canBeNull;
		int accessIndex = -1;
		long offset = -1;
		boolean varIntsEnabled = true;

Parameters:
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;
			this. = null;
		}

Parameters:
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 valueClassSerializer serializer) {
			this. = valueClass;
			this. = serializer;
		}
		public void setSerializer (Serializer serializer) {
			this. = serializer;
		}
		public Serializer getSerializer () {
			return this.;
		}
		public void setCanBeNull (boolean canBeNull) {
			this. = canBeNull;
		}
		public Field getField () {
			return ;
		}
		public String toString () {
			return .getName();
		}
		abstract public void write (Output outputObject object);
		abstract public void read (Input inputObject object);
		abstract public void copy (Object originalObject copy);
	}
	public static interface CachedFieldFactory {
		public CachedField createCachedField (Class fieldClassField fieldFieldSerializer ser);
	}

Indicates a field should be ignored when its declaring class is registered unless the 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(s):
Nathan Sweet <misc@n4te.com>
	static public @interface Optional {
		public String value();
	}

Used to annotate fields with a specific Kryo serializer.
	public @interface Bind {

Value.

Returns:
the class<? extends serializer> used for this field
		@SuppressWarnings("rawtypes")
		Class<? extends Serializervalue();
	}
New to GrepCode? Check out our FAQ X