/* * This software is Copyright 2005,2006,2007,2008 Langdale Consultants. * Langdale Consultants can be contacted at: http://www.langdale.com.au */ package au.com.langdale.inference; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import com.hp.hpl.jena.graph.Graph; import com.hp.hpl.jena.graph.Node; import com.hp.hpl.jena.graph.Triple; import com.hp.hpl.jena.reasoner.TriplePattern; import com.hp.hpl.jena.util.PrintUtil; import com.hp.hpl.jena.util.iterator.ExtendedIterator; import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDFS; import au.com.langdale.inference.Extractor.FunctorActions; import au.com.langdale.inference.Extractor.RuleState; import au.com.langdale.inference.Extractor.TerminateExtractor; import au.com.langdale.kena.ModelFactory; import au.com.langdale.kena.Resource; import au.com.langdale.kena.ResourceFactory; import au.com.langdale.util.NSMapper; import au.com.langdale.splitmodel.SplitReader; /** * Functor definitions for use by <code>Extractor</code>. * * These are different to those declared in <code>ValidationBuiltins</code> because * they implement <code>FunctorActions</code> and are compatible with the * asynchronous design of <code>Extractor</code> and <code>SplitReader</code>. */ public class StandardFunctorActions extends Reporting { public static final String ARGS = "incorrect number of arguments"; public static final String SUBJECT = "no subject supplied"; public static final String INT_ARG = "first argument must be an integer"; public static final String HEAD = "not allowed in rule head"; public static final String BODY = "not allowed in rule body"; public static final String NS = "http://langdale.com.au/2007/Functor#"; public static final Node SUB_CLASS_OF = RDFS.Nodes.subClassOf; public static final Node RDF_TYPE = RDF.Nodes.type; public static final Node HAS_PROBLEMS = LOG.hasProblems.asNode(); public static final Node COMMENT = RDFS.Nodes.comment; public static final Node PROBLEM_PER_SUBJECT = Node.createURI(NS + "problem_per_subject"); public static final Node OPTION = Node.createURI(NS + "Option"); public static void setOption(Graph axioms, Node option, boolean state) { Triple flag = Triple.create(option, RDF_TYPE, OPTION); if( state ) axioms.add(flag); else axioms.delete(flag); } public static boolean getOption(Graph axioms, Node option) { return axioms.contains(Triple.create(option, RDF_TYPE, OPTION)); } public static void check(boolean assertion, String message) { } public static Node var2Any(Node node) { return SplitReader.var2Any(node); } public static Integer getInteger(Node node) { if( node.isLiteral()) { Object value = node.getLiteralValue(); if( value instanceof Integer) return (Integer) value; } return null; } public static abstract class Test implements FunctorActions { public void apply(Node[] nodes, Graph model, Graph axioms, RuleState state) { check(false, HEAD); } } public static abstract class SimpleTest extends Test { public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { if( eval(nodes, axioms, state)) state.dispatch(); else state.cancel(); } protected abstract boolean eval(Node[] nodes, Graph axioms, RuleState state); } public static class Axiom extends SimpleTest { @Override protected boolean eval(Node[] nodes, Graph axioms, RuleState state) { check(nodes.length == 3, ARGS); Iterator it = axioms.find(var2Any(nodes[0]), var2Any(nodes[1]), var2Any(nodes[2])); if( it.hasNext()) { Triple t = (Triple) it.next(); state.bind(nodes[0], t.getSubject()); state.bind(nodes[1], t.getPredicate()); state.bind(nodes[2], t.getObject()); return true; } else { return false; } } } public static class Same extends SimpleTest { @Override protected boolean eval(Node[] nodes, Graph axioms, RuleState state) { check(nodes.length == 2, ARGS); return nodes[0].equals(nodes[1]); } } public static class Greater extends SimpleTest { @Override protected boolean eval(Node[] nodes, Graph axioms, RuleState state) { check(nodes.length == 2, ARGS); Integer a = getInteger(nodes[0]); Integer b = getInteger(nodes[1]); return a != null && b != null && a.intValue() > b.intValue(); } } public static class DatatypeTest extends SimpleTest { @Override protected boolean eval(Node[] nodes, Graph axioms, RuleState state) { check(nodes.length == 2, ARGS); return isLexicalForm(nodes[0], nodes[1]); } } public static class LiteralTest extends SimpleTest { @Override protected boolean eval(Node[] nodes, Graph axioms, RuleState state) { check(nodes.length == 1, ARGS); return nodes[0].isLiteral(); } } public static class AnonTest extends SimpleTest { @Override protected boolean eval(Node[] nodes, Graph axioms, RuleState state) { check(nodes.length == 1, ARGS); return nodes[0].isBlank(); } } public static class Not extends SimpleTest { private SimpleTest delegate; public Not(SimpleTest delegate) { this.delegate = delegate; } @Override protected boolean eval(Node[] nodes, Graph axioms, RuleState state) { return ! delegate.eval(nodes, axioms, state); } } public static class Any extends Test { private boolean sense; public Any(boolean sense) { this.sense = sense; } public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { check(nodes.length == 3, ARGS); boolean axiom = axioms.contains(var2Any(nodes[0]), var2Any(nodes[1]), var2Any(nodes[2])); if(sense && axiom) state.dispatch(); else if( (! sense) && axiom ) state.cancel(); else model.find(new TriplePattern(nodes[0], nodes[1], nodes[2]), new CountResult(state, sense, 1)); } } public static class TypeTest extends Test { private boolean sense; public TypeTest(boolean sense) { this.sense = sense; } public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { check(nodes.length == 2, ARGS); model.find(new TriplePattern(nodes[0], RDF_TYPE, Node.ANY), new TypeResult(axioms, nodes[1], state, sense)); } } public static class TypeResult extends CountResult { private Graph axioms; private Node clss; public TypeResult(Graph axioms, Node clss, RuleState state, boolean sense) { super(state, sense, 1); this.axioms = axioms; this.clss = clss; } @Override public boolean add(Triple result) { if(result.getObject().equals(clss) || axioms.contains(result.getObject(), SUB_CLASS_OF, clss)) return super.add(result); else return true; } } public static class NoMatch extends Test { public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { check(nodes.length == 3, ARGS); model.find(new TriplePattern(nodes[0], nodes[1], nodes[2]), new CountResult(state, false, 1)); } } public static class Count extends Test { private boolean greater, equal; public Count(boolean greater, boolean equal) { this.greater = greater; this.equal = equal; } public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { check(nodes.length == 4, ARGS); Integer arg = getInteger(nodes[0]); check(arg != null, INT_ARG); int limit = (greater^equal)? arg.intValue()+1: arg.intValue(); model.find(new TriplePattern(nodes[1], nodes[2], nodes[3]), new CountResult(state, greater, limit)); } } public static class CountResult implements AsyncResult { private RuleState state; private boolean sense; private int limit; private int count; public CountResult(RuleState state, boolean sense, int limit) { this.state = state; this.sense = sense; this.limit = limit; } public boolean add(Triple result) { count++; if(count >= limit) { if( sense ) state.dispatch(); else state.cancel(); return false; // do not continue } return true; } public void close() { if( ! sense ) state.dispatch(); else state.cancel(); } } public static class Problem implements FunctorActions { public static final int MAX_REPORT_SIZE = 1000; private static final Node FINAL_MESSAGE = Node.createLiteral("Too many problems found. First " + MAX_REPORT_SIZE + " are shown."); private static final int STATEMENTS_PER_PROBLEM = 5; public void apply(Node[] nodes, Graph model, Graph axioms, RuleState state) { int start = nodes.length > 0 && nodes[0].isVariable() ? 1 : 0; Node subject = getReportSubject(nodes, start, nodes.length); Node phrase = getReportPhrase("error", nodes, start, nodes.length); if(subject != null && getOption(axioms, PROBLEM_PER_SUBJECT)) { ExtendedIterator it = model.find(subject, HAS_PROBLEMS, Node.ANY); while (it.hasNext()) { Triple report = (Triple) it.next(); if(model.contains(report.getObject(), COMMENT, phrase)) return; } } Node report = createReport(model, subject, phrase, nodes, start, nodes.length); if( start == 1) { state.bind(nodes[0], report); } int size = model.size()/STATEMENTS_PER_PROBLEM; if( size % 100 == 0) System.out.println("Result count: " + size); if( size >= MAX_REPORT_SIZE) { createReport(model, null, FINAL_MESSAGE, new Node[0], 0, 0); throw new TerminateExtractor(); } } public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { check(false, BODY); } } public static class Correction implements FunctorActions { public void apply(Node[] nodes, Graph model, Graph axioms, RuleState state) { check(nodes.length >= 2, ARGS); Node repair = Node.createAnon(); model.add(new Triple(repair, RDF.Nodes.type, LOG.Repair.asNode())); model.add(new Triple(repair, RDFS.Nodes.comment, nodes[1])); model.add(new Triple(nodes[0], LOG.hasRepairs.asNode(), repair)); } public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { check(false, BODY); } } public static class Similar implements FunctorActions { Graph currentAxioms; NSMapper mapper; public void apply(Node[] nodes, Graph model, Graph axioms, RuleState state) { check(false, HEAD); } public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { check(nodes.length == 3, ARGS); Node type = nodes[0]; Node subject = nodes[1]; Node variable = nodes[2]; if(axioms != currentAxioms) { mapper = new NSMapper(ModelFactory.createMem(axioms)); currentAxioms = axioms; } if(subject.isURI()) { Resource result = mapper.map( subject.getLocalName(), ResourceFactory.createResource(type)); if(result != null) { state.bind(variable, result.asNode()); state.dispatch(); } else state.cancel(); } else state.cancel(); } } public static class Debug implements FunctorActions { public void apply(Node[] nodes, Graph model, Graph axioms, RuleState state) { System.out.print("fired:"); String name = state.getRule().getName(); if(name != null) { System.out.print( name + ":" ); } print(nodes); } public void match(Node[] nodes, AsyncModel model, Graph axioms, RuleState state) { System.out.print("matched:"); String name = state.getRule().getName(); if(name != null) { System.out.print( name + ":" + state.getClause()); } print(nodes); state.dispatch(); } private void print(Node[] nodes) { for (int ix = 0; ix < nodes.length; ix++) { System.out.print(" " + PrintUtil.print(nodes[ix])); } System.out.println(); } } public static Map create() { Map map = new HashMap(); map.put("axiom", new Axiom()); map.put("notAxiom", new Not(new Axiom())); map.put("same", new Same()); map.put("notSame", new Not(new Same())); map.put("greater", new Greater()); map.put("notGreater", new Not(new Greater())); map.put("datatype", new DatatypeTest()); map.put("notDatatype", new Not( new DatatypeTest())); map.put("literal", new LiteralTest()); map.put("notLiteral", new Not( new LiteralTest())); map.put("anon", new AnonTest()); map.put("uri", new Not( new AnonTest())); map.put("any", new Any(true)); map.put("notAny", new Any(false)); map.put("type", new TypeTest(true)); map.put("notType", new TypeTest(false)); map.put("not", new NoMatch()); map.put("countMoreThan", new Count(true, false)); map.put("countLessThan", new Count(false, false)); map.put("countAtLeast", new Count(true, true)); map.put("countAtMost", new Count(false, true)); map.put("problem", new Problem()); map.put("correction", new Correction()); map.put("similar", new Similar()); map.put("debug", new Debug()); return map; } }