/* TeXFormulaParser.java * ========================================================================= * This file is originally part of the JMathTeX Library - http://jmathtex.sourceforge.net * * Copyright (C) 2004-2007 Universiteit Gent * Copyright (C) 2009 DENIZET Calixte * * 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 2 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. * * A copy of the GNU General Public License can be found in the file * LICENSE.txt provided with the source distribution of this program (see * the META-INF directory in the source jar). This license can also be * found on the GNU website at http://www.gnu.org/licenses/gpl.html. * * If you did not receive a copy of the GNU General Public License along * with this program, contact the lead developer, or write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Linking this library statically or dynamically with other modules * is making a combined work based on this library. Thus, the terms * and conditions of the GNU General Public License cover the whole * combination. * * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce * an executable, regardless of the license terms of these independent * modules, and to copy and distribute the resulting executable under terms * of your choice, provided that you also meet, for each linked independent * module, the terms and conditions of the license of that module. * An independent module is a module which is not derived from or based * on this library. If you modify this library, you may extend this exception * to your version of the library, but you are not obliged to do so. * If you do not wish to do so, delete this exception statement from your * version. * */ package com.himamis.retex.renderer.share; import java.util.HashMap; import java.util.Map; import com.himamis.retex.renderer.share.exception.ResourceParseException; import com.himamis.retex.renderer.share.exception.XMLResourceParseException; import com.himamis.retex.renderer.share.platform.parser.Element; import com.himamis.retex.renderer.share.platform.parser.Node; import com.himamis.retex.renderer.share.platform.parser.NodeList; /** * Parses a "TeXFormula"-element representing a predefined TeXFormula's from an XML-file. */ public class TeXFormulaParser { private interface ActionParser { // NOPMD public void parse(Element el) throws ResourceParseException; } private interface ArgumentValueParser { // NOPMD public Object parseValue(String value, String type) throws ResourceParseException; } private class MethodInvocationParser implements ActionParser { MethodInvocationParser() { // avoids creation of special accessor type } @Override public void parse(Element el) throws ResourceParseException { // get required string attributes String methodName = getAttrValueAndCheckIfNotNull("name", el); String objectName = getAttrValueAndCheckIfNotNull(ARG_OBJ_ATTR, el); // check if temporary TeXFormula exists Object object = tempFormulas.get(objectName); if (object == null) {// doesn't exist throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_OBJ_ATTR, "has an unknown temporary TeXFormula name as value : '" + objectName + "'!"); } // parse arguments // NodeList args = el.getElementsByTagName("Argument"); // get argument classes and values // Class[] argClasses = getArgumentClasses(args); // Object[] argValues = getArgumentValues(args); // invoke method try { throw new UnsupportedOperationException("Not implemented yet."); // TeXFormula.class.getMethod(methodName, // argClasses).invoke((TeXFormula) object, argValues); } catch (Exception e) { throw new XMLResourceParseException( "Error invoking the method '" + methodName + "' on the temporary TeXFormula '" + objectName + "' while constructing the predefined TeXFormula '" + formulaName + "'!\n" + e.toString()); } } } private class CreateTeXFormulaParser implements ActionParser { CreateTeXFormulaParser() { // avoids creation of special accessor type } @Override public void parse(Element el) throws ResourceParseException { // get required string attribute String name = getAttrValueAndCheckIfNotNull("name", el); // parse arguments //NodeList args = el.getElementsByTagName("Argument"); // get argument classes and values //Class[] argClasses = getArgumentClasses(args); //Object[] argValues = getArgumentValues(args); // create TeXFormula object // String code = "TeXFormula.predefinedTeXFormulasAsString.put(\"%s\", \"%s\");"; // System.out.println(String.format(code, formulaName, argValues[0])); try { throw new UnsupportedOperationException("Not implemented"); //TeXFormula f = TeXFormula.class.getConstructor(argClasses).newInstance(argValues); // succesfully created, so add to "temporary formula's"-hashtable //tempFormulas.put(name, f); } catch (Exception e) { throw new XMLResourceParseException("Error creating the temporary TeXFormula '" + name + "' while constructing the predefined TeXFormula '" + formulaName + "'!\n" + e.toString()); } } } private class CreateCommandParser implements ActionParser { CreateCommandParser() { // avoids creation of special accessor type } @Override public void parse(Element el) throws ResourceParseException { // get required string attribute String name = getAttrValueAndCheckIfNotNull("name", el); // parse arguments NodeList args = el.getElementsByTagName("Argument"); // get argument classes and values Class<?>[] argClasses = getArgumentClasses(args); Object[] argValues = getArgumentValues(args); // create TeXFormula object try { throw new UnsupportedOperationException("Not implemented"); //MacroInfo f = MacroInfo.class.getConstructor(argClasses).newInstance(argValues); // succesfully created, so add to "temporary formula's"-hashtable //tempCommands.put(name, f); } catch (IllegalArgumentException e) { String err = "IllegalArgumentException:\n"; err += "ClassLoader to load this class (TeXFormulaParser): " + this.getClass() + "\n"; for (Class cl : argClasses) { err += "Created class: " + cl + " loaded with the ClassLoader: " + cl + "\n"; } for (Object obj : argValues) { err += "Created object: " + obj + "\n"; } throw new XMLResourceParseException("Error creating the temporary command '" + name + "' while constructing the predefined command '" + formulaName + "'!\n" + err); } catch (Exception e) { throw new XMLResourceParseException("Error creating the temporary command '" + name + "' while constructing the predefined command '" + formulaName + "'!\n" + e.toString()); } } } private class FloatValueParser implements ArgumentValueParser { FloatValueParser() { // avoids creation of special accessor type } @Override public Object parseValue(String value, String type) throws ResourceParseException { checkNullValue(value, type); try { return new Double(Double.parseDouble(value)); } catch (NumberFormatException e) { throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_VAL_ATTR, "has an invalid '" + type + "'-value : '" + value + "'!", e); } } } private class CharValueParser implements ArgumentValueParser { CharValueParser() { // avoids creation of special accessor type } @Override public Object parseValue(String value, String type) throws ResourceParseException { checkNullValue(value, type); if (value.length() == 1) { return new Character(value.charAt(0)); } throw new XMLResourceParseException( PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_VAL_ATTR, "must have a value that consists of exactly 1 character!"); } } private class BooleanValueParser implements ArgumentValueParser { BooleanValueParser() { // avoids creation of special accessor type } @Override public Object parseValue(String value, String type) throws ResourceParseException { checkNullValue(value, type); if ("true".equals(value)) { return Boolean.TRUE; } else if ("false".equals(value)) { return Boolean.FALSE; } else { throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_VAL_ATTR, "has an invalid '" + type + "'-value : '" + value + "'!"); } } } private class IntValueParser implements ArgumentValueParser { IntValueParser() { // avoids creation of special accessor type } @Override public Object parseValue(String value, String type) throws ResourceParseException { checkNullValue(value, type); try { int val = Integer.parseInt(value); return new Double(val); } catch (NumberFormatException e) { throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_VAL_ATTR, "has an invalid '" + type + "'-value : '" + value + "'!", e); } } } private class ReturnParser implements ActionParser { ReturnParser() { // avoids creation of special accessor type } @SuppressWarnings("synthetic-access") @Override public void parse(Element el) throws ResourceParseException { // get required string attribute String name = getAttrValueAndCheckIfNotNull("name", el); Object res = type == COMMAND ? tempCommands.get(name) : tempFormulas.get(name); if (res == null) { throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, RETURN_EL, "name", "contains an unknown temporary TeXFormula variable name '" + name + "' for the predefined TeXFormula '" + formulaName + "'!"); } result = res; } } private class StringValueParser implements ArgumentValueParser { StringValueParser() { // avoids creation of special accessor type } @Override public Object parseValue(String value, String type) throws ResourceParseException { return value; } } private class TeXFormulaValueParser implements ArgumentValueParser { TeXFormulaValueParser() { // avoids creation of special accessor type } @Override public Object parseValue(String value, String type) throws ResourceParseException { if (value == null) {// null pointer argument return null; } Object formula = tempFormulas.get(value); if (formula == null) {// unknown temporary TeXFormula! throw new XMLResourceParseException( PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_VAL_ATTR, "has an unknown temporary TeXFormula name as value : '" + value + "'!"); } return formula; } } private class TeXConstantsValueParser implements ArgumentValueParser { TeXConstantsValueParser() { // avoids creation of special accessor type } @Override public Object parseValue(String value, String type) throws ResourceParseException { checkNullValue(value, type); try { // get constant value (if present) int constant = TeXConstants.CONSTANTS_MAP.get(value); // return constant integer value return Integer.valueOf(constant); } catch (Exception e) { throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_VAL_ATTR, "has an unknown constant name as value : '" + value + "'!", e); } } } private class ColorConstantValueParser implements ArgumentValueParser { ColorConstantValueParser() { // avoids creation of special accessor type } @Override public Object parseValue(String value, String type) throws ResourceParseException { checkNullValue(value, type); try { // return Color constant (if present) return ColorUtil.COLOR_CONSTANTS.get(value); } catch (Exception e) { throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_VAL_ATTR, "has an unknown color constant name as value : '" + value + "'!", e); } } } private static final String ARG_VAL_ATTR = "value", RETURN_EL = "Return", ARG_OBJ_ATTR = "formula"; private static Map<String, Class<?>> classMappings = new HashMap<String, Class<?>>(); private final Map<String, ArgumentValueParser> argValueParsers = new HashMap<String, ArgumentValueParser>(); private final Map<String, ActionParser> actionParsers = new HashMap<String, ActionParser>(); private final Map<String, TeXFormula> tempFormulas = new HashMap<String, TeXFormula>(); private final Map<String, MacroInfo> tempCommands = new HashMap<String, MacroInfo>(); private Object result = new Object(); private final String formulaName; private final Element formula; private static final int COMMAND = 0, TEXFORMULA = 1; private int type; static { // string-to-class mappings classMappings.put("TeXConstants", int.class); // all integer constants classMappings.put("TeXFormula", TeXFormula.class); classMappings.put("String", String.class); classMappings.put("double", double.class); classMappings.put("int", int.class); classMappings.put("boolean", boolean.class); classMappings.put("char", char.class); classMappings.put("ColorConstant", ColorUtil.class); } public TeXFormulaParser(String name, Element formula, String type) { formulaName = name; this.formula = formula; this.type = "Command".equals(type) ? COMMAND : TEXFORMULA; // action parsers if ("Command".equals(type)) { actionParsers.put("CreateCommand", new CreateCommandParser()); } else { actionParsers.put("CreateTeXFormula", new CreateTeXFormulaParser()); } actionParsers.put("MethodInvocation", new MethodInvocationParser()); actionParsers.put(RETURN_EL, new ReturnParser()); // argument value parsers argValueParsers.put("TeXConstants", new TeXConstantsValueParser()); argValueParsers.put("TeXFormula", new TeXFormulaValueParser()); argValueParsers.put("String", new StringValueParser()); argValueParsers.put("double", new FloatValueParser()); argValueParsers.put("int", new IntValueParser()); argValueParsers.put("boolean", new BooleanValueParser()); argValueParsers.put("char", new CharValueParser()); argValueParsers.put("ColorConstant", new ColorConstantValueParser()); } public Object parse() throws ResourceParseException { // parse and execute actions NodeList list = formula.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); if (node.getNodeType() != Node.TEXT_NODE) { Element el = node.castToElement(); ActionParser p = actionParsers.get(el.getTagName()); if (p != null) {// ignore unknown elements p.parse(el); } } } return result; } private Object[] getArgumentValues(NodeList args) { Object[] res = new Object[args.getLength()]; int i = 0; for (int j = 0; j < args.getLength(); j++) { Element arg = args.item(j).castToElement(); // get required string attribute String type = getAttrValueAndCheckIfNotNull("type", arg); // get value, not present means a nullpointer String value = arg.getAttribute(ARG_VAL_ATTR); // parse value, hashtable will certainly contain a parser for the class type, // because the class types have been checked before! res[i] = argValueParsers.get(type).parseValue(value, type); i++; } return res; } private static Class[] getArgumentClasses(NodeList args) throws ResourceParseException { Class<?>[] res = new Class[args.getLength()]; int i = 0; for (int j = 0; j < args.getLength(); j++) { Element arg = args.item(j).castToElement(); // get required string attribute String type = getAttrValueAndCheckIfNotNull("type", arg); // find class mapping Object cl = classMappings.get(type); if (cl == null) {// no class mapping found throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", "type", "has an invalid class name value!"); } res[i] = (Class<?>) cl; i++; } return res; } private static void checkNullValue(String value, String type) throws ResourceParseException { if ("".equals(value)) { throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, "Argument", ARG_VAL_ATTR, "is required for an argument of type '" + type + "'!"); } } private static String getAttrValueAndCheckIfNotNull(String attrName, Element element) throws ResourceParseException { String attrValue = element.getAttribute(attrName); if ("".equals(attrValue)) { throw new XMLResourceParseException(PredefinedTeXFormulaParser.RESOURCE_NAME, element.getTagName(), attrName, null); } return attrValue; } }