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

/*
 * An application to demonstrate how to setup models (i.e. modelling
 * languages such as ER, Relational, etc) using the
 * Automed API
 */

public class DefineModels {

  /*
   * Define an ER modelling language, with entities, attributes,
   * relationships and generalisations
   */
  public static void createERModel() 
    throws InconsistentException,
    AutoMedException, SQLException, IOException {
    // Zap any existing model called "er"
    if (Model.exists("er")) {
      System.out.println("Removed old er modelling language");
      Model.getModel("er").retract();
    }

    // COMMENT Should "add" match "remove"?
    Model er=Model.createModel("er"); 

    // Create the four constructs this particular ER modelling language

    // ENTITY ...
    // Entities can stand by themselves; hence NODAL
    Construct entity=
      er.createConstruct("entity",Construct.CLASS_NODAL,true);
    // The scheme of an entity contains just the entity name,
    // in the form <<ent_name>> where we use ent_name as name of the
    // node

    // addNodeNameScheme defaults to isKey=true, cardinality=1..1
    // (as construct's class is Nodal, cardinality must be 1..1)
    entity.addNodeNameScheme();

    /*
    entity.createConstructScheme(Scheme.TYPE_NAME, 
				 // Textual name
				 null, // and hence references nothing
				 true, // forms part of the key
				 Scheme.HDM_TYPE_N1, 
				 // the first HDM node
				 1,1 // exactly one of these
				 );
    */
      
    // ATTRIBUTE ...
    // Attributes have values not present in other constructs, but
    // must be associated to other objects; hence LINK_NODAL
    Construct attribute=
      er.createConstruct("attribute",
			 Construct.CLASS_LINK_NODAL,
			 true);
    
    // addEdgeEdgeNameScheme & addReferenceScheme default to isKey=true
    // all default to cardinality 1..1 (EdgeName must be 1..1)
    attribute.addReferenceScheme(entity);
    attribute.addEdgeNameScheme();
    attribute.addConstraintScheme(false); // constraint's not key

    /*
    attribute.createConstructScheme(Scheme.TYPE_CONSTRUCT, entity,
				    true, Scheme.HDM_TYPE_P1, 1,1);
    attribute.createConstructScheme(Scheme.TYPE_NAME, null,
				    true, Scheme.HDM_TYPE_N1, 1,1);
    // constraint on attribute is one of "key", "null" or "notnull"
    attribute.createConstructScheme(Scheme.TYPE_NAME, null,
				    true, Scheme.HDM_TYPE_C, 1,1);
    */

    // Relationships link other existing objects, and hence are LINK
    Construct relationship=
      er.createConstruct("relationship",
			 Construct.CLASS_LINK,
			 true);

    // we want an edge between two existing entities and we
    // want two cardinality constraints, one for each entity
    // We just want the edgeName and entity references to be key
    relationship.addEdgeNameScheme();
    relationship.addReferenceScheme(entity, 2, 2);
    relationship.addConstraintScheme(2, 2, false);

    /*
    relationship.createConstructScheme(Scheme.TYPE_NAME, null,
				       true, 
				       Scheme.HDM_TYPE_N1,1,1);
    relationship.createConstructScheme(Scheme.TYPE_CONSTRUCT,
				       entity, true, 
				       Scheme.HDM_TYPE_P1,
				       2,2);
    relationship.createConstructScheme(Scheme.TYPE_NAME, null,
				       false, 
				       Scheme.HDM_TYPE_C,
				       2,2);
    */

    // Generalisations are constraints on the values of entities, and
    // have not associated values, hence CONSTRAINT
    Construct generalisation=
      er.createConstruct("generalisation",
			 Construct.CLASS_CONSTRAINT,
			 true);
    
    // need labels here as we need to name the generalization and type
    // but there is no corresponding HDM edge or node
    // Note the cardinality of the sub entities ReferenceScheme, as lower>
    // bigger this means lower..n i.e. 2..n in this case
    generalisation.addLabelScheme();
    generalisation.addLabelScheme(false);
    generalisation.addReferenceScheme(entity);
    generalisation.addReferenceScheme(entity, 2, 0, false);
  
    /*
    generalisation.createConstructScheme(Scheme.TYPE_NAME, null,
					 true, 
					 Scheme.HDM_TYPE_C,1,1);
    generalisation.createConstructScheme(Scheme.TYPE_NAME, null,
					 false, 
					 Scheme.HDM_TYPE_C,1,1);
    generalisation.createConstructScheme(Scheme.TYPE_CONSTRUCT,
					 entity, true, 
					 Scheme.HDM_TYPE_C,1,1);
    generalisation.createConstructScheme(Scheme.TYPE_CONSTRUCT,
					 entity, false, 
					 Scheme.HDM_TYPE_C,2,0);
    */
    System.out.println("Created ER Model");
  }

  // TODO: in the above er modelling language definition we have cardinality
  // constraints defined as part of the relationship scheme and a constraint
  // construct to model er generalizations.  In each case where does the 
  // MDR level constraint string (with embedded references to construct
  // references in the scheme) go and in the first case where does the STR
  // level cardinality numbers go?  mb - 25/01/02

  // TODO: or at least think about.  Constants for Construct type are defined
  // in Construct (an MDR level class) while the constants for the scheme
  // argument types are defined in Scheme (an STR level class).  Shouldn't
  // the latter be defined in ConstructScheme?  Looks like it ended up that
  // way simply because the constants were originally called CONTRUCT_*
  // and SCHEME_* when defined in Constants class.

  /*
   * Define an HDM modelling language, using nodes, edges and
   * constraints
   */
  public static void createHDMModel() 
    throws InconsistentException,
    AutoMedException, SQLException, IOException {
    // Zap any existing model called "hdm"
    if (Model.exists("hdm")) {
      System.out.println("Removed old hdm modelling language");
      Model.getModel("hdm").retract();
    }

    // COMMENT Should "add" match "remove"?
    Model hdm=Model.createModel("hdm"); 

    // Create the three constructs of the HDM modelling language

    // NODE ... by definition NODAL
    Construct node=
      hdm.createConstruct("node",Construct.CLASS_NODAL,true);
    // The scheme of an node contains just the node name,
    // in the form <<node_name>> where we use node_name as name of the
    // node
    /*
    node.createConstructScheme(Scheme.TYPE_NAME,   // Textual name
			       null, // and hence references nothing
			       true, // forms part of the key
			       Scheme.HDM_TYPE_N1, // the first HDM node
			       1,1   // exactly one of these
			       );
    */

    node.addNodeNameScheme();

    // EDGE ... by definition a LINK
    /*
    Construct edge=
      hdm.createConstruct("edge",
			 Construct.CLASS_LINK,true);
    Construct nodeOrEdge=
      hdm.createConstruct("node_or_edge",
			 Construct.CLASS_CONSTRAINT,false);

    nodeOrEdge.createConstructScheme(Scheme.TYPE_CONSTRUCT, node,
				       true, 
				       Scheme.HDM_TYPE_C,1,1);
    nodeOrEdge.createConstructScheme(Scheme.TYPE_CONSTRUCT, edge,
				       true, 
				       Scheme.HDM_TYPE_C,1,1);
    edge.createConstructScheme(Scheme.TYPE_NAME, null,
				       true, 
				       Scheme.HDM_TYPE_N1,1,1);
    edge.createConstructScheme(Scheme.TYPE_ALTERNATION, 
			       nodeOrEdge,
			       true, Scheme.HDM_TYPE_P1,
			       2,0);
    */

    Construct edge=
      hdm.createConstruct("edge",
			  Construct.CLASS_LINK,
			  true);
    Construct nodeOrEdge=
      hdm.createConstruct("nodeOrEdge",
			  Construct.CLASS_ALTERNATION,
			  false);
    nodeOrEdge.addReferenceScheme(node);
    nodeOrEdge.addReferenceScheme(edge);
    
    edge.addEdgeNameScheme();
    edge.addReferenceScheme(nodeOrEdge, 2, 0); // 2..0 (lower>upper) => 2..n
    

    // CONSTRAINT ... by definition a CONSTRAINT
    Construct constraint=
      hdm.createConstruct("constraint",
			  Construct.CLASS_CONSTRAINT,
			  true);
    /*
    constraint.createConstructScheme(Scheme.TYPE_NAME, null,
					 true, 
					 Scheme.HDM_TYPE_C,1,1);
    */

    constraint.addConstraintScheme(1, 1, true);
    // TODO: do constraints have a concept of isKey? mb - 01/02/02
    
    System.out.println("Created HDM Model");
  }

  /*
   * Define an relational modelling language, using nodes, edges and
   * constraints
   */
  public static void createRelationalModel() 
    throws InconsistentException,
    AutoMedException, SQLException, IOException {
    // Zap any existing model called "rel"
    if (Model.exists("rel")) {
      System.out.println("Removed old rel modelling language");
      Model.getModel("rel").retract();
    }

    Model rel=Model.createModel("rel"); 

    // TABLE
    Construct table=
      rel.createConstruct("table",
			  Construct.CLASS_NODAL,
			  true);

    table.addNodeNameScheme();

    /*
    table.createConstructScheme(Scheme.TYPE_NAME, 
			       null, true, 
			       Scheme.HDM_TYPE_N1, 
			       1,1);
    */
			       
    // FIELD
    Construct field=
      rel.createConstruct("field",
			 Construct.CLASS_LINK_NODAL,
			  true);

    field.addReferenceScheme(table);
    field.addEdgeNameScheme();  // should be addNodeNameScheme()? mb - 11/04/02
    field.addLabelScheme(); // TODO fudged card until card constraint construct
                            // is done 01/02/02 - mb

    /*
    field.createConstructScheme(Scheme.TYPE_CONSTRUCT,table,
				true,Scheme.HDM_TYPE_P1,1,1);
    field.createConstructScheme(Scheme.TYPE_NAME, null,
				true,Scheme.HDM_TYPE_N1,1,1);
    // describe the position 1,2,3... of the field in the relation
    field.createConstructScheme(Scheme.TYPE_NAME, null,
				true,Scheme.HDM_TYPE_C,1,1);
    */

    // PRIMARY KEY
    Construct primary_key=
      rel.createConstruct("primary_key",
			  Construct.CLASS_CONSTRAINT,
			  true);
    // TODO: where's the generic constraint string? mb - 01/02/02
    
    primary_key.addLabelScheme(1, 1, true);
    primary_key.addReferenceScheme(table, 1, 1, true);
    primary_key.addReferenceScheme(field, 1, 1, true);
    // TODO: shouldn't the above be 1..n ?  mb - 01/02/02

    // Get "uk.ac.ic.doc.automed.reps.AutoRepException: 
    // Can't find value position in construct scheme"
    // if this is not present
    /*
    primary_key.createConstructScheme(Scheme.TYPE_NAME, null,
					 true, 
					 Scheme.HDM_TYPE_C,1,1);
    primary_key.createConstructScheme(Scheme.TYPE_CONSTRUCT,
				      table, true, 
				      Scheme.HDM_TYPE_C,1,1);
    primary_key.createConstructScheme(Scheme.TYPE_CONSTRUCT,
				      field, true, 
				      Scheme.HDM_TYPE_C,1,1);
    */
    Construct foreign_key=
      rel.createConstruct("foreign_key",
			  Construct.CLASS_CONSTRAINT,
			  true);
    // TODO: where's the generic constraint string? mb - 01/02/02

    foreign_key.addLabelScheme(1, 1, true);
    foreign_key.addReferenceScheme(table, 1, 1, true);
    foreign_key.addReferenceScheme(field, 1, 1, true);
    // TODO: shouldn't the above be 1..n ?  mb - 01/02/02
    foreign_key.addReferenceScheme(table, 1, 1, true);
    foreign_key.addReferenceScheme(field, 1, 1, true);
    // TODO: shouldn't the above be 1..n ?  mb - 01/02/02
    // TODO: shouldn't the fields referred to be represented as a Primary Key
    //       - mb 02/05/02

    /*
    foreign_key.createConstructScheme(Scheme.TYPE_NAME, null,
					 true, 
					 Scheme.HDM_TYPE_C,1,1);
    foreign_key.createConstructScheme(Scheme.TYPE_CONSTRUCT,
				      table, true, 
				      Scheme.HDM_TYPE_C,1,1);
    foreign_key.createConstructScheme(Scheme.TYPE_CONSTRUCT,
				      field, true, 
				      Scheme.HDM_TYPE_C,1,1);
    foreign_key.createConstructScheme(Scheme.TYPE_CONSTRUCT,
				      table, true, 
				      Scheme.HDM_TYPE_C,1,1);
    foreign_key.createConstructScheme(Scheme.TYPE_CONSTRUCT,
				      field, true, 
				      Scheme.HDM_TYPE_C,1,1);
    */

    System.out.println("Created Relational Model");
  }

  public static void main(String[] args) {
    try {
      System.out.println("AutoMed Repository: Define Models");
      createERModel();
      createHDMModel();
      createRelationalModel();
    }
    catch (InconsistentException aie) {
      aie.printStackTrace();
    }
    catch (SQLException se) { 
      se.printStackTrace();
    }
    catch (IOException ie) {
      ie.printStackTrace();
    }
    catch (AutoMedException ame) {
      ame.printStackTrace();
    }      
  }
}

























