// 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.io.File; import java.io.IOException; import java.io.StringReader; import java.util.Iterator; import java.util.Vector; import java.util.prefs.PreferenceChangeEvent; import java.util.prefs.PreferenceChangeListener; import nu.xom.Builder; import nu.xom.Document; import nu.xom.Element; import nu.xom.Nodes; import nu.xom.ParsingException; import org.apache.log4j.Level; import org.ruleml.oojdrew.Configuration; import org.ruleml.oojdrew.util.DefiniteClause; /** * A class for parsing RuleML. This is broken into two section. The RuleMLParser * class which is the public interface that users access; and the RuleML88Parser * class; which implements the parsing of the RuleML 0.88 + rests syntax that is * currently supported. * * <p> * Title: OO jDREW * </p> * * <p> * Description: Reasoning Engine for the Semantic Web - Supporting OO RuleML * 0.88 * </p> * * <p> * Copyright: Copyright (c) 2005 * </p> * * @author Marcel A. Ball * @version 0.89 */ public class RuleMLParser implements PreferenceChangeListener { /** * A buffer that stores clauses that have already been parsed. */ private Vector<DefiniteClause> clauses; private Configuration config; private RuleMLFormat rmlFormat; private Level logLevel; /** * Constructs a new parser object. * * @param config * The configuration instance which should be used */ public RuleMLParser(Configuration config) { clauses = new Vector<DefiniteClause>(); this.config = config; config.addPreferenceChangeListener(this); preferenceChange(null); } /** * Gets an iterator over all clauses that are stored in the internal clause * buffer. This method does not automatically clear items from the buffer. * * @return Iterator An iterator over all clauses in the buffer. */ public Iterator<DefiniteClause> iterator() { return clauses.iterator(); } /** * Clears the internal buffer; and forces a garbage collection cycle. This * allows the easy reuse of a parser object. */ public void clear() { clauses = new Vector<DefiniteClause>(); System.gc(); } /** * @see jdrew.oo.parsing.RuleMLParser#parseDocument */ public void parseFile(String filename) throws ParseException, ParsingException, IOException { File file = new File(filename); parseFile(file); } /** * @see jdrew.oo.parsing.RuleMLParser#parseDocument */ public void parseFile(File file) throws ParseException, ParsingException, IOException { Builder bl = new Builder(); Document doc = bl.build(file); parseDocument(doc); } /** * @see jdrew.oo.parsing.RuleMLParser#parseDocument */ public void parseRuleMLString(String contents) throws ParseException, ParsingException, IOException { Builder bl = new Builder(); StringReader sr = new StringReader(contents); Document doc = bl.build(sr); parseDocument(doc); } /** * Parses a document containing a knowledge base that is in the indicated * format. If additional backed parsers are created then additional formats * will be added. * * NOTE: It may be a good idea to add format auto detection based upon the * XSD and/or DTD that is referenced by the document. * * @param format * RuleMLVersion The RuleML version for the back-end parser * * @param doc * The document which should be parsed * * @throws ParseException * A ParseException is thrown if there is an error in the * document that causes parsing to fail. * * @throws ParsingException * A ParsingException is thrown if there is an error in parsing * the document at an XML level or a ValidityException * (subclass) is thrown if the XML document is not well * formed * or does not conform to the DTD specified. */ public void parseDocument(Document doc) throws ParseException, ParsingException { RuleMLDocumentParser parser = new RuleMLDocumentParser(rmlFormat, clauses); parser.setLogLevel(logLevel); parser.parseRuleMLDocument(doc); } /** * Parses a RuleML query * * @param contents * The document content * * @return A DefiniteClause containing the parsed document * * @throws ParseException * @throws ParsingException * @throws IOException */ public DefiniteClause parseRuleMLQuery(String contents) throws ParseException, ParsingException, IOException { contents = ensureQueryTag(contents); contents = buildFakeImplication(contents); parseRuleMLString(contents); return (DefiniteClause) clauses.lastElement(); } /** * Builds a fake implication which is needed to satisfy the reasoning * engine. * * @param query * An input query * * @return Input query as a RuleML implication */ private String buildFakeImplication(String query) { Builder builder = new Builder(); StringReader stringReader = new StringReader(query); Document document; try { document = builder.build(stringReader); } catch (Exception e) { // Cannot happen e.printStackTrace(); return ""; } Element queryElement = document.getRootElement(); RuleMLTagNames rmlTags = new RuleMLTagNames(rmlFormat); Element rel = new Element(rmlTags.REL); rel.insertChild("$top", 0); Element topAtom = new Element(rmlTags.ATOM); topAtom.appendChild(rel); Nodes atoms = queryElement.removeChildren(); Element implies = new Element(rmlTags.IMPLIES); for (int i = 0; i < atoms.size(); ++i) { implies.appendChild(atoms.get(i)); } implies.appendChild(topAtom); queryElement.appendChild(implies); return document.toXML(); } /** * Manually add <Query> wrapper if not exists * * @param query * An input query * * @return Input query encapsulated with a <Query> tag */ private String ensureQueryTag(String query) { // Evil hack: encapsulate the query contents in a pair of <Query> tags RuleMLTagNames rmlTags = new RuleMLTagNames(rmlFormat); query = query.trim(); String queryTagOpen = String.format("<%s>", rmlTags.QUERY); String queryTagClose = String.format("</%s>", rmlTags.QUERY); if (!query.contains(queryTagOpen)) { query = String.format("%s%s%s", queryTagOpen, query, queryTagClose); } return query; } /** * Updates parser configuration when preferences have changed */ public void preferenceChange(PreferenceChangeEvent evt) { rmlFormat = config.getRuleMLFormat(); logLevel = config.getLogLevel(); } }