(*****************************************************************************
 * su4sml --- a SML repository for managing (Secure)UML/OCL models
 *             http://projects.brucker.ch/su4sml/
 *                                                                            
 * xmi_idtable.sml --- 
 * This file is part of su4sml.
 *
 * Copyright (c) 2005-2007 ETH Zurich, Switzerland
 *               2008-2009 Achim D. Brucker, Germany
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 *     * Neither the name of the copyright holders nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************)
(* $Id$ *)

structure Xmi_IDTable = 
struct
open Rep_Helper

(**
 * special keys:
 *    - aends introduced to handle the implicit aend of association 
 *      classes for their association: 
 *      (xmiid of the association class)_aend
 *    - associations split off from their association class:
 *      (xmiid of the association class)_association
 * 
 * 
 * 
 * 
 *)
datatype HashTableEntry = Package of Rep_OclType.Path
		        | Type of (Rep_OclType.OclType * 
				   (*(XMI.AssociationEnd list)* *)
				   (Rep_OclType.Path list)*  (* associations *)
				   (Rep_OclType.Path)*       (* association of an association class *)
				   XMI.Classifier * 
				   (XMI.ActivityGraph list)) 
			| Association of (Rep_OclType.Path * 
					 XMI.Association)
                        | Generalization of (string * string) 
			| Constraint of XMI.Constraint 
		        | Stereotype of string
		        | Variable of XMI.VariableDeclaration 
			| Attribute of Rep_OclType.Path
			| AssociationEnd of (Rep_OclType.Path *
					    XMI.AssociationEnd)
			| Operation of Rep_OclType.Path
			| State of XMI.StateVertex
			| Transition of XMI.Transition 
		        | Dependency of XMI.Dependency
		        | TagDefinition of string
		        | ClassifierInState of string 
		        | Event of XMI.Event
			| UniqueName of int (* xmiid=-1;for naming when a name is missing *)
 
fun next_unique_name t:string=
    let
	val number = case valOf (HashTable.find t "-1")
		      of UniqueName x => x
		       | _ => raise Option
    in
	(HashTable.insert t ("-1",UniqueName (number+1));
	 Int.toString number
	)
    end 
    handle Option => Logger.error ("expected UniqueName to be defined in table")
 
fun find_tagdefinition t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of TagDefinition x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected TagDefinition "^xmiid^" in table")

fun find_state t xmiid = 
    (case valOf (HashTable.find t xmiid) 
      of State x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected State "^xmiid^" in table")

fun find_event t xmiid = 
    (case valOf (HashTable.find t xmiid) 
      of Event x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected Event "^xmiid^" in table")

fun find_transition t xmiid = 
    (case valOf (HashTable.find t xmiid) 
      of Transition x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected Transition "^xmiid^" in table")

fun find_dependency t xmiid = 
    (case valOf (HashTable.find t xmiid) 
      of Dependency x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected Dependency "^xmiid^" in table")

fun find_generalization t xmiid = 
    (case valOf (HashTable.find t xmiid) 
      of Generalization x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected Generalization "^xmiid^" in table")

fun find_stereotype t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Stereotype x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected Stereotype "^xmiid^" in table")

fun find_attribute t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Attribute x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected Attribute "^xmiid^" in table")

fun find_operation t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Operation x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected Operation "^xmiid^" in table")

fun find_type t xmiid = 
    (case valOf (HashTable.find t xmiid) 
      of Type x  => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected Type "^xmiid^" in table (find_type)")

fun find_assoc t xmiid = 
    (case valOf (HashTable.find t xmiid) 
      of (Association(path,assoc))  => assoc
       | _                => raise Option) 
    handle Option => Logger.error ("expected Type "^xmiid^" in table (find_assocs)")

fun find_aend t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of (AssociationEnd(path,aend)) => aend
       | _                => raise Option) 
    handle Option => Logger.error ("expected AssociationEnd "^xmiid^" in table (find_aend)")

fun find_variable_dec t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Variable x => x
       | _                => raise Option) 
    handle Option => Logger.error ("expected VariableDeclaration "^xmiid^" in table")

fun find_parent t xmiid = #2 (find_generalization t xmiid)

fun find_package t xmiid  = 
    (case valOf (HashTable.find t xmiid) 
      of Package path => path
       | _                => raise Option) 
    handle Option => Logger.error ("expected Path "^xmiid^" in table")
					
fun path_of_classifier (Rep_OclType.Classifier x) = x
  | path_of_classifier _ = Logger.error ("path_of_classifier called on non-Classifier argument")

fun find_constraint t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Constraint c => c
       | _                => raise Option) 
    handle Option => Logger.error ("expected Constraint "^xmiid^" in table")

fun find_associationend t xmiid  = 
    (case valOf (HashTable.find t xmiid) 
      of AssociationEnd (path,ae) => ae
       | _                   => raise Option) 
    handle Option => Logger.error ("expected AssociationEnd "^xmiid^" in table")


fun path_of_association t xmiid  = 
    (case valOf (HashTable.find t xmiid) 
      of Association (path,ae) => path
       | _                   => raise Option) 
    handle Option => Logger.error ("expected Association "^xmiid^" in table") 

fun path_of_associationend t xmiid  = 
    (case valOf (HashTable.find t xmiid) 
      of AssociationEnd (path,ae) => path
       | _                   => raise Option) 
    handle Option => Logger.error ("expected AssociationEnd "^xmiid^" in table") 


fun find_association t xmiid  = 
    (case valOf (HashTable.find t xmiid) 
      of Association (p,a) => a
       | _                   => raise Option) 
    handle Option => Logger.error ("expected Association "^xmiid^" in table")


fun find_association_of_associationend t xmiid =
    let 
	val aend = find_associationend t xmiid
	val assoc_xmiid = #association aend
    in
	find_association t assoc_xmiid
    end

fun find_classifier_associations t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Type(_,assocs,_,_,_) => assocs
       | _                   => raise Option) 
    handle Option => Logger.error ("expected Association "^xmiid^" in table")
		     
fun filter_exists t cs = 
	filter (fn x => Option.isSome (HashTable.find t x)) cs 

fun filter_precondition t  cs 
  = filter (fn x => let val constraint = find_constraint t x
			val name = #name constraint
			val constr_type_ref = #constraint_type constraint
			val constr_type_name = find_stereotype t constr_type_ref
		    in 
			constr_type_name = "pre"
		    end) cs

fun filter_postcondition t cs
  = filter (fn x => let val constraint = find_constraint t x
			val name = #name constraint
			val constr_type_ref = #constraint_type constraint
			val constr_type_name = find_stereotype t constr_type_ref
		    in 
			constr_type_name = "post"
		    end) cs

fun filter_bodyconstraint t cs
  = filter (fn x => let val constraint = find_constraint t x
			val name = #name constraint
			val constr_type_ref = #constraint_type constraint
			val constr_type_name = find_stereotype t constr_type_ref
		    in 
			constr_type_name = "body"
		    end) cs

fun find_classifier_entries t xmiid =
    let
	val _ = Logger.debug2 "Xmi_IDTable.find_classifier_entries \n"
	val res = (case valOf (HashTable.find t xmiid) of 
		      Type c => c
		    | _  => raise Option
		  ) handle Option => Logger.error ("expected Classifier "^xmiid^" in table (in find_classifer_entries)")
	val _ = Logger.debug2 "end Xmi_IDTable.find_classifiers_entries \n"
    in
	res
    end
									  
fun find_classifier t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Type (_,_,_,c,_) => c
       | _                => raise Option) 
    handle Option =>  Logger.error ("expected Classifier with xmiid '"^xmiid^"' in table (in find_classifer)")
	 

fun exists_classifier t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Type (_,_,_,_,_) => true
       | _                => false) 

fun find_classifierInState_classifier t cis_id =
    (case valOf (HashTable.find t cis_id)
      of ClassifierInState c => find_classifier t c
       | Type (_,_,_,c,_)      => c
	   | _                   => raise Option)
    handle Option =>  Logger.error ("expected ClassifierInState "
				       ^cis_id^" in table")
fun find_association_of_associationclass t xmiid =
    (case valOf (HashTable.find t xmiid) 
      of Type (_,_,ac,_,_) => ac
       | _                => raise Option) 
    handle Option => Logger.error ("expected associationclass "^xmiid^" in table (in find_association_of_associationclass)") 

fun find_activity_graph_of t xmiid = 
    (case valOf (HashTable.find t xmiid) 
      of Type (_,_,_,_,ag) => ag
       | _                => raise Option) 
    handle Option => Logger.error ("expected Classifier "^xmiid^" in table (in find_activity_graph_of)")

			
fun find_classifier_type t xmiid
  = let val ocltype = case valOf (HashTable.find t xmiid) of (Type (x,xs,ac,_,_)) => x
							   | _  => raise Option
    in 
	case ocltype of Rep_OclType.Integer      => ocltype
		      | Rep_OclType.String       => ocltype
		      | Rep_OclType.Real         => ocltype
		      | Rep_OclType.Boolean      => ocltype
		      | Rep_OclType.Classifier x => ocltype
		      | Rep_OclType.OclVoid      => ocltype
		      | Rep_OclType.OclAny       => ocltype
		      | Rep_OclType.DummyT       => ocltype
		      | Rep_OclType.Collection (Rep_OclType.Classifier [x]) => Rep_OclType.Collection (find_classifier_type t x)
		      | Rep_OclType.Sequence   (Rep_OclType.Classifier [x]) => Rep_OclType.Sequence (find_classifier_type t x)
		      | Rep_OclType.Set        (Rep_OclType.Classifier [x]) => Rep_OclType.Set (find_classifier_type t x)
		      | Rep_OclType.Bag        (Rep_OclType.Classifier [x]) => Rep_OclType.Bag (find_classifier_type t x)
		      | Rep_OclType.OrderedSet (Rep_OclType.Classifier [x]) => Rep_OclType.OrderedSet (find_classifier_type t x)
		      (* <ArgoUML-Hack> *)
		      | Rep_OclType.Collection (Rep_OclType.Classifier c) => 
			let val _ = Logger.warn ("ArgoUML workaround for supporting collection types ...")
			in Rep_OclType.Collection (Rep_OclType.Classifier c) end
		      | Rep_OclType.Sequence   (Rep_OclType.Classifier c) => 
			let val _ = Logger.warn ("ArgoUML workaround for supporting collection types ...")
			in Rep_OclType.Sequence (Rep_OclType.Classifier c) end 
		      | Rep_OclType.Set        (Rep_OclType.Classifier c) => 
			let val _ = Logger.warn ("ArgoUML workaround for supporting collection types ...")
			in Rep_OclType.Set (Rep_OclType.Classifier c) end
		      | Rep_OclType.Bag        (Rep_OclType.Classifier c) => 
			let val _ = Logger.warn ("ArgoUML workaround for supporting collection types ...")
			in Rep_OclType.Bag (Rep_OclType.Classifier c) end 
		      | Rep_OclType.OrderedSet (Rep_OclType.Classifier c) => 
			let val _ = Logger.warn ("ArgoUML workaround for supporting collection types ...")
			in Rep_OclType.OrderedSet (Rep_OclType.Classifier c) end 
		      (* </ArgoUML-Hack> *)
		      | _ => Logger.error ("unexpected Classifier-Type "^xmiid^" in table")
	 end
	handle Option => Logger.error ("expected Classifier with xmiid '"^xmiid^"' in table (in find_classifer_type)")		    

fun find_association_path t xmiid = 
    case valOf (HashTable.find t xmiid) of (Association (x,xs)) => x
					 | _  => raise Option
    handle Option => Logger.error ("expected Association "^xmiid^" in table (in find_association_path)")	    

fun find_association_name t xmiid = 
    case valOf (HashTable.find t xmiid) of (Association (_,{xmiid,name,connection})) => name
					 | _  => raise Option
    handle Option => Logger.error ("expected Association "^xmiid^" in table (in find_association_name)")

fun insert_constraint table (c:XMI.Constraint) =
    HashTable.insert table (#xmiid c, Constraint c)

fun insert_variable_dec table (v:XMI.VariableDeclaration) =
    HashTable.insert table (#xmiid v, Variable v)

fun insert_stereotype table (s:XMI.Stereotype) =
    HashTable.insert table (#xmiid s, Stereotype (#name s))

fun insert_generalization table (g:XMI.Generalization) =
    HashTable.insert table (#xmiid g, Generalization (#child_id g, #parent_id g))

fun insert_attribute table path_prefix (a:XMI.Attribute) =
    HashTable.insert table (#xmiid a, Attribute (path_prefix @ [#name a]))

fun insert_operation table path_prefix (a:XMI.Operation) =
    HashTable.insert table (#xmiid a, Operation (path_prefix @ [#name a]))

fun add_aend table xmiid (aend:Rep.associationend) = () (* FIX *)

fun insert_state table (XMI.CompositeState st) =
    (List.app (insert_state table) (#subvertex st);
    HashTable.insert table (#xmiid st, State (XMI.CompositeState st)))
  | insert_state table (XMI.SubactivityState st) =
    (List.app (insert_state table) (#subvertex st);
    HashTable.insert table (#xmiid st, State (XMI.SubactivityState st)))
  | insert_state table (st:XMI.StateVertex) = 
    HashTable.insert table (XMI.state_xmiid_of st, State st)
    
fun insert_event table (e as XMI.CallEvent ev) =
    HashTable.insert table (#xmiid ev, Event e)
  | insert_event  table (e as XMI.SignalEvent ev) =
    HashTable.insert table (#xmiid ev, Event e)

fun insert_transition table (XMI.mk_Transition trans) =
    HashTable.insert table (#xmiid trans, Transition (XMI.mk_Transition trans))

fun insert_activity_graph table (XMI.mk_ActivityGraph ag) =
    let val context = #contextxmiid ag 
    in  
	(case valOf (HashTable.find table context) 
	  of (Type (c,xs,assocs,ac,ags))  => HashTable.insert 
						 table (context, Type (c,xs,assocs,ac,
XMI.mk_ActivityGraph ag::ags))
	   | _                => raise Option) 
	      handle Option => Logger.error ("expected Type "^context^" in table (insert_activity_graph)");
	List.app (insert_transition table) (#transitions ag);
	insert_state table (#top ag)
    end

fun insert_dependency table dep =
    HashTable.insert table (#xmiid dep, Dependency dep)

fun insert_tagdefinition table (td:XMI.TagDefinition) =
    HashTable.insert table (#xmiid td, TagDefinition (#name td))

fun insert_classifierInState table cls_id cis_id =
    HashTable.insert table (cis_id,ClassifierInState cls_id)

(** insert an association into the hashtable *)
fun insert_association table package_prefix (association:XMI.Association) = 
    let 
	val _ = Logger.debug2 ("Xmi_IDTable.insert_association\n")
	val id      = #xmiid association
	val name    = #name association
	val path    = if (isSome name) then package_prefix@[valOf name]
		      else package_prefix@["association_"^(next_unique_name table)]
	val res = HashTable.insert table (id,Association(path,association))
	val _ = Logger.debug2  ("Xmi_IDTable.insert_association\n")
    in
	res
	
    end


fun insert_classifier table package_prefix class = 
    let val _ = Logger.debug2 ("Xmi_IDTable.insert_classifier\n")
	val id      = XMI.classifier_xmiid_of class
	val name    = XMI.classifier_name_of class
	val path    = package_prefix @ [name]
		      
	fun argoUMLWorkaround name = (* ugly hack *) 
	    let
	      val _ = Logger.warn ("Warning: argument ("^name
			     ^") is not a collection value,\n"
			     ^"assuming ArgoUML workaround for supporting collection types ...")
	      val [colType,elementtype,tail] = String.fields (fn c => c = #"(" orelse c= #")") name
		  handle _ =>  ["","",""]
	    in 
	      if elementtype = "" 
	      then NONE 
	      else SOME (Rep_OclType.Classifier (String.fields (fn c => #"."  = c) elementtype))
	    end  
	    
	val ocltype = if (package_prefix = ["oclLib"]
			  orelse package_prefix = ["UML_OCL"])
		      then if      name = "Integer" then Rep_OclType.Integer
			   else if name = "Boolean" then Rep_OclType.Boolean
			   else if name = "String"  then Rep_OclType.String
			   else if name = "Real"    then Rep_OclType.Real
			   else if name = "OclVoid" then Rep_OclType.OclVoid
                           (* this shouldn't be necessary: *) 
			   else if name = "Void"    then Rep_OclType.OclVoid 
			   else if name = "OclAny"  then Rep_OclType.OclAny
			   (* now this is really ugly... *)
			   else if String.isPrefix "Collection(" name 
			   then Rep_OclType.Collection (Rep_OclType.Classifier [
							XMI.classifier_elementtype_of class])
				handle ex => case argoUMLWorkaround name of 
					      SOME c => Rep_OclType.Collection (c)
					    | NONE => raise ex
			   else if String.isPrefix "Sequence("   name 
			   then Rep_OclType.Sequence   (Rep_OclType.Classifier [
							XMI.classifier_elementtype_of class])
				handle ex => case argoUMLWorkaround name of 
					      SOME c => Rep_OclType.Sequence c
					    | NONE => raise ex
			   else if String.isPrefix "Set("        name 
			   then Rep_OclType.Set        (Rep_OclType.Classifier [
							XMI.classifier_elementtype_of class])
				handle ex => case argoUMLWorkaround name of 
					      SOME c => Rep_OclType.Set c
					    | NONE => raise ex
			   else if String.isPrefix "Bag("        name 
			   then Rep_OclType.Bag        (Rep_OclType.Classifier [
							XMI.classifier_elementtype_of class])
				handle ex => case argoUMLWorkaround name of 
					      SOME c => Rep_OclType.Bag c
					    | NONE => raise ex
			   else if String.isPrefix "OrderedSet(" name 
			   then Rep_OclType.OrderedSet (Rep_OclType.Classifier [
							XMI.classifier_elementtype_of class])
				handle ex => case argoUMLWorkaround name of 
					      SOME c => Rep_OclType.OrderedSet c
					    | NONE => raise ex
			   else Logger.error ("didn't recognize ocltype "^name) 
		      else Rep_OclType.Classifier path
	(* This function is called before the associations are handled, *)
	(* so we do not have to take care of them now...                *)
	val assocs = nil 
	(* The association denoted by an association class, however, is *)
	(* known at this time *)
	val (acAssoc,acPath) = case class of
				   XMI.AssociationClass c => 
				   let 
				       val acAssocName =  name^"_association_"^(next_unique_name table)
				       val acAssoc = {xmiid = id^"_association",
						      name = SOME acAssocName,
						      connection = #connection c}:XMI.Association
				   in
				       ([acAssoc],package_prefix @[acAssocName])
				   end
				 | _ => ([],[])
	val ag    = nil
	val res = 
	    let
		val _ = HashTable.insert table (id,Type (ocltype,assocs,acPath,class,ag))
	    in
		(case class 
		  of XMI.Class c => (Logger.debug2 "insert_classifier: Class\n";
				     List.app (insert_attribute table path) (#attributes c);
				     List.app (insert_operation table path) (#operations c);
				     List.app (insert_classifierInState table id) (#classifierInState c);
				     ())
		   | XMI.Primitive c => (List.app (insert_operation table path) (#operations c); ())
		   | XMI.Enumeration c => (List.app (insert_operation table path) (#operations c); ())
		   | XMI.Interface c => (List.app (insert_operation table path) (#operations c); ())
		   | XMI.Collection c => (List.app (insert_operation table path) (#operations c); ())
		   | XMI.Sequence c => (List.app (insert_operation table path) (#operations c); ())
		   | XMI.Set c => (List.app (insert_operation table path) (#operations c); ())
		   | XMI.Bag c => (List.app (insert_operation table path) (#operations c); ())
		   | XMI.OrderedSet c => (List.app (insert_operation table path) (#operations c); ())
		   | XMI.AssociationClass c => (Logger.debug2 "insert_classifier: AssociationClass\n";
						List.app (insert_attribute table path) (#attributes c);   
						List.app (insert_operation table path) (#operations c);              
						List.app (insert_classifierInState table id) [];
						insert_association table package_prefix (hd acAssoc);
						()
					       )
		   | _ => ()
		)
	    end
	val _ = Logger.debug2 ("end Xmi_IDTable.insert_classifier \n")
    in
	res
    end
    
    
(* recursively insert mapping of xmi.id's to model elements into Hashtable *)
fun insert_package table package_prefix (XMI.Package p) =
    let
	val _ = Logger.debug2 ("Xmi_IDTable.insert_package\n")
	val full_name = package_prefix @ [#name p]
	val res = 
	    let
		val _ = List.app (insert_generalization table)           (#generalizations p)
		val _ = Logger.debug3 "insert_package: constraints\n"
		val _ = List.app (insert_constraint     table)           (#constraints p)
		val _ = Logger.debug3 "insert_package: stereotypes\n"
		val _ = List.app (insert_stereotype     table)           (#stereotypes p)
		val _ = Logger.debug3 "insert_package: classifiers\n"
		val _ = List.app (insert_classifier     table full_name) (#classifiers p)
		val _ = Logger.debug3 "insert_package: associations\n"
		val _ = List.app (insert_association    table full_name) (#associations p)
		val _ = Logger.debug3 "insert_package: packages\n"
		val _ = List.app (insert_package        table full_name) (#packages p)
		val _ = Logger.debug3 "insert_package: activity_graphs\n"
		val _ = List.app (insert_activity_graph table)           (#activity_graphs p)
		val _ = Logger.debug3 "insert_package: dependencies\n"
		val _ = List.app (insert_dependency     table)           (#dependencies p)
		val _ = Logger.debug3 "insert_package: tag defenitions\n"
		val _ = List.app (insert_tagdefinition  table)           (#tag_definitions p)
		val _ = Logger.debug3 "insert_package: events\n"
		val _ = List.app (insert_event          table)           (#events p)
		val _ = Logger.debug3 "insert_package: insert package\n"
	    in
		HashTable.insert table (#xmiid p,Package full_name)
	    end
	val _ = Logger.debug2 ("Xmi_IDTable.insert_package \n")
    in
	res
    end 
    
(* We do not want the name of the model to be part of the package hierarchy, *)
(* therefore we handle the top-level model seperately                        *)
fun insert_model table (XMI.Package p) = 
    let 
	val _ = Logger.debug2 ("insert_model\n")
	val full_name = nil
	val res = 
	    let
		val _ = List.app (insert_generalization table)           (#generalizations p)
		val _ = List.app (insert_constraint     table)           (#constraints p)
		val _ = List.app (insert_stereotype     table)           (#stereotypes p)
		val _ = List.app (insert_classifier     table full_name) (#classifiers p)
		val _ = List.app (insert_association    table full_name) (#associations p)
		val _ = List.app (insert_package        table full_name) (#packages p)
		val _ = List.app (insert_activity_graph table)           (#activity_graphs p)
		val _ = List.app (insert_dependency     table)           (#dependencies p)
		val _ = List.app (insert_tagdefinition  table)           (#tag_definitions p)
		val _ = List.app (insert_event          table)           (#events p)
	    in
		HashTable.insert table (#xmiid p,Package full_name)
	    end
	val _ = Logger.debug2 ("Xmi_IDTable.insert_model\n")
    in
	res
    end 


fun initial_state_of table (XMI.mk_ActivityGraph ag) = 
    valOf (List.find (fn (XMI.PseudoState{kind,...})  => kind = XMI.initial
		       | _ => false) 
		     (XMI.state_subvertices_of (#top ag)))


fun successor_states_of table (st:XMI.StateVertex) =
    map  ((find_state table) o  XMI.transition_target_of o 
	  (find_transition table)) 
	 (XMI.state_outgoing_trans_of st)   

(* returns a list of tag-value pairs *)
fun class_taggedvalues_of table (XMI.Class c) = 
    map (fn x => (find_tagdefinition table (#tag_type x),#dataValue x)) 
	(#taggedValue c)
  | class_taggedvalues_of table (XMI.AssociationClass c) = 
    map (fn x => (find_tagdefinition table (#tag_type x),#dataValue x)) 
	(#taggedValue c)
  | class_taggedvalues_of table (XMI.Primitive c) =
    map (fn x => (find_tagdefinition table (#tag_type x),#dataValue x))
        (#taggedValue c)
  | class_taggedvalues_of table _ = Logger.error "in class_taggedvalues_of: \
                                          \argument doesn't support tagged values"
	

(* returns the value of the given tag *)
fun class_taggedvalue_of table tag (XMI.Class c) =
    Option.map #2 ((List.find (fn (x,y) => x=tag)) 
		       (class_taggedvalues_of table (XMI.Class c)))
  | class_taggedvalue_of table tag (XMI.AssociationClass c) =
    Option.map #2 ((List.find (fn (x,y) => x=tag)) 
		       (class_taggedvalues_of table (XMI.AssociationClass c)))
  | class_taggedvalue_of table tag (XMI.Primitive c) =
    Option.map #2 ((List.find (fn (x,y) => x=tag))
                       (class_taggedvalues_of table (XMI.Primitive c)))
  | class_taggedvalue_of table tag _ = Logger.error "in class_taggedvalues_of: \
                                             \argument doesn't support tagged values"
	

(* returns a list of tag-value pairs *)
fun attribute_taggedvalues_of table (a:XMI.Attribute) = 
    map (fn x => (find_tagdefinition table (#tag_type x),#dataValue x)) 
	(#taggedValue a)
 

(* returns the value of the given tag *)
fun attribute_taggedvalue_of table tag (a:XMI.Attribute) =
    Option.map #2 ((List.find (fn (x,y) => x=tag)) 
		       (attribute_taggedvalues_of table a))
	

(* returns a list of tag-value pairs *)
fun state_taggedvalues_of table st = 
    map (fn x => (find_tagdefinition table (#tag_type x),#dataValue x)) 
	(XMI.state_taggedValue_of st)

(* returns the value of the given tag *)
fun state_taggedvalue_of table tag st =
    Option.map #2 ((List.find (fn (x,y) => x=tag)) 
		       (state_taggedvalues_of table st))
    

(* returns a list of tag-value pairs *)
fun package_taggedvalues_of table (XMI.Package p) = 
    map (fn x => (find_tagdefinition table (#tag_type x),#dataValue x)) 
	(#taggedValue p)

(* returns the value of the given tag *)
fun package_taggedvalue_of table tag (XMI.Package p) =
    Option.map #2 ((List.find (fn (x,y) => x=tag)) 
		       (package_taggedvalues_of table (XMI.Package p)))
    

(* check whether a class has the given stereotype *)
fun classifier_has_stereotype t st c = 
    List.exists (fn x => (find_stereotype t x) = st) 
		(XMI.classifier_stereotype_of c)


fun fix_associationend t (assoc_path:Rep_OclType.Path) (aend:XMI.AssociationEnd) =
    let	
	val id = #xmiid aend
	val participant_id = #participant_id aend
	val (cls_type,assocs,assoc,cls,ags)  = find_classifier_entries t participant_id
        val name = Option.getOpt(#name aend,
				 (StringHandling.uncapitalize o XMI.classifier_name_of o
				  find_classifier t) participant_id)
    in 
	((if not (List.exists (fn x => x = assoc_path) assocs) then 
	    (* add the association to the participant *)
	    (HashTable.insert t (participant_id, Type (cls_type,assoc_path::assocs,assoc,cls,ags)))
	  else 
	      ());
         HashTable.insert t (#xmiid aend, AssociationEnd (List.concat [assoc_path, [name]], aend)))
    end

(** 
 * This handles only regular associations. An association class's belonging
 * association is handled at insert_classifier. However, the normal classes
 * that part of that association class's association still need to add the 
 * association to their list of associations.
 * Traverse the list of aends and update all listed classifiers with the path
 * of the current association.
 *)
fun fix_association t (assoc as {xmiid,name,connection}:XMI.Association) =
    let
	fun updateTableEntry t pathUpdate typeId =
	    let
		val (oclType,associations,association,class,ag) = find_type t typeId
		(* prefix version refused to work *)
		fun existsPath assocList = List.exists (fn x => x=pathUpdate) assocList
		val modifiedAssociations = if(existsPath associations)
					   then
					       associations
					   else
					       pathUpdate::associations					       					       
	    in
		HashTable.insert t (typeId,Type(oclType,modifiedAssociations,association,class,ag))
	    end
	val assocPath = find_association_path t xmiid
	val participantIds = map (#participant_id) connection
    in
	List.app (updateTableEntry t assocPath) participantIds
    end

fun fixAssociationFromAsssociationClass table (XMI.AssociationClass{xmiid,
								    ...}) =
    let
	val association = find_association table (xmiid ^ "_association")
    in
	fix_association table association
    end

(** 
 * Handel the broken association references, meaning updating the 
 * association path list for classes and association classes.
 * Since classifiers do not store their belonging aends, we traverse the 
 * associations: 
 *         We skip the aends and instead process the set of associations. For
 *         each association, we traverse the connection part and search for 
 *         the classifier listed as participant_id. Then we simply add the 
 *         association path to the found classifier.
 * For the classifiers part of an association class's class, no association
 * construct is present in the package p, while those constructs are already
 * in the hashtable. To traverse them as well, we extract all association
 * classes and reconstruct the associations.
 *)
fun fix_associations t (XMI.Package p) =
    let
	    val associationClasses = filter (fn (XMI.AssociationClass x) => true
					                              | _ => false) (#classifiers p)
    in
	    (* All association ends are stored in associations, so we will 
	     * traverse them an update affected Classes and AssociationClasses *)
	     (List.app (fix_associations t) (#packages p);
	      List.app (fix_association t) (#associations p);
	      List.app (fixAssociationFromAsssociationClass t) associationClasses)
    end


end
