com.dyuproject.protostuff.runtime.EnumIO Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protostuff-runtime Show documentation
Show all versions of protostuff-runtime Show documentation
protobuf serialization for pre-existing objects
The newest version!
//========================================================================
//Copyright 2007-2010 David Yu [email protected]
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package com.dyuproject.protostuff.runtime;
import static com.dyuproject.protostuff.runtime.RuntimeEnv.ENUMS_BY_NAME;
import java.io.IOException;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import com.dyuproject.protostuff.CollectionSchema;
import com.dyuproject.protostuff.EnumMapping;
import com.dyuproject.protostuff.Input;
import com.dyuproject.protostuff.MapSchema;
import com.dyuproject.protostuff.Output;
import com.dyuproject.protostuff.Pipe;
import com.dyuproject.protostuff.runtime.PolymorphicSchema.Handler;
/**
* Determines how enums are serialized/deserialized.
* Default is BY_NUMBER.
* To enable BY_NAME, set the property "protostuff.runtime.enums_by_name=true".
*
* @author David Yu
* @created Oct 20, 2010
*/
public abstract class EnumIO> implements PolymorphicSchema.Factory
{
public static abstract class Wrapper
{
public abstract EnumIO> get();
}
public static final class Eager extends Wrapper
{
public final EnumIO> eio;
public Eager(EnumIO> eio)
{
this.eio = eio;
}
public EnumIO> get()
{
return eio;
}
}
public static final class Lazy extends Wrapper
{
public final Class> enumClass;
public final IdStrategy strategy;
private volatile EnumIO> eio;
public Lazy(Class> enumClass, IdStrategy strategy)
{
this.enumClass = enumClass;
this.strategy = strategy;
}
public EnumIO> get()
{
EnumIO> eio = this.eio;
if (eio == null)
{
synchronized(this)
{
if (null == (eio = this.eio))
this.eio = eio = createFrom(enumClass, strategy);
}
}
return eio;
}
}
// Used by ObjectSchema to ser/deser both EnumMap and EnumSet.
private static final java.lang.reflect.Field __keyTypeFromEnumMap;
private static final java.lang.reflect.Field __elementTypeFromEnumSet;
static
{
boolean success = false;
java.lang.reflect.Field keyTypeFromMap = null, valueTypeFromSet = null;
try
{
keyTypeFromMap = EnumMap.class.getDeclaredField("keyType");
keyTypeFromMap.setAccessible(true);
valueTypeFromSet = EnumSet.class.getDeclaredField("elementType");
valueTypeFromSet.setAccessible(true);
success = true;
}
catch(Exception e)
{
// ignore
}
__keyTypeFromEnumMap = success ? keyTypeFromMap : null;
__elementTypeFromEnumSet = success ? valueTypeFromSet : null;
}
/**
* Retrieves the enum key type from the EnumMap via reflection.
* This is used by {@link ObjectSchema}.
*/
static Class> getKeyTypeFromEnumMap(Object enumMap)
{
if(__keyTypeFromEnumMap == null)
{
throw new RuntimeException("Could not access (reflection) the private " +
"field *keyType* (enumClass) from: class java.util.EnumMap");
}
try
{
return (Class>)__keyTypeFromEnumMap.get(enumMap);
}
catch (IllegalArgumentException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
/**
* Retrieves the enum key type from the EnumMap via reflection.
* This is used by {@link ObjectSchema}.
*/
static Class> getElementTypeFromEnumSet(Object enumSet)
{
if(__elementTypeFromEnumSet == null)
{
throw new RuntimeException("Could not access (reflection) the private " +
"field *elementType* (enumClass) from: class java.util.EnumSet");
}
try
{
return (Class>)__elementTypeFromEnumSet.get(enumSet);
}
catch (IllegalArgumentException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
public static Class> resolveEnumClass(Class> clazz)
{
Class> superClass = clazz.getSuperclass();
return superClass != null && superClass.isEnum() ? superClass : clazz;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
static EnumIO extends Enum>> createFrom(Class> clazz, IdStrategy strategy)
{
if (ENUMS_BY_NAME)
return new ByName((Class extends Enum>>)clazz, strategy);
EnumMapping mapping = EnumMapping.getOrCreateFrom(clazz);
final Class extends Enum>> enumClass = (Class extends Enum>>)clazz;
return mapping == null ? new ByName(enumClass, strategy) :
new ByMapping(enumClass, strategy, mapping);
}
private static > CollectionSchema.MessageFactory newEnumSetFactory(
final EnumIO eio)
{
return new CollectionSchema.MessageFactory()
{
@SuppressWarnings("unchecked")
public Collection newMessage()
{
return (Collection)eio.newEnumSet();
}
public Class> typeClass()
{
return EnumSet.class;
}
};
}
private static > MapSchema.MessageFactory newEnumMapFactory(
final EnumIO eio)
{
return new MapSchema.MessageFactory()
{
@SuppressWarnings("unchecked")
public Map newMessage()
{
return (Map)eio.newEnumMap();
}
public Class> typeClass()
{
return EnumMap.class;
}
};
}
/**
* The enum class.
*/
public final Class enumClass;
private volatile CollectionSchema.MessageFactory enumSetFactory;
private volatile MapSchema.MessageFactory enumMapFactory;
final ArraySchemas.Base genericElementSchema;
public EnumIO(Class enumClass, IdStrategy strategy)
{
this.enumClass = enumClass;
genericElementSchema = new ArraySchemas.EnumArray(strategy, null, this);
}
public PolymorphicSchema newSchema(Class> typeClass,
IdStrategy strategy, Handler handler)
{
return new ArraySchemas.EnumArray(strategy, handler, this);
}
/**
* Returns the factory for an EnumSet (lazy).
*/
public CollectionSchema.MessageFactory getEnumSetFactory()
{
CollectionSchema.MessageFactory enumSetFactory = this.enumSetFactory;
if(enumSetFactory == null)
{
synchronized(this)
{
if((enumSetFactory = this.enumSetFactory) == null)
this.enumSetFactory = enumSetFactory = newEnumSetFactory(this);
}
}
return enumSetFactory;
}
/**
* Returns the factory for an EnumMap (lazy).
*/
public MapSchema.MessageFactory getEnumMapFactory()
{
MapSchema.MessageFactory enumMapFactory = this.enumMapFactory;
if(enumMapFactory == null)
{
synchronized(this)
{
if((enumMapFactory = this.enumMapFactory) == null)
this.enumMapFactory = enumMapFactory = newEnumMapFactory(this);
}
}
return enumMapFactory;
}
/**
* Returns an empty {@link EnumSet}.
*/
public EnumSet newEnumSet()
{
return EnumSet.noneOf(enumClass);
}
/**
* Returns an empty {@link EnumMap}.
*/
public EnumMap newEnumMap()
{
return new EnumMap(enumClass);
}
/**
* Read the enum from the input.
*/
public abstract E readFrom(Input input) throws IOException;
/**
* Writes the {@link Enum} to the output.
*/
public abstract void writeTo(Output output, int number, Enum> e, boolean repeated) throws IOException;
/**
* Transfers the {@link Enum} from the input to the output.
*/
public abstract void transfer(Pipe pipe, Input input, Output output,
int number, boolean repeated) throws IOException;
/**
* Reads the enum by its name.
*
*/
public static final class ByName> extends EnumIO
{
public ByName(Class enumClass, IdStrategy strategy)
{
super(enumClass, strategy);
}
public E readFrom(Input input) throws IOException
{
try
{
return Enum.valueOf(enumClass, input.readString());
}
catch (IllegalArgumentException e)
{
return null;
}
}
@Override
public void writeTo(Output output, int number, Enum> e, boolean repeated) throws IOException
{
output.writeString(number, e.name(), repeated);
}
@Override
public void transfer(Pipe pipe, Input input, Output output, int number, boolean repeated)
throws IOException
{
input.transferByteRangeTo(output, true, number, repeated);
}
}
public static final class ByMapping> extends EnumIO
{
final EnumMapping mapping;
public ByMapping(Class enumClass, IdStrategy strategy, EnumMapping mapping)
{
super(enumClass, strategy);
this.mapping = mapping;
}
@Override
public E readFrom(Input input) throws IOException
{
int idx = input.readEnumIdx(mapping);
return idx == -1 ? null : enumClass.getEnumConstants()[idx];
}
@Override
public void writeTo(Output output, int number, Enum> e, boolean repeated) throws IOException
{
output.writeEnumFromIdx(number, e.ordinal(), mapping, repeated);
}
@Override
public void transfer(Pipe pipe, Input input, Output output, int number, boolean repeated)
throws IOException
{
input.transferEnumTo(output, mapping, number, repeated);
}
}
}