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

import org.apache.jena.graph.* ;
import org.apache.jena.rdf.model.* ;

/**
 * <p>
 * A specialisation of Polymorphic that models an extended node in a an extended graph. An extended node
 * wraps a normal node, and adds additional convenience access or user affordances, though the state
 * remains in the graph itself.
 * </p>
*/
public class EnhNode extends Polymorphic<RDFNode> implements FrontsNode
{
    
    /** The graph node that this enhanced node is wrapping */
    final protected Node node;
    
    /** The enhanced graph containing this node */
    final protected EnhGraph enhGraph;
    
    public EnhNode( Node n, EnhGraph g ) {
        super();
        node=n;
        enhGraph = g;
   }

    // External interface
    
    /** 
     * Answer the graph node that this enhanced node wraps
     * @return A plain node
     */
    @Override
    public Node asNode() {
        return node;
    }
    
    /**
     * Answer the graph containing this node
     * @return An enhanced graph 
     */
    public EnhGraph getGraph() {
        return enhGraph;
    }
    
    /**
        An enhanced node is Anon[ymous] iff its underlying node is Blank.
    */
    public final boolean isAnon() {
        return node.isBlank();
    }
    
    /**
        An enhanced node is Literal iff its underlying node is too.
    */
    public final boolean isLiteral() {
        return node.isLiteral();
    }
 
    /**
        An enhanced node is a URI resource iff its underlying node is too.
    */
    public final boolean isURIResource() {
        return node.isURI();
    }
    
    /**
        An enhanced node is a statement resource iff its underlying node is a triple term (RDF-star).
     */
    public final boolean isStmtResource() {
        return node.isNodeTriple();
    }

    /**
        An enhanced node is a resource if it's node is a URI node, a blank node or a triple term.
    */
    public final boolean isResource() {
        return node.isURI() || node.isBlank() || node.isNodeTriple();
    }
    
    /**
     * Answer a facet of this node, where that facet is denoted by the
     * given type.
     * 
     * @param t A type denoting the desired facet of the underlying node
     * @return An enhanced nodet that corresponds to t; this may be <i>this</i>
     *         Java object, or a different object.
     */ 
    public <X extends RDFNode> X viewAs( Class<X> t ) {
        return asInternal( t ); 
    }
    
    /** allow subclasses to implement RDFNode &amp; its subinterface */
    public <T extends RDFNode> T as( Class<T> t )
        { return asInternal( t ); }
      
    /**
        API-level method for polymorphic testing
    */
    public <X extends RDFNode> boolean canAs( Class<X> t )
        { return canSupport( t ); }
        
    /**
     * The hash code of an enhanced node is defined to be the same as the underlying node.
     * @return The hashcode as an int
     */
    @Override final public int hashCode() {
     	return node.hashCode();
    }
       
    /**
     * An enhanced node is equal to another enhanced node n iff the underlying 
     * nodes are equal. We generalise to allow the other object to be any class
     * implementing asNode, because we allow other implemementations of
     * Resource than EnhNodes, at least in principle.
     * This is deemed to be a complete and correct interpretation of enhanced node
     * equality, which is why this method has been marked final.
     * 
     * @param o An object to test for equality with this node
     * @return True if o is equal to this node.
     */
    @Override final public boolean equals( Object o )
        { return o instanceof FrontsNode && node.equals(((FrontsNode) o).asNode()); }
    
    @Override public boolean isValid()
        { return true; }
        
    /** 
        Answer an new enhanced node object that presents <i>this</i> in a way 
        which satisfies type <code>t</code>. The new object is linked into this
        object's sibling ring. If the node cannot be converted, throw an
        UnsupportedPolymorphismException.
    */
    @Override protected <X extends RDFNode> X convertTo( Class<X> t ) 
        {
        EnhGraph eg = getGraph();
        if (eg == null) throw new UnsupportedPolymorphismException( this, false, t );
        Implementation imp = getPersonality().getImplementation( t );
        if (imp == null) throw new UnsupportedPolymorphismException( this, true, t );
        EnhNode result = imp.wrap( asNode(), eg );          
        this.addView( result );
        return t.cast( result );
        }
    
    /**
        answer true iff this enhanced node can support the class <code>t</code>,
        ie it is already a value <code>t</code> or it can be reimplemented
        as a <code>t</code> via the graph's personality's implementation.
        If this node has no graph, answer false.       
    */
    @Override protected <X extends RDFNode> boolean canSupport( Class<X> t )
        {
        if (alreadyHasView( t )) return true;
        if (getGraph() == null) return false;
        Implementation imp = getPersonality().getImplementation( t );
        return imp == null ? false : imp.canWrap( asNode(), getGraph() );
        }

    /**
     * Answer the personality object bound to this enhanced node, which we obtain from 
     * the associated enhanced graph.
     * 
     * @return The personality object
     */
    @Override protected Personality<RDFNode> getPersonality() {
        return getGraph().getPersonality();
    }
    
}
