/*
* JDynamiTe - Dynamic Template in Java
* Copyright (C) 2001, 2002, 2014, Christophe Bouleau
*
* This file is part of JDynamiTe.
*
* JDynamiTe is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JDynamiTe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JDynamiTe. If not, see .
*
*/
package cb.jdynamite.tool;
import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import cb.jdynamite.JDynamiTe;
import cb.jdynamite.analyser.DefaultAnalyser;
import cb.jdynamite.analyser.IDynamicElement;
/**
* JDynTool is a tool which is based on JDynamiTe, allowing to use JDynamiTe template files by two very simple ways:
* - by command line invocation: no java code is needed,
* - or by JDynTool "run" method call: only few lines of code in your java application.
*
* 1) Command line invocation
*
See {@link JDynTool#main(String[])} usage.
*
* 2) Programmatic invocation
*
* Typical instructions sequence in your Java code:
*
* - Create a JDynTool object.
* - For the first call: you have to specify the "JDynamiTe input Template file" in addition to other parameters.
* So, call either {@link JDynTool#run(String, String, Hashtable, boolean)} method,
* or {@link JDynTool#run(InputStream, String, Hashtable, boolean)} method.
* - For the subsequent calls (if any):
* You can call the second {@link JDynTool#run(String, Hashtable, boolean)} method,
* which will reuse the same template (previously loaded).
* - Then call {@link JDynamiTe#toString()} method on the JDynamiTe returned object to get the parsing result
* (i.e. the populated output document).
*
*
* Dependencies:
* In addition to JDynamiTe classes, JDynTool requires "java-getopt-1.0.14.jar", included in JDynamiTe distribution.
* "java-getopt" is a Java port of GNU getopt, provided by Aaron M. Renn
* (thanks to him and to developers who support this package).
*
*/
public class JDynTool {
private String inputTemplateFileOpt = "";
private InputStream inputStreamTemplateFileOpt;
private String defaultRootXMLdoc;
private Hashtable keyValuesOpt = new Hashtable();
private boolean verboseOpt = false;
private boolean getDefOpt = false;
private boolean getKeysOpt = false;
private JDynamiTe jDyn;
private ArrayList undefinedInputXMLElements;
/**
* Special variable tag, {__INPUT_TEMPLATE__}, that you can use in your
* template, and which is automatically populated with the JDynamiTe input file path string.
*/
public static final String INPUT_TEMPLATE_PATH_VAR_TAG = "__INPUT_TEMPLATE__";
/**
* Creates a JDynTool object and its associated JDynamiTe object.
* This JDynTool object is then used to call its {@link #run(String, String, Hashtable, boolean)} method.
* @see #run(String, String, Hashtable, boolean)
*/
public JDynTool() {
jDyn = new JDynamiTe("RootJDyn");
undefinedInputXMLElements = new ArrayList();
}
/**
* Reads, analyses and processes the input template file and then produces
* the output result. This method uses a JDynamiTe object to process the
* input template file and then produce the output result.
* The global process of this method consists in:
* 1) processing all "simple" JDynamiTe Variables, by calling
* {@link JDynamiTe#setVariable(String, String)} method for each key-value
* pair.
* 2) processing recursively all XML Dynamic blocks, by calling the
* {@link JDynamiTe#parseXMLDynElem()} method.
*
* @param inputTemplateFile
* Input template file path. This path will be used to call the
* {@link JDynamiTe#setInput(String)} method.
* @param defaultRootXMLInputDoc
* Default URI for first level XML dynamic block(s) which do not
* have an URI in their definition in template file. This
* optional parameter can be null.
* @param keyValuesList
* List of key-value pairs used to set values to the JDynamiTe
* variables contained in the input template. Each key-value pair
* will be passed to the
* {@link JDynamiTe#setVariable(String, String)} method.
* @param verbose
* If true, print on stderr output some information messages
* during document processing.
* @return If the inputTemplateFile can be read and analysed, this method
* returns the JDynamiTe object used to process the template
* document.
* Use then the {@link JDynamiTe#toString()} method on this returned
* object to get the parsing result (i.e. the populated output
* document).
* If the inputTemplateFile is undefined or can not be read, this
* method returns null.
* @see JDynamiTe#setInput(String)
* @see JDynamiTe#setVariable(String, String)
*/
public JDynamiTe run(String inputTemplateFile, String defaultRootXMLInputDoc, Hashtable keyValuesList, boolean verbose) {
inputTemplateFileOpt = inputTemplateFile;
inputStreamTemplateFileOpt = null;
defaultRootXMLdoc = defaultRootXMLInputDoc;
if (keyValuesList != null)
keyValuesOpt.putAll(keyValuesList);
verboseOpt = verbose;
return run(true);
}
/**
* Reads, analyses and processes the input template file and then produces
* the output result. This method uses a JDynamiTe object to process the
* input template file and then produce the output result.
* The global process of this method consists in:
* 1) processing all "simple" JDynamiTe Variables, by calling
* {@link JDynamiTe#setVariable(String, String)} method for each key-value
* pair.
* 2) processing recursively all XML Dynamic blocks, by calling the
* {@link JDynamiTe#parseXMLDynElem()} method.
*
* @param inputStreamTemplateFile
* InputStream attached to the input template. This value will be used to call the
* {@link JDynamiTe#setInput(InputStream)} method.
* @param defaultRootXMLInputDoc
* Default URI for first level XML dynamic block(s) which do not
* have an URI in their definition in template file. This
* optional parameter can be null.
* @param keyValuesList
* List of key-value pairs used to set values to the JDynamiTe
* variables contained in the input template. Each key-value pair
* will be passed to the
* {@link JDynamiTe#setVariable(String, String)} method.
* @param verbose
* If true, print on stderr output some information messages
* during document processing.
* @return If the inputStreamTemplateFile can be read and analysed, this method
* returns the JDynamiTe object used to process the template
* document.
* Use then the {@link JDynamiTe#toString()} method on this returned
* object to get the parsing result (i.e. the populated output
* document).
* If the inputTemplateFile is undefined or can not be read, this
* method returns null.
* @see JDynamiTe#setInput(String)
* @see JDynamiTe#setVariable(String, String)
*/
public JDynamiTe run(InputStream inputStreamTemplateFile, String defaultRootXMLInputDoc, Hashtable keyValuesList, boolean verbose) {
inputStreamTemplateFileOpt = inputStreamTemplateFile;
inputTemplateFileOpt = "";
defaultRootXMLdoc = defaultRootXMLInputDoc;
if (keyValuesList != null)
keyValuesOpt.putAll(keyValuesList);
verboseOpt = verbose;
return run(true);
}
/**
* Processes the input template file and then produces the output result. It
* is assumed that the input template file has already been read and
* analysed by calling either {@link #run(String, String, Hashtable, boolean)}
* method, or {@link #run(InputStream, String, Hashtable, boolean)} method.
* This method is the same than the
* {@link #run(String, String, Hashtable, boolean)} method, except that
* there is no call to the {@link JDynamiTe#setInput(String)} or
* {@link JDynamiTe#setInput(InputStream)} method.
* Call this method if the input template file does not change from one call
* to another, while you need to change other parameters such as Variable
* key-value list.
*
* @param defaultRootXMLInputDoc
* Default URI for first level XML dynamic block(s) which do not
* have an URI in their definition in template file. This
* optional parameter can be null.
* @param keyValuesList
* keyValuesList List of key-value pairs used to set values to
* the JDynamiTe variables contained in the input template. Each
* key-value pair will be passed to the
* {@link JDynamiTe#setVariable(String, String)} method.
* @param verbose
* If true, print on stderr output some information messages
* during document processing.
* @return This method returns null if no input template file was defined by
* a previous call to the
* {@link #run(String, String, Hashtable, boolean)} method.
* Otherwise this method returns the JDynamiTe object used to
* process the template document.
* Use then the {@link JDynamiTe#toString()} method on this returned
* object to get the parsing result (i.e. the populated output
* document).
* If the inputTemplateFile is undefined or can not be read, this
* method returns null.
*/
public JDynamiTe run(String defaultRootXMLInputDoc, Hashtable keyValuesList, boolean verbose) {
defaultRootXMLdoc = defaultRootXMLInputDoc;
if (keyValuesList != null)
keyValuesOpt.putAll(keyValuesList);
verboseOpt = verbose;
return run(false);
}
/**
* Processes the input template file and then produces the output result.
* This method uses a JDynamiTe object to process the input template file
* and then produce the output result.
* 1) processing all "simple" JDynamiTe Variables, by calling
* {@link JDynamiTe#setVariable(String, String)} method for each key-value
* pair.
* 2) processing recursively all XML Dynamic blocks, by calling the
* {@link JDynamiTe#parseXMLDynElem()} method.
*
* @param doSetInput
* If true, the previously defined input template file name will
* be used to call the {@link JDynamiTe#setInput(String)} method.
* If false, the current template is used, after clearing all
* variables.
*
* @return If the inputTemplateFile is undefined or can not be read, this
* method returns null.
* Otherwise this method returns the JDynamiTe object used to
* process the template document.
*/
private JDynamiTe run(boolean doSetInput) {
JDynamiTe.setVerbose(verboseOpt);
if (inputTemplateFileOpt.equals("") && inputStreamTemplateFileOpt == null) {
System.err.println("Error: undefined input template file !");
return null;
}
if (doSetInput) {
if (verboseOpt)
System.err.println("Calling jDyn.setInput...");
try {
if (inputStreamTemplateFileOpt != null)
jDyn.setInput(inputStreamTemplateFileOpt);
else
jDyn.setInput(inputTemplateFileOpt);
}
catch (Exception e) {
System.err.println(e.getMessage());
return null;
}
undefinedInputXMLElements.clear();
}
else {
jDyn.resetAll();
restoreUndefinedXMLInput();
}
if (verboseOpt)
System.err.println("XMLdefaultRootDoc=" + ((defaultRootXMLdoc != null) ? defaultRootXMLdoc : "null"));
if (getDefOpt) {
String totalDef = jDyn.getDefinition(0);
System.err.println("\n----- Template Definition -----\n\n" + totalDef + "\n-----\n");
}
if (getKeysOpt) {
System.err.println("\n===== keys =====\n");
Enumeration keys = jDyn.getVariableKeys();
while (keys.hasMoreElements()) {
System.err.println("[" + keys.nextElement() + "]");
}
System.err.println("\n=====\n");
}
processVariables();
processDynElem();
parseXMLDynElem();
///processXML___test__manuel();
jDyn.parse();
return jDyn;
}
private void parseXMLDynElem() {
jDyn.parseXMLDynElem();
}
private void processDynElem() {
Enumeration keyDynElems = jDyn.getDynElemenKeys();
while (keyDynElems.hasMoreElements()) {
String elemName = keyDynElems.nextElement();
if (jDyn.isXMLDynElement(elemName)) {
if (defaultRootXMLdoc != null) {
// Set an "XMLInput" for all "first level" XML dyn. elem which have none.
IDynamicElement xmlDynElem = jDyn.getDynElem(elemName);
if (xmlDynElem.getFatherDynElem() == jDyn) {
// Parent is jDyn so this is a first level element.
String xmlInput = xmlDynElem.getXMLInput();
if (xmlInput == DefaultAnalyser.XML_UNDEFINED_INPUT) {
if (verboseOpt)
System.err.println("Setting default XML input \"" + defaultRootXMLdoc + "\" for \"" + elemName + "\" block");
jDyn.setXMLInput(elemName, defaultRootXMLdoc);
undefinedInputXMLElements.add(xmlDynElem);
}
else {
if (verboseOpt)
System.err.println("XML input for \"" + elemName + "\" block = " + xmlInput);
}
}
}
}
else if (jDyn != jDyn.getDynElem(elemName)) {
// parse all non XML blocks, except the "root elem." (i.e jDyn itself)
/* In fact: can suppress this code block because it can not do anything: it is useless
(without input data set by setVariable method) to iterate on these blocks ! */
if (verboseOpt)
System.err.println("Parsing dyn. elem. \"" + elemName + "\"");
jDyn.parseDynElem(elemName);
}
}
}
private void restoreUndefinedXMLInput() {
for (IDynamicElement xmlElem : undefinedInputXMLElements) {
xmlElem.setXMLInput(DefaultAnalyser.XML_UNDEFINED_INPUT);
}
}
private void processVariables() {
Enumeration keyVars = keyValuesOpt.keys();
while (keyVars.hasMoreElements()) {
String key = keyVars.nextElement();
String value = keyValuesOpt.get(key);
jDyn.setVariable(key, value);
if (verboseOpt)
System.err.println("Setting value \"" + value + "\" for variable \"" + key + '\"');
}
// Set __INPUT_TEMPLATE__ as "special" variable if not already set by the user.
String inputTemplVar = jDyn.getVariable(JDynTool.INPUT_TEMPLATE_PATH_VAR_TAG);
if (inputTemplVar != null && inputTemplVar.equals("")) {
// Set default value for __INPUT_TEMPLATE__ : relative to exampleRootPath or not ? to be improved ...
jDyn.setVariable(JDynTool.INPUT_TEMPLATE_PATH_VAR_TAG, /*exampleRootPath + /"input/"*/ new File(inputTemplateFileOpt).getName());
if (verboseOpt)
System.err.println("Setting value \"" + inputTemplateFileOpt + "\" for special variable \"" + JDynTool.INPUT_TEMPLATE_PATH_VAR_TAG + '\"');
}
}
private String getInputTemplateFileName() {
return inputTemplateFileOpt;
}
private void setInputTemplateFileName(String inputTemplateFile) {
inputTemplateFileOpt = inputTemplateFile;
}
/* private InputStream getInputStreamTemplateFile() {
return inputStreamTemplateFileOpt;
}
*/
private void setInputStreamTemplateFile(InputStream inputStreamTemplateFile) {
inputStreamTemplateFileOpt = inputStreamTemplateFile;
}
private static void usage() {
System.err.println("\nUsage: JDynTool --template jdyn-template-file [--definition] [--keys] [--verbose] [--XMLdefaultRootDoc URI] [-D = ...]\n");
System.err.println(" --template jdyn-file\n -t jdyn-file \t specify JDynamiTe input template file");
System.err.println(" --definition\n -d \t\t\t output on stderr the JDynamiTe input template file structure definition (as interpreted by JDynamiTe)");
System.err.println(" --keys\n -k \t\t\t output on stderr the list of JDynamiTe Variable tags (keys) found in the JDynamiTe input template file");
System.err.println(" --verbose\n -v \t\t\t output on stderr some information messages during document processing");
System.err.println(" --XMLdefaultRootDoc URI-path\n -X URI-path \t\t Default URI/path for first level XML dynamic blocks which do not have an URI in their definition in template file");
System.err.println(" -D = ...\t List of key-value pairs used to set values to the JDynamiTe variables contained in the input template");
}
private boolean processOpt(int opt, Getopt g) {
//System.out.println("JDynTool.processOpt(), opt=" + (char)opt);
boolean optValid = true;
String arg;
switch (opt) {
case 'd':
getDefOpt = true;
break;
case 'k':
getKeysOpt = true;
break;
case 't':
arg = g.getOptarg();
inputTemplateFileOpt = arg;
break;
case 'X':
arg = g.getOptarg();
defaultRootXMLdoc = arg;
break;
case 'D':
arg = g.getOptarg();
String[] subargs = arg.split("=", 2);
//System.err.println("key=" + subargs[0] + ", value=" + subargs[1]);
keyValuesOpt.put(subargs[0], subargs[1]);
break;
case 'h':
optValid = false;
case 'v':
verboseOpt = true;
break;
default:
optValid = false;
}
return optValid;
}
private boolean parseCommandLine(String[] args) {
StringBuffer longOption = new StringBuffer();
LongOpt[] longopts = new LongOpt[5];
longopts[0] = new LongOpt("verbose", LongOpt.NO_ARGUMENT, null, 'v');
longopts[1] = new LongOpt("definition", LongOpt.NO_ARGUMENT, null, 'd');
longopts[2] = new LongOpt("keys", LongOpt.NO_ARGUMENT, null, 'k');
longopts[3] = new LongOpt("template", LongOpt.REQUIRED_ARGUMENT, longOption, 't');
longopts[4] = new LongOpt("XMLdefaultRootDoc", LongOpt.REQUIRED_ARGUMENT, longOption, 'X');
Getopt g = new Getopt("JDynTool", args, "vhdkt:D:X:", longopts);
//
int c, opt;
boolean optOK = true;
while ((c = g.getopt()) != -1) {
switch (c) {
case 0:
opt = (new Integer(longOption.toString())).intValue();
break;
case '?':
opt = -1;
optOK = false;
break; // getopt() already printed an error
default:
opt = c;
}
if (opt != -1)
if (!processOpt(opt, g))
optOK = false;
}
//System.err.println("inputTemplateFile=" + inputTemplateFile + ", inputXMLFile=" + inputXMLFile);
if (!optOK || inputTemplateFileOpt.equals("")) {
return false;
}
return true;
}
/**
* @param args Parameters for Command line invocation.
*
* Usage:
*
{@code JDynTool --template jdyn-template-file [--definition] [--keys] [--verbose] [--XMLdefaultRootDoc URI] [-D = ...]}
{@code --template jdyn-file}
{@code -t jdyn-file}
Specify JDynamiTe input template file.
{@code --definition}
{@code -d}
Output on stderr the JDynamiTe input template file structure definition (as interpreted by JDynamiTe).
{@code --keys}
{@code -k}
Output on stderr the list of JDynamiTe Variable tags (keys) found in the JDynamiTe input template file.
{@code --verbose}
{@code -v}
Output on stderr some information messages during document processing.
{@code --XMLdefaultRootDoc URI-path}
{@code -X URI-path}
Default URI/path for first level XML dynamic blocks which do not have an URI-path in their definition in template file.
{@code -D = ...}
List of key-value pairs used to set values to the JDynamiTe variables contained in the input template}
*/
public static void main(String[] args) {
JDynTool jDynTool = new JDynTool();
if (!jDynTool.parseCommandLine(args)) {
usage();
return;
}
JDynamiTe jDyn = jDynTool.run(true);
if (jDyn != null)
System.out.println(jDyn.toString());
}
// Test via InputStream
protected static void mainTestInputStream(String[] args) {
JDynTool jDynTool = new JDynTool();
if (!jDynTool.parseCommandLine(args)) {
usage();
return;
}
try {
InputStream istream = new FileInputStream(jDynTool.getInputTemplateFileName());
jDynTool.setInputStreamTemplateFile(istream);
jDynTool.setInputTemplateFileName("");
JDynamiTe jDyn = jDynTool.run(true);
if (jDyn != null)
System.out.println(jDyn.toString());
istream.close();
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}