Coverage Report - no.sesat.Interpreter
 
Classes in this File Line Coverage Branch Coverage Complexity
Interpreter
0%
0/51
0%
0/14
0
Interpreter$1
0%
0/24
0%
0/12
0
Interpreter$10
0%
0/14
0%
0/4
0
Interpreter$11
0%
0/4
N/A
0
Interpreter$11$1
0%
0/2
N/A
0
Interpreter$12
0%
0/4
N/A
0
Interpreter$13
0%
0/1
N/A
0
Interpreter$2
0%
0/3
N/A
0
Interpreter$3
0%
0/3
N/A
0
Interpreter$4
0%
0/16
0%
0/6
0
Interpreter$5
0%
0/4
N/A
0
Interpreter$6
0%
0/10
0%
0/4
0
Interpreter$7
0%
0/18
0%
0/12
0
Interpreter$8
0%
0/13
0%
0/4
0
Interpreter$9
0%
0/12
0%
0/2
0
Interpreter$Context
0%
0/55
0%
0/29
0
Interpreter$Context$State
0%
0/2
N/A
0
Interpreter$Function
0%
0/3
N/A
0
Interpreter$FunctionInterface
N/A
N/A
0
Interpreter$InterpreterRepl
0%
0/5
N/A
0
 
 1  
 /**
 2  
  * Copyright (2008) Schibsted Søk AS
 3  
  * This file is part of Sesat Commons.
 4  
  *
 5  
  *   Sesat Commons is free software: you can redistribute it and/or modify
 6  
  *   it under the terms of the GNU Lesser General Public License as published by
 7  
  *   the Free Software Foundation, either version 3 of the License, or
 8  
  *   (at your option) any later version.
 9  
  *
 10  
  *   Sesat Commons is distributed in the hope that it will be useful,
 11  
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  
  *   GNU Lesser General Public License for more details.
 14  
  *
 15  
  *   You should have received a copy of the GNU Lesser General Public License
 16  
  *   along with Sesat Commons.  If not, see <http://www.gnu.org/licenses/>.
 17  
  *
 18  
  */
 19  
 
 20  
 package no.sesat;
 21  
 
 22  
 import java.io.BufferedReader;
 23  
 import java.io.BufferedWriter;
 24  
 import java.io.Console;
 25  
 import java.io.FileReader;
 26  
 import java.io.FileWriter;
 27  
 import java.io.IOException;
 28  
 import java.io.Reader;
 29  
 import java.util.ArrayList;
 30  
 import java.util.Collections;
 31  
 import java.util.Enumeration;
 32  
 import java.util.HashMap;
 33  
 import java.util.Iterator;
 34  
 import java.util.List;
 35  
 import java.util.Map;
 36  
 import java.util.Properties;
 37  
 import java.util.Set;
 38  
 import java.util.TreeSet;
 39  
 import java.util.Vector;
 40  
 
 41  
 import org.apache.log4j.Level;
 42  
 import org.apache.log4j.Logger;
 43  
 import org.apache.log4j.spi.LoggerRepository;
 44  
 
 45  
 /**
 46  
  * Simple interpreter that will start if a console is available. You can add
 47  
  * functions to it by using addFunction(String, Function).
 48  
  *
 49  
  */
 50  0
 public class Interpreter {
 51  0
     public static final boolean ENABLED = Boolean.parseBoolean(System.getenv("SESAT_INTERPRETER"));
 52  0
     private static Map<String, Function> FUNCTIONS = ENABLED ? new HashMap<String, Function>() : null;
 53  0
     private static boolean RUN = ENABLED;
 54  0
     private static ArrayList<InterpreterRepl> REPL = new ArrayList<InterpreterRepl>();
 55  
 
 56  
     private static interface FunctionInterface {
 57  
         String execute(Context ctx);
 58  
     }
 59  
 
 60  
     private static abstract class InterpreterRepl extends Thread {
 61  
         public InterpreterRepl(String name) {
 62  0
             super(name);
 63  0
             init();
 64  0
         }
 65  
 
 66  
         protected void init() {
 67  0
         };
 68  
 
 69  
         protected void output(String s) {
 70  0
         };
 71  
     }
 72  
 
 73  
     private static void broadcastToRepl(String s) {
 74  0
         for (InterpreterRepl repl : REPL) {
 75  0
             repl.output(s);
 76  
         }
 77  0
     }
 78  
 
 79  
     /**
 80  
      * Add a function to this Interpreter.
 81  
      *
 82  
      * @param name
 83  
      *            Name of function
 84  
      * @param fun
 85  
      *            The function
 86  
      */
 87  
     public synchronized static void addFunction(final String name, final Function fun) {
 88  0
         if (FUNCTIONS != null) {
 89  0
             if (FUNCTIONS.containsKey(name)) {
 90  0
                 broadcastToRepl("Redefined function: " + name + "\n");
 91  
             } else {
 92  0
                 broadcastToRepl("Added function: " + name + "\n");
 93  
             }
 94  0
             FUNCTIONS.put(name, fun);
 95  
         }
 96  0
     }
 97  
 
 98  
     /**
 99  
      * Remove function from interpreter.
 100  
      *
 101  
      * @param name
 102  
      *            Named function
 103  
      */
 104  
     public synchronized static void removeFunction(final String name) {
 105  0
         if (FUNCTIONS != null) {
 106  0
             broadcastToRepl("Removed function: " + name + "\n");
 107  0
             FUNCTIONS.remove(name);
 108  
         }
 109  0
     }
 110  
 
 111  
     /**
 112  
      * Function that you can implement if you want to add a function to this
 113  
      * Interpreter.
 114  
      */
 115  0
     public abstract static class Function implements FunctionInterface {
 116  
         /**
 117  
          * One line that describes the function.
 118  
          *
 119  
          * @return Description
 120  
          */
 121  
         protected String describe() {
 122  0
             return "";
 123  
         }
 124  
 
 125  
         /**
 126  
          * If there is need for a longer description then what you can write in
 127  
          * description then write it here.
 128  
          *
 129  
          * @return Help text
 130  
          */
 131  
         protected String help() {
 132  0
             return describe();
 133  
         }
 134  
     }
 135  
 
 136  
     /**
 137  
      * Context for a function.
 138  
      */
 139  0
     public static final class Context {
 140  
         /**
 141  
          * The original argument array.
 142  
          */
 143  
         final private String argString;
 144  0
         final private Vector<String> argVector = new Vector<String>();
 145  0
         final private HashMap<String, String> argHash = new HashMap<String, String>();
 146  
 
 147  0
         private enum State {
 148  0
             normal, quote, keywordNormal, keywordQuote
 149  
         };
 150  
 
 151  0
         private Context(final String arguments) {
 152  0
             argString = arguments.trim();
 153  0
             if (!argString.isEmpty()) {
 154  0
                 State state = State.normal;
 155  
 
 156  0
                 String arg = "";
 157  0
                 String value = "";
 158  0
                 for (char c : (argString + " ").toCharArray()) {
 159  0
                     switch (state) {
 160  
                     case normal:
 161  0
                         if (c == ' ') {
 162  0
                             argVector.add(arg);
 163  0
                             arg = "";
 164  0
                         } else if (c == '\'') {
 165  0
                             state = State.quote;
 166  0
                         } else if (c == '=') {
 167  0
                             state = State.keywordNormal;
 168  
                         } else {
 169  0
                             arg += c;
 170  
                         }
 171  0
                         break;
 172  
 
 173  
                     case quote:
 174  0
                         if (c == '\'') {
 175  0
                             state = State.normal;
 176  
                         } else {
 177  0
                             arg += c;
 178  
                         }
 179  0
                         break;
 180  
 
 181  
                     case keywordNormal:
 182  0
                         if (c == ' ') {
 183  0
                             state = State.normal;
 184  0
                             argHash.put(arg.toUpperCase(), value);
 185  0
                             arg = "";
 186  0
                             value = "";
 187  0
                         } else if (c == '\'') {
 188  0
                             state = State.keywordQuote;
 189  
                         } else {
 190  0
                             value += c;
 191  
                         }
 192  0
                         break;
 193  
 
 194  
                     case keywordQuote:
 195  0
                         if (c == '\'') {
 196  0
                             state = State.keywordNormal;
 197  
                         } else {
 198  0
                             value += c;
 199  
                         }
 200  
                         break;
 201  
 
 202  
                     }
 203  
                 }
 204  
             }
 205  0
         }
 206  
 
 207  
         /**
 208  
          * @return Number of arguments.
 209  
          */
 210  
         public int length() {
 211  0
             return argVector.size();
 212  
         }
 213  
 
 214  
         /**
 215  
          *
 216  
          * @param key
 217  
          * @return The value of the argument if it exists, or null.
 218  
          */
 219  
         public String getKeywordArgument(final String key) {
 220  0
             return argHash.get(key.toUpperCase());
 221  
         }
 222  
 
 223  
         /**
 224  
          *
 225  
          * @return available keys
 226  
          */
 227  
         public Set<String> getKeywords() {
 228  0
             return argHash.keySet();
 229  
         }
 230  
 
 231  
         /**
 232  
          *
 233  
          * @return Copy of the parsed arguments
 234  
          */
 235  
         public String[] getArgumentArray() {
 236  0
             String[] res = new String[argVector.size()];
 237  0
             return argVector.toArray(res);
 238  
         }
 239  
 
 240  
         /**
 241  
          *
 242  
          * @param index
 243  
          * @return argument at index
 244  
          */
 245  
         public String getArgument(final int index) {
 246  0
             if (index < argVector.size()) {
 247  0
                 return argVector.get(index);
 248  
             } else
 249  0
                 return null;
 250  
         }
 251  
 
 252  
         /**
 253  
          *
 254  
          * @return the raw argument string
 255  
          */
 256  
         public String getArgumentString() {
 257  0
             return argString;
 258  
         }
 259  
 
 260  
         public String toString() {
 261  0
             String res = "";
 262  0
             res += "Raw argument string:\n    " + argString + "\n";
 263  0
             res += "Argument array: " + length() + "\n";
 264  0
             for (String arg : getArgumentArray()) {
 265  0
                 res += "    " + arg + "\n";
 266  
             }
 267  0
             res += "Keywords:\n";
 268  0
             for (String key : getKeywords()) {
 269  0
                 res += "    " + key + " --> " + getKeywordArgument(key) + "\n";
 270  
             }
 271  0
             return res;
 272  
         }
 273  
     }
 274  
 
 275  
     static {
 276  0
         if (ENABLED){
 277  
 
 278  
             // Attach to console if running Java6 or greater
 279  0
             if(!System.getProperty("java.version").startsWith("1.5.")) { // <1.5 is not supported
 280  
 
 281  0
                 final InterpreterRepl consoleReplThread = new InterpreterRepl("CONSOLE_REPL") {
 282  0
                     Console console = System.console();
 283  
 
 284  
                     @Override
 285  
                     public void run() {
 286  0
                         while (console != null) {
 287  0
                             Reader r = console.reader();
 288  0
                             char c = ' ';
 289  
                             try {
 290  0
                                 c = (char) r.read();
 291  0
                             } catch (IOException e) {
 292  0
                                 return;
 293  0
                             }
 294  
 
 295  0
                             String line = "";
 296  0
                             while (RUN && c != '\n') {
 297  0
                                 line += c;
 298  
                                 try {
 299  0
                                     c = (char) r.read();
 300  0
                                 } catch (IOException e) {
 301  0
                                     return;
 302  0
                                 }
 303  
                             }
 304  0
                             if (line != null) { // check for null, this will happen
 305  
                                                 // on shutdown
 306  0
                                 if (line.length() > 0) {
 307  0
                                     console.printf("%s\n", eval(line));
 308  
                                 }
 309  
                             }
 310  0
                         }
 311  0
                     }
 312  
 
 313  
                     @Override
 314  
                     protected void output(String s) {
 315  0
                         if (console != null) {
 316  0
                             console.printf("%s\n", s);
 317  
                         }
 318  0
                     }
 319  
                 };
 320  0
                 consoleReplThread.setDaemon(true);
 321  0
                 consoleReplThread.start();
 322  0
                 REPL.add(consoleReplThread);
 323  
             }
 324  
 
 325  0
             addFunction("echo", new Function() {
 326  
                 public String execute(final Context ctx) {
 327  0
                     return ctx.getArgumentString();
 328  
                 }
 329  
 
 330  
                 public String describe() {
 331  0
                     return "Echo arguments.";
 332  
                 }
 333  
             });
 334  
 
 335  0
             addFunction("test-arguments", new Function() {
 336  
                 public String execute(final Context ctx) {
 337  0
                     return ctx.toString();
 338  
                 }
 339  
 
 340  
                 public String describe() {
 341  0
                     return "Just for testing.";
 342  
                 }
 343  
             });
 344  
 
 345  0
             addFunction("help", new Function() {
 346  
                 public String execute(final Context ctx) {
 347  0
                     if (ctx.length() == 1) {
 348  0
                         String res = "Help for " + ctx.getArgument(0) + "\n";
 349  0
                         final Function fun = FUNCTIONS.get(ctx.getArgument(0).trim());
 350  0
                         if (fun != null) {
 351  0
                             res += "    " + fun.help();
 352  
                         } else {
 353  0
                             res += "    Not found!\n";
 354  
                         }
 355  0
                         return res;
 356  
                     } else {
 357  0
                         String res = "Functions available: \n";
 358  
 
 359  0
                         List<String> tmp = new Vector<String>(FUNCTIONS.keySet());
 360  0
                         Collections.sort(tmp);
 361  
 
 362  0
                         for (String name : tmp) {
 363  0
                             res += "    " + name + "\n";
 364  0
                             res += "        " + FUNCTIONS.get(name).describe() + "\n";
 365  
                         }
 366  0
                         return res;
 367  
                     }
 368  
                 }
 369  
 
 370  
                 public String describe() {
 371  0
                     return "Print this help message";
 372  
                 }
 373  
             });
 374  
 
 375  0
             addFunction("gc", new Function() {
 376  
                 public String execute(final Context ctx) {
 377  0
                     System.gc();
 378  0
                     return "GC requested";
 379  
                 }
 380  
 
 381  
                 public String describe() {
 382  0
                     return "Request a gc run.";
 383  
                 }
 384  
             });
 385  
 
 386  0
             addFunction("all-stacktraces", new Function() {
 387  
                 public String execute(final Context ctx) {
 388  0
                     String res = "";
 389  0
                     Map<Thread, StackTraceElement[]> m = Thread.getAllStackTraces();
 390  0
                     for (Thread thread : m.keySet()) {
 391  0
                         res += "Thread:: " + thread.toString() + "\n";
 392  0
                         for (StackTraceElement ste : m.get(thread)) {
 393  0
                             res += "   " + ste.toString() + "\n";
 394  
                         }
 395  0
                         res += "\n";
 396  
                     }
 397  0
                     return res;
 398  
                 }
 399  
 
 400  
                 public String describe() {
 401  0
                     return "Look at all stacktraces.";
 402  
                 }
 403  
             });
 404  
 
 405  0
             addFunction("loggers", new Function() {
 406  
                 public String execute(final Context ctx) {
 407  0
                     String res = "Active loggers:\n";
 408  0
                     final LoggerRepository repo = Logger.getRootLogger().getLoggerRepository();
 409  
 
 410  0
                     final Enumeration<?> e = repo.getCurrentLoggers();
 411  0
                     while (e.hasMoreElements()) {
 412  0
                         final Logger logger = (Logger) e.nextElement();
 413  0
                         if (ctx.length() == 0 || (ctx.length() > 0 && logger.getName().matches(ctx.getArgument(0)))) {
 414  0
                             res += logger.getName() + " " + logger.getLevel();
 415  0
                             if (ctx.length() == 2) {
 416  0
                                 final Level level = Level.toLevel(ctx.getArgument(1));
 417  0
                                 if (level.toString().equalsIgnoreCase(ctx.getArgument(1))) {
 418  0
                                     res += " (Setting level to " + level + ")";
 419  0
                                     logger.setLevel(level);
 420  
                                 } else {
 421  0
                                     res += " (unknown debug level: " + ctx.getArgument(1) + ")";
 422  
                                 }
 423  
                             }
 424  0
                             res += "\n";
 425  
                         }
 426  0
                     }
 427  0
                     return res;
 428  
                 }
 429  
 
 430  
                 public String describe() {
 431  0
                     return "Print active loggers, and set level if specified. 'loggers [regexp] [level]'";
 432  
                 }
 433  
             });
 434  
 
 435  0
             addFunction("properties", new Function() {
 436  
                 public String execute(final Context ctx) {
 437  0
                     String res = "Properties: \n";
 438  0
                     Properties p = System.getProperties();
 439  0
                     TreeSet<Object> propKeys = new TreeSet<Object>(p.keySet());
 440  0
                     for (Iterator<Object> it = propKeys.iterator(); it.hasNext();) {
 441  0
                         String key = it.next().toString();
 442  0
                         res += key + "=" + p.get(key) + "\n";
 443  0
                     }
 444  0
                     for (String key : ctx.getKeywords()) {
 445  0
                         res += "Setting : " + key + " --> " + ctx.getKeywordArgument(key) + "\n";
 446  0
                         System.setProperty(key, ctx.getKeywordArgument(key));
 447  
                     }
 448  0
                     return res;
 449  
                 }
 450  
 
 451  
                 public String describe() {
 452  0
                     return "List System.getProperties()";
 453  
                 }
 454  
             });
 455  
 
 456  0
             addFunction("save", new Function() {
 457  
                 public String execute(final Context ctx) {
 458  0
                     if (ctx.length() == 2) {
 459  
                         try {
 460  0
                             String file = ctx.getArgument(1);
 461  0
                             FileWriter s = new FileWriter(file);
 462  0
                             BufferedWriter o = new BufferedWriter(s);
 463  0
                             o.write(eval(ctx.getArgument(0)));
 464  0
                             o.close();
 465  0
                             return "Wrote output to file: " + file;
 466  0
                         } catch (Exception e) {
 467  0
                             return "Error: " + e.getMessage();
 468  
                         }
 469  
                     } else {
 470  0
                         return "Argument count must be 2";
 471  
                     }
 472  
                 }
 473  
 
 474  
                 public String describe() {
 475  0
                     return "Save output of function to file. 'save function filename'";
 476  
                 }
 477  
             });
 478  
 
 479  0
             addFunction("read", new Function() {
 480  
                 public String execute(final Context ctx) {
 481  0
                     if (ctx.length() == 1) {
 482  
                         try {
 483  0
                             String file = ctx.getArgument(0);
 484  0
                             FileReader s = new FileReader(file);
 485  0
                             BufferedReader i = new BufferedReader(s);
 486  
 
 487  0
                             String inline = "";
 488  0
                             while ((inline = i.readLine()) != null) {
 489  0
                                 eval(inline);
 490  
                             }
 491  0
                             i.close();
 492  0
                             return "Read file: " + file;
 493  0
                         } catch (Exception e) {
 494  0
                             return "Error: " + e.getMessage();
 495  
                         }
 496  
                     } else {
 497  0
                         return "Argument count must be 1";
 498  
                     }
 499  
                 }
 500  
 
 501  
                 public String describe() {
 502  0
                     return "Read the file, and evaluate each line. 'read file'";
 503  
                 }
 504  
             });
 505  
 
 506  0
             addFunction("macro", new Function() {
 507  
                 public String execute(final Context ctx) {
 508  0
                     addFunction(ctx.getArgument(0), new Function() {
 509  
                         public String execute(final Context c) {
 510  0
                             return eval(ctx.getArgumentString().substring(ctx.getArgument(0).length()).trim());
 511  
                         }
 512  
                     });
 513  0
                     return ctx.getArgumentString();
 514  
                 }
 515  
 
 516  
                 public String describe() {
 517  0
                     return "Make a macro";
 518  
                 }
 519  
             });
 520  
 
 521  0
             addFunction("quit", new Function() {
 522  
                 public String execute(final Context ctx) {
 523  0
                     RUN = false;
 524  0
                     return "Bye!";
 525  
                 }
 526  
 
 527  
                 public String describe() {
 528  0
                     return "Stop this interpreter.";
 529  
                 }
 530  
             });
 531  
         }
 532  0
     }
 533  
 
 534  
     /**
 535  
      * Evaluate the expression
 536  
      *
 537  
      * @param expr
 538  
      *            to be evaluated.
 539  
      * @return result
 540  
      */
 541  
     public synchronized static String eval(String expr) {
 542  0
         final int split = expr.trim().indexOf(" ");
 543  
         final String function;
 544  
         final String arguments;
 545  0
         if (split > 0) {
 546  0
             function = expr.substring(0, split);
 547  0
             arguments = expr.substring(split).trim();
 548  
         } else {
 549  0
             function = expr;
 550  0
             arguments = "";
 551  
         }
 552  0
         if (FUNCTIONS.containsKey(function)) {
 553  
             try {
 554  0
                 return FUNCTIONS.get(function).execute(new Context(arguments));
 555  0
             } catch (Throwable e) {
 556  0
                 String error = "Error: " + e + "\n";
 557  0
                 for (StackTraceElement ste : e.getStackTrace()) {
 558  0
                     error += "    " + ste.toString() + "\n";
 559  
                 }
 560  0
                 return error + "\n";
 561  
             }
 562  
         } else {
 563  0
             return "Unknown function: " + function;
 564  
         }
 565  
     }
 566  
 }