1   /* 
2    *  Jacl.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   * 
11   *  Hamish Cunningham, 14/03/00
12   *
13   *  $Id: Jacl.java,v 1.8 2001/01/30 14:18:02 hamish Exp $
14   */
15  
16  
17  package gate.util;
18  
19  import java.util.*;
20  import tcl.lang.*;
21  
22  
23  /**
24    * This class provides access to the Jacl Tcl interpreter, and
25    * caters for loading any Tcl scripts that live in the GATE source.
26    * It also serves as examples of how Tcl can be used from Java using
27    * the Jacl library (which is my excuse for those cases where there was
28    * an obvious easier way!).
29    * <P>
30    * Note that all GATE Tcl scripts should be in the namespace "GATE".
31    */
32  public class Jacl
33  {
34    /** Debug flag */
35    private static final boolean DEBUG = false;
36  
37    /** The Tcl interpreter */
38    private Interp interp;
39  
40    /** Construction */
41    public Jacl() { interp = new Interp(); }
42  
43    /** Get the interpreter */
44    public Interp getInterp() { return interp; }
45  
46    /** Local fashion for newlines */
47    private String nl = Strings.getNl();
48  
49    /** Some Tcl code to get us into the gate/src directory (from gate
50      * or a subdir).
51      */
52    String goToGateSrcScript =
53      "set WD [pwd]                                                       "+nl+
54      "if { ! [string match \"*gate*\" $WD] } {                          "+nl+
55      "  error \"not in the gate directories\"                           "+nl+
56      "}                                                                  "+nl+
57      "while { [file tail $WD] != \"gate\" } { cd ..; set WD [pwd] }     "+nl+
58      "cd src                                                             "+nl;
59  
60    /** Some Tcl code to find all the .tcl files under a directory. */
61    private String findTclScript =
62      "set tclFiles [list]                                                "+nl+
63      "                                                                   "+nl+
64      "proc filter { dir } {                                              "+nl+
65      "  global tclFiles                                                  "+nl+
66      "                                                                   "+nl+
67      "  foreach f [glob -nocomplain ${dir}/*] {                          "+nl+
68      "    if [file isdirectory $f] { filter $f }                         "+nl+
69      "    if [string match {*.tcl} $f] {                                 "+nl+
70      "      lappend tclFiles [string range $f 2 end]                     "+nl+
71      "    }                                                              "+nl+
72      "  }                                                                "+nl+
73      "}                                                                  "+nl+
74      "                                                                   "+nl+
75      "filter {.}         ;# do the search                                "+nl+
76      "return $tclFiles   ;# return the result to the interpreter         "+nl;
77  
78    /** Locate any files named .tcl in the directory hierarchy under .
79      * and return a list of them.
80      */
81    public List findScripts()  throws TclException {
82      return findScripts("");
83    } // findScripts()
84  
85    /** Locate any files named .tcl in the directory hierarchy under .
86      * and return a list of them. The prelimScript parameter should be
87      * a non-null string containing Tcl code that will be evaluated before
88      * the finder script runs (so it can be used to change directory,
89      * for e.g.).
90      */
91    public List findScripts(String prelimScript) throws TclException {
92      List scriptPaths = new ArrayList();
93  
94      String finderScript = prelimScript + findTclScript;
95  
96      // "return" in a script evaluated from Java works by throwing an
97      // exception with completion code of TCL.RETURN (so using "set" to
98      // return a value is easier where possible)
99      try {
100       interp.eval(finderScript);
101     } catch(TclException e) {
102       if(e.getCompletionCode() != TCL.RETURN) // wasn't a "return" exception
103         throw(e);
104     }
105 
106     TclObject resultObject = interp.getResult();
107     TclObject pathsArray[] = TclList.getElements(interp, resultObject);
108     for(int i = 0; i < pathsArray.length; i++)
109       scriptPaths.add(pathsArray[i].toString());
110 
111     return scriptPaths;
112   } // findScripts
113 
114   /** Copy scripts from the GATE source tree into the classes dir, so
115     * that they will make it into gate.jar, and so that getResource
116     * (used by Interp.evalResource) will find them.
117     */
118   void copyGateScripts(List scriptPaths) throws TclException {
119     // tcl code to do the copy (move to GATE src dir first)
120     String copyScript = goToGateSrcScript +
121       "foreach f $scriptFilesToCopy {                                   "+nl+
122       "  file copy -force $f ../classes/$f                              "+nl+
123       "}                                                                "+nl;
124 
125     // set a variable containing the list of paths to the scripts
126     TclObject tclPathsList = TclList.newInstance();
127     ListIterator iter = scriptPaths.listIterator();
128     while(iter.hasNext()) {
129       TclObject path = TclString.newInstance((String) iter.next());
130       TclList.append(interp, tclPathsList, path);
131     }
132     interp.setVar("scriptFilesToCopy", tclPathsList, TCL.GLOBAL_ONLY);
133 
134     // evaluate the copy script
135     interp.eval(copyScript);
136   } // copyGateScripts
137 
138   /** Load a list of Tcl scripts. The class loader is used to find the
139     * scripts, so they must be on the class path, preferably in the same
140     * code base as this class. Naming: each path in the list should be
141     * the path to the script relative to the CLASSPATH. So, for e.g., if
142     * you have MyJar.jar on the classpath, and it contains a script housed
143     * in package x.y called z.tcl, the name should be x/y/z.tcl. (The class
144     * loader can then be asked to retrieve /x/y/z.tcl and will find the
145     * file in the jar.)
146     */
147   public void loadScripts(List scriptPaths) throws TclException {
148     ListIterator iter = scriptPaths.listIterator();
149     while(iter.hasNext()) {
150       String path = (String) iter.next();
151       String leadingSlash = ""; 
152 
153       // leading "/" on path needed by classloader
154       if(! path.startsWith("/"))
155         leadingSlash = "/";
156       interp.evalResource(leadingSlash + path);
157     }
158   } // loadScripts(scriptPaths)
159 
160   /** Loads all the scripts in the GATE source. So to get a Tcl interpreter
161     * that's fully initialised with all the GATE Tcl code do:
162     * <PRE>
163     * Jacl jacl = new Jacl();
164     * jacl.loadScripts();
165     * </PRE>
166     */
167   public void loadScripts() throws TclException {
168     listGateScripts();
169     loadScripts(gateScriptsList);
170   } // loadScripts()
171 
172   /** Set up the gateScriptsList member. This uses the ScriptsList.tcl
173     * script, which is built by "make tcl".
174     */
175   void listGateScripts() throws TclException {
176     gateScriptsList = new ArrayList();
177 
178     interp.evalResource("/gate/util/ScriptsList.tcl");
179     TclObject scriptsList = interp.getResult();
180 
181     TclObject pathsArray[] = TclList.getElements(interp, scriptsList);
182     for(int i = 0; i < pathsArray.length; i++)
183       gateScriptsList.add(pathsArray[i].toString());
184   } // listGateScripts
185 
186   /** This is a list of all the .tcl files in the GATE source, used by
187     * the loadScripts() method. 
188     */
189   private List gateScriptsList;
190 
191 } // class Jacl
192 
193 
194 
195 
196 
197 
198