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_p2p {
  public static Schema transformS1ToS2(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");

      SchemaObject s1_person = s1.getSchemaObject("<<person>>");

      Schema s1a = s1.applyRenameTransformation(s1_person,new Object[]{"employee"});

      SchemaObject s1a_employee = s1a.getSchemaObject("<<employee>>");

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

      SchemaObject s1b_division =
                  Transformation.getTransformation(s1a,s1b).getSchemaObject(s1b);

      Schema s1c = s1b.applyExtendTransformation(att,
                        new Object[]{s1b_division,"dname","key"},
                        "range(Void,Any)",null);
      Schema s1d = s1c.applyExtendTransformation(att,
                        new Object[]{s1b_division,"location","null"},
                        "range(Void,Any)",null);


      Schema s1e = s1d.applyExtendTransformation(rel,
                        new Object[]{"worksin",s1a_employee,s1b_division,"1:1","1:N"},
                        "range(Void,Any)",null);

      return s1e;
}

public static Schema transformS2ToS3(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.applyAddTransformation(ent, new Object[]{"male"},
                     "[{x} | {x,'m'} <- <<person,sex>>]",null);
      Schema s2c = s2b.applyAddTransformation(ent, new Object[]{"female"},
                     "[{x} | {x,'f'} <- <<person,sex>>]",null);

      SchemaObject s2c_person = s2c.getSchemaObject("<<person>>");
      SchemaObject s2c_person_sex = s2c.getSchemaObject("<<person,sex>>");
      SchemaObject s2c_male = s2c.getSchemaObject("<<male>>");
      SchemaObject s2c_female = s2c.getSchemaObject("<<female>>");

      Schema s2d = s2c.applyAddTransformation(gen,
                     new Object[]{"sex","total",s2c_person,s2c_male,s2c_female},
                     null,null);
      Schema s2e = s2d.applyDeleteTransformation(s2c_person_sex,
                     "[{x,'m'} | {x} <- <<male>>] ++ [{x,'f'} | {x} <- <<female>>]",
                     null);
      Schema s2f = s2e.applyAddTransformation(att,
                     new Object[]{s2c_person,"dname","notnull"},
                     "<<worksin,person,division>>",
                     null);

      SchemaObject s2f_worksin = s2f.getSchemaObject("<<worksin,person,division>>");
      SchemaObject s2f_division = s2f.getSchemaObject("<<division>>");
      SchemaObject s2f_division_dname = s2f.getSchemaObject("<<division,dname>>");
      SchemaObject s2f_division_location = s2f.getSchemaObject("<<division,location>>");

      Schema s2g = s2f.applyDeleteTransformation(s2f_worksin,
                     "<<person,dname>>",null);
      Schema s2h = s2g.applyContractTransformation(s2f_division_location,
                     "range(Void,Any)",null);
      Schema s2i = s2h.applyDeleteTransformation(s2f_division_dname,
                     "[{x,x} | {y,x} <- <<person,dname>>]",
                     null);
      Schema s2j = s2i.applyDeleteTransformation(s2f_division,
                     "[{x} | {y,x} <- <<person,dname>>]",null);

      Schema s2k = s2j.applyExtendTransformation(att,
                     new Object[]{s2c_person,"year","notnull"},
                     "range(Void,Any)",null);

      return s2k;
  }

  public static Schema transformS3ToS4(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_person_dname = s3.getSchemaObject("<<person,dname>>");
      SchemaObject s3_person_year = s3.getSchemaObject("<<person,year>>");
      SchemaObject s3_sex = s3.getSchemaObject("<<sex,total,person,male,female>>");
      SchemaObject s3_female = s3.getSchemaObject("<<female>>");
      SchemaObject s3_male = s3.getSchemaObject("<<male>>");

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

      SchemaObject s3a_dept = s3a.getSchemaObject("<<dept>>");

      Schema s3b = s3a.applyAddTransformation(att,
                     new Object[]{s3a_dept,"dname","key"},
                     "[{x,x} | {x} <- <<dept>>]", null);
      Schema s3c = s3b.applyContractTransformation(s3_sex, "range(Void,Any)", null);
      Schema s3d = s3c.applyContractTransformation(s3_male, "range(Void,Any)", null);
      Schema s3e = s3d.applyContractTransformation(s3_female, "range(Void,Any)", null);
      Schema s3f = s3e.applyContractTransformation(s3_person_year, "range(Void,Any)", null);
      Schema s3g = s3f.applyExtendTransformation(ent, new Object[]{"branch"},
                     "range(Void,Any)", null);

      SchemaObject s3g_branch = s3g.getSchemaObject("<<branch>>");

      Schema s3h = s3g.applyExtendTransformation(att,
                     new Object[]{s3g_branch,"bname","key"},
                     "range(Void,Any)", null);
      Schema s3i = s3h.applyExtendTransformation(rel,
                     new Object[]{"basedat",s3_person,s3g_branch,"1:1","1:N"},
                     "range(Void,Any)", null);
      Schema s3j = s3i.applyExtendTransformation(rel,
                     new Object[]{"within",s3g_branch,s3a_dept,"1:1","1:N"},
                     "range(Void,Any)", null);
      Schema s3k = s3j.applyDeleteTransformation(s3_person_dname,
                     "[{x,y} | {x,z} <- <<basedat,person,branch>>; {z,y} <- <<within,branch,dept>>]",
                     null);

      return s3k;
  }

  // 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 s1_s2equiv=transformS1ToS2(s1);
      s1_s2equiv.listSchema();
      Schema s2_s3equiv=transformS2ToS3(s2); 
      s2_s3equiv.listSchema();
      Schema s3_s4equiv=transformS3ToS4(s3); 
      s3_s4equiv.listSchema();

      // create ident trans
      integrateSchemas(s1_s2equiv,s2);
      integrateSchemas(s2_s3equiv,s3); 
      integrateSchemas(s3_s4equiv,s4); 
    }
    catch (AutoMedException ame) {
      ame.printStackTrace();
    }
  }
}
