/*
   This software was developed within the context of the project Metaphor for Science
   Museums (Mesmuses) in the framework of the Information Society Technology
   Programme, part of the Fifth Research And Technological Development
   Framework Programme of the European Community, under the Consortium
   Agreement dated <June 30, 2001>, between INRIA, Finsiel Spa, IMSS,
   Valoris, CSI, ICS-FORTH, ENSTB, EDW International.
*/

// Link.java
package gr.forth.ics.vrp.corevrp.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.lang.Exception;
import com.sun.msv.datatype.xsd.XSDatatype;
import com.sun.msv.datatype.xsd.DatatypeFactory;
import gr.forth.ics.vrp.corevrp.vocabulary.*;

/**
 * Link.java - A class for links containing subject and object of a statement.
 *
 * @author Karsten Tolle & Sofia Alexaki
 * @version 2.5  (30 Januar 2003)
 */
public class Link {

	/**
	 * Create a new Link.
	 */
	 Link() {}

	/**
	 * Create a new Link and set the subject and object. Where the oject is a resource identified by a URI.
	 */
         Link(int subj, int obj) {
		this.subject = new Integer(subj);
		this.object = new Integer(obj);
	}
	/**
	 * Create a new Link and set the subject and object. Where the object is a Literal.
	 */
	 Link(int subj, Literal obj) {
		this.subject = new Integer(subj);
		this.object = obj;
	}

	// Variables
	/**
	 * Containing the subject of the Link.
	 */
	private Object subject;

	/**
	 * Containing the object of the Link.
	 */
	private Object object;

	// Getting methodes
    	/**
    	 * Get the int for the subject of a link. Only during creation of the model useful
         * therefor it is not public.
     	 */
	int getsubject_int() {
          if (subject instanceof Integer) {
              Integer I = (Integer)subject;
              return I.intValue();
          } else return -1;
        }
    	/**
    	 * Get the subject of a link.
     	 */
        public Object getsubject() {
          return subject;
        }

    	/**
    	 * Set the subject of a link.
     	 */
	void setsubject(int subj) { this.subject = new Integer(subj); }
    public void setsubject(Resource r) { this.subject = r; }


    	/**
    	 * Get the object of a link.
     	 */
	public Object getobject() { return object; }

    	/**
    	 * Set the object of a link.
     	 */
	 void setobject(int obj) { this.object = new Integer(obj); }
	 public void setobject(Resource r) { this.object = r; }
	 public void setobject(Literal lit) { this.object = lit; }


    /**
    * This method is for changing the Integer to links to the real java objects
    * so, we do not need the HashTables any more.
    */
    public void solve_links(Existing existing) {
      if (this.subject instanceof Integer) {
        subject = existing.get(((Integer)subject).intValue());
      }
      if (this.object instanceof Integer) {
        object = existing.get(((Integer)object).intValue());
      }

    }

    public boolean domain_range_check(ArrayList domain, ArrayList range) throws Exception {
      try {
        this.domain_check(domain);
        this.range_check(range);
      } catch(Exception e) {
            throw e;
      }
      return true;
    }

    private boolean range_check(ArrayList range) throws Exception {
      if (range.size()<=0) {
          return true;
      }
      boolean lit = false;
      ArrayList al = new ArrayList(1);
      if (this.getobject() instanceof Literal) lit = true;
      for (Iterator it = range.iterator(); it.hasNext();) {
          RDF_Class c = (RDF_Class)it.next();
          al.add(c.getID());
          if (lit) { // the object is a Literal
              if (c.getID().equals(rdfschema.Literal)) return true;
              if (c.getID().startsWith(XMLSchema_DT.NS)) {
                // we test if the given datatype is valid
                String object_string = ((Literal)this.getobject()).getvalue();
                if (checkLiteralType(object_string, c.getID())) return true;
              }
              if (c.containssubClassOf_type(rdfschema.Literal)) return true;
          } else { // the object is a resource
            if (this.getobject() instanceof RDF_Resource) {
                if (((RDF_Resource)this.getobject()).has_type(c)) return true;
            } else { // the object is just a resource
                if (c.getID().equals(rdfschema.Resource)) return true;
            }
          }
      }
      String s = "";
      if (lit) s = ((Literal)this.getobject()).getvalue();
      else s = ((Resource)this.getobject()).getID();
      throw new Exception("range error: "+s+" should be of type "+al+" ");
    }

    private boolean domain_check(ArrayList domain) throws Exception {
      if (domain.size()<=0) {
          return true;
      }
      ArrayList al = new ArrayList(1);
      for (Iterator it = domain.iterator(); it.hasNext();) {
          RDF_Class c = (RDF_Class)it.next();
          al.add(c.getID());
          if (this.getsubject() instanceof RDF_Resource) {
            if (((RDF_Resource)this.getsubject()).has_type(c)) return true;
          } else { // the subject is just a resource
            if (c.getID().equals(rdfschema.Resource)) return true;
          }
      }
      throw new Exception("domain error: "+((Resource)this.getobject()).getID()+" should be of type "+al+" ");
   }

   /**
    * Checks the typing of literal values.
    */
   public static boolean checkLiteralType(String obj, String dts) {
      int i = dts.indexOf("#");
      if (i >= 0) dts = dts.substring(++i);
      try {
        XSDatatype dt = DatatypeFactory.getTypeByName(dts);
        // validates a type
        if( dt.isValid(obj,null) )
            return true; // v is valid
        else
            return false; // v is not valid
      } catch (Exception e) {System.out.println(e);}
      return false;
  }
}


	/**
	 * Helper method for 'domain_range_check' in case range is set.
	 */
/*   private void range_check(String Property_ID, Object obj, ArrayList range_list) {
     String by = "RDF_Validator.range_check";
     String object_string = null;
     if (!(obj instanceof Literal)) {
        object_string = obj.toString();
     }
     String range = range_list.get(0).toString();
     if (! (range.equals(rdfschema.Literal) || XMLSchema_DT.XMLSchemaDatatypes.contains(range))) {
       if (!(obj instanceof Literal)) {
        if (!(basis_model.contains(object_string)))
           e.emit_error(3, 41, object_string+" is not found in the Model ", by);
		  if (! (range.equals(rdfschema.Resource))) {
			   Object object = basis_model.getResource(object_string);
			   // for RDF_Classes and RDF_Properties we already entered all infos
			   if (! (object instanceof RDF_Class || object instanceof RDF_Property)) {
				   object = basis_model.getResource(object_string);
			   }
			   if (! (object instanceof RDF_Resource)) {
				   e.emit_error(3, 41, object_string+" is only a Resource, but should be of type: "+range+" used with the property: "+Property_ID, by);
			   } else {
				//   if (! (has_type(object_string, range))) {
					   e.emit_error(3, 41, "Range "+range+" not inside typelist for "+object_string+" used with the property: "+Property_ID, by);
				  // }
			   }
		  }
     }
     }
   }

	/**
	 * Helper method for 'domain_range_check' in case domain is set.
	 */
/*	private void domain_check(String Property_ID, String subj, ArrayList domain_list) {
	  String by = "RDF_Validator.domain_check";
	  if (domain_list.contains(rdfschema.Literal) && (!(basis_model.contains(subj)))) {
  	  } else {
		if (basis_model.contains(subj)) {
			if (!(domain_list.contains(rdfschema.Resource))) {
				Object subject = basis_model.getResource(subj);
				if (! (subject instanceof RDF_Class || subject instanceof RDF_Property)) {
					subject = basis_model.getResource(subj);
				}
		  		if (! (subject instanceof RDF_Resource)) {
			   		e.emit_error(3, 42, subj+" is only a Resource and not of any type of "+domain_list+" used with the property: "+Property_ID, by);
				} else { // subject is instance of RDF_Resource
					RDF_Resource rdf_resource = (RDF_Resource)subject;
					ArrayList types = rdf_resource.gettype();
					// we need one type to fit
					boolean d_bool = false;
					for (int j=0; j<domain_list.size(); ) {
						// for each domain we test if it has the type
					//	if (has_type(subj, domain_list.get(j++).toString())) {
							d_bool = true;
							break;
					//	}
					}
					if (!d_bool) e.emit_error(3, 42, subj+" is not of any type of "+domain_list+" used with the property: "+Property_ID, by);
				}
			}
		}
	  }
	}
   /**
    * Checks if the Resource and Literal types are used properly
    */
 /*  public void resourceLiteralTypeCheck(String Property_ID, Object obj, ArrayList range_list)
   throws IOException {
      String by = "RDF_Validator.resourceLiteralTypeCheck";
      String object_string;
      Literal l;
      if (obj instanceof Literal) {
         l = (Literal)obj;
         object_string = l.getvalue();
      } else {
         object_string = obj.toString();
      }
      String range = ((RDF_Class)range_list.get(0)).getID();
      if (range.equals(rdfschema.Literal) || range.startsWith(XMLSchema_DT.NS)) {
         if (! (obj instanceof Literal)) {
            e.emit_error(3, 46, object_string+" should be a " + range +
				      	 " value to be used with the property: "+ Property_ID, by);
        } else {
            if (!range.equals(rdfschema.Literal)) {
              if ( !checkLiteralType(object_string, range) )
                e.emit_error(3, 46, object_string+" should be a " + range +
                  " but did not passed the datatype check used with the property: "+Property_ID, by);
              } else {
                e.emit_error(3, 46, object_string+" is a Literal, but should be of type: "
                  + range +" used with the property: "+Property_ID, by);
              }
          }
      }
}

*/