package com.plectix.simulator.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.core.ReferenceByIdMarshallingStrategy;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
/**
* PersistenceUtils provides static methods to persist objects into XML or Serialized binary files
*
* @author ecemis
*/
public final class PersistenceUtils {
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
/** common xml extension */
private static final String XML_EXTENSION = ".xml";
/** common zip extension */
private static final String ZIP_EXTENSION = ".zip";
private static XStream xStream = null;
public static final XStream getXStream(){
initialize();
return xStream;
}
// ************************************************************************************
private static void saveToXML(Object object, String filename, boolean zipped)
throws IOException {
initialize();
OutputStream outputStream = getOutputStream(filename, zipped);
xStream.toXML(object, outputStream);
outputStream.flush();
outputStream.close();
}
// ************************************************************************************
private static Object loadFromXML(String filename, boolean zipped)
throws IOException {
initialize();
return xStream.fromXML(getInputStream(filename, zipped));
}
// ************************************************************************************
/**
*
* @param object
* @param filename
* @throws IOException
*/
private static void saveToSerializedBinary(Object object, String filename, boolean zipped) throws IOException {
OutputStream outputStream = getOutputStream(filename, zipped);
ObjectOutputStream objStream = new ObjectOutputStream(outputStream);
objStream.writeObject(object);
outputStream.flush();
outputStream.close();
}
// ************************************************************************************
/**
*
* @param filename
* @return the object read from the binary file
* @throws IOException
* @throws ClassNotFoundException
*/
private static Object loadFromSerializedBinary(String filename,
boolean zipped) throws IOException, ClassNotFoundException {
ObjectInputStream objStream = new ObjectInputStream(getInputStream(filename, zipped));
return objStream.readObject();
}
// ************************************************************************************
/**
*
* @param fromFilename
* @param fromXML
* @param toFilename
* @param toXML
* @param zipped
* @throws IOException
* @throws ClassNotFoundException
*/
private static void convertFile(String fromFilename, boolean fromXML,
String toFilename, boolean toXML, boolean zipped)
throws IOException, ClassNotFoundException {
Object object;
if (fromXML) {
object = loadFromXML(fromFilename, fromFilename
.endsWith(ZIP_EXTENSION));
} else {
object = loadFromSerializedBinary(fromFilename, fromFilename
.endsWith(ZIP_EXTENSION));
}
if (toXML) {
saveToXML(object, toFilename, zipped);
} else {
saveToSerializedBinary(object, toFilename, zipped);
}
}
// ************************************************************************************
/**
*
* @param filename
* @param zipped
* @return an input stream
* @throws IOException
*/
private static InputStream getInputStream(String filename, boolean zipped)
throws IOException {
if (zipped) {
ZipInputStream zipInputStream = new ZipInputStream(
new BufferedInputStream(new FileInputStream(filename
+ ZIP_EXTENSION)));
zipInputStream.getNextEntry();
return zipInputStream;
} else {
return new BufferedInputStream(new FileInputStream(filename));
}
}
// ************************************************************************************
/**
*
* @param filename
* @param zipped
* @return an output stream
* @throws IOException
*/
private static OutputStream getOutputStream(String filename, boolean zipped)
throws IOException {
if (zipped) {
OutputStream outputStream = new BufferedOutputStream(
new FileOutputStream(filename + ZIP_EXTENSION));
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
ZipEntry zipEntry = new ZipEntry(new File(filename).getName());
zipOutputStream.putNextEntry(zipEntry);
return zipOutputStream;
} else {
return new BufferedOutputStream(new FileOutputStream(filename));
}
}
// ************************************************************************************
/**
*
*
*/
private static final void initialize() {
if (xStream != null) {
return;
}
xStream = new XStream();
xStream.setMarshallingStrategy(new ReferenceByIdMarshallingStrategy());
// Register converters: For example:
// xStream.registerConverter(new DateConverter());
// Set aliases:
// addAlias(Class.class);
}
// ************************************************************************************
/**
*
* @param object
*/
public static final void addAlias(Object object) {
initialize();
Class clazz = object.getClass();
xStream.alias(clazz.getSimpleName(), clazz);
}
// ************************************************************************************
/**
* Usage: <br>
* <code>-i /tmp/localDataService.xml -o /tmp/convertedLocalDataService.bin</code>
*
* @param args
* @throws ClassNotFoundException
* @throws IOException
*/
public static void main(String[] args) throws IOException,
ClassNotFoundException {
String fromFilename = null;
String toFilename = null;
boolean zipped = false;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-i")) {
fromFilename = args[++i];
} else if (args[i].equals("-o")) {
toFilename = args[++i];
} else if (args[i].equals("-z")) {
zipped = true;
} else {
System.err.println("\nUnknown argument: " + args[i]
+ "\nAborting...\n\n");
System.exit(-1);
}
}
if (fromFilename == null || toFilename == null) {
System.err.println("\nFilename(s) not set! \nAborting...\n\n");
System.exit(-1);
}
assert fromFilename!=null;
assert toFilename!=null;
System.err.println("\nStarting to convert...");
convertFile(fromFilename, fromFilename.endsWith(XML_EXTENSION),
toFilename, toFilename.endsWith(XML_EXTENSION), zipped);
System.err.println("\nDone.\n");
System.exit(0);
}
// ************************************************************************************
/**
*
*
*/
private static final class DateConverter implements Converter {
public DateConverter() {
super();
}
public boolean canConvert(Class clazz) {
return clazz.equals(Date.class);
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
writer.setValue(DATE_FORMAT.format((Date) value));
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
try {
// return DateUtils.removeUnnecessaryFields(DATE_FORMAT.parse(reader.getValue()));
return DATE_FORMAT.parse(reader.getValue());
} catch (ParseException e) {
throw new ConversionException(e.getMessage(), e);
}
}
}
// -------------------------------------------------------------------------
/**
* reads the values of non-transient fields of an object o into a map keyed
* by their names. Static fields are excluded as well. The returned map may
* contain null values.
*
* DO NOT USE THIS AS A GENERAL PURPOSE SERIALIZATION MECHANISM. it is meant
* for enumerating field for objects that have an otherwise prohibitively
* large number of members.
*
* @throws Error
* if a field is not accessible, typically because runtime
* security settings prevent access to private fields. Also
* fails if a field name is repeated in a class's hierarchy.
*/
public static final Map<String, Object> getNonTransientFields(Object o) {
Map<String, Object> ret = new LinkedHashMap<String, Object>();
// copied from DMComposite r6104
Class c = o.getClass();
// ERROR must call c.setAccessible true if we have a private inner class
do {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
try {
int mods = field.getModifiers();
// skip transient
if (Modifier.isTransient(mods))
continue;
if (Modifier.isStatic(mods))
continue;
field.setAccessible(true);
// TODO deal with element repeated in super class and
// subclass
Object old = ret.put(field.getName(), field.get(o));
if (old != null) {
throw new Error("field " + field.getName()
+ " does not have a unique name");
}
} catch (Exception e) {
throw new Error("failed to read field: " + field.getName(),
e);
}
}
c = c.getSuperclass();
} while (c != null);
return ret;
}
/**
* Set all fields of o named in m. <code>m</code> contain null values.
* Transient/static fields are skipped. First occurrence of name walking up
* inheritance hierarchy will be assigned new value.
*
* @throws IOException
* if a field name can't be found or the specified value can't
* be assigned to that field (typically a ClassCastException).
*/
public static final void setFields(Object o, Map<String,?> m) throws IOException {
Class c = o.getClass();
// ERROR must call c.setAccessible true if we have a private inner class
do {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
try {
int mods = field.getModifiers();
// skip transient
if (Modifier.isTransient(mods))
continue;
if (Modifier.isStatic(mods))
continue;
field.setAccessible(true);
// TODO deal with element repeated in super class and
// subclass
String n = field.getName();
if (m.containsKey(n))
field.set(o, m.remove(n));
} catch (Exception e) {
String msg = "failed to read field: " + field.getName();
IOException e2 = new IOException(msg);
e2.initCause(e);
throw e2;
}
}
c = c.getSuperclass();
} while (c != null);
if (!m.isEmpty()) {
String msg = "the following fields could not be resolved:"
+ m.toString();
throw new IOException(msg);
}
}
}