Java tutorial
/** * @author Daniel Guedes Barrigas - danielgbarrigas@hotmail.com / danielgbarrigas@gmail.com * * Parses and transforms a mathematical function to a SPARQL Query. * Based on: * -https://code.google.com/p/symja/wiki/MathExpressionParser * -http://scanftree.com/Data_Structure/prefix-to-infix * * * ---------------------------------------------------------------------------------------- * This file is part of LinkedUSDLPricingAPI. * * LinkedUSDLPricingAPI 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. * * LinkedUSDLPricingAPI 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 LinkedUSDLPricingAPI. If not, see <http://www.gnu.org/licenses/>. * --------------------------------------------------------------------------------------- */ package FunctionParser; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; import org.apache.commons.lang3.StringUtils; import org.matheclipse.core.convert.ConversionException; import org.matheclipse.core.expression.F; import org.matheclipse.core.interfaces.IAST; import org.matheclipse.core.interfaces.IExpr; import org.matheclipse.core.interfaces.IInteger; import org.matheclipse.core.interfaces.ISymbol; import org.matheclipse.parser.client.Parser; import org.matheclipse.parser.client.ast.ASTNode; import org.matheclipse.parser.client.ast.FloatNode; import org.matheclipse.parser.client.ast.FractionNode; import org.matheclipse.parser.client.ast.FunctionNode; import org.matheclipse.parser.client.ast.IntegerNode; import org.matheclipse.parser.client.ast.PatternNode; import org.matheclipse.parser.client.ast.StringNode; import org.matheclipse.parser.client.ast.SymbolNode; import org.matheclipse.parser.client.operator.Operator; import usdl.servicemodel.*; public class MathExp2SPARQL { private String SPARQLQuery = ""; private Stack<String> stack = new Stack<String>(); private Stack<String> infixstack = new Stack<String>(); private ArrayList<String> usageVariables = new ArrayList<>(); private Map<String, String> opmap = new HashMap<String, String>(); public static final String[] SYMBOLS = { "True", "False", "List", "Modulus", "Flat", "HoldAll", "HoldFirst", "HoldRest", "Listable", "NumericFunction", "OneIdentity", "Orderless", "Slot", "SlotSequence", "Abs", "AddTo", "And", "Apart", "Append", "AppendTo", "Apply", "ArcCos", "ArcSin", "ArcTan", "Arg", "Array", "AtomQ", "Binomial", "Blank", "Block", "Boole", "Break", "Cancel", "CartesianProduct", "Cases", "Catalan", "CatalanNumber", "Catch", "Ceiling", "CharacteristicPolynomial", "ChessboardDistance", "Chop", "Clear", "ClearAll", "Coefficient", "CoefficientList", "Complement", "Complex", "ComplexInfinity", "ComposeList", "CompoundExpression", "Condition", "Conjugate", "ConstantArray", "Continue", "ContinuedFraction", "CoprimeQ", "Cos", "Cosh", "Cot", "Count", "Cross", "Csc", "Curl", "D", "Decrement", "Default", "Definition", "Degree", "Delete", "Denominator", "Depth", "Derivative", "Det", "DiagonalMatrix", "DigitQ", "Dimensions", "Discriminant", "Distribute", "Divergence", "DivideBy", "Do", "Dot", "Drop", "E", "Eigenvalues", "Eigenvectors", "Equal", "Erf", "EuclidianDistance", "EulerGamma", "EulerPhi", "EvenQ", "Exp", "Expand", "ExpandAll", "Exponent", "ExtendedGCD", "Extract", "Factor", "Factorial", "Factorial2", "FactorInteger", "FactorSquareFree", "FactorSquareFreeList", "FactorTerms", "Fibonacci", "FindRoot", "First", "Fit", "FixedPoint", "Floor", "Fold", "FoldList", "For", "FractionalPart", "FreeQ", "FromCharacterCode", "FromContinuedFraction", "FullForm", "FullSimplify", "Function", "Gamma", "GCD", "Glaisher", "GoldenRatio", "Greater", "GreaterEqual", "GroebnerBasis", "HarmonicNumber", "Head", "HilbertMatrix", "Hold", "Horner", "I", "IdentityMatrix", "If", "Im", "Increment", "Infinity", "Inner", "IntegerPartitions", "IntegerQ", "Integrate", "Intersection", "Inverse", "InverseFunction", "JacobiMatrix", "JacobiSymbol", "JavaForm", "Join", "Khinchin", "KOrderlessPartitions", "KPartitions", "Last", "LCM", "LeafCount", "Length", "Less", "LessEqual", "LetterQ", "Level", "Limit", "LinearProgramming", "LinearSolve", "Log", "LowerCaseQ", "LUDecomposition", "ManhattanDistance", "Map", "MapAll", "MapThread", "MatchQ", "MatrixPower", "MatrixQ", "Max", "Mean", "Median", "MemberQ", "Min", "Mod", "Module", "MoebiusMu", "Most", "Multinomial", "N", "Negative", "Nest", "NestList", "NextPrime", "NFourierTransform", "NIntegrate", "NonCommutativeMultiply", "NonNegative", "Norm", "Not", "NRoots", "NumberQ", "Numerator", "NumericQ", "OddQ", "Or", "Order", "OrderedQ", "Out", "Outer", "Package", "PadLeft", "PadRight", "ParametricPlot", "Part", "Partition", "Pattern", "Permutations", "Pi", "Plot", "Plot3D", "Plus", "PolynomialExtendedGCD", "PolynomialGCD", "PolynomialLCM", "PolynomialQ", "PolynomialQuotient", "PolynomialQuotientRemainder", "PolynomialRemainder", "Position", "Positive", "PossibleZeroQ", "Power", "PowerExpand", "PowerMod", "PreDecrement", "PreIncrement", "Prepend", "PrependTo", "PrimeQ", "PrimitiveRoots", "Print", "Product", "Quotient", "RandomInteger", "RandomReal", "Range", "Rational", "Rationalize", "Re", "Reap", "ReplaceAll", "ReplacePart", "ReplaceRepeated", "Rest", "Resultant", "Return", "Reverse", "Riffle", "RootIntervals", "Roots", "RotateLeft", "RotateRight", "Round", "Rule", "RuleDelayed", "SameQ", "Scan", "Sec", "Select", "Set", "SetAttributes", "SetDelayed", "Sign", "SignCmp", "Simplify", "Sin", "SingularValueDecomposition", "Sinh", "Solve", "Sort", "Sow", "Sqrt", "SquaredEuclidianDistance", "SquareFreeQ", "StirlingS2", "StringDrop", "StringJoin", "StringLength", "StringTake", "Subsets", "SubtractFrom", "Sum", "SyntaxLength", "SyntaxQ", "Table", "Take", "Tan", "Tanh", "Taylor", "Thread", "Through", "Throw", "Times", "TimesBy", "Timing", "ToCharacterCode", "Together", "ToString", "Total", "ToUnicode", "Tr", "Trace", "Transpose", "TrigExpand", "TrigReduce", "TrigToExp", "TrueQ", "Trunc", "Unequal", "Union", "UnitStep", "UnsameQ", "UpperCaseQ", "ValueQ", "VandermondeMatrix", "Variables", "VectorQ", "While" };; static final Map<String, String> SYMBOLS_MAP = new HashMap<String, String>(); static { for (String str : SYMBOLS) { SYMBOLS_MAP.put(str.toLowerCase(), str); } } public MathExp2SPARQL(String StringFunction, List<Provider> prov, List<Usage> usage) { Parser p = new Parser(true); Map<String, Operator> temp = p.getFactory().getIdentifier2OperatorMap(); Iterator<String> it = temp.keySet().iterator(); while (it.hasNext()) { String key = it.next().toString(); String val = temp.get(key).getOperatorString(); //System.out.println(key + " " +val); //print the key-value entry opmap.put(key, val); } if (StringFunction.contains("IF")) { SPARQLQuery = "SELECT ?result\n" + "WHERE {\n"; String[] partitions = StringFunction.split("~"); ArrayList<String> parcels = new ArrayList<String>(); int counter = 0; for (String part : partitions) { String[] data = part.split(";"); String cond = data[0].replace("IF", ""); cond = cond.replace("ELSEIF", ""); cond = cond.replace("ELSE", ""); String form = data[1]; //System.out.println("Cond: "+cond+"\nForm: "+form); stack.clear(); infixstack.clear(); usageVariables.clear(); ASTNode objcond = p.parse(cond); convert(objcond); String fa = prefixToInfix(stack, infixstack); fa = fa.replace("[", ""); fa = fa.replace("]", ""); for (String s : usageVariables)//here we can detect the type of the variable by matching the variables name with the JAVA Object. Through the JAVA object we can see if its a Qualitative or Quantitative Value and treat it as such { if (!SPARQLQuery.contains(s)) { if (!StringUtils.isAllUpperCase(s)) SPARQLQuery = SPARQLQuery + "\n:" + s + " price:hasValue ?" + s + "_instance .\n" + "?" + s + "_instance gr:hasValue ?" + s + "_value .\n"; else { //add syntax to deal with constant qualitative attributes like, WINDOWS will probably be 'windows' in the SPARQL Query } } } stack.clear(); infixstack.clear(); usageVariables.clear(); ASTNode objform = p.parse(form); convert(objform); String fb = prefixToInfix(stack, infixstack); fb = fb.replace("[", ""); fb = fb.replace("]", ""); for (String s : usageVariables)//here we can detect the type of the variable by matching the variables name with the JAVA Object. Through the JAVA object we can see if its a Qualitative or Quantitative Value and treat it as such { if (!SPARQLQuery.contains(s)) { if (!StringUtils.isAllUpperCase(s)) SPARQLQuery = SPARQLQuery + "\n:" + s + " price:hasValue ?" + s + "_instance .\n" + "?" + s + "_instance gr:hasValue ?" + s + "_value .\n"; else { //add syntax to deal with constant qualitative attributes like, WINDOWS will probably be 'windows' in the SPARQL Query } } } //BIND (IF(((?gbs > 1) && (?gbs <= (10 * 1024))), ((?gbs - 1) * ?price10), 0) AS ?priceA) . String parcel = "BIND(IF(( " + fa + " ),(" + fb + "),0) AS ?result" + counter++ + " )."; parcels.add(parcel); //System.out.println("Cond : "+fa+"\nForm: "+fb+"\n***************************"); } // BIND ((((?priceA + ?priceB) + ?priceC) + ?priceD) AS ?price) . String sum = ""; counter--; for (; counter >= 0; counter--) { if (counter == 0) sum = sum + "?result" + counter; else sum = sum + "?result" + counter + "+"; } String lastParcel = "BIND((" + sum + ") AS ?result ) ."; for (String s : parcels) SPARQLQuery = SPARQLQuery + s + "\n"; SPARQLQuery = SPARQLQuery + lastParcel + "\n"; SPARQLQuery = SPARQLQuery + "\n}\n"; //System.out.println(SPARQLQuery); } else { ASTNode obj = p.parse(StringFunction);//insert the mathematical formula here convert(obj); SPARQLQuery = "SELECT ?result\n" + "WHERE {\n"; for (String s : usageVariables)//here we can detect the type of the variable by matching the variables name with the JAVA Object. Through the JAVA object we can see if its a Qualitative or Quantitative Value and treat it as such { if (!StringUtils.isAllUpperCase(s)) SPARQLQuery = SPARQLQuery + "\n:" + s + " price:hasValue ?" + s + "_instance .\n" + "?" + s + "_instance gr:hasValue ?" + s + "_value .\n"; else { } //add syntax to deal with constant qualitative attributes like, WINDOWS will probably be 'windows' in the SPARQL Query } String f = prefixToInfix(stack, infixstack); f = f.replace("[", ""); f = f.replace("]", ""); SPARQLQuery = SPARQLQuery + "\nBIND((" + f + ") AS ?result ) .\n" + "}"; } } public ArrayList<String> getParsedVariables() { return this.usageVariables; } public String getSPARQLQuery() { return this.SPARQLQuery; } public IExpr convert(ASTNode node) throws ConversionException { if (node == null) { return null; } if (node instanceof FunctionNode) { //function node final FunctionNode functionNode = (FunctionNode) node; final IAST ast = F.ast(convert((ASTNode) functionNode.get(0))); for (int i = 1; i < functionNode.size(); i++) { ast.add(convert((ASTNode) functionNode.get(i))); } // code below return ast; } if (node instanceof FractionNode) { //fraction node FractionNode fr = (FractionNode) node; //System.out.println(node.toString()); stack.add("(" + node.toString() + ")"); // if (fr.isSign()) { // return F.fraction((IInteger) convert(fr.getNumerator()),(IInteger) convert(fr.getDenominator())).negate(); //only need the fraction as a string // } // return F.fraction( // (IInteger) convert(((FractionNode) node).getNumerator()),(IInteger) convert(((FractionNode) node).getDenominator())); } if (node instanceof PatternNode) { //pattern node final PatternNode pn = (PatternNode) node; return F.pattern((ISymbol) convert(pn.getSymbol()), convert(pn.getConstraint())); } if (node instanceof SymbolNode) {//symbol node if (SYMBOLS_MAP.containsKey(node.getString().toLowerCase())) { //System.out.println("Operator - " + node.getString()); stack.add(opmap.get(node.toString())); } else { //System.out.println("Variable - " + node.getString()); if (StringUtils.isAllUpperCase(node.getString())) stack.add(node.getString()); else stack.add("?" + node.getString().concat("_value")); usageVariables.add(node.getString()); } return F.symbol(node.getString()); } if (node instanceof IntegerNode) { // integer node final IntegerNode integerNode = (IntegerNode) node; final String iStr = integerNode.getString(); if (iStr != null) { //System.out.println("IntegerNode - "+F.integer(iStr, integerNode.getNumberFormat())); stack.add("" + F.integer(iStr, integerNode.getNumberFormat())); return F.integer(iStr, integerNode.getNumberFormat()); } //System.out.println("IntegerNode - " + integerNode.getIntValue()); stack.add("" + integerNode.getIntValue()); return F.integer(integerNode.getIntValue()); } if (node instanceof StringNode) { //string node //System.out.println("StringNode - " + node.getString()); return F.stringx(node.getString()); } if (node instanceof FloatNode) { //float node //System.out.println("FloatNode - " + node.getString()); stack.add(node.getString()); return F.num(node.getString()); } return F.symbol(node.toString()); } private String prefixToInfix(Stack<String> stack, Stack<String> infixstack) { String s; while (!stack.isEmpty()) { s = stack.pop(); if (opmap.containsValue(s)) { String pop1 = infixstack.pop(); String pop2 = infixstack.pop(); //System.out.println(pop1 + s +pop2); if (s.equals("==")) s = "="; String nelem = "( " + pop1 + " " + s + " " + pop2 + " )"; infixstack.push(nelem); } else if (s.equals("CEIL")) { String nelem = "CEIL" + infixstack.pop(); infixstack.push(nelem); } else { infixstack.add(s); } } return infixstack.toString(); } }