/**
 * Looking up package and class objects in the repository.
 *
 * Use the strategies in this module to get references to packages, classes, 
 * methods, fields, and all other code.
 *
 * @author Martin Bravenboer <martin@cs.uu.nl>
 */
module dryad/model/repository
imports
  dryad/components
  dryad/model/array-class
  dryad/model/classpath  
  dryad/model/class  
  dryad/model/compilation-unit
  dryad/model/package
  dryad/simplify/Sanity

/**
 * Lookup packages
 */
strategies

  /**
   * Returns the root package.
   *
   * @type _ -> Package Object
   */
  get-root-package =
    table-hashtable
    ; (hashtable-get(|RootPackage()) <+ init-root-package)
    
  /**
   * Checks if the specified PackageName exists as a package in the repository.
   *
   * @type PackageName -> PackageName
   */
  is-package-observable =
    where(lookup-package)

  /**
   * Looks up the package object for a given PackageName.
   * The package is not created if it does not exist.
   *
   * @type  PackageName -> Package Object
   */
  lookup-package = 
    ?pkg@PackageName(<map(?Id(<id>))>)
    ; lookup-package

  /**
   * Looks up the package object for a given list of strings.
   * The package is not created if it does not exist.
   *  
   * @type List(String) -> Package Object
   */    
  lookup-package =
    is-list
    ; ?names
    ; get-root-package
    ; get-subsubpackage(|names)
    
  /**
   * Looks up or adds the package object for a given PackageName.
   * The package is added if it does not exist.
   *
   * @type PackageName -> Package Object
   */
  lookup-or-add-package =
    ?PackageName(<map(?Id(<id>))>) => names
    ; lookup-or-add-package
    
  /**
   * Looks up or adds the package object for a given list of strings.
   * The package is added if it does not exist.
   *  
   * @type List(String) -> Package Object
   */    
  lookup-or-add-package =
    is-list
    ; ?names
    ; get-root-package
    ; get-or-add-subsubpackage(|names)

  /**
   * Looks up the package object and logs an error if is not available.
   */
  log-lookup-package =
    lookup-package <+ log(|Error(), "package not found", <id>); fail


/**
 * Lookup classes
 */
strategies

  /**
   * Looks up the class object for the given TypeName.
   * This alternative implements toplevel classes.   
   *
   * @type  TypeName -> Class Object
   */
  lookup-class =
    ?TypeName(pkg@PackageName(_), Id(simplename))
    ; <lookup-package> pkg
    ; get-toplevel-class(|simplename)
    
  /**
   * Looks up the class object for the given TypeName.
   * This alternative implements member classes.
   *
   * @type  TypeName -> Class Object
   */    
  lookup-class =
    ?TypeName(type@TypeName(_, _), Id(simplename))
    ; <lookup-class> type
    ; get-member-type(|simplename)

  /**
   * Looks up the class object for the given class type.
   *
   * @type  Type -> Class Object
   */
  lookup-class =
    ?ClassType(<id>, _); lookup-class

  /**
   * Looks up the class object for the given interface type.  
   *
   * @type  Type -> Class Object
   */
  lookup-class =
    ?InterfaceType(<id>, _); lookup-class
    
  /**
   * Looks up the class object for the given array type.  
   *
   * @type  Type -> ArrayClass Object
   */
  lookup-class =
    ?ArrayType(<get-array-class>)

  /**
   * Looks up the class object for the given TypeName or Type and logs an error if is not available.
   */
  log-lookup-class =
    lookup-class <+ log(|Error(), "class not found", <try(pp-java-string)>); fail
    
/**
 * Lookup methods
 */
strategies

  /**
   * Lookup a method by its canonical name.
   *
   * @type MethodName -> Method Object
   */
  lookup-method =
    ?MethodName(tn@TypeName(_, _), Id(x), param-types, return-type)
    ; <lookup-class> tn
    ; get-declared-methods(|x)
    ; retain-all(
        where(get-formal-parameter-types => param-types)
      ; where(get-return-type => return-type)
      )
    ; ?[<id>]
    

/**
 * Lookup constructors
 */
strategies

  /**
   * Lookup a constructor by its canonical name.
   *
   * @type ConstructorName -> Constructor Object
   */
  lookup-constructor =
    ?ConstructorName(tn@TypeName(_, _), param-types)
    ; <lookup-class> tn
    ; get-declared-constructors
    ; retain-all(
        where(get-formal-parameter-types => param-types)
      )
    ; ?[<id>]

/**
 * Lookup type variables of methods.
 */
strategies

  lookup-type-parameter =
    ?TypeVar(tn@TypeName(_, _), Id(simplename))
    ; <lookup-class> tn
    ; get-formal-type-parameter(|simplename)

  /**
   * @todo Use the new canonical name for methods (DRY-223)
   * @todo Lookup of type param does not support overloading. This is not acceptable.
   */
  lookup-type-parameter =
    ?TypeVar(mn@MethodName(tn, Id(simplemethodname)), Id(simplename))
    ; <lookup-class> tn
    ; get-declared-methods(|simplemethodname)
    ; ?[_]
    ; get-formal-type-parameter(|simplename)

/**
 * Registration
 */
strategies

  /**
   * Initializes the root package object.
   *
   * @type _ -> Package Object
   */    
  init-root-package =
    new-package(|None(), "/") => rootpkg
    ; table-hashtable
    ; hashtable-put(|RootPackage(), rootpkg)
    ; !rootpkg

  /**
   * Register the classes defined in a list of source files in the Dryad repository.
   *  
   * @type List(FILE) -> List(CompilationUnit Object)
   */
  define-source-files =
    parse-java
    ; map(define-compilation-unit)
    ; dryad-reclassify
    
  /**
   * Registers all classes of a source compilation unit in the Dryad repository.
   *
   * @type CompilationUnit -> CompilationUnit Object
   */
  define-compilation-unit =
    ?CompilationUnit(_, _, _)
    ; dryad-simplify-sanity => ast
    ; new-compilation-unit
    ; set-ast(|ast)

  /**
   * Destroy the stateful observable data structures.
   *
   * @todo Not all fields are in the global field table: some use local hashtables.
   *       These local hashtables should be dropped and real objects should be used.
   *       (suggested by Eelco Visser)
   */
  destroy-repository =
    where(
      classes_get-fieldtbl
      ; hashtable-clear
      ; table-hashtable
      ; hashtable-remove(|RootPackage())
    )

/**
 * Keys for Dryad repository.
 */
signature
  constructors
    RootPackage : TableTableKey