import java.sql.*;
import java.io.*;
import uk.ac.ic.doc.automed.*;
import uk.ac.ic.doc.automed.reps.*;
import uk.ac.ic.doc.automed.editor.*;
import java.util.*;
import uk.ac.ic.doc.automed.p2p.*;
import uk.ac.ic.doc.automed.wrappers.*;
import uk.ac.bbk.dcs.automed.xml.wrappers.*;
import uk.ac.bbk.dcs.automed.xml.*;
import org.w3c.dom.Document;

import uk.ac.bbk.dcs.automed.qproc.iql.ASG;

/**
 * An application to demonstrate how to create schemas in existing models
 */
public class AutoMedInANutshell {
  public static Schema createAndTransformERSchema() 
    throws NotFoundException,TypeMismatchException,IntegrityException {
    Model er=Model.getModel("eer");
    Construct ent=er.getConstruct("entity");
    Construct att=er.getConstruct("attribute");
    Construct rel=er.getConstruct("relationship");
    Construct gen=er.getConstruct("generalisation");

    Schema.retract("er_ct");
    Schema s=Schema.createSchema("er_ct");
    Schema start=s;

    SchemaObject transact=s.createSchemaObject(ent,new Object[]{"transact"});
    System.out.println(SchemaObject.
		       getSchemeNamesString(new Object[]{"transact"}));
    Object sch[]=new Object[]{transact,"tid","key"};
    s.createSchemaObject(att,sch);
    sch[1]="val";
    sch[2]="notnull";
    System.out.println(SchemaObject.
		       getSchemeNamesString(sch));
    SchemaObject t_val=s.createSchemaObject(att,sch);
    sch[1]="value";
    Schema sc=s.applyRenameTransformation(t_val,sch);
    sch[1]="date";
    s.createSchemaObject(att,sch);
    sch[1]="atm_id";
    sch[2]="null";
    SchemaObject trans_atm_id=s.createSchemaObject(att,sch);
    sch[1]="clerk_id";
    sch[2]="null";
    SchemaObject trans_clerk_id=s.createSchemaObject(att,sch);

    SchemaObject customer=s.createSchemaObject(ent,new Object[]{"customer"});
    sch[0]=customer;
    sch[1]="cid";
    sch[2]="key";
    s.createSchemaObject(att,sch);
    sch[1]="name";
    sch[2]="notnull";

    SchemaObject owns=
      s.createSchemaObject(rel,
			   new Object[]{"owns",customer,"0:N",transact,"1:1"});

    s=sc.applyAddTransformation(ent,new Object[]{"clerk_trans"},
    "[{x} | {x,y} <- <<transact,clerk_id>>]",null);
    SchemaObject clerk_trans= 
      Transformation.getTransformation(s,sc).getSchemaObject(s);
    sc=s.applyAddTransformation(att,
				new Object[]{clerk_trans,"clerk_id","notnull"},
				"<<transact,clerk_id>>",
				null);
    sc=sc.applyDeleteTransformation(trans_clerk_id,
				"<<clerk_trans,clerk_id>>",
				    null);

    s=sc.applyAddTransformation(ent,
				new Object[]{"atm_trans"},
				"[{x} | {x,y} <- <<transact,atm_id>>]",
				null);
    SchemaObject atm_trans= 
      Transformation.getTransformation(s,sc).getSchemaObject(s);
    s=s.applyAddTransformation(gen,
       new Object[]{"type","total",transact,clerk_trans,atm_trans},null,null);
    sc=s.applyAddTransformation(ent,
				new Object[]{"atm"},
				"[{y} | {x,y} <- <<transact,atm_id>>]",
				null);
    SchemaObject atm= 
      Transformation.getTransformation(s,sc).getSchemaObject(sc);
    sc=sc.applyAddTransformation(att,
				 new Object[]{atm,"id","key"},
				 "[{y,y} | {x,y} <- <<transact,atm_id>>]",
				 null);
    sc=sc.applyAddTransformation(rel,
				 new Object[]{"on",atm_trans,"1:1",atm,"0:N"},
				 "[{x,y} | {x,y} <- <<transact,atm_id>>]",
				 null);
    sc=sc.applyDeleteTransformation(trans_atm_id,
				    "<<transact,atm_id>>",null);

    System.out.println(Pathway.findShortestPathway(start,sc).toLaTeX());
    
    return sc;
  }

  public static Schema createMDRERSchema(boolean force)
    throws NotFoundException,TypeMismatchException,IntegrityException {
    if (force)
      Schema.retract("mdr");
    Schema s=Schema.createSchema("mdr",true);
    Model er=Model.getModel("er");
    Construct ent=er.getConstruct("entity");
    Construct att=er.getConstruct("attribute");
    Construct rel=er.getConstruct("relationship");

    // Now build the schema
    SchemaObject model=s.createSchemaObject(ent,new Object[]{"model"});
    s.createSchemaObject(att,new Object[] {model,"mid","key"});
    s.createSchemaObject(att,new Object[] {model,"name","notnull"});

    SchemaObject construct=s.createSchemaObject(ent,new Object[]{"construct"});
    s.createSchemaObject(att,new Object[] {construct,"cid","key"});
    s.createSchemaObject(att,new Object[] {construct,"name","notnull"});

    SchemaObject scheme=s.createSchemaObject(ent,new Object[]{"scheme"});

    s.createSchemaObject(rel,new Object[]{"",construct,model,"1:1","0:N"});
    s.createSchemaObject(rel,new Object[]{"",scheme,construct,"1:1","1:N"});

    return s;
  }

  public static void openEditorPanel(String sn) {
    try {
      Schema s=Schema.getSchema(sn);
      SchemaObject se=s.getSchemaObject("<<person>>");
      SchemaObject sa=s.getSchemaObject("<<person,pid>>");
      Gui gui=Gui.openMainWindow();
      gui.createEditorFrame(s);
      DrawableVertex dse=(DrawableVertex)gui.getDrawable(se);
      DrawableVertex dsa=(DrawableVertex)gui.getDrawable(sa);

      System.out.println(dsa.getConnectionPoint(dse.getCentre()));
    }
    catch (NotFoundException e) {
      System.out.println("Schema "+sn+" not found");
      e.printStackTrace();
    }
  }

  public static Schema wrapPublicSchema(String sn)   
    throws IntegrityException, DataSourceException, IOException{
    //AutoMedPeer.autoConnect(); // Ensure connected to P2P network
    AutoMedWrapperFactory wf=new P2PWrapperFactory();

    Schema.retract(sn);
    AutoMedWrapper sw=AutoMedWrapper.
      selectNewAutoMedWrapper("pjm@jdbc:postgresql://localhost/automed",
			      "lab",null,
			      "uk.ac.ic.doc.automed.wrappers.P2PWrapper",
			      "p2p:",sn,wf);
    //+AutoMedPeer.getName()
    return sw.getSchema(sn);
  }

  public static Schema wrapUniversity() 
    throws IntegrityException, DataSourceException {
    AutoMedWrapperFactory wf=new TransactSQLWrapperFactory();
    wf.setFeatures(SQLWrapperFactory.PRIMARY_KEY);
    wf.setFeatures(SQLWrapperFactory.FOREIGN_KEY);
    wf.setFeatures(SQLWrapperFactory.DATA_TYPE);
    wf.setFeatures(SQLWrapperFactory.DATA_SIZE);
    wf.setFeatures(SQLWrapperFactory.NULL_CONSTRAINT);

    Schema.retract("uni_s1_src");
    AutoMedWrapper sw=AutoMedWrapper.
      selectNewAutoMedWrapper("lab","lab",null,
	"com.microsoft.jdbc.sqlserver.SQLServerDriver",
	"jdbc:microsoft:sqlserver://db-ms.doc.ic.ac.uk\\;"+
			      "databaseName=pjm_university1","uni_s1_src",wf);

    return sw.getSchema("uni_s1_src");
  }

  public static void testUniversity()
    throws IntegrityException, DataSourceException, Exception {
    Schema s=Schema.getSchema("uni_s2_src");

    AccessMethod am[]=s.getAccessMethods();

    AutoMedWrapper w=AutoMedWrapper.getAutoMedWrapper(am[0]);
    w.setDebugLevel(3);

    ASG a;
    a=new ASG("[{x1,y1,w} | {x,y,'M',w} <- <<staff,4>>; "+
    		  "{x1,y1,'F',w} <- uni_s1_src:<<staff,4>>; {w} <- <<dept,1>>; "+
    		  "y='Peter';x > 1]");
    a=new ASG("[{y,w} | {1,y,'F',w} <- uni_s1_src:<<staff,4>>]");
    a=new ASG("<<female,1>> ++ <<male,1>>++ <<female,1>>");
    //a=new ASG("<<female,1>>");
    a=new ASG("[{x} | {x}<-<<female,1>>; x>2] ++ [{x} | {x}<-<<male,1>>; x<5]");
    //new ASGExplore().print(a);
    System.out.println(w.executeIQL(a));
  }

  public static Schema wrapTest() 
    throws IntegrityException, DataSourceException, NotFoundException {
    PostgresWrapperFactory wf=new PostgresWrapperFactory();
    wf.setFeatures(SQLWrapperFactory.PRIMARY_KEY);
    wf.setFeatures(SQLWrapperFactory.FOREIGN_KEY);
    wf.setFeatures(SQLWrapperFactory.DATA_TYPE);
    //wf.setFeatures(SQLWrapperFactory.DATA_SIZE);
    //wf.setFeatures(SQLWrapperFactory.NULL_CONSTRAINT);
    AutoMedWrapper sw=
      AutoMedWrapper.selectNewAutoMedWrapper(
                       "pjm","Again11",null,"org.postgresql.Driver",
                       "jdbc:postgresql://localhost/cate",
                       "test_src",wf);

    Schema.retract("test_src");
    sw.getSchema("test_src");
    return wf.createAutoMedSchema(sw,"test");
  }

  public static Schema wrapBankBranch() 
    throws IntegrityException, DataSourceException, NotFoundException {
    TransactSQLWrapper sw=(TransactSQLWrapper)TransactSQLWrapper.
      newAutoMedWrapper("lab","lab",
	"com.microsoft.jdbc.sqlserver.SQLServerDriver",
	"jdbc:microsoft:sqlserver://db-ms.doc.ic.ac.uk\\;databaseName=lab_bank_branch",null,null);
    TransactSQLWrapperFactory wf=(TransactSQLWrapperFactory)
      sw.getAutoMedWrapperFactory();
    wf.setFeatures(SQLWrapperFactory.PRIMARY_KEY);
    wf.setFeatures(SQLWrapperFactory.FOREIGN_KEY);
    wf.setFeatures(SQLWrapperFactory.DATA_TYPE);
    wf.setFeatures(SQLWrapperFactory.DATA_SIZE);
    wf.setFeatures(SQLWrapperFactory.NULL_CONSTRAINT);

    Schema.retract("branch_src");
    sw.getSchema("branch_src");
    return wf.createAutoMedSchema(sw,"branch");
  }

  public static Schema wrapMP3DB1Postgres() 
    throws IntegrityException, DataSourceException, NotFoundException {
    SQLWrapper sw=(SQLWrapper)AutoMedWrapper.
      selectNewAutoMedWrapper("pjm","mustpotout",null,null,
	"jdbc:postgresql://db/automed");
    SQLWrapperFactory wf=(SQLWrapperFactory)sw.getAutoMedWrapperFactory();
    wf.setFeatures(wf.PRIMARY_KEY | wf.FOREIGN_KEY | wf.DATA_TYPE |
		   wf.DATA_SIZE | wf.NULL_CONSTRAINT | wf.SCHEMA_AWARE |
		   wf.SCHEMA_MERGING);
    sw.setOption(sw.OPTION_SCHEMA_NAME,"mp3_db1");

    Schema.retract("mp3_db1_src");
    sw.getSchema("mp3_db1_src");

    return wf.createAutoMedSchema(sw,"branch");
  }

  public static Schema wrapBankBranchPostgres() 
    throws IntegrityException, DataSourceException, NotFoundException {
    SQLWrapper sw=(SQLWrapper)AutoMedWrapper.
      selectNewAutoMedWrapper("lab","lab",null,null,
	"jdbc:postgresql://localhost/lab_bank_branch");
    SQLWrapperFactory wf=(SQLWrapperFactory)sw.getAutoMedWrapperFactory();
    wf.setFeatures(wf.PRIMARY_KEY | wf.FOREIGN_KEY | wf.DATA_TYPE |
		   wf.DATA_SIZE | wf.NULL_CONSTRAINT | wf.SCHEMA_AWARE |
		   wf.SCHEMA_MERGING);
    sw.setOption(sw.OPTION_SCHEMA_NAME,"university1");

    Schema.retract("branch_src");
    sw.getSchema("branch_src");

    return wf.createAutoMedSchema(sw,"branch");
  }

  public static Schema wrapBiomapPostgres() 
    throws IntegrityException, DataSourceException, NotFoundException {
    AutoMedWrapper sw=AutoMedWrapper.
      selectNewAutoMedWrapper("lab","secret",null,null,
	"jdbc:postgresql://localhost/biomap");
    SQLWrapperFactory wf=(SQLWrapperFactory)sw.getAutoMedWrapperFactory();
    wf.setFeatures(wf.PRIMARY_KEY | wf.FOREIGN_KEY | wf.DATA_TYPE |
		   wf.DATA_SIZE | wf.NULL_CONSTRAINT);

    Schema.retract("biomap_src");
    sw.getSchema("biomap_src");

    return wf.createAutoMedSchema(sw,"biomap");
  }

  public static void wrapHalevy() 
    throws IntegrityException, DataSourceException, NotFoundException {
    AutoMedWrapper sw=AutoMedWrapper.
      selectNewAutoMedWrapper("lab","lab",null,null,
	"jdbc:postgresql://localhost/automed");
    SQLWrapperFactory wf=(SQLWrapperFactory)sw.getAutoMedWrapperFactory();
    wf.setFeatures(SQLWrapperFactory.PRIMARY_KEY);
    wf.setFeatures(SQLWrapperFactory.FOREIGN_KEY);
    //wf.setFeatures(SQLWrapperFactory.DATA_TYPE);
    //wf.setFeatures(SQLWrapperFactory.DATA_SIZE);
    //wf.setFeatures(SQLWrapperFactory.NULL_CONSTRAINT);
    //wf.setFeatures(SQLWrapperFactory.SCHEMA_AWARE);
    //wf.setFeatures(SQLWrapperFactory.SCHEMA_MERGING);
    sw.setOption(SQLWrapper.OPTION_SCHEMA_NAME,"sc_s4");
    //System.out.println("Set "+SQLWrapper.OPTION_SCHEMA_NAME+"+staff");
    Schema.retract("sc_s4_src");
    sw.getSchema("sc_s4_src");
    wf.createAutoMedSchema(sw,"sc_s4");

    sw=AutoMedWrapper.
      selectNewAutoMedWrapper("lab","lab",null,null,
	"jdbc:postgresql://localhost/automed",null,wf); 
    sw.setOption(SQLWrapper.OPTION_SCHEMA_NAME,"sc_s2");
    Schema.retract("sc_s2_src");
    sw.getSchema("sc_s2_src");
    wf.createAutoMedSchema(sw,"sc_s2");

    sw=AutoMedWrapper.
      selectNewAutoMedWrapper("lab","lab",null,null,
	"jdbc:postgresql://localhost/automed",null,wf); 

    sw.setOption(SQLWrapper.OPTION_SCHEMA_NAME,"sc_s3");
    Schema.retract("sc_s3_src");
    sw.getSchema("sc_s3_src");
    wf.createAutoMedSchema(sw,"sc_s3");

    sw=AutoMedWrapper.
      selectNewAutoMedWrapper("lab","lab",null,null,
	"jdbc:postgresql://localhost/automed",null,wf); 
    sw.setOption(SQLWrapper.OPTION_SCHEMA_NAME,"sc_s1");
    Schema.retract("sc_s1_src");
    sw.getSchema("sc_s1_src");
    wf.createAutoMedSchema(sw,"sc_s1");
 }


  public static Schema wrapOracle() 
    throws IntegrityException, DataSourceException, NotFoundException {
    AutoMedWrapper sw=AutoMedWrapper.
      selectNewAutoMedWrapper("PETER","secret",null,null,
	"jdbc:oracle:thin:@hermes.dcs.bbk.ac.uk:1521:teach");
    SQLWrapperFactory wf=(SQLWrapperFactory)sw.getAutoMedWrapperFactory();
    wf.setFeatures(SQLWrapperFactory.PRIMARY_KEY);
    wf.setFeatures(SQLWrapperFactory.FOREIGN_KEY);
    wf.setFeatures(SQLWrapperFactory.DATA_TYPE);
    wf.setFeatures(SQLWrapperFactory.DATA_SIZE);
    wf.setFeatures(SQLWrapperFactory.NULL_CONSTRAINT);
    wf.setFeatures(SQLWrapperFactory.SCHEMA_AWARE);
    wf.setFeatures(SQLWrapperFactory.SCHEMA_MERGING);
    sw.setOption(SQLWrapper.OPTION_SCHEMA_NAME,"NMARTIN");

    Schema.retract("bbk_src");
    sw.getSchema("bbk_src");
    return wf.createAutoMedSchema(sw,"bbk");
  }

  public static Schema wrapAnsitest() 
    throws IntegrityException, DataSourceException, NotFoundException {
    AutoMedWrapper sw=AutoMedWrapper.
      selectNewAutoMedWrapper("lab","lab",null,null,
	"jdbc:postgresql://localhost/ansitest");
    SQLWrapperFactory wf=(SQLWrapperFactory)sw.getAutoMedWrapperFactory();
    wf.setFeatures(SQLWrapperFactory.PRIMARY_KEY);
    wf.setFeatures(SQLWrapperFactory.FOREIGN_KEY);
    wf.setFeatures(SQLWrapperFactory.DATA_TYPE);
    wf.setFeatures(SQLWrapperFactory.NULL_CONSTRAINT);

    Schema.retract("ansitest_src");
    sw.getSchema("ansitest_src");
    return wf.createAutoMedSchema(sw,"ansitest");
  }

  public static void transformUniversity() {
  }

  public static Schema extendIntoNewTable(Schema s,String newTableName,
					  String newTablePrimaryKeyName,
					  SchemaObject oldTable,
					  HashMap limitIQL,
					  boolean isExact) 
  throws NotFoundException, TypeMismatchException, IntegrityException {
    Construct table=oldTable.getConstruct();
    Construct column=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_COLUMN);
    Construct primarykey=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_PRIMARY_KEY);
    Construct foreignkey=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_FOREIGN_KEY);

      
    Object[] scheme=oldTable.getSchemeDefinition();
    scheme[0]=newTableName;
    Schema ns=s.applyExtendTransformation(table,scheme,
                oldTable.toString(),null);
    SchemaObject newTable=
      Transformation.getTransformation(s,ns).getSchemaObject(ns);

    SchemaObject[] oldColumns=s.getDependentSchemaObjects(oldTable,column);
    SchemaObject[] newColumns=new SchemaObject[oldColumns.length];
    HashMap newToOld=new HashMap();
    HashMap oldToNew=new HashMap();

    for (int i=0;i<oldColumns.length;i++) {
      Schema os=ns;
      scheme=oldColumns[i].getSchemeDefinition();
      scheme[0]=newTable;
      ns=ns.applyExtendTransformation(column,scheme,
				      oldColumns[i].toString(),null);
      newColumns[i]=
	Transformation.getTransformation(os,ns).getSchemaObject(ns);
      oldToNew.put(oldColumns[i],newColumns[i]);
      newToOld.put(newColumns[i],oldColumns[i]);
    }

    // Now build the restriction (if any) on newTable that gives us the
    // old table. As a side effect, some more columns may be added to newTable
    String restriction=newTable.toString();
    if (limitIQL!=null && limitIQL.size()>0) {
      restriction="[k | k <- "+restriction;
      Iterator i=limitIQL.keySet().iterator();
      int j=0;

      while (i.hasNext()) {
	Object key=i.next();
	String filter=(String)limitIQL.get(key);
	SchemaObject so=(SchemaObject)oldToNew.get(key);
	String attVar="a"+j++;

	// key might instead be a column name, so have to find that!
	if (so==null) {
	  scheme[1]=key;
	  Schema os=ns;
	  ns=ns.applyExtendTransformation(column,scheme,"Void",null);
	  so=Transformation.getTransformation(os,ns).getSchemaObject(ns);
	}
	restriction+="; {k,"+attVar+"} <- "+so+"; "+attVar+filter;
      }

      restriction+="]";
    }

    if (!isExact)
      restriction="Range Void "+restriction;

    // remove old PK if there
    SchemaObject[] oldPK=s.getDependentSchemaObjects(oldTable,primarykey);
    if (oldPK.length>0) {
      scheme=oldPK[0].getSchemeDefinition();

      if (newTablePrimaryKeyName!=null) {
	scheme[0]=newTablePrimaryKeyName;
	scheme[1]=newTable;
	for (int i=2;i<scheme.length;i++)
	  scheme[i]=(SchemaObject)oldToNew.get(scheme[i]);
	ns=ns.applyAddTransformation(primarykey,scheme,null,null);
      }

      ns=ns.applyDeleteTransformation(oldPK[0],null,null);
    }

    // New table complete, now delete old table

    SchemaObject[] oldFKs=s.getDependentSchemaObjects(oldTable,foreignkey);
    for (int i=0;i<oldFKs.length;i++) 
      ns=ns.applyContractTransformation(oldFKs[i],null,null);

    for (int i=0;i<newColumns.length;i++) {
      String query="[{k,a} | k <- "+oldTable+"; {k,a} <- "+newColumns[i]+"]";
      ns=ns.applyContractTransformation(oldColumns[i],query,null);
    }

    ns=ns.applyDeleteTransformation(oldTable,restriction,null);

    return ns;
  }

  /**
   * Change which columns are used to identify a table.
   */
  public static Schema changePrimaryKey(Schema s,
					SchemaObject table,
					SchemaObject[] newKeyColumns) 
  throws NotFoundException, TypeMismatchException, IntegrityException {
    Construct primarykeytype=
      table.getConstruct().getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_PRIMARY_KEY);
    SchemaObject[] primarykey=
      s.getDependentSchemaObjects(table,primarykeytype);

    return s;
  }


  /**
   * Split a table into two tables.
   */
  public static Schema splitTable(Schema s,
				  SchemaObject oldTable,
				  String newTableName1,
				  SchemaObject[] newKeyColumns1,
				  SchemaObject[] newFDColumns1,
				  String newTableName2,
				  SchemaObject[] newKeyColumns2,
				  SchemaObject[] newFDColumns2) 
  throws NotFoundException, TypeMismatchException, IntegrityException {
    Construct table=oldTable.getConstruct();
    Construct primarykey=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_PRIMARY_KEY);
    Construct column=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_COLUMN);
    SchemaObject[] pk=s.getDependentSchemaObjects(oldTable,primarykey);

    if (pk.length==0)
      throw new NotFoundException("Table "+oldTable+
				  " must have a primary key");

    s=s.applyDeleteTransformation(pk[0],null,null);
    
    DeriveTable dt1=new DeriveTable(oldTable,newKeyColumns1,newFDColumns1);
    s=dt1.generateNewTable(s,newTableName1,newTableName1+"_pk");
    DeriveTable dt2=new DeriveTable(oldTable,newKeyColumns2,newFDColumns2);
    s=dt2.generateNewTable(s,newTableName2,newTableName2+"_pk");

    HashSet commonKeys=new HashSet(dt1.pkColSet);
    commonKeys.retainAll(dt2.pkColSet);

    String pkVars=null;
    String pkQuery=null;
    Object[] scheme=pk[0].getSchemeDefinition();

    HashSet oldPKSet=new HashSet();

    for (int i=2;i<scheme.length;i++) {
      oldPKSet.add(scheme[i]);
      String kv="k"+(i-2);

      pkVars=(pkVars==null ? kv : pkVars+","+kv);

      SchemaObject so=(SchemaObject)dt1.newCols.get(scheme[i]);
      if (so!=null) {
	String suffix="{kt1,"+kv+"} <- "+so;

	if (pkQuery==null)
	  pkQuery=suffix;
	else
	  pkQuery+="; "+suffix;
      }

      so=(SchemaObject)dt2.newCols.get(scheme[i]);
      if (so!=null) {
	String suffix="{kt2,"+kv+"} <- "+so;

	if (pkQuery==null)
	  pkQuery=suffix;
	else
	  pkQuery+="; "+suffix;
      }
    }

    String tableVars="{"+pkVars+"}";
    if (scheme.length>3)
      pkVars="{"+pkVars+"}";

    SchemaObject[] cols=s.getDependentSchemaObjects(oldTable,column);

    for (int i=0;i<cols.length;i++) {
      if (!oldPKSet.contains(cols[i])) {
	String query="[{"+pkVars+",a} | "+pkQuery;

	SchemaObject so=(SchemaObject)dt1.newCols.get(cols[i]);
	if (so!=null)
	  query+="; {kt1,a} <- "+so;
      
	so=(SchemaObject)dt2.newCols.get(cols[i]);
	if (so!=null)
	  query+="; {kt2,a} <- "+so;

	s=s.applyDeleteTransformation(cols[i],query+"]",null,null);
      }
    }

    for (int i=2;i<scheme.length;i++) 
      s=s.applyDeleteTransformation((SchemaObject)scheme[i],
				    "[{"+pkVars+",k"+(i-2)+"} | "+
				    tableVars+"<-"+oldTable+"]",null,null);

    s=s.applyDeleteTransformation(oldTable,"["+tableVars+" | "+pkQuery+"]",
				  null,null);

    return s;
  }

  /**
   * Convert a table into Third Normal Form (3NF). If a table breaks
   * 3NF by having some `fdcols' columns fuctionally depend on non-key
   * 'pkcols' columns, we can normalise the schema by placing those
   * functionally dependent columns in a new table, which shares with
   * the old table the 'pkcols'
   * 
   * 
   */
  public static Schema normaliseTable(Schema s,String newTableName,
				      String newTablePrimaryKeyName,
				      String oldTableForeignKeyName,
				      SchemaObject[] pkcols,
				      SchemaObject[] fdcols) 
  throws NotFoundException, TypeMismatchException, IntegrityException {
    return deriveTable(s,newTableName,newTablePrimaryKeyName,
		       oldTableForeignKeyName,pkcols,fdcols,fdcols);
  }

  /**
   * Derive a new table from the contents of an old table. The
   * new table will have some `fdcols' columns fuctionally depend 
   * 'pkcols' columns.
   * 
   * 
   */
  public static Schema deriveTable(Schema s,String newTableName,
				   String newTablePrimaryKeyName,
				   String oldTableForeignKeyName,
				   SchemaObject[] pkcols,
				   SchemaObject[] fdcols,
				   SchemaObject[] delcols) 
  throws NotFoundException, TypeMismatchException, IntegrityException {

    Object[] scheme=pkcols[0].getSchemeDefinition();
    SchemaObject oldTable=(SchemaObject)scheme[0];
    Construct table=oldTable.getConstruct();
    Construct column=((SchemaObject)pkcols[0]).getConstruct();
    Construct primarykey=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_PRIMARY_KEY);
    Construct foreignkey=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_FOREIGN_KEY);

    //SchemaObject[] newpkcols=new SchemaObject[pkcols.length];
    //SchemaObject[] newfdcols=new SchemaObject[fdcols.length];
    HashMap newCols=new HashMap();

    String keyVars="k0";
    String keyQuery="{k,k0} <- "+pkcols[0];
    HashSet pkcolset=new HashSet();

    for (int i=1;i<pkcols.length;i++) {
      keyVars+=",k"+i;
      keyQuery+="; {k,k"+i+"} <- "+pkcols[i];
      pkcolset.add(pkcols[i]);
    }

    Schema ns=s.applyAddTransformation(table,new Object[]{newTableName},
                "distinct [{"+keyVars+"} | "+keyQuery+"]",null);
    SchemaObject newTable=
      Transformation.getTransformation(s,ns).getSchemaObject(ns);
    HashSet joinCols=new HashSet();

    // Something to hold the scheme of the new table's primary key
    Object[] newpk=new Object[2+pkcols.length];
    newpk[0]=newTablePrimaryKeyName;
    newpk[1]=newTable;
    Object[] newfk=new Object[3+(pkcols.length)*2];
    newfk[0]=oldTableForeignKeyName;
    newfk[1]=oldTable;
    newfk[2+pkcols.length]=newTable;
    
    if (pkcols.length>1)
      keyVars="{"+keyVars+"}";
	       
    for (int i=0;i<pkcols.length;i++) {
      Schema os=ns;
      scheme=pkcols[i].getSchemeDefinition();
      scheme[0]=newTable;
      ns=ns.applyAddTransformation(column,scheme,
	"distinct [{"+keyVars+",k"+i+"} | "+keyQuery+"]",null);
      SchemaObject nc=
	Transformation.getTransformation(os,ns).getSchemaObject(ns);
      newCols.put(pkcols[i],nc);
      newpk[i+2]=nc;
      newfk[i+2]=pkcols[i];
      newfk[newpk.length+1+i]=nc;
      joinCols.add(pkcols[i]);
    }

    if (newTablePrimaryKeyName!=null)
      ns=ns.applyAddTransformation(primarykey,newpk,null,null);

    //System.out.println("Construct "+foreignkey);
    //for (int i=0;i<newfk.length;i++) 
    //  System.out.println("newfk["+i+"]="+newfk[i]);

    if (oldTableForeignKeyName!=null)
      ns=ns.applyAddTransformation(foreignkey,newfk,null,null);
							   
    for (int i=0;i<fdcols.length;i++) {
      Schema os=ns;
      scheme=fdcols[i].getSchemeDefinition();
      scheme[0]=newTable;
      ns=ns.applyAddTransformation(column,scheme,
	"distinct [{"+keyVars+",a} | "+keyQuery+"; {k,a} <- "+
			      fdcols[i]+"]",null);
      newCols.put(fdcols[i],
		  Transformation.getTransformation(os,ns).getSchemaObject(ns));
      joinCols.add(fdcols[i]);
    }

    HashSet pkdelcols=new HashSet();
    HashSet fddelcols=new HashSet();
    HashSet condelcols=new HashSet();

    for (int i=0;i<delcols.length;i++) {
      boolean done=false;

      for (int j=0;j<pkcols.length;j++)
	if (delcols[i]==pkcols[j]) {
	  pkdelcols.add(delcols[i]);
	  pkcolset.remove(pkcols[j]);
	  done=true;
	}

      for (int j=0;j<fdcols.length;j++)
	if (delcols[i]==fdcols[j]) {
	  fddelcols.add(delcols[i]);
	  done=true;
	}

      if (!done)
	condelcols.add(delcols[i]);
    }

    // New table now complete, remove columns from old table

    // First just contract those columns not appearing in the new table
    // but which we have been told to delete.
    Iterator i=condelcols.iterator();

    while (i.hasNext())
      ns=ns.applyContractTransformation((SchemaObject)i.next(),null,null);
     
    // Now build a new query that joins the two tables on those columns
    // they still have in common 
    String join="";
    i=joinCols.iterator();
    int j=0;

    while (i.hasNext()) {
      SchemaObject oldpk=(SchemaObject)i.next();
      if (!fddelcols.contains(oldpk)) {
	join+="{ko,k"+j+"} <- "+oldpk+"; {kn,k"+j+"} <- "+
	  newCols.get(oldpk)+";";
	j++;
      }
    }

    // !!! If deleting attributes from old table PK, need to "downsize"
    // the PK now!

    // Now step through the fd cols, and delete them
    i=fddelcols.iterator();
    while (i.hasNext()) {
      SchemaObject delc=(SchemaObject)i.next();

      ns=ns.applyDeleteTransformation(delc,
       "[{ko,a} | "+join+" {kn,a} <- "+newCols.get(delc)+"]",null);
    }

    i=pkdelcols.iterator();
    while (i.hasNext()) {
      SchemaObject delc=(SchemaObject)i.next();

      ns=ns.applyDeleteTransformation(delc,
       "[{ko,a} | "+join+" {kn,a} <- "+newCols.get(delc)+"]",null);
    }

    return ns;
  }

  public static void writePathways(String sch,OutputStream os) 
    throws NotFoundException, IntegrityException, IllegalOperationException {
    Schema sg=Schema.getSchema(sch);

    Schema srcs[]=sg.getAttachedSchemas(Schema.DATA_SOURCE_TYPE);

    for (int i=0;i<srcs.length;i++) {
      srcs[i].write(os);
      try { 
	os.write(0xC);
      }
      catch (IOException ioe) {
      }
      Pathway p=Pathway.findShortestPathway(srcs[i],sg);
      p.write(os);
      try { 
	os.write(0xC);
      }
      catch (IOException ioe) {
      }
    }

    sg.write(os);
  }

  public static void main(String[] args) 
  throws Exception {
     System.out.println("AutoMed In A Nutshell: Examples");

     STR.setDebugLevel(0);
     //wrapAnsitest().listSchema();
     //halevyExample();
     //testUniversity();
     //wrapPublicSchema("university_staff");
     //openEditorPanel("er_s2");
     //wrapMP3DB1Postgres();  
     //createAndTransformERSchema();
     wrapTest();
     //OutputStream os=new FileOutputStream(new File("halevy.str"));
     //writePathways("sc_sg",os);  
  }

  public static void xmlWrapperExample() 
  throws Exception {
    String URI="http://www.doc.ic.ac.uk/~pjm/adb/branch.xml";
    //String URI="file:///homes/pjm/teaching/ic-312/xml/branch.xml";
    //String URI="file:///tmp/branch.xml";
    String sn="xml_bank_branch";
    String domdriver="org.w3c.dom";
    String saxdriver="org.saxproject.driver";
    Schema.retract(sn);

    AutoMedWrapper xw=
      AutoMedWrapper.selectNewAutoMedWrapper("","",null,
					     saxdriver,URI,null,null);
    AutoMedWrapperFactory xwf=xw.getAutoMedWrapperFactory();
    Schema s=xw.getSchema(sn+"_sax");
    s.listSchema();
    xw=AutoMedWrapper.selectNewAutoMedWrapper("","",null,
					      domdriver,URI,null,null);
    xwf=xw.getAutoMedWrapperFactory();
    s=xw.getSchema(sn+"_dom");
    s.listSchema();  }

  public static void halevyExample() 
  throws Exception {
    try {
      System.out.println("AutoMed In A Nutshell: Halevy Example");
    
      wrapHalevy();

      //OutputStream os=
      //	new FileOutputStream(new File("data/sc_global_test.atm"));
      //Schema s1=Schema.getSchema("uni_s1");
      //s1.write(os);

      Schema.retract("sc_su");
      InputStream is=new FileInputStream(new File("data/sc_global_sql_6.atm"));
      Schema sc_su=Schema.read(is,"sc_su");

      Schema.retract("sc_s2_s4");
      is=new FileInputStream(new File("data/sc_union_s2_s4_sql_6.atm"));
      Schema sc_s2_s4=Schema.read(is,"sc_s2_s4");

      Schema sc_s1=Schema.getSchema("sc_s1");
      SchemaObject courseccode=sc_s1.getSchemaObject("<<studies,ccode>>");
      SchemaObject coursetitle=sc_s1.getSchemaObject("<<studies,title>>");
      sc_s1=normaliseTable(sc_s1,"somecourse","course_pk","course_fk",
		     new SchemaObject[]{courseccode},
		     new SchemaObject[]{coursetitle});
      SchemaObject somecourse=sc_s1.getSchemaObject("<<somecourse>>");
      SchemaObject somecoursenumber=
	sc_s1.getSchemaObject("<<somecourse,ccode>>");
      HashMap filter=new HashMap();
      filter.put(somecoursenumber,">=500");
      sc_s1=extendIntoNewTable(sc_s1,"course","course_pk",somecourse,
			       filter,false);
      SchemaObject studsname=sc_s1.getSchemaObject("<<studies,sname>>");
      sc_s1=normaliseTable(sc_s1,"somestudent","student_pk","student_fk",
		     new SchemaObject[]{studsname},
		     new SchemaObject[]{});
      SchemaObject somestudent=sc_s1.getSchemaObject("<<somestudent>>");
      sc_s1=extendIntoNewTable(sc_s1,"student","student_pk",somestudent,
			       null,false);
      SchemaObject studies=sc_s1.getSchemaObject("<<studies>>");
      SchemaObject studiesterm=sc_s1.getSchemaObject("<<studies,term>>");
      filter=new HashMap();
      filter.put(courseccode,">=500");
      filter.put(studiesterm,">='1995-2'");
      sc_s1=extendIntoNewTable(sc_s1,"attempt","attempt_pk",studies,
			       filter,false);
      sc_s1=sc_s1.extendToMatch(sc_su);
      Transformation.createIdentTransformation(sc_s1,sc_su,null,null);


      Schema sc_s2=Schema.getSchema("sc_s2");
      SchemaObject teaching=sc_s2.getSchemaObject("<<teaching>>");
      SchemaObject teachingsname=sc_s2.getSchemaObject("<<teaching,sname>>");
      SchemaObject teachingccode=sc_s2.getSchemaObject("<<teaching,ccode>>");
      SchemaObject teachingterm=sc_s2.getSchemaObject("<<teaching,term>>");
      SchemaObject teachingpname=sc_s2.getSchemaObject("<<teaching,pname>>");
      SchemaObject teachinggid=sc_s2.getSchemaObject("<<teaching,gid>>");
      Construct table=teaching.getConstruct();
      Construct column=teachingsname.getConstruct();
      sc_s2=splitTable(sc_s2,teaching,"someteaches",
	 new SchemaObject[]{teachingpname,teachingccode,teachingterm},
         new SchemaObject[]{},
	 "someattempt",
	 new SchemaObject[]{teachingsname,teachingccode,
			    teachingterm},
         new SchemaObject[]{teachinggid});
      SchemaObject someteaches=sc_s2.getSchemaObject("<<someteaches>>");
      SchemaObject someattempt=sc_s2.getSchemaObject("<<someattempt>>");
      SchemaObject sasname=sc_s2.getSchemaObject("<<someattempt,sname>>");
      SchemaObject sagid=sc_s2.getSchemaObject("<<someattempt,gid>>");
      sc_s2=normaliseTable(sc_s2,"somestudent","student_pk","student_fk",
			   new SchemaObject[]{sasname},
			   new SchemaObject[]{sagid});
      somestudent=sc_s2.getSchemaObject("<<somestudent>>");

      sc_s2=extendIntoNewTable(sc_s2,"teaches","teaches_pk",someteaches,
			       null,false);
      sc_s2=extendIntoNewTable(sc_s2,"attempt","attempt_pk",
			       someattempt,null,false);
      sc_s2=extendIntoNewTable(sc_s2,"student","student_pk",
			       somestudent,null,false);

      sc_s2=sc_s2.extendToMatch(sc_s2_s4);
      Transformation.createIdentTransformation(sc_s2,sc_s2_s4,null,null);

      Schema sc_s4=Schema.getSchema("sc_s4");
      SchemaObject profpname=sc_s4.getSchemaObject("<<staff,pname>>");
      sc_s4=normaliseTable(sc_s4,"someprof","prof_pk","studies_fk",
		     new SchemaObject[]{profpname},
		     new SchemaObject[]{});
      SchemaObject someprof=sc_s4.getSchemaObject("<<someprof>>");
      sc_s4=extendIntoNewTable(sc_s4,"prof","prof_pk",someprof,null,false);

      SchemaObject staffccode=sc_s4.getSchemaObject("<<staff,ccode>>");
      SchemaObject stafftitle=sc_s4.getSchemaObject("<<staff,title>>");
      sc_s4=normaliseTable(sc_s4,"somecourse","course_pk",null,
		     new SchemaObject[]{staffccode},
		     new SchemaObject[]{stafftitle});
      somecourse=sc_s4.getSchemaObject("<<somecourse>>");
      sc_s4=extendIntoNewTable(sc_s4,"course","course_pk",somecourse,
			       null,false);

      SchemaObject staff=sc_s4.getSchemaObject("<<staff>>");
      SchemaObject staffterm=sc_s4.getSchemaObject("<<staff,term>>");
      filter=new HashMap();
      filter.put(staffterm,"<='1997-2'");
      sc_s4=extendIntoNewTable(sc_s4,"teaches","teaches_pk",staff,
			       filter,false);
      sc_s4=sc_s4.extendToMatch(sc_s2_s4);
      Transformation.createIdentTransformation(sc_s4,sc_s2_s4,null,null);

      // Poistion union schema neatly under pathways
      int x=(sc_s2.getXPos()+sc_s4.getXPos())/2;
      int y=Math.max(sc_s2.getYPos(),sc_s4.getYPos())+25;
      sc_s2_s4.setPosition(x,y);

      SchemaObject cl=sc_s2_s4.getSchemaObject("<<class>>");
      SchemaObject cldname=sc_s2_s4.getSchemaObject("<<class,dname>>");
      SchemaObject clgid=sc_s2_s4.getSchemaObject("<<class,gid>>");
      SchemaObject clpname=sc_s2_s4.getSchemaObject("<<class,pname>>");
      SchemaObject studentgid=sc_s2_s4.getSchemaObject("<<student,gid>>");
      SchemaObject studentdcode=sc_s2_s4.getSchemaObject("<<student,gid>>");

      // Now define join student-degree-dept from student-class-dept
      sc_s2_s4=sc_s2_s4.
	applyAddTransformation(table,new Object[]{"degree"},
			       "[{dc} | {s,dc} <- <<student,dcode>>]",null);

      SchemaObject degree=sc_s2_s4.getSchemaObject("<<degree>>");

      sc_s2_s4=sc_s2_s4.
	applyAddTransformation(column,new Object[]{degree,"dcode"},
			       "[{dc} | {s,dc} <- <<student,dcode>>]",null);
      sc_s2_s4=sc_s2_s4.
	applyAddTransformation(column,new Object[]{degree,"dname"},
	  "[{dc} | {s,dc} <- <<student,dcode>>; {s,g} <- <<student,gid>>; "+
	  "{g,dn} <- <<class,dname>>]",null);      

      // Remove all constraints from class
      SchemaObject deps[]=cl.getDependentSchemaObjects();
      for (int i=deps.length-1;i>=0;i--) {
	if (deps[i].getConstruct().getConstructClass()==
	    Construct.CLASS_CONSTRAINT && sc_s2_s4.contains(deps[i]))
	  sc_s2_s4=sc_s2_s4.applyContractTransformation(deps[i],null,null);
      }

      // Now define join student-class-dept from student-degree-dept 
      sc_s2_s4=sc_s2_s4.applyContractTransformation(clpname,null,null);
      sc_s2_s4=sc_s2_s4.
	applyDeleteTransformation(cldname,
	   "[{g,dn} | {s,g} <- <<student,gid>>; {s,dc} <- <<student,dcode>>; "+
	   "{dc,dn} <- <<degree,dname>>]",
	null);
      sc_s2_s4=sc_s2_s4.
	applyDeleteTransformation(clgid,
				  "[{g,g} | {s,g} <- <<student,gid>>]",null);
      sc_s2_s4=sc_s2_s4.
	applyDeleteTransformation(cl,"[{g} | {s,g} <- <<student,gid>>]",null);
      sc_s2_s4=sc_s2_s4.applyContractTransformation(studentgid,null,null);
      sc_s2_s4=sc_s2_s4.extendToMatch(sc_su);
      Transformation.createIdentTransformation(sc_s2_s4,sc_su,null,null);
      
      // ******************** SC_S3 ************************

      Schema sc_s3=Schema.getSchema("sc_s3");
      SchemaObject ug=sc_s3.getSchemaObject("<<ug>>");
      sc_s3=extendIntoNewTable(sc_s3,"student","student_pk",
			       ug,null,false);
      courseccode=
	sc_s3.getSchemaObject("<<registration,ccode>>");
      sc_s3=normaliseTable(sc_s3,"somecourse","course_pk","course_fk",
		     new SchemaObject[]{courseccode},
		     new SchemaObject[]{});
      somecourse=sc_s3.getSchemaObject("<<somecourse>>");
      sc_s3=extendIntoNewTable(sc_s3,"course","course_pk",somecourse,
			       null,false);
      SchemaObject registration=sc_s3.getSchemaObject("<<registration>>");
      SchemaObject registrationcwmark=
	sc_s3.getSchemaObject("<<registration,cwmark>>");
      SchemaObject registrationsname=
	sc_s3.getSchemaObject("<<registration,sname>>");
      
      // 30/1/05 This is wrong, since different key
      //filter=new HashMap();
      //filter.put("term","<='1994-2'");
      //sc_s3=extendIntoNewTable(sc_s3,"attempt",null,registration,
      //			       filter,false);
      Schema old=sc_s3;
      sc_s3=sc_s3.applyExtendTransformation(table,new Object[]{"attempt"},
	null,null);
      SchemaObject attempt=Transformation.getTransformation(old,sc_s3).
	getSchemaObject(sc_s3);
      sc_s3=sc_s3.
	applyExtendTransformation(column,new Object[]{attempt,"sname"},
	null,null);
      sc_s3=sc_s3.
	applyExtendTransformation(column,new Object[]{attempt,"ccode"},
	null,null);
      sc_s3=sc_s3.
	applyAddTransformation(column,new Object[]{attempt,"cwmark"},
        "[{k,cw} | {k,s} <- <<attempt,sname>>; "+
	"{k,cn} <- <<attempt,ccode>>; "+	
	"{{s,cn},cw} <- <<registration,cwmark>>]",null);
      sc_s3=sc_s3.
	applyExtendTransformation(column,new Object[]{attempt,"term"},
	null,null);

      // First remove all constraints from registration
      deps=registration.getDependentSchemaObjects();
      for (int i=deps.length-1;i>=0;i--) {
	if (deps[i].getConstruct().getConstructClass()==
	    Construct.CLASS_CONSTRAINT && sc_s3.contains(deps[i]))
	  sc_s3=sc_s3.applyContractTransformation(deps[i],null,null);
      }

      // Should be "Range Void [" really
      String pre="Range Void [";
      String qs=" | {k,s} <- <<attempt,sname>>; "+
	"{k,cn} <- <<attempt,ccode>>; "+
	"{k,q} <- <<attempt,term>>; q>='1994-1']";
      sc_s3=sc_s3.
	applyContractTransformation(registrationsname,
				    pre+"{{s,cn},s}"+qs,null,null);
      sc_s3=sc_s3.
	applyContractTransformation(courseccode,
				    pre+"{{s,cn},cn}"+qs,null,null);
      sc_s3=sc_s3.
	applyDeleteTransformation(registrationcwmark,"[{{s,cn},cw} | "+
				  "{k,s} <- <<attempt,sname>>; "+
				  "{k,cn} <- <<attempt,ccode>>; "+
				  "{k,cw} <- <<attempt,cwmark>>]",
				  null,null);
      sc_s3=sc_s3.
	applyContractTransformation(registration,pre+"{s,cn}"+qs,null,null);
      
      sc_s3=sc_s3.extendToMatch(sc_su);
      Transformation.createIdentTransformation(sc_s3,sc_su,null,null);
      
      //Schema u1=Schema.read(is,"sc_su");
      //u1.listSchema();

      //Schema uni=wrapUniversity();
      //uni.listSchema();

      //Schema bb=wrapBankBranch();
      //bb.listSchema();

      //Schema bbk=wrapOracle();
      //bbk.listSchema();

      //Schema mdr=createMDRERSchema(true);
      //mdr.listSchema();

      x=(sc_s1.getXPos()+sc_s2_s4.getXPos()+sc_s3.getXPos())/3;
      y=Math.max(sc_s1.getYPos(),
		 Math.max(sc_s2_s4.getYPos(),sc_s3.getYPos()))+25;
      sc_su.setPosition(x,y);

      attempt=sc_su.getSchemaObject("<<attempt>>");
      Schema sc_su1=sc_su.
	applyAddTransformation(column,new Object[]{attempt,"mark"},
			       "[{k,cw+em} | "+
			       "{k,em} <- <<attempt,exmark>>; "+
			       "{k,cw} <- <<attempt,cwmark>>]",
			       null);
      SchemaObject rcw=sc_su.getSchemaObject("<<attempt,cwmark>>");
      SchemaObject rex=sc_su.getSchemaObject("<<attempt,exmark>>");
      Schema sc_su2=sc_su1.
	applyDeleteTransformation(rcw,"[{k,m-ex} | "+
				  "{k,em} <- <<attempt,exmark>>; "+
				  "{k,m} <- <<attempt,mark>>]",
				  null);
      sc_su2.setXPos(sc_su2.getXPos()-50);
      Schema sc_sg=sc_su2.applyContractTransformation(rex,null,null,"sc_sg");
      sc_su2=sc_su1.
	applyDeleteTransformation(rex,"[{k,m-cw} | "+
				  "{k,cw} <- <<attempt,cwmark>>; "+
				  "{k,m} <- <<attempt,mark>>]",
				  null);      
      sc_su2.setXPos(sc_su2.getXPos()+50);
      sc_su2=sc_su2.applyContractTransformation(rcw,null,null);
      sc_sg.applyIdentTransformation(sc_su2);
    }
    catch (AutoMedException ame) {
      ame.printStackTrace();
    }
  }
}

class DeriveTable {
  public SchemaObject oldTable;
  SchemaObject[] pkCols;
  HashSet pkColSet=new HashSet();
  SchemaObject[] fdCols;
  public SchemaObject newTable;
  public HashMap newCols=new HashMap(); // map old columns to new columns
  public HashMap oldCols=new HashMap(); // map new columns to old columns
  Construct table;
  Construct column;
  Construct primarykey;
  Construct foreignkey;

  public DeriveTable(SchemaObject oldTable,
		     SchemaObject[] pkCols,SchemaObject[] fdCols) 
    throws NotFoundException {
    this.oldTable=oldTable;
    this.pkCols=pkCols;
    this.fdCols=fdCols;
    table=oldTable.getConstruct();
    column=((SchemaObject)pkCols[0]).getConstruct();
    primarykey=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_PRIMARY_KEY);
    foreignkey=
      table.getModel().getConstruct(SQLWrapperFactory.
				    AUTOMED_SQL_MODEL_FOREIGN_KEY);
  }

  public Schema generateNewTable(Schema baseSchema,
				 String newTableName, String newPKName) 
    throws NotFoundException, TypeMismatchException, IntegrityException {
    String keyVars="k0";
    String keyQuery="{k,k0} <- "+pkCols[0];

    for (int i=1;i<pkCols.length;i++) {
      keyVars+=",k"+i;
      keyQuery+="; {k,k"+i+"} <- "+pkCols[i];
      pkColSet.add(pkCols[i]);
    }
      
    Schema ns=baseSchema.applyAddTransformation(table,
      new Object[]{newTableName},
      "distinct [{"+keyVars+"} | "+keyQuery+"]",null);
    SchemaObject newTable=
      Transformation.getTransformation(baseSchema,ns).getSchemaObject(ns);

    Object[] newpk=new Object[2+pkCols.length];
    newpk[0]=newPKName;
    newpk[1]=newTable;
      
    if (pkCols.length>1)
      keyVars="{"+keyVars+"}";
      
    for (int i=0;i<pkCols.length;i++) {
      Schema os=ns;
      Object[] scheme=pkCols[i].getSchemeDefinition();
      scheme[0]=newTable;
      ns=ns.applyAddTransformation(column,scheme,
				   "distinct [{"+keyVars+",k"+i+"} | "+keyQuery+"]",null);
      SchemaObject nc=
	Transformation.getTransformation(os,ns).getSchemaObject(ns);
      newCols.put(pkCols[i],nc);
      oldCols.put(nc,pkCols[i]);
      newpk[i+2]=nc;
    }
   
    // Might not have an explicit primary key construct ...
    if (newPKName!=null && primarykey!=null) 
      ns=ns.applyAddTransformation(primarykey,newpk,null,null);

    for (int i=0;i<fdCols.length;i++) {
      Schema os=ns;
      Object[] scheme=fdCols[i].getSchemeDefinition();
      scheme[0]=newTable;
      ns=ns.applyAddTransformation(column,scheme,
				   "distinct [{"+keyVars+",a} | "+keyQuery+"; {k,a} <- "+
				   fdCols[i]+"]",null);

      SchemaObject nc=
	Transformation.getTransformation(os,ns).getSchemaObject(ns);

      newCols.put(fdCols[i],nc);
      oldCols.put(nc,fdCols[i]);		
    }

    return ns;
  }
}
