package pt.treps.autocompletion;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import org.fife.ui.autocomplete.AbstractCompletionProvider;
import org.fife.ui.autocomplete.Completion;
import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.autocomplete.DefaultCompletionProvider;
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
import org.fife.ui.rsyntaxtextarea.Token;
import org.pushingpixels.substance.api.renderers.SubstanceDefaultListCellRenderer;
import pt.opensoft.util.ListUtil;
import pt.opensoft.util.StringUtil;
import pt.treps.TReps;
import pt.treps.parser.MatchableItem;
import pt.treps.parser.MatchableShorthandCompletion;
import pt.treps.parser.Parser;
import pt.treps.parser.Project;
import pt.treps.parser.ProjectTask;
import pt.treps.parser.Task;
import pt.treps.ui.MainFrame;
import pt.treps.ui.ProjectTaskListRenderer;
public class TimeCompletionProvider extends AbstractCompletionProvider {
public MainFrame mainFrame;
/**
* The provider to use when no provider is assigned to a particular token
* type.
*/
private CompletionProvider defaultProvider;
private Parser<ProjectTask> projectsTasksParser;
/**
* The provider to use when completing in a string.
*/
private Parser<Project> projectsParser;
/**
* The provider to use when completing in a comment.
*/
private Parser<Task> tasksParser;
public TimeCompletionProvider(MainFrame mainF) {
this.mainFrame = mainF;
this.defaultProvider = createDefaultCompletionProvider();
createProjectsCompletionProvider();
completions = new ArrayList(0);
super.setListCellRenderer(new SubstanceDefaultListCellRenderer());
}
private CompletionProvider createDefaultCompletionProvider() {
DefaultCompletionProvider cp = new DefaultCompletionProvider();
List<MatchableItem<ProjectTask>> items = new ArrayList<MatchableItem<ProjectTask>>();
for (Project project : TReps.getProjects()) {
for (Task task : project.getTasks()) {
items.add(new MatchableItem<ProjectTask>(new ProjectTask(project, task), task.getId(), project.getLabel() + " - " + task.getLabel()));
}
}
projectsTasksParser = new Parser<ProjectTask>(items);
return cp;
}
/**
* Returns the completion provider to use when the caret is in a string.
*
* @return The provider.
* @see #createTaskCompletionProvider()
*/
private CompletionProvider createProjectsCompletionProvider() {
DefaultCompletionProvider cp = new DefaultCompletionProvider();
List<MatchableItem<Project>> items = new ArrayList<MatchableItem<Project>>();
for (Project project : TReps.getProjects()) {
items.add(new MatchableItem<Project>(project, project.getId(), project.getLabel()));
}
projectsParser = new Parser<Project>(items);
return cp;
}
/**
* Returns the provider to use when in a comment.
*
* @return The provider.
* @see #createProjectsCompletionProvider()
*/
private CompletionProvider createTaskCompletionProvider() {
DefaultCompletionProvider cp = new DefaultCompletionProvider();
List<MatchableItem<Task>> items = new ArrayList<MatchableItem<Task>>();
for (Task task : TReps.getSelectedProject().getTasks()) {
items.add(new MatchableItem<Task>(task, task.getId(), task.getLabel()));
}
tasksParser = new Parser<Task>(items);
return cp;
}
private CompletionProvider getProviderFor(JTextComponent comp) {
RSyntaxTextArea rsta = (RSyntaxTextArea)comp;
RSyntaxDocument doc = (RSyntaxDocument)rsta.getDocument();
int line = rsta.getCaretLineNumber();
Token t = doc.getTokenListForLine(line);
if (t==null) {
return this.defaultProvider;
}
int dot = rsta.getCaretPosition();
//Token curToken = RSyntaxUtilities.getTokenAtOffset(t, dot);
Token curToken = RSyntaxUtilities.getTokenAtOffset(t, dot);
if (curToken==null) { // At end of the line
int type = doc.getLastTokenTypeOnLine(line);
if (type==Token.NULL) {
Token temp = t.getLastPaintableToken();
if (temp==null) {
return this.defaultProvider;
}
type = temp.type;
}
System.out.println("Token@EOL: " + type);
type = getClosestTokenType(doc, rsta);
String textAfterToken = getTextAfterToken(doc, comp, type);
switch (type) {
case Token.VARIABLE:
return getProjectCompletionProvider(textAfterToken);
case Token.COMMENT_EOL:
if (TReps.getSelectedProject() != null) {
return getTaskCompletionProvider(textAfterToken);
}
return getProjectTaskCompletionProvider(textAfterToken);
default:
return getProjectTaskCompletionProvider(textAfterToken);
}
}
// FIXME: This isn't always a safe assumption.
if (dot==curToken.offset) { // At the very beginning of a new token
// Need to check previous token for its type before deciding.
// Previous token may also be on previous line!
return this.defaultProvider;
}
System.out.println("Token: " + curToken.type);
String textAfterToken = getTextAfterToken(doc, comp, curToken.type);
switch (getClosestTokenType(doc, rsta)) {
case Token.VARIABLE:
return getProjectCompletionProvider(textAfterToken);
case Token.COMMENT_EOL:
if (TReps.getSelectedProject() != null) {
return getTaskCompletionProvider(textAfterToken);
}
return getProjectTaskCompletionProvider(textAfterToken);
case Token.WHITESPACE:
case Token.IDENTIFIER:
case Token.PREPROCESSOR:
case Token.DATA_TYPE:
case Token.FUNCTION:
return getProjectTaskCompletionProvider(textAfterToken);
}
return null; // In a token type we can't auto-complete from.
}
private int getClosestTokenType(RSyntaxDocument doc, JTextComponent comp) {
Caret caret = comp.getCaret();
int dot = caret.getDot();
String textUntilCaret = "";
try {
textUntilCaret = doc.getText(0, dot);
} catch (BadLocationException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return -1;
}
int tokenProjectsLast = textUntilCaret.lastIndexOf("$");
int tokenTasksLast = textUntilCaret.lastIndexOf("#");
if (tokenProjectsLast > tokenTasksLast) {
return Token.VARIABLE;
} else {
return Token.COMMENT_EOL;
}
}
private String getTextAfterToken(RSyntaxDocument doc, JTextComponent comp, int tokenType) {
// int textDot = comp.getCaretPosition();
// Element root = doc.getDefaultRootElement();
// int index = root.getElementIndex(textDot);
// Element elem = root.getElement(index);
// int start = elem.getStartOffset();
// int len = textDot-start;
//
// System.out.println("token: " + (token != null ? token.getLexeme() : ""));
// System.out.println("textDot: " + textDot);
// System.out.println("elem: " + elem);
//
// try {
// int offset = 0;
// if (len == 0) {
// offset = 1;
// }
// return doc.getText(start + 1, len - 1 + offset);
// } catch (BadLocationException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
//
// }
String token = "";
switch (tokenType) {
case Token.VARIABLE:
token = "$";
break;
case Token.COMMENT_EOL:
token = "#";
break;
default:
return token;
}
Caret caret = comp.getCaret();
int dot = caret.getDot();
String textUntilCaret = "";
try {
textUntilCaret = doc.getText(0, dot);
} catch (BadLocationException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return "";
}
int tokenLast = textUntilCaret.lastIndexOf(token);
return StringUtil.substring(textUntilCaret, tokenLast + 1, dot);
}
private CompletionProvider getProjectCompletionProvider(final String filter) {
System.out.println("Filter: " + filter);
final DefaultCompletionProvider cp = new DefaultCompletionProvider() {
@Override
public List getCompletions(JTextComponent comp) {
projectsParser.processMatches(filter);
List<Completion> completions = new ArrayList<Completion>();
if (!ListUtil.isEmpty(projectsParser.getMatchedItems())) {
mainFrame.getComboBoxProjects().setSelectedItem(projectsParser.getMatchedItems().get(0).getObject());
}
for (MatchableItem<Project> projectItem : projectsParser.getMatchedItems()) {
//System.out.println("Matched Item: " + projectItem.getFullDescription());
completions.add(new MatchableShorthandCompletion<Project>(this, projectItem.getFullDescription(), projectItem.getFullDescription(), projectItem));
}
return completions;
}
};
return cp;
}
private CompletionProvider getTaskCompletionProvider(final String filter) {
System.out.println("Filter: " + filter);
final DefaultCompletionProvider cp = new DefaultCompletionProvider() {
@Override
public List getCompletions(JTextComponent comp) {
createTaskCompletionProvider();
tasksParser.processMatches(filter);
List<Completion> completions = new ArrayList<Completion>();
if (!ListUtil.isEmpty(tasksParser.getMatchedItems())) {
mainFrame.getComboBoxTasks().setSelectedItem(tasksParser.getMatchedItems().get(0).getObject());
}
for (MatchableItem<Task> taskItem : tasksParser.getMatchedItems()) {
//System.out.println("Matched Item: " + taskItem.getFullDescription());
completions.add(new MatchableShorthandCompletion<Task>(this, taskItem.getFullDescription(), taskItem.getFullDescription(), taskItem));
}
return completions;
}
};
return cp;
}
private CompletionProvider getProjectTaskCompletionProvider(final String filter) {
System.out.println("Filter: " + filter);
final DefaultCompletionProvider cp = new DefaultCompletionProvider() {
@Override
public List getCompletions(JTextComponent comp) {
projectsTasksParser.processMatches(filter);
List<Completion> completions = new ArrayList<Completion>();
for (MatchableItem<ProjectTask> projectTaskItem : projectsTasksParser.getMatchedItems()) {
//System.out.println("Matched Item: " + projectTaskItem.getFullDescription());
completions.add(new MatchableShorthandCompletion<ProjectTask>(this, projectTaskItem.getFullDescription(), projectTaskItem.getObject().getOutput(), projectTaskItem));
}
return completions;
}
};
return cp;
}
/**
* Does the dirty work of creating a list of completions.
*
* @param comp The text component to look in.
* @return The list of possible completions, or an empty list if there
* are none.
*/
@Override
protected List getCompletionsImpl(JTextComponent comp) {
if (!(comp instanceof RSyntaxTextArea)) {
return new ArrayList(0);
}
CompletionProvider provider = getProviderFor(comp);
return provider!=null ? provider.getCompletions(comp) : new ArrayList(0);
}
@Override
public String getAlreadyEnteredText(JTextComponent comp) {
if (!(comp instanceof RSyntaxTextArea)) {
return EMPTY_STRING;
}
CompletionProvider provider = getProviderFor(comp);
return provider.getAlreadyEnteredText(comp);
}
@Override
public List getCompletionsAt(JTextComponent comp, Point p) {
return null;
}
@Override
public List getParameterizedCompletions(JTextComponent tc) {
return null;
}
}