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