/*****************************************************************************
 * Source code information
 * -----------------------
 * Original author    Ian Dickinson, HP Labs Bristol
 * Author email       Ian.Dickinson@hp.com
 * Package            Jena 2
 * Web                http://sourceforge.net/projects/jena/
 * Created            27-Mar-2003
 * Filename           $RCSfile: ClassHierarchy.java.html,v $
 * Revision           $Revision: 1.2 $
 * Release status     $State: Exp $
 *
 * Last modified on   $Date: 2003-09-02 14:48:50 $
 *               by   $Author: ian_dickinson $
 *
 *****************************************************************************/

// Package
///////////////


// Imports
///////////////
import com.hp.hpl.jena.ontology.*;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.shared.PrefixMapping;

import java.io.PrintStream;
import java.util.*;


/**
 * <p>
 * Simple demonstration program to show how to list a hierarchy of classes. This
 * is not a complete solution to the problem (sub-classes of restrictions, for example,
 * are not shown).  It is inteded only to be illustrative of the general approach.
 * </p>
 *
 * @author Ian Dickinson, HP Labs
 *         (<a  href="mailto:Ian.Dickinson@hp.com" >email</a>)
 * @version CVS $Id: ClassHierarchy.java.html,v 1.2 2003-09-02 14:48:50 ian_dickinson Exp $
 */
public class ClassHierarchy {
    // Constants
    //////////////////////////////////

    // Static variables
    //////////////////////////////////

    // Instance variables
    //////////////////////////////////

    protected OntModel m_model;
    private Map m_anonIDs = new HashMap();
    private int m_anonCount = 0;
    


    // Constructors
    //////////////////////////////////

    // External signature methods
    //////////////////////////////////

    /** Show the sub-class hierarchy encoded by the given model */
    public void showHierarchy( PrintStream out, OntModel m ) {
        for (Iterator i = rootClasses( m );  i.hasNext();  ) {
            showClass( out, (OntClass) i.next(), new ArrayList(), 0 );
        }
    }


    // Internal implementation methods
    //////////////////////////////////

    /** Present a class, then recurse down to the sub-classes.
     *  Use occurs check to prevent getting stuck in a loop
     */
    protected void showClass( PrintStream out, OntClass cls, List occurs, int depth ) {
        renderClassDescription( out, cls, depth );
        out.println();

        // recurse to the next level down
        if (cls.canAs( OntClass.class )  &&  !occurs.contains( cls )) {
            for (Iterator i = cls.listSubClasses( true );  i.hasNext(); ) {
                OntClass sub = (OntClass) i.next();

                // we push this expression on the occurs list before we recurse
                occurs.add( cls );
                showClass( out, sub, occurs, depth + 1 );
                occurs.remove( cls );
            }
        }
    }


    /**
     * <p>Render a description of the given class to the given output stream.</p>
     * @param out A print stream to write to
     * @param c The class to render
     */
    public void renderClassDescription( PrintStream out, OntClass c, int depth ) {
        indent( out, depth );
        
        if (c.isRestriction()) {
            renderRestriction( out, (Restriction) c.as( Restriction.class ) );
        }
        else {
            if (!c.isAnon()) {
                out.print( "Class " );
                renderURI( out, c.getModel(), c.getURI() );
                out.print( ' ' );
            }
            else {
                renderAnonymous( out, c, "class" );
            }
        }
    }

    /**
     * <p>Handle the case of rendering a restriction.</p>
     * @param out The print stream to write to
     * @param r The restriction to render
     */
    protected void renderRestriction( PrintStream out, Restriction r ) {
        if (!r.isAnon()) {
            out.print( "Restriction " );
            renderURI( out, r.getModel(), r.getURI() );
        }
        else {
            renderAnonymous( out, r, "restriction" );
        }
        
        out.print( " on property " );
        renderURI( out, r.getModel(), r.getOnProperty().getURI() );
    }
    
    /** Render a URI */
    protected void renderURI( PrintStream out, PrefixMapping prefixes, String uri ) {
        out.print( prefixes.usePrefix( uri ) );
    }
    
    /** Render an anonymous class or restriction */
    protected void renderAnonymous( PrintStream out, Resource anon, String name ) {
        String anonID = (String) m_anonIDs.get( anon.getId() );
        if (anonID == null) {
            anonID = "a-" + m_anonCount++;
            m_anonIDs.put( anon.getId(), anonID );
        }
        
        out.print( "Anonymous ");
        out.print( name );
        out.print( " with ID " );
        out.print( anonID );
    }
    
    /** Generate the indentation */
    protected void indent( PrintStream out, int depth ) {
        for (int i = 0;  i < depth; i++) {
            out.print( "  " );
        }
    }

    /**
     * Answer an iterator over the classes we will use as the roots of the depicted
     * hierarchy.  We use named classes that either have Thing as a direct super-class,
     * or which have no declared super-classes.  The first condition is helpful if
     * using a reasoner, the second otherwise.
     * @param m A model
     * @return An iterator over the named class hierarchy roots in m
     */
    protected Iterator rootClasses( OntModel m ) {
        List roots = new ArrayList();
        
        for (Iterator i = m.listClasses();  i.hasNext(); ) {
            OntClass c = (OntClass) i.next();
            
            // too confusing to list all the restrictions as root classes 
            if (c.isAnon()) {
                continue;
            }
            
            if (c.hasSuperClass( m.getProfile().THING(), true ) ) {
                // this class is directly descended from Thing
                roots.add( c );
            }
            else if (c.getCardinality( m.getProfile().SUB_CLASS_OF() ) == 0 ) {
                // this class has no super-classes (can occur if we're not using the reasoner)
                roots.add( c );
            }
        }
        
        return roots.iterator();
    }

    //==============================================================================
    // Inner class definitions
    //==============================================================================

}