/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.shell;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import org.jnode.driver.console.CompletionInfo;
/**
* A CommandCompletions object is used to accumulate completion strings
* during the process of command line completion.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
* @author crawley@jnode.org
*/
public class CommandCompletions implements CompletionInfo {
private static final SortedSet<String> NO_COMPLETIONS =
Collections.unmodifiableSortedSet(new TreeSet<String>());
private TreeSet<String> completions;
private int completionStart = -1;
private final CommandInterpreter interpreter;
/**
* Instantiate with a CommandInterpreter instance that will be used
* to escape shell meta-characters in the completions.
* @param interpreter the CommandInterpreter for escaping completions.
*/
public CommandCompletions(CommandInterpreter interpreter) {
this.interpreter = interpreter;
}
/**
* Instantiate without a CommandInterpreter. Completions are captured
* as-is.
*/
public CommandCompletions() {
this.interpreter = null;
}
/**
* This method is called to register a possible completion. A null or empty
* completion string will be quietly ignored.
*
* @param completion the completion string
* @param partial if <code>true</code>, further completions of the
* completion string may be possible.
*/
public void addCompletion(String completion, boolean partial) {
if (completion == null || completion.length() == 0) {
return;
}
if (completions == null) {
completions = new TreeSet<String>();
}
if (interpreter != null) {
completion = interpreter.escapeWord(completion);
}
if (!partial) {
completion += ' ';
}
completions.add(completion);
}
/**
* This method is called to register a completion than cannot be completed
* further. A null or empty completion string will be quietly ignored.
*
* @param completion the completion string
*/
public void addCompletion(String completion) {
addCompletion(completion, false);
}
/**
* Retrieve the completion details.
*
* @return a TreeSet consisting of all possible completions
*/
public SortedSet<String> getCompletions() {
return completions == null ? NO_COMPLETIONS : completions;
}
/**
* Render for debug purposes
*/
public String toString() {
StringBuilder sb = new StringBuilder("CommandCompletions{");
sb.append("competionStart=").append(completionStart);
sb.append(",completions=");
if (completions == null) {
sb.append("null");
} else {
sb.append('[');
boolean first = true;
for (String completion : completions) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append('\'');
sb.append(completion);
sb.append('\'');
}
sb.append(']');
}
sb.append('}');
return sb.toString();
}
/**
* The completion start is the offset in the original string of the first
* character to be replaced with the 'completed'.
*
* @return the completion start position, or <code>-1</code>
*/
public int getCompletionStart() {
return completionStart;
}
/**
* Set the completion start position. This can only be set once. After that,
* attempts to change the start position will throw {@link IllegalArgumentException}.
*
* @param completionStart
*/
public void setCompletionStart(int completionStart) {
if (this.completionStart != completionStart) {
if (this.completionStart != -1) {
throw new IllegalArgumentException(
"completionStart cannot be changed");
}
this.completionStart = completionStart;
}
}
/**
* Get the combined completion string. If there are multiple alternatives,
* this will be the longest common left substring of the alternatives. If
* the substring is zero length, or if there were no alternatives in the
* first place, the result is <code>null</code>.
*
* @return the combined completion, or <code>null</code>.
*/
public String getCompletion() {
if (completions == null) {
return null;
}
int nos = completions.size();
if (nos == 0) {
return null;
}
if (nos == 1) {
return completions.first();
}
String common = completions.first();
for (String completion : completions) {
if (common != completion && !completion.startsWith(common)) {
for (int i = 0; i < common.length(); i++) {
if (common.charAt(i) != completion.charAt(i)) {
if (i == 0) {
return null;
}
common = common.substring(0, i);
break;
}
}
}
}
return common;
}
}