001: /*
002: * This is free software, licensed under the Gnu Public License (GPL)
003: * get a copy from <http://www.gnu.org/licenses/gpl.html>
004: *
005: * author: Henner Zeller <H.Zeller@acm.org>
006: */
007: package henplus.commands;
008:
009: import henplus.HenPlus;
010: import henplus.Interruptable;
011: import henplus.SQLSession;
012: import henplus.AbstractCommand;
013: import henplus.CommandDispatcher;
014: import henplus.SigIntHandler;
015:
016: import java.util.StringTokenizer;
017: import java.util.Iterator;
018: import java.util.Set;
019: import java.util.Stack;
020: import java.util.HashSet;
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileReader;
024: import java.io.BufferedReader;
025: import java.io.IOException;
026: import java.io.InputStreamReader;
027:
028: /**
029: * The Load command loads scripts; it implemnts the
030: * commands 'load', 'start', '@' and '@@'.
031: */
032: public class LoadCommand extends AbstractCommand implements
033: Interruptable {
034: /**
035: * to determine recursively loaded files, we remember all open files.
036: */
037: private final Set/*<File>*/_openFiles;
038:
039: /**
040: * current working directory stack - to always open files relative to
041: * the currently open file.
042: */
043: private final Stack/*<File>*/_cwdStack;
044:
045: private volatile boolean _running;
046:
047: /**
048: * returns the command-strings this command can handle.
049: */
050: public String[] getCommandList() {
051: return new String[] { "load", "start", "@", "@@" };
052: }
053:
054: public LoadCommand() {
055: _openFiles = new HashSet();
056: _cwdStack = new Stack();
057: try {
058: File cwd = new File(".");
059: _cwdStack.push(cwd.getCanonicalFile());
060: } catch (IOException e) {
061: HenPlus.msg().println(
062: "cannot determine current working directory: " + e);
063: }
064: }
065:
066: /**
067: * filename completion by default.
068: */
069: public Iterator complete(CommandDispatcher disp,
070: String partialCommand, String lastWord) {
071: return new FileCompletionIterator(partialCommand, lastWord);
072: }
073:
074: /**
075: * open a file. If this is a relative filename, then open according
076: * to current working directory.
077: *
078: * @param filename the filename to open
079: * @return a file that represents the correct file name.
080: */
081: File openFile(String filename) {
082: File f = new File(filename);
083: if (!f.isAbsolute()) {
084: f = new File((File) _cwdStack.peek(), filename);
085: }
086: return f;
087: }
088:
089: /**
090: * execute the command given.
091: */
092: public int execute(SQLSession session, String cmd, String param) {
093: StringTokenizer st = new StringTokenizer(param);
094: int argc = st.countTokens();
095: if (argc < 1) {
096: return SYNTAX_ERROR;
097: }
098: final HenPlus henplus = HenPlus.getInstance();
099: while (st.hasMoreElements()) {
100: int commandCount = 0;
101: final String filename = (String) st.nextElement();
102: final long startTime = System.currentTimeMillis();
103: File currentFile = null;
104: try {
105: henplus.pushBuffer();
106: henplus.getDispatcher().startBatch();
107: File f = openFile(filename);
108: f = f.getCanonicalFile();
109: if (_openFiles.contains(f)) {
110: throw new IOException(
111: "recursive inclusion alert: skipping file "
112: + f.getName());
113: }
114: HenPlus.msg().println(f.getName());
115: currentFile = f;
116: _openFiles.add(currentFile);
117: _cwdStack.push(currentFile.getParentFile());
118: String encoding = System.getProperty("file.encoding");//"UTF-8";
119: FileInputStream is = new FileInputStream(currentFile);
120: BufferedReader reader = new BufferedReader(
121: new InputStreamReader(is, encoding));
122: _running = true;
123: SigIntHandler.getInstance().pushInterruptable(this );
124: String line;
125: while (_running && (line = reader.readLine()) != null) {
126: byte execResult = henplus.executeLine(line);
127: if (execResult == HenPlus.LINE_EXECUTED) {
128: ++commandCount;
129: }
130: }
131: } catch (Exception e) {
132: //e.printStackTrace();
133: HenPlus.msg().println(e.getMessage());
134: if (st.hasMoreElements()) {
135: HenPlus.msg().println("..skipping to next file.");
136: continue;
137: }
138: return EXEC_FAILED;
139: } finally {
140: henplus.popBuffer(); // no open state ..
141: if (!_running) {
142: HenPlus.msg().println(
143: "cancel file loading " + currentFile);
144: }
145: henplus.getDispatcher().endBatch();
146: if (currentFile != null) {
147: _openFiles.remove(currentFile);
148: _cwdStack.pop();
149: }
150: SigIntHandler.getInstance().popInterruptable();
151:
152: }
153: long execTime = System.currentTimeMillis() - startTime;
154: HenPlus.msg().print(commandCount + " commands in ");
155: TimeRenderer.printTime(execTime, HenPlus.msg());
156: if (commandCount != 0) {
157: HenPlus.msg().print("; avg. time ");
158: TimeRenderer.printFraction(execTime, commandCount,
159: HenPlus.msg());
160: }
161: if (execTime != 0 && commandCount > 0) {
162: HenPlus.msg().print(
163: "; " + (1000 * commandCount / execTime)
164: + " per second");
165: }
166: HenPlus.msg().println(" (" + filename + ")");
167: }
168: return SUCCESS;
169: }
170:
171: public void interrupt() {
172: _running = false;
173: }
174:
175: public boolean requiresValidSession(String cmd) {
176: return false;
177: }
178:
179: /**
180: * return a descriptive string.
181: */
182: public String getShortDescription() {
183: return "load file and execute commands";
184: }
185:
186: public String getSynopsis(String cmd) {
187: return cmd + " <filename> [<filename> ..]";
188: }
189:
190: public String getLongDescription(String cmd) {
191: return "\tOpens one file or a sequence of files and reads the\n"
192: + "\tcontained sql-commands line by line. If the path of the\n"
193: + "\tfilename is not absolute, it is interpreted relative to\n"
194: + "\tthe current working directory. If the load command itself\n"
195: + "\tis executed in some loaded file, then the current working\n"
196: + "\tdirectory is the directory that file is in.\n"
197: + "\tThe commands 'load' and 'start' do exactly the same;\n"
198: + "\t'start', '@' and '@@' are provided for compatibility \n"
199: + "\twith oracle SQLPLUS scripts. However, there is no\n"
200: + "\tdistinction between '@' and '@@' as in SQLPLUS; henplus\n"
201: + "\talways reads subfiles relative to the contained file.\n";
202: }
203: }
204:
205: /*
206: * Local variables:
207: * c-basic-offset: 4
208: * compile-command: "ant -emacs -find build.xml"
209: * End:
210: */
|