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.utils.ExpressionUtils;
\r
37 import org.simantics.ui.SimanticsUI;
\r
41 * IContentAssistProcessor to determine which options (the functions and
\r
42 * variables available) are shown for ContentAssistant; this assist of
\r
43 * text field allows long variable names to be selected from a popup menu.
\r
44 * @author Tuomas Miettinen
\r
47 public class CompletionProcessor implements IContentAssistProcessor {
\r
49 private final Table allowedVariables;
\r
50 private ArrayList<Function> functions;
\r
51 private ArrayList<String> variables = null;
\r
52 private ArrayList<String> timeAndSelfVariables = null;
\r
53 private final ExpressionWidgetInput input;
\r
55 private LocalResourceManager resourceManager;
\r
57 private final char[] allowedCharacters = {
\r
58 '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
59 '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
60 '1','2','3','4','5','6','7','8','9','0','.','_','(',')'};
\r
62 private final String allowedConnectedCharactersRegExp = "[\\Q({[:;,<=>+-*/^\\E]";
\r
64 public CompletionProcessor(Table allowedVariables, boolean allowFunctions, ExpressionWidgetInput input) {
\r
65 this.allowedVariables = allowedVariables;
\r
67 this.functions = new ArrayList<Function>();
\r
69 if (allowFunctions) {
\r
70 if (input != null && CompletionProcessor.this.input.variable != null) {
\r
71 // Get the respective model
\r
72 Resource model = null;
\r
74 model = SimanticsUI.getSession().syncRequest(new Read<Resource>() {
\r
77 public Resource perform(ReadGraph graph) throws DatabaseException {
\r
78 return Variables.getModel(graph, CompletionProcessor.this.input.variable);
\r
82 //User defined functions
\r
83 functions.addAll(Function.getUserDefinedFunctions(model));
\r
85 functions.addAll(Function.getSharedFunctions(model));
\r
86 } catch (DatabaseException e) {
\r
87 e.printStackTrace();
\r
91 // Collect built in functions and sort all functions.
\r
92 functions.addAll(Function.getAllBuiltInFunctions());
\r
93 Collections.sort(functions);
\r
98 * Collect and sort all variables.
\r
100 private void findVariables() {
\r
101 if (variables == null) {
\r
102 variables = new ArrayList<String>();
\r
103 timeAndSelfVariables = new ArrayList<String>();
\r
104 if(allowedVariables != null && !allowedVariables.isDisposed()) {
\r
105 TableItem[] connectedVariables = allowedVariables.getItems();
\r
106 for(TableItem ti : connectedVariables) {
\r
107 // The status of the variable is determined using the color of its table item :(
\r
108 if (ExpressionUtils.variableTimeAndSelfColor(resourceManager).equals(ti.getForeground())) {
\r
109 this.timeAndSelfVariables.add(ti.getText());
\r
111 this.variables.add(ti.getText());
\r
116 Collections.sort(variables);
\r
117 Collections.sort(timeAndSelfVariables);
\r
122 * Create CompletionProposals of the variables and add them to array.
\r
123 * @param array result array of CompletionProposals
\r
124 * @param token current token
\r
125 * @param offset an offset within the document for which completions should be computed
\r
127 private void addVariables(ArrayList<ICompletionProposal> array, String token, int offset) {
\r
128 Image imageVariable = resourceManager.createImage(ImageDescriptor.createFromURL(Activator.getDefault().getBundle().getResource("icons/variable.png")));
\r
129 Image imageVariableGray = resourceManager.createImage(ImageDescriptor.createFromURL(Activator.getDefault().getBundle().getResource("icons/variableGray.png")));
\r
131 for (String variable : variables) {
\r
132 if (token.length() == 0 || variable.toUpperCase().startsWith(token.toUpperCase())) {
\r
133 array.add(new CompletionProposal(variable,
\r
134 offset - token.length(),
\r
136 variable.length(),
\r
143 for (String variable : timeAndSelfVariables) {
\r
144 if (token.length() == 0 || variable.toUpperCase().startsWith(token.toUpperCase())) {
\r
145 array.add(new CompletionProposal(variable,
\r
146 offset - token.length(),
\r
148 variable.length(),
\r
149 imageVariableGray,
\r
158 * Create CompletionProposals of the functions and add them to array.
\r
159 * @param array result array of CompletionProposals
\r
160 * @param token current token
\r
161 * @param offset an offset within the document for which completions should be computed
\r
163 private void addFunctions(ArrayList<ICompletionProposal> array, String token, int offset) {
\r
164 // Parameters don't have functions
\r
165 if (functions == null)
\r
168 // Create CompletionProposals out of Functions
\r
169 for (Function function : functions) {
\r
170 if (token.length() == 0 || function.getName().toUpperCase().startsWith(token.toUpperCase())) {
\r
171 Image image = Function.getImage(resourceManager, function);
\r
172 array.add(new CompletionProposal(
\r
173 function.getName() + "(" + function.getParameterList() + ")",
\r
174 offset - token.length(),
\r
176 function.getName().length() + 1,
\r
178 function.getName() + "(" + function.getParameterList() + ")",
\r
180 function.getDescriptionHTML()));
\r
186 * Collect all matching proposals
\r
187 * @param token current token
\r
188 * @param offset an offset within the document for which completions should be computed
\r
189 * @return Array of matching proposals
\r
191 private ICompletionProposal[] collectProposals(String token, int offset) {
\r
192 ArrayList<ICompletionProposal> resultArray = new ArrayList<ICompletionProposal>();
\r
194 // Find variables and functions and create CompletionProposals out of them.
\r
196 addVariables(resultArray, token, offset);
\r
197 addFunctions(resultArray, token, offset);
\r
199 ICompletionProposal[] result = new ICompletionProposal[resultArray.size()];
\r
200 for (int i = 0; i < result.length; ++i) {
\r
201 result[i] = resultArray.get(i);
\r
207 public ICompletionProposal[] computeCompletionProposals(
\r
208 ITextViewer viewer, int offset) {
\r
209 String equation = viewer.getDocument().get();
\r
210 Control control = viewer.getTextWidget();
\r
211 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), control);
\r
213 if (equation.length() == 0
\r
215 || Character.isWhitespace(equation.charAt(offset - 1))) {
\r
216 return collectProposals("", offset);
\r
219 equation = equation.substring(0, offset);
\r
221 // Split into tokens on whitespace characters
\r
222 String[] tokens = equation.split("[\\s]");
\r
223 if (tokens.length == 0) {
\r
224 return collectProposals("", offset);
\r
226 String token = tokens[tokens.length - 1];
\r
228 // If a '+', '-', etc. character is in the end, return all.
\r
229 if (allowedConnectedCharactersRegExp.indexOf(token.charAt(token.length() - 1)) != -1) {
\r
230 return collectProposals("", offset);
\r
233 // Split the last token on '+', '-', etc. characters
\r
234 String tokensOfLastToken[] = token.split(allowedConnectedCharactersRegExp);
\r
235 if (tokensOfLastToken.length == 0) {
\r
236 return collectProposals("", offset);
\r
238 token = tokensOfLastToken[tokensOfLastToken.length - 1];
\r
239 //System.out.println(token + "\noffset = " + offset);
\r
241 return collectProposals(token, offset);
\r
245 public IContextInformation[] computeContextInformation(
\r
246 ITextViewer viewer, int offset) {
\r
251 public char[] getCompletionProposalAutoActivationCharacters() {
\r
252 return allowedCharacters;
\r
256 public char[] getContextInformationAutoActivationCharacters() {
\r
261 public String getErrorMessage() {
\r
262 return "Error in CompletionProcessor";
\r
266 public IContextInformationValidator getContextInformationValidator() {
\r