// 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);
}
}