// Main.java

package VRP;

import java.io.*;
import java.util.*;
import VRP.Parser.*;
import VRP.Model.*;
import VRP.Validator.*;
import Statistics.*;
import GUI.*;

/**
 *  Main.java - Containing the main method and the parameters for the 
 * RDF and the Schema namespaces.
 *
 * @author Karsten Tolle
 * @version 20. Feb. 2001
 */
public class Main /*extends Thread*/ {
	/** String containing the name of the output file. Default is standard output (if not specified). */
	static public String OutputFile;
	/** Will be used mainly by the RDF_Error.class. */
	static public parser p_all;
	/** A global error counter. */
	static public int error_counter = 0;

	/** When this option is set the lexer will print the token names and values. Default is 'false'. */
	static public boolean debug = false; 
	/** Flag to print textual representation of the model. Default is 'false'. */
	static public boolean graph = false; 
	/** Flag to print the triples of the model. Default is 'false'. */ 
	static public boolean statements = false;
	/** Flag to print also the triples of the Parser. Default is 'false'. */
	static public boolean triples = false;
   /** Flag wether to conntact to explicit stated namespace and enter all
    * triples or not. Default is 'false'.
    */
   static public boolean fetch_all = false;
 	/** Flag wether to conntact to namespace or not. Default is 'false'.*/
	static public boolean fetch = false; 
	/** Not creating the model, this means no validation and just creating the triples by the parser.*/
	static public boolean only = false;
	/** Printing a feedback about the things VRP does to standard output.*/
	static public boolean verbose = false;
	
	/** new since 8 April 2001
	 *  Duplicate links of a property are permitted
	 **/
	 static public boolean duplicate = false;

	 //Enable/Disable validation constraints
	 static public boolean classLoop= false;
	 static public boolean propertyLoop= false;
	 static public boolean inheritance= false;
	 static public boolean subsetSubpropertyDomainRange = false;
	 static public boolean uniqueDefinedDomainRange = false;
  	 static public boolean typing = false;
    static public boolean sourceTargetTypes = false;

	//Define what statistics information is necessary
	 static public boolean[] modelStatistics = {false, false, false, false};
    public boolean validationStatistics = false;
    public long validationTime = 0;
    public long validationMemory = 0;
	 /** String containing the name of the output file of statistics. Default is standard output (if not specified). */
//	static public String statisticsFile;
	/**
     * HashMap containing  classes' URI and pointer.
     */
     public HashMap c_hmap = new HashMap();
    /**
     * HashMap containing properties' URI and pointer.
     */
     public HashMap p_hmap = new HashMap();
   /**
     * HashMap containing statements' URI and pointer.
     */
     public HashMap st_hmap = new HashMap();
   /**
     * HashMap containing containers' URI and pointer.
     */
     public HashMap cont_hmap = new HashMap();
   /**
     * HashMap containing resources' URI and pointer.
     */
     public HashMap r_hmap = new HashMap();

	 
	 // the address for the RDF and RDF-Schema namespaces
	//  OLD NS The RDF Schema namespace URL: "http://www.w3.org/TR/1999/PR-rdf-schema-19990303#"*/
    	//  OLD NS final static public String Schema_NS = "http://www.w3.org/TR/1999/PR-rdf-schema-19990303#";
	/** The RDF Schema namespace URL: "http://www.w3.org/2000/01/rdf-schema#"*/
    	final static public String Schema_NS = "http://www.w3.org/2000/01/rdf-schema#";
	/** The RDF namespace URL: "http://www.w3.org/1999/02/22-rdf-syntax-ns#"*/
    	final static public String RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";

 	// The old RDF namespace used in the 500 files for testing the parser.
 	//final static public String RDF_NS = new String("http://w3.org/TR/1999/PR-rdf-syntax-19990105#");


	// The elements of the Schema_NS: "S_"+<element name>
	/** Element from the RDF Schema namespace: Schema_NS+"Resource"*/
	final static public String S_Resource = Schema_NS+"Resource";
	/** Element from the RDF Schema namespace: Schema_NS+"comment"*/
	final static public String S_comment = Schema_NS+"comment";
	/** Element from the RDF Schema namespace: Schema_NS+"label"*/
	final static public String S_label = Schema_NS+"label";
	/** Element from the RDF Schema namespace: Schema_NS+"Class"*/
	final static public String S_Class = Schema_NS+"Class";
	/** Element from the RDF Schema namespace: Schema_NS+"subClassOf"*/
	final static public String S_subClassOf = Schema_NS+"subClassOf";
	/** Element from the RDF Schema namespace: Schema_NS+"subPropertyOf"*/
	final static public String S_subPropertyOf = Schema_NS+"subPropertyOf";
	/** Element from the RDF Schema namespace: Schema_NS+"seeAlso"*/
	final static public String S_seeAlso = Schema_NS+"seeAlso";
	/** Element from the RDF Schema namespace: Schema_NS+"isDefinedBy"*/
	final static public String S_isDefinedBy = Schema_NS+"isDefinedBy";
	/** Element from the RDF Schema namespace: Schema_NS+"ConstraintResource"*/
	final static public String S_ConstraintResource = Schema_NS+"ConstraintResource";
	/** Element from the RDF Schema namespace: Schema_NS+"ConstraintProperty"*/
	final static public String S_ConstraintProperty = Schema_NS+"ConstraintProperty";
	/** Element from the RDF Schema namespace: Schema_NS+"domain"*/
	final static public String S_domain = Schema_NS+"domain";
	/** Element from the RDF Schema namespace: Schema_NS+"range"*/
	final static public String S_range = Schema_NS+"range";
	/** Element from the RDF Schema namespace: Schema_NS+"Literal"*/
	final static public String S_Literal = Schema_NS+"Literal";
	/** Element from the RDF Schema namespace: Schema_NS+"Container"*/
	final static public String S_Container = Schema_NS+"Container";
	/** Element from the RDF Schema namespace: Schema_NS+"ContainerMembershipProperty"*/
	final static public String S_ContainerMembershipProperty = Schema_NS+"ContainerMembershipProperty";

	/** The set of Classes from the RDF Schema namespace. */
	static public ArrayList S_Classes = init_S_Classes();
	/** The set of Properties from the RDF Schema namespace. */
	static public ArrayList S_Properties = init_S_Properties();	

	// The elements of the RDF_NS: "R_"+<element name>
	/** Element from the RDF namespace: RDF_NS+"Statement"*/
	final static public String R_Statement = RDF_NS+"Statement";
	/** Element from the RDF namespace: RDF_NS+"Property"*/
	final static public String R_Property = RDF_NS+"Property";
	/** Element from the RDF namespace: RDF_NS+"Bag"*/
	final static public String R_Bag = RDF_NS+"Bag";
	/** Element from the RDF namespace: RDF_NS+"Seq"*/
	final static public String R_Seq = RDF_NS+"Seq";
	/** Element from the RDF namespace: RDF_NS+"Alt"*/
	final static public String R_Alt = RDF_NS+"Alt";
	/** Element from the RDF namespace: RDF_NS+"predicate"*/
	final static public String R_predicate = RDF_NS+"predicate";
	/** Element from the RDF namespace: RDF_NS+"subject"*/
	final static public String R_subject = RDF_NS+"subject";
	/** Element from the RDF namespace: RDF_NS+"object"*/
	final static public String R_object = RDF_NS+"object";
	/** Element from the RDF namespace: RDF_NS+"type"*/
	final static public String R_type = RDF_NS+"type";
	/** Element from the RDF namespace: RDF_NS+"value"*/
	final static public String R_value = RDF_NS+"value";
	// Attributes belonging to the RDF_NS
	/** Attribute from the RDF namespace: RDF_NS+"ID"*/
	final static public String R_ID = RDF_NS+"ID";
	/** Attribute from the RDF namespace: RDF_NS+"bagID"*/
	final static public String R_bagID = RDF_NS+"bagID";
	/** Attribute from the RDF namespace: RDF_NS+"about"*/
	final static public String R_about = RDF_NS+"about";
	/** Attribute from the RDF namespace: RDF_NS+"aboutEach"*/
	final static public String R_aboutEach = RDF_NS+"aboutEach";
	/** Attribute from the RDF namespace: RDF_NS+"aboutEachPrefix"*/
	final static public String R_aboutEachPrefix = RDF_NS+"aboutEachPrefix";
	/** Attribute from the RDF namespace: RDF_NS+"Description"*/
	final static public String R_Description = RDF_NS+"Description";
	/** Attribute from the RDF namespace: RDF_NS+"resource"*/
	final static public String R_resource = RDF_NS+"resource";

	/** Element from the RDF namespace: RDF_NS+"_" (will be used to test the start of rdf:_n | n integer) */
	final static public String R_msp = RDF_NS+"_"; // for membership properties _1, _2, _3, ...


	/** The set of Classes from the RDF namespace. */
	static public ArrayList R_Classes = init_R_Classes();
	/** The set of Properties from the RDF namespace. */
	static public ArrayList R_Properties = init_R_Properties();

	/** new since 2 April 2001
	 *  Literal type is specialized into string, integer,
   *  float, boolean and dateTime datatypes
	 **/

	/** Address of the namespace where XML Schema datatypes are defined */
  final static public String XMLSchema_DT_NS = "http://www.w3.org/1999/XMLSchema-datatypes#";
  /** string XML Schema datatype */
	final static public String stringDT =  XMLSchema_DT_NS +"string";
  /** integer XML Schema datatype */
	final static public String intDT =  XMLSchema_DT_NS +"integer";
  /** float XML Schema datatype */
	final static public String floatDT =  XMLSchema_DT_NS +"float";
  /** boolean XML Schema datatype */
	final static public String booleanDT =  XMLSchema_DT_NS +"boolean";
  /** dateTime XML Schema datatype */
	final static public String dateTimeDT =  XMLSchema_DT_NS +"dateTime";

/** The subset of XML Schema Datatypes supported by VRP. */
	static public ArrayList XMLSchemaDatatypes = init_XMLS_Types();

	static public boolean simple = false;
	static public boolean noerrormsg = false;
	static public String file = "";
	static public GUI gui;
//   static public StorageGUI storageGUI;
	static public Output Print = new Output();
    /** A global warning counter. */
   static public int warning_counter = 0;


	public Main() {}


	public void setNoErrormsg(boolean b) {
		this.noerrormsg=b;
	}

	public void setGUI(GUI f) {
		this.gui = f;
	}
	
	public void setfile(String f) {
		this.file = f;
	}




	/**
         * The classes of RDF-Schema namespace.
	 */
	private static ArrayList init_S_Classes() {
		ArrayList AL = new ArrayList(); 
		AL.add(S_Resource);
		AL.add(S_Class);
		AL.add(S_ConstraintResource);
		AL.add(S_ConstraintProperty);
		AL.add(S_Literal);
		AL.add(S_Container);
		AL.add(S_ContainerMembershipProperty);
		return AL;
	}

	/**
         * The properties of RDF-Schema namespace.
	 */
	private static ArrayList init_S_Properties() {
		ArrayList AL = new ArrayList(); 
		AL.add(S_comment);
		AL.add(S_label);
		AL.add(S_domain);
		AL.add(S_range);
		AL.add(S_isDefinedBy);
		AL.add(S_seeAlso);
		AL.add(S_subPropertyOf);
		AL.add(S_subClassOf);
		return AL;
	}


	/**
         * The classes of RDF namespace.
	 */
	private static ArrayList init_R_Classes() {
		ArrayList AL = new ArrayList();
		AL.add(R_Bag);
		AL.add(R_Seq);
		AL.add(R_Alt);
		AL.add(R_Statement);
		AL.add(R_Property);
		return AL;
	}

	/**
         * The properties of RDF namespace. R_msp (Mebership property is not entered her
	 * because we can not enter it for each integer.)
	 */
	private static ArrayList init_R_Properties() {
		ArrayList AL = new ArrayList(); 
		AL.add(R_object);
		AL.add(R_subject);
		AL.add(R_predicate);
		AL.add(R_value);
		AL.add(R_type);
		return AL;
	}

   /**
    * The XML Schema datatypes supported by VRP.
	  */
   private static ArrayList init_XMLS_Types() {
		ArrayList AL = new ArrayList();
		AL.add(stringDT);
		AL.add(intDT);
		AL.add(floatDT);
		AL.add(booleanDT);
		AL.add(dateTimeDT);
	  return AL;
	}


    /**
	 * Clear the output file and enter time of gerneration.
	 * If the <OutputFile> exists it asks for overwriting the file.
	 * The answere 'n' (no) will exit the program.
	 */
	private void clean_file(String filename) throws Exception {
		PrintWriter pw = null;
		try {
			// clear the outputfile
			File f = new File(OutputFile);
			if (f.exists()) {
				try {
					BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
					String act;
					// changed on 04.10.2000 by Karsten Tolle
					if (gui != null) {
					    
						gui.clean_file_request(OutputFile);
						if (! gui.clean_file) {
						    throw new Exception("Main.clean_file Exception");
						}
					    
					} else {
					    do {
						System.out.println("Do you want to overwrite the file "+OutputFile+"? (y or n) ");
						act = in.readLine();
						if (act.startsWith("n")) throw new Exception("Main.clean_file Exception");
					    } while (! act.startsWith("y"));
					}
					// changed on 04.10.2000 by Karsten Tolle
				} catch (IOException e) {}
			}
			FileWriter fw = new FileWriter(OutputFile);
			if (!f.exists() || !f.canRead()) Print.outln("geht nicht", false);
			if (!f.canWrite()) Print.outln("Not allowed to write!", false);
			pw = new PrintWriter(fw);
			pw.println("");

			// bugfix by Olivier Corby
			pw.close();

			// enter the filename and actual date to the OutputFile
			Output put = new Output();
			Date d = new Date();
			put.out("Information about: "+filename+"\nTime of gerneration: "+d.toString()+"\n", false);
		} catch (Exception e) {
			Print.outln(e.getMessage(), false);

		}
	}

   /**
    * Prints validation statistics.
    */
    public void getValidationStatistics() {
      Print.outln("Validation time: " +	(int)validationTime/1000 + " sec",true);
      Print.outln("Main Memory: " + validationMemory + " Kbytes", true);
    }

	 /**
	  * Create and start the parser and the validator.
	  */
   public Model start(String Name) {
      Model out_model = new Model();
      parser p = null;
      RDF_Validator v = null;
      //Modified
      if (verbose) Print.outln("Filename: " +Name, false);
		try {
		   if (OutputFile != null) clean_file(Name); // empty the outputfile
      //Modified
         long start_time = 0;
     	   long end_time = 0;
         if ( validationStatistics )
           start_time = System.currentTimeMillis();
         p = new parser(Name);
         setparseroptions(p);
         p_all = p;
			boolean valid = false;
			if (verbose) Print.outln("Parsing the file ...", false);
			Object result = p.parse().value;
			if (verbose) Print.outln("", false);
			if (only) return null;
			if (result instanceof Model) {
				out_model = (Model)result;
			   v = new RDF_Validator(out_model);
         //Note: change in order not mix VRP Loader. Find a way to determine
        //Loader if we will have statistics.
				//if (!Loader.dbvalid)
			   v.validate();
            if ( validationStatistics )
              end_time = System.currentTimeMillis();

				if (error_counter == 0) {
					Print.outln("The file "+Name+" is a valid RDF-File.", true);
					valid = true;
				} else Print.outln("The file "+Name+" is not a valid RDF-File.", true);
				error_counter = 0;
				if (graph) {
					if (verbose) Print.outln("Writing the model to file "+OutputFile, false);
					out_model.dump();
				}
				if (statements) {
					if (verbose) Print.outln("Writing the triples of the model to file "+OutputFile, false);
					out_model.printStatements();
				}
        //Note: change in order not mix VRP Loader. Find a way to determine
        //Loader if we will have statistics.
            if ( validationStatistics) {
               validationTime = end_time - start_time;
               Runtime rt =  Runtime.getRuntime();
               validationMemory = rt.totalMemory()/1000;
            }
            if (valid)
 		         //getModelStatistics(out_model, v.getsubClassOfDAG(), v.getsubPropertyOfDAG(), v.getClassHierarchyRoots(), v.getPropertyHierarchyRoots());
				if (valid)  {
              /* System.out.println("Before");
               v.getClassHierarchyRoots();
               System.out.println("After");*/
				   return out_model;
				}
			} else {
				Print.outln("The file "+Name+" is not a valid RDF-File.", false);
			}
		} catch (Exception e) {
			Print.outln(e.getMessage(), false);
		}
	    return null;
	}


	/**
	 * Setting all the options of the parser according to the options setted here.
	 */
	public void setparseroptions(parser p) {
		p.setonly(only);
		p.setdebug(debug);
		p.settriples(triples);
		p.setfetch_all(fetch_all);
		p.setsimple(simple);
		p.setverbose(verbose);
	}
	
	/**
	 * Method to set the options: count, debug, fetch, lexer, print, triples and statements.
	 */
	private static void set_options(String options) {
		RDF_Error e = new RDF_Error();
		for (int i = options.length(); i>0 ; ) {
			String o = options.valueOf(options.charAt(--i));
			// Print.outln(o);
			if (o.equals("t")) triples = true;
			if (o.equals("f")) fetch = true;
			if (o.equals("a")) fetch_all = true;
			if (o.equals("d")) debug = true;
			if (o.equals("g")) graph = true;
			if (o.equals("s")) statements = true;
			if (o.equals("e")) simple = true;
			if (o.equals("o")) only =true;
			if (o.equals("v")) verbose =true;
			if (! (o.equals("t") ||
				o.equals("f") ||
				o.equals("a") ||
				o.equals("d") ||
				o.equals("g") ||
				o.equals("e") ||
				o.equals("s") ||
				o.equals("o") ||
				o.equals("v") )) {
					e.emit_error(4, 7, "", "VRP.Main.set_options");
					System.exit(0);
			}
		} // for
		// we do not want to have the flags fetch and fetch_all true at the same time
		int j = options.indexOf("a");
		int k = options.indexOf("f");
		if (j>=0 && k>=0) {
		     e.emit_error(4, 8, "The options 'a' and 'f' are not allowd together!", "VRP.Main.set_options");
		     System.exit(0);
		}
	}

   void getModelStatistics(Model outModel, RDF_DAG class_dag, RDF_DAG property_dag, Collection CRoot, Collection PRoot)  {
      if (modelStatistics[0] || modelStatistics[1] || modelStatistics[2] || modelStatistics[3] )
         if (! (classLoop &&  propertyLoop && typing) )
             Print.outln("Model statistics will be displayed only if the first, second and sixth validation constraints are checked", false);
         else {
            if (modelStatistics[0])
	            outModel.printStatistics();
            SchemaStatistics stats = new SchemaStatistics();
            if (modelStatistics[1]) {
               stats.printHierarchyStatistics(outModel.getClasses(), class_dag.dag, CRoot, 0);
            }
            if (modelStatistics[2]) {
               stats.printHierarchyStatistics(outModel.getProperties(), property_dag.dag, PRoot, 1);
            }
            if (modelStatistics[3])
               stats.printInstanceDistribution(outModel);

          }
	}

   void splitHmap(HashMap hmap){
       Collection value = hmap.values();
       Iterator value_iter = value.iterator();
       Set key = hmap.keySet();
       Iterator key_iter = key.iterator();
       while (value_iter.hasNext()) {
           Object valueobj = value_iter.next();
           Object keyobj = key_iter.next();
           if (valueobj != null && keyobj != null){
               if (valueobj instanceof RDF_Class) {
                  c_hmap.put(keyobj,valueobj);
               } else if (valueobj instanceof RDF_Property) {
                  p_hmap.put(keyobj,valueobj);
			   } else if (valueobj instanceof RDF_Statement) {
                  st_hmap.put(keyobj,valueobj);
               } else if (valueobj instanceof RDF_Container) {
                  cont_hmap.put(keyobj,valueobj);
               } else  {
                  r_hmap.put(keyobj,valueobj);
               }
           }
       }
     } //:splitHmap



   /**
	 * The main method.
	 */
	public static void main(String args[]) {
		// wrong use
		if (args.length == 0) {
			StringBuffer b = new StringBuffer();
			b.append("usage: java VRP.Main <filename> [<-options> [<filename>]]");
			b.append("\n\n");
			b.append(" valid options:");
         b.append("\n\n");
		   b.append("\ta \tEnables the fetching of all explicit stated \n\t\tnamespaces. Should not be used together with the f option.");
			b.append("\n\n");
			b.append("\td \tForces the lexical analyser to print the \n\t\ttoken name and token value he sends to \n\t\tthe parser.  If no file is specified for \n\t\tthe output it will be printed to stdout.");
			b.append("\n\n");
			b.append("\te \tUsing a more simple lexical \n\t\tanalyser. Quicker and needs less memory.");
			b.append("\n\n");
			b.append("\tf \tEnables the validator to connect to \n\t\tnamespaces.");
			b.append("\n\n");
			b.append("\tg \tA textual representation of the model will be\n\t\tprinted. If no file is specified for the \n\t\toutput it will be printed to stdout.");
			b.append("\n\n");
			b.append("\to \tThe parser will not send the triples to \n\t\tthe model. This means we have no \n\t\tvalidation against the RDF Schema and just \n\t\tmake a syntactic checking.");
			b.append("\n\n");
			b.append("\ts \tThe triples of the model will be printed. \n\t\tIf no file is specified for the output \n\t\tit will be printed to stdout.");
			b.append("\n\n");
			b.append("\tt \tThe triples of the parser will be printed. \n\t\tIf no file is specified for the output \n\t\tit will be printed to stdout.");
			b.append("\n\n");
			b.append("\tv \tCauses VRP to print a feedback of the activities \n\t\tto stdout.");
			b.append("\n\n\n");
			b.append("\t<filename> \tChanges the default output 'stdout' to <filename>.");
			Print.outln(b.toString(), false);
			return;
		}
		// set the options
		// the options are already set to default value in the rdfparse class
		Main pav = new Main();
		String filename = args[0];
		if (args.length > 3) {
			RDF_Error e = new RDF_Error();
			e.emit_error(4, 8, "", "VRP.Main");
			return;
		}
		if (args.length > 1) { // two or more options
			String options = args[1];
			if (! options.startsWith("-")) {
				RDF_Error e = new RDF_Error();
				e.emit_error(4, 9, "", "VRP.Main");
				return;
			} else set_options(options.substring(1));
		} // if
		if (args.length > 2) OutputFile = args[2];
		pav.start(filename );
	}
}
