1 /*******************************************************************************
\r
2 * Copyright (c) 2010 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.sysdyn.ui.properties.widgets.expressions;
\r
14 import java.util.ArrayList;
\r
15 import java.util.Collections;
\r
17 import org.eclipse.jface.resource.ImageDescriptor;
\r
18 import org.eclipse.jface.resource.JFaceResources;
\r
19 import org.eclipse.jface.resource.LocalResourceManager;
\r
20 import org.eclipse.jface.text.ITextViewer;
\r
21 import org.eclipse.jface.text.contentassist.CompletionProposal;
\r
22 import org.eclipse.jface.text.contentassist.ICompletionProposal;
\r
23 import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
\r
24 import org.eclipse.jface.text.contentassist.IContextInformation;
\r
25 import org.eclipse.jface.text.contentassist.IContextInformationValidator;
\r
26 import org.eclipse.swt.graphics.Image;
\r
27 import org.eclipse.swt.widgets.Control;
\r
28 import org.eclipse.swt.widgets.Table;
\r
29 import org.eclipse.swt.widgets.TableItem;
\r
30 import org.simantics.db.ReadGraph;
\r
31 import org.simantics.db.Resource;
\r
32 import org.simantics.db.exception.DatabaseException;
\r
33 import org.simantics.db.layer0.variable.Variables;
\r
34 import org.simantics.db.request.Read;
\r
35 import org.simantics.sysdyn.ui.Activator;
\r
36 import org.simantics.sysdyn.ui.properties.widgets.ShortcutTabWidget;
\r
37 import org.simantics.sysdyn.ui.utils.ExpressionUtils;
\r
38 import org.simantics.sysdyn.utils.Function;
\r
39 import org.simantics.ui.SimanticsUI;
\r
43 * IContentAssistProcessor to determine which options (the functions and
\r
44 * variables available) are shown for ContentAssistant; this assist of
\r
45 * text field allows long variable names to be selected from a popup menu.
\r
46 * @author Tuomas Miettinen
\r
49 public class CompletionProcessor implements IContentAssistProcessor {
\r
51 private final Table allowedVariables;
\r
52 private ArrayList<Function> functions;
\r
53 private ArrayList<String> variables = null;
\r
54 private ArrayList<String> timeAndSelfVariables = null;
\r
55 private final ExpressionWidgetInput input;
\r
57 private LocalResourceManager resourceManager;
\r
59 private final char[] allowedCharacters = {
\r
60 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','å','ä','ö',
\r
61 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','Å','Ä','Ö',
\r
62 '1','2','3','4','5','6','7','8','9','0','.','(',')'};
\r
64 private final String allowedConnectedCharactersRegExp = "[\\Q({[:;,<=>+-*/^\\E]";
\r
66 public CompletionProcessor(Table allowedVariables, boolean allowFunctions, ExpressionWidgetInput input) {
\r
67 this.allowedVariables = allowedVariables;
\r
69 this.functions = new ArrayList<Function>();
\r
71 if (allowFunctions) {
\r
72 if (input != null && CompletionProcessor.this.input.variable != null) {
\r
73 // Get the respective model
\r
74 Resource model = null;
\r
76 model = SimanticsUI.getSession().syncRequest(new Read<Resource>() {
\r
79 public Resource perform(ReadGraph graph) throws DatabaseException {
\r
80 return Variables.getModel(graph, CompletionProcessor.this.input.variable);
\r
84 //User defined functions
\r
85 functions.addAll(Function.getUserDefinedFunctions(model));
\r
87 functions.addAll(Function.getSharedFunctions(model));
\r
88 } catch (DatabaseException e) {
\r
89 e.printStackTrace();
\r
93 // Collect built in functions and sort all functions.
\r
94 functions.addAll(Function.getAllBuiltInFunctions());
\r
95 Collections.sort(functions);
\r
100 * Collect and sort all variables.
\r
102 private void findVariables() {
\r
103 if (variables == null) {
\r
104 variables = new ArrayList<String>();
\r
105 timeAndSelfVariables = new ArrayList<String>();
\r
106 if(allowedVariables != null && !allowedVariables.isDisposed()) {
\r
107 TableItem[] connectedVariables = allowedVariables.getItems();
\r
108 for(TableItem ti : connectedVariables) {
\r
109 // The status of the variable is determined using the color of its table item :(
\r
110 if (ExpressionUtils.variableTimeAndSelfColor(resourceManager).equals(ti.getForeground())) {
\r
111 this.timeAndSelfVariables.add(ti.getText());
\r
113 this.variables.add(ti.getText());
\r
118 Collections.sort(variables);
\r
119 Collections.sort(timeAndSelfVariables);
\r
124 * Create CompletionProposals of the variables and add them to array.
\r
125 * @param array result array of CompletionProposals
\r
126 * @param token current token
\r
127 * @param offset an offset within the document for which completions should be computed
\r
129 private void addVariables(ArrayList<ICompletionProposal> array, String token, int offset) {
\r
130 Image imageVariable = resourceManager.createImage(ImageDescriptor.createFromURL(Activator.getDefault().getBundle().getResource("icons/variable.png")));
\r
131 Image imageVariableGray = resourceManager.createImage(ImageDescriptor.createFromURL(Activator.getDefault().getBundle().getResource("icons/variableGray.png")));
\r
133 for (String variable : variables) {
\r
134 if (token.length() == 0 || variable.toUpperCase().startsWith(token.toUpperCase())) {
\r
135 array.add(new CompletionProposal(variable,
\r
136 offset - token.length(),
\r
138 variable.length(),
\r
145 for (String variable : timeAndSelfVariables) {
\r
146 if (token.length() == 0 || variable.toUpperCase().startsWith(token.toUpperCase())) {
\r
147 array.add(new CompletionProposal(variable,
\r
148 offset - token.length(),
\r
150 variable.length(),
\r
151 imageVariableGray,
\r
160 * Create CompletionProposals of the functions and add them to array.
\r
161 * @param array result array of CompletionProposals
\r
162 * @param token current token
\r
163 * @param offset an offset within the document for which completions should be computed
\r
165 private void addFunctions(ArrayList<ICompletionProposal> array, String token, int offset) {
\r
166 // Parameters don't have functions
\r
167 if (functions == null)
\r
170 // Create CompletionProposals out of Functions
\r
171 for (Function function : functions) {
\r
172 if (token.length() == 0 || function.getName().toUpperCase().startsWith(token.toUpperCase())) {
\r
173 Image image = ShortcutTabWidget.getImage(resourceManager, function);
\r
174 String parameterList = Function.inputListToString(function.getInputList());
\r
175 array.add(new CompletionProposal(
\r
176 function.getName() + "(" + parameterList + ")",
\r
177 offset - token.length(),
\r
179 function.getName().length() + 1,
\r
181 function.getName() + "(" + parameterList + ")",
\r
183 function.getDescriptionHTML()));
\r
189 * Collect all matching proposals
\r
190 * @param token current token
\r
191 * @param offset an offset within the document for which completions should be computed
\r
192 * @return Array of matching proposals
\r
194 private ICompletionProposal[] collectProposals(String token, int offset) {
\r
195 ArrayList<ICompletionProposal> resultArray = new ArrayList<ICompletionProposal>();
\r
197 // Find variables and functions and create CompletionProposals out of them.
\r
199 addVariables(resultArray, token, offset);
\r
200 addFunctions(resultArray, token, offset);
\r
202 ICompletionProposal[] result = new ICompletionProposal[resultArray.size()];
\r
203 for (int i = 0; i < result.length; ++i) {
\r
204 result[i] = resultArray.get(i);
\r
210 public ICompletionProposal[] computeCompletionProposals(
\r
211 ITextViewer viewer, int offset) {
\r
212 String equation = viewer.getDocument().get();
\r
213 Control control = viewer.getTextWidget();
\r
214 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), control);
\r
216 if (equation.length() == 0
\r
218 || Character.isWhitespace(equation.charAt(offset - 1))) {
\r
219 return collectProposals("", offset);
\r
222 equation = equation.substring(0, offset);
\r
224 // Split into tokens on whitespace characters
\r
225 String[] tokens = equation.split("[\\s]");
\r
226 if (tokens.length == 0) {
\r
227 return collectProposals("", offset);
\r
229 String token = tokens[tokens.length - 1];
\r
231 // If a '+', '-', etc. character is in the end, return all.
\r
232 if (allowedConnectedCharactersRegExp.indexOf(token.charAt(token.length() - 1)) != -1) {
\r
233 return collectProposals("", offset);
\r
236 // Split the last token on '+', '-', etc. characters
\r
237 String tokensOfLastToken[] = token.split(allowedConnectedCharactersRegExp);
\r
238 if (tokensOfLastToken.length == 0) {
\r
239 return collectProposals("", offset);
\r
241 token = tokensOfLastToken[tokensOfLastToken.length - 1];
\r
242 //System.out.println(token + "\noffset = " + offset);
\r
244 return collectProposals(token, offset);
\r
248 public IContextInformation[] computeContextInformation(
\r
249 ITextViewer viewer, int offset) {
\r
254 public char[] getCompletionProposalAutoActivationCharacters() {
\r
255 return allowedCharacters;
\r
259 public char[] getContextInformationAutoActivationCharacters() {
\r
264 public String getErrorMessage() {
\r
265 return "Error in CompletionProcessor";
\r
269 public IContextInformationValidator getContextInformationValidator() {
\r