Convert Narya (most of the way) over to a Maven Ant task based build. The
ActionScript bits remain belligerent, but the Java stuff is mostly shipshape. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6222 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.io;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ReflectPermission;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import static com.threerings.NaryaLog.log;
|
||||
|
||||
/**
|
||||
* Used to read and write a single field of a {@link Streamable} instance.
|
||||
*/
|
||||
public abstract class FieldMarshaller
|
||||
{
|
||||
public FieldMarshaller ()
|
||||
{
|
||||
this(null);
|
||||
}
|
||||
|
||||
public FieldMarshaller (String type)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of the supplied field from the supplied stream and sets it in the
|
||||
* supplied object.
|
||||
*/
|
||||
public abstract void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Writes the contents of the supplied field in the supplied object to the supplied stream.
|
||||
*/
|
||||
public abstract void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception;
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
if (_type != null) {
|
||||
return "FieldMarshaller " + _type;
|
||||
}
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
protected final String _type;
|
||||
|
||||
/**
|
||||
* Returns a field marshaller appropriate for the supplied field or null if no marshaller
|
||||
* exists for the type contained by the field in question.
|
||||
*/
|
||||
public static FieldMarshaller getFieldMarshaller (Field field)
|
||||
{
|
||||
if (_marshallers == null) {
|
||||
createMarshallers();
|
||||
}
|
||||
|
||||
// if necessary (we're running in a sandbox), look for custom field accessors
|
||||
if (useFieldAccessors()) {
|
||||
Method reader = null, writer = null;
|
||||
try {
|
||||
reader = field.getDeclaringClass().getMethod(
|
||||
getReaderMethodName(field.getName()), READER_ARGS);
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
// no problem
|
||||
}
|
||||
try {
|
||||
writer = field.getDeclaringClass().getMethod(
|
||||
getWriterMethodName(field.getName()), WRITER_ARGS);
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
// no problem
|
||||
}
|
||||
if (reader != null && writer != null) {
|
||||
return new MethodFieldMarshaller(reader, writer);
|
||||
}
|
||||
if ((reader == null && writer != null) || (writer == null && reader != null)) {
|
||||
log.warning("Class contains one but not both custom field reader and writer",
|
||||
"class", field.getDeclaringClass().getName(), "field", field.getName(),
|
||||
"reader", reader, "writer", writer);
|
||||
// fall through to using reflection on the fields...
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> ftype = field.getType();
|
||||
|
||||
// use the intern marshaller for pooled strings
|
||||
if (ftype == String.class && field.isAnnotationPresent(Intern.class)) {
|
||||
return _internMarshaller;
|
||||
}
|
||||
|
||||
// if we have an exact match, use that
|
||||
FieldMarshaller fm = _marshallers.get(ftype);
|
||||
|
||||
// otherwise if the class is a pure interface or streamable, use the streamable marshaller
|
||||
if (fm == null && (ftype.isInterface() || Streamer.isStreamable(ftype))) {
|
||||
fm = _marshallers.get(Streamable.class);
|
||||
}
|
||||
|
||||
return fm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the custom reader method which will be used if it exists to stream a
|
||||
* field with the supplied name.
|
||||
*/
|
||||
public static final String getReaderMethodName (String field)
|
||||
{
|
||||
return "readField_" + field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the custom writer method which will be used if it exists to stream a
|
||||
* field with the supplied name.
|
||||
*/
|
||||
public static final String getWriterMethodName (String field)
|
||||
{
|
||||
return "writeField_" + field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we should use the generated field marshaller methods that allow us to work
|
||||
* around our inability to read and write protected and private fields of a {@link Streamable}.
|
||||
*/
|
||||
protected static boolean useFieldAccessors ()
|
||||
{
|
||||
try {
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security != null) {
|
||||
security.checkPermission(new ReflectPermission("suppressAccessChecks"));
|
||||
}
|
||||
return false;
|
||||
} catch (SecurityException se) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to marshall and unmarshall classes for which we have a basic {@link Streamer}.
|
||||
*/
|
||||
protected static class StreamerMarshaller extends FieldMarshaller
|
||||
{
|
||||
public StreamerMarshaller (Streamer streamer)
|
||||
{
|
||||
_streamer = streamer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception
|
||||
{
|
||||
if (in.readBoolean()) {
|
||||
Object value = _streamer.createObject(in);
|
||||
_streamer.readObject(value, in, true);
|
||||
field.set(target, value);
|
||||
} else {
|
||||
field.set(target, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception
|
||||
{
|
||||
Object value = field.get(source);
|
||||
if (value == null) {
|
||||
out.writeBoolean(false);
|
||||
} else {
|
||||
out.writeBoolean(true);
|
||||
_streamer.writeObject(value, out, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return "StreamerMarshaller:" + _streamer.toString();
|
||||
}
|
||||
|
||||
/** The streamer we use to read and write our field. */
|
||||
protected Streamer _streamer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses custom accessor methods to read and write a field.
|
||||
*/
|
||||
protected static class MethodFieldMarshaller extends FieldMarshaller
|
||||
{
|
||||
public MethodFieldMarshaller (Method reader, Method writer)
|
||||
{
|
||||
_reader = reader;
|
||||
_writer = writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception
|
||||
{
|
||||
_reader.invoke(target, in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception
|
||||
{
|
||||
_writer.invoke(source, out);
|
||||
}
|
||||
|
||||
protected Method _reader, _writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instances of all known field marshaller types and populates the {@link
|
||||
* #_marshallers} table with them. This is called the first time a marshaller is requested.
|
||||
*/
|
||||
protected static void createMarshallers ()
|
||||
{
|
||||
// create our table
|
||||
_marshallers = Maps.newHashMap();
|
||||
|
||||
// create a generic marshaller for streamable instances
|
||||
FieldMarshaller gmarsh = new FieldMarshaller("Generic") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.set(target, in.readObject());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeObject(field.get(source));
|
||||
}
|
||||
};
|
||||
_marshallers.put(Streamable.class, gmarsh);
|
||||
|
||||
// use the same generic marshaller for fields declared as Object with the expectation that
|
||||
// they will contain only primitive types or Streamables; the runtime will fail
|
||||
// informatively if we attempt to store non-Streamable objects in that field
|
||||
_marshallers.put(Object.class, gmarsh);
|
||||
|
||||
// create marshallers for the primitive types
|
||||
_marshallers.put(Boolean.TYPE, new FieldMarshaller("boolean") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.setBoolean(target, in.readBoolean());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeBoolean(field.getBoolean(source));
|
||||
}
|
||||
});
|
||||
_marshallers.put(Byte.TYPE, new FieldMarshaller("byte") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.setByte(target, in.readByte());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeByte(field.getByte(source));
|
||||
}
|
||||
});
|
||||
_marshallers.put(Character.TYPE, new FieldMarshaller("char") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.setChar(target, in.readChar());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeChar(field.getChar(source));
|
||||
}
|
||||
});
|
||||
_marshallers.put(Short.TYPE, new FieldMarshaller("short") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.setShort(target, in.readShort());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeShort(field.getShort(source));
|
||||
}
|
||||
});
|
||||
_marshallers.put(Integer.TYPE, new FieldMarshaller("int") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.setInt(target, in.readInt());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeInt(field.getInt(source));
|
||||
}
|
||||
});
|
||||
_marshallers.put(Long.TYPE, new FieldMarshaller("long") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.setLong(target, in.readLong());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeLong(field.getLong(source));
|
||||
}
|
||||
});
|
||||
_marshallers.put(Float.TYPE, new FieldMarshaller("float") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.setFloat(target, in.readFloat());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeFloat(field.getFloat(source));
|
||||
}
|
||||
});
|
||||
_marshallers.put(Double.TYPE, new FieldMarshaller("double") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.setDouble(target, in.readDouble());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeDouble(field.getDouble(source));
|
||||
}
|
||||
});
|
||||
_marshallers.put(Date.class, new FieldMarshaller("Date") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.set(target, new Date(in.readLong()));
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeLong(((Date)field.get(source)).getTime());
|
||||
}
|
||||
});
|
||||
|
||||
// create field marshallers for all of the basic types
|
||||
for (Map.Entry<Class<?>,Streamer> entry : BasicStreamers.BSTREAMERS.entrySet()) {
|
||||
_marshallers.put(entry.getKey(), new StreamerMarshaller(entry.getValue()));
|
||||
}
|
||||
|
||||
// create the field marshaller for pooled strings
|
||||
_internMarshaller = new FieldMarshaller("intern") {
|
||||
@Override
|
||||
public void readField (Field field, Object target, ObjectInputStream in)
|
||||
throws Exception {
|
||||
field.set(target, in.readIntern());
|
||||
}
|
||||
@Override
|
||||
public void writeField (Field field, Object source, ObjectOutputStream out)
|
||||
throws Exception {
|
||||
out.writeIntern((String)field.get(source));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Contains a mapping from field type to field marshaller instance for that type. */
|
||||
protected static HashMap<Class<?>, FieldMarshaller> _marshallers;
|
||||
|
||||
/** The field marshaller for pooled strings. */
|
||||
protected static FieldMarshaller _internMarshaller;
|
||||
|
||||
/** Defines the signature to a custom field reader method. */
|
||||
protected static final Class<?>[] READER_ARGS = { ObjectInputStream.class };
|
||||
|
||||
/** Defines the signature to a custom field writer method. */
|
||||
protected static final Class<?>[] WRITER_ARGS = { ObjectOutputStream.class };
|
||||
}
|
||||
Reference in New Issue
Block a user