blob: 65ca307a6fd584b66d5f0af693ba7f4643004c2b [file] [log] [blame]
/**
* **********************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* Copyright 2009, 2010 Oracle and/or its affiliates. All rights reserved.
*
* Use is subject to license terms.
*
* 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. You can also
* obtain a copy of the License at http://odftoolkit.org/docs/license.txt
*
* 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 schema2template.example.odf;
import com.sun.msv.grammar.Expression;
import com.sun.msv.reader.trex.ng.RELAXNGReader;
import java.io.File;
import java.io.FileWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.SAXParserFactory;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import schema2template.OutputFileListEntry;
import schema2template.OutputFileListHandler;
import schema2template.model.XMLModel;
/**
* Three ODF examples in one:
* 1) Create an ODF Reference in HTMLl
* 2) Create Source Code
* 3) Create simple ODF Python source
*
*/
public class OdfHelper {
private static final Logger LOG = Logger.getLogger(OdfHelper.class.getName());
public static final Boolean DEBUG = Boolean.FALSE;
/** Expresses the amount of elements in ODF 1.1. There are some issues in the schema that have to be fixed before the full number can be returned by MSV:
Reference table-table-template is never used, therefore several elements are not taking into account::
"table:body"
"table:even-columns"
"table:even-rows"
"table:first-column"
"table:first-row"
"table:last-column"
"table:last-row"
"table:odd-columns"
"table:odd-rows"
"table:table-template"
NOTE: Ignoring the '*' there can be 525 elements parsed, but with fixed schema it should be 535. */
public static final int ODF11_ELEMENT_NUMBER = 525; //ToDo: 535 - by search/Replace using RNGSchema and tools, prior exchange <name> to element or attribute declaration
public static final int ODF12_ELEMENT_NUMBER = 598;
/** Expresses the amount of attributes in ODF 1.1. There are some issues in the schema that have to be fixed before the full number can be returned by MSV:
Following references are never used, therefore its attribute is not taking into account::
draw-glue-points-attlist with "draw:escape-direction"
office-process-content with "office:process-content" (DEPRECATED in ODF1.2 only on foreign elements)
Following attributes are member of the not referenced element "table:table-template":
"text:first-row-end-column"
"text:first-row-start-column"
"text:last-row-end-column"
"text:last-row-start-column"
"text:paragraph-style-name"
NOTE: Ignoring the '*' there can be 1162 elements parsed, but with fixed schema it should be 1169. */
public static final int ODF11_ATTRIBUTE_NUMBER = 1162; //ToDo: 1169 - by search/Replace using RNGSchema and tools, prior exchange <name> to element or attribute declaration
public static final int ODF12_ATTRIBUTE_NUMBER = 1300; //in RNG 1301 as there is one deprecated attribute on foreign elements not referenced (ie. @office:process-content)
private static String odfDomResourceDir;
private static String odfPkgResourceDir;
private static String odfPythonResourceDir;
private static String odfReferenceResourceDir;
private static String odfSchemaResourceDir;
private static String outputRoot;
public static final String INPUT_ROOT = "target" + File.separator + "odf-schemas";
public static final String TEST_REFERENCE_DIR = "target" + File.separator + "test-classes" + File.separator
+ "examples" + File.separator + "odf";
public static final String TEST_INPUT_ROOT = "target" + File.separator + "classes" + File.separator
+ "examples" + File.separator + "odf";
public static final String ODF10_RNG_FILE_NAME = "OpenDocument-strict-schema-v1.0-os.rng";
public static final String ODF11_RNG_FILE_NAME = "OpenDocument-strict-schema-v1.1.rng";
public static final String ODF12_RNG_FILE_NAME = "OpenDocument-v1.2-os-schema.rng";
public static final String ODF12_SIGNATURE_RNG_FILE_NAME = "OpenDocument-v1.2-os-dsig-schema.rng";
public static final String ODF12_MANIFEST_RNG_FILE_NAME = "OpenDocument-v1.2-os-manifest-schema.rng";
static String odf12RngFile;
static String odf12SignatureRngFile;
static String odf12ManifestRngFile;
static String odf11RngFile;
static String odf10RngFile;
private static String mConfigFile;
private static final String REFERENCE_OUTPUT_FILES_TEMPLATE = "dom-output-files.vm";
private static final String REFERENCE_OUTPUT_FILES = "target" + File.separator + "reference-output-files.xml";
private static final String PYTHON_OUTPUT_FILES_TEMPLATE = "dom-output-files.vm";
private static final String PYTHON_OUTPUT_FILES = "target" + File.separator + "python-output-files.xml";
private static final String DOM_OUTPUT_FILES_TEMPLATE = "dom-output-files.vm";
private static final String DOM_OUTPUT_FILES = "target" + File.separator + "dom-output-files.xml";
private static final String PKG_MANIFEST_OUTPUT_FILES_TEMPLATE = "pkg-manifest-output-files.vm";
private static final String PKG_MANIFEST_OUTPUT_FILES = "target" + File.separator + "pkg-manifest-output-files.xml";
private static final String PKG_DSIG_OUTPUT_FILES_TEMPLATE = "pkg-dsig-output-files.vm";
private static final String PKG_DSIG_OUTPUT_FILES = "target" + File.separator + "pkg-dsig-output-files.xml";
private static XMLModel mOdf12SignatureSchemaModel;
private static XMLModel mOdf12ManifestSchemaModel;
private static XMLModel mOdf12SchemaModel;
private static XMLModel mOdf11SchemaModel;
private static OdfModel mOdfModel;
private static SourceCodeModel mJavaModel;
private static Expression mOdf12SignatureRoot;
private static Expression mOdf12ManifestRoot;
private static Expression mOdf12Root;
private static Expression mOdf11Root;
public OdfHelper(String domResourceRoot, String odf12SchemaFile, String odf11SchemaFile, String pkgResourceRoot, String odf12SignatureSchemaFile, String odf12ManifestSchemaFile, String targetRoot, String configFile) {
odfDomResourceDir = domResourceRoot;
odf12RngFile = odf12SchemaFile;
odf11RngFile = odf11SchemaFile;
odfPkgResourceDir = pkgResourceRoot;
odf12SignatureRngFile = odf12SignatureSchemaFile;
odf12ManifestRngFile = odf12ManifestSchemaFile;
outputRoot = targetRoot;
mConfigFile = configFile;
}
static {
odfDomResourceDir = TEST_INPUT_ROOT + File.separator + "odfdom-java" + File.separator + "dom";
odfPkgResourceDir = TEST_INPUT_ROOT + File.separator + "odfdom-java" + File.separator + "pkg";
odfPythonResourceDir = TEST_INPUT_ROOT + File.separator + "odfdom-python";
odfReferenceResourceDir = TEST_INPUT_ROOT + File.separator + "odf-reference";
odfSchemaResourceDir = TEST_INPUT_ROOT + File.separator + "odf-schemas";
odf12SignatureRngFile = odfSchemaResourceDir + File.separator + ODF12_SIGNATURE_RNG_FILE_NAME;
odf12ManifestRngFile = odfSchemaResourceDir + File.separator + ODF12_MANIFEST_RNG_FILE_NAME;
odf12RngFile = odfSchemaResourceDir + File.separator + ODF12_RNG_FILE_NAME;
odf11RngFile = odfSchemaResourceDir + File.separator + ODF11_RNG_FILE_NAME;
odf10RngFile = odfSchemaResourceDir + File.separator + ODF10_RNG_FILE_NAME;
outputRoot = "target";
mConfigFile = TEST_INPUT_ROOT + File.separator + "config.xml";
}
public void start() throws Exception {
LOG.info("Starting code generation:");
initialize();
// ODF 1.2 Code Generation
fillTemplates(odfDomResourceDir, mOdf12Root, DOM_OUTPUT_FILES_TEMPLATE, DOM_OUTPUT_FILES, mOdf12SchemaModel);
fillTemplates(odfPkgResourceDir, mOdf12ManifestRoot, PKG_MANIFEST_OUTPUT_FILES_TEMPLATE, PKG_MANIFEST_OUTPUT_FILES, mOdf12ManifestSchemaModel);
fillTemplates(odfPkgResourceDir, mOdf12SignatureRoot, PKG_DSIG_OUTPUT_FILES_TEMPLATE, PKG_DSIG_OUTPUT_FILES, mOdf12SignatureSchemaModel);
}
public static void main(String[] args) throws Exception {
LOG.info("Starting code generation:");
initialize();
// ODF 1.2 HTML Reference (yet without BNF nor images)
fillTemplates(odfReferenceResourceDir, mOdf12Root, REFERENCE_OUTPUT_FILES_TEMPLATE, REFERENCE_OUTPUT_FILES, mOdf12SchemaModel);
// ODF 1.2 Python (The generated Python source is from a former colleague and might not work any longer..)
fillTemplates(odfPythonResourceDir, mOdf12Root, PYTHON_OUTPUT_FILES_TEMPLATE, PYTHON_OUTPUT_FILES, mOdf12SchemaModel);
// ODF 1.2 Code Generation
fillTemplates(odfDomResourceDir, mOdf12Root, DOM_OUTPUT_FILES_TEMPLATE, DOM_OUTPUT_FILES, mOdf12SchemaModel);
fillTemplates(odfPkgResourceDir, mOdf12ManifestRoot, PKG_MANIFEST_OUTPUT_FILES_TEMPLATE, PKG_MANIFEST_OUTPUT_FILES, mOdf12ManifestSchemaModel);
fillTemplates(odfPkgResourceDir, mOdf12SignatureRoot, PKG_DSIG_OUTPUT_FILES_TEMPLATE, PKG_DSIG_OUTPUT_FILES, mOdf12SignatureSchemaModel);
}
private static void initialize() throws Exception {
LOG.info("Starting initilization..");
// calling MSV to parse the ODF 1.2 DSIG RelaxNG, returning a tree
mOdf12SignatureRoot = loadSchema(new File(odf12SignatureRngFile));
// calling MSV to parse the ODF 1.2 Manifest RelaxNG, returning a tree
mOdf12ManifestRoot = loadSchema(new File(odf12ManifestRngFile));
// calling MSV to parse the ODF 1.2 RelaxNG, returning a tree
mOdf12Root = loadSchema(new File(odf12RngFile));
// calling MSV to parse the ODF 1.1 RelaxNG, returning a tree
mOdf11Root = loadSchema(new File(odf11RngFile));
// Read config.xml 2DO WHAT IS ODFDOM GENERATOR CONFIG FILE
// Manual added Java specific info - Base class for inheritance
Map<String, String> elementToBaseNameMap = new HashMap<String, String>();
// Manual added ODF specific info - style family mapping
Map<String, List<String>> elementStyleFamiliesMap = new HashMap<String, List<String>>();
// 2DO - still existent? -- Manual added Java specific info - mapping ODF datatype to Java datatype -> {odfValueType, javaConversionClassName}
Map<String, String[]> datatypeValueAndConversionMap = new HashMap<String, String[]>();
Map<String, OdfModel.AttributeDefaults> attributeDefaultMap = new HashMap<String, OdfModel.AttributeDefaults>();
OdfConfigFileHandler.readConfigFile(new File(mConfigFile), elementToBaseNameMap, attributeDefaultMap, elementStyleFamiliesMap, datatypeValueAndConversionMap);
mOdf12SignatureSchemaModel = new XMLModel(mOdf12SignatureRoot);
mOdf12ManifestSchemaModel = new XMLModel(mOdf12ManifestRoot);
mOdf12SchemaModel = new XMLModel(mOdf12Root);
mOdf11SchemaModel = new XMLModel(mOdf11Root);
mOdfModel = new OdfModel(elementStyleFamiliesMap, attributeDefaultMap);
// Needed for the base classes - common attributes are being moved into the base classes
mJavaModel = new SourceCodeModel(mOdf12SchemaModel, mOdf12SignatureSchemaModel, mOdf12ManifestSchemaModel, mOdfModel, elementToBaseNameMap, datatypeValueAndConversionMap);
LOG.info("Finished initilization..");
}
private static void fillTemplates(String sourceDir, Expression root, String outputRuleTemplate, String outputRuleFile, XMLModel model) throws Exception {
// intialising template engine (ie. Velocity)
Properties props = new Properties();
props.setProperty("file.resource.loader.path", sourceDir);
VelocityEngine ve = new VelocityEngine(props);
ve.init();
// Create output-files.xml
createOutputFileList(ve, outputRuleTemplate, outputRuleFile, model);
LOG.info("output-files.xml created done.");
// Process output-files.xml, create output files
LOG.fine("Processing output files... ");
processFileList(ve, root, outputRuleFile, model);
LOG.fine("DONE.\n");
}
/**
* Load and parse the ODF 1.0 Schema.
*
* @return MSV Expression Tree of ODF 1.0 RelaxNG schema (more specific: The
* tree's MSV root expression)
* @throws Exception
*/
public static Expression loadSchemaODF10() throws Exception {
return loadSchema(new File(odf10RngFile));
}
/**
* Load and parse the ODF 1.1 Schema.
*
* @return MSV Expression Tree of ODF 1.1 RelaxNG schema (more specific: The
* tree's MSV root expression)
* @throws Exception
*/
public static Expression loadSchemaODF11() throws Exception {
return loadSchema(new File(odf11RngFile));
}
/**
* Load and parse the ODF 1.2 Schema.
*
* @return MSV Expression Tree of ODF 1.2 RelaxNG schema (more specific: The
* tree's MSV root expression)
* @throws Exception
*/
public static Expression loadSchemaODF12() throws Exception {
return loadSchema(new File(odf12RngFile));
}
/**
* Load and parse a Schema from File.
*
* @param rngFile
* @return MSV Expression Tree (more specific: The tree's MSV root
* expression)
* @throws Exception
*/
public static Expression loadSchema(File rngFile) throws Exception {
SAXParserFactory saxFactory = new org.apache.xerces.jaxp.SAXParserFactoryImpl();
saxFactory.setNamespaceAware(true);
saxFactory.setValidating(false);
try {
saxFactory.setXIncludeAware(false);
saxFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// removing potential vulnerability: see https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing
saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
saxFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
} catch (Exception ex) {
Logger.getLogger(OdfHelper.class.getName()).log(Level.SEVERE, null, ex);
throw new RuntimeException();
}
// Parsing the Schema with MSV
String absolutePath = rngFile.getAbsolutePath();
com.sun.msv.reader.util.IgnoreController ignoreController = new com.sun.msv.reader.util.IgnoreController();
Expression root = RELAXNGReader.parse(absolutePath, saxFactory, ignoreController).getTopLevel();
if (root == null) {
throw new Exception("Schema could not be parsed.");
}
return root;
}
private static VelocityContext getContext(String contextStr, String param) {
VelocityContext context = new VelocityContext();
context.put("oldmodel", mOdf11SchemaModel);
context.put("odfmodel", mOdfModel);
context.put("javamodel", mJavaModel);
context.put("context", contextStr);
context.put("param", param);
return context;
}
private static void createOutputFileList(VelocityEngine ve, String template, String output, XMLModel model) throws Exception {
VelocityContext context = getContext(null, null);
context.put("model", model);
File parentPatch = new File(output).getParentFile();
if (!parentPatch.exists()) {
parentPatch.mkdirs();
}
FileWriter listout = new FileWriter(new File(output));
String encoding = "utf-8";
ve.mergeTemplate(template, encoding, context, listout);
listout.close();
}
private static String generateFilename(String rawName) {
String retFilePath = null;
StringTokenizer toktok = new StringTokenizer(rawName.replaceAll(":", "_"), "/");
if (toktok.hasMoreTokens()) {
File retfile = null;
retfile = new File(toktok.nextToken());
while (toktok.hasMoreTokens()) {
retfile = new File(retfile, toktok.nextToken());
}
retFilePath = retfile.getPath();
}
return retFilePath;
}
private static void ensureParentFolders(File newFile) {
File parent = newFile.getParentFile();
if (parent != null && !parent.exists()) {
try {
parent.mkdirs();
} catch (Exception e) {
LOG.log(Level.WARNING, "Could not create parent directory {0}", parent.getAbsolutePath());
}
}
}
public static void processFileList(VelocityEngine ve, Expression root, String outputRuleFile, XMLModel model) throws Exception {
File outputFiles = new File(outputRuleFile);
List<OutputFileListEntry> fl = OutputFileListHandler.readFileListFile(outputFiles);
for (OutputFileListEntry f : fl) {
switch (f.getType()) {
case PATH:
break;
case FILE:
LOG.log(Level.INFO, "Processing line {0}: Generating file {1}\n", new Object[]{f.getLineNumber(), generateFilename(f.getAttribute("path"))});
String odfContextStr = f.getAttribute("context");
String param = f.getAttribute("param");
VelocityContext context = getContext(odfContextStr, param);
if (context == null) {
throw new RuntimeException("Error in output-files.xml, line " + f.getLineNumber() + ": no or invalid odf-scope");
}else{
context.put("model", model);
}
File out = new File(outputRoot + File.separator + generateFilename(f.getAttribute("path"))).getCanonicalFile();
LOG.info("Absolute path of generated file: " + out.getAbsolutePath());
ensureParentFolders(out);
FileWriter fileout = new FileWriter(out);
String encoding = "utf-8";
ve.mergeTemplate(f.getAttribute("template"), encoding, context, fileout);
fileout.close();
break;
}
}
}
}