package com.dbxml.db.client.tools;
/*
* dbXML - Native XML Databasein
* Copyright (c) 1999-2006 The dbXML Group, L.L.C.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* $Id: CommandLine.java,v 1.10 2006/02/02 18:53:46 bradford Exp $
*/
import java.io.*;
import java.util.*;
import com.dbxml.db.client.CollectionClient;
import com.dbxml.db.client.dbXMLClient;
import com.dbxml.db.server.dbXML;
import com.dbxml.util.Configuration;
import com.dbxml.util.ConfigurationCallback;
import com.dbxml.util.Stopwatch;
import com.dbxml.util.StringUtilities;
import com.dbxml.util.dbXMLException;
import com.dbxml.xml.dom.DOMHelper;
import org.w3c.dom.Document;
/**
* CommandLine
*/
public final class CommandLine {
private static final String[] EmptyStrings = new String[0];
private static final String SET = "set";
private static final String CATEGORY = "category";
private static final String COMMAND = "command";
private static final String CLASS = "class";
private static final String VERB = "verb";
private static final String NAME = "name";
private static final String VALUE = "value";
private static final String USAGE = "usage";
private static final String DESCRIPTION = "description";
private static final String HELP = "help";
public static final String COLLECTION = "collection";
public static final String VERBOSE = "verbose";
public static final String INTERACTIVE = "interactive";
public static final String TOTALS = "totals";
public static final String DURATIONS = "durations";
public static final String CONFIRM = "confirm";
private static final int TOKEN_VALUE = 0;
private static final int TOKEN_WHITESPACE = 1;
private static final int TOKEN_OPERATOR = 2;
private static final int TOKEN_STRING = 3;
private dbXMLClient client;
private Map commands = new HashMap();
private Map props = new TreeMap();
private BufferedReader in;
private StringTokenizer st;
private String token;
private PrintWriter out;
private PrintWriter err;
public CommandLine() {
setProperty(TOTALS, Boolean.TRUE);
setProperty(DURATIONS, Boolean.TRUE);
setProperty(CONFIRM, Boolean.FALSE);
try {
OutputStreamWriter osw = new OutputStreamWriter(System.out, "UTF8");
BufferedWriter bw = new BufferedWriter(osw, 4096);
setWriter(bw);
osw = new OutputStreamWriter(System.err, "UTF8");
bw = new BufferedWriter(osw, 4096);
setErrorWriter(bw);
readCommands();
}
catch ( Exception e ) {
e.printStackTrace(System.err);
System.exit(1);
}
}
private void readCommands() throws Exception {
String home = System.getProperty(dbXML.PROP_DBXML_HOME);
File homeFile = new File(home);
File cfgFile = new File(homeFile, "config/commands.xml");
Document doc = DOMHelper.parse(cfgFile);
Configuration cfg = new Configuration(doc);
final String[] lastCatName = new String[1];
final Map helpMap = new HashMap();
final List catList = new ArrayList();
setProperties(cfg);
cfg.processChildren(CATEGORY, new ConfigurationCallback() {
public void process(Configuration cc) {
String name = cc.getAttribute(NAME);
lastCatName[0] = name;
String desc = cc.getAttribute(DESCRIPTION);
catList.add(StringUtilities.leftJustify(name, 15) + " " + desc);
final StringBuffer sbHelp = new StringBuffer();
sbHelp.append(cc.getAttribute(DESCRIPTION) + ":\n");
cc.processChildren(COMMAND, new ConfigurationCallback() {
public void process(Configuration c) {
try {
String cName = c.getAttribute(CLASS);
Class cl = Class.forName(cName);
Command cmd = (Command)cl.newInstance();
cmd.setCommandLine(CommandLine.this);
String verb = c.getAttribute(VERB).toUpperCase();
String usage = c.getChild(USAGE).getValue().trim();
String desc = c.getChild(DESCRIPTION).getValue().trim();
String help = c.getChild(HELP).getValue();
StringBuffer sb = new StringBuffer();
sb.append("Command: "+verb+"\n");
sb.append("Purpose: "+desc+"\n");
sb.append(" Usage: "+usage+"\n");
sb.append(help);
CommandInfo info = new CommandInfo(verb, cmd, desc, sb.toString());
commands.put(verb, info);
sbHelp.append("\n " + StringUtilities.leftJustify(verb, 15) + " " + desc);
}
catch ( Exception e ) {
e.printStackTrace(System.err);
System.exit(1);
}
}
});
helpMap.put(name.toUpperCase(), sbHelp.toString());
}
});
StringBuffer sbHelp = new StringBuffer();
sbHelp.append("There are several help categories to choose from:\n\n");
for ( int i = 0; i < catList.size(); i++ )
sbHelp.append(" " + catList.get(i) + "\n");
sbHelp.append("\nExample: help " + lastCatName[0]);
// Add the hardcoded help Command
commands.put("HELP", new CommandInfo("HELP", new Help(helpMap), "", sbHelp.toString()));
}
private void setProperties(Configuration cfg) {
cfg.processChildren(SET, new ConfigurationCallback() {
public void process(Configuration cc) {
String name = cc.getAttribute(NAME);
String value = cc.getAttribute(VALUE);
try {
setProperty(name, value);
}
catch ( Exception e ) {
err.println("Error: " + e.getMessage());
if ( isPropertyTrue(VERBOSE) )
e.printStackTrace(err);
if ( isPropertyTrue(INTERACTIVE) )
err.println();
}
}
});
}
public void setProperty(String name, Object value) {
props.put(name, value);
}
public void setProperty(String name, String value) throws dbXMLException {
Object obj = props.get(name);
Object newObj = null;
if ( value == null ) {
props.remove(name);
return;
}
// Settings for boolean values
if ( value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") )
newObj = Boolean.TRUE;
else if ( value.equals("1") ) {
if ( obj instanceof Boolean )
newObj = Boolean.TRUE;
else
newObj = new Integer(1);
}
else if ( value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no") )
newObj = Boolean.FALSE;
else if ( value.equals("0") ) {
if ( obj instanceof Boolean )
newObj = Boolean.FALSE;
else
newObj = new Integer(0);
}
// Settings for integer values
if ( newObj == null ) {
try {
newObj = new Integer(value);
}
catch ( Exception e ) {
newObj = null;
}
}
// String value
if ( newObj == null )
newObj = value;
if ( obj != null && (obj.getClass() != newObj.getClass()) )
throw new dbXMLException("Cannot change type of property '" + name + "'");
props.put(name, newObj);
}
public Object getProperty(String name) {
return props.get(name);
}
public String[] listProperties() {
return (String[])props.keySet().toArray(EmptyStrings);
}
public BufferedReader getReader() {
return in;
}
public PrintWriter getWriter() {
return out;
}
public PrintWriter getErrorWriter() {
return err;
}
public void setClient(dbXMLClient client) {
this.client = client;
}
public dbXMLClient getClient() throws dbXMLException {
if ( client == null )
throw new dbXMLException("Not connected");
return client;
}
public boolean isPropertyTrue(String name) {
Object obj = getProperty(name);
return obj instanceof Boolean && ((Boolean)obj).booleanValue();
}
public String readLine(String prefix) throws IOException {
if ( getProperty(INTERACTIVE) == Boolean.TRUE ) {
out.print(prefix);
out.flush();
}
return in.readLine();
}
private void unknownVerb(String verb) {
err.println();
err.println("Unknown command verb '" + verb + "'");
err.println("Type 'HELP' for a list of command categories");
err.println();
}
public void setReader(Reader in) {
if ( in instanceof BufferedReader )
this.in = (BufferedReader)in;
else
this.in = new BufferedReader(in, 4096);
}
public void setWriter(Writer out) {
if ( out instanceof PrintWriter )
this.out = (PrintWriter)out;
else
this.out = new PrintWriter(out, true);
}
public void setErrorWriter(Writer err) {
if ( err instanceof PrintWriter )
this.err = (PrintWriter)err;
else
this.err = new PrintWriter(err, true);
}
public void process() {
String line = null;
CollectionClient col = null;
String colName = "";
if ( getProperty(INTERACTIVE) == Boolean.TRUE ) {
out.println(dbXML.Title + " " + dbXML.Version + " Interactive Mode");
out.println("Type 'HELP' for a list of command categories");
}
do {
try {
CollectionClient nc = (CollectionClient)getProperty(COLLECTION);
if ( nc != col ) {
col = nc;
if ( col != null )
colName = col.getCanonicalName();
else
colName = "";
}
line = readLine(colName + ">");
if ( line != null ) {
st = new StringTokenizer(line, "=\" \t\n\r", true);
fetchNextToken();
if ( hasMoreTokens() ) {
String verb = getNextToken().toUpperCase();
CommandInfo info = (CommandInfo)commands.get(verb);
if ( info != null ) {
if ( isPropertyTrue(INTERACTIVE) )
out.println();
Stopwatch sw = new Stopwatch("Execution", true);
info.command.process();
sw.stop();
if ( isPropertyTrue(DURATIONS) )
out.println("(" + sw.toString() + ")");
if ( isPropertyTrue(INTERACTIVE) )
out.println();
}
else
unknownVerb(verb);
}
}
}
catch ( Exception e ) {
err.println("Error: " + e.getMessage());
if ( isPropertyTrue(VERBOSE) )
e.printStackTrace(err);
if ( isPropertyTrue(INTERACTIVE) )
err.println();
}
}
while ( line != null );
try {
if ( client != null )
client.disconnect();
}
catch ( Exception e ) {
// Whatever
}
}
public static void main(String[] args) {
CommandLine cl = new CommandLine();
try {
String userHome = System.getProperty("user.home");
File cfgFile = new File(userHome, ".dbxml.config");
if ( cfgFile.exists() && cfgFile.isFile() ) {
Document cfgDoc = DOMHelper.parse(cfgFile);
Configuration cfg = new Configuration(cfgDoc);
cl.setProperties(cfg);
}
}
catch ( Exception e ) {
System.err.println("Error: "+e.getMessage());
e.printStackTrace(System.err);
}
try {
if ( args.length == 0 ) {
cl.setProperty(INTERACTIVE, Boolean.TRUE);
InputStreamReader isr = new InputStreamReader(System.in, "UTF8");
cl.setReader(isr);
}
else {
cl.setProperty(INTERACTIVE, Boolean.FALSE);
FileInputStream fis = new FileInputStream(args[0]);
BufferedInputStream bis = new BufferedInputStream(fis, 4096);
InputStreamReader isr = new InputStreamReader(bis, "UTF8");
cl.setReader(isr);
}
cl.process();
}
catch ( Exception e ) {
System.err.println("Error: "+e.getMessage());
e.printStackTrace(System.err);
}
}
/**
* parseProperties parses a list of name/value pairs.
*/
public Properties parseProperties() throws dbXMLException {
Properties properties = new Properties();
while ( st.hasMoreTokens() ) {
String name = getNextToken();
if ( getTokenType(name) != TOKEN_VALUE )
throw new dbXMLException("Name Token Expected");
String operator = getNextToken();
if ( getTokenType(operator) != TOKEN_OPERATOR )
throw new dbXMLException("Operator Token Expected");
String value = getNextToken();
switch ( getTokenType(value) ) {
case TOKEN_VALUE:
properties.setProperty(name, value);
break;
default:
throw new dbXMLException("Value Token Expected");
}
}
return properties;
}
public int getTokenType(String token) {
if ( token.length() == 1 ) {
switch ( token.charAt(0) ) {
case '=':
return TOKEN_OPERATOR;
case '\"':
return TOKEN_STRING;
case ' ':
case '\t':
case '\n':
case '\r':
return TOKEN_WHITESPACE;
default:
return TOKEN_VALUE;
}
}
else
return TOKEN_VALUE;
}
public boolean hasMoreTokens() {
return token != null;
}
public void fetchNextToken() throws dbXMLException {
while ( true ) {
if ( !st.hasMoreTokens() ) {
token = null;
return;
}
token = st.nextToken();
switch ( getTokenType(token) ) {
case TOKEN_STRING:
StringBuffer sb = new StringBuffer();
boolean done = false;
while ( !done ) {
if ( st.hasMoreTokens() ) {
String value = st.nextToken();
switch ( getTokenType(value) ) {
case TOKEN_STRING:
done = true;
break;
default:
sb.append(value);
break;
}
}
else
throw new dbXMLException("String Token Not Terminated");
}
token = sb.toString();
return;
case TOKEN_WHITESPACE:
continue;
default:
return;
}
}
}
public String getNextToken(String tokenName) throws dbXMLException {
if ( token == null )
throw new dbXMLException(tokenName+" Expected");
String result = token;
fetchNextToken();
return result;
}
public String getNextToken() throws dbXMLException {
return getNextToken("Token");
}
/**
* CommandInfo
*/
private class CommandInfo {
public String verb;
public Command command;
public String description;
public String help;
public CommandInfo(String verb, Command command, String description, String help) {
this.verb = verb;
this.command = command;
this.description = description;
this.help = help;
}
}
/**
* Help
*/
private class Help implements Command {
private Map helpMap;
public Help(Map helpMap) {
this.helpMap = helpMap;
}
public CommandLine getCommandLine() {
return CommandLine.this;
}
public void setCommandLine(CommandLine cl) {
// NOOP
}
public void process() throws dbXMLException {
String verb = "HELP";
if ( hasMoreTokens() )
verb = getNextToken().toUpperCase();
String catHelp = (String)helpMap.get(verb);
if ( catHelp != null )
out.println(catHelp);
else {
CommandInfo info = (CommandInfo)commands.get(verb);
if ( info != null )
out.println(info.help);
else
unknownVerb(verb);
}
}
}
}