/*
* @(#)ExiProcessor.java
*
* Copyright (C) 2011 The MITRE Corporation
*
* This program and its interfaces are free software;
* you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Map;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import com.siemens.ct.exi.CodingMode;
import com.siemens.ct.exi.EXIFactory;
import com.siemens.ct.exi.FidelityOptions;
import com.siemens.ct.exi.GrammarFactory;
import com.siemens.ct.exi.SchemaIdResolver;
import com.siemens.ct.exi.api.sax.EXIResult;
import com.siemens.ct.exi.api.sax.EXISource;
import com.siemens.ct.exi.exceptions.EXIException;
import com.siemens.ct.exi.exceptions.UnsupportedOption;
import com.siemens.ct.exi.grammar.Grammar;
import com.siemens.ct.exi.helpers.DefaultEXIFactory;
/**
* The ExiProcessor class defines static methods (including main())
* to demonstrate the use of EXI encoding and decoding
* using the EXIficient library (http://exificient.sourceforge.net/).
*
* For more information on Efficient XML Interchange (XML),
* see http://www.w3.org/TR/2011/REC-exi-20110310/
*
* @author Craig Garrett, MITRE, garrett@mitre.org
*/
public class ExiProcessor {
private static NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH); // for pretty-printing file sizes
private static final String COMMAND_LINE = "java -jar ExiProcessor.jar";
public static boolean isEncoding(Map<ExiOption, String> commandLineArgs) {
return commandLineArgs.containsKey(ExiOption.XML_IN) && commandLineArgs.containsKey(ExiOption.EXI_OUT) && !commandLineArgs.containsKey(ExiOption.XML_OUT) && !commandLineArgs.containsKey(ExiOption.EXI_IN);
}
public static void main(String[] args) throws IOException {
Map<ExiOption, String> commandLineArgs = null;
if (args.length < 2)
printHelp(); // this calls System.exit(0)
else
commandLineArgs = parseCommandLineArgs(args); // this calls System.exit(0) if there is an error
try {
if (isEncoding(commandLineArgs)) {
// read in XML and write out EXI to a file
File inputFile = new File(commandLineArgs.get(ExiOption.XML_IN));
long inputSize = inputFile.length();
System.out.println("Reading XML: " + commandLineArgs.get(ExiOption.XML_IN) + "\n");
util.BufferedOutputStream os = new util.BufferedOutputStream(new FileOutputStream(commandLineArgs.get(ExiOption.EXI_OUT)));
EXIFactory exiFactory = createEXIFactory(commandLineArgs);
System.out.println("Writing EXI: " + commandLineArgs.get(ExiOption.EXI_OUT) + "\n");
xmlToExi(commandLineArgs, inputFile, os, exiFactory);
long outputSize = os.getCount();
os.close();
double percentageSaved = ExiProcessor.round((1.0 - ((double) outputSize) / ((double) inputSize)) * 100.0, 1);
double ratio = ExiProcessor.round((double) inputSize / (double) outputSize, 1);
System.out.println("EXI encoding reduced the XML from " + nf.format(inputSize) + " bytes to " + nf.format(outputSize) + " bytes");
System.out.println("Space Savings: " + percentageSaved + "%, Compression Ratio: " + ratio);
}
else if (commandLineArgs.containsKey(ExiOption.EXI_IN) && commandLineArgs.containsKey(ExiOption.XML_OUT) && !commandLineArgs.containsKey(ExiOption.EXI_OUT) && !commandLineArgs.containsKey(ExiOption.XML_IN)) {
// read in EXI and write out XML to a file
System.out.println("Reading EXI: " + commandLineArgs.get(ExiOption.EXI_IN));
util.BufferedOutputStream os = new util.BufferedOutputStream(new FileOutputStream(commandLineArgs.get(ExiOption.XML_OUT)));
EXIFactory exiFactory = createEXIFactory(commandLineArgs);
System.out.println("Writing XML: " + commandLineArgs.get(ExiOption.XML_OUT) + "\n");
exiToXml(commandLineArgs, os, exiFactory);
System.out.println("Finished writing " + nf.format(os.getCount()) + " bytes of XML to " + commandLineArgs.get(ExiOption.XML_OUT));
os.close();
}
else if (commandLineArgs.containsKey(ExiOption.EXI_IN) && !commandLineArgs.containsKey(ExiOption.XML_OUT) && !commandLineArgs.containsKey(ExiOption.EXI_OUT) && !commandLineArgs.containsKey(ExiOption.XML_IN)) {
// read in EXI and print XML to stdout
System.out.println("Reading EXI: " + commandLineArgs.get(ExiOption.EXI_IN));
System.out.println();
util.BufferedOutputStream os = new util.BufferedOutputStream(System.out);
EXIFactory exiFactory = createEXIFactory(commandLineArgs);
System.out.println("Resulting XML:\n");
exiToXml(commandLineArgs, os, exiFactory);
os.realFlush();
System.out.println("\n\n(" + nf.format(os.getCount()) + " bytes)");
}
else {
printBriefHelp(); // this calls System.exit(0)
}
} catch (FileNotFoundException e) {
System.err.println("ERROR: FileNotFoundException: " + e.getMessage());
System.exit(1);
}
}
/**
* Reads a text XML file from disk, encodes it to EXI, and writes the EXI to an output stream.
*
* @param commandLineArgs a map of ExiOptions and values to be used in encoding the XML into EXI
* @param inputFile the file containing the source XML (in normal text form)
* @param outputStream the destination of the EXI
* @param exiFactory the EXIficient object containing all of the EXI Options and configurations to be used when encoding the XML into EXI
*/
public static void xmlToExi(Map<ExiOption, String> commandLineArgs, File inputFile, OutputStream outputStream, EXIFactory exiFactory) {
try {
EXIResult exiResult = new EXIResult(exiFactory);
exiResult.setOutputStream(outputStream);
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setContentHandler(exiResult.getHandler());
xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", exiResult.getLexicalHandler()); // set LexicalHandler - see http://xerces.apache.org/xerces2-j/properties.html
xmlReader.setProperty("http://xml.org/sax/properties/declaration-handler", exiResult.getLexicalHandler()); // set DeclHandler - see http://xerces.apache.org/xerces2-j/properties.html
xmlReader.parse(new InputSource(new FileInputStream(inputFile)));
} catch (FileNotFoundException e) {
System.err.println("ERROR: FileNotFoundException: " + e.getMessage());
e.printStackTrace();
System.exit(1);
} catch (IOException e) {
System.err.println("ERROR: IOException: " + e.getMessage());
e.printStackTrace();
System.exit(1);
} catch (EXIException e) {
System.err.println("ERROR: EXIException: " + e.getMessage());
e.printStackTrace();
System.exit(1);
} catch (SAXException e) {
System.err.println("ERROR: SAXException: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
/**
* Reads a binary EXI file from disk, decodes it to text XML, and writes the XML to an output stream.
*
* @param commandLineArgs a map of ExiOptions and values to be used in decoding the EXI into XML
* @param outputStream the destination of the EXI
* @param exiFactory the EXIficient object containing all of the EXI Options and configurations to be used when encoding the XML into EXI
*/
public static void exiToXml(Map<ExiOption, String> commandLineArgs, OutputStream outputStream, EXIFactory exiFactory) {
try {
EXISource exiSource = new EXISource(exiFactory);
XMLReader xmlReader = exiSource.getXMLReader();
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
InputStream inputStream = new FileInputStream(commandLineArgs.get(ExiOption.EXI_IN));
SAXSource saxSource = new SAXSource(new InputSource(inputStream));
saxSource.setXMLReader(xmlReader);
//transformer.setOutputProperty(OutputKeys.METHOD, "xml");
//transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "");
//System.out.println("transformer.getOutputProperties().toString() = " + transformer.getOutputProperties().toString());
transformer.transform(saxSource, new StreamResult(outputStream));
} catch (EXIException e) {
System.err.println("ERROR: EXIException: " + e.getMessage());
e.printStackTrace();
System.exit(1);
} catch (TransformerConfigurationException e) {
System.err.println("ERROR: TransformerConfigurationException: " + e.getMessage());
e.printStackTrace();
System.exit(1);
} catch (FileNotFoundException e) {
System.err.println("ERROR: FileNotFoundException: " + e.getMessage());
e.printStackTrace();
System.exit(1);
} catch (TransformerException e) {
System.err.println("ERROR: TransformerException: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
/**
* This method uses the given command line arguments to
* construct and configure a com.siemens.ct.exi.EXIFactory
* object which will be used to either encode or decode EXI.
*
* @param commandLineArgs a map of ExiOption enum values and String values (not all ExiOption enums require a String value)
*/
private static com.siemens.ct.exi.EXIFactory createEXIFactory(Map<ExiOption, String> commandLineArgs) {
System.out.println("\tUsing the following EXI Options:");
EXIFactory exiFactory = DefaultEXIFactory.newInstance(); // construct a basic com.siemens.ct.exi.EXIFactory
boolean headerOptionsAutoEnabledBecauseOfSchemaID = false;
if (commandLineArgs.containsKey(ExiOption.SCHEMAID) && !commandLineArgs.containsKey(ExiOption.HEADER_OPTIONS)) {
commandLineArgs.put(ExiOption.HEADER_OPTIONS, null);
headerOptionsAutoEnabledBecauseOfSchemaID = true;
}
try {
com.siemens.ct.exi.EncodingOptions encodingOptions = com.siemens.ct.exi.EncodingOptions.createDefault();
// COOKIE
if (commandLineArgs.containsKey(ExiOption.COOKIE)) {
printEnabledMessage(ExiOption.COOKIE);
encodingOptions.setOption(com.siemens.ct.exi.EncodingOptions.INCLUDE_COOKIE);
}
// HEADER_OPTIONS
if (commandLineArgs.containsKey(ExiOption.HEADER_OPTIONS)) {
printEnabledMessage(ExiOption.HEADER_OPTIONS, headerOptionsAutoEnabledBecauseOfSchemaID ? " (auto-enabled due to -" + ExiOption.SCHEMAID.getCommandLineArg() + " option)" : "");
encodingOptions.setOption(com.siemens.ct.exi.EncodingOptions.INCLUDE_OPTIONS);
}
// SCHEMAID
// see below in grammar section
// XSI_SCHEMALOCATION
if (commandLineArgs.containsKey(ExiOption.XSI_SCHEMALOCATION)) {
printEnabledMessage(ExiOption.XSI_SCHEMALOCATION);
encodingOptions.setOption(com.siemens.ct.exi.EncodingOptions.INCLUDE_XSI_SCHEMALOCATION);
}
exiFactory.setEncodingOptions(encodingOptions);
FidelityOptions fidelityOptions = FidelityOptions.createDefault();
// STRICT
if (commandLineArgs.containsKey(ExiOption.STRICT)) {
printEnabledMessage(ExiOption.STRICT);
fidelityOptions.setFidelity(FidelityOptions.FEATURE_STRICT, true);
}
else
fidelityOptions.setFidelity(FidelityOptions.FEATURE_STRICT, false);
// PRESERVE_NAMESPACE_PREFIXES
if (commandLineArgs.containsKey(ExiOption.PRESERVE_NAMESPACE_PREFIXES)) {
printEnabledMessage(ExiOption.PRESERVE_NAMESPACE_PREFIXES);
fidelityOptions.setFidelity(FidelityOptions.FEATURE_PREFIX, true);
}
else
fidelityOptions.setFidelity(FidelityOptions.FEATURE_PREFIX, false);
// PRESERVE_DTD
if (commandLineArgs.containsKey(ExiOption.PRESERVE_DTD)) {
printEnabledMessage(ExiOption.PRESERVE_DTD);
fidelityOptions.setFidelity(FidelityOptions.FEATURE_DTD, true);
}
else
fidelityOptions.setFidelity(FidelityOptions.FEATURE_DTD, false);
// PRESERVE_LEXICAL_VALUES
if (commandLineArgs.containsKey(ExiOption.PRESERVE_LEXICAL_VALUES)) {
printEnabledMessage(ExiOption.PRESERVE_LEXICAL_VALUES);
fidelityOptions.setFidelity(FidelityOptions.FEATURE_LEXICAL_VALUE, true);
}
else
fidelityOptions.setFidelity(FidelityOptions.FEATURE_LEXICAL_VALUE, false);
// PRESERVE_COMMENTS
if (commandLineArgs.containsKey(ExiOption.PRESERVE_COMMENTS)) {
printEnabledMessage(ExiOption.PRESERVE_COMMENTS);
fidelityOptions.setFidelity(FidelityOptions.FEATURE_COMMENT, true);
}
else
fidelityOptions.setFidelity(FidelityOptions.FEATURE_COMMENT, false);
// PRESERVE_PIS
if (commandLineArgs.containsKey(ExiOption.PRESERVE_PI)) {
printEnabledMessage(ExiOption.PRESERVE_PI);
fidelityOptions.setFidelity(FidelityOptions.FEATURE_PI, true);
}
else
fidelityOptions.setFidelity(FidelityOptions.FEATURE_PI, false);
// SELF_CONTAINED
if (commandLineArgs.containsKey(ExiOption.SELF_CONTAINED)) {
printEnabledMessage(ExiOption.SELF_CONTAINED);
fidelityOptions.setFidelity(FidelityOptions.FEATURE_SC, true);
}
else
fidelityOptions.setFidelity(FidelityOptions.FEATURE_SC, false);
exiFactory.setFidelityOptions(fidelityOptions); // tell the EXIFactory to use these FidelityOptions
// CodingMode.COMPRESSION
// CodingMode.BIT_PACKED is recommended for small messages, CodingMode.COMPRESSION is recommended for large messages
if (commandLineArgs.containsKey(ExiOption.COMPRESSION)) {
printEnabledMessage(ExiOption.COMPRESSION);
exiFactory.setCodingMode(CodingMode.COMPRESSION);
}
else {
// handle ALIGNMENT only when COMPRESSION is not specified
if (commandLineArgs.containsKey(ExiOption.ALIGNMENT)) {
CodingMode codingMode = null;
try {
codingMode = CodingMode.valueOf(commandLineArgs.get(ExiOption.ALIGNMENT).toUpperCase());
} catch (Exception e) {
System.out.println("\nError: the value '" + commandLineArgs.get(ExiOption.ALIGNMENT) + "' is not recognized. Permissible values are " + ExiOption.ALIGNMENT.getValuePlacemark());
printHelp(); // this calls System.exit(0)
}
System.out.println("\t\t" + ExiOption.ALIGNMENT.getName() + " set to " + codingMode);
exiFactory.setCodingMode(codingMode);
}
}
// FRAGMENT
if (commandLineArgs.containsKey(ExiOption.FRAGMENT)) {
printEnabledMessage(ExiOption.FRAGMENT);
exiFactory.setFragment(true);
}
// SCHEMA OPTIONS
ExiProcessor.loadGrammar(exiFactory, commandLineArgs);
// BLOCK_SIZE
if (commandLineArgs.containsKey(ExiOption.BLOCK_SIZE)) {
int blockSize = Integer.parseInt(commandLineArgs.get(ExiOption.BLOCK_SIZE));
System.out.println("\t\t" + ExiOption.BLOCK_SIZE.getName() + " = " + blockSize + " bytes");
exiFactory.setBlockSize(blockSize);
}
// VALUE_MAX_LENGTH
if (commandLineArgs.containsKey(ExiOption.VALUE_MAX_LENGTH)) {
int valueMaxLength = Integer.parseInt(commandLineArgs.get(ExiOption.VALUE_MAX_LENGTH));
System.out.println("\t\t" + ExiOption.VALUE_MAX_LENGTH.getName() + " = " + valueMaxLength + " bytes");
exiFactory.setValueMaxLength(valueMaxLength);
}
// VALUE_PARTITION_CAPACITY
if (commandLineArgs.containsKey(ExiOption.VALUE_PARTITION_CAPACITY)) {
int valuePartitionCapacity = Integer.parseInt(commandLineArgs.get(ExiOption.VALUE_PARTITION_CAPACITY));
System.out.println("\t\t" + ExiOption.VALUE_PARTITION_CAPACITY.getName() + " = " + valuePartitionCapacity + " bytes");
exiFactory.setValuePartitionCapacity(valuePartitionCapacity);
}
System.out.println();
} catch (UnsupportedOption e) {
System.err.println("ERROR: UnsupportedOption: " + e.getMessage());
System.exit(1);
}
return exiFactory;
}
/**
* The MySchemaIdResolver class implements the Grammar resolveSchemaId(String schemaId)
* method defined by the EXIficient SchemaIdResolver interface. This class
* assists in loading XML Schemas (i.e. creating EXI Grammars) via the
* EXI Header->schemaId value and the -schema [XML Schema filename]
* command line value.
*/
public static class MySchemaIdResolver implements SchemaIdResolver {
Map<ExiOption, String> commandLineArgs;
GrammarFactory grammarFactory;
public MySchemaIdResolver(Map<ExiOption, String> commandLineArgs, GrammarFactory grammarFactory) {
this.commandLineArgs = commandLineArgs;
this.grammarFactory = grammarFactory;
}
@Override
/**
* Creates or loads an EXI Grammar based on the value of schemaId.
* In EXI processing, this could involve a lookup table or other
* index to find the correct XML schema, or it could simply use
* the value as a filename and attempt to load the file. In this
* case, the method first attempts to find a file at a location
* specified by commandLineArgs.get(ExiOption.SCHEMA) (in other
* words, the value specified by the -schema option on the command
* line). If no such file exists, then the schemaId value is used
* as a file path/name, and the method attempts to load the grammar
* from that location. If neither string value points to an existing
* file, then an error message is printed to System.err and
* System.exit(0) is called.
*
* @param schemaId the schemaId value from the EXI Header
*/
public Grammar resolveSchemaId(String schemaId) throws EXIException {
return resolveSchemaId(schemaId, false);
}
/**
* Creates or loads an EXI Grammar based on the value of schemaId.
* In EXI processing, this could involve a lookup table or other
* index to find the correct XML schema, or it could simply use
* the value as a filename and attempt to load the file. In this
* case, the method first attempts to find a file at a location
* specified by commandLineArgs.get(ExiOption.SCHEMA) (in other
* words, the value specified by the -schema option on the command
* line). If no such file exists, then the schemaId value is used
* as a file path/name, and the method attempts to load the grammar
* from that location. If neither string value points to an existing
* file, then an error message is printed to System.err and
* System.exit(0) is called.
*
* @param schemaId the schemaId value from the EXI Header
* @param manualCall if true, indicates that this method was called directly by code outside of EXIficient, if false, then this method was called by EXIficient as part of EXI processing
*/
public Grammar resolveSchemaId(String schemaId, boolean manualCall) throws EXIException {
Grammar grammar = null;
String explicitSchemaName = commandLineArgs.get(ExiOption.SCHEMA);
File schemaFile = null;
if (explicitSchemaName != null && !explicitSchemaName.isEmpty()) {
schemaFile = new File(explicitSchemaName);
if (!schemaFile.exists() && manualCall && !isEncoding(commandLineArgs)) {
System.out.println("Warning: file given by -" + ExiOption.SCHEMA.getCommandLineArg() + " = '" + explicitSchemaName + "' not found. If the EXI Header contains a schemaId, this program will attempt to use that value.");
}
}
if ((schemaFile == null || !schemaFile.exists()) && schemaId != null && !schemaId.isEmpty()) {
schemaFile = new File(schemaId);
if (schemaFile.exists()) {
System.out.println("EXI Header->SchemaId = '" + schemaId + "'\n");
//inputStream = new FileInputStream(schemaFile);
}
else if (!manualCall && !isEncoding(commandLineArgs)) {
System.out.println("Warning: file given by EXI Header->SchemaId = '" + schemaId + "' not found.");
}
}
if (schemaFile.exists()) {
System.out.println("Loading schema: " + schemaFile.getAbsolutePath());
grammar = grammarFactory.createGrammar(schemaFile.getAbsolutePath());
}
else {
if (!manualCall && !isEncoding(commandLineArgs)) {
System.err.println("ERROR: No Schema Found. -" + ExiOption.SCHEMA.getCommandLineArg() + " = " + explicitSchemaName + ", EXI Header->schemaId = '" + schemaId + "'");
System.exit(0);
}
}
return grammar;
}
}
/**
* This method loads a EXI Grammar object and provides it to the given EXIFactory.
*
* @param exiFactory the EXIFactory to be modified (i.e. given the new Grammar)
* @param commandLineArgs a map of ExiOption enum values and String values (not all ExiOption enums require a String value)
*/
public static void loadGrammar(EXIFactory exiFactory, Map<ExiOption, String> commandLineArgs) {
GrammarFactory grammarFactory = GrammarFactory.newInstance();
Grammar grammar = null;
MySchemaIdResolver schemaIdResolver = new MySchemaIdResolver(commandLineArgs, grammarFactory);
exiFactory.setSchemaIdResolver(schemaIdResolver);
try {
if (commandLineArgs.containsKey(ExiOption.SCHEMA)) {
printEnabledMessage(ExiOption.SCHEMA);
//System.out.println("\t\t\tloading " + commandLineArgs.get(ExiOption.SCHEMA)); // a print statement is given in schemaIdResolver.resolveSchemaId
grammar = schemaIdResolver.resolveSchemaId(null, true); // commandLineArgs.get(ExiOption.SCHEMA) was already passed to schemaIdResolver
// SCHEMAID - only valid for encoding if ExiOption.SCHEMA is enabled
if (commandLineArgs.containsKey(ExiOption.SCHEMAID)) {
printEnabledMessage(ExiOption.SCHEMAID);
exiFactory.getEncodingOptions().setOption(com.siemens.ct.exi.EncodingOptions.INCLUDE_SCHEMA_ID);
System.out.println("\t\t\tschemaId set to '" + commandLineArgs.get(ExiOption.SCHEMAID) + "'");
grammar.setSchemaId(commandLineArgs.get(ExiOption.SCHEMAID));
}
else {
if (commandLineArgs.containsKey(ExiOption.HEADER_OPTIONS) && isEncoding(commandLineArgs)) {
System.out.println("Warning: When using schema-informed encoding, enabling -" + ExiOption.HEADER_OPTIONS.getCommandLineArg() + " without also enabling -" + ExiOption.SCHEMAID.getCommandLineArg() + " will require the use of -" + ExiOption.SCHEMA.getCommandLineArg() + " " + ExiOption.SCHEMA.getValuePlacemark() + " when decoding.");
}
}
}
else if (commandLineArgs.containsKey(ExiOption.XSD_TYPES_ONLY)) {
printEnabledMessage(ExiOption.XSD_TYPES_ONLY);
grammar = grammarFactory.createXSDTypesOnlyGrammar();
}
else {
if (isEncoding(commandLineArgs))
System.out.println("\t\tSchemaless encoding will be used.");
else
System.out.println("\t\tSchemaless decoding will be used unless the EXI Header contains a schemaID value.");
grammar = grammarFactory.createSchemaLessGrammar();
}
if (grammar != null)
exiFactory.setGrammar(grammar);
} catch (EXIException e) {
e.printStackTrace();
}
}
/**
* A small helper method to print an 'enabled' message to stdout.
*
* @param exiOption the ExiOption to print
*/
private static void printEnabledMessage(ExiOption exiOption) {
printEnabledMessage(exiOption, "");
}
/**
* A small helper method to print an 'enabled' message to stdout.
*
* @param exiOption the ExiOption to print
* @param additionalText additional text to include after the enabled message - typically begin this String with a space or comma.
*/
private static void printEnabledMessage(ExiOption exiOption, String additionalText) {
System.out.println("\t\t" + exiOption.getName() + " enabled" + additionalText);
}
/**
* Parses the command line arguments for ExiOptions and values. If an error
* is detected, a short version of command line help is printed and System.exit(0)
* is called.
*
* @return a map of ExiOption enums and String values
*/
private static Map<ExiOption, String> parseCommandLineArgs(String[] args) {
Map<ExiOption, String> commandLineArgs = new java.util.EnumMap<ExiOption, String>(ExiOption.class);
int i = 0;
while (i < args.length) {
ExiOption exiOption = ExiOption.valueOfCommandLineArg(args[i].replaceFirst("-", ""));
if (exiOption != null) {
if (i + 1 < args.length && !exiOption.getValuePlacemark().isEmpty()) {
commandLineArgs.put(exiOption, args[i + 1]);
i = i + 2;
}
else if (!(i + 1 < args.length) && !exiOption.getValuePlacemark().isEmpty()) {
System.out.println("\nError: option " + args[i] + " requires a value");
printBriefHelp(); // this calls System.exit(0)
}
else {
commandLineArgs.put(exiOption, null);
i++;
}
}
else {
System.out.println();
System.out.println("ERROR: command line option '" + args[i] + "' not known");
printBriefHelp(); // this calls System.exit(0)
}
}
System.out.println();
return commandLineArgs;
}
/**
* Prints command-line help to stdout and then calls System.exit(0)
*/
private static void printHelp() {
System.out.println();
String tab = "\t";
System.out.println("Efficient XML Interchange (EXI) Processor");
System.out.println("This program can be used to encode text XML to binary EXI, and decode EXI to text XML.");
System.out.println();
System.out.println("Encode XML to EXI:");
System.out.println(tab + COMMAND_LINE + " -" + ExiOption.XML_IN.getCommandLineArg() + " sample.xml -" + ExiOption.EXI_OUT.getCommandLineArg() + " sample.exi");
System.out.println();
System.out.println("Decode EXI to XML:");
System.out.println(tab + COMMAND_LINE + " -" + ExiOption.EXI_IN.getCommandLineArg() + " sample.exi -" + ExiOption.XML_OUT.getCommandLineArg() + " sample.xml");
System.out.println();
System.out.println("Decode EXI to stdout:");
System.out.println(tab + COMMAND_LINE + " -" + ExiOption.EXI_IN.getCommandLineArg() + " sample.exi");
System.out.println();
System.out.println("Command Line Options:");
System.out.println();
for (ExiOption exiOption : ExiOption.values()) {
System.out.println(tab + "-" + exiOption.getCommandLineArg() + " " + exiOption.getValuePlacemark());
System.out.println(tab + tab + exiOption.getDescription());
if (exiOption.getUrl().length() > 0)
System.out.println(tab + tab + "URL: " + exiOption.getUrl());
if (exiOption.getValuePlacemark().length() > 0)
System.out.println(tab + tab + "Example: " + exiOption.getExample());
System.out.println();
}
System.exit(0);
}
/**
* Prints a short version of command-line help to stdout and then calls System.exit(0)
*/
private static void printBriefHelp() {
System.out.println();
String tab = "\t";
System.out.println("Efficient XML Interchange (EXI) Processor");
System.out.println("This program can be used to encode text XML to binary EXI, and decode EXI to text XML.");
System.out.println();
System.out.println("For a full list of options, run:");
System.out.println(tab + COMMAND_LINE);
System.exit(0);
}
/**
* Rounds a floating-point number to arbitrary precision.
*
* @param d the floating-point number to round
* @param n the number of decimal places to the right of the decimal point to leave. Negative values will round to the nearest 10, 100, etc.
*/
private static double round(double d, int n){
double factor = Math.pow(10, n);
return Math.round(d * factor) / factor;
}
}