// Copyright 2002-2007, FreeHEP.
package hep.aida.ref.xml;
import hep.aida.ITree;
import hep.aida.dev.IDevTree;
import hep.aida.dev.IOnDemandStore;
import hep.aida.ref.AidaUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PushbackInputStream;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import de.schlichtherle.io.File;
import de.schlichtherle.io.FileInputStream;
import de.schlichtherle.io.FileOutputStream;
import hep.aida.IManagedObject;
import hep.aida.dev.IAddable;
import hep.aida.util.Addable;
/**
* Store associated to XML file, directory structure or archive.
*
* @author tonyj
* @author Mark Donszelmann
* @version $Id: AidaXMLStore.java 13360 2007-10-02 23:13:06Z serbo $
*/
public class AidaXMLStore implements IOnDemandStore {
protected File root;
protected boolean createNew = false;
protected boolean useProxies = false;
public boolean isReadOnly() {
return false;
}
int n = 0;
// IOnDemandStore methods
// This method is only called for a root file with structure: "zip" or "dir"
public void read(IDevTree tree, String path)
throws IllegalArgumentException, IOException {
// System.err.println("--> "+root+" "+path);
if (root == null)
return;
path = path.startsWith("/") ? path.substring(1) : path;
File file = new File(root, path);
//System.err.println("file dir="+file.isDirectory()+", path="+file.getInnerEntryName());
if (file.isDirectory()) {
tree.mkdirs("/"+path);
File[] files = (File[]) file.listFiles();
int rootLength = root.getPath().length()+1;
for (int i = 0; i < files.length; i++) {
String tmpPath = files[i].getInnerEntryName();
if (tmpPath == null) tmpPath = files[i].getPath().substring(rootLength);
// System.err.println(i+"\t "+tmpPath);
if (files[i].isDirectory()) {
tree.mkdirs("/"+tmpPath);
n++;
} else {
read(tree, "/"+tmpPath);
}
}
tree.hasBeenFilled("/"+path);
} else if (useProxies) {
String treePath = file.getInnerEntryName();
if (treePath == null) treePath = file.getPath();
String type = "IManagedObject";
int index = treePath.lastIndexOf(".");
if (index >= 0) {
type = treePath.substring(index+1);
treePath = treePath.substring(0, index);
}
IManagedObject mo = AidaObjectProxy.createProxy(this, path, type);
tree.add(AidaUtils.parseDirName(treePath), mo);
} else {
InputStream in = new FileInputStream(file);
try {
parse(tree, false, in, true);
n++;
} catch (Exception x) {
IOException xx = new IOException("Error reading "
+ tree.storeName() + " " + path);
xx.initCause(x);
throw xx;
} finally {
if (in != null) {
in.close();
}
}
}
// System.err.println(n);
}
// IStore methds
public void read(IDevTree tree, Map optionsMap, boolean readOnly,
boolean createNew) throws IOException {
File file = new File(tree.storeName());
file.isDirectory();
this.createNew = createNew;
boolean exists = file.exists();
if (!exists && (readOnly || !createNew)) {
throw new IOException("File " + file + " does not exist.");
}
if (exists && !readOnly && !createNew && !file.canWrite()) {
throw new IOException("File " + file + " is a read-only file.");
}
if (exists) {
// FIXME ???
readOnly |= file.canWrite();
if (file.isFile()) {
// we select a plain (xml) file
// (recognized archive files are NOT files)
root = null;
InputStream in = new FileInputStream(file);
try {
String validateString = (String) optionsMap.get("validate");
boolean validate = (validateString == null)
|| validateString.equalsIgnoreCase("true");
parse(tree, true, in, validate);
} catch (Exception x) {
IOException xx = new IOException("Error reading "
+ tree.storeName());
xx.initCause(x);
System.err.println(x);
x.printStackTrace();
throw xx;
} finally {
if (in != null) {
in.close();
}
}
} else {
// archive and/or directory
root = file;
useProxies = toBoolean(optionsMap,"useProxies", true);
read(tree, "/");
}
}
}
public void commit(IDevTree tree, Map optionsMap) throws IOException {
String createString = (String) optionsMap.get("createNew");
if (createString != null && createString.equalsIgnoreCase("true"))
createNew = true;
String cString = (String) optionsMap.get("compress");
boolean zip = cString != null && cString.equalsIgnoreCase("zip");
boolean compress = (cString == null) || cString.equalsIgnoreCase("yes")
|| cString.equalsIgnoreCase("true")
|| cString.equalsIgnoreCase("gzip");
String bString = (String) optionsMap.get("binary");
boolean binary = (bString != null)
&& (bString.equalsIgnoreCase("yes") || bString
.equalsIgnoreCase("true"));
String[] skip = null;
if (optionsMap.get("skip") != null)
skip = AidaUtils.parseString((String) optionsMap.get("skip"));
java.io.File f = new java.io.File(tree.storeName());
if (createNew && f.exists() && !f.isDirectory()) f.delete();
commit(tree, new File(tree.storeName()), skip, zip,
compress, binary);
}
public void commit(ITree tree, File file, String[] skip, boolean zip,
boolean compress, boolean binary) throws IOException {
if (file.isDirectory() || zip) {
AidaZipXMLWriter zw = new AidaZipXMLWriter(file, binary, skip);
zw.toXML(tree);
zw.close();
} else {
OutputStream os = new FileOutputStream(file);
if (compress) {
os = new GZIPOutputStream(os);
}
AidaXMLWriter out;
if (binary) {
out = new AidaXMLWriter(new DataOutputStream(
new BufferedOutputStream(os)));
} else {
out = new AidaXMLWriter(new BufferedWriter(
new OutputStreamWriter(os)));
}
out.toXML(tree);
out.close();
}
}
public void close() {
root = null;
}
IManagedObject readManagedObject(String path) throws IOException {
Addable addable = new Addable();
File file = new File(root, path);
InputStream in = new FileInputStream(file);
try {
parse(addable, false, in, true);
n++;
} catch (Exception x) {
IOException xx = new IOException("Error creating managed object for "+ path);
xx.initCause(x);
throw xx;
} finally {
if (in != null) {
in.close();
}
}
return addable.object();
}
// NOTE: we need to change this to handle partial reads...
protected void parse(IAddable tree, boolean markAsFilled, InputStream in,
boolean validate) throws SAXException,
ParserConfigurationException, IOException {
byte[] magic = new byte[4];
// Check filetype
PushbackInputStream pin = new PushbackInputStream(in, magic.length);
// peek first four bytes
int b = pin.read(magic);
if (b != magic.length)
throw new IOException("Unexpected EOF");
pin.unread(magic);
// gzipped stream ?
if (((magic[0] & 0xff) == 0x1f) && ((magic[1] & 0xff) == 0x8b)) {
pin = new PushbackInputStream(new GZIPInputStream(pin),
magic.length);
b = pin.read(magic);
if (b != magic.length)
throw new IOException("Unexpected EOF");
pin.unread(magic);
}
in = pin;
// WBXML ?
boolean binary = ((magic[0] & 0xff) == 0x03)
&& ((magic[1] & 0xff) == 0x00)
&& ((magic[2] & 0xff) == 0x00)
&& ((magic[3] & 0xff) == 0x6a);
in = new BufferedInputStream(in);
AidaHandlerImpl handler = new AidaHandlerImpl(tree, markAsFilled);
if (binary) {
// Binary XML
AidaWBXMLParser parser = new AidaWBXMLParser(handler);
parser.parse(in);
} else {
// Plain XML
EntityResolver er = new AIDAEntityResolver(AidaParser.class,
"http://aida.freehep.org/");
AidaParser parser = new AidaParser(handler, er);
parser.setValidate(validate);
InputSource is = new InputSource(in);
String id = (tree instanceof ITree) ? ((ITree) tree).storeName() : "AidaXMLStore";
is.setSystemId(id);
parser.parse(is);
}
}
private boolean toBoolean(Map options, String key) {
return toBoolean(options, key, false);
}
private boolean toBoolean(Map options, String key, boolean def)
{
Object value = options.get(key);
if (value == null) return def;
return Boolean.valueOf(value.toString()).booleanValue();
}
}