io.protostuff.runtime.IncrementalIdStrategy Maven / Gradle / Ivy
//================================================================================
//Copyright (c) 2012, David Yu
//All rights reserved.
//--------------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 2. 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.
// 3. Neither the name of protostuff 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 OWNER 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 io.protostuff.runtime;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
import io.protostuff.CollectionSchema;
import io.protostuff.Input;
import io.protostuff.MapSchema;
import io.protostuff.Message;
import io.protostuff.Output;
import io.protostuff.Pipe;
import io.protostuff.Schema;
/**
* The ids are generated (incremental) on the fly and you can optionally register classes by reserving the first x ids
* via {@link Registry}. To minimize overhead, {@link ArrayList}s are used for the id mapping rather than
* {@link ConcurrentHashMap}. This optimization has a disadvantage though. Ids will not be unlimited. You'll have to
* specificy a max id for the 4 types (pojo, enum, collection, map)
*
* @author David Yu
* @created Mar 27, 2012
*/
public final class IncrementalIdStrategy extends NumericIdStrategy
{
/**
* To use {@link IncrementalIdStrategy} without registering anything, set the system property:
* "-Dprotostuff.runtime.id_strategy_factory=io.protostuff.runtime.IncrementalIdStrategy$Factory"
*
* Note that the pojos will be limited to 63 and the enums to 15.
*
* It is best that you use the {@link Registry} to configure the strategy and set the max limits for each type.
*/
public static class Factory implements IdStrategy.Factory
{
@Override
public IdStrategy create()
{
return new IncrementalIdStrategy(
CollectionSchema.MessageFactories.values().length, 1,
MapSchema.MessageFactories.values().length, 1,
16, 1, // enums
64, 1); // pojos
}
@Override
public void postCreate()
{
}
}
/**
* This Registry is only way to register your pojos/enums/collections/maps/delegates.
*/
public static class Registry implements NumericIdStrategy.Registry
{
public final IncrementalIdStrategy strategy;
public Registry(
int collectionIdMax, int collectionIdStart,
int mapIdMax, int mapIdStart,
int enumIdMax, int enumIdStart,
int pojoIdMax, int pojoIdStart)
{
this(DEFAULT_FLAGS, null, 0,
collectionIdMax, collectionIdStart,
mapIdMax, mapIdStart,
enumIdMax, enumIdStart,
pojoIdMax, pojoIdStart);
}
public Registry(
int flags,
IdStrategy primaryGroup, int groupId,
int collectionIdMax, int collectionIdStart,
int mapIdMax, int mapIdStart,
int enumIdMax, int enumIdStart,
int pojoIdMax, int pojoIdStart)
{
strategy = new IncrementalIdStrategy(
flags,
primaryGroup, groupId,
collectionIdMax, collectionIdStart,
mapIdMax, mapIdStart,
enumIdMax, enumIdStart,
pojoIdMax, pojoIdStart);
}
/**
* Collection ids start at 1.
*/
@Override
public > Registry registerCollection(
CollectionSchema.MessageFactory factory, int id)
{
if (id < 1)
throw new IllegalArgumentException("collection ids start at 1.");
if (id >= strategy.collectionIdStart)
throw new IllegalArgumentException("collection ids must be lesser than " + strategy.collectionIdStart);
else if (strategy.collections.get(id) != null)
{
throw new IllegalArgumentException("Duplicate id registration: " + id +
" (" + factory.typeClass() + ")");
}
RuntimeCollectionFactory rf = new RuntimeCollectionFactory();
rf.id = id;
rf.factory = factory;
strategy.collections.set(id, rf);
// just in case
if (strategy.collectionMapping.put(factory.typeClass(), rf) != null)
throw new IllegalArgumentException("Duplicate registration for: " + factory.typeClass());
return this;
}
/**
* Map ids start at 1.
*/
@Override
public > Registry registerMap(
MapSchema.MessageFactory factory, int id)
{
if (id < 1)
throw new IllegalArgumentException("map ids start at 1.");
if (id >= strategy.mapIdStart)
throw new IllegalArgumentException("map ids must be lesser than " + strategy.mapIdStart);
else if (strategy.maps.get(id) != null)
{
throw new IllegalArgumentException("Duplicate id registration: " + id +
" (" + factory.typeClass() + ")");
}
RuntimeMapFactory rf = new RuntimeMapFactory();
rf.id = id;
rf.factory = factory;
strategy.maps.set(id, rf);
// just in case
if (strategy.mapMapping.put(factory.typeClass(), rf) != null)
throw new IllegalArgumentException("Duplicate registration for: " + factory.typeClass());
return this;
}
/**
* Enum ids start at 1.
*/
@Override
public > Registry registerEnum(Class clazz, int id)
{
if (id < 1)
throw new IllegalArgumentException("enum ids start at 1.");
if (id >= strategy.enumIdStart)
throw new IllegalArgumentException("enum ids must be lesser than " + strategy.enumIdStart);
else if (strategy.enums.get(id) != null)
{
throw new IllegalArgumentException("Duplicate id registration: " + id +
" (" + clazz.getName() + ")");
}
EnumIO> eio = EnumIO.newEnumIO(clazz, strategy);
RuntimeEnumIO reio = new RuntimeEnumIO();
reio.id = id;
reio.eio = eio;
strategy.enums.set(id, reio);
// just in case
if (strategy.enumMapping.put(clazz, reio) != null)
throw new IllegalArgumentException("Duplicate registration for: " + clazz);
return this;
}
/**
* Enum ids start at 1.
*/
@Override
public Registry registerEnum(EnumIO> eio, int id)
{
if (id < 1)
throw new IllegalArgumentException("enum ids start at 1.");
if (id >= strategy.enumIdStart)
throw new IllegalArgumentException("enum ids must be lesser than " + strategy.enumIdStart);
else if (strategy.enums.get(id) != null)
{
throw new IllegalArgumentException("Duplicate id registration: " + id +
" (" + eio.enumClass.getName() + ")");
}
RuntimeEnumIO reio = new RuntimeEnumIO();
reio.id = id;
reio.eio = eio;
strategy.enums.set(id, reio);
// just in case
if (strategy.enumMapping.put(eio.enumClass, reio) != null)
throw new IllegalArgumentException("Duplicate registration for: " + eio.enumClass);
return this;
}
/**
* Pojo ids start at 1.
*/
@Override
public Registry registerPojo(Class clazz, int id)
{
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()))
throw new IllegalArgumentException("Not a concrete class: " + clazz);
if (id < 1)
throw new IllegalArgumentException("pojo ids start at 1.");
if (id >= strategy.pojoIdStart)
throw new IllegalArgumentException("pojo ids must be lesser than " + strategy.pojoIdStart);
else if (strategy.pojos.get(id) != null)
{
throw new IllegalArgumentException("Duplicate id registration: " + id +
" (" + clazz.getName() + ")");
}
if (strategy.pojoMapping.containsKey(clazz))
throw new IllegalArgumentException("Duplicate registration for: " + clazz);
BaseHS wrapper = new Lazy<>(clazz, strategy);
wrapper.id = id;
strategy.pojos.set(id, wrapper);
strategy.pojoMapping.put(clazz, wrapper);
return this;
}
/**
* Pojo ids start at 1.
*/
@Override
public Registry registerPojo(Schema schema, Pipe.Schema pipeSchema,
int id)
{
if (id >= strategy.pojoIdStart)
throw new IllegalArgumentException("pojo ids must be lesser than " + strategy.pojoIdStart);
else if (strategy.pojos.get(id) != null)
{
throw new IllegalArgumentException("Duplicate id registration: " + id +
" (" + schema.typeClass().getName() + ")");
}
if (strategy.pojoMapping.containsKey(schema.typeClass()))
throw new IllegalArgumentException("Duplicate registration for: " + schema.typeClass());
Registered wrapper = new Registered<>(id, schema, pipeSchema, strategy);
strategy.pojos.set(id, wrapper);
strategy.pojoMapping.put(schema.typeClass(), wrapper);
return this;
}
/**
* If you are sure that you are only using a single implementation of your interface/abstract class, then it
* makes sense to map it directly to its impl class to avoid writing the type.
*
* Note that the type is always written when your field is {@link java.lang.Object}.
*
* Pojo ids start at 1.
*/
@Override
public Registry mapPojo(Class super T> baseClass, Class implClass)
{
if (strategy.pojoMapping.containsKey(baseClass))
throw new IllegalArgumentException("Duplicate registration for: " + baseClass);
BaseHS> wrapper = strategy.pojoMapping.get(implClass);
if (wrapper == null)
throw new IllegalArgumentException("Must register the impl class first. " + implClass);
strategy.pojoMapping.put(baseClass, wrapper);
return this;
}
/**
* Register a {@link Delegate} and assign an id.
*
* Delegate ids start at 1.
*/
@Override
public Registry registerDelegate(Delegate delegate, int id)
{
if (id < 1)
throw new IllegalArgumentException("delegate ids start at 1.");
if (id >= strategy.delegates.size())
grow(strategy.delegates, id + 1);
else if (strategy.delegates.get(id) != null)
{
throw new IllegalArgumentException("Duplicate id registration: " + id +
" (" + delegate.typeClass() + ")");
}
RegisteredDelegate rd = new RegisteredDelegate<>(id, delegate, strategy);
strategy.delegates.set(id, rd);
// just in case
if (strategy.delegateMapping.put(delegate.typeClass(), rd) != null)
throw new IllegalArgumentException("Duplicate registration for: " + delegate.typeClass());
return this;
}
}
final ConcurrentHashMap, RuntimeCollectionFactory> collectionMapping;
final ArrayList collections;
final ConcurrentHashMap, RuntimeMapFactory> mapMapping;
final ArrayList maps;
final ConcurrentHashMap, RuntimeEnumIO> enumMapping;
final ArrayList enums;
final ConcurrentHashMap, BaseHS>> pojoMapping;
final ArrayList> pojos;
final IdentityHashMap, RegisteredDelegate>> delegateMapping;
final ArrayList> delegates;
final AtomicInteger pojoId, enumId, collectionId, mapId;
final int pojoIdStart, enumIdStart, collectionIdStart, mapIdStart;
public IncrementalIdStrategy(
int collectionIdMax, int collectionIdStart,
int mapIdMax, int mapIdStart,
int enumIdMax, int enumIdStart,
int pojoIdMax, int pojoIdStart)
{
this(DEFAULT_FLAGS, null, 0,
collectionIdMax, collectionIdStart,
mapIdMax, mapIdStart,
enumIdMax, enumIdStart,
pojoIdMax, pojoIdStart);
}
public IncrementalIdStrategy(
int flags,
IdStrategy primaryGroup, int groupId,
int collectionIdMax, int collectionIdStart,
int mapIdMax, int mapIdStart,
int enumIdMax, int enumIdStart,
int pojoIdMax, int pojoIdStart)
// int delegateIdMax, int delegateIdStart)
{
super(flags, primaryGroup, groupId);
assert collectionIdMax > collectionIdStart;
assert mapIdMax > mapIdStart;
assert enumIdMax > enumIdStart;
assert pojoIdMax > pojoIdStart;
// assert delegateIdMax > delegateIdStart;
this.collectionIdStart = collectionIdStart;
collectionId = new AtomicInteger(collectionIdStart);
collectionMapping = new ConcurrentHashMap<>(
collectionIdMax);
collections = newList(collectionIdMax + 1);
this.mapIdStart = mapIdStart;
mapId = new AtomicInteger(mapIdStart);
mapMapping = new ConcurrentHashMap<>(mapIdMax);
maps = newList(mapIdMax + 1);
this.enumIdStart = enumIdStart;
enumId = new AtomicInteger(enumIdStart);
enumMapping = new ConcurrentHashMap<>(enumIdMax);
enums = newList(enumIdMax + 1);
this.pojoIdStart = pojoIdStart;
pojoId = new AtomicInteger(pojoIdStart);
pojoMapping = new ConcurrentHashMap<>(pojoIdMax);
pojos = newList(pojoIdMax + 1);
// delegates require explicit registration
delegateMapping = new IdentityHashMap<>(
10);// delegateIdMax);
delegates = newList(11);// delegateIdMax + 1);
}
@Override
public boolean isRegistered(Class> typeClass)
{
return pojoMapping.get(typeClass) instanceof Registered;
}
@SuppressWarnings("unchecked")
private BaseHS getBaseHS(Class typeClass, boolean create)
{
BaseHS hs = (BaseHS) pojoMapping.get(typeClass);
if (hs == null && create)
{
hs = new Lazy<>(typeClass, this);
final BaseHS last = (BaseHS) pojoMapping.putIfAbsent(
typeClass, hs);
if (last != null)
hs = last;
else
{
int id = pojoId.getAndIncrement();
pojos.set(id, hs);
// commit
hs.id = id;
}
}
return hs;
}
@Override
public HasSchema getSchemaWrapper(Class typeClass, boolean create)
{
return getBaseHS(typeClass, create);
}
private RuntimeEnumIO getRuntimeEnumIO(Class> enumClass)
{
RuntimeEnumIO reio = enumMapping.get(enumClass);
if (reio == null)
{
reio = new RuntimeEnumIO();
final RuntimeEnumIO existing = enumMapping.putIfAbsent(enumClass, reio);
if (existing != null)
reio = existing;
else
{
reio.eio = EnumIO.newEnumIO(enumClass, this);
int id = enumId.getAndIncrement();
enums.set(id, reio);
// commit
reio.id = id;
}
}
return reio;
}
@Override
protected EnumIO extends Enum>> getEnumIO(Class> enumClass)
{
return getRuntimeEnumIO(enumClass).eio;
}
private RuntimeCollectionFactory getRuntimeCollectionFactory(Class> clazz)
{
RuntimeCollectionFactory rfactory = collectionMapping.get(clazz);
if (rfactory == null)
{
rfactory = new RuntimeCollectionFactory();
RuntimeCollectionFactory f = collectionMapping.putIfAbsent(
clazz, rfactory);
if (f != null)
rfactory = f;
else
{
if (clazz.getName().startsWith("java.util"))
{
rfactory.factory = CollectionSchema.MessageFactories.valueOf(
clazz.getSimpleName());
}
else
{
rfactory.instantiator = RuntimeEnv.newInstantiator(clazz);
rfactory.collectionClass = clazz;
}
int id = collectionId.getAndIncrement();
collections.set(id, rfactory);
// commit
rfactory.id = id;
}
}
return rfactory;
}
@Override
protected CollectionSchema.MessageFactory getCollectionFactory(Class> clazz)
{
return getRuntimeCollectionFactory(clazz);
}
private RuntimeMapFactory getRuntimeMapFactory(Class> clazz)
{
RuntimeMapFactory rfactory = mapMapping.get(clazz);
if (rfactory == null)
{
rfactory = new RuntimeMapFactory();
RuntimeMapFactory f = mapMapping.putIfAbsent(
clazz, rfactory);
if (f != null)
rfactory = f;
else
{
if (clazz.getName().startsWith("java.util"))
{
rfactory.factory = MapSchema.MessageFactories.valueOf(
clazz.getSimpleName());
}
else
{
rfactory.instantiator = RuntimeEnv.newInstantiator(clazz);
rfactory.mapClass = clazz;
}
int id = mapId.getAndIncrement();
maps.set(id, rfactory);
// commit
rfactory.id = id;
}
}
return rfactory;
}
@Override
protected MapSchema.MessageFactory getMapFactory(Class> clazz)
{
return getRuntimeMapFactory(clazz);
}
@Override
protected void writeCollectionIdTo(Output output, int fieldNumber, Class> clazz)
throws IOException
{
int id;
RuntimeCollectionFactory factory = getRuntimeCollectionFactory(clazz);
// wait till everything is completely set
while (0 == (id = factory.id))
LockSupport.parkNanos(1);
output.writeUInt32(fieldNumber, id, false);
}
@Override
protected void transferCollectionId(Input input, Output output, int fieldNumber)
throws IOException
{
output.writeUInt32(fieldNumber, input.readUInt32(), false);
}
@Override
protected CollectionSchema.MessageFactory resolveCollectionFrom(Input input)
throws IOException
{
final int id = input.readUInt32();
final RuntimeCollectionFactory factory = id < collections.size() ?
collections.get(id) : null;
if (factory == null)
throw new UnknownTypeException("Unknown collection id: " + id);
return factory;
}
@Override
protected void writeMapIdTo(Output output, int fieldNumber, Class> clazz)
throws IOException
{
int id;
RuntimeMapFactory factory = getRuntimeMapFactory(clazz);
// wait till everything is completely set
while (0 == (id = factory.id))
LockSupport.parkNanos(1);
output.writeUInt32(fieldNumber, id, false);
}
@Override
protected void transferMapId(Input input, Output output, int fieldNumber)
throws IOException
{
output.writeUInt32(fieldNumber, input.readUInt32(), false);
}
@Override
protected MapSchema.MessageFactory resolveMapFrom(Input input)
throws IOException
{
final int id = input.readUInt32();
final RuntimeMapFactory factory = id < maps.size() ? maps.get(id) : null;
if (factory == null)
throw new UnknownTypeException("Unknown map id: " + id);
return factory;
}
@Override
protected void writeEnumIdTo(Output output, int fieldNumber, Class> clazz)
throws IOException
{
int id;
RuntimeEnumIO reio = getRuntimeEnumIO(clazz);
// wait till everything is completely set
while (0 == (id = reio.id))
LockSupport.parkNanos(1);
output.writeUInt32(fieldNumber, id, false);
}
@Override
protected void transferEnumId(Input input, Output output, int fieldNumber)
throws IOException
{
output.writeUInt32(fieldNumber, input.readUInt32(), false);
}
@Override
protected EnumIO> resolveEnumFrom(Input input) throws IOException
{
final int id = input.readUInt32();
final RuntimeEnumIO reio = id < enums.size() ? enums.get(id) : null;
if (reio == null)
throw new UnknownTypeException("Unknown enum id: " + id);
return reio.eio;
}
@Override
public boolean isDelegateRegistered(Class> typeClass)
{
return delegateMapping.containsKey(typeClass);
}
@Override
@SuppressWarnings("unchecked")
public Delegate getDelegate(Class super T> typeClass)
{
final RegisteredDelegate rd = (RegisteredDelegate) delegateMapping.get(
typeClass);
return rd == null ? null : rd.delegate;
}
@Override
@SuppressWarnings("unchecked")
public HasDelegate getDelegateWrapper(Class super T> typeClass)
{
return (HasDelegate) delegateMapping.get(typeClass);
}
@Override
@SuppressWarnings("unchecked")
protected HasDelegate tryWriteDelegateIdTo(Output output, int fieldNumber,
Class clazz) throws IOException
{
final RegisteredDelegate rd = (RegisteredDelegate) delegateMapping.get(
clazz);
if (rd == null)
return null;
output.writeUInt32(fieldNumber, rd.id, false);
return rd;
}
@Override
@SuppressWarnings("unchecked")
protected HasDelegate transferDelegateId(Input input, Output output, int fieldNumber)
throws IOException
{
final int id = input.readUInt32();
final RegisteredDelegate rd = id < delegates.size() ?
(RegisteredDelegate) delegates.get(id) : null;
if (rd == null)
throw new UnknownTypeException("delegate id: " + id + " (Outdated registry)");
output.writeUInt32(fieldNumber, id, false);
return rd;
}
@Override
@SuppressWarnings("unchecked")
protected HasDelegate resolveDelegateFrom(Input input) throws IOException
{
final int id = input.readUInt32();
final RegisteredDelegate rd = id < delegates.size() ?
(RegisteredDelegate) delegates.get(id) : null;
if (rd == null)
throw new UnknownTypeException("delegate id: " + id + " (Outdated registry)");
return rd;
}
@Override
protected HasSchema tryWritePojoIdTo(Output output, int fieldNumber,
Class clazz, boolean registered) throws IOException
{
BaseHS wrapper = getBaseHS(clazz, false);
if (wrapper == null || (registered && !(wrapper instanceof Registered)))
return null;
int id;
// wait till everything is completely set
while (0 == (id = wrapper.id))
LockSupport.parkNanos(1);
output.writeUInt32(fieldNumber, id, false);
return wrapper;
}
@Override
protected HasSchema writePojoIdTo(Output output, int fieldNumber, Class clazz)
throws IOException
{
int id;
BaseHS wrapper = getBaseHS(clazz, true);
// wait till everything is completely set
while (0 == (id = wrapper.id))
LockSupport.parkNanos(1);
output.writeUInt32(fieldNumber, id, false);
return wrapper;
}
@Override
@SuppressWarnings("unchecked")
protected HasSchema transferPojoId(Input input, Output output, int fieldNumber)
throws IOException
{
final int id = input.readUInt32();
final BaseHS wrapper = id < pojos.size() ? (BaseHS) pojos.get(id) : null;
if (wrapper == null)
throw new UnknownTypeException("unknown pojo id: " + id);
output.writeUInt32(fieldNumber, id, false);
return wrapper;
}
@Override
@SuppressWarnings("unchecked")
protected HasSchema resolvePojoFrom(Input input, int fieldNumber) throws IOException
{
final int id = input.readUInt32();
final BaseHS wrapper = id < pojos.size() ? (BaseHS) pojos.get(id) : null;
if (wrapper == null)
throw new UnknownTypeException("unknown pojo id: " + id);
return wrapper;
}
@Override
@SuppressWarnings("unchecked")
protected Schema writeMessageIdTo(Output output, int fieldNumber,
Message message) throws IOException
{
int id;
BaseHS wrapper = (BaseHS) getSchemaWrapper(message.getClass(), true);
// wait till everything is completely set
while (0 == (id = wrapper.id))
LockSupport.parkNanos(1);
output.writeUInt32(fieldNumber, id, false);
// TODO allow the wrapper to return an override schema?
return message.cachedSchema();
}
@Override
protected Class> collectionClass(int id)
{
final RuntimeCollectionFactory factory = id < collections.size() ?
collections.get(id) : null;
if (factory == null)
throw new UnknownTypeException("Unknown collection id: " + id);
return factory.typeClass();
}
@Override
protected Class> mapClass(int id)
{
final RuntimeMapFactory factory = id < maps.size() ? maps.get(id) : null;
if (factory == null)
throw new UnknownTypeException("Unknown map id: " + id);
return factory.typeClass();
}
@Override
protected Class> enumClass(int id)
{
final RuntimeEnumIO reio = id < enums.size() ? enums.get(id) : null;
if (reio == null)
throw new UnknownTypeException("Unknown enum id: " + id);
return reio.eio.enumClass;
}
@Override
protected Class> delegateClass(int id)
{
final RegisteredDelegate> rd = id < delegates.size() ? delegates.get(id) : null;
return rd == null ? null : rd.delegate.typeClass();
}
@Override
protected Class> pojoClass(int id)
{
final BaseHS> wrapper = id < pojos.size() ? pojos.get(id) : null;
if (wrapper == null)
throw new UnknownTypeException("Unknown pojo id: " + id);
return wrapper.getSchema().typeClass();
}
@Override
protected RegisteredDelegate> getRegisteredDelegate(Class> clazz)
{
return delegateMapping.get(clazz);
}
@Override
protected int getEnumId(Class> clazz)
{
final RuntimeEnumIO reio = getRuntimeEnumIO(clazz);
// wait till everything is completely set
int id;
while (0 == (id = reio.id))
LockSupport.parkNanos(1);
return (id << 5) | CID_ENUM;
}
@Override
protected int getId(Class> clazz)
{
int id;
if (Message.class.isAssignableFrom(clazz))
{
final BaseHS> wrapper = getBaseHS(clazz, true);
// wait till everything is completely set
while (0 == (id = wrapper.id))
LockSupport.parkNanos(1);
return (id << 5) | CID_POJO;
}
if (Map.class.isAssignableFrom(clazz))
{
if (EnumMap.class.isAssignableFrom(clazz))
return CID_ENUM_MAP;
final RuntimeMapFactory factory = getRuntimeMapFactory(clazz);
// wait till everything is completely set
while (0 == (id = factory.id))
LockSupport.parkNanos(1);
return (id << 5) | CID_MAP;
}
if (Collection.class.isAssignableFrom(clazz))
{
if (EnumSet.class.isAssignableFrom(clazz))
return CID_ENUM_SET;
final RuntimeCollectionFactory factory = getRuntimeCollectionFactory(clazz);
// wait till everything is completely set
while (0 == (id = factory.id))
LockSupport.parkNanos(1);
return (id << 5) | CID_COLLECTION;
}
final BaseHS> wrapper = getBaseHS(clazz, true);
// wait till everything is completely set
while (0 == (id = wrapper.id))
LockSupport.parkNanos(1);
return (id << 5) | CID_POJO;
}
static final class RuntimeCollectionFactory implements CollectionSchema.MessageFactory
{
volatile int id;
CollectionSchema.MessageFactory factory;
Class> collectionClass;
RuntimeEnv.Instantiator> instantiator;
@Override
@SuppressWarnings("unchecked")
public Collection newMessage()
{
if (factory == null)
return (Collection) instantiator.newInstance();
return factory.newMessage();
}
@Override
public Class> typeClass()
{
return factory == null ? collectionClass : factory.typeClass();
}
}
static final class RuntimeMapFactory implements MapSchema.MessageFactory
{
volatile int id;
MapSchema.MessageFactory factory;
Class> mapClass;
RuntimeEnv.Instantiator> instantiator;
@Override
@SuppressWarnings("unchecked")
public Map newMessage()
{
if (factory == null)
return (Map) instantiator.newInstance();
return factory.newMessage();
}
@Override
public Class> typeClass()
{
return factory == null ? mapClass : factory.typeClass();
}
}
static final class RuntimeEnumIO
{
volatile int id;
EnumIO> eio;
}
static abstract class BaseHS extends HasSchema
{
volatile int id;
protected BaseHS(IdStrategy strategy)
{
super(strategy);
}
}
static final class Registered extends BaseHS
{
final Schema schema;
final Pipe.Schema pipeSchema;
Registered(int id, Schema schema, Pipe.Schema pipeSchema,
IdStrategy strategy)
{
super(strategy);
this.id = id;
this.schema = schema;
this.pipeSchema = pipeSchema;
}
@Override
public Schema getSchema()
{
return schema;
}
@Override
public io.protostuff.Pipe.Schema getPipeSchema()
{
return pipeSchema;
}
}
static final class Lazy extends BaseHS
{
final Class typeClass;
private volatile Schema schema;
private volatile Pipe.Schema pipeSchema;
Lazy(Class typeClass, IdStrategy strategy)
{
super(strategy);
this.typeClass = typeClass;
}
@Override
@SuppressWarnings("unchecked")
public Schema getSchema()
{
Schema schema = this.schema;
if (schema == null)
{
synchronized (this)
{
if ((schema = this.schema) == null)
{
if (Message.class.isAssignableFrom(typeClass))
{
// use the message's schema.
final Message m = (Message) createMessageInstance(typeClass);
this.schema = schema = m.cachedSchema();
}
else
{
// create new
this.schema = schema = strategy.newSchema(typeClass);
}
}
}
}
return schema;
}
@Override
public Pipe.Schema getPipeSchema()
{
Pipe.Schema pipeSchema = this.pipeSchema;
if (pipeSchema == null)
{
synchronized (this)
{
if ((pipeSchema = this.pipeSchema) == null)
{
this.pipeSchema = pipeSchema = RuntimeSchema.resolvePipeSchema(
getSchema(), typeClass, true);
}
}
}
return pipeSchema;
}
}
}