/*
 * 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.graph.test;


import junit.framework.TestSuite;
import org.apache.jena.atlas.lib.Creator;
import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.TypeMapper;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.graph.BlankNodeId;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.GraphMemFactory;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.NodeVisitor;
import org.apache.jena.graph.Node_ANY;
import org.apache.jena.graph.Node_Blank;
import org.apache.jena.graph.Node_Graph;
import org.apache.jena.graph.Node_Literal;
import org.apache.jena.graph.Node_Triple;
import org.apache.jena.graph.Node_URI;
import org.apache.jena.graph.Node_Variable;
import org.apache.jena.graph.Triple;
import org.apache.jena.graph.impl.LiteralLabel;
import org.apache.jena.graph.impl.LiteralLabelFactory;
import org.apache.jena.shared.JenaException;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.util.SplitIRI;
import org.apache.jena.vocabulary.DC;
import org.apache.jena.vocabulary.OWL;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RSS;
import org.apache.jena.vocabulary.VCARD;

/**
    Exercise nodes. Make sure that the different node types do not overlap
    and that the test predicates work properly on the different node kinds.
 */
public class TestNode extends GraphTestBase
{
    public TestNode( String name )
    { super( name ); }

    public static TestSuite suite()
    { return new TestSuite( TestNode.class ); }

    private static final String U = "http://some.domain.name/magic/spells.incant";
    private static final String N = "Alice";
    private static final LiteralLabel L = LiteralLabelFactory.createLang( "ashes are burning", "en" );
    private static final String A = BlankNodeId.createFreshId();

    public void testBlanks()
    {
        assertTrue( "anonymous nodes are blank", NodeFactory.createBlankNode().isBlank() );
        assertFalse( "anonymous nodes aren't literal", NodeFactory.createBlankNode().isLiteral() );
        assertFalse( "anonymous nodes aren't URIs", NodeFactory.createBlankNode().isURI() );
        assertFalse( "anonymous nodes aren't variables", NodeFactory.createBlankNode().isVariable() );
        assertEquals( "anonymous nodes have the right id", NodeFactory.createBlankNode(A).getBlankNodeLabel(), A);
    }

    @SuppressWarnings("deprecation")
    public void testLiterals()
    {
        assertFalse( "literal nodes aren't blank", NodeFactory.createLiteral( L ).isBlank() );
        assertTrue( "literal nodes are literal", NodeFactory.createLiteral( L ).isLiteral() );
        assertFalse( "literal nodes aren't variables", NodeFactory.createLiteral( L ).isVariable() );
        assertFalse( "literal nodes aren't URIs", NodeFactory.createLiteral( L ).isURI() );
        assertEquals( "literal nodes preserve value", NodeFactory.createLiteral( L ).getLiteral(), L );
    }

    public void testURIs()
    {
        assertFalse( "URI nodes aren't blank", NodeFactory.createURI( U ).isBlank() );
        assertFalse( "URI nodes aren't literal", NodeFactory.createURI( U ).isLiteral() );
        assertFalse( "URI nodes aren't variables", NodeFactory.createURI( U ).isVariable() );
        assertTrue( "URI nodes are URIs", NodeFactory.createURI( U ).isURI() );
        assertEquals( "URI nodes preserve URI", NodeFactory.createURI( U ).getURI(), U );
    }

    public void testVariables()
    {
        assertFalse( "variable nodes aren't blank", NodeFactory.createVariable( N ).isBlank() );
        assertFalse( "variable nodes aren't literal", NodeFactory.createVariable( N ).isLiteral() );
        assertFalse( "variable nodes aren't URIs", NodeFactory.createVariable( N ).isURI() );
        assertTrue( "variable nodes are variable", NodeFactory.createVariable( N ).isVariable() );
        assertEquals( "variable nodes keep their name", N, NodeFactory.createVariable( N ).getName() );
        assertEquals( "variable nodes keep their name", N + "x", NodeFactory.createVariable( N + "x" ).getName() );
    }

    public void testANY()
    {
        assertFalse( "ANY nodes aren't blank", Node.ANY.isBlank() );
        assertFalse( "ANY nodes aren't literals", Node.ANY.isLiteral() );
        assertFalse( "ANY nodes aren't URIs", Node.ANY.isURI() );
        assertFalse( "ANY nodes aren't variables", Node.ANY.isVariable() );
        assertFalse( "ANY nodes aren't blank", Node.ANY.isBlank() );
        assertFalse( "ANY nodes aren't blank", Node.ANY.isBlank() );
    }

    public void testNodeVariableConstructor()
    {
        assertEquals( NodeFactory.createVariable( "hello" ), new Node_Variable( "hello" ) );
        assertEquals( NodeFactory.createVariable( "world" ), new Node_Variable( "world" ) );
        assertDiffer( NodeFactory.createVariable( "hello" ), new Node_Variable( "world" ) );
        assertEquals( "myName", new Node_Variable( "myName" ).getName() );
    }

    /**
        test cases for equality: an array of (Node, String) pairs. [It's not worth
        making a special class for these pairs.] The nodes are created with caching
        off, to make sure that caching effects don't hide the effect of using .equals().
        The strings are "equality groups": the nodes should test equal iff their
        associated strings test equal.
     */
    @SuppressWarnings("deprecation")
    private Object [][] eqTestCases()
    {
        String id = BlankNodeId.createFreshId();
        LiteralLabel L2 = LiteralLabelFactory.createString(id.toString());

        LiteralLabel LLang1 = LiteralLabelFactory.createLang( "xyz", "en") ;
        LiteralLabel LLang2 = LiteralLabelFactory.createLang( "xyz", "EN") ;

        String U2 = id.toString();
        String N2 = id.toString();
        return new Object [][]
            {
            { Node.ANY, "0" },
            { NodeFactory.createBlankNode( id ), "1" },
            { NodeFactory.createBlankNode(), "2" },
            { NodeFactory.createBlankNode( id ), "1" },
            { NodeFactory.createLiteral( L ), "3" },

            { NodeFactory.createLiteral( L2 ), "4" },
            { NodeFactory.createLiteral( L ), "3" },
            { NodeFactory.createURI( U ), "5" },
            { NodeFactory.createURI( U2 ), "6" },
            { NodeFactory.createURI( U ), "5" },
            { NodeFactory.createVariable( N ), "7" },
            { NodeFactory.createVariable( N2 ), "8" },
            { NodeFactory.createVariable( N ), "7" } ,

            { NodeFactory.createLiteral( LLang1 ), "9" },
            { NodeFactory.createLiteral( LLang2 ), "10" },
            };
    }

    public void testNodeEquals()
    {
        Object [][] tests = eqTestCases();
        for ( Object[] I : tests )
        {
            assertFalse( I[0] + " should not equal null", I[0].equals( null ) );
            assertFalse( I[0] + "should not equal 'String'", I[0].equals( "String" ) );
            for ( Object[] J : tests )
            {
                testEquality( I[1].equals( J[1] ), I[0], J[0] );
            }
        }
    }

    private void testEquality( boolean testEq, Object x, Object y )
    {
        String testName = getType( x ) + " " + x + " and " + getType( y ) + " " + y;
        if (testEq)
            assertEquals( testName + "should be equal", x, y );
        else
            assertDiffer( testName + " should differ", x, y );
    }

    private String getType( Object x )
    {
        String fullName = x.getClass().getName();
        return fullName.substring( fullName.lastIndexOf( '.' ) + 1 );
    }

    @SuppressWarnings("deprecation")
    public void testEquals()
    {
        assertDiffer( "different variables", NodeFactory.createVariable( "xx" ), NodeFactory.createVariable( "yy" ) );
        assertEquals( "same vars", NodeFactory.createVariable( "aa" ), NodeFactory.createVariable( "aa" ) );
        assertEquals( "same URI", NodeFactory.createURI( U ), NodeFactory.createURI( U ) );
        assertEquals( "same anon", NodeFactory.createBlankNode( A ), NodeFactory.createBlankNode( A ) );
        assertEquals( "same literal", NodeFactory.createLiteral( L ), NodeFactory.createLiteral( L ) );
        assertFalse( "distinct URIs", NodeFactory.createURI( U ) == NodeFactory.createURI( U ) );
        assertFalse( "distinct hyphens", NodeFactory.createBlankNode( A ) == NodeFactory.createBlankNode( A ) );
        assertFalse( "distinct literals", NodeFactory.createLiteral( L ) == NodeFactory.createLiteral( L ) );
        assertFalse( "distinct vars", NodeFactory.createVariable( "aa" ) == NodeFactory.createVariable( "aa" ) );
    }

    /**
        test that the label of a Node can be retrieved from that Node in
        a way appropriate to that Node.
     */
    @SuppressWarnings("deprecation")
    public void testLabels()
    {
        String id = BlankNodeId.createFreshId();
        assertEquals( "get URI value", U, NodeFactory.createURI( U ).getURI() );
        assertEquals( "get blank value", id, NodeFactory.createBlankNode( id ).getBlankNodeLabel() );
        assertEquals( "get literal value", L, NodeFactory.createLiteral( L ).getLiteral() );
        assertEquals( "get variable name", N, NodeFactory.createVariable( N ).getName() );
    }

    /**
        this is where we test that using the wrong accessor on a Node gets you
        an exception.
     */
    @SuppressWarnings("deprecation")
    public void testFailingLabels()
    {
        Node u = NodeFactory.createURI( U ), b = NodeFactory.createBlankNode();
        Node l = NodeFactory.createLiteral( L ), v = NodeFactory.createVariable( N );
        Node a = Node.ANY;
        /* */
        testGetURIFails( a );
        testGetURIFails( b );
        testGetURIFails( l );
        testGetURIFails( v );
        /* */
        testGetLiteralFails( a );
        testGetLiteralFails( u );
        testGetLiteralFails( b );
        testGetLiteralFails( v );
        /* */
        testGetNameFails( a );
        testGetNameFails( u );
        testGetNameFails( b );
        testGetNameFails( l );
        /* */
        testGetBlankNodeIdFails( a );
        testGetBlankNodeIdFails( u );
        testGetBlankNodeIdFails( l );
        testGetBlankNodeIdFails( v );
    }

    public void testGetBlankNodeIdFails( Node n )
    { try { n.getBlankNodeLabel(); fail( n.getClass() + " should fail getName()" ); } catch (UnsupportedOperationException e) {} }

    public void testGetURIFails( Node n )
    { try { n.getURI(); fail( n.getClass() + " should fail getURI()" ); } catch (UnsupportedOperationException e) {} }

    public void testGetNameFails( Node n )
    { try { n.getName(); fail( n.getClass() + " should fail getName()" ); } catch (UnsupportedOperationException e) {} }

    public void testGetLiteralFails( Node n )
    { try { n.getLiteral(); fail( n.getClass() + " should fail getLiteral()" ); } catch (UnsupportedOperationException e) {} }


    public void testGetBlankNodeLabelString()
    {
        Node n = NodeFactory.createBlankNode();
        assertNotNull(n.getBlankNodeLabel());
    }

    public void testVariableSupport()
    {
        assertEquals( new Node_Variable( "xxx" ), new Node_Variable( "xxx" ) );
        assertDiffer( new Node_Variable( "xxx" ), new Node_Variable( "yyy" ) );
    }

    /**
        Test that the create method does sensible things on null and ""
     */
    public void testCreateBadString()
    {
        try { NodeCreateUtils.create( null ); fail( "must catch null argument" ); }
        catch (NullPointerException e) {}
        catch (JenaException e) {}
        try { NodeCreateUtils.create( "" ); fail("must catch empty argument" ); }
        catch (JenaException e) {}
    }

    /**
        Test that anonymous nodes are created with the correct labels
     */
    public void testCreateBlankNode()
    {
        String idA = "_xxx";
        String idB = "_yyy";
        Node a = NodeCreateUtils.create( idA );
        Node b = NodeCreateUtils.create( idB );
        assertTrue( "both must be bnodes", a.isBlank() && b.isBlank() );
        assertEquals( NodeFactory.createBlankNode( idA ).getBlankNodeLabel(), a.getBlankNodeLabel() );
        assertEquals( NodeFactory.createBlankNode( idB ).getBlankNodeLabel(), b.getBlankNodeLabel() );
    }

    public void testCreateVariable()
    {
        String V = "wobbly";
        Node v = NodeCreateUtils.create( "?" + V );
        assertTrue( "must be a variable", v.isVariable() );
        assertEquals( "name must be correct", V, v.getName() );
    }

    public void testCreateANY()
    {
        assertEquals( "?? must denote ANY", Node.ANY, NodeCreateUtils.create( "??" ) );
    }

    public void testCreatePlainLiteralSingleQuotes()
    {
        Node n = NodeCreateUtils.create( "'xxx'" );
        assertEquals( "xxx", n.getLiteralLexicalForm() );
        assertString(n);
    }

    public void testCreatePlainLiteralDoubleQuotes()
    {
        Node n = NodeCreateUtils.create( "\"xxx\"" );
        assertEquals( "xxx", n.getLiteralLexicalForm() );
        assertString(n);
    }

    public void testCreateLiteralBackslashEscape()
    {
        testStringConversion( "xx\\x", "'xx\\\\x'" );
        testStringConversion( "xx\\x\\y", "'xx\\\\x\\\\y'" );
        testStringConversion( "\\xyz\\", "'\\\\xyz\\\\'" );
    }

    public void testCreateLiteralQuoteEscapes()
    {
        testStringConversion( "x\'y", "'x\\'y'" );
        testStringConversion( "x\"y", "'x\\\"y'" );
        testStringConversion( "x\'y\"z", "'x\\\'y\\\"z'" );
    }

    public void testCreateLiteralOtherEscapes()
    {
        testStringConversion( " ", "'\\s'" );
        testStringConversion( "\t", "'\\t'" );
        testStringConversion( "\n", "'\\n'" );
    }

    protected void testStringConversion(String wanted, String template) {
        Node n = NodeCreateUtils.create(template) ;
        assertEquals(wanted, n.getLiteralLexicalForm()) ;
        assertEquals("", n.getLiteralLanguage()) ;
        assertString(n) ;
    }

    public void testCreateLanguagedLiteralEN1()
    {
        Node n = NodeCreateUtils.create( "'chat'en-UK" );
        assertEquals( "chat", n.getLiteralLexicalForm() );
        assertLangString(n);
        assertEquals( "en-UK", n.getLiteralLanguage() );
    }

    public void testCreateLanguagedLiteralEN2()
    {
        Node n1 = NodeCreateUtils.create( "'chat'en-UK" );
        Node n2 = NodeCreateUtils.create( "'chat'EN-UK" );
        assertTrue( n1.sameValueAs(n2) ) ;
        // Jena5: normalized language tags.
        assertTrue( n1.equals(n2) ) ;
    }

    public void testCreateLanguagedLiteralXY()
    {
        Node n = NodeCreateUtils.create( "\"chat\"xy-AB" );
        assertEquals( "chat", n.getLiteralLexicalForm() );
        assertEquals( "xy-AB", n.getLiteralLanguage() );
        assertLangString(n) ;
    }

    public void testCreateTypedLiteralInteger()
    {
        Node n = NodeCreateUtils.create( "'42'xsd:integer" );
        assertEquals( "42", n.getLiteralLexicalForm() );
        assertEquals( "", n.getLiteralLanguage() );
        assertEquals( expand( "xsd:integer" ), n.getLiteralDatatypeURI() );
    }

    public void testCreateTypedLiteralBoolean()
    {
        Node n = NodeCreateUtils.create( "\"true\"xsd:boolean" );
        assertEquals( "true", n.getLiteralLexicalForm() );
        assertEquals( "", n.getLiteralLanguage() );
        assertEquals( expand( "xsd:boolean" ), n.getLiteralDatatypeURI() );
    }

    public void testGetPlainLiteralLexicalForm()
    {
        Node n = NodeCreateUtils.create( "'stuff'" );
        assertEquals( "stuff", n.getLiteralLexicalForm() );
    }

    public void testGetNumericLiteralLexicalForm()
    {
        Node n = NodeCreateUtils.create( "17" );
        assertEquals( "17", n.getLiteralLexicalForm() );
    }

    public void testTypesExpandPrefix()
    {
        testTypeExpandsPrefix( "rdf:spoo" );
        testTypeExpandsPrefix( "rdfs:bar" );
        testTypeExpandsPrefix( "owl:henry" );
        testTypeExpandsPrefix( "xsd:bool" );
        testTypeExpandsPrefix( "unknown:spoo" );
    }

    private void testTypeExpandsPrefix( String type )
    {
        Node n = NodeCreateUtils.create( "'stuff'" + type );
        String wanted = PrefixMapping.Extended.expandPrefix( type );
        assertEquals( wanted, n.getLiteralDatatypeURI() );
    }

    public void testCreateURI()
    {
        String uri = "http://www.electric-hedgehog.net/";
        testCreateURI( uri );
        testCreateURI( "rdf:trinket", "http://www.w3.org/1999/02/22-rdf-syntax-ns#trinket" );
        testCreateURI( "rdfs:device", "http://www.w3.org/2000/01/rdf-schema#device" );
        testCreateURI( "dc:creator", DC.getURI() + "creator" );
        testCreateURI( "rss:something", RSS.getURI() + "something" );
        testCreateURI( "vcard:TITLE", VCARD.getURI() + "TITLE" );
        testCreateURI( "owl:wol", OWL.getURI() + "wol" );
    }

    public void testCreateURIOtherMap()
    {
        String myNS = "eh:foo/bar#", suffix = "something";
        PrefixMapping mine = PrefixMapping.Factory.create().setNsPrefix( "mine", myNS );
        Node n = NodeCreateUtils.create( mine, "mine:" + suffix );
        assertEquals( myNS + suffix, n.getURI() );
    }

    private void testCreateURI( String inOut )
    { testCreateURI( inOut, inOut ); }

    private void testCreateURI( String in, String wanted )
    {
        String got = NodeCreateUtils.create( in ).getURI();
        if (!wanted.equals( got ))
        {
            if (in.equals( wanted )) fail( "should preserve " + in );
            else fail( "should translate " + in + " to " + wanted + " not " + got );
        }
    }

    public void testCreatePrefixed()
    {
        PrefixMapping pm = PrefixMapping.Factory.create();
        NodeCreateUtils.create( pm, "xyz" );
    }

    public void testToStringWithPrefixMapping()
    {
        PrefixMapping pm = PrefixMapping.Factory.create();
        String prefix = "spoo", ns = "abc:def/ghi#";
        pm.setNsPrefix( prefix, ns );
        String suffix = "bamboozle";
        assertEquals( prefix + ":" + suffix, NodeCreateUtils.create( ns + suffix ).toString( pm ) );
    }

    public void testNodeHelp()
    {
        assertTrue( "node() making URIs", node( "hello" ).isURI() );
        assertTrue( "node() making literals", node( "123" ).isLiteral() );
        assertTrue( "node() making literals", node( "'hello'" ).isLiteral() );
        assertTrue( "node() making hyphens", node( "_x" ).isBlank() );
        assertTrue( "node() making variables", node( "?x" ).isVariable() );
    }

    public void testVisitorPatternNode()
    {
        NodeVisitor returnNode = new NodeVisitor()
        {
            @Override
            public Object visitAny( Node_ANY it ) { return it; }
            @Override
            public Object visitBlank( Node_Blank it, String id ) { return it; }
            @Override
            public Object visitLiteral( Node_Literal it, String lex, String lang, RDFDatatype dtype) { return it; }
            @Override
            public Object visitURI( Node_URI it, String uri ) { return it; }
            @Override
            public Object visitVariable( Node_Variable it, String name ) { return it; }
            @Override
            public Object visitTriple(Node_Triple it, Triple triple) { return it; }
            @Override
            public Object visitGraph(Node_Graph it, Graph graph) { return it; }
        };
        testVisitorPatternNode( "sortOfURI", returnNode );
        testVisitorPatternNode( "?variable", returnNode );
        testVisitorPatternNode( "_anon", returnNode );
        testVisitorPatternNode( "11", returnNode );
        testVisitorPatternNode( "??", returnNode );
    }

    private void testVisitorPatternNode( String ns, NodeVisitor v )
    {
        Node n = node( ns );
        assertEquals( n, n.visitWith( v ) );
    }

    private void visitExamples( NodeVisitor nv )
    {
        node( "sortOfURI" ).visitWith( nv );
        node( "?variableI" ).visitWith( nv );
        node( "_anon" ).visitWith( nv );
        node( "11" ).visitWith( nv );
        node( "??" ).visitWith( nv );
        // ---
        Node s = node( "uri1" );
        Node p = node( "uri2" );
        Node o = node( "uri1" );
        Node nt = NodeFactory.createTripleNode(s, p, o);
        nt.visitWith(nv);
        // ---
        Graph g = GraphMemFactory.empty();
        Node_Graph ng = new Node_Graph(g);
        ng.visitWith(nv);
    }

    public void testVisitorPatternValue()
    {
        NodeVisitor checkValue = new NodeVisitor()
        {
            @Override
            public Object visitAny( Node_ANY it )
            { return null; }
            @Override
            public Object visitBlank( Node_Blank it, String label )
            { assertTrue( it.getBlankNodeLabel() == label ); return null; }
            @Override
            public Object visitLiteral( Node_Literal it, String lex, String lang, RDFDatatype dtype) {
                assertEquals(lex, it.getLiteralLexicalForm());
                assertEquals(lang, it.getLiteralLanguage());
                assertEquals(dtype, it.getLiteralDatatype());
                return null;
            }

            @Override
            public Object visitURI( Node_URI it, String uri )
            { assertTrue( it.getURI() == uri ); return null; }
            @Override
            public Object visitVariable( Node_Variable it, String name )
            { assertEquals( it.getName(), name ); return null; }
            @Override
            public Object visitTriple(Node_Triple it, Triple triple)
            { assertEquals( it.getTriple(), triple ); return null; }
            @Override
            public Object visitGraph(Node_Graph it, Graph graph)
            { assertEquals( it.getGraph(), graph ); return null; }
        };
        visitExamples( checkValue );
    }

    /**
        Test that the appropriate elements of the visitor are called exactly once;
        this relies on the order of the visits in visitExamples.
     */
    public void testVisitorPatternCalled()
    {
        final String [] strings = new String [] { "" };
        NodeVisitor checkCalled = new NodeVisitor()
        {
            @Override
            public Object visitAny( Node_ANY it )
            { strings[0] += " any"; return null; }
            @Override
            public Object visitBlank( Node_Blank it, String id )
            { strings[0] += " blank"; return null; }
            @Override
            public Object visitLiteral( Node_Literal it, String lex, String lang, RDFDatatype dtype)
            { strings[0] += " literal"; return null; }
            @Override
            public Object visitURI( Node_URI it, String uri )
            { strings[0] += " uri"; return null; }
            @Override
            public Object visitVariable( Node_Variable it, String name )
            { strings[0] += " variable"; return null; }
            @Override
            public Object visitTriple(Node_Triple it, Triple triple)
            { strings[0] += " termTriple"; return null; }
            @Override
            public Object visitGraph(Node_Graph it, Graph graph)
            { strings[0] += " termGraph"; return null; }

        };
        String desired = " uri variable blank literal any termTriple termGraph";
        visitExamples( checkCalled );
        assertEquals( "all visits must have been made", desired, strings[0] );
    }

    public void testSimpleMatches()
    {
        assertTrue( NodeCreateUtils.create( "S").matches( NodeCreateUtils.create( "S" ) ) );
        assertFalse(  "", NodeCreateUtils.create( "S").matches( NodeCreateUtils.create( "T" ) ) );
        assertFalse( "", NodeCreateUtils.create( "S" ).matches( null ) );
        assertTrue( NodeCreateUtils.create( "_X").matches( NodeCreateUtils.create( "_X" ) ) );
        assertFalse( "", NodeCreateUtils.create( "_X").matches( NodeCreateUtils.create( "_Y" ) ) );
        assertFalse( "", NodeCreateUtils.create( "_X").matches( null ) );
        assertTrue( NodeCreateUtils.create( "10" ).matches( NodeCreateUtils.create( "10" ) ) );
        assertFalse( "", NodeCreateUtils.create( "10" ).matches( NodeCreateUtils.create( "11" ) ) );
        assertFalse( "", NodeCreateUtils.create( "10" ).matches( null ) );
        assertTrue( Node.ANY.matches( NodeCreateUtils.create( "S" ) ) );
        assertTrue( Node.ANY.matches( NodeCreateUtils.create( "_X" ) ) );
        assertTrue( Node.ANY.matches( NodeCreateUtils.create( "10" ) ) );
        assertFalse( "", Node.ANY.matches( null ) );
    }

    public void testDataMatches()
    {
        TypeMapper tm = TypeMapper.getInstance();
        RDFDatatype dt1 = tm.getTypeByValue( Integer.valueOf( 10 ) );
        RDFDatatype dt2 = tm.getTypeByValue( Short.valueOf( (short) 10 ) );
        Node a = NodeFactory.createLiteralDT( "10", dt1 );
        Node b = NodeFactory.createLiteralDT( "10", dt2 );
        assertDiffer( "types must make a difference", a, b );
        assertTrue( "A and B must express the same value", a.sameValueAs( b ) );
        assertTrue( "matching literals must respect sameValueAs", a.matches( b ) );
    }

    public void testLiteralToString()
    {
        TypeMapper tm = TypeMapper.getInstance();
        RDFDatatype dtInt = tm.getTypeByValue( Integer.valueOf( 10 ) );
        Node plain = NodeFactory.createLiteralLang( "rhubarb", "");
        Node english = NodeFactory.createLiteralLang( "eccentric", "en-UK");
        Node typed = NodeFactory.createLiteralDT( "10", dtInt );
        assertEquals( "\"rhubarb\"", plain.toString() );
        assertEquals( "\"eccentric\"@en-UK", english.toString() );
        assertEquals( "\"10\"^^xsd:int", typed.toString() );
    }

    public void testGetIndexingValueURI()
    {
        Node u = NodeCreateUtils.create( "eh:/telephone" );
        assertSame( u, u.getIndexingValue() );
    }

    public void testGetIndexingValueBlank()
    {
        Node b = NodeCreateUtils.create( "_television" );
        assertSame( b, b.getIndexingValue() );
    }

    public void testGetIndexingValuePlainString()
    { testIndexingValueLiteral( ()->NodeCreateUtils.create( "'literally'" ) ); }

    public void testGetIndexingValueLanguagedString()
    { testIndexingValueLiteral( ()->NodeCreateUtils.create( "'chat'fr" ) ); }

    public void testGetIndexingValueXSDString()
    { testIndexingValueLiteral( ()->NodeCreateUtils.create( "'string'xsd:string" ) ); }

    // JENA-1936
    public void testGetIndexingValueHexBinary1()
    { testIndexingValueLiteral( ()->NodeCreateUtils.create( "''xsd:hexBinary" ) ); }

    public void testGetIndexingValueHexBinary2()
    { testIndexingValueLiteral( ()->NodeCreateUtils.create( "'ABCD'xsd:hexBinary" ) ); }

    public void testGetIndexingValueBase64Binary1()
    { testIndexingValueLiteral( ()->NodeCreateUtils.create( "''xsd:base64Binary" ) ); }

    // "sure." encodes to "c3VyZS4="
    public void testGetIndexingValueBase64Binary2()
    { testIndexingValueLiteral( ()->NodeCreateUtils.create( "'c3VyZS4='xsd:base64Binary" ) ); }

    private void testIndexingValueLiteral( Creator<Node> creator) {
        Node n1 = creator.create();
        Node n2 = creator.create();
        testIndexingValueLiteral(n1,n2);
    }

    private void testIndexingValueLiteral(Node n1, Node n2) {
        assertNotSame(n1, n2); // Test the test.
        assertEquals(n1.getLiteral().getIndexingValue(), n2.getIndexingValue());
        assertEquals(n1.getLiteral().getIndexingValue().hashCode(), n2.getIndexingValue().hashCode());
    }

    public void  testGetLiteralValuePlainString()
    {
        Node s = NodeCreateUtils.create( "'aString'" );
        assertSame( s.getLiteral().getValue(), s.getLiteralValue() );
    }

    public void testGetLiteralDatatypePlainString()
    {
        assertString(NodeCreateUtils.create( "'plain'" )) ;
    }

    public void testConcrete()
    {
        assertTrue( NodeCreateUtils.create( "S" ).isConcrete() );
        assertTrue( NodeCreateUtils.create( "_P" ).isConcrete() );
        assertTrue( NodeCreateUtils.create( "11" ).isConcrete() );
        assertTrue( NodeCreateUtils.create( "'hello'" ).isConcrete() );
        assertFalse( NodeCreateUtils.create( "??" ).isConcrete() );
        assertFalse( NodeCreateUtils.create( "?x" ).isConcrete() );
    }

    static String [] someURIs = new String []
        {
        "http://domainy.thing/stuff/henry",
        "http://whatever.com/stingy-beast/bee",
        "ftp://erewhon/12345",
        "potatoe:rhubarb"
        };

    /**
        test that URI nodes have namespace/localname splits which are consistent
        with Util.splitNamepace.
     */
    public void testNamespace()
    {
        for ( String uri : someURIs )
        {
            int split = SplitIRI.splitXML( uri );
            Node n = NodeCreateUtils.create( uri );
            assertEquals( "check namespace", uri.substring( 0, split ), n.getNameSpace() );
            assertEquals( "check localname", uri.substring( split ), n.getLocalName() );
        }
    }

    protected static String [] someNodes =
    {
        "42",
        "'hello'",
        "_anon",
        "'robotic'tick",
        "'teriffic'abc:def"
    };

    public void testHasURI()
    {
        for ( String someURI : someURIs )
        {
            testHasURI( someURI );
        }
        for ( String someNode : someNodes )
        {
            testHasURI( someNode );
        }
    }

    protected void testHasURI( String uri )
    {
        Node n = NodeCreateUtils.create( uri );
        assertTrue( uri, !n.isURI() || n.hasURI( uri ) );
        assertFalse( uri, n.hasURI( uri + "x" ) );
    }

    private static void assertString(Node n) {
        RDFDatatype dt = n.getLiteralDatatype() ;
        assertEquals("", n.getLiteralLanguage() ) ;
        assertEquals(XSDDatatype.XSDstring, dt) ;
    }

    private static void assertLangString(Node n) {
        RDFDatatype dt = n.getLiteralDatatype() ;
        assertDiffer("", n.getLiteralLanguage() ) ;    // "" is not legal.
        assertEquals(RDF.dtLangString, dt) ;
    }

    /**
     	Answer the string <code>s</code> prefix-expanded using the built-in
     	PrefixMapping.Extended.
     */
    private String expand( String s )
    { return PrefixMapping.Extended.expandPrefix( s ); }
}
