/**
* AnalyzerBeans
* Copyright (C) 2014 Neopost - Customer Information Management
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.eobjects.analyzer.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.ObjectInputStream.GetField;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import org.eobjects.analyzer.connection.UsageAwareDatastore;
import org.eobjects.analyzer.reference.AbstractReferenceData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Builder object to make it convenient to implement a readObject(
* {@link ObjectInputStream}) method in a Serializable class.
*
* The main functionality of this helper is to aid in setting field values of
* fields that have been moved around in the class hierarchy. This is eg. the
* case with implementations of {@link UsageAwareDatastore} and
* {@link AbstractReferenceData} (where the _name fields have been moved to
* these super classes).
*
*
*/
public final class ReadObjectBuilder<E extends Serializable> {
private static final Logger logger = LoggerFactory.getLogger(ReadObjectBuilder.class);
/**
* Annotation used to mark fields in classes that have been moved in the
* class hierarchy, typically from a subclass to a superclass. Such fields
* will be discovered and treated accordingly during deserialization.
*
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@Documented
@Inherited
public static @interface Moved {
}
/**
* A custom adaptor interface which can be provided externally to do custom
* field deserialization logic.
*/
public static interface Adaptor {
public void deserialize(GetField getField, Serializable serializable) throws IOException;
}
public static <E extends Serializable> ReadObjectBuilder<E> create(E serializable, Class<? super E> clazz) {
logger.debug("Creating ReadObjectBuilder for new object of {}", clazz);
return new ReadObjectBuilder<E>(serializable, clazz);
}
private final E _serializable;
private final Class<? super E> _clazz;
private ReadObjectBuilder(E serializable, Class<? super E> clazz) {
_serializable = serializable;
_clazz = clazz;
}
public void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
readObject(stream, null);
}
public void readObject(ObjectInputStream stream, Adaptor adaptor) throws IOException, ClassNotFoundException {
try {
GetField getField = stream.readFields();
Field[] fields;
fields = _clazz.getDeclaredFields();
deserializeFields(fields, getField);
fields = ReflectionUtils.getFields(_clazz, Moved.class);
deserializeFields(fields, getField);
if (adaptor != null) {
adaptor.deserialize(getField, _serializable);
}
} catch (IOException e) {
logger.error("Could not deserialize object!", e);
throw e;
}
}
private void deserializeFields(Field[] fields, GetField getField) throws IOException {
for (Field field : fields) {
int modifiers = field.getModifiers();
if (!Modifier.isTransient(modifiers) && !Modifier.isStatic(modifiers)) {
deserializeField(field, getField);
}
}
}
private void deserializeField(Field field, GetField getField) throws IOException {
final String fieldName = field.getName();
try {
field.setAccessible(true);
Class<?> fieldType = field.getType();
if (fieldType.isPrimitive()) {
if (fieldType == boolean.class) {
final boolean value = getField.get(fieldName, false);
field.setBoolean(_serializable, value);
} else if (fieldType == byte.class) {
final byte value = getField.get(fieldName, (byte) 0);
field.setByte(_serializable, value);
} else if (fieldType == short.class) {
final short value = getField.get(fieldName, (short) 0);
field.setShort(_serializable, value);
} else if (fieldType == int.class) {
final int value = getField.get(fieldName, 0);
field.setInt(_serializable, value);
} else if (fieldType == long.class) {
final long value = getField.get(fieldName, 0l);
field.setLong(_serializable, value);
} else if (fieldType == float.class) {
final float value = getField.get(fieldName, 0f);
field.setFloat(_serializable, value);
} else if (fieldType == double.class) {
final double value = getField.get(fieldName, 0d);
field.setDouble(_serializable, value);
} else if (fieldType == char.class) {
final char value = getField.get(fieldName, (char) 0);
field.setChar(_serializable, value);
}
} else {
final Object value = getField.get(fieldName, null);
if (logger.isDebugEnabled()) {
logger.debug("{}.{} was {}", new Object[] { _clazz, field, value });
}
if (value != null) {
field.set(_serializable, value);
}
}
} catch (IllegalAccessException e) {
logger.warn("Not allowed to access field: {}", fieldName);
} catch (IllegalArgumentException e) {
logger.debug("No such field found in GetFields: {}", fieldName);
}
}
}