Permalink
Browse files

Merge branch 'develop' of github.com:bendisposto/probcore into develop

  • Loading branch information...
bendisposto committed May 17, 2012
2 parents 3a092a4 + 621ddaa commit 11cba1175f223f51f09503607df93ecc0a0d425e
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * 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 org.codehaus.groovy.tools.shell
+
+import jline.Completor
+import jline.ConsoleReader
+import jline.History
+import jline.MultiCompletor
+import org.codehaus.groovy.tools.shell.util.Logger
+
+
+/**
+ * Support for running a {@link Shell} interactively using the JLine library.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class PInteractiveShellRunner
+extends ShellRunner
+implements Runnable {
+ final ConsoleReader reader
+
+ final Closure prompt
+
+ final CommandsMultiCompletor completor
+
+ PInteractiveShellRunner(final Shell shell, final Closure prompt) {
+ super(shell)
+
+ this.prompt = prompt
+
+ this.reader = new ConsoleReader(shell.io.inputStream, new PrintWriter(shell.io.outputStream, true))
+
+ reader.addCompletor(new PReflectionCompletor(shell))
+ this.completor = new CommandsMultiCompletor()
+
+ reader.addCompletor(completor)
+ }
+
+ void run() {
+ for (command in shell.registry) {
+ completor << command
+ }
+
+ // Force things to become clean
+ completor.refresh()
+
+ // And then actually run
+ adjustHistory()
+ super.run()
+ }
+
+ void setHistory(final History history) {
+ reader.history = history
+ }
+
+ void setHistoryFile(final File file) {
+ def dir = file.parentFile
+
+ if (!dir.exists()) {
+ dir.mkdirs()
+
+ log.debug("Created base directory for history file: $dir")
+ }
+
+ log.debug("Using history file: $file")
+
+ reader.history.historyFile = file
+ }
+
+ protected String readLine() {
+ try {
+ return reader.readLine(prompt.call())
+ }
+ catch (StringIndexOutOfBoundsException e) {
+ log.debug("HACK: Try and work around GROOVY-2152 for now", e)
+
+ return "";
+ }
+ }
+
+ @Override
+ protected boolean work() {
+ boolean result= super.work()
+ adjustHistory()
+
+ result
+ }
+
+ private void adjustHistory() {
+ if (shell instanceof Groovysh) {
+ shell.historyFull = shell.history.size() >= shell.history.maxSize
+ if (shell.historyFull) shell.evictedLine = shell.history.historyList[0]
+ }
+ }
+}
+
+/**
+ * Completor for interactive shells.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class CommandsMultiCompletor
+extends MultiCompletor {
+ protected final Logger log = Logger.create(this.class)
+
+ List/*<Completor>*/ list = []
+
+ private boolean dirty = false
+
+ def leftShift(final Command command) {
+ assert command
+
+ //
+ // FIXME: Need to handle completor removal when things like aliases are rebound
+ //
+
+ def c = command.completor
+
+ if (c) {
+ list << c
+
+ log.debug("Added completor[${list.size()}] for command: $command.name")
+
+ dirty = true
+ }
+ }
+
+ void refresh() {
+ log.debug("Refreshing the completor list")
+
+ completors = list as Completor[]
+ dirty = false
+ }
+
+ int complete(final String buffer, final int pos, final List cand) {
+ assert buffer != null
+
+ //
+ // FIXME: This is a bit of a hack, I'm too lazy to rewrite a more efficient
+ // completor impl that is more dynamic than the jline.MultiCompletor version
+ // so just re-use it and reset the list as needed
+ //
+
+ if (dirty) {
+ refresh()
+ }
+
+ return super.complete(buffer, pos, cand)
+ }
+}
+
@@ -0,0 +1,131 @@
+package org.codehaus.groovy.tools.shell
+
+import jline.Completor
+import org.codehaus.groovy.runtime.InvokerHelper
+
+/**
+ * Implements the Completor interface to provide competions for
+ * GroovyShell by using reflection on global variables.
+ *
+ * @author <a href="mailto:probabilitytrees@gmail.com">Marty Saxton</a>
+ */
+class PReflectionCompletor implements Completor {
+
+ private Shell shell;
+
+ PReflectionCompletor(Shell shell) {
+ this.shell = shell
+ }
+
+ int complete(String buffer, int cursor, List candidates) {
+
+ int identifierStart = findIdentifierStart(buffer, cursor)
+ //FIXME: Figure out a new way to get the identifier prefix
+ //It should be the substring before the last '.' or '('
+ String identifierPrefix = identifierStart != -1 ? buffer.substring(identifierStart, cursor) : ""
+ int lastDot = buffer.lastIndexOf('.')
+
+ // if there are no dots, and there is a valid identifier prefix
+ if (lastDot == -1 || noDotsBeforeParentheses(buffer, cursor) ) {
+ if (identifierStart != -1) {
+ List myCandidates = findMatchingVariables(identifierPrefix)
+ if (myCandidates.size() > 0) {
+ candidates.addAll(myCandidates)
+ return identifierStart
+
+ }
+ }
+ } else {
+ // there are 1 or more dots
+ // if ends in a dot, or if there is a valid identifier prefix
+ if (lastDot == cursor-1 || identifierStart != -1){
+ // evaluate the part before the dot to get an instance
+ int predecessorStart=findIdentifierStart(buffer,lastDot)
+
+ String instanceRefExpression = buffer.substring(predecessorStart, lastDot)
+ def instance = shell.interp.evaluate([instanceRefExpression])
+ if (instance != null) {
+ // look for public methods/fields that match the prefix
+ List myCandidates = getPublicFieldsAndMethods(instance, identifierPrefix)
+ if (myCandidates.size() > 0) {
+ candidates.addAll(myCandidates)
+ return lastDot+1
+ }
+ }
+ }
+ }
+
+ // no candidates
+ return -1
+ }
+
+ /**
+ * Parse a buffer to determine the start index of the groovy identifier
+ * @param buffer the buffer to parse
+ * @param endingAt the end index with the buffer
+ * @return the start index of the identifier, or -1 if the buffer
+ * does not contain a valid identifier that ends at endingAt
+ */
+ int findIdentifierStart(String buffer, int endingAt) {
+ // if the string is empty then there is no expression
+ if (endingAt == 0)
+ return -1
+ // if the last character is not valid then there is no expression
+ char lastChar = buffer.charAt(endingAt-1)
+ if (!Character.isJavaIdentifierPart(lastChar) )
+ return -1
+ // scan backwards until the beginning of the expression is found
+ int startIndex = endingAt-1
+ while (startIndex > 0 && Character.isJavaIdentifierPart(buffer.charAt(startIndex-1)))
+ --startIndex
+ return startIndex
+ }
+
+
+ /**
+ * Build a list of public fields and methods for an object
+ * that match a given prefix.
+ * @param instance the object
+ * @param prefix the prefix that must be matched
+ * @return the list of public methods and fields that begin with the prefix
+ */
+ List getPublicFieldsAndMethods(Object instance, String prefix) {
+ def rv = []
+ instance.class.fields.each {
+ if (it.name.startsWith(prefix))
+ rv << it.name
+ }
+ instance.class.methods.each {
+ if (it.name.startsWith(prefix))
+ rv << it.name + (it.parameterTypes.length == 0 ? "()" : "(")
+ }
+ InvokerHelper.getMetaClass(instance).metaMethods.each {
+ if (it.name.startsWith(prefix))
+ rv << it.name + (it.parameterTypes.length == 0 ? "()" : "(")
+ }
+ return rv.sort().unique()
+ }
+
+ /**
+ * Build a list of variables defined in the shell that
+ * match a given prefix.
+ * @param prefix the prefix to match
+ * @return the list of variables that match the prefix
+ */
+ List findMatchingVariables(String prefix) {
+ def matches = []
+ for (String varName in shell.interp.context.variables.keySet())
+ if (varName.startsWith(prefix))
+ matches << varName
+ return matches
+ }
+
+ Boolean noDotsBeforeParentheses(String buffer, int endingAt) {
+ int lastDotIndex = buffer.lastIndexOf('.')
+ int lastParanIndex = buffer.lastIndexOf('(')
+
+ if(lastDotIndex==-1&&lastParanIndex==-1)
+ return true;
+ return lastDotIndex < lastParanIndex
+ }
+}
@@ -60,7 +60,7 @@ extends Shell {
final version
- InteractiveShellRunner runner
+ PInteractiveShellRunner runner
History history
@@ -413,7 +413,7 @@ extends Shell {
loadUserScript('groovysh.rc')
// Setup the interactive runner
- runner = new InteractiveShellRunner(this, this.&renderPrompt as Closure)
+ runner = new PInteractiveShellRunner(this, this.&renderPrompt as Closure)
// Setup the history
runner.history = history = new History()
@@ -26,7 +26,7 @@
/**
* Command to execute an event that has not been enumerated by ProB, for further
- * information see ({@link #getOperation})
+ * information see ({@link #getOperations})
*
* @author Jens Bendisposto
*
@@ -58,7 +58,7 @@ public GetOperationByPredicateCommand(final String stateId,
*
* @throws ProBException
*
- * @see de.prob.core.command.IComposableCommand#writeCommand(de.prob.prolog.output.IPrologTermOutput)
+ * @see de.prob.animator.command.ICommand#writeCommand(de.prob.prolog.output.IPrologTermOutput)
*/
@Override
public void writeCommand(final IPrologTermOutput pto) throws ProBException {
@@ -78,7 +78,7 @@ public void writeCommand(final IPrologTermOutput pto) throws ProBException {
*
* @throws ProBException
*
- * @see de.prob.core.command.IComposableCommand#writeCommand(de.prob.prolog.output.IPrologTermOutput)
+ * @see de.prob.animator.command.ICommand#writeCommand(de.prob.prolog.output.IPrologTermOutput)
*/
@Override
public void processResult(
@@ -43,8 +43,7 @@
/**
* @param predicate
* is a parsed predicate or <code>null</code>
- * @see LanguageDependendAnimationPart#parsePredicate(IPrologTermOutput,
- * String, boolean)
+ *
*/
public ConstraintBasedDeadlockCheckCommand(final PrologTerm predicate) {
this.predicate = predicate;
@@ -104,7 +103,7 @@ public void processResult(
logger.error("Result from Prolog was not as expected.", e);
throw new ProBException();
}
-
+
} else {
logger.error("unexpected result from deadlock check: " + resultTerm);
throw new ProBException();
Oops, something went wrong.

0 comments on commit 11cba11

Please sign in to comment.