/*
 * 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 java.util.StringTokenizer;

import org.apache.jena.datatypes.xsd.XSDDatatype ;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.NodeFactory ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.shared.JenaException;
import org.apache.jena.shared.PrefixMapping;

/**
    Creating nodes from string specifications.
*/
public class NodeCreateUtils
    {
    /**
        Returns a Node described by the string, primarily for testing purposes.
        The string represents a URI, a numeric literal, a string literal, a bnode label,
        or a variable.
        <ul>
        <li> 'some text' :: a string literal with that text
        <li> 'some text'someLanguage:: a string literal with that text and language
        <li> 'some text'someURI:: a typed literal with that text and datatype
        <li> digits :: a literal [OF WHAT TYPE] with that [numeric] value
        <li> _XXX :: a bnode with an AnonId built from _XXX
        <li> ?VVV :: a variable with name VVV
        <li> &PPP :: to be done
        <li> name:stuff :: the URI; name may be expanded using the Extended map
        </ul>
        @param x the string describing the node
        @return a node of the appropriate type with the appropriate label
    */
    public static Node create( String x )
        { return create( PrefixMapping.Extended, x ); }

    /**
    Returns a Node described by the string, primarily for testing purposes.
    The string represents a URI, a numeric literal, a string literal, a bnode label,
    or a variable.
    <ul>
    <li> 'some text' :: a string literal with that text
    <li> 'some text'someLanguage:: a string literal with that text and language
    <li> 'some text'someURI:: a typed literal with that text and datatype
    <li> digits :: a literal [OF WHAT TYPE] with that [numeric] value
    <li> _XXX :: a bnode with an AnonId built from _XXX
    <li> ?VVV :: a variable with name VVV
    <li> &PPP :: to be done
    <li> name:stuff :: the URI; name may be expanded using the Extended map
    </ul>

    @param pm the PrefixMapping for translating pre:X strings
    @param x the string encoding the node to create
    @return a node with the appropriate type and label
    */
    public static Node create( PrefixMapping pm, String x )
        {
        if (x.equals( "" ))
            throw new JenaException( "Node.create does not accept an empty string as argument" );
        char first = x.charAt( 0 );
        if (first == '\'' || first == '\"')
            return newStringNode( pm, first, x);
        if (Character.isDigit( first ))
            return NodeFactory.createLiteral( x, XSDDatatype.XSDinteger );
        if (first == '_')
            return NodeFactory.createBlankNode( x );
        if (x.equals( "??" ))
            return Node.ANY;
        if (first == '?')
            return NodeFactory.createVariable( x.substring( 1 ) );
        if (first == '&')
            return NodeFactory.createURI( "q:" + x.substring( 1 ) );
        int colon = x.indexOf( ':' );
        String d = pm.getNsPrefixURI( "" );
        return colon < 0
            ? NodeFactory.createURI( (d == null ? "eh:/" : d) + x )
            : NodeFactory.createURI( pm.expandPrefix( x ) )
            ;
        }

    private static String unEscape( String spelling )
        {
        if (spelling.indexOf( '\\' ) < 0) return spelling;
        StringBuilder result = new StringBuilder( spelling.length() );
        int start = 0;
        while (true)
            {
            int b = spelling.indexOf( '\\', start );
            if (b < 0) break;
            result.append( spelling.substring( start, b ) );
            result.append( unEscape( spelling.charAt( b + 1 ) ) );
            start = b + 2;
            }
        result.append( spelling.substring( start ) );
        return result.toString();
        }

    private static char unEscape( char ch )
        {
        switch (ch)
        	{
            case '\\':
            case '\"':
            case '\'': return ch;
            case 'n': return '\n';
            case 's': return ' ';
            case 't': return '\t';
            default: return 'Z';
        	}
        }

    public static Node literal( PrefixMapping pm, String spelling, String langOrType )
        {
        String content = unEscape( spelling );
        int colon = langOrType.indexOf( ':' );
        return colon < 0
            ? NodeFactory.createLiteralLang( content, langOrType )
            : NodeFactory.createLiteral( content, NodeFactory.getType( pm.expandPrefix( langOrType )))
            ;
        }

    public static Node newStringNode( PrefixMapping pm, char quote, String nodeString )
        {
        int close = nodeString.lastIndexOf( quote );
        return literal( pm, nodeString.substring( 1, close ), nodeString.substring( close + 1 ) );
        }

	/**
	    Utility factory as for create(String), but allowing the PrefixMapping to
	    be specified explicitly.
	*/
	public static Triple createTriple( PrefixMapping pm, String fact )
	    {
	    StringTokenizer st = new StringTokenizer( fact );
	    Node sub = create( pm, st.nextToken() );
	    Node pred = create( pm, st.nextToken() );
	    Node obj = create( pm, st.nextToken() );
	    return Triple.create( sub, pred, obj );
	    }

	/**
	    Utility factory method for creating a triple based on the content of an
	    "S P O" string. The S, P, O are processed by Node.create, see which for
	    details of the supported syntax. This method exists to support test code.
	    Nodes are interpreted using the Standard prefix mapping.
	*/

	public static Triple createTriple( String fact )
	    { return createTriple( PrefixMapping.Standard, fact ); }
    }
