1 package net.argius.stew;
9 import net.argius.stew.io.*;
10 import net.argius.stew.ui.*;
11 import net.argius.stew.ui.window.*;
16 final class CommandProcessor {
18 private static Logger log = Logger.getLogger(CommandProcessor.class);
19 private static ResourceManager res = ResourceManager.getInstance(WindowLauncher.class);
20 private static final String HYPHEN_E = "-e";
22 private final Environment env;
23 private final OutputProcessor op;
25 CommandProcessor(Environment env) {
27 this.op = env.getOutputProcessor();
31 * Invokes this command.
32 * @param parameterString
33 * @return whether this application continues or not
34 * @throws CommandException
36 boolean invoke(String parameterString) throws CommandException {
37 Parameter p = new Parameter(parameterString);
38 if (parameterString.replaceFirst("^\\s+", "").startsWith(HYPHEN_E)) {
39 final int offset = parameterString.indexOf(HYPHEN_E) + 2;
40 for (String s : parameterString.substring(offset).split(HYPHEN_E)) {
41 op.output(" >> " + s);
43 outputMessage("w.exit-not-available-in-sequencial-command");
48 final String commandName = p.at(0);
50 return invoke(commandName, new Parameter(parameterString));
51 } catch (UsageException ex) {
52 outputMessage("e.usage", commandName, ex.getMessage());
53 } catch (DynamicLoadingException ex) {
55 outputMessage("e.not-found", commandName);
56 } catch (CommandException ex) {
58 Throwable cause = ex.getCause();
59 String message = (cause == null) ? ex.getMessage() : cause.getMessage();
60 outputMessage("e.command", message);
61 } catch (IOException ex) {
63 outputMessage("e.command", ex.getMessage());
64 } catch (SQLException ex) {
66 SQLException parent = ex;
68 SQLException sqle = parent.getNextException();
69 if (sqle == null || sqle == parent) {
72 log.error(sqle, "------ SQLException.getNextException ------");
75 outputMessage("e.database", ex.getMessage());
76 } catch (UnsupportedOperationException ex) {
78 outputMessage("e.unsupported", ex.getMessage());
79 } catch (RuntimeException ex) {
81 outputMessage("e.runtime", ex.getMessage());
82 } catch (Throwable th) {
84 outputMessage("e.fatal", th.getMessage());
87 Connection conn = env.getCurrentConnection();
89 boolean isClosed = conn.isClosed();
91 log.info("connection is already closed");
95 } catch (SQLException ex) {
101 private boolean invoke(String commandName, Parameter p) throws IOException, SQLException {
102 assert commandName != null;
103 // do nothing if blank
104 if (commandName.length() == 0) {
108 if (commandName.equalsIgnoreCase("exit")) {
110 outputMessage("i.exit");
114 if (commandName.equalsIgnoreCase("connect") || commandName.equalsIgnoreCase("-c")) {
119 if (commandName.equals("-f")) {
120 final File file = Path.resolve(env.getCurrentDirectory(), p.at(1));
121 if (!file.isFile()) {
122 throw new UsageException(res.get("usage.-f"));
124 log.debug("-f %s", file.getAbsolutePath());
125 invoke(String.format("%s%s", Command.readFileAsString(file), p.after(2)));
129 if (commandName.equals("-s")) {
130 final File file = Path.resolve(env.getCurrentDirectory(), p.at(1));
131 if (!file.isFile()) {
132 throw new UsageException(res.get("usage.-s"));
134 log.debug("-s %s", file.getAbsolutePath());
135 ScriptEngineManager factory = new ScriptEngineManager();
136 ScriptEngine engine = factory.getEngineByName("JavaScript");
137 engine.put("connection", env.getCurrentConnection());
138 engine.put("conn", env.getCurrentConnection());
139 engine.put("patameter", p);
141 engine.put("outputProcessor", op);
142 engine.put("op", op);
144 Reader r = new FileReader(file);
146 engine.eval("function using(o, f) { f(o); o.close() }");
151 } catch (Exception ex) {
152 throw new CommandException(ex);
157 AliasMap aliasMap = env.getAliasMap();
158 if (commandName.equalsIgnoreCase("alias") || commandName.equalsIgnoreCase("unalias")) {
160 if (commandName.equalsIgnoreCase("alias")) {
162 final String keyword = p.at(1);
163 if (isUsableKeywordForAlias(keyword)) {
164 outputMessage("w.unusable-keyword-for-alias", keyword);
167 aliasMap.setValue(keyword, p.after(2));
169 } else if (p.has(1)) {
170 final String keyword = p.at(1);
171 if (isUsableKeywordForAlias(keyword)) {
172 outputMessage("w.unusable-keyword-for-alias", keyword);
175 if (aliasMap.containsKey(keyword)) {
176 outputMessage("i.dump-alias", keyword, aliasMap.getValue(keyword));
179 if (aliasMap.isEmpty()) {
180 outputMessage("i.noalias");
182 for (final String key : new TreeSet<String>(aliasMap.keys())) {
183 outputMessage("i.dump-alias", key, aliasMap.getValue(key));
187 } else if (commandName.equalsIgnoreCase("unalias")) {
189 aliasMap.remove(p.at(1));
192 throw new UsageException(res.get("usage.unalias"));
196 } else if (aliasMap.containsKey(commandName)) {
197 final String command = aliasMap.expand(commandName, p);
198 op.output(" >> " + command);
203 if (commandName.equalsIgnoreCase("cd")) {
205 throw new UsageException(res.get("usage.cd"));
207 File olddir = env.getCurrentDirectory();
208 final String path = p.at(1);
209 final File dir = new File(path);
210 final File newdir = ((dir.isAbsolute()) ? dir : new File(olddir, path)).getCanonicalFile();
211 if (!newdir.isDirectory()) {
212 outputMessage("e.dir-not-exists", newdir);
215 env.setCurrentDirectory(newdir);
216 outputMessage("i.directory-changed", olddir.getAbsolutePath(), newdir.getAbsolutePath());
220 if (commandName.equals("@")) {
221 final String currentDirectory = env.getCurrentDirectory().getAbsolutePath();
222 final String systemDirectory = Bootstrap.getSystemDirectory().getAbsolutePath();
223 op.output(String.format("current dir : %s", currentDirectory));
224 op.output(String.format("system dir : %s", systemDirectory));
228 if (commandName.equals("-")) {
229 return invoke("report -");
231 // runtime informations
232 if (commandName.equals("?")) {
234 for (final String k : p.asArray()) {
238 final String s = System.getProperties().containsKey(k)
239 ? String.format("[%s]", System.getProperty(k))
241 op.output(String.format("%s=%s", k, s));
244 op.output(String.format("JRE : %s %s",
245 System.getProperty("java.runtime.name"),
246 System.getProperty("java.runtime.version")));
247 op.output(String.format("OS : %s (osver=%s)",
248 System.getProperty("os.name"),
249 System.getProperty("os.version")));
250 op.output(String.format("Locale : %s", Locale.getDefault()));
255 Connection conn = env.getCurrentConnection();
257 outputMessage("e.not-connect");
258 } else if (commandName.equalsIgnoreCase("disconnect") || commandName.equalsIgnoreCase("-d")) {
260 outputMessage("i.disconnected");
261 } else if (commandName.equalsIgnoreCase("commit")) {
263 outputMessage("i.committed");
264 } else if (commandName.equalsIgnoreCase("rollback")) {
266 outputMessage("i.rollbacked");
268 executeDynamicCommand(commandName, conn, p);
273 private static boolean isUsableKeywordForAlias(String keyword) {
274 return keyword != null && keyword.matches("(?i)-.*|exit|alias|unalias");
277 private void connect(Parameter p) throws SQLException {
278 log.info("connect start");
280 final String id = p.at(1);
283 connector = AnonymousConnector.getConnector(id, p.at(2), p.at(3));
284 } else if (id.indexOf('@') >= 0) {
285 connector = AnonymousConnector.getConnector(id);
287 connector = env.getConnectorMap().getConnector(id);
289 if (connector != null) {
290 env.establishConnection(connector);
292 outputMessage("e.no-connector", id);
294 log.info("connect end");
297 private void disconnect() {
298 log.debug("disconnect start");
300 env.releaseConnection();
301 } catch (SQLException ex) {
302 outputMessage("w.connection-closed-abnormally");
304 log.debug("disconnect end");
307 private void executeDynamicCommand(String commandName, Connection conn, Parameter p) {
308 assert commandName != null && !commandName.contains(" ");
310 if (commandName.indexOf('.') > 0) {
313 fqcn = "net.argius.stew.command."
314 + commandName.substring(0, 1).toUpperCase()
315 + commandName.substring(1).toLowerCase();
317 Class<? extends Command> c;
319 c = DynamicLoader.loadClass(fqcn);
320 } catch (DynamicLoadingException ex) {
321 c = Command.isSelect(p.asString()) ? Select.class : UpdateAndOthers.class;
323 Command command = DynamicLoader.newInstance(c);
325 Connector connector = env.getCurrentConnector();
326 if (connector.isReadOnly() && !command.isReadOnly()) {
327 outputMessage("e.readonly");
330 command.setEnvironment(env);
331 log.info("command: %s start", command);
333 command.initialize();
334 command.execute(conn, p);
338 log.info("command: %s end", command);
343 * @param id message-id (resource)
345 * @throws CommandException
347 void outputMessage(String id, Object... args) throws CommandException {
348 op.output(res.get(id, args));
352 * SQL statement command.
354 abstract static class RawSQL extends Command {
357 public final void execute(Connection conn, Parameter parameter) throws CommandException {
358 final String rawString = parameter.asString();
360 Statement stmt = prepareStatement(conn, rawString);
362 execute(stmt, rawString);
366 } catch (SQLException ex) {
367 throw new CommandException(ex);
371 protected abstract void execute(Statement stmt, String sql) throws SQLException;
376 * Select statement command.
378 static final class Select extends RawSQL {
385 public boolean isReadOnly() {
390 public void execute(Statement stmt, String rawString) throws SQLException {
391 final long startTime = System.currentTimeMillis();
392 ResultSet rs = executeQuery(stmt, rawString);
394 outputMessage("i.response-time", (System.currentTimeMillis() - startTime) / 1000f);
395 ResultSetReference ref = new ResultSetReference(rs, rawString);
397 outputMessage("i.selected", ref.getRecordCount());
406 * Update statement (contains all SQL excepted a Select SQL) command.
408 static final class UpdateAndOthers extends RawSQL {
410 public UpdateAndOthers() {
415 public boolean isReadOnly() {
420 protected void execute(Statement stmt, String sql) throws SQLException {
421 final int updatedCount = executeUpdate(stmt, sql);
423 if (sql.matches("(?i)\\s*UPDATE.*")) {
425 } else if (sql.matches("(?i)\\\\s*INSERT.*")) {
426 msgId = "i.inserted";
427 } else if (sql.matches("(?i)\\\\s*DELETE.*")) {
430 msgId = "i.proceeded";
432 outputMessage(msgId, updatedCount);