You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2021/02/14 09:31:55 UTC

[jena] branch main updated: JENA-2019: Parser fix; printing fix; more tests

This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/main by this push:
     new 7d095fd  JENA-2019: Parser fix; printing fix; more tests
     new 7812905  Merge pull request #922 from afs/jena2019-rule-parser
7d095fd is described below

commit 7d095fdbf04bef7d06a1737938067027ba0bf16c
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Sat Feb 13 18:52:01 2021 +0000

    JENA-2019: Parser fix; printing fix; more tests
---
 .github/workflows/maven.yml                        |  23 ++
 .../java/org/apache/jena/datatypes/TypeMapper.java |  11 +
 .../org/apache/jena/reasoner/rulesys/Functor.java  |  68 ++---
 .../jena/reasoner/rulesys/FunctorDatatype.java     |  50 +++
 .../org/apache/jena/reasoner/rulesys/Rule.java     |  54 ++--
 .../reasoner/rulesys/impl/RETEClauseFilter.java    |   2 +-
 .../main/java/org/apache/jena/util/PrintUtil.java  |  45 +--
 .../main/java/org/apache/jena/util/Tokenizer.java  |  88 +++---
 .../jena/reasoner/rulesys/test/TestBasics.java     | 338 +++++++++++++--------
 9 files changed, 432 insertions(+), 247 deletions(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
new file mode 100644
index 0000000..0d16a49
--- /dev/null
+++ b/.github/workflows/maven.yml
@@ -0,0 +1,23 @@
+## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0
+
+name: Apache Jena CI
+
+on:
+  push:
+    branches: [ main ]
+#  pull_request:
+#    branches: [ main ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up JDK 11
+      uses: actions/setup-java@v1
+      with:
+        java-version: '11'
+    - name: Build with Maven
+      run: mvn -B verify --file pom.xml
diff --git a/jena-core/src/main/java/org/apache/jena/datatypes/TypeMapper.java b/jena-core/src/main/java/org/apache/jena/datatypes/TypeMapper.java
index 1f03d86..872f0bb 100644
--- a/jena-core/src/main/java/org/apache/jena/datatypes/TypeMapper.java
+++ b/jena-core/src/main/java/org/apache/jena/datatypes/TypeMapper.java
@@ -189,4 +189,15 @@ public class TypeMapper {
             classToDT.put(jc, type);
         }
     }
+    
+    /**
+     * Remove a datatype registration.
+     */
+    public void unregisterDatatype(final RDFDatatype type) {
+        uriToDT.remove(type.getURI());
+        final Class<?> jc = type.getJavaClass();
+        if (jc != null) {
+            classToDT.remove(jc);
+        }
+    }
 }
diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Functor.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Functor.java
index 1491e44..db5c733 100755
--- a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Functor.java
+++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Functor.java
@@ -18,17 +18,19 @@
 
 package org.apache.jena.reasoner.rulesys;
 
-import org.apache.jena.datatypes.* ;
-import org.apache.jena.graph.* ;
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.Node_ANY;
+import org.apache.jena.graph.Triple;
 import org.apache.jena.util.PrintUtil ;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.*;
-import java.util.function.Predicate;
-
 /**
- * A functor comprises a functor name and a list of 
+ * A functor comprises a functor name and a list of
  * arguments. The arguments are Nodes of any type except functor nodes
  * (there is no functor nesting).  Functors play three roles in rules -
  * in heads they represent actions (procedural attachment); in bodies they
@@ -39,24 +41,24 @@ import java.util.function.Predicate;
 public class Functor implements ClauseEntry {
     /** Functor's name */
     protected String name;
-    
+
     /** Argument list - an array of nodes */
     protected Node[] args;
-    
+
     /** A built in that implements the functor */
     protected Builtin implementor;
-    
+
     /** A static Predicate instance that detects triples with Functor objects */
     public static final Predicate<Triple> acceptFilter = t ->  {
                     if (t.getSubject().isLiteral()) return true;
                     Node n = t.getObject();
                     return n.isLiteral() && n.getLiteralDatatype() == FunctorDatatype.theFunctorDatatype;
             };
-    
+
     protected static Logger logger = LoggerFactory.getLogger(Functor.class);
 
     /**
-     * Constructor. 
+     * Constructor.
      * @param name the name of the functor
      * @param args an array of nodes defining the arguments, this will not be copied so beware of
      * accidental structure sharing
@@ -65,7 +67,7 @@ public class Functor implements ClauseEntry {
         this.name = name;
         this.args = args;
     }
-    
+
     /**
      * Constructor
      * @param name the name of the functor
@@ -113,28 +115,28 @@ public class Functor implements ClauseEntry {
         this.args = args;
         this.implementor = impl;
     }
-    
+
     /**
      * Return the functor name
      */
     public String getName() {
         return name;
     }
-    
+
     /**
      * Return the functor arguments as an array of nodes
      */
     public Node[] getArgs() {
         return args;
     }
-    
+
     /**
      * Return the length of the functor argument array.
      */
     public int getArgLength() {
         return args.length;
     }
-    
+
     /**
      * Returns true if the functor is fully ground, no variables
      */
@@ -148,7 +150,7 @@ public class Functor implements ClauseEntry {
         }
         return true;
     }
-    
+
     /**
      * Returns true if the functor is fully ground in the given environment
      */
@@ -162,7 +164,7 @@ public class Functor implements ClauseEntry {
         }
         return true;
     }
-    
+
     /**
      * Execute the given built in as a body clause.
      * @param context an execution context giving access to other relevant data
@@ -175,7 +177,7 @@ public class Functor implements ClauseEntry {
         }
         return implementor.bodyCall(getBoundArgs(context.getEnv()), args.length, context);
     }
-    
+
     /**
      * Execute the given built in as a body clause, only if it is side-effect-free.
      * @param context an execution context giving access to other relevant data
@@ -192,7 +194,7 @@ public class Functor implements ClauseEntry {
             return false;
         }
     }
-    
+
     /**
      * Return a new Node array containing the bound versions of this Functor's arguments
      */
@@ -203,7 +205,7 @@ public class Functor implements ClauseEntry {
         }
         return boundargs;
     }
-    
+
     /**
      * Return the Builtin that implements this functor
      * @return the Builtin or null if there isn't one
@@ -214,14 +216,14 @@ public class Functor implements ClauseEntry {
         }
         return implementor;
     }
-    
+
     /**
      * Set the Builtin that implements this functor.
      */
     public void setImplementor(Builtin implementor) {
         this.implementor = implementor;
     }
-    
+
     /**
      * Printable string describing the functor
      */
@@ -246,7 +248,7 @@ public class Functor implements ClauseEntry {
         if (n == null) return false;
         return n.isLiteral() && n.getLiteralDatatype() == FunctorDatatype.theFunctorDatatype;
     }
-    
+
     /**
      * Equality is based on structural comparison
      */
@@ -263,13 +265,13 @@ public class Functor implements ClauseEntry {
         }
         return false;
     }
-    
+
     /** hash function override */
     @Override
     public int hashCode() {
         return (name.hashCode()) ^ (args.length << 2);
     }
-    
+
     /**
      * Compare Functors, taking into account variable indices.
      * The equality function ignores differences between variables.
@@ -305,18 +307,4 @@ public class Functor implements ClauseEntry {
     public static Node makeFunctorNode(Functor f) {
         return NodeFactory.createLiteralByValue(f, FunctorDatatype.theFunctorDatatype);
     }
-    
-   /**
-    * Inner class. Dummy datatype definition for 
-    * functor-valued literals.
-    */
-   public static class FunctorDatatype extends BaseDatatype {
-    
-        public FunctorDatatype() {
-            super("urn:x-hp-jena:Functor");
-        }
-        
-        public static final RDFDatatype theFunctorDatatype = new FunctorDatatype();
-   }
-
 }
diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/FunctorDatatype.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/FunctorDatatype.java
new file mode 100644
index 0000000..56f7977
--- /dev/null
+++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/FunctorDatatype.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.reasoner.rulesys;
+
+import org.apache.jena.datatypes.BaseDatatype;
+import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.graph.impl.LiteralLabel;
+
+/**
+ * Datatype definition for functor-valued literals.
+ */
+public class FunctorDatatype extends BaseDatatype {
+
+    /** Used when print a functor as an RDF literal, and in parsing tests */
+    public static final RDFDatatype theFunctorDatatype = new FunctorDatatype();
+
+    private FunctorDatatype() {
+        super("urn:org.apache.jena:Functor");
+    }
+
+    /**
+     * Compares two instances of values of the given datatype.
+     */
+    @Override
+    public boolean isEqual(LiteralLabel value1, LiteralLabel value2) {
+        return isEqualByTerm(value1, value2) ;
+    }
+
+    @Override
+    public Object parse(String lexicalForm) { return lexicalForm ; }
+
+    @Override
+    public String unparse(Object value) { return value.toString(); }
+}
diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Rule.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Rule.java
index afa9dc6..df3f29d 100755
--- a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Rule.java
+++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Rule.java
@@ -712,7 +712,8 @@ public class Rule implements ClauseEntry {
 
         // Literal parse state flags
         private static final int NORMAL = 0;
-        private static final int STARTED_LITERAL = 1;
+        private static final int IN_LITERAL = 1;
+        private static final int END_LITERAL = 2;
 
         /** Literal parse state */
         private int literalState = NORMAL;
@@ -785,27 +786,38 @@ public class Rule implements ClauseEntry {
                 String temp = lookahead;
                 lookahead = null;
                 return temp;
-            } else {
-                String token = stream.nextToken();
-                if (literalState == NORMAL) {
-                    // Skip separators unless within a literal
+            }
+            if ( !stream.hasMoreTokens() ) {}
+            String token = stream.nextToken();
+
+            // Three states
+            // NORMAL
+            // IN_LITERAL: entered when a literal string delimite is found.
+            // END_LITERAL: expecting finish delimiter
+            switch( literalState ) {
+                case NORMAL:
+                    // Skip separators - not in a literal
                     while (isSeparator(token)) {
                         token = stream.nextToken();
                     }
-                }
-                if (token.equals("'")) {
-                    if (literalState == NORMAL) {
-                        literalState = STARTED_LITERAL;
-                    } else {
-                        literalState = NORMAL;
-                    }
-                }
-                priorTokens.add(0, token);
-                if (priorTokens.size() > maxPriors) {
-                    priorTokens.remove(priorTokens.size()-1);
-                }
-                return token;
+                    // Start delimiter
+                    if ( token.equals("'") || token.equals("\"") )
+                        literalState = IN_LITERAL;
+                    break;
+                case IN_LITERAL:
+                    // Between delimiters
+                    literalState = END_LITERAL;
+                    break;
+                case END_LITERAL:
+                    // Finish Delimiter.
+                    literalState = NORMAL;
+                    break;
+            }
+            priorTokens.add(0, token);
+            if (priorTokens.size() > maxPriors) {
+                priorTokens.remove(priorTokens.size()-1);
             }
+            return token;
         }
 
         /**
@@ -934,7 +946,7 @@ public class Rule implements ClauseEntry {
                     RDFDatatype dt = TypeMapper.getInstance().getSafeTypeByName(dtURI);
                     return NodeFactory.createLiteral(lit, dt);
                 } else {
-                    return NodeFactory.createLiteral(lit, "");
+                    return NodeFactory.createLiteral(lit);
                 }
             } else  if ( Character.isDigit(token.charAt(0)) ||
                          (token.charAt(0) == '-' && token.length() > 1 && Character.isDigit(token.charAt(1))) ) {
@@ -1018,7 +1030,7 @@ public class Rule implements ClauseEntry {
                 List<Node> args = parseNodeList();
                 Functor clause = new Functor(name, args, registry);
                 if (clause.getImplementor() == null) {
-                    // Not a.error error becase later processing can add this
+                    // Not an error because later processing can add this
                     // implementation to the registry
                     logger.warn("Rule references unimplemented functor: " + name);
                 }
@@ -1036,7 +1048,7 @@ public class Rule implements ClauseEntry {
 
         /**
          * Parse a rule, terminated by a "]" or "." character.
-         * @param retainVarMap set to true to ccause the existing varMap to be left in place, which
+         * @param retainVarMap set to true to cause the existing varMap to be left in place, which
          * is required for nested rules.
          */
         private Rule doParseRule(boolean retainVarMap) {
diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java
index 9620e33..f864eab 100644
--- a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java
+++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java
@@ -206,7 +206,7 @@ public class RETEClauseFilter implements RETESourceNode {
                 // for possible later functor argument accesses
                 n = triple.getObject();
                 if ( !n.isLiteral() ) return;
-                if ( n.getLiteralDatatype() != Functor.FunctorDatatype.theFunctorDatatype) return;
+                if ( n.getLiteralDatatype() != FunctorDatatype.theFunctorDatatype) return;
                 lastFunctor = (Functor)n.getLiteralValue();
                 if ( !lastFunctor.getName().equals(args[instructions[pc++]]) ) return;
                 break;
diff --git a/jena-core/src/main/java/org/apache/jena/util/PrintUtil.java b/jena-core/src/main/java/org/apache/jena/util/PrintUtil.java
index 5253b74..8d4f2cc 100755
--- a/jena-core/src/main/java/org/apache/jena/util/PrintUtil.java
+++ b/jena-core/src/main/java/org/apache/jena/util/PrintUtil.java
@@ -40,16 +40,16 @@ import org.apache.jena.vocabulary.ReasonerVocabulary ;
  * prefix map which is preloaded with known prefixes.
  */
 public class PrintUtil {
-    
+
     protected static PrefixMapping prefixMapping = PrefixMapping.Factory.create();
-    
+
     /** Default built in eg namespace used in testing */
     public static final String egNS = "urn:x-hp:eg/";
-        
+
     static {
         init();
     }
-    
+
     /**
      * Load built in prefixes.
      */
@@ -63,7 +63,7 @@ public class PrintUtil {
         registerPrefix("eg", egNS);
         registerPrefix("xsd", XSDDatatype.XSD + "#");
     }
-    
+
     /**
      * Register a new prefix/namespace mapping which will be used to shorten
      * the print strings for resources in known namespaces.
@@ -71,7 +71,7 @@ public class PrintUtil {
     public static void registerPrefix(String prefix, String namespace) {
         prefixMapping.setNsPrefix( prefix, namespace );
     }
-    
+
     /**
      * Register a set of new prefix/namespace mapping which will be used to shorten
      * the print strings for resources in known namespaces.
@@ -79,14 +79,14 @@ public class PrintUtil {
     public static void registerPrefixMap(Map<String, String> map) {
         prefixMapping.setNsPrefixes( map );
     }
-    
+
     /**
      * Remove a registered prefix from the table of known short forms
      */
     public static void removePrefix(String prefix) {
         prefixMapping.removeNsPrefix(prefix);
     }
-    
+
     /**
      * Remove a set of prefix mappings from the table of known short forms
      */
@@ -96,9 +96,9 @@ public class PrintUtil {
             prefixMapping.removeNsPrefix( s );
         }
     }
-    
+
     /**
-     * Return a simplified print string for a Node. 
+     * Return a simplified print string for a Node.
      */
     public static String print(Node node) {
         if (node instanceof Node_URI) {
@@ -111,8 +111,11 @@ public class PrintUtil {
             }
         } else if (node instanceof Node_Literal) {
             String lf = node.getLiteralLexicalForm();
-            // RDF 1.1 : Print xsd:string without ^^xsd:string 
-            return "'" + lf + "'" + (Util.isSimpleString(node) ? "" : "^^" + node.getLiteralDatatypeURI());
+            String singleQuote = "'";
+            if ( lf.contains(singleQuote) )
+                lf = lf.replace(singleQuote, "\\'");
+            // RDF 1.1 : Print xsd:string without ^^xsd:string
+            return singleQuote + lf + singleQuote + (Util.isSimpleString(node) ? "" : "^^" + node.getLiteralDatatypeURI());
         } else if (node instanceof Node_ANY) {
             return "*";
         }
@@ -121,15 +124,15 @@ public class PrintUtil {
         }
         return node.toString();
     }
-    
+
     /**
-     * Return a simplified print string for an RDFNode. 
+     * Return a simplified print string for an RDFNode.
      */
     public static String print(RDFNode node) {
         if (node == null) return "null";
         return print(node.asNode());
     }
-    
+
     /**
      * Return a simplified print string for a Triple
      */
@@ -139,7 +142,7 @@ public class PrintUtil {
                       print(triple.getPredicate()) + " " +
                       print(triple.getObject()) + ")";
     }
-    
+
     /**
      * Return a simplified print string for a TriplePattern
      */
@@ -149,7 +152,7 @@ public class PrintUtil {
                       print(triple.getPredicate()) + " " +
                       print(triple.getObject()) + ")";
     }
-    
+
     /**
      * Return a simplified print string for a statement
      */
@@ -157,7 +160,7 @@ public class PrintUtil {
         if (stmt == null) return "(null)";
         return print(stmt.asTriple());
     }
-    
+
     /**
      * Default print which just uses tostring
      */
@@ -177,7 +180,7 @@ public class PrintUtil {
             return obj.toString();
         }
     }
-    
+
     /**
      * Expand qnames to URIs. If the given URI appears
      * to start with one of the registered prefixes then
@@ -186,7 +189,7 @@ public class PrintUtil {
     public static String expandQname(String uri) {
         return prefixMapping.expandPrefix( uri );
     }
-    
+
     /**
      * Print an n-space indent to the given output stream
      */
@@ -195,7 +198,7 @@ public class PrintUtil {
         for (int i = 0; i < indent; i++) spaces.append(" ");
         out.print(spaces.toString());
     }
-    
+
     /**
      * Print all the Triple values from a find iterator.
      */
diff --git a/jena-core/src/main/java/org/apache/jena/util/Tokenizer.java b/jena-core/src/main/java/org/apache/jena/util/Tokenizer.java
index 0ad5414..61526bc 100644
--- a/jena-core/src/main/java/org/apache/jena/util/Tokenizer.java
+++ b/jena-core/src/main/java/org/apache/jena/util/Tokenizer.java
@@ -25,37 +25,37 @@ import java.util.NoSuchElementException;
  * character strings which can include other separators.
  */
 public class Tokenizer {
-    
+
     /** The string being parsed */
     protected String source;
-    
+
     /** The index of the first unreturned char in source */
     protected int p;
 
     /** The set of delimiter characters */
     protected String delim;
-    
+
     /** If true then delimiters should be returned as tokens */
     protected boolean returnDelims;
-    
+
     /** Literal string delimiters */
     protected String literalDelim;
-    
+
     /** The lex state */
     protected int state;
-    
+
     /** A lookahead for tokens */
     protected String lookahead;
-    
+
     /** State flag: normal parse */
     protected static final int NORMAL = 1;
-    
+
     /** State flag: start of literal */
     protected static final int LITERAL_START = 2;
-    
+
     /** State flag: end of literal */
     protected static final int LITERAL_END = 3;
-    
+
     /**
      * Constructor.
      * @param str the source string to be parsed
@@ -68,10 +68,10 @@ public class Tokenizer {
         this.delim = delim;
         this.literalDelim = literalDelim;
         this.returnDelims = returnDelims;
-        p = 0; 
+        p = 0;
         state = NORMAL;
     }
-    
+
     /**
      * Return the next token.
      * @throws java.util.NoSuchElementException if there are no more tokens available
@@ -86,7 +86,7 @@ public class Tokenizer {
         }
         if (result == null) {
             throw new NoSuchElementException("No more elements in tokenized string");
-        } 
+        }
         if (!returnDelims) {
             if (result.length() == 1) {
                 char c = result.charAt(0);
@@ -97,7 +97,7 @@ public class Tokenizer {
         }
         return result;
     }
-    
+
     /**
      * Test if there are more tokens which can be returned.
      */
@@ -105,7 +105,7 @@ public class Tokenizer {
         if (lookahead == null) lookahead = getNextToken();
         return lookahead != null;
     }
-    
+
     /**
      * Find the next token which can either be a delimiter or a real token.
      */
@@ -114,46 +114,44 @@ public class Tokenizer {
             return null;
         }
         switch(state) {
-        case NORMAL:
-            if (is(literalDelim)) {
-                state = LITERAL_START;
-                p++;
-                return source.substring(p-1, p);
-            } else if (is(delim)) {
-                p++;
-                return source.substring(p-1, p);
-            } else {
+            case NORMAL:
+                if (is(literalDelim)) {
+                    state = LITERAL_START;
+                    p++;
+                    return source.substring(p-1, p);
+                } else if (is(delim)) {
+                    p++;
+                    return source.substring(p-1, p);
+                }
                 int start = p;
                 p++;
                 while (p < source.length() && ! is(delim)) p++;
                 return source.substring(start, p);
-            }
-        case LITERAL_START:
-            char delim = source.charAt(p-1);
-            StringBuilder literal = new StringBuilder();
-            while (p < source.length()) {
-                char c = source.charAt(p);
-                if (c == '\\') {
+            case LITERAL_START:
+                char delim = source.charAt(p-1);
+                StringBuilder literal = new StringBuilder();
+                while (p < source.length()) {
+                    char c = source.charAt(p);
+                    if (c == '\\') {
+                        p++;
+                        if (p >= source.length()) break;
+                        c = source.charAt(p);
+                    } else {
+                        if (c == delim) break;
+                    }
+                    literal.append(c);
                     p++;
-                    if (p >= source.length()) break;
-                    c = source.charAt(p);
-                } else {
-                    if (c == delim) break;
                 }
-                literal.append(c);
+                state = LITERAL_END;
+                return literal.toString();
+            case LITERAL_END:
+                state = NORMAL;
                 p++;
-            }
-            state = LITERAL_END;
-            return literal.toString();
-        case LITERAL_END:
-            state = NORMAL;
-            p++;
-            return source.substring(p-1, p);
+                return source.substring(p-1, p);
         }
         return null;
     }
-    
-    
+
     /**
      * Returns true if the current character is contained in the given classification.
      */
diff --git a/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/test/TestBasics.java b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/test/TestBasics.java
index 6d9abd2..db4d7cd 100755
--- a/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/test/TestBasics.java
+++ b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/test/TestBasics.java
@@ -24,6 +24,8 @@ import junit.framework.TestSuite;
 import java.util.*;
 import java.io.*;
 
+import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.datatypes.TypeMapper;
 import org.apache.jena.graph.* ;
 import org.apache.jena.rdf.model.* ;
 import org.apache.jena.reasoner.* ;
@@ -37,10 +39,10 @@ import org.apache.jena.vocabulary.* ;
  * Unit tests for simple infrastructure pieces of the rule systems.
  */
 public class TestBasics extends TestCase  {
-    
+
     // Maximum size of binding environment needed in the tests
     private static final int MAX_VARS = 10;
-    
+
     // Useful constants
     Node p = NodeFactory.createURI("p");
     Node q = NodeFactory.createURI("q");
@@ -52,93 +54,191 @@ public class TestBasics extends TestCase  {
     Node n4 = NodeFactory.createURI("n4");
     Node n5 = NodeFactory.createURI("n5");
     Node res = NodeFactory.createURI("res");
-        
-     
+
+
     /**
      * Boilerplate for junit
-     */ 
+     */
     public TestBasics( String name ) {
-        super( name ); 
+        super( name );
     }
-    
+
     /**
      * Boilerplate for junit.
      * This is its own test suite
      */
     public static TestSuite suite() {
-        return new TestSuite( TestBasics.class ); 
-    }  
+        return new TestSuite( TestBasics.class );
+    }
 
     /**
      * Test the internal rule parser
      */
-    public void testRuleParser() {
-       String[] testRules = new String[] {
-            "(?a rdf:type ?_) -> (?a rdf:type ?b).",
-            "(?a rdf:type ?_), (?a rdf:type ?_) -> (?a rdf:type ?b).",
-            "(?a rdf:type max(?a,1)) -> (?a rdf:type 'foo').",
-            "(?a rdf:type ?_) -> addOne(?a).",
-            "(?a rdf:type ?_) -> [(?a rdf:type ?_) -> addOne(?a)].",
-           "(?a rdf:type ?_) -> (?a rdf:type '42').",
-           "(?a rdf:type ?_) -> (?a rdf:type 4.2).",
-           "(?a rdf:type ?_) -> (?a rdf:type ' fool that,I(am)').",
-            "[rule1: (?a rdf:type ?_) -> (?a rdf:type a)]",
-            "-> print(' ').",
-            "-> print(' literal with embedded \\' characters ').",
-            "-> print(\" literal characters \").",
-            "-> print(42). ",
-            "-> print('42'^^xsd:byte). ",
-            "-> print('42'^^http://www.w3.org/2001/XMLSchema#int). ",
-            "-> print('42'^^foobar:byte). ",
-            "-> print(<foo://a/file>). " ,
-            "-> print('(').",
-            "-> print(\"(\")."
-        };
-        String[] testResults = new String[] {
-            "[ (?a rdf:type ?_) -> (?a rdf:type ?b) ]",
-            "[ (?a rdf:type ?_) (?a rdf:type ?_) -> (?a rdf:type ?b) ]",
-            "[ (?a rdf:type 'max(?a '1'^^http://www.w3.org/2001/XMLSchema#int)'^^urn:x-hp-jena:Functor) -> (?a rdf:type 'foo') ]",
-            "[ (?a rdf:type ?_) -> addOne(?a) ]",
-            "[ (?a rdf:type ?_) -> [ (?a rdf:type ?_) -> addOne(?a) ] ]",
-            "[ (?a rdf:type ?_) -> (?a rdf:type '42') ]",
-            "[ (?a rdf:type ?_) -> (?a rdf:type '4.2'^^http://www.w3.org/2001/XMLSchema#float) ]",
-            "[ (?a rdf:type ?_) -> (?a rdf:type ' fool that,I(am)') ]",
-            "[ rule1: (?a rdf:type ?_) -> (?a rdf:type <a>) ]",
-            "[ -> print(' ') ]",
-            "[ -> print(' literal with embedded ' characters ') ]",
-            "[ -> print(' literal characters ') ]",
-            "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#int) ]",
-            "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#byte) ]",
-            "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#int) ]",
-            "[ -> print('42'^^http://foobar#byte) ]",
-            "[ -> print(<foo://a/file>) ]",
-            "[ -> print('(') ]",
-            "[ -> print('(') ]"
-        };
-        
+    public void testRuleParserBad01() {
+        execTestBad("(foo(?A) eg:p ?B) <- (?a, eg:p, ?B).");
+    }
+
+    public void testRuleParserBad02() {
+        execTestBad("(foo(?A) eg:p ?B) -> (?a, eg:p, ?B).");
+    }
+
+    private static void execTestBad(String ruleStr) {
+        boolean foundError = false;
+        try {
+            Rule r = Rule.parseRule(ruleStr);
+        } catch (Rule.ParserException e) {
+            foundError = true;
+        }
+        assertTrue("Failed to find illegal rule: " + ruleStr, foundError);
+    }
+
+    public void testParser01() {
+        execTest("(?a rdf:type ?_) -> (?a rdf:type ?b).", "[ (?a rdf:type ?_) -> (?a rdf:type ?b) ]");
+    }
+
+    public void testParser02() {
+        execTest("(?a rdf:type ?_), (?a rdf:type ?_) -> (?a rdf:type ?b).", "[ (?a rdf:type ?_) (?a rdf:type ?_) -> (?a rdf:type ?b) ]");
+    }
+
+    public void testParser03() {
+        // Register so that parsing the string form works.
+        RDFDatatype dt = FunctorDatatype.theFunctorDatatype;
+        TypeMapper.getInstance().registerDatatype(dt);
+        execTest("(?a rdf:type max(?a,1)) -> (?a rdf:type 'foo').",
+                 "[ (?a rdf:type 'max(?a \\'1\\'^^http://www.w3.org/2001/XMLSchema#int)'^^urn:org.apache.jena:Functor) -> (?a rdf:type 'foo') ]");
+        TypeMapper.getInstance().unregisterDatatype(dt);
+    }
+
+    public void testParser04() {
+        execTest("(?a rdf:type ?_) -> addOne(?a).", "[ (?a rdf:type ?_) -> addOne(?a) ]");
+    }
+
+    public void testParser05() {
+        execTest("(?a rdf:type ?_) -> [(?a rdf:type ?_) -> addOne(?a)].", "[ (?a rdf:type ?_) -> [ (?a rdf:type ?_) -> addOne(?a) ] ]");
+    }
+
+    public void testParser06() {
+        execTest("(?a rdf:type ?_) -> (?a rdf:type '42').", "[ (?a rdf:type ?_) -> (?a rdf:type '42') ]");
+    }
+
+    public void testParser07() {
+        execTest("(?a rdf:type ?_) -> (?a rdf:type 4.2).",
+                 "[ (?a rdf:type ?_) -> (?a rdf:type '4.2'^^http://www.w3.org/2001/XMLSchema#float) ]");
+    }
+
+    public void testParser08() {
+        execTest("(?a rdf:type ?_) -> (?a rdf:type ' fool that,I(am)').", "[ (?a rdf:type ?_) -> (?a rdf:type ' fool that,I(am)') ]");
+    }
+
+    public void testParser09() {
+        execTest("[rule1: (?a rdf:type ?_) -> (?a rdf:type a)]", "[ rule1: (?a rdf:type ?_) -> (?a rdf:type <a>) ]");
+    }
+
+    public void testParser10() {
+        execTest("-> print(' ').", "[ -> print(' ') ]");
+    }
+
+    public void testParser11() {
+        execTest("-> print(' literal with embedded \\' characters ').", "[ -> print(' literal with embedded \\' characters ') ]");
+    }
+
+    public void testParser12() {
+        execTest("-> print(\" literal characters \").", "[ -> print(' literal characters ') ]");
+    }
+
+    public void testParser13() {
+        execTest("-> print(42). ", "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#int) ]");
+    }
+
+    public void testParser14() {
+        execTest("-> print('42'^^xsd:byte). ", "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#byte) ]");
+    }
+
+    public void testParser15() {
+        execTest("-> print('42'^^http://www.w3.org/2001/XMLSchema#int). ", "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#int) ]");
+    }
+
+    public void testParser16() {
         PrintUtil.registerPrefix("foobar", "http://foobar#");
-        for (int i = 0; i < testRules.length; i++) {
-            Rule r = Rule.parseRule(testRules[i]);
-            assertEquals(testResults[i], r.toString());
+        try {
+            execTest("-> print('42'^^foobar:byte). ", "[ -> print('42'^^http://foobar#byte) ]");
+        } finally {
+            PrintUtil.removePrefix("foobar");
         }
-        
-        // Test for an illegal rule format
-        String[] testBadRules = new String[] {
-                "(foo(?A) eg:p ?B) <- (?a, eg:p, ?B)." ,
-                "(foo(?A) eg:p ?B) -> (?a, eg:p, ?B)." 
-        };
-        for ( String testBadRule : testBadRules )
-        {
-            boolean foundError = false;
-            try
-            {
-                Rule r = Rule.parseRule( testBadRule );
-            }
-            catch ( Rule.ParserException e )
-            {
-                foundError = true;
-            }
-            assertTrue( "Failed to find illegal rule", foundError );
+    }
+
+    public void testParser17() {
+        execTest("-> print(<foo://a/file>). ", "[ -> print(<foo://a/file>) ]");
+    }
+
+    public void testParser18() {
+        execTest("-> print(\"(\").", "[ -> print('(') ]");
+    }
+
+    public void testParser19() {
+        execTest("-> print(\",\").", "[ -> print(',') ]");
+    }
+
+    public void testParser20() {
+        execTest("-> print(',').", "[ -> print(',') ]");
+    }
+
+    public void testParser21() {
+        // Leading quote!
+        execTest("-> print(\"\\\"\").", "[ -> print('\"') ]");
+    }
+
+    public void testParser22() {
+        execTest("-> print('\"').", "[ -> print('\"') ]");
+    }
+
+    public void testParser23() {
+        execTest("-> print(\"'\").", "[ -> print('\\'') ]");
+    }
+
+    public void testParser24() {
+        execTest("-> print('\\'').", "[ -> print('\\'') ]");
+    }
+
+    public void testParser25() {
+        execTest("-> print('(').", "[ -> print('(') ]");
+    }
+
+    public void testParser26() {
+        execTest("-> print(')').", "[ -> print(')') ]");
+    }
+
+    public void testParser27() {
+        execTest("-> print(']').", "[ -> print(']') ]");
+    }
+
+    public void testParser28() {
+        execTest("-> print('[').", "[ -> print('[') ]");
+    }
+
+    public void testParser29() {
+        execTest("-> print(123).", "[ -> print('123'^^http://www.w3.org/2001/XMLSchema#int) ]");
+    }
+
+    public void testParser30() {
+        execTest("-> print(123, 'AB' 'CD').", "[ -> print('123'^^http://www.w3.org/2001/XMLSchema#int 'AB' 'CD') ]");
+    }
+
+    public void testParser31() {
+        execTest("-> print(123) print('AB') print('CD').", "[ -> print('123'^^http://www.w3.org/2001/XMLSchema#int) print('AB') print('CD') ]");
+    }
+
+    private static void execTest(String ruleStr, String expected) {
+        // Parse , check, parse the output, check.
+        Rule r = Rule.parseRule(ruleStr);
+        assertEquals(expected, r.toString());
+        try {
+            // And run on expected
+            Rule r2 = Rule.parseRule(expected);
+            assertEquals(r, r2);
+        } catch (Rule.ParserException ex) {
+            System.err.println(expected);
+            throw ex;
         }
     }
 
@@ -162,14 +262,14 @@ public class TestBasics extends TestCase  {
         assertTrue(! r3.equals(r5));
         assertTrue(! r3.equals(r6));
     }
-    
+
     /**
      * Test the BindingEnvironment machinery
      */
     public void testBindingEnvironment() {
         BindingStack env = new BindingStack();
         env.reset(MAX_VARS);
-        
+
         env.bind(3, n1);
         assertEquals(n1, env.getEnvironment()[3]);
         env.push();
@@ -201,8 +301,8 @@ public class TestBasics extends TestCase  {
             assertTrue("Failed to catch end of stack", false);
         } catch (IndexOutOfBoundsException e) {
         }
-    }  
-    
+    }
+
     /**
      * Test simple single clause binding
      */
@@ -227,21 +327,21 @@ public class TestBasics extends TestCase  {
         assertEquals(null, env.getEnvironment()[0]);
         assertEquals(null, env.getEnvironment()[1]);
         assertEquals(null, env.getEnvironment()[2]);
-        
+
         // Should succeed with two bindings
         match = FRuleEngine.match(p1, new Triple(n2, n1, n3), env);
         assertTrue(match);
         assertEquals(n2, env.getEnvironment()[0]);
         assertEquals(n3, env.getEnvironment()[1]);
         assertEquals(null, env.getEnvironment()[2]);
-        
+
         // should fail but leave prior bindings intact
         match = FRuleEngine.match(p2, new Triple(n1, n2, n2), env);
         assertTrue(!match);
         assertEquals(n2, env.getEnvironment()[0]);
         assertEquals(n3, env.getEnvironment()[1]);
         assertEquals(null, env.getEnvironment()[2]);
-        
+
         // should succeed with full binding set
         match = FRuleEngine.match(p2, new Triple(n3, n1, n2), env);
         assertTrue(match);
@@ -249,7 +349,7 @@ public class TestBasics extends TestCase  {
         assertEquals(n3, env.getEnvironment()[1]);
         assertEquals(n1, env.getEnvironment()[2]);
     }
-      
+
     /**
      * Minimal rule tester to check basic pattern match
      */
@@ -259,13 +359,13 @@ public class TestBasics extends TestCase  {
                        "[r3: (?a p ?a), (n1 p ?c), (n1, p, ?a) -> (?a, p, ?c)]" +
                        "[r4: (n4 ?p ?a) -> (n4, ?a, ?p)]";
         List<Rule> ruleList = Rule.parseRules(rules);
-        
+
         InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem());
         infgraph.add(new Triple(n1, p, n2));
         infgraph.add(new Triple(n2, p, n3));
         infgraph.add(new Triple(n2, q, n3));
         infgraph.add(new Triple(n4, p, n4));
-        
+
         TestUtil.assertIteratorValues(this, infgraph.find(null, null, null),
             new Triple[] {
                 new Triple(n1, p, n2),
@@ -277,7 +377,7 @@ public class TestBasics extends TestCase  {
                 new Triple(n4, n4, p),
             });
     }
-      
+
     /**
      * Test derivation machinery
      */
@@ -286,7 +386,7 @@ public class TestBasics extends TestCase  {
                        "[testRule2: (n1 q ?a) -> (n2, q, ?a)]" +
                        "[testRule3: (n2 p ?a), (n2 q ?a) -> (res p ?a)]";
         List<Rule> ruleList = Rule.parseRules(rules);
-        
+
         InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem());
         infgraph.setDerivationLogging(true);
         infgraph.add(new Triple(n1, p, n3));
@@ -303,7 +403,7 @@ public class TestBasics extends TestCase  {
                 new Triple(n2, q, n3),
                 new Triple(res, p, n3)
             });
-        
+
         Iterator<Derivation> derivs = infgraph.getDerivation(new Triple(res, p, n3));
         StringWriter outString = new StringWriter(250);
         PrintWriter out = new PrintWriter(outString);
@@ -312,7 +412,7 @@ public class TestBasics extends TestCase  {
             d.printTrace(out, true);
         }
         out.flush();
-        
+
         String testString = TestUtil.normalizeWhiteSpace("Rule testRule3 concluded (<res> <p> <n3>) <-\n" +
                 "    Rule testRule1 concluded (<n2> <p> <n3>) <-\n" +
                 "        Fact (<n1> <p> <n3>)\r\n" +
@@ -320,8 +420,8 @@ public class TestBasics extends TestCase  {
                 "        Fact (<n1> <q> <n3>)\r\n");
         assertEquals(testString, TestUtil.normalizeWhiteSpace(outString.getBuffer().toString()));
     }
-    
-          
+
+
     /**
      * Test axiom handling machinery
      */
@@ -331,14 +431,14 @@ public class TestBasics extends TestCase  {
                        "[testRule3: (n2 p ?a), (n2 q ?a) -> (res p ?a)]" +
                        "[axiom1: -> (n1 p n3)]";
         List<Rule> ruleList = Rule.parseRules(rules);
-        
+
         InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem());
         TestUtil.assertIteratorValues(this, infgraph.find(null, null, null),
             new Triple[] {
                 new Triple(n1, p, n3),
                 new Triple(n2, p, n3),
             });
-        
+
         infgraph.add(new Triple(n1, q, n4));
         infgraph.add(new Triple(n1, q, n3));
 
@@ -352,9 +452,9 @@ public class TestBasics extends TestCase  {
                 new Triple(n2, q, n3),
                 new Triple(res, p, n3)
             });
-        
+
     }
-     
+
     /**
      * Test schema partial binding machinery
      */
@@ -368,7 +468,7 @@ public class TestBasics extends TestCase  {
         Graph data = Factory.createGraphMem();
         data.add(new Triple(n1, q, n4));
         data.add(new Triple(n1, q, n3));
-        
+
         Reasoner reasoner =  new BasicForwardRuleReasoner(ruleList);
         Reasoner boundReasoner = reasoner.bindSchema(schema);
         InfGraph infgraph = boundReasoner.bind(data);
@@ -384,7 +484,7 @@ public class TestBasics extends TestCase  {
                 new Triple(res, p, n3)
             });
     }
-    
+
     /**
      * Test functor handling
      */
@@ -396,7 +496,7 @@ public class TestBasics extends TestCase  {
                        "[ (?x eg:prop functor(?v, ?*)) -> (?x eg:propfunc ?v) ]" +
                        "";
         List<Rule> ruleList = Rule.parseRules(rules);
-        
+
         Model data = ModelFactory.createDefaultModel();
         Resource R1 = data.createResource(PrintUtil.egNS + "R1");
         Resource D = data.createResource(PrintUtil.egNS + "D");
@@ -406,13 +506,13 @@ public class TestBasics extends TestCase  {
         Property propfunc = data.createProperty(PrintUtil.egNS, "propfunc");
         Property rbr = data.createProperty(ReasonerVocabulary.RBNamespace, "restriction");
         R1.addProperty(OWL.onProperty, p).addProperty(OWL.allValuesFrom, D);
-        
+
         Reasoner reasoner =  new BasicForwardRuleReasoner(ruleList);
         InfGraph infgraph = reasoner.bind(data.getGraph());
         Model infModel = ModelFactory.createModelForGraph(infgraph);
         Resource foo = infModel.createResource(PrintUtil.egNS + "foo");
         Resource bar = infModel.createResource(PrintUtil.egNS + "bar");
-        
+
         RDFNode flit = infModel.getResource(R1.getURI()).getRequiredProperty(rbr).getObject();
         assertNotNull(flit);
         assertEquals(flit.toString(), "allOK");
@@ -421,11 +521,11 @@ public class TestBasics extends TestCase  {
 //        assertEquals("all", func.getName());
 //        assertEquals(p.getNode(), func.getArgs()[0]);
 //        assertEquals(D.getNode(), func.getArgs()[1]);
-        
+
         Literal one = (Literal)foo.getRequiredProperty(propbar).getObject();
         assertEquals(Integer.valueOf(1), one.getValue());
     }
-    
+
     /**
      * The the minimal machinery for supporting builtins
      */
@@ -437,7 +537,7 @@ public class TestBasics extends TestCase  {
                        "[axiom2: -> (n1 p 4)]" +
                        "";
         List<Rule> ruleList = Rule.parseRules(rules);
-        
+
         InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem());
         TestUtil.assertIteratorValues(this, infgraph.find(n1, q, null),
             new Triple[] {
@@ -448,14 +548,14 @@ public class TestBasics extends TestCase  {
             new Triple[] {
                 new Triple(n2, q, Util.makeIntNode(1))
             });
-        
+
     }
-    
+
     /**
      * The the "remove" builtin
      */
     public void testRemoveBuiltin() {
-        String rules =  
+        String rules =
                        "[rule1: (?x p ?y), (?x q ?y) -> remove(0)]" +
                        "";
         List<Rule> ruleList = Rule.parseRules(rules);
@@ -464,20 +564,20 @@ public class TestBasics extends TestCase  {
         infgraph.add(new Triple(n1, p, Util.makeIntNode(1)));
         infgraph.add(new Triple(n1, p, Util.makeIntNode(2)));
         infgraph.add(new Triple(n1, q, Util.makeIntNode(2)));
-        
+
         TestUtil.assertIteratorValues(this, infgraph.find(n1, null, null),
             new Triple[] {
                 new Triple(n1, p, Util.makeIntNode(1)),
                 new Triple(n1, q, Util.makeIntNode(2))
             });
-        
+
     }
-    
+
     /**
      * The the "drop" builtin
      */
     public void testDropBuiltin() {
-        String rules =  
+        String rules =
                        "[rule1: (?x p ?y) -> drop(0)]" +
                        "";
         List<Rule> ruleList = Rule.parseRules(rules);
@@ -486,14 +586,14 @@ public class TestBasics extends TestCase  {
         infgraph.add(new Triple(n1, p, Util.makeIntNode(1)));
         infgraph.add(new Triple(n1, p, Util.makeIntNode(2)));
         infgraph.add(new Triple(n1, q, Util.makeIntNode(2)));
-        
+
         TestUtil.assertIteratorValues(this, infgraph.find(n1, null, null),
             new Triple[] {
                 new Triple(n1, q, Util.makeIntNode(2))
             });
-        
+
     }
-    
+
     /**
      * Test the rebind operation.
      */
@@ -517,7 +617,7 @@ public class TestBasics extends TestCase  {
                 new Triple(n1, q, n3)
             });
     }
-     
+
     /**
      * Test size bug, used to blow up if size was called before any queries.
      */
@@ -529,7 +629,7 @@ public class TestBasics extends TestCase  {
         InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(data);
         assertEquals(infgraph.size(), 2);
     }
-    
+
     /**
      * Check validity report implementation, there had been a stupid bug here.
      */
@@ -540,7 +640,7 @@ public class TestBasics extends TestCase  {
         assertTrue(report.isValid());
         report.add(true,  "dummy", "dummy2");
         assertTrue( ! report.isValid());
-        
+
         report = new StandardValidityReport();
         report.add(false, "dummy", "dummy1");
         report.add(true,  "dummy", "dummy2");
@@ -553,7 +653,7 @@ public class TestBasics extends TestCase  {
         report.add(new ValidityReport.Report(false, "dummy", "dummy3"));
         assertTrue( ! report.isValid());
     }
-       
+
     /**
      * Test the list conversion utility that is used in some of the builtins.
      */
@@ -572,11 +672,11 @@ public class TestBasics extends TestCase  {
         data.add(new Triple(n4, rest, n5));
         data.add(new Triple(n5, first, q));
         data.add(new Triple(n5, rest, nil));
-        
+
         String rules = "[rule1: (?x p ?y) -> (?x q ?y)]";
         List<Rule> ruleList = Rule.parseRules(rules);
         InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(data);
-        
+
         RuleContext context = new BFRuleContext( (ForwardRuleInfGraphI) infgraph);
         List<Node> result = Util.convertList(n1, context);
         assertEquals(result.size(), 2);
@@ -587,5 +687,5 @@ public class TestBasics extends TestCase  {
         assertEquals(result2.size(), 1);
         assertEquals(result2.get(0), p);
     }
-     
+
 }