/////////////////////////////////////////////////////////////////////////
//
// © University of Southampton IT Innovation Centre, 2004
//
// Copyright in this library belongs to the IT Innovation Centre of
// 2 Venture Road, Chilworth Science Park, Southampton, SO16 7NP, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//      Created by:             Stuart E. Middleton
//      Created date:           2004/04/30
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

package uk.ac.soton.itinnovation.gemss.negotiation.owl;

import java.lang.*;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.hp.hpl.jena.ontology.*;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.shared.PrefixMapping;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.impl.LiteralLabel;

import uk.ac.soton.itinnovation.gemss.negotiation.owl.OWLIndividualHandler;

/*
* Interface for an OWL individual handler that will allow inspection fo the instances held within
* an OWL ontology. The OWL model is initially loaded, and then property values can be requested etc.
*/
public class QoSOntologyImpl implements OWLIndividualHandler
{
      // members
      private OntModel mOntologyModel;

      // logging
      private static Logger mlogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.negotiation.owl.qosontologyimpl");

      /**
       * constructor - does nothing but initialize members
       */
      public QoSOntologyImpl() {
            // init members
            mOntologyModel = null;
      }

      /**
       * load an OWL ontology from a given URI.
       * @param strURI ontology URI from where the OWL ontology can be read
       */
      public void loadOntology( String strURI ) throws Exception
      {
            try
            {
                  // make a new model
                  mOntologyModel = ModelFactory.createOntologyModel( OntModelSpec.OWL_MEM, null );
                  if( mOntologyModel == null ) throw new Exception("null model");

                  // read in the owl URI
                  mOntologyModel.read( strURI );

                  // all done
                  return;

            }
            catch( Exception ex )
            {
                  mlogger.log( Level.SEVERE,"Failed to load ontology",ex );
                  throw new Exception("Failed to load ontology from "+strURI );
            }
      }

      /**
       * load an OWL ontology from a given URI.
       * @param strURI ontology URI from where the OWL ontology can be read
       * @param strAlternativeURI An alternative URI, such as a local file, for the ontology URI.
       * This URI will be opened, read, and treated as if the original URI has been read.
       */
      public void loadOntology( String strURI, String strAlternativeURI ) throws Exception
      {
            try
            {
                  // make a new model
                  mOntologyModel = ModelFactory.createOntologyModel( OntModelSpec.OWL_MEM, null );
                  if( mOntologyModel == null ) throw new Exception("null model");

                  // setup alternative URI's in case of local files etc.
                  mOntologyModel.getDocumentManager().addAltEntry( strURI, strAlternativeURI );

                  // read in the owl URI
                  mOntologyModel.read( strAlternativeURI );

                  // all done
                  return;

            }
            catch( Exception ex )
            {
                  mlogger.log( Level.SEVERE,"Failed to load ontology from alternative URI",ex );
                  throw new Exception("Failed to load ontology from "+strAlternativeURI );
            }
      }


      /**
       * list the properties associated with an individual, within the context of the
       * loaded ontology
       * @param strURI URI pointing to an OWL individual
       * @return vector of property names (strings) for this individual, or null if could not find property list (e.g. bad URI)
       */
      public Vector listProperties( String strURI ) throws Exception
      {
            Individual indTarget;
            Resource resource;
            OntClass classObject;
            com.hp.hpl.jena.rdf.model.Property prop;  // avoid duplication with java.io
            Iterator it1,it2;
            Vector vectorProperties;
            String strName;

            try {
                  // get individual from this URI
                  indTarget = findIndividual( strURI );
                  if( indTarget == null ) return null; // URI not found

                  // get all direct classes of this individual (not super classes)
                  vectorProperties = new Vector();
                  it1 = indTarget.listRDFTypes( true );
                  while( it1.hasNext() ) {
                        // get its class (rdf type)
                        resource = (Resource) it1.next();
                        if( resource == null ) throw new Exception("null resource");

                        // check to see if resource type can be typecast as an ontology class
                        if( resource.canAs( OntClass.class ) ) {
                              classObject = (OntClass) resource.as( OntClass.class );
                              if( classObject == null ) throw new Exception("null class");

                              // get this classes (and all superclasses) set of properties
                              it2 = classObject.listDeclaredProperties( true );
                              while( it2.hasNext() ) {
                                    // get the property
                                    prop = (com.hp.hpl.jena.rdf.model.Property) it2.next();
                                    if( prop == null ) throw new Exception("null property");

                                    // get name of property
                                    strName = prop.getLocalName();
                                    if( strName == null ) throw new Exception("null property name");

                                    // add name to vector if not already there
                                    if( !vectorProperties.contains( strName ) ) vectorProperties.addElement( strName );
                              } // next property
                        } // check class typecast
                  } // next resource

                  // return vector of property names
                  return vectorProperties;
            }
            catch( Exception ex ) {
                  mlogger.log( Level.INFO,"Failed to get list of properties",ex );
                  return null;
            }
      }

      /**
       * get the property name for a specific individual, within the context of the
       * loaded ontology
       * @param strURI URI pointing to an OWL individual
       * @param strProperty name of the property
       * @return string representing the value of the property, or null if no value or property was obtainable (e.g. bad URI)
       */
      public String getPropertyValue( String strURI, String strProperty ) throws Exception
      {
            Individual indTarget;
            Resource resource;
            OntClass classObject;
            RDFNode nodeRDF;
            com.hp.hpl.jena.rdf.model.Property prop, propTarget;  // avoid duplication with java.io
            com.hp.hpl.jena.graph.Node node;
            Iterator it1,it2;
            String strName;

            try {
                  // get individual from this URI
                  indTarget = findIndividual( strURI );
                  if( indTarget == null ) return null; // did not find URI

                  // loop on all properties to find the property object that matches
                  propTarget = null;

                  // get all direct classes of this individual (not super classes)
                  it1 = indTarget.listRDFTypes( true );
                  while( it1.hasNext() && propTarget == null ) {
                        // get its class (rdf type)
                        resource = (Resource) it1.next();
                        if( resource == null ) throw new Exception("null resource");

                        // check to see if resource type can be typecast as an ontology class
                        if( resource.canAs( OntClass.class ) ) {
                              classObject = (OntClass) resource.as( OntClass.class );
                              if( classObject == null ) throw new Exception("null class");

                              // get this classes (and all superclasses) set of properties
                              it2 = classObject.listDeclaredProperties( true );
                              while( it2.hasNext() && propTarget == null ) {
                                    // get the property
                                    prop = (com.hp.hpl.jena.rdf.model.Property) it2.next();
                                    if( prop == null ) throw new Exception("null property");

                                    // get name of property
                                    strName = prop.getLocalName();
                                    if( strName == null ) throw new Exception("null property name");

                                    // add name to vector if not already there
                                    if( strName.equals( strProperty ) ) {
                                          propTarget = prop;
                                    }
                              } // next property
                        } // check class typecast
                  } // next resource

                  // did we find it?
                  if( propTarget == null ) return null; // no property found

                  // get node value for this property
                  nodeRDF = indTarget.getPropertyValue( propTarget );
                  if( nodeRDF == null ) throw new Exception("null node value for property "+strProperty);
                  node = nodeRDF.asNode();

                  // return its value
                  if( node.isURI() ) {
                        return node.getURI();
                  }
                  if( node.isLiteral() ) {
                        // node Jena javadocs have missed out the LiteralLabel class so I got this from a decompile
                        // therefore it is possible this might be unsupported in a later release
                        // stuart apr 2004
                        return node.getLiteral().getLexicalForm();
                  }
                  if( node.isVariable() ) {
                        return node.getName();
                  }

                  // return string name of node
                  mlogger.log( Level.INFO,"Unknown node type obtained for property, null returned" );
                  return null;
            }
            catch( Exception ex ) {
                  mlogger.log( Level.INFO,"Failed to get property value",ex );
                  return null;
            }
      }

      /**
       * find an individual with a URI
       * @param strURI URI pointing to an OWL individual
       * @return Individual instance or null if not found
       */
      private Individual findIndividual( String strURI ) throws Exception
      {
            Individual indObject, indTarget;
            Iterator it1;

            try {
                  // find individual from its URI
                  indTarget = null;
                  it1 = mOntologyModel.listIndividuals();
                  while( it1.hasNext() && indTarget == null ) {
                        // get individual
                        indObject = (Individual) it1.next();
                        if( indObject == null ) throw new Exception("null individual");

                        // match?
                        if( strURI.equals( indObject.getNameSpace() + indObject.getLocalName() ) ) {
                              indTarget = indObject;
                        } // instance check
                  } // next individual

                  // return what we found (if anything)
                  return indTarget;
            }
            catch( Exception ex ) {
                  mlogger.log( Level.INFO,"Failed to find individual",ex );
                  return null;
            }
      }

} // end of OWLIndividualHandler