blob: 41346bdeaeabdce61166ea2981c77101a37f344a [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.binding.tylar;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.xml.xmlbeans.bindingConfig.BindingConfigDocument;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.SchemaTypeSystem;
import org.apache.xmlbeans.XmlBeans;
import org.apache.xmlbeans.SchemaTypeLoader;
import org.apache.xmlbeans.impl.binding.bts.BindingFile;
import org.apache.xmlbeans.impl.binding.joust.FileWriterFactory;
import org.apache.xmlbeans.impl.binding.joust.JavaOutputStream;
import org.apache.xmlbeans.impl.binding.joust.SourceJavaOutputStream;
import org.apache.xmlbeans.impl.binding.joust.ValidatingJavaOutputStream;
import org.apache.xmlbeans.impl.common.JarHelper;
import org.apache.xmlbeans.impl.schema.SchemaTypeSystemImpl;
import org.apache.xmlbeans.impl.tool.SchemaCodeGenerator;
import org.w3.x2001.xmlSchema.SchemaDocument;
/**
* Concrete implementation of ExplodedTylar - a tylar which exists in an open
* directory structure on disk. Note that this class also implements
* TylarWriter, which allows the compile time to build up the tylar files
* and then hand them directly to the runtime if desired.
*
* @author Patrick Calahan <pcal@bea.com>
*/
public class ExplodedTylarImpl extends BaseTylarImpl
implements TylarConstants, ExplodedTylar, TylarWriter {
// ========================================================================
// Constants
private static final int XML_INDENT = 2;
private static final boolean VERBOSE = false;
// ========================================================================
// Variables
private File mRootDir;
private File mSourceRoot;
private File mSchemaDir;
private BindingFile mBindingFile = null;
private JavaOutputStream mJoust = null;
private Collection mSchemaDocuments = null;
private SchemaTypeSystem mSchemaTypeSystem = null;
// ========================================================================
// Factory methods
/**
* Creates a new tylar from the given directory. The directory must exist
* or be creatable. The default JavaOutputStream will be used for output.
*/
public static ExplodedTylarImpl create(File dir) throws IOException {
return create(dir, createDefaultJoust(dir));
}
/**
* Loads a tylar from the given directory. The directory must exist
* and contain at least a binding file. The default JavaOutputStream
* will be used for output.
*/
public static ExplodedTylarImpl load(File dir)
throws IOException, XmlException {
return load(dir, createDefaultJoust(dir));
}
/**
* Creates a new tylar from the given directory. The directory must exist
* or be creatable.
*/
public static ExplodedTylarImpl create(File dir, JavaOutputStream joust)
throws IOException {
if (dir.exists()) {
if (dir.isFile()) throw new IOException("Already a file at " + dir);
} else {
if (!dir.mkdirs()) throw new IOException("Failed to create " + dir);
}
return new ExplodedTylarImpl(dir, null, null, null, joust);
}
/**
* Loads a tylar from the given directory. The directory must exist
* and contain at least a binding file.
*/
public static ExplodedTylarImpl load(File dir, JavaOutputStream joust)
throws IOException, XmlException {
if (dir.exists()) {
if (dir.isFile()) throw new IOException(dir + " is a file");
} else {
throw new IOException("No such directory " + dir);
}
BindingFile bf = parseBindingFile(new File(dir, BINDING_FILE));
Collection schemas = new ArrayList();
parseSchemas(new File(dir, SCHEMA_DIR), schemas);
SchemaTypeSystem sts = null;
{
//try to read the xsbs. first we need to figure out the sts name.
try {
File stsDir = new File(dir, "schema"+SEP+"system");
String stsName = stsDir.list()[0];
ClassLoader cl = new URLClassLoader(new URL[]{dir.toURL()});
sts = SchemaTypeSystemImpl.forName(STS_PACKAGE+"."+stsName,cl);
if (sts == null) throw new IllegalStateException("null returned by SchemaTypeSystemImpl.forName()");
if (VERBOSE) System.out.println
("[XBEANS] loaded schema type system '"+stsName+"'");
} catch(Exception e) {
showXsbError(e,dir.toURI(),"read",TylarConstants.SHOW_XSB_ERRORS);
}
}
return new ExplodedTylarImpl(dir, bf, schemas, sts, joust);
}
// ========================================================================
// Constructors
/**
* Constructs a new ExplodedTylarImpl in the given directory and using
* the given JavaOutputStream.
*/
private ExplodedTylarImpl(File dir, // must exist
BindingFile bindingFile, // null ok
Collection schemas, // null ok,
SchemaTypeSystem sts, // null ok
JavaOutputStream joust) // null ok
{
mRootDir = dir;
mSourceRoot = new File(mRootDir, SRC_ROOT);
mSchemaDir = new File(mRootDir, SCHEMA_DIR);
mJoust = joust;
mBindingFile = bindingFile;
mSchemaDocuments = schemas;
mSchemaTypeSystem = sts;
}
// ========================================================================
// TylarWriter implementation
public void writeBindingFile(BindingFile bf) throws IOException {
mBindingFile = bf;
writeBindingFile(bf, new File(mRootDir, BINDING_FILE));
}
public void writeSchema(SchemaDocument xsd, String schemaFileName)
throws IOException {
if (schemaFileName == null) {
throw new IllegalArgumentException("null schemaFileName");
}
if (mSchemaDocuments == null) mSchemaDocuments = new ArrayList();
mSchemaDocuments.add(xsd);
writeXsd(xsd, new File(mSchemaDir, schemaFileName));
}
public void writeSchemaTypeSystem(SchemaTypeSystem sts) throws IOException {
if (sts == null) throw new IllegalArgumentException("null sts");
mSchemaTypeSystem = sts;
try {
SchemaCodeGenerator.saveTypeSystem(sts,
mRootDir,
null,null,null);
} catch(Exception e) {
showXsbError(e,mRootDir.toURI(),"write",TylarConstants.SHOW_XSB_ERRORS);
}
}
public JavaOutputStream getJavaOutputStream() {
return mJoust;
}
public void close() throws IOException {
if (mJoust != null) mJoust.close();
}
// ========================================================================
// Public static methods
public static void showXsbError(Throwable e,
URI where,
String readOrWrite,
boolean showTrace)
{
System.out.println
("[XBEANS] Notice: an unexpected error occurred while trying to\n "+
readOrWrite+" a binary version of your schemas"+
((where == null) ? "." : " at\n"+where+"\n")+
"\nYour bindings are ok, but you may suffer some performance\n" +
"degradation if your schemas are very large or complicated.\n"+
"(exception is "+e.getClass()+", message is '"+e.getMessage()+"'");
if (showTrace) e.printStackTrace();
System.out.flush();
}
// ========================================================================
// Tylar implementation
public BindingFile[] getBindingFiles() {
return new BindingFile[] {mBindingFile};
}
public SchemaDocument[] getSchemas() {
if (mSchemaDocuments == null) return new SchemaDocument[0];
SchemaDocument[] out = new SchemaDocument[mSchemaDocuments.size()];
mSchemaDocuments.toArray(out);
return out;
}
public SchemaTypeLoader getSchemaTypeLoader() throws IOException, XmlException {
if (mSchemaTypeSystem == null) {
mSchemaTypeSystem = getDefaultSchemaTypeSystem();
}
return mSchemaTypeSystem;
}
//not sure we ever need this
public void resetCaches() {
mSchemaDocuments = null;
mBindingFile = null;
}
// ========================================================================
// ExplodedTylar implementation
/**
* Returns the directory on disk in which the tylar is stored. Never
* returns null.
*/
public File getRootDir() {
return mRootDir;
}
public Tylar toJar(File jarfile) throws IOException {
JarHelper j = new JarHelper();
jarfile.getParentFile().mkdirs(); //make sure the path exists
j.jarDir(mRootDir,jarfile);
return new TylarImpl(new URL[]{jarfile.toURL()},mBindingFile,mSchemaDocuments);
}
public File getSourceDir() {
return mSourceRoot;
}
public File getClassDir() {
return mRootDir;
}
public File getSchemaDir() {
return mSchemaDir;
}
public ClassLoader createClassLoader(ClassLoader parent) {
try {
return new URLClassLoader(new URL[] {mRootDir.toURL()},parent);
} catch(MalformedURLException mue){
throw new RuntimeException(mue); //FIXME this is bad
}
}
// ========================================================================
// Private methods
private static JavaOutputStream createDefaultJoust(File dir) {
File srcDir = new File(dir,TylarConstants.SRC_ROOT);
return new ValidatingJavaOutputStream
(new SourceJavaOutputStream(new FileWriterFactory(srcDir)));
}
private static SchemaTypeSystem buildSts(Collection xsds)
throws XmlException
{
SchemaDocument[] x = new SchemaDocument[xsds.size()];
xsds.toArray(x);
return XmlBeans.compileXsd(x,XmlBeans.getBuiltinTypeSystem(),null);
}
private static void parseSchemas(File schemaDir, Collection out)
throws IOException, XmlException {
File[] xsds = schemaDir.listFiles();
if (xsds != null) {
for (int i = 0; i < xsds.length; i++) {
if (VERBOSE) System.out.println("parsing "+xsds[i]);
out.add(parseXsd(xsds[i]));
}
}
}
private static SchemaDocument parseXsd(File file)
throws IOException, XmlException {
FileReader in = null;
try {
in = new FileReader(file);
return SchemaDocument.Factory.parse(in);
} catch (IOException ioe) {
throw ioe;
} catch (XmlException xe) {
throw xe;
} finally {
if (in != null) {
try {
in.close();
} catch (Exception ohwell) {
ohwell.printStackTrace();
}
}
}
}
private static void writeXsd(SchemaDocument xsd, File file)
throws IOException {
FileOutputStream out = null;
try {
file.getParentFile().mkdirs();
out = new FileOutputStream(file);
xsd.save(out,
new XmlOptions().setSavePrettyPrint().
setSavePrettyPrintIndent(XML_INDENT));
} catch (IOException ioe) {
throw ioe;
} finally {
if (out != null) {
try {
out.close();
} catch (Exception ohwell) {
ohwell.printStackTrace();
}
}
}
}
private static BindingFile parseBindingFile(File file)
throws IOException, XmlException {
FileReader in = null;
try {
in = new FileReader(file);
return BindingFile.forDoc(BindingConfigDocument.Factory.parse(in));
} catch (IOException ioe) {
throw ioe;
} catch (XmlException xe) {
throw xe;
} finally {
if (in != null) {
try {
in.close();
} catch (Exception ohwell) {
ohwell.printStackTrace();
}
}
}
}
private static void writeBindingFile(BindingFile bf, File file)
throws IOException {
FileWriter out = null;
try {
file.getParentFile().mkdirs();
out = new FileWriter(file);
BindingConfigDocument doc = bf.write();
doc.save(out,
new XmlOptions().setSavePrettyPrint().
setSavePrettyPrintIndent(XML_INDENT));
out.flush();
} catch (IOException ioe) {
throw ioe;
} finally {
if (out != null) {
try {
out.close();
} catch (Exception ohwell) {
ohwell.printStackTrace();
}
}
}
}
}