// OO jDREW - An Object Oriented extension of the Java Deductive Reasoning Engine for the Web // Copyright (C) 2005 Marcel Ball // // This library 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 2.1 of the License, or (at your option) any later version. // // This library 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 this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA package org.ruleml.oojdrew.parsing; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import nu.xom.Attribute; import nu.xom.Document; import nu.xom.Element; import nu.xom.Elements; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.ruleml.oojdrew.util.DefiniteClause; import org.ruleml.oojdrew.util.SymbolTable; import org.ruleml.oojdrew.util.Term; import org.ruleml.oojdrew.util.Types; /** * This class implements the back-end parser for the RuleML 0.91 format. * * <p> * Title: OO jDREW * </p> * * <p> * Description: Reasoning Engine for the Semantic Web - Supporting OO RuleML * 0.91 * </p> * * <p> * Copyright: Copyright (c) 2005 * </p> * * @author Ben Craig * @version 0.93 */ public class RuleMLDocumentParser { private Hashtable<String, String> skolemMap; private Vector<DefiniteClause> clauses; private Vector<String> variableNames; /** * This is used for generating unique anonymous variable IDs */ private int anonid = 1; /** * RuleML tag names */ private RuleMLTagNames tagNames = null; /** * Log4J logger for document parser class */ private Logger logger = Logger.getLogger("jdrew.oo.util.RuleMLParser"); /** * A vector to hold the class information for the variables in the current * clause. This is used for normalizing the types given to a variable if * more than one type is given to a variable. * * For example: in the following clause p(?x: type1, ?x:type2). the types * are not the same on all occurrences of the variable ?x - therefore the * types will be normalized and receive a type that is the intersection of * type1 and type2. */ private Hashtable<Integer, Vector<Integer>> varClasses; /** * Constructs the back-end parser. * * @param clauses * A vector to use as a buffer - this is generally passed by the * RuleMLParser front-end. */ public RuleMLDocumentParser(RuleMLFormat format, Vector<DefiniteClause> clauses) { this.clauses = clauses; // Set default RuleML version this.tagNames = new RuleMLTagNames(format); } /** * This method is used to parse a RuleML 0.91 document that is stored in a * XOM tree. * * @param doc * The XOM Document object that represents the RuleML document to * be parsed. * * @throws ParseException * A ParseException is thrown if there is an error parsing. */ public void parseRuleMLDocument(Document doc) throws ParseException { this.skolemMap = new Hashtable<String, String>(); Element documentRoot = doc.getRootElement(); removeReifyElements(documentRoot); Element rmlRoot = getRuleMLRootElement(documentRoot); parseRuleMLRoot(rmlRoot); } /** * Gets the root element used to begin RuleML parsing at * * possible elements are: <RuleML>, <Assert>, <Rulebase> or <Query> if * document is a RuleML query * * @param documentRoot * Document's root element * * @return The RuleML root element on which the parser should start at * * @throws ParseException */ private Element getRuleMLRootElement(Element documentRoot) throws ParseException { String rootName = documentRoot.getLocalName(); Element ruleMLRoot = null; // If <RuleML> is root element if (rootName.equals(tagNames.RULEML)) { Element assertElement = getFirstChildElement(documentRoot, tagNames.ASSERT); if (assertElement == null) { throw new ParseException("<Assert> has to be child of the <RuleML>."); } documentRoot = assertElement; rootName = assertElement.getLocalName(); } // Now, <Assert> has to be the root element, check for optional children if (rootName.equals(tagNames.ASSERT)) { ruleMLRoot = documentRoot; // <Rulebase> is a optional child of <Assert> Element ruleBaseElement = getFirstChildElement(documentRoot, tagNames.RULEBASE); // <And> is a optional child of either <Rulebase> or <Assert> // (0.88+) if (ruleBaseElement != null) { ruleMLRoot = ruleBaseElement; } } else if (rootName.equals(tagNames.QUERY)) { ruleMLRoot = documentRoot; } if (ruleMLRoot == null) { throw new ParseException(String.format("Undefined RuleML root element (%s)", rootName)); } return ruleMLRoot; } /** * Parses the RuleML root element (<RuleML>, <Assert> or <Rulebase>) or if * document is query parses <Query> element * * @param rmlRoot * The RuleML root element on which we start parsing at * * @throws ParseException */ private void parseRuleMLRoot(Element rmlRoot) throws ParseException { Elements children = rmlRoot.getChildElements(); for (int i = 0; i < children.size(); i++) { Element child = skipRoleTag(children.get(i)); String childName = child.getLocalName(); if (childName.equals(tagNames.ATOM)) { resetVariables(); clauses.add(parseFact(child)); } else if (childName.equals(tagNames.NEG)) { clauses.addAll(parseNegFact(child)); } else if (childName.equals(tagNames.IMPLIES)) { resetVariables(); clauses.addAll(parseImplies(child)); } else if (childName.equals(tagNames.AND)) { parseRuleMLRoot(child); } } } /** * Removes <Reify> elements recursively from a given element * * @param element * Element to skip <Reify> elements in */ private void removeReifyElements(Element element) { Elements children = element.getChildElements(); for (int i = 0; i < children.size(); i++) { Element child = children.get(i); if (child.getLocalName().equals(tagNames.REIFY)) { element.removeChild(child); } else { removeReifyElements(child); } } } /** * This method is used to parse an Assertion in the RuleML Document. * * @param ass * The XOM Element object that represents the assertion. * * @return A term object that represents the assertion in a way that can be * used by the reasoning engine. * * @throws ParseException * Thrown if there is a serious error parsing the assertion. */ private Term parseAssert(Element ass) throws ParseException { Elements children = ass.getChildElements(); Element firstChild = skipRoleTag(children.get(0)); if (firstChild.getLocalName().equals(tagNames.ATOM)) { DefiniteClause dc = parseFact(firstChild); Vector<Term> v = new Vector<Term>(); for (int i = 0; i < dc.atoms.length; i++) { v.add(dc.atoms[i]); } Term t = new Term(SymbolTable.IASSERT, SymbolTable.INOROLE, Types.IOBJECT, v); t.setAtom(true); return t; } else if (firstChild.getLocalName().equals(tagNames.IMPLIES)) { Vector<DefiniteClause> v2 = parseImplies(firstChild); DefiniteClause dc = (DefiniteClause) v2.get(0); Vector<Term> v = new Vector<Term>(); for (int i = 0; i < dc.atoms.length; i++) { v.add(dc.atoms[i]); } Term t = new Term(SymbolTable.IASSERT, SymbolTable.INOROLE, Types.IOBJECT, v); t.setAtom(true); return t; } else { throw new ParseException("<Assert> should only contain <Atom> (fact) or <Implies> elements."); } } /** * This method is used to parse a Negative Fact in the RuleML Document. * * @param neg * The XOM Element object that represents the neg fact. * * @return A definite clause vector that represents the negFact in a way * that can be used by the reasoning engine. * * @throws ParseException * Thrown if there is a serious error parsing the negFact. */ private Vector<DefiniteClause> parseNegFact(Element neg) throws ParseException { resetVariables(); Elements atoms = neg.getChildElements("Atom"); if (atoms.size() != 1) { logger.error("OO jDREW only supports classical negation over single atoms."); throw new ParseException("OO jDREW only supports classical negation over single atoms."); } Element firstAtom = skipRoleTag(atoms.get(0)); Term atm = parseAtom(firstAtom, true, true); Hashtable<Integer, Integer> types = this.buildTypeTable(); this.fixVarTypes(atm, types); Vector<Term> atms = new Vector<Term>(); atms.add(atm); DefiniteClause dc = new DefiniteClause(atms, variableNames); Vector<DefiniteClause> v = new Vector<DefiniteClause>(); v.add(dc); // Add code to generate consistency check // This should be redone to create the consistency check better String atmstr = dc.toPOSLString(); String clause = "$inconsistent() :-"; clause += atmstr.substring(0, atmstr.length() - 1) + ","; clause += "\"" + atmstr.substring(atmstr.indexOf("-") + 1); System.err.println(clause); POSLParser pp = new POSLParser(); try { v.add(pp.parseDefiniteClause(clause)); } catch (Exception e) { throw new ParseException("Error creating consistency check."); } return v; } /** * This method is used to parse a fact element, creating a new variable name * list if indicated. Typically a new variable list is wanted; but in * certain cases (such as parsing an inner clause in an assert) the same * variable list must be used. * * @param atom * The XOM Element object that represents the fact to be parsed. * * @return A DefiniteClause data structure that represents the fact in a way * that can be used by the reasoning engine. * * @throws ParseException * Thrown if there is a serious error parsing the fact. */ private DefiniteClause parseFact(Element atom) throws ParseException { Term atm = parseAtom(atom, true, false); Hashtable<Integer, Integer> types = this.buildTypeTable(); this.fixVarTypes(atm, types); Vector<Term> atoms = new Vector<Term>(); atoms.add(atm); DefiniteClause dc = new DefiniteClause(atoms, variableNames); return dc; } /** * This method is used to parse a implication element, creating a new * variable name list if indicated. Typically a new variable list is wanted; * but in certain cases (such as parsing an inner clause in an assert) the * same variable list must be used. * * @param implies * The XOM Element object that represents the implication to be * parsed. * * @return A vector of DefiniteClause data structures that represents the * implication in a way that can be used by the reasoning engine. * * @throws ParseException * Thrown if there is a serious error parsing the implication. */ private Vector<DefiniteClause> parseImplies(Element implies) throws ParseException { Vector<DefiniteClause> newclauses = new Vector<DefiniteClause>(); // Implies must have two children (excluding OID elements) Elements children = implies.getChildElements(); // Set first and second child and skip OID element if exists. Element firstChild = skipRoleTag(children.get(0)); Element secondChild = skipRoleTag(children.get(1)); if (firstChild.getLocalName().equals(tagNames.OID)) { firstChild = skipRoleTag(children.get(1)); secondChild = skipRoleTag(children.get(2)); } String firstChildName = firstChild.getLocalName(); Element premise, conclusion; // Check if implies starts with premise element if (firstChildName.equals(tagNames.PREMISE)) { premise = firstChild.getChildElements().get(0); conclusion = secondChild.getChildElements().get(0); } // If implies starts with conclusion element else if (firstChildName.equals(tagNames.CONCLUSION)) { premise = secondChild.getChildElements().get(0); conclusion = firstChild.getChildElements().get(0); } // No premise or conclusion tag available else { // Use canonical order for stripe-skipped syntax premise = firstChild; conclusion = secondChild; } premise = skipRoleTag(premise); conclusion = skipRoleTag(conclusion); Vector<Term> subterms = new Vector<Term>(); if (conclusion.getLocalName().equals(tagNames.ATOM)) { subterms.add(parseAtom(conclusion, true, false)); } else if (conclusion.getLocalName().equals(tagNames.NEG)) { Elements headatms = conclusion.getChildElements(tagNames.ATOM); if (headatms.size() != 1) { throw new ParseException("<Neg> should only contain one <Atom>"); } Term atom = parseAtom(headatms.get(0), true, true); subterms.add(atom); String atomstr = atom.toPOSLString(true); String clause = "$Sinconsistent() :-"; clause += atomstr + ", \"" + atomstr.substring(6) + "."; System.err.println(clause); POSLParser pp = new POSLParser(); try { DefiniteClause dc2 = pp.parseDefiniteClause(clause); if (dc2 != null) newclauses.add(dc2); } catch (Exception e) { throw new ParseException("Error creating inconsistency check rule."); } } else { throw new ParseException( "Second child of <Implies> must be <Atom> or <Neg>."); } String premiseName = premise.getLocalName(); if (premiseName.equals(tagNames.ATOM)) { subterms.add(parseAtom(premise, false, false)); } else if (premiseName.equals(tagNames.NAF)) { subterms.add(parseNaf(premise)); } else if (premiseName.equals(tagNames.ASSERT)) { subterms.add(parseAssert(premise)); } else if (premiseName.equals(tagNames.NEG)) { subterms.add(parseAtom(premise, false, true)); } else if (premiseName.equals(tagNames.AND)) { children = premise.getChildElements(); for (int i = 0; i < children.size(); i++) { Element element = skipRoleTag(children.get(i)); String elementName = element.getLocalName(); if (elementName.equals(tagNames.ATOM)) { subterms.add(parseAtom(element, false, false)); } else if (elementName.equals(tagNames.NAF)) { subterms.add(parseNaf(element)); } else if (elementName.equals(tagNames.ASSERT)) { subterms.add(parseAssert(element)); } else if (elementName.equals(tagNames.NEG)) { subterms.add(parseAtom(element, false, true)); } else { throw new ParseException(String.format("<%s> is not allowed as child of <And>", elementName)); } } } else { throw new ParseException( "First element of <Implies> must be <Atom>, <And> or <Naf>."); } logger.debug("Building Types"); Hashtable<Integer, Integer> types = this.buildTypeTable(); logger.debug("Built Types"); Iterator<Term> it = subterms.iterator(); int i = 0; while (it.hasNext()) { Term t = (Term) it.next(); this.fixVarTypes(t, types); logger.debug("Fixed atom : " + i++); } DefiniteClause dc = new DefiniteClause(subterms, variableNames); newclauses.add(0, dc); return newclauses; } /** * This method is used to parse and <oid> element. * * @param oid * The XOM element that represents the oid to be parsed. * * @return Returns the data structure that represents the oid in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the oid. */ private Term parseOid(Element oid) throws ParseException { Element element = oid.getChildElements().get(0); Term term = parseDefaultElement(element); term.role = SymbolTable.IOID; return term; } /** * Method to parse an individual constant (Ind) * * @param ind * The XOM element that represents the Ind to be parsed. * * @return Returns the data structure that represents the Ind in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the Ind. */ private Term parseInd(Element ind) throws ParseException { return parseSimpleElement(ind); } /** * Method to parse an Data (Data) * * @param ind * The XOM element that represents the Data to be parsed. * * @return Returns the data structure that represents the Data in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the Data. */ // this is where you change stuff about the name space // uri anchoring and such, test to see if it supports any at all yet // Also INOROLE may be changed later not sure yet when creating a Data // Term Data has no role so it will probally not change private Term parseData(Element data) throws ParseException { Term term = parseSimpleElement(data); term.setData(true); return term; } /** * Generates a anonymous identifier for a given symbol name * * @param symbolName * Input symbol * * @return String Symbol name as a unique anonymous identifier */ private String generateAnonymousIdentifier() { return "$ANON" + anonid++; } /** * Method to parse a Var (variable) * * @param var * The XOM element that represents the Var to be parsed. * * @return Returns the data structure that represents the Var in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the Var. */ private Term parseVar(Element var) throws ParseException { String symbolName = var.getValue().trim(); if (symbolName.isEmpty()) { symbolName = generateAnonymousIdentifier(); } int sym = this.internVariable(symbolName); int typeid = parseTypeAttribute(var); logger.debug("Parsing variable: symbol = " + sym + " type = " + typeid); Vector<Integer> v; if (this.varClasses.containsKey(sym)) { v = varClasses.get(sym); } else { v = new Vector<Integer>(); varClasses.put(sym, v); } v.add(typeid); logger.debug("Added Type Information"); return new Term(sym, SymbolTable.INOROLE, typeid); } /** * Method to parse a plex * * @param plex * Element The XOM element that represents the plex to be parsed. * * @return Returns the data structure that represents the plex in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the plex. */ private Term parsePlex(Element plex) throws ParseException { Vector<Term> subterms = parseDefaultElements(plex); Term t = new Term(SymbolTable.IPLEX, SymbolTable.INOROLE, Types.IOBJECT, subterms); return t; } /** * Method to parse an Expr (expression) * * @param expr * The XOM element that represents the Expr to be parsed. * * @return Returns the data structure that represents the Expr in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the Expr. */ private Term parseExpression(Element expr) throws ParseException { Elements children = expr.getChildElements(); Element op = children.get(0); boolean foundOp = false; if (op.getLocalName().equals(tagNames.OP)) { foundOp = true; } Element fun = null; if (foundOp) { Elements funTag = op.getChildElements(); fun = funTag.get(0); // Remove <op> element including its child element <Fun>, so // it will not be parsed twice expr.removeChild(op); } if (!foundOp) { fun = children.get(0); // Remove <Fun> element so that it will not be parsed twice expr.removeChild(fun); } if (!fun.getLocalName().equals(tagNames.FUN)) { throw new ParseException(String.format( "First child of <%s> in <%s> must be <%s>", tagNames.OP, tagNames.EXPR, tagNames.FUN)); } int symbol = SymbolTable.internSymbol(fun.getValue().trim()); int typeid = parseTypeAttribute(expr); Vector<Term> subterms = parseDefaultElements(expr); Term t = new Term(symbol, SymbolTable.INOROLE, typeid, subterms); return t; } /** * Method to parse an Atom * * @param Atom * The XOM element that represents the Atom to be parsed. * * @param head * This tells the engine if the atom is a head of a rule. * * @param neg * This tells the engine if the atom is a negative atom (Neg) * * @return Returns the data structure that represents the Atom in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the Atom. */ private Term parseAtom(Element atom, boolean head, boolean neg) throws ParseException { boolean foundoid = false; Elements children = atom.getChildElements(); // checking for op tag before the rel Element op = getFirstChildElement(atom, tagNames.OP); Element rel = null; if (op != null) { Elements relTag = op.getChildElements(); rel = relTag.get(0); } else { rel = getFirstChildElement(atom, tagNames.REL); } if (rel == null) { throw new ParseException(String.format( "In an <Atom>, first child of <%s> must be a <Rel>.", tagNames.OP)); } String relname = rel.getValue().trim(); if (neg) { relname = "$neg-" + relname; } int symbol = SymbolTable.internSymbol(relname); Vector<Term> subterms = new Vector<Term>(); int startIndex = getFirstChildElementIndex(children, 0) + 1; for (int i = startIndex; i < children.size(); i++) { Element element = children.get(i); Term term = null; if (element.getLocalName().equals(tagNames.OID)) { if (foundoid) { throw new ParseException(String.format( "<%s> must contain at most one <%s>.", tagNames.ATOM, tagNames.OID)); } term = parseOid(element); foundoid = true; } else { // Hack: remove <arg index="..."> element and assume // that elements are in canonical order element = skipRoleTag(element); term = parseDefaultElement(element); } subterms.add(term); } // Generate a fake OID if the atom does not have one. // This is a requirement put in place by the reasoner. if (!foundoid) { if (head) { String symname = "$gensym" + SymbolTable.genid++; int symid = SymbolTable.internSymbol(symname); Term t2 = new Term(symid, SymbolTable.IOID, Types.IOBJECT); subterms.add(t2); } else { String varname = generateAnonymousIdentifier(); int symid = this.internVariable(varname); Vector<Integer> types = new Vector<Integer>(); types.add(Types.IOBJECT); this.varClasses.put(symid, types); Term t2 = new Term(symid, SymbolTable.IOID, Types.IOBJECT); subterms.add(t2); } } Term t = new Term(symbol, SymbolTable.INOROLE, Types.IOBJECT, subterms); t.setAtom(true); return t; } /** * Method to parse a Slot * * @param slot * The XOM element that represents the slot to be parsed. * * @return Returns the data structure that represents the slot in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the slot. */ private Term parseSlot(Element slot) throws ParseException { Elements children = slot.getChildElements(); Element firstChildName = children.get(0); boolean dataSlot = false; if (firstChildName.getLocalName().equals(tagNames.DATA)) { dataSlot = true; } if (!firstChildName.getLocalName().equals(tagNames.IND) && !firstChildName.getLocalName().equals(tagNames.DATA)) { throw new ParseException( "In a <slot> only <Ind> and <Data> are allowed."); } // Getting the Role from the symbol Table, it will assign one if it // does not already exist int role = SymbolTable.internRole(firstChildName.getValue().trim()); Element element = children.get(1); Term term = parseDefaultElement(element); term.setRole(role); if (dataSlot) { term.setDataSlot(true); } return term; } /** * Method to parse a resl (Rested Slot) * * @param resl * The XOM element that represents the resl to be parsed. * * @return Returns the data structure that represents the resl in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the resl. */ private Term parseResl(Element resl) throws ParseException { Elements children = resl.getChildElements(); Element firstChild = skipRoleTag(children.get(0)); Term t = parseDefaultElement(firstChild); // HACK: only change symbol, if term is no <Plex> if (t.getSymbol() != SymbolTable.IPLEX) { t.setRole(SymbolTable.IREST); } return t; } /** * Method to parse a repo (Rested Positional Slot) * * @param repo * The XOM element that represents the repo to be parsed. * * @return Returns the data structure that represents the repo in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the repo. */ private Term parseRepo(Element repo) throws ParseException { Term t = parseResl(repo); t.setRole(SymbolTable.IPREST); return t; } /** * Method to parse a naf (Negation as Failure) * * @param naf * The XOM element that represents the naf atom to be parsed. * * @return Returns the data structure that represents the naf in a way that * can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the naf. */ private Term parseNaf(Element naf) throws ParseException { Elements children = naf.getChildElements(); Element el = skipRoleTag(children.get(0)); Vector<Term> subterms = new Vector<Term>(); if (el.getLocalName().equals(tagNames.ATOM)) { subterms.add(parseAtom(el, false, false)); } else if (el.getLocalName().equals(tagNames.AND)) { children = el.getChildElements(); for (int i = 0; i < children.size(); i++) { Element el2 = children.get(i); if (!el2.getLocalName().equals(tagNames.ATOM)) { throw new ParseException( "<And> as child of <Naf> should only contain <Atom> elements."); } subterms.add(parseAtom(el2, false, false)); } } else { throw new ParseException( "<Naf> should only contain <Atom> or <And>."); } Term t = new Term(SymbolTable.INAF, SymbolTable.INOROLE, Types.IOBJECT, subterms); t.setAtom(true); return t; } /** * Method to parse a Skolem constant * * @param sko * Element The XOM element that represents the Skolem to be * parsed. * * @return Returns the data structure that represents the Skolem in a way * that can be used by the reasoning engine. * * @throws ParseException * Thrown if there is an error parsing the Skolem. */ private Term parseSkolem(Element sko) throws ParseException { String skoname = sko.getValue().trim(); if (skoname.isEmpty()) { return new Term(SymbolTable.internSymbol("$gensym" + SymbolTable.genid++), SymbolTable.INOROLE, Types.ITHING); } else { if (this.skolemMap.containsKey(skoname)) { String sym = skolemMap.get(skoname); return new Term(SymbolTable.internSymbol(sym), SymbolTable.INOROLE, Types.ITHING); } else { String sym = "$gensym" + (SymbolTable.genid++) + "$" + skoname; skolemMap.put(skoname, sym); return new Term(SymbolTable.internSymbol(sym), SymbolTable.INOROLE, Types.ITHING); } } } /** * Initialize a variable for use. Stores the variable name and assigns a * integer identifier if it is unused in this clause; returns the existing * identifier if it has already been used in this clause. * * Variables are Negative and Ind are positive * * @param varName * String The variable name. * * @return The integer identifier for this variable */ private int internVariable(String varName) { int idx; idx = variableNames.indexOf(varName); if (idx == -1) { idx = variableNames.size(); variableNames.add(varName); } return -(idx + 1); } /** * A method that will go through a term and fix all variable types to be * consistent. * * @param complexTerm * Term The term to normalize the types in. * * @param types * A hash table containing the normalized types for each variable * in the clause. */ private void fixVarTypes(Term complexTerm, Hashtable<Integer, Integer> types) { logger.debug("Fixing term: " + complexTerm.toPOSLString(true)); for (int i = 0; i < complexTerm.subTerms.length; i++) { if (complexTerm.subTerms[i].isExpr()) { fixVarTypes(complexTerm.subTerms[i], types); } else if (complexTerm.subTerms[i].getSymbol() < 0) { Integer sym = complexTerm.subTerms[i].getSymbol(); logger.debug("Fixing symbol = " + sym); Integer type = (Integer) types.get(sym); logger.debug("Type = " + type); complexTerm.subTerms[i].type = type.intValue(); } } } /** * A method to find the normalized types for each variable in the clause. * * @return A hash table containing the normalized type for each variable in * the current clause. * * @throws ParseException * A ParseException is thrown a variable does not have a * normalized form (type that is an intersection of all given * types); since Nothing inherits from all types this should * never occur. */ private Hashtable<Integer, Integer> buildTypeTable() throws ParseException { Hashtable<Integer, Integer> ht = new Hashtable<Integer, Integer>(); Enumeration<Integer> e = varClasses.keys(); while (e.hasMoreElements()) { int key = e.nextElement(); Vector<Integer> value = varClasses.get(key); int[] types = new int[value.size()]; for (int i = 0; i < types.length; i++) { types[i] = ((Integer) value.get(i)).intValue(); } int type = Types.greatestLowerBound(types); ht.put(key, type); } return ht; } /** * Parses the given element and adds parsed data to sub terms * * Known elements are: Plex, Expr, Ind, Data, sko, Var, slot, repo, resl * * @param element * The element which has to be parsed * * @return Returns the data structure that represents the Element in a way * that can be used by the reasoning engine. * * @throws ParseException * Thrown if this method is not able to handle element. */ private Term parseDefaultElement(Element element) throws ParseException { String elementName = element.getLocalName(); Term result = null; if (elementName.equals(tagNames.PLEX)) { result = parsePlex(element); } else if (elementName.equals(tagNames.EXPR)) { result = parseExpression(element); } else if (elementName.equals(tagNames.IND)) { result = parseInd(element); } else if (elementName.equals(tagNames.DATA)) { result = parseData(element); } else if (elementName.equals(tagNames.SKOLEM)) { result = parseSkolem(element); } else if (elementName.equals(tagNames.VAR)) { result = parseVar(element); } else if (elementName.equals(tagNames.SLOT)) { result = parseSlot(element); } else if (elementName.equals(tagNames.RESL)) { result = parseResl(element); } else if (elementName.equals(tagNames.REPO)) { result = parseRepo(element); } else { throw new ParseException(String.format( "Element (%s) not supported!", elementName)); } return result; } /** * Get the inner element with role tag skipped * * (e.g. <arg><Atom>...</Atom></arg> returns Atom element including all * children of the Atom element) * * @param element * Element for which role tags should be skipped * * @return The inner element without role tag encapsulation */ private Element skipRoleTag(Element element) { Element result = element; String elementName = element.getLocalName(); boolean hasRoleTag = false; if (elementName.equals(tagNames.ARG)) { hasRoleTag = true; } else if (elementName.equals(tagNames.FORMULA)) { hasRoleTag = true; } else if (elementName.equals(tagNames.ACT)) { hasRoleTag = true; } else if (elementName.equals(tagNames.DECLARE)) { hasRoleTag = true; } else if (elementName.equals(tagNames.STRONG)) { hasRoleTag = true; } else if (elementName.equals(tagNames.WEAK)) { hasRoleTag = true; } else if (elementName.equals(tagNames.TORSO)) { hasRoleTag = true; } else if (elementName.equals(tagNames.DEGREE)) { hasRoleTag = true; } if (hasRoleTag) { logger.debug(String.format("%s element skipped", elementName)); result = element.getChildElements().get(0); } return result; } /** * Parses all children of the given element with the parseDefaultElement * method and returns all sub-terms created from those elements. * * @param element * The element whose children should be parsed. * * @return A Vector of Term objects generated by parsing the element's * children. * * @throws ParseException * Thrown if the parseDefaultElement method is unable to handle * one of element's children. */ private Vector<Term> parseDefaultElements(Element element) throws ParseException { Elements children = element.getChildElements(); Vector<Term> subterms = new Vector<Term>(); for (int i = 0; i < children.size(); i++) { Element child = children.get(i); Term term = parseDefaultElement(child); subterms.add(term); } return subterms; } /** * Get the first element with the given name * * @param elements * Element to search for the child * * @param childName * Name of the element to look for * * @return The first Element which is labeled with childName */ private Element getFirstChildElement(Element element, String childName) { Elements children = element.getChildElements(); for (int i = 0; i < children.size(); i++) { Element child = skipRoleTag(children.get(i)); if (child.getLocalName().equals(childName)) { return child; } } return null; } /** * Get the first element which is not labeled with OID * * @param elements * Elements to search for child * * @param startIndex * Start index to start search at * * @return Index of the first element which is not labeled with OID. If not * exist, returns -1. */ private int getFirstChildElementIndex(Elements elements, int startIndex) { for (int i = startIndex; i < elements.size(); i++) { Element child = skipRoleTag(elements.get(i)); if (!child.getLocalName().equals(tagNames.OID)) { return i; } } return -1; } /** * Parse the type attribute of <Expr>, <Ind>, <Data> and <Var> elements. * * @param element * The element whose type attribute should be parsed. * * @return A unique numeric identifier generated by the type system. * * @throws ParseException * Thrown if the type specified in the type attribute is * invalid. */ private int parseTypeAttribute(Element element) throws ParseException { Attribute type = element.getAttribute(tagNames.TYPE); int typeid = Types.IOBJECT; if (type != null) { typeid = Types.typeID(type.getValue().trim()); if (typeid == -1) { throw new ParseException("Type " + type.getValue().trim() + " is not defined."); } } return typeid; } /** * Parse a simple element that just contains plain data (like <Ind> and * <Data>). * * @param element * The element to parse * * @return A Term data structure that represents the element in a way that * can be understood by the reasoning engine. * * @throws ParseException * Thrown if the type specified in the optional type attribute * is invalid. */ private Term parseSimpleElement(Element element) throws ParseException { String symbolName = element.getValue().trim(); if (symbolName.isEmpty()) { symbolName = generateAnonymousIdentifier(); } int sym = SymbolTable.internSymbol(symbolName); int typeid = parseTypeAttribute(element); return new Term(sym, SymbolTable.INOROLE, typeid); } /** * Reset the internal variable name and variable type lookup tables. */ private void resetVariables() { this.variableNames = new Vector<String>(); this.varClasses = new Hashtable<Integer, Vector<Integer>>(); } /** * Set the logging level of the log4j logger * * @param logLevel * The logging level */ public void setLogLevel(Level logLevel) { logger.setLevel(logLevel); } }