package nl.tudelft.simulation.language.reflection;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Set;
import nl.tudelft.simulation.language.DSOLException;
/**
* StateSaver can serialize a full state of a single object, including the fields of its superclass, to an object.
* Later, this object can be used to reset the full state of the object to the old value. This is useful when doing
* rollback in a simulation; the state of objects can be rolled back to their old values, including random number
* generators, which will reset their seed to the old value. Be careful with objects with shared pointers to e.g.,
* collections, as the restoreState() on a single object might lead to the duplication of the shared objects indicated
* by these pointers. When the rollback is needed for an entire simulation, use XmlUtil, GSON, or another library to
* serialize / deserialize an entire simulation model at once.
*
* Copyright (c) 2003-2018 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights
* reserved. See for project information
* www.simulation.tudelft.nl. The source code and
* binary code of this software is proprietary information of Delft University of Technology.
* @author Alexander Verbraeck
*/
public final class StateSaver
{
/** */
private StateSaver()
{
// utility class
}
/**
* Save the state of a single object into another object. Fields of the superclass are included. The state save is a
* deep copy, using the writeObject() method of serialization.
* @param object the object to save the state from
* @return the state packed into a memory object
* @throws DSOLException on serialization error
*/
public static Object saveState(final Object object) throws DSOLException
{
Object state = null;
try
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(object);
state = bos.toByteArray();
out.close();
bos.close();
}
catch (IOException exception)
{
throw new DSOLException(exception);
}
return state;
}
/**
* Retrieve the earlier saved state of a single object and write it into an object (which could be a clone or the
* original object). Fields of the superclass are included. The state retrieval uses all information from a deep
* copy, using the readObject() method of deserialization to fill the target object.
* @param target the target object to write the deserialized information into
* @param state the earlier saved state to write
* @throws DSOLException on deserialization error
*/
public static void restoreState(final Object target, final Object state) throws DSOLException
{
try
{
byte[] byteState = (byte[]) state;
ByteArrayInputStream bis = new ByteArrayInputStream(byteState);
ObjectInputStream in = new ObjectInputStream(bis);
Object tempObject = in.readObject();
Set allFields = ClassUtil.getAllFields(target.getClass());
for (Field field : allFields)
{
if (!Modifier.isTransient(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()))
{
field.setAccessible(true);
field.set(target, field.get(tempObject));
}
}
in.close();
bis.close();
}
catch (IOException | ClassNotFoundException | IllegalArgumentException | IllegalAccessException
| SecurityException exception)
{
throw new DSOLException(exception);
}
}
}