import java.sql.*;
import java.io.*;
import uk.ac.ic.doc.automed.*;
import uk.ac.ic.doc.automed.reps.*;

import java.util.*;

/*
 * An application to demonstrate how various aspects of the AutoMed
 * repository may be used to link schemas together 
 * via transformation pathways
 */

public class DefineTransformations_nnyt98_evolution {
  public static Schema transformS1ToUS1(Schema s1) 
    throws NotFoundException,TypeMismatchException,IntegrityException {

      Model er=Model.getModel("er");
      Construct ent=er.getConstruct("entity");
      Construct att=er.getConstruct("attribute");
      Construct rel=er.getConstruct("relationship");
      Construct gen=er.getConstruct("generalisation");

      Schema s1a = s1.applyExtendTransformation(ent,new Object[]{"dept"},
                        "range(Void,Any)",null);

      SchemaObject s1a_dept =
            Transformation.getTransformation(s1,s1a).getSchemaObject(s1a);
      Schema s1b = s1a.applyExtendTransformation(att,
                     new Object[]{s1a_dept,"dname","key"},
                     "range(Void,Any)",null);
      Schema s1c = s1b.applyExtendTransformation(att,
                     new Object[]{s1a_dept,"location","null"},
                     "range(Void,Any)",null);

      SchemaObject person = s1c.getSchemaObject("<<person>>");
      Schema s1d = s1c.applyExtendTransformation(rel,
                     new Object[]{"worksin",person,s1a_dept,"1:1","1:N"},
                     "range(Void,Any)",null);
      return s1d;
}

public static Schema transformS2ToUS2(Schema s2) 
  throws NotFoundException,TypeMismatchException,IntegrityException {
      Model er=Model.getModel("er");
      Construct ent=er.getConstruct("entity");
      Construct att=er.getConstruct("attribute");
      Construct rel=er.getConstruct("relationship");
      Construct gen=er.getConstruct("generalisation");

      SchemaObject s2_employee = s2.getSchemaObject("<<employee>>");
      SchemaObject s2_division = s2.getSchemaObject("<<division>>");

      Schema s2a = s2.applyRenameTransformation(s2_employee,new Object[]{"person"});
      Schema s2b = s2a.applyRenameTransformation(s2_division,new Object[]{"dept"});
      return s2b;
  }

  public static Schema transformS3ToUS3(Schema s3) 
    throws NotFoundException,TypeMismatchException,IntegrityException {
      Model er=Model.getModel("er");
      Construct ent=er.getConstruct("entity");
      Construct att=er.getConstruct("attribute");
      Construct rel=er.getConstruct("relationship");
      Construct gen=er.getConstruct("generalisation");

      SchemaObject s3_person = s3.getSchemaObject("<<person>>");
      SchemaObject s3_sex = s3.getSchemaObject("<<sex,total,person,male,female>>");
      SchemaObject s3_female = s3.getSchemaObject("<<female>>");
      SchemaObject s3_male = s3.getSchemaObject("<<male>>");
      SchemaObject s3_person_dname = s3.getSchemaObject("<<person,dname>>");
      SchemaObject s3_person_year = s3.getSchemaObject("<<person,year>>");

      Schema s3a = s3.applyAddTransformation(att,new Object[]{s3_person,"sex","notnull"},
                     "[{x,'m'} | {x} <- <<male>>] ++ [{x,'f'} | {x} <- <<female>>]",
                     null);
      Schema s3b = s3a.applyDeleteTransformation(s3_sex,null,null);
      Schema s3c = s3b.applyDeleteTransformation(s3_female,
                     "[{x} | {x,'f'} <- <<person,sex>>]",null);
      Schema s3d = s3c.applyDeleteTransformation(s3_male,
                     "[{x} | {x,'m'} <- <<person,sex>>]",null);
      Schema s3e = s3d.applyAddTransformation(ent,new Object[]{"dept"},
                     "[{x} | {y,x} <- <<person,dname>>]",null);

      SchemaObject s3e_dept = Transformation.getTransformation(s3d,s3e).getSchemaObject(s3e);

      Schema s3f = s3e.applyAddTransformation(att,
                     new Object[]{s3e_dept,"dname","key"},
                     "[{x,x} | {x} <- <<dept>>]",null);
      Schema s3g = s3f.applyAddTransformation(rel,
                     new Object[]{"worksin",s3_person,s3e_dept,"1:1","1:N"},
                     "<<person,dname>>",null);
      Schema s3h = s3g.applyDeleteTransformation(s3_person_dname,
                     "<<worksin,person,dept>>",null);
      Schema s3i = s3h.applyExtendTransformation(att,
                     new Object[]{s3e_dept,"location","null"},
                     "range(Void,Any)",null);
      Schema s3j = s3i.applyContractTransformation(s3_person_year,
                     "range(Void,Any)",null);
      return s3j;
  }

  public static Schema transformS4ToUS4(Schema s4) 
    throws NotFoundException,TypeMismatchException,IntegrityException {
      Model er=Model.getModel("er");
      Construct ent=er.getConstruct("entity");
      Construct att=er.getConstruct("attribute");
      Construct rel=er.getConstruct("relationship");
      Construct gen=er.getConstruct("generalisation");

      SchemaObject s4_person = s4.getSchemaObject("<<person>>");
      SchemaObject s4_dept = s4.getSchemaObject("<<dept>>");
      SchemaObject s4_basedat = s4.getSchemaObject("<<basedat,person,branch>>");
      SchemaObject s4_within = s4.getSchemaObject("<<within,branch,dept>>");
      SchemaObject s4_branch = s4.getSchemaObject("<<branch>>");
      SchemaObject s4_branch_bname = s4.getSchemaObject("<<branch,bname>>");

      Schema s4a = s4.applyAddTransformation(att,
                     new Object[]{s4_person,"dname","notnull"},
                     "[{x,y} | {x,z} <- <<basedat,person,branch>>; {z,y} <- <<within,branch,dept>>]",
                     null);
      Schema s4b = s4a.applyDeleteTransformation(s4_basedat,
                     "[{x,y} | {x,z} <- <<person,dname>>; {y,z} <- <<within,branch,dept>>]",null);
      Schema s4c = s4b.applyContractTransformation(s4_within,
                     "range(Void,Any)",null);
      Schema s4d = s4c.applyDeleteTransformation(s4_branch_bname,
                     "[{x,x} | {x} <- <<branch>>]",null);
      Schema s4e = s4d.applyContractTransformation(s4_branch,"range(Void,Any)",null);
      Schema s4f = s4e.applyAddTransformation(rel,
                     new Object[]{"worksin",s4_person,s4_dept,"1:1","1:N"},
                     "<<person,dname>>",null);

      SchemaObject s4a_person_dname = s4a.getSchemaObject("<<person,dname>>");
      Schema s4g = s4f.applyDeleteTransformation(s4a_person_dname,
                     "<<worksin,person,dept>>",null);
      Schema s4h = s4g.applyExtendTransformation(att,
                     new Object[]{s4_person,"sex","notnull"},"range(Void,Any)",null);
      Schema s4i = s4h.applyExtendTransformation(att,
                     new Object[]{s4_dept,"location","null"},"range(Void,Any)",null);
      return s4i;
  }

  public static Schema transformS6ToUS6(Schema s6) 
    throws NotFoundException,TypeMismatchException,IntegrityException {
      Model er=Model.getModel("er");
      Construct ent=er.getConstruct("entity");
      Construct att=er.getConstruct("attribute");
      Construct rel=er.getConstruct("relationship");
      Construct gen=er.getConstruct("generalisation");

      SchemaObject s6_employee = s6.getSchemaObject("<<employee>>");

      Schema s6a = s6.applyRenameTransformation(s6_employee,new Object[]{"person"});

      SchemaObject s6a_person = s6a.getSchemaObject("<<person>>");
      SchemaObject s6a_sex = s6a.getSchemaObject("<<sex,total,person,male,female>>");
      SchemaObject s6a_female = s6a.getSchemaObject("<<female>>");
      SchemaObject s6a_male = s6a.getSchemaObject("<<male>>");
      SchemaObject s6a_person_dname = s6a.getSchemaObject("<<person,dname>>");
      SchemaObject s6a_person_year = s6a.getSchemaObject("<<person,year>>");

      Schema s6b = s6a.applyAddTransformation(att,new Object[]{s6a_person,"sex","notnull"},
                     "[{x,'m'} | {x} <- <<male>>] ++ [{x,'f'} | {x} <- <<female>>]",
                     null);
      Schema s6c = s6b.applyDeleteTransformation(s6a_sex,null,null);
      Schema s6d = s6c.applyDeleteTransformation(s6a_female,
                     "[{x} | {x,'f'} <- <<person,sex>>]",null);
      Schema s6e = s6d.applyDeleteTransformation(s6a_male,
                     "[{x} | {x,'m'} <- <<person,sex>>]",null);
      Schema s6f = s6e.applyAddTransformation(ent,new Object[]{"dept"},
                     "[{x} | {y,x} <- <<person,dname>>]",null);

      SchemaObject s6f_dept = Transformation.getTransformation(s6e,s6f).getSchemaObject(s6f);

      Schema s6g = s6f.applyAddTransformation(att,
                     new Object[]{s6f_dept,"dname","key"},
                     "[{x,x} | {x} <- <<dept>>]",null);
      Schema s6h = s6g.applyAddTransformation(rel,
                     new Object[]{"worksin",s6a_person,s6f_dept,"1:1","1:N"},
                     "<<person,dname>>",null);
      Schema s6i = s6h.applyDeleteTransformation(s6a_person_dname,
                     "<<worksin,person,dept>>",null);
      Schema s6j = s6i.applyExtendTransformation(att,
                     new Object[]{s6f_dept,"location","null"},
                     "range(Void,Any)",null);
      Schema s6k = s6j.applyContractTransformation(s6a_person_year,
                     "range(Void,Any)",null);
      return s6k;
  }

  public static Schema transformUS1ToSg(Schema us1) 
    throws NotFoundException,TypeMismatchException,IntegrityException {
      Model er=Model.getModel("er");
      Construct ent=er.getConstruct("entity");
      Construct att=er.getConstruct("attribute");
      Construct rel=er.getConstruct("relationship");
      Construct gen=er.getConstruct("generalisation");

      Schema us1a = us1.applyAddTransformation(ent,new Object[]{"male"},
                     "[{x} | {x,'m'} <- <<person,sex>>]",null);
      Schema us1b = us1a.applyAddTransformation(ent,new Object[]{"female"},
                     "[{x} | {x,'f'} <- <<person,sex>>]",null);

      SchemaObject us1_person = us1.getSchemaObject("<<person>>");
      SchemaObject us1_person_sex = us1.getSchemaObject("<<person,sex>>");
      SchemaObject us1b_male = us1b.getSchemaObject("<<male>>");
      SchemaObject us1b_female = us1b.getSchemaObject("<<female>>");

      Schema us1c = us1b.applyAddTransformation(gen,
                     new Object[]{"sex","total",us1_person,us1b_male,us1b_female},
                     null,null);
      Schema us1d = us1c.applyDeleteTransformation(us1_person_sex,
                     "[{x,'m'} | {x} <- <<male>>] ++ [{x,'f'} | {x} <- <<female>>]",
                     null);

      SchemaObject us1_dept = us1.getSchemaObject("<<dept>>");
      SchemaObject us1_dept_location = us1.getSchemaObject("<<dept,location>>");
      Schema us1e = us1d.applyRenameTransformation(us1_dept_location,
                     new Object[]{us1_dept,"site","null"});
      return us1e;
  }

  public static Schema evolveS6ToS6prime(Schema s6) 
    throws NotFoundException,TypeMismatchException,IntegrityException {
      Model er=Model.getModel("er");
      Construct ent=er.getConstruct("entity");
      Construct att=er.getConstruct("attribute");
      Construct rel=er.getConstruct("relationship");
      Construct gen=er.getConstruct("generalisation");

      // rename <<employee>> to <<staff>> moved to last
      SchemaObject s6_employee = s6.getSchemaObject("<<employee>>");
      SchemaObject s6_employee_dname = s6.getSchemaObject("<<employee,dname>>");
      SchemaObject s6_employee_year = s6.getSchemaObject("<<employee,year>>");
      SchemaObject s6_male = s6.getSchemaObject("<<male>>");
      SchemaObject s6_female = s6.getSchemaObject("<<female>>");

      Schema s6a = s6.applyRenameTransformation(s6_male,new Object[]{"M"});
      Schema s6b = s6a.applyRenameTransformation(s6_female,new Object[]{"F"});

      Schema s6c = s6b.applyAddTransformation(ent,new Object[]{"dept"},
                     "[{x} | {x,y} <- <<employee,dname>>]",null);

      SchemaObject s6c_dept = s6c.getSchemaObject("<<dept>>");

      Schema s6d = s6c.applyAddTransformation(att,new Object[]{s6c_dept,"dname","key"},
                     "[{x,x} | {x} <- <<dept>>]",null);
      Schema s6e = s6d.applyExtendTransformation(att,
                     new Object[]{s6c_dept,"location","null"},
                     "range(Void,Any)",null);
      Schema s6f = s6e.applyAddTransformation(rel,
                     new Object[]{"worksin",s6_employee,s6c_dept,"1:1","1:N"},
                     "<<employee,dname>>",null);
      Schema s6g = s6f.applyDeleteTransformation(s6_employee_dname,
                     "<<worksin,employee,dept>>",null);
      Schema s6h = s6g.applyContractTransformation(s6_employee_year,
                     "range(Void,Any)",null);
      Schema s6i = s6h.applyRenameTransformation(s6_employee,new Object[]{"staff"});

      return s6i;
  }

  // This is a fairly standard check to see if two schemas can be
  // "integrated" via ID transformations
  public static void integrateSchemas(Schema s1,Schema s2) {
      SchemaObject[] aLessB=s1.differenceByName(s2);
      SchemaObject[] bLessA=s2.differenceByName(s1);
      boolean integrationOK=true;

      if (aLessB.length>0) {
	System.out.println(s1.getName()+"-"+s2.getName()+"=");
	for (int i=0;i<aLessB.length;i++)
	  System.out.println("  "+aLessB[i]);
	integrationOK=false;
      }
	  
      if (bLessA.length>0) {
	System.out.println(s2.getName()+"-"+s1.getName()+"=");
	for (int i=0;i<bLessA.length;i++)
	  System.out.println("  "+bLessA[i]);
	integrationOK=false;
      }

      if (integrationOK) {	
	try {
	  Transformation.createIdentTransformation(s1,s2,null,null);
	  System.out.println("Integration of "+s1.getName()+" and "+
			     s2.getName()+" succeeded");
	}
	catch (SchemasNotIdenticalException e) {
	  System.out.println(e.toString());
	  e.printStackTrace(System.out);
	  System.out.println("================");
	  System.out.println("*** Schema " + e.getFromSchema().getName());
	  System.out.println(e.getFromSchema().getAllSchemeNamesString(true, -1, -1));
	  //	  s1.listSchema();
	  System.out.println("----------------");
	  System.out.println("*** Objects not matched");
	  SchemaObject[] fromSo = e.getFromObjectsUnmatched();
	  for (int i=0; i<fromSo.length; i++)
	    System.out.println(fromSo[i].getSchemeNamesString(-1, -1));
	  System.out.println("================");
	  System.out.println("*** Schema " + e.getToSchema().getName());
	  System.out.println(e.getToSchema().getAllSchemeNamesString(true, -1, -1));
	  //	  s2.listSchema();
	  System.out.println("----------------");
	  System.out.println("*** Objects not matched");
	  SchemaObject[] toSo = e.getToObjectsUnmatched();
	  for (int i=0; i<toSo.length; i++)
	    System.out.println(toSo[i].getSchemeNamesString(-1, -1));

	  System.out.println("================");
	}
	catch (Throwable t) {
	  System.out.println("================");
	  System.out.println("*** Schema " + s1.getName());
	  System.out.println(s1.getAllSchemeNamesString(true, -1, -1));
	  System.out.println("----------------");
	  System.out.println("*** Schema " + s2.getName());
	  System.out.println(s2.getAllSchemeNamesString(true, -1, -1));
	  System.out.println("================");
	  System.out.println(t.toString());
	  t.printStackTrace(System.out);
	}
      }
      else
	System.out.println("Integration failed, since it is "+
			   "only allowed if schemas identical");
  }

  public static void main(String[] args) {
    try {
      System.out.println("AutoMed Repository: Transformation and "+
			 "Integration Demo");

      // level 5 prints all the old debug information, 0 makes it silent
      STR.setDebugLevel(0);

      Schema s1=Schema.getSchema("s1");
      Schema s2=Schema.getSchema("s2");
      Schema s3=Schema.getSchema("s3");
      Schema s4=Schema.getSchema("s4");
      Schema s6=Schema.getSchema("s6");

      Schema us1=transformS1ToUS1(s1);
      us1.listSchema();
      Schema us2=transformS2ToUS2(s2); 
      us2.listSchema();
      Schema us3=transformS3ToUS3(s3); 
      us3.listSchema();
      Schema us4=transformS4ToUS4(s4); 
      us4.listSchema();
      Schema us6=transformS6ToUS6(s6); 
      us6.listSchema();
      Schema sg=transformUS1ToSg(us1);
      sg.listSchema();

      Schema ev6=evolveS6ToS6prime(s6);
      ev6.listSchema();

      // create ident trans
      integrateSchemas(us1,us2);
      integrateSchemas(us2,us3); 
      integrateSchemas(us3,us4); 
      integrateSchemas(us4,us6); 
    }
    catch (AutoMedException ame) {
      ame.printStackTrace();
    }
  }
}
