/*
* Copyright (c) 2010, SQL Power Group Inc.
*/
package ca.sqlpower.dao;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Stack;
import javax.swing.ProgressMonitor;
import org.apache.commons.codec.binary.Base64;
import ca.sqlpower.dao.upgrade.UpgradePersisterManager;
import ca.sqlpower.util.SQLPowerUtils;
/**
* A persister to write persist calls to XML. This persister requires that its
* input has been previously sorted such that all of an objects properties and
* children come before its siblings. This persister also does not support
* incremental changes. Once the workspace is committed, this persister will
* close.
*/
public class XMLPersister implements SPPersister {
public final String PROJECT_TAG;
private static UpgradePersisterManager upgradePersisterManager;
public static void setUpgradePersisterManager(UpgradePersisterManager upgradePersisterManager) {
XMLPersister.upgradePersisterManager = upgradePersisterManager;
}
private final Stack<String> currentObject = new Stack<String>();
private final Stack<String> currentType = new Stack<String>();
private final PrintWriter out;
private final ByteArrayOutputStream bufferedOut;
/**
* The fully qualified class name of the object that is the root of the tree of objects being
* persisted.
*/
private final String rootObject;
private int transactionCount = 0;
private final OutputStream finalOut;
private final ProgressMonitor pm;
private int progress = 0;
public XMLPersister(OutputStream out, String rootObject, String projectTag) {
this(out, rootObject, projectTag, null);
}
public XMLPersister(OutputStream out, String rootObject, String projectTag, ProgressMonitor pm) {
bufferedOut = new ByteArrayOutputStream();
this.out = new PrintWriter(bufferedOut);
this.finalOut = out;
this.rootObject = rootObject;
this.pm = pm;
PROJECT_TAG = projectTag;
}
@Override
public void begin() throws SPPersistenceException {
if (transactionCount == 0) {
out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
out.println("<" + PROJECT_TAG + " file-version=\"" + upgradePersisterManager.getStateVersion() + "\">");
}
transactionCount++;
}
@Override
public void commit() throws SPPersistenceException {
transactionCount--;
if (transactionCount == 0) {
while (!currentType.isEmpty()) {
currentObject.pop();
out.println(tab() + "</" + currentType.pop().replace("$", "..") + ">");
}
out.println("</" + PROJECT_TAG + ">");
out.flush();
try {
byte[] byteArray = bufferedOut.toByteArray();
if (pm != null) {
pm.setMaximum(byteArray.length);
}
for (int i = 0; i < byteArray.length; i++) {
finalOut.write(byteArray[i]);
if (pm != null) {
pm.setProgress(++progress);
}
}
finalOut.flush();
} catch (IOException e) {
throw new SPPersistenceException(null, e);
}
}
}
@Override
public void persistObject(String parentUUID, String type, String uuid,
int index) throws SPPersistenceException {
if (uuid == null) uuid = "";
if (parentUUID == null) parentUUID = "";
while (!currentObject.isEmpty() && !parentUUID.equals(currentObject.peek())) {
currentObject.pop();
out.println(tab() + "</" + currentType.pop().replace("$", "..") + ">");
}
if (currentObject.isEmpty()) {
if (!type.equals(rootObject)) {
throw new SPPersistenceException(null,
"This persister does not support incremental updates."
+ " The first object persisted must be the root.");
}
} else if (!parentUUID.equals(currentObject.peek())) {
throw new SPPersistenceException(null,
"This persister requires persists to be ordered. An object at ["
+ uuid + "] as a child of [" + parentUUID
+ "] was persisted while the current object was ["
+ currentObject.peek() + "]");
}
out.println(tab() + "<" + type.replace("$", "..") + " UUID=\"" + SQLPowerUtils.escapeXML(uuid) + "\" index=\"" + index + "\">");
currentObject.push(uuid);
currentType.push(type);
}
@Override
public void persistProperty(String uuid, String propertyName,
DataType propertyType, Object oldValue, Object newValue)
throws SPPersistenceException {
throw new UnsupportedOperationException("This persister does not support incremental updates. Use the unconditional persistProperty instead");
}
@Override
public void persistProperty(String uuid, String propertyName,
DataType propertyType, Object newValue)
throws SPPersistenceException {
if (uuid == null) uuid = "";
if (currentObject.isEmpty()) {
throw new SPPersistenceException("Recieved property for object [" + uuid + "] which does not exist");
}
if (!uuid.equals(currentObject.peek())) {
throw new SPPersistenceException(null,
"This persister requires persists to be ordered. An property of ["
+ uuid + "] was persisted while the current object was ["
+ currentObject.peek() + "]");
}
if (propertyType != DataType.NULL && newValue != null) {
out.print(tab() + "<property name=\"" + SQLPowerUtils.escapeNewLines(SQLPowerUtils.escapeXML(propertyName)) + "\" type=\"" + propertyType.toString() + "\"");
if (propertyType == DataType.PNG_IMG) {
try {
out.print(" value=\"");
ByteArrayOutputStream data = new ByteArrayOutputStream();
SQLPowerUtils.copyStream((InputStream) newValue, data);
byte[] bytes = data.toByteArray();
byte[] base64Bytes = Base64.encodeBase64(bytes);
out.print(new String(base64Bytes));
out.println("\"/>");
} catch (IOException e) {
throw new SPPersistenceException(uuid, e);
}
} else {
out.println(" value=\"" + SQLPowerUtils.escapeXML(newValue.toString()) + "\"/>");
}
}
}
@Override
public void removeObject(String parentUUID, String uuid)
throws SPPersistenceException {
throw new UnsupportedOperationException("This persister does not support incremental updates");
}
@Override
public void rollback() {
}
private String tab() {
StringBuilder tab = new StringBuilder();
for (int i = 0; i <= currentObject.size(); i++) {
tab.append(" ");
}
return tab.toString();
}
}