/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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, see <http://www.gnu.org/licenses/>. */ /* * XMLOptions.java * Copyright (C) 2004-2012 University of Waikato, Hamilton, New Zealand * */ package weka.core.xml; import java.io.File; import java.io.InputStream; import java.io.Reader; import java.util.Vector; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import weka.core.RevisionHandler; import weka.core.RevisionUtils; import weka.core.Utils; /** * A class for transforming options listed in XML to a regular WEKA command * line string.<p> * * @author FracPete (fracpete at waikato dot ac dot nz) * @version $Revision: 8034 $ */ public class XMLOptions implements RevisionHandler { /** tag for a single option. */ public final static String TAG_OPTION = "option"; /** tag for a list of options. */ public final static String TAG_OPTIONS = "options"; /** the name attribute. */ public final static String ATT_NAME = "name"; /** the type attribute. */ public final static String ATT_TYPE = "type"; /** the value attribute. */ public final static String ATT_VALUE = "value"; /** a value of the type attribute. */ public final static String VAL_TYPE_FLAG = "flag"; /** a value of the type attribute. */ public final static String VAL_TYPE_SINGLE = "single"; /** a value of the type attribute. */ public final static String VAL_TYPE_HYPHENS = "hyphens"; /** a value of the type attribute. */ public final static String VAL_TYPE_QUOTES = "quotes"; /** a value of the type attribute. */ public final static String VAL_TYPE_CLASSIFIER = "classifier"; /** a value of the type attribute. */ public final static String VAL_TYPE_OPTIONHANDLER = "optionhandler"; /** the root node. */ public final static String ROOT_NODE = TAG_OPTIONS; /** the DTD for the XML file. */ public final static String DOCTYPE = "<!DOCTYPE " + ROOT_NODE + "\n" + "[\n" + " <!ELEMENT " + TAG_OPTIONS + " (" + TAG_OPTION + ")*>\n" + " <!ATTLIST " + TAG_OPTIONS + " " + ATT_TYPE + " CDATA \"" + VAL_TYPE_OPTIONHANDLER + "\">\n" + " <!ATTLIST " + TAG_OPTIONS + " " + ATT_VALUE + " CDATA \"\">\n" + " <!ELEMENT " + TAG_OPTION + " (#PCDATA | " + TAG_OPTIONS + ")*>\n" + " <!ATTLIST " + TAG_OPTION + " " + ATT_NAME + " CDATA #REQUIRED>\n" + " <!ATTLIST " + TAG_OPTION + " " + ATT_TYPE + " (" + VAL_TYPE_FLAG + " | " + VAL_TYPE_SINGLE + " | " + VAL_TYPE_HYPHENS + " | " + VAL_TYPE_QUOTES + ") \"" + VAL_TYPE_SINGLE + "\">\n" + "]\n" + ">"; /** the XML document. */ protected XMLDocument m_XMLDocument = null; /** * Creates a new instance of XMLOptions. * * @throws Exception if the construction of the DocumentBuilder fails * @see #setValidating(boolean) */ public XMLOptions() throws Exception { m_XMLDocument = new XMLDocument(); m_XMLDocument.setRootNode(ROOT_NODE); m_XMLDocument.setDocType(DOCTYPE); setValidating(true); } /** * Creates a new instance of XMLOptions. * * @param xml the xml to parse (if "<?xml" is not found then it is considered a file) * @throws Exception if the construction of the DocumentBuilder fails * @see #setValidating(boolean) */ public XMLOptions(String xml) throws Exception { this(); getXMLDocument().read(xml); } /** * Creates a new instance of XMLOptions. * * @param file the XML file to parse * @throws Exception if the construction of the DocumentBuilder fails * @see #setValidating(boolean) */ public XMLOptions(File file) throws Exception { this(); getXMLDocument().read(file); } /** * Creates a new instance of XMLOptions. * * @param stream the XML stream to parse * @throws Exception if the construction of the DocumentBuilder fails * @see #setValidating(boolean) */ public XMLOptions(InputStream stream) throws Exception { this(); getXMLDocument().read(stream); } /** * Creates a new instance of XMLOptions. * * @param reader the XML reader to parse * @throws Exception if the construction of the DocumentBuilder fails * @see #setValidating(boolean) */ public XMLOptions(Reader reader) throws Exception { this(); getXMLDocument().read(reader); } /** * returns whether a validating parser is used. * * @return whether a validating parser is used */ public boolean getValidating() { return m_XMLDocument.getValidating(); } /** * sets whether to use a validating parser or not. <br> * Note: this does clear the current DOM document! * * @param validating whether to use a validating parser * @throws Exception if the instantiating of the DocumentBuilder fails */ public void setValidating(boolean validating) throws Exception { m_XMLDocument.setValidating(validating); } /** * returns the parsed DOM document. * * @return the parsed DOM document */ public Document getDocument() { fixHyphens(); return m_XMLDocument.getDocument(); } /** * returns the handler of the XML document. the internal DOM document can * be accessed via the <code>getDocument()</code> method. * * @return the object handling the XML document * @see #getDocument() */ public XMLDocument getXMLDocument() { fixHyphens(); return m_XMLDocument; } /** * pushes any options with type VAL_TYPE_HYPHENS to the end, i.e., the "--" * are really added at the end. * * @see #VAL_TYPE_HYPHENS */ protected void fixHyphens() { NodeList list; Vector<Element> hyphens; int i; Node node; Node tmpNode; boolean isLast; // get all option tags list = m_XMLDocument.findNodes("//" + TAG_OPTION); // get all hyphen tags hyphens = new Vector<Element>(); for (i = 0; i < list.getLength(); i++) { if (((Element) list.item(i)).getAttribute(ATT_TYPE).equals(VAL_TYPE_HYPHENS)) hyphens.add((Element)list.item(i)); } // check all hyphen tags whether they are at the end, if not fix it for (i = 0; i < hyphens.size(); i++) { node = (Node) hyphens.get(i); // at the end? isLast = true; tmpNode = node; while (tmpNode.getNextSibling() != null) { // normal tag? if (tmpNode.getNextSibling().getNodeType() == Node.ELEMENT_NODE) { isLast = false; break; } tmpNode = tmpNode.getNextSibling(); } // move if (!isLast) { tmpNode = node.getParentNode(); tmpNode.removeChild(node); tmpNode.appendChild(node); } } } /** * converts the given node into a command line representation and returns it. * * @param parent the node to convert to command line * @return the new command line */ protected String toCommandLine(Element parent) { Vector<String> result; Vector list; Vector subList; NodeList subNodeList; String[] params; int i; int n; String tmpStr; result = new Vector<String>(); // "options" tag if (parent.getNodeName().equals(TAG_OPTIONS)) { // children list = XMLDocument.getChildTags(parent); if (parent.getAttribute(ATT_TYPE).equals(VAL_TYPE_CLASSIFIER)) { System.err.println( "Type '" + VAL_TYPE_CLASSIFIER + "' is deprecated, " + "use '" + VAL_TYPE_OPTIONHANDLER + "' instead!"); parent.setAttribute(ATT_TYPE, VAL_TYPE_OPTIONHANDLER); } if (parent.getAttribute(ATT_TYPE).equals(VAL_TYPE_OPTIONHANDLER)) { result.add(parent.getAttribute(ATT_VALUE)); // hyphens? if ( (list.size() > 0) && (parent.getParentNode() != null) && (parent.getParentNode() instanceof Element) && (((Element) parent.getParentNode()).getNodeName().equals(TAG_OPTION)) && (((Element) parent.getParentNode()).getAttribute(ATT_TYPE).equals(VAL_TYPE_HYPHENS)) ) result.add("--"); } // process children for (i = 0; i < list.size(); i++) { tmpStr = toCommandLine((Element) list.get(i)); try { params = Utils.splitOptions(tmpStr); for (n = 0; n < params.length; n++) result.add(params[n]); } catch (Exception e) { System.err.println("Error splitting: " + tmpStr); e.printStackTrace(); } } } // "option" tag else if (parent.getNodeName().equals(TAG_OPTION)) { subList = XMLDocument.getChildTags(parent); subNodeList = parent.getChildNodes(); result.add("-" + parent.getAttribute(ATT_NAME)); // single argument if (parent.getAttribute(ATT_TYPE).equals(VAL_TYPE_SINGLE)) { if ( (subNodeList.getLength() > 0) && (subNodeList.item(0).getNodeValue().trim().length() > 0) ) result.add(subNodeList.item(0).getNodeValue()); } // compound argument surrounded by quotes else if (parent.getAttribute(ATT_TYPE).equals(VAL_TYPE_QUOTES)) { result.add(toCommandLine((Element) subList.get(0))); } // classname + further options after "--" else if (parent.getAttribute(ATT_TYPE).equals(VAL_TYPE_HYPHENS)) { tmpStr = toCommandLine((Element) subList.get(0)); try { params = Utils.splitOptions(tmpStr); for (n = 0; n < params.length; n++) result.add(params[n]); } catch (Exception e) { System.err.println("Error splitting: " + tmpStr); e.printStackTrace(); } } } // other tag else { System.err.println("Unsupported tag '" + parent.getNodeName() + "' - skipped!"); } return Utils.joinOptions(result.toArray(new String[result.size()])); } /** * returns the given DOM document as command line. * * @return the document as command line * @throws Exception if anything goes wrong initializing the parsing */ public String toCommandLine() throws Exception { return toCommandLine(getDocument().getDocumentElement()); } /** * returns the current DOM document as string array. * * @return the document as string array * @throws Exception if anything goes wrong initializing the parsing */ public String[] toArray() throws Exception { return Utils.splitOptions(toCommandLine()); } /** * returns the object in a string representation (as indented XML output). * * @return the object in a string representation */ public String toString() { return getXMLDocument().toString(); } /** * Returns the revision string. * * @return the revision */ public String getRevision() { return RevisionUtils.extract("$Revision: 8034 $"); } /** * for testing only. prints the given XML file, the resulting commandline and * the string array. * * @param args the commandline options. * @throws Exception if something goes wrong */ public static void main(String[] args) throws Exception { if (args.length > 0) { System.out.println("\nXML:\n\n" + new XMLOptions(args[0]).toString()); System.out.println("\nCommandline:\n\n" + new XMLOptions(args[0]).toCommandLine()); System.out.println("\nString array:\n"); String[] options = new XMLOptions(args[0]).toArray(); for (int i = 0; i < options.length; i++) System.out.println(options[i]); } } }