1 package net.argius.stew;
9 import net.argius.stew.io.*;
10 import net.argius.stew.ui.*;
15 final class CommandProcessor {
17 private static Logger log = Logger.getLogger(CommandProcessor.class);
18 private static ResourceManager res = ResourceManager.getInstance(Command.class);
19 private static final String HYPHEN_E = "-e";
21 private final Environment env;
22 private final OutputProcessor op;
24 CommandProcessor(Environment env) {
26 this.op = env.getOutputProcessor();
30 * Invokes this command.
31 * @param parameterString
32 * @return whether this application continues or not
33 * @throws CommandException
35 boolean invoke(String parameterString) throws CommandException {
36 Parameter p = new Parameter(parameterString);
37 if (parameterString.replaceFirst("^\\s+", "").startsWith(HYPHEN_E)) {
38 final int offset = parameterString.indexOf(HYPHEN_E) + 2;
39 for (String s : parameterString.substring(offset).split(HYPHEN_E)) {
40 op.output(" >> " + s);
42 outputMessage("w.exit-not-available-in-sequencial-command");
47 final String commandName = p.at(0);
49 return invoke(commandName, new Parameter(parameterString));
50 } catch (UsageException ex) {
51 outputMessage("e.usage", commandName, ex.getMessage());
52 } catch (DynamicLoadingException ex) {
54 outputMessage("e.not-found", commandName);
55 } catch (CommandException ex) {
57 Throwable cause = ex.getCause();
58 String message = (cause == null) ? ex.getMessage() : cause.getMessage();
59 outputMessage("e.command", message);
60 } catch (IOException ex) {
62 outputMessage("e.command", ex.getMessage());
63 } catch (SQLException ex) {
65 SQLException parent = ex;
67 SQLException sqle = parent.getNextException();
68 if (sqle == null || sqle == parent) {
71 log.error(sqle, "------ SQLException.getNextException ------");
74 outputMessage("e.database", ex.getMessage());
75 } catch (UnsupportedOperationException ex) {
77 outputMessage("e.unsupported", ex.getMessage());
78 } catch (RuntimeException ex) {
80 outputMessage("e.runtime", ex.getMessage());
81 } catch (Throwable th) {
83 outputMessage("e.fatal", th.getMessage());
86 Connection conn = env.getCurrentConnection();
88 boolean isClosed = conn.isClosed();
90 log.info("connection is already closed");
94 } catch (SQLException ex) {
100 private boolean invoke(String commandName, Parameter p) throws IOException, SQLException {
101 assert commandName != null;
102 // do nothing if blank
103 if (commandName.length() == 0) {
107 if (commandName.equalsIgnoreCase("exit")) {
109 outputMessage("i.exit");
113 if (commandName.equalsIgnoreCase("connect") || commandName.equalsIgnoreCase("-c")) {
118 if (commandName.equals("-f")) {
119 final File file = Path.resolve(env.getCurrentDirectory(), p.at(1));
120 if (!file.isFile()) {
121 throw new UsageException(res.get("usage.-f"));
123 log.debug("-f %s", file.getAbsolutePath());
124 invoke(String.format("%s%s", Command.readFileAsString(file), p.after(2)));
128 if (commandName.equals("-s")) {
129 final File file = Path.resolve(env.getCurrentDirectory(), p.at(1));
130 if (!file.isFile()) {
131 throw new UsageException(res.get("usage.-s"));
133 log.debug("-s %s", file.getAbsolutePath());
134 ScriptEngineManager factory = new ScriptEngineManager();
135 ScriptEngine engine = factory.getEngineByName("JavaScript");
136 engine.put("connection", env.getCurrentConnection());
137 engine.put("conn", env.getCurrentConnection());
138 engine.put("patameter", p);
140 engine.put("outputProcessor", op);
141 engine.put("op", op);
143 Reader r = new FileReader(file);
145 engine.eval("function using(o, f) { f(o); o.close() }");
150 } catch (Exception ex) {
151 throw new CommandException(ex);
156 AliasMap aliasMap = env.getAliasMap();
157 if (commandName.equalsIgnoreCase("alias") || commandName.equalsIgnoreCase("unalias")) {
159 if (commandName.equalsIgnoreCase("alias")) {
161 final String keyword = p.at(1);
162 if (isUsableKeywordForAlias(keyword)) {
163 outputMessage("w.unusable-keyword-for-alias", keyword);
166 aliasMap.setValue(keyword, p.after(2));
168 } else if (p.has(1)) {
169 final String keyword = p.at(1);
170 if (isUsableKeywordForAlias(keyword)) {
171 outputMessage("w.unusable-keyword-for-alias", keyword);
174 if (aliasMap.containsKey(keyword)) {
175 outputMessage("i.dump-alias", keyword, aliasMap.getValue(keyword));
178 if (aliasMap.isEmpty()) {
179 outputMessage("i.noalias");
181 for (final String key : new TreeSet<String>(aliasMap.keys())) {
182 outputMessage("i.dump-alias", key, aliasMap.getValue(key));
186 } else if (commandName.equalsIgnoreCase("unalias")) {
188 aliasMap.remove(p.at(1));
191 throw new UsageException(res.get("usage.unalias"));
195 } else if (aliasMap.containsKey(commandName)) {
196 final String command = aliasMap.expand(commandName, p);
197 op.output(" >> " + command);
202 if (commandName.equalsIgnoreCase("cd")) {
204 throw new UsageException(res.get("usage.cd"));
206 File olddir = env.getCurrentDirectory();
207 final String path = p.at(1);
208 final File dir = new File(path);
209 final File newdir = ((dir.isAbsolute()) ? dir : new File(olddir, path)).getCanonicalFile();
210 if (!newdir.isDirectory()) {
211 outputMessage("e.dir-not-exists", newdir);
214 env.setCurrentDirectory(newdir);
215 outputMessage("i.directory-changed", olddir.getAbsolutePath(), newdir.getAbsolutePath());
219 if (commandName.equals("@")) {
220 final String currentDirectory = env.getCurrentDirectory().getAbsolutePath();
221 final String systemDirectory = Bootstrap.getSystemDirectory().getAbsolutePath();
222 op.output(String.format("current dir : %s", currentDirectory));
223 op.output(String.format("system dir : %s", systemDirectory));
227 if (commandName.equals("-")) {
228 return invoke("report -");
230 // runtime informations
231 if (commandName.equals("?")) {
233 for (final String k : p.asArray()) {
237 final String s = System.getProperties().containsKey(k)
238 ? String.format("[%s]", System.getProperty(k))
240 op.output(String.format("%s=%s", k, s));
243 op.output(String.format("JRE : %s %s",
244 System.getProperty("java.runtime.name"),
245 System.getProperty("java.runtime.version")));
246 op.output(String.format("OS : %s (osver=%s)",
247 System.getProperty("os.name"),
248 System.getProperty("os.version")));
249 op.output(String.format("Locale : %s", Locale.getDefault()));
254 Connection conn = env.getCurrentConnection();
256 outputMessage("e.not-connect");
257 } else if (commandName.equalsIgnoreCase("disconnect") || commandName.equalsIgnoreCase("-d")) {
259 outputMessage("i.disconnected");
260 } else if (commandName.equalsIgnoreCase("commit")) {
262 outputMessage("i.committed");
263 } else if (commandName.equalsIgnoreCase("rollback")) {
265 outputMessage("i.rollbacked");
267 executeDynamicCommand(commandName, conn, p);
272 private static boolean isUsableKeywordForAlias(String keyword) {
273 return keyword != null && keyword.matches("(?i)-.*|exit|alias|unalias");
276 private void connect(Parameter p) throws SQLException {
277 log.info("connect start");
279 final String id = p.at(1);
282 connector = AnonymousConnector.getConnector(id, p.at(2), p.at(3));
283 } else if (id.indexOf('@') >= 0) {
284 connector = AnonymousConnector.getConnector(id);
286 connector = env.getConnectorMap().getConnector(id);
288 if (connector != null) {
289 env.establishConnection(connector);
291 outputMessage("e.no-connector", id);
293 log.info("connect end");
296 private void disconnect() {
297 log.debug("disconnect start");
299 env.releaseConnection();
300 } catch (SQLException ex) {
301 outputMessage("w.connection-closed-abnormally");
303 log.debug("disconnect end");
306 private void executeDynamicCommand(String commandName, Connection conn, Parameter p) {
307 assert commandName != null && !commandName.contains(" ");
309 if (commandName.indexOf('.') > 0) {
312 fqcn = "net.argius.stew.command."
313 + commandName.substring(0, 1).toUpperCase()
314 + commandName.substring(1).toLowerCase();
316 Class<? extends Command> c;
318 c = DynamicLoader.loadClass(fqcn);
319 } catch (DynamicLoadingException ex) {
320 c = Command.isSelect(p.asString()) ? Select.class : UpdateAndOthers.class;
322 Command command = DynamicLoader.newInstance(c);
324 Connector connector = env.getCurrentConnector();
325 if (connector.isReadOnly() && !command.isReadOnly()) {
326 outputMessage("e.readonly");
329 command.setEnvironment(env);
330 log.info("command: %s start", command);
332 command.initialize();
333 command.execute(conn, p);
337 log.info("command: %s end", command);
342 * @param id message-id (resource)
344 * @throws CommandException
346 void outputMessage(String id, Object... args) throws CommandException {
347 op.output(res.get(id, args));
351 * SQL statement command.
353 abstract static class RawSQL extends Command {
356 public final void execute(Connection conn, Parameter parameter) throws CommandException {
357 final String rawString = parameter.asString();
359 Statement stmt = prepareStatement(conn, rawString);
361 execute(stmt, rawString);
365 } catch (SQLException ex) {
366 throw new CommandException(ex);
370 protected abstract void execute(Statement stmt, String sql) throws SQLException;
375 * Select statement command.
377 static final class Select extends RawSQL {
384 public boolean isReadOnly() {
389 public void execute(Statement stmt, String rawString) throws SQLException {
390 final long startTime = System.currentTimeMillis();
391 ResultSet rs = executeQuery(stmt, rawString);
393 outputMessage("i.response-time", (System.currentTimeMillis() - startTime) / 1000f);
394 ResultSetReference ref = new ResultSetReference(rs, rawString);
396 outputMessage("i.selected", ref.getRecordCount());
405 * Update statement (contains all SQL excepted a Select SQL) command.
407 static final class UpdateAndOthers extends RawSQL {
409 public UpdateAndOthers() {
414 public boolean isReadOnly() {
419 protected void execute(Statement stmt, String sql) throws SQLException {
420 final int updatedCount = executeUpdate(stmt, sql);
422 if (sql.matches("(?i)\\s*UPDATE.*")) {
424 } else if (sql.matches("(?i)\\\\s*INSERT.*")) {
425 msgId = "i.inserted";
426 } else if (sql.matches("(?i)\\\\s*DELETE.*")) {
429 msgId = "i.proceeded";
431 outputMessage(msgId, updatedCount);