/* * Copyright 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.dev.typeinfo.test; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.BadTypeArgsException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JConstructor; import com.google.gwt.core.ext.typeinfo.JField; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.ParseException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.core.ext.typeinfo.TypeOracleException; import com.google.gwt.dev.javac.TypeOracleMediator; import com.google.gwt.dev.util.log.AbstractTreeLogger; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import java.util.Set; public class InteractiveTypeOracle { public static interface CommandHandler { /** * Specifies the keyword causes this handler to be invoked. */ String getCommandToken(); /** * Executes the command given the specified args, writing the results or an * error. * * @return <code>false</code> if there was a problem, in which case the * error description will have been written to results */ boolean process(TreeLogger logger, String[] args); boolean requiresCurrentType(); } public static void main(String[] args) throws UnableToCompleteException { AbstractTreeLogger logger = new PrintWriterTreeLogger(); // See if we should create a gui logger. // TODO: this was removed to avoid making an SWT dependency from test code // // for (int i = 0; i < args.length; i++) { // if ("-gui".equals(args[i])) { // logger = TreeLoggerWidget.getAsDetachedWindow( // "Interactive Type Oracle Log", 700, 600, true); // break; // } // } String logLevel = System.getProperty("gwt.logLevel"); if (logLevel != null) { logger.setMaxDetail(TreeLogger.Type.valueOf(logLevel)); } InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); // Build an oracle. // TypeOracleMediator mediator = new TypeOracleMediator(); TypeOracle oracle = mediator.getTypeOracle(); // TODO: add compilation units // Create an interactive wrapper around the oracle. // InteractiveTypeOracle ito = new InteractiveTypeOracle(oracle); try { String command; System.out.print("> "); System.out.flush(); while (null != (command = br.readLine())) { ito.processCommand(logger, command); System.out.print("> "); System.out.flush(); } } catch (IOException e) { System.err.println("Error reading stdin"); e.printStackTrace(); } } public InteractiveTypeOracle(TypeOracle oracle) { this.oracle = oracle; registerCommandHandler(cmdHelp); registerCommandHandler(cmdParse); registerCommandHandler(cmdSelectType); registerCommandHandler(cmdAllTypes); registerCommandHandler(cmdSubtypes); registerCommandHandler(cmdSupertypes); registerCommandHandler(cmdEnclosing); registerCommandHandler(cmdOverloads); registerCommandHandler(cmdNested); registerCommandHandler(cmdConstructors); registerCommandHandler(cmdMethods); registerCommandHandler(cmdFields); } public boolean processCommand(TreeLogger logger, String command) { String[] tokens = command.split("[ \t]"); return processCommand(logger, tokens); } public boolean processCommand(TreeLogger logger, String[] tokens) { if (tokens.length == 0) { logger.log(TreeLogger.WARN, "Expecting a command", null); return false; } CommandHandler handler = handlers.get(tokens[0]); if (handler == null) { logger.log(TreeLogger.WARN, "Unknown command: " + tokens[0], null); cmdHelp.process(logger, new String[0]); return false; } if (currType == null && handler.requiresCurrentType()) { logger.log(TreeLogger.WARN, "This command requires a current type to be selected", null); return false; } String[] args = new String[tokens.length - 1]; System.arraycopy(tokens, 1, args, 0, args.length); return handler.process(logger, args); } /** * Registers a command handler. * * @return if not <code>null</code>, the previous handler for the same * command token */ public CommandHandler registerCommandHandler(CommandHandler handler) { String token = handler.getCommandToken(); CommandHandler old = handlers.put(token, handler); return old; } private final CommandHandler cmdAllTypes = new CommandHandler() { public String getCommandToken() { return "all-types"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 0) { logger.log(TreeLogger.WARN, "No arguments expected", null); logger.log(TreeLogger.INFO, "Usage: all-types", null); logger.log(TreeLogger.INFO, "Prints all known types", null); return false; } JClassType[] types = oracle.getTypes(); oracle.sort(types); TreeLogger sublogger = null; for (int i = 0; i < types.length; i++) { if (sublogger == null) { sublogger = logger.branch(TreeLogger.INFO, "All known types", null); } JClassType type = types[i]; String typename = type.getQualifiedSourceName(); sublogger.log(TreeLogger.INFO, typename, null); } return true; } public boolean requiresCurrentType() { return false; } }; private final CommandHandler cmdHelp = new CommandHandler() { public String getCommandToken() { return "help"; } public boolean process(TreeLogger logger, String[] args) { TreeLogger sublogger = null; Set<String> keySet = handlers.keySet(); String[] cmdTokens = keySet.toArray(new String[keySet.size()]); for (int i = 0; i < cmdTokens.length; i++) { String cmdToken = cmdTokens[i]; if (sublogger == null) { sublogger = logger.branch(TreeLogger.INFO, "Available commands", null); } sublogger.log(TreeLogger.INFO, cmdToken, null); } return true; } public boolean requiresCurrentType() { return false; } }; private final CommandHandler cmdSelectType = new CommandHandler() { public String getCommandToken() { return "select"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 1) { logger.log(TreeLogger.WARN, "Expected a type name", null); logger.log(TreeLogger.INFO, "Usage: select <typename>", null); return false; } String typename = args[0]; JClassType type = oracle.findType(typename); if (type == null) { logger.log(TreeLogger.WARN, "Cannot find type " + typename, null); return false; } currType = type; logger.log(TreeLogger.INFO, "Current type is now " + type.getQualifiedSourceName(), null); return true; } public boolean requiresCurrentType() { return false; } }; private final CommandHandler cmdSubtypes = new CommandHandler() { public String getCommandToken() { return "subtypes"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 0) { logger.log(TreeLogger.WARN, "No arguments expected", null); logger.log(TreeLogger.INFO, "Usage: subtypes", null); logger.log(TreeLogger.INFO, "Prints all subtypes of the current type", null); return false; } String typename = currType.getQualifiedSourceName(); TreeLogger sublogger = null; JClassType[] subtypes = currType.getSubtypes(); oracle.sort(subtypes); for (int i = 0; i < subtypes.length; i++) { if (sublogger == null) { sublogger = logger.branch(TreeLogger.INFO, "Subtypes of " + typename, null); } sublogger.log(TreeLogger.INFO, subtypes[i].getQualifiedSourceName(), null); } return true; } public boolean requiresCurrentType() { return true; } }; private final CommandHandler cmdFields = new CommandHandler() { public String getCommandToken() { return "fields"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 0) { logger.log(TreeLogger.WARN, "No arguments expected", null); logger.log(TreeLogger.INFO, "Usage: fields", null); logger.log(TreeLogger.INFO, "Prints the fields of the current type", null); return false; } String typename = currType.getQualifiedSourceName(); TreeLogger sublogger = null; JField[] fields = currType.getFields(); oracle.sort(fields); for (int i = 0; i < fields.length; i++) { if (sublogger == null) { sublogger = logger.branch(TreeLogger.INFO, "Fields of " + typename, null); } sublogger.log(TreeLogger.INFO, fields[i].toString(), null); } return true; } public boolean requiresCurrentType() { return true; } }; private final CommandHandler cmdMethods = new CommandHandler() { public String getCommandToken() { return "methods"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 0) { logger.log(TreeLogger.WARN, "No arguments expected", null); logger.log(TreeLogger.INFO, "Usage: methods", null); logger.log(TreeLogger.INFO, "Prints the methods of the current type", null); return false; } String typename = currType.getQualifiedSourceName(); TreeLogger sublogger = null; JMethod[] methods = currType.getMethods(); oracle.sort(methods); for (int i = 0; i < methods.length; i++) { if (sublogger == null) { sublogger = logger.branch(TreeLogger.INFO, "Methods of " + typename, null); } sublogger.log(TreeLogger.INFO, methods[i].toString(), null); } return true; } public boolean requiresCurrentType() { return true; } }; private final CommandHandler cmdNested = new CommandHandler() { public String getCommandToken() { return "nested"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 0) { logger.log(TreeLogger.WARN, "No arguments expected", null); logger.log(TreeLogger.INFO, "Usage: nested", null); logger.log(TreeLogger.INFO, "Prints the nested types of the current type", null); return false; } String typename = currType.getQualifiedSourceName(); TreeLogger sublogger = null; JClassType[] nestedTypes = currType.getNestedTypes(); oracle.sort(nestedTypes); for (int i = 0; i < nestedTypes.length; i++) { if (sublogger == null) { sublogger = logger.branch(TreeLogger.INFO, "Types nested inside " + typename, null); } sublogger.log(TreeLogger.INFO, nestedTypes[i].toString(), null); } return true; } public boolean requiresCurrentType() { return true; } }; private final CommandHandler cmdParse = new CommandHandler() { public String getCommandToken() { return "parse"; } public boolean process(TreeLogger logger, String[] args) { if (args.length == 0) { logger.log(TreeLogger.WARN, "At least one argument expected", null); logger.log(TreeLogger.INFO, "Usage: parse ...", null); logger.log(TreeLogger.INFO, "Parses a string into a type", null); return false; } String mushed = ""; for (int i = 0; i < args.length; i++) { mushed += args[i]; } try { JType type = oracle.parse(mushed); logger.log(TreeLogger.INFO, type.getQualifiedSourceName(), null); } catch (NotFoundException e) { logger.log(TreeLogger.WARN, "Could not find type", e); } catch (ParseException e) { logger.log(TreeLogger.WARN, "Unable to parse " + mushed, e); } catch (BadTypeArgsException e) { logger.log(TreeLogger.WARN, "Bad arguments " + mushed, e); } catch (TypeOracleException e) { logger.log(TreeLogger.WARN, "Some other type oracle exception while parsing " + mushed, e); } return true; } public boolean requiresCurrentType() { return false; } }; private final CommandHandler cmdEnclosing = new CommandHandler() { public String getCommandToken() { return "enclosing"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 0) { logger.log(TreeLogger.WARN, "No arguments", null); logger.log(TreeLogger.INFO, "Usage: enclosing", null); logger.log(TreeLogger.INFO, "Prints the enclosing type of the current type", null); return false; } JClassType enclosingType = currType.getEnclosingType(); if (enclosingType != null) { logger.log(TreeLogger.INFO, enclosingType.getQualifiedSourceName(), null); } return true; } public boolean requiresCurrentType() { return true; } }; private final CommandHandler cmdConstructors = new CommandHandler() { public String getCommandToken() { return "ctors"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 0) { logger.log(TreeLogger.WARN, "No arguments", null); logger.log(TreeLogger.INFO, "Usage: ctors", null); logger.log(TreeLogger.INFO, "Prints the constructors of the current type", null); return false; } String typename = currType.getQualifiedSourceName(); JConstructor[] ctors = currType.getConstructors(); oracle.sort(ctors); TreeLogger sublogger = null; for (int i = 0; i < ctors.length; i++) { if (sublogger == null) { sublogger = logger.branch(TreeLogger.INFO, "Constructors of " + typename, null); } sublogger.log(TreeLogger.INFO, ctors[i].toString(), null); } return true; } public boolean requiresCurrentType() { return true; } }; private final CommandHandler cmdOverloads = new CommandHandler() { public String getCommandToken() { return "overloads"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 1) { logger.log(TreeLogger.WARN, "One argument is expected", null); logger.log(TreeLogger.INFO, "Usage: overloads <method-name>", null); logger.log(TreeLogger.INFO, "Prints the overloads of a particular method in the current type", null); return false; } String typename = currType.getQualifiedSourceName(); JMethod[] overloads = currType.getOverloads(args[0]); oracle.sort(overloads); TreeLogger sublogger = null; for (int i = 0; i < overloads.length; i++) { if (sublogger == null) { sublogger = logger.branch(TreeLogger.INFO, "Overloads in " + typename + " of " + args[0], null); } sublogger.log(TreeLogger.INFO, overloads[i].toString(), null); } return true; } public boolean requiresCurrentType() { return true; } }; private final CommandHandler cmdSupertypes = new CommandHandler() { public String getCommandToken() { return "supertypes"; } public boolean process(TreeLogger logger, String[] args) { if (args.length != 0) { logger.log(TreeLogger.WARN, "No arguments", null); logger.log(TreeLogger.INFO, "Usage: supertypes", null); logger.log(TreeLogger.INFO, "Prints the hierarchy of supertypes of the current type", null); return false; } String typename = currType.getQualifiedSourceName(); logger = logger.branch(TreeLogger.INFO, "Supertypes of " + typename, null); printSupertypesImpl(logger, currType); return true; } private void printSupertypesImpl(TreeLogger parentLogger, JClassType type) { TreeLogger childLogger; // Superclass JClassType superclass = type.getSuperclass(); if (superclass != null) { String name = superclass.getQualifiedSourceName(); childLogger = parentLogger.branch(TreeLogger.INFO, name, null); printSupertypesImpl(childLogger, superclass); } // Superinterfaces JClassType[] superintfs = type.getImplementedInterfaces(); for (int i = 0; i < superintfs.length; i++) { JClassType superintf = superintfs[i]; String name = superintf.getQualifiedSourceName(); childLogger = parentLogger.branch(TreeLogger.INFO, name, null); printSupertypesImpl(childLogger, superintf); } } public boolean requiresCurrentType() { return true; } }; private JClassType currType; private final Map<String, CommandHandler> handlers = new HashMap<String, CommandHandler>(); private final TypeOracle oracle; }