blob: bc0029a1548c45a4837f6239f131ead47ae64e8b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2012 Oracle Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Oracle Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.bpel.ui.contentassist;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.Iterator;
import java.util.Stack;
import org.eclipse.bpel.model.Link;
import org.eclipse.bpel.model.Variable;
import org.eclipse.bpel.model.util.BPELUtils;
import org.eclipse.bpel.ui.BPELUIPlugin;
import org.eclipse.bpel.ui.IBPELUIConstants;
import org.eclipse.bpel.ui.details.providers.LinkContentProvider;
import org.eclipse.bpel.ui.expressions.IEditorConstants;
import org.eclipse.bpel.ui.util.BPELUtil;
import org.eclipse.bpel.ui.util.XSDUtils;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ContentAssistEvent;
import org.eclipse.jface.text.contentassist.ICompletionListener;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistantExtension2;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.wst.wsdl.Message;
import org.eclipse.wst.wsdl.Part;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTypeDefinition;
/**
* @author Edward Gee (edward.gee@oracle.com)
* @author Michal Chmielewski (michal.chmielewski@oracle.com)
*
*/
public class ExpressionContentAssistProcessor
implements IContentAssistProcessor, ICompletionListener {
static String EMPTY_STRING = ""; //$NON-NLS-1$
static ICompletionProposal[] EMPTY_COMPLETION_PROPOSALS = {} ;
// members of class ExpressionContentAssistProcessor
private Object theModel = null;
private int theToggle = 3;
private String theLastBeginsWith = EMPTY_STRING;
private String theExpressionContext = EMPTY_STRING;
private IContentAssistantExtension2 theContentAssistant;
/**
* The function templates content assist processor.
*/
FunctionTemplatesContentAssistProcessor fFunctionTemplates = new FunctionTemplatesContentAssistProcessor();
XPathTemplateCompletionProcessor fXpathTemplates = new XPathTemplateCompletionProcessor();
// public constants
static final String MINUS = "-"; //$NON-NLS-1$
static final String PLUS = "+"; //$NON-NLS-1$
static final String MULTIPLY = "*"; //$NON-NLS-1$
static final String DIVIDE = "/"; //$NON-NLS-1$
static final String OPEN_PAREN = "("; //$NON-NLS-1$
static final String CLOSE_PAREN = ")"; //$NON-NLS-1$
static final String OPEN_BRACKET = "["; //$NON-NLS-1$
static final String CLOSE_BRACKET = "]"; //$NON-NLS-1$
static final String COMMA = ","; //$NON-NLS-1$
static final String DOLLAR = "$"; //$NON-NLS-1$
static final String EQUAL = "="; //$NON-NLS-1$
static final String NOT_EQUAL = "!="; //$NON-NLS-1$
static final String LESS_THAN = "<"; //$NON-NLS-1$
static final String LESS_THAN_EQUAL = "<="; //$NON-NLS-1$
static final String GREATER_THAN = ">"; //$NON-NLS-1$
static final String GREATER_THAN_EQUAL = ">="; //$NON-NLS-1$
static final String MOD = "mod"; //$NON-NLS-1$
static final String DIV = "div"; //$NON-NLS-1$
static final String AND = "and"; //$NON-NLS-1$
static final String OR = "or"; //$NON-NLS-1$
static final String COLON = ":"; //$NON-NLS-1$
static final String AT = "@"; //$NON-NLS-1$
// helper class for defining proposal types
class ProposalType {
private int theType;
private String theBeginsWith;
// bitwise flags for different types
static final int PROPTYPE_VARIABLE = 1;
static final int PROPTYPE_FUNCTION = 2;
static final int PROPTYPE_OPERATOR = 4;
ProposalType(int type, String beginsWith) {
theType = type;
theBeginsWith = beginsWith;
}
boolean isVariable() { return ((theType & PROPTYPE_VARIABLE) == PROPTYPE_VARIABLE); }
boolean isFunction() { return ((theType & PROPTYPE_FUNCTION) == PROPTYPE_FUNCTION); }
boolean isOperator() { return ((theType & PROPTYPE_OPERATOR) == PROPTYPE_OPERATOR); }
boolean isVariableAndFunction() { return ((theType & (PROPTYPE_VARIABLE | PROPTYPE_FUNCTION)) == (PROPTYPE_VARIABLE | PROPTYPE_FUNCTION)); }
}
// helper class for defining expression types within xpath expression... used by XPathStack
class ExpressionType {
private int theType;
private String theGrammar;
static final int EXPRTYPE_VARIABLE = 1;
static final int EXPRTYPE_FUNCTION = 2;
static final int EXPRTYPE_LITERAL = 3;
static final int EXPRTYPE_NUMBER = 4;
static final int EXPRTYPE_UNARY_OPERATOR = 5;
static final int EXPRTYPE_NUMERIC_OPERATOR = 6;
static final int EXPRTYPE_BOOLEAN_OPERATOR = 7;
static final int EXPRTYPE_NEW_EXPRESSION = 8;
static final int EXPRTYPE_FUNCTION_ARGUMENTS = 9;
static final int EXPRTYPE_FUNCTION_ARGUMENT_SEPARATOR = 10;
static final int CLASS_NUMERIC = 100;
static final int CLASS_BOOLEAN = 101;
static final int CLASS_EXPRESSION = 102;
ExpressionType(int type, String grammar) {
theType = type;
theGrammar = grammar;
}
}
// simple call stack used for parsing XPath expressions
protected class XPathStack {
Stack<ExpressionType> theCallStack;
int theStatus;
IDocument theDocument;
int theOffset;
int theIndex;
int theTopOfStackExprType = -1;
XPathStack(ITextViewer viewer, int offset) {
theCallStack = new Stack<ExpressionType>();
theStatus = 0; // flag for stack's integrity
theDocument = viewer.getDocument();
theOffset = offset;
theIndex = 0;
}
//parse numeric expression
private boolean parseNumberExpression() throws BadLocationException {
char currChar;
boolean proceed = false;
while (theIndex < theOffset) {
currChar = theDocument.getChar(theIndex);
// variable
if (currChar == '$') {
if ((theTopOfStackExprType == ExpressionType.EXPRTYPE_UNARY_OPERATOR) ||
//(theTopOfStackExprType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_NUMERIC_OPERATOR))
return parseVariable();
if (!parseVariable())
return false;
}
// literal
else if ((currChar == '\'') || (currChar == '"')) {
if ((theTopOfStackExprType == ExpressionType.EXPRTYPE_UNARY_OPERATOR) ||
//(theTopOfStackExprType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_NUMERIC_OPERATOR))
return parseLiteral();
if (!parseLiteral())
return false;
}
else if (Character.isDigit(currChar) || (currChar == '.')) {
if ((theTopOfStackExprType == ExpressionType.EXPRTYPE_UNARY_OPERATOR) ||
//(theTopOfStackExprType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_NUMERIC_OPERATOR))
return parseNumber();
if (!parseNumber())
return false;
}
else if (isReservedOperatorCharacter(currChar)) {
if ((theTopOfStackExprType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_NUMERIC_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_UNARY_OPERATOR) ||
(theTopOfStackExprType == -1)) {
// check to see if it's a unary operator
if (currChar == '-') {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_UNARY_OPERATOR, MINUS));
theTopOfStackExprType = ExpressionType.EXPRTYPE_UNARY_OPERATOR;
theIndex++;
if (parseNumberExpression()) {
// pop the operand
theCallStack.pop();
// pop the unary operator
theCallStack.pop();
// push "numeric" value
theCallStack.push(new ExpressionType(ExpressionType.CLASS_NUMERIC, EMPTY_STRING));
theTopOfStackExprType = ExpressionType.CLASS_NUMERIC;
}
}
else {
theStatus = -1;
return false;
}
}
else {
String tempOper = Character.toString(currChar);
if ((theIndex+1) < theOffset) {
if (isReservedOperatorCharacter(theDocument.getChar(theIndex+1))) {
theIndex++;
tempOper = tempOper + Character.toString(theDocument.getChar(theIndex));
}
int tempType = 0;
if ((tempOper.compareTo(PLUS) == 0) || (tempOper.compareTo(MINUS) == 0) ||
(tempOper.compareTo(MULTIPLY) == 0) || (tempOper.compareTo(DIVIDE) == 0))
tempType = ExpressionType.EXPRTYPE_NUMERIC_OPERATOR;
else
tempType = ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR;
theCallStack.push(new ExpressionType(tempType, tempOper));
theTopOfStackExprType = tempType;
theIndex++;
if (tempType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) {
return parseBooleanExpression();
}
if (parseNumberExpression()) {
// pop the operand
theCallStack.pop();
// pop the operator
theCallStack.pop();
// pop the first operand;
theCallStack.pop();
// push "numeric value"
theCallStack.push(new ExpressionType(ExpressionType.CLASS_NUMERIC, EMPTY_STRING));
theTopOfStackExprType = ExpressionType.CLASS_NUMERIC;
}
else {
return false;
}
}
else {// return
int tempType = 0;
if ((tempOper.compareTo(PLUS) == 0) || (tempOper.compareTo(MINUS) == 0) ||
(tempOper.compareTo(MULTIPLY) == 0) || (tempOper.compareTo(DIVIDE) == 0))
tempType = ExpressionType.EXPRTYPE_NUMERIC_OPERATOR;
else
tempType = ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR;
theCallStack.push(new ExpressionType(tempType, tempOper));
theTopOfStackExprType = tempType;
return false;
}
}
}
else if (Character.isWhitespace(currChar)) {
// just ignore
}
else if (currChar == '(') {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_NEW_EXPRESSION, Character.toString(currChar)));
theTopOfStackExprType = ExpressionType.EXPRTYPE_NEW_EXPRESSION;
theIndex++;
if (parseBooleanExpression()) {
// peek and set top of stack id
ExpressionType tempExpr = theCallStack.peek();
int type = tempExpr.theType;
theCallStack.push(new ExpressionType(ExpressionType.CLASS_EXPRESSION, EMPTY_STRING));
theTopOfStackExprType = ExpressionType.CLASS_EXPRESSION;
if ((type == ExpressionType.EXPRTYPE_UNARY_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_NUMERIC_OPERATOR))
return true;
}
else
return false;
}
else if (isClosingExpressionCharacter(currChar)) {
try {
ExpressionType tempExpr = theCallStack.pop();
while (!((tempExpr.theType == ExpressionType.EXPRTYPE_NEW_EXPRESSION) ||
(tempExpr.theType == ExpressionType.EXPRTYPE_FUNCTION_ARGUMENTS))) {
tempExpr = theCallStack.pop();
}
if (tempExpr != null) {
if (tempExpr.theType == ExpressionType.EXPRTYPE_NEW_EXPRESSION) {
switch (currChar) {
case ')':
if (tempExpr.theGrammar.compareTo(OPEN_PAREN) == 0) {
return true;
}
theStatus = -1;
return false;
case ']':
if (tempExpr.theGrammar.compareTo(OPEN_BRACKET) == 0) {
return true;
}
theStatus = -1;
return false;
default:
theStatus = -1;
return false;
}
}
else if (tempExpr.theType == ExpressionType.EXPRTYPE_FUNCTION_ARGUMENTS) {
if (currChar == ')') {
return true;
}
theStatus = -1;
return false;
}
theStatus = -1;
return false;
}
}
catch (EmptyStackException e) {
theStatus = -1;
return false;
}
theStatus = -1;
return false;
}
else if (currChar == ',') {
// could be a function argument
return true;
}
else {
int type = theTopOfStackExprType;
proceed = parseWord();
if (proceed) {
if ((theTopOfStackExprType == ExpressionType.EXPRTYPE_NUMERIC_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR)) {
if ((type == ExpressionType.EXPRTYPE_NUMERIC_OPERATOR) ||
(type == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) ||
(type == ExpressionType.EXPRTYPE_UNARY_OPERATOR) ||
(type == -1)) {
theStatus = -1;
return false;
}
theIndex++;
if (theTopOfStackExprType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) {
return parseBooleanExpression();
}
if (parseNumberExpression()) {
// pop the operand
theCallStack.pop();
// pop the operator
theCallStack.pop();
// pop the first operand;
theCallStack.pop();
// push "numeric value"
theCallStack.push(new ExpressionType(ExpressionType.CLASS_NUMERIC, EMPTY_STRING));
theTopOfStackExprType = ExpressionType.CLASS_NUMERIC;
} else {
return false;
}
}
else {
if ((type == ExpressionType.EXPRTYPE_UNARY_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR) ||
(theTopOfStackExprType == ExpressionType.EXPRTYPE_NUMERIC_OPERATOR))
return true;
}
}
else
return false;
}
theIndex++;
}
return false;
}
// parse boolean expressions
boolean parseBooleanExpression() throws BadLocationException {
boolean proceed = parseNumberExpression();
while (proceed) {
ExpressionType tempExpr = theCallStack.peek();
if (tempExpr.theType == ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR)
proceed = parseNumberExpression();
else
return proceed;
}
return proceed;
}
// parse word found in numeric expression
private boolean parseWord() throws BadLocationException {
String word = EMPTY_STRING;
char nextChar;
while (theIndex < theOffset) {
nextChar = theDocument.getChar(theIndex);
if (nextChar == '(') {
if (word.length() > 0) {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_FUNCTION, word));
theTopOfStackExprType = ExpressionType.EXPRTYPE_FUNCTION;
boolean proceed = parseFunction();
if (proceed) {
// pop the function name
theCallStack.pop();
// push the expression
theCallStack.push(new ExpressionType(ExpressionType.CLASS_EXPRESSION, EMPTY_STRING));
theTopOfStackExprType = ExpressionType.CLASS_EXPRESSION;
return true;
}
return false;
}
}
if (Character.isWhitespace(nextChar)) {
if (word.length() > 0) {
if ((word.compareToIgnoreCase(MOD) == 0) ||
(word.compareToIgnoreCase(DIV) == 0)) {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_NUMERIC_OPERATOR, word));
theTopOfStackExprType = ExpressionType.EXPRTYPE_NUMERIC_OPERATOR;
return true;
}
if ((word.compareToIgnoreCase(AND) == 0) ||
(word.compareToIgnoreCase(OR) == 0)) {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR, word));
theTopOfStackExprType = ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR;
return true;
}
//maybe it's a function name... next character should be whitespace or (
int tempIndex = theIndex;
while (tempIndex < theOffset) {
if (!Character.isWhitespace(theDocument.getChar(tempIndex))) {
if (theDocument.getChar(tempIndex) == '(') {
break;
}
theStatus = -1;
return false;
}
tempIndex++;
}
}
}
word = word + nextChar;
theIndex++;
}
if (theIndex >= theOffset) {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_FUNCTION, word));
theStatus = 1;
return false;
}
return false;
}
// parse function
private boolean parseFunction() throws BadLocationException {
char nextChar;
while (theIndex < theOffset) {
nextChar = theDocument.getChar(theIndex);
if (nextChar == '(')
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_FUNCTION_ARGUMENTS, OPEN_PAREN));
else if (nextChar == ')') {
try {
ExpressionType tempExpr = theCallStack.peek();
if (tempExpr != null) {
if (tempExpr.theType == ExpressionType.EXPRTYPE_FUNCTION)
return true;
else if (tempExpr.theType == ExpressionType.EXPRTYPE_FUNCTION_ARGUMENTS) {
theCallStack.pop();
return true;
}
else {
theStatus = -1;
return false;
}
}
}
catch (EmptyStackException e) {
theStatus = -1;
return false;
}
theStatus = -1;
return false;
}
else if (nextChar == ',') {
theIndex++;
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_FUNCTION_ARGUMENT_SEPARATOR, COMMA));
boolean proceed = parseBooleanExpression();
if (!proceed)
return false;
continue;
}
else if (Character.isWhitespace(nextChar)) {
//ignore
}
else {
boolean proceed = parseBooleanExpression();
if (!proceed)
return false;
continue;
}
theIndex++;
}
return false;
}
// parse literal values
private boolean parseLiteral() throws BadLocationException {
String literal = EMPTY_STRING;
char nextChar = theDocument.getChar(theIndex);
// delimiter could be single or double quote
char delimiter = nextChar;
literal = literal + nextChar;
theIndex++;
while (theIndex < theOffset) {
nextChar = theDocument.getChar(theIndex);
if (nextChar != delimiter) {
literal = literal + nextChar;
}
else {
literal = literal + nextChar;
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_LITERAL, literal));
theTopOfStackExprType = ExpressionType.EXPRTYPE_LITERAL;
return true;
}
theIndex++;
}
if (theIndex >= theOffset) {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_LITERAL, literal));
theStatus = 1;
return false;
}
return false;
}
// parse number values
private boolean parseNumber() throws BadLocationException {
String number = EMPTY_STRING;
char nextChar;
while (theIndex < theOffset) {
nextChar = theDocument.getChar(theIndex);
if ((Character.isDigit(nextChar) || (nextChar == '.'))) {
number = number + nextChar;
}
else {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_NUMBER, number));
theTopOfStackExprType = ExpressionType.EXPRTYPE_NUMBER;
theIndex--;
return true;
}
theIndex++;
}
if (theIndex >= theOffset) {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_NUMBER, number));
return false;
}
return false;
}
// parse variable
private boolean parseVariable() throws BadLocationException {
theIndex++; // move past $ token
String variable = DOLLAR;
char nextChar;
while (theIndex < theOffset) {
nextChar = theDocument.getChar(theIndex);
if (isLocationPathCharacter(nextChar)) {
variable = variable + nextChar;
}
else if (nextChar == '[') {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_VARIABLE, variable));
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_NEW_EXPRESSION, OPEN_BRACKET));
theIndex++;
boolean proceed = parseBooleanExpression();
if (proceed) {
nextChar = theDocument.getChar(theIndex);
if (nextChar == ']') {
try {
ExpressionType tempExpr = theCallStack.pop();
while (tempExpr.theType != ExpressionType.EXPRTYPE_NEW_EXPRESSION) {
// continue popping until we find a new expression type
tempExpr = theCallStack.pop();
}
if (tempExpr != null) {
if (tempExpr.theGrammar.compareTo(CLOSE_BRACKET) == 0) {
return true;
}
}
}
catch (EmptyStackException e) {
theStatus = -1;
return false;
}
}
else {
// in error ... maybe throw something here
theStatus = -1;
return false;
}
}
else {
return proceed;
}
}
else {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_VARIABLE, variable));
theTopOfStackExprType = ExpressionType.EXPRTYPE_VARIABLE;
theIndex--;
return true;
}
theIndex++;
}
if (theIndex >= theOffset) {
theCallStack.push(new ExpressionType(ExpressionType.EXPRTYPE_VARIABLE, variable));
theStatus = 1;
return false;
//return new ProposalType(ProposalType.PROPTYPE_VARIABLE, variable);
}
// shouldn't get here
theStatus = -1;
return false;
}
/**
* Parse XPath expression
* @return true of parsed OK
*/
public boolean parse() {
try {
parseBooleanExpression();
return (theStatus != -1);
}
catch (BadLocationException e) {
System.out.println(e.toString());
}
return false;
}
/**
* Retrieve suggestion after parsing
* @return the suggestion.
*/
public ExpressionType getSuggestion() {
try {
return theCallStack.peek();
}
catch (EmptyStackException e) {
return null;
}
}
}
/**
* @see org.eclipse.jface.text.contentassist.ICompletionListener#assistSessionStarted(org.eclipse.jface.text.contentassist.ContentAssistEvent)
*/
public void assistSessionStarted(ContentAssistEvent event) {
IContentAssistant assistant= event.assistant;
if (assistant instanceof IContentAssistantExtension2) {
theContentAssistant= (IContentAssistantExtension2)assistant;
}
}
/**
* @see org.eclipse.jface.text.contentassist.ICompletionListener#assistSessionEnded(org.eclipse.jface.text.contentassist.ContentAssistEvent)
*/
public void assistSessionEnded(ContentAssistEvent event) {
theContentAssistant= null;
theToggle = 3;
theLastBeginsWith = EMPTY_STRING;
}
/**
* @see org.eclipse.jface.text.contentassist.ICompletionListener#selectionChanged(org.eclipse.jface.text.contentassist.ICompletionProposal, boolean)
*/
public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
// do nothing
}
/**
* @param model
*/
public void setModelObject(Object model) {
theModel = model;
fFunctionTemplates.setModel(model);
}
/**
* @param expressionContext
*/
public void setExpressionContext(String expressionContext) {
theExpressionContext = expressionContext;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
return generateProposals(viewer, offset);
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int)
*/
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
return null;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
*/
public char[] getCompletionProposalAutoActivationCharacters() {
// for variables
return new char[] { '$', '/', '@', '.' };
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
*/
public char[] getContextInformationAutoActivationCharacters() {
// do nothing for now
return null;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
*/
public IContextInformationValidator getContextInformationValidator() {
// do nothing for now
return null;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
*/
public String getErrorMessage() {
// do nothing for now
return Messages.getString("ExpressionContentAssistProcessor.32"); //$NON-NLS-1$
}
/*
* Second iteration of determining proposal type. Attempts to parse the xpath
* expression until offset is reached or it can not ascertain what the expression
* means. If parsing fails, tries to identify the word located right before
* the offset and provide proposal types based on that information.
*/
private ProposalType determineProposalType2(ITextViewer viewer, int offset) {
XPathStack callStack = new XPathStack(viewer, offset);
if (callStack.parse()) {
ExpressionType expr = callStack.getSuggestion();
if (expr != null) {
switch (expr.theType) {
case ExpressionType.EXPRTYPE_VARIABLE:
if (callStack.theStatus == 1) {
return new ProposalType(ProposalType.PROPTYPE_VARIABLE, expr.theGrammar);
}
return new ProposalType(ProposalType.PROPTYPE_OPERATOR, EMPTY_STRING);
case ExpressionType.EXPRTYPE_FUNCTION:
if (callStack.theStatus == 1) {
return new ProposalType(ProposalType.PROPTYPE_FUNCTION, expr.theGrammar);
}
return new ProposalType(ProposalType.PROPTYPE_OPERATOR, EMPTY_STRING);
case ExpressionType.EXPRTYPE_BOOLEAN_OPERATOR:
case ExpressionType.EXPRTYPE_NUMERIC_OPERATOR:
case ExpressionType.EXPRTYPE_UNARY_OPERATOR:
case ExpressionType.EXPRTYPE_FUNCTION_ARGUMENTS:
case ExpressionType.EXPRTYPE_FUNCTION_ARGUMENT_SEPARATOR:
case ExpressionType.EXPRTYPE_NEW_EXPRESSION:
return new ProposalType(ProposalType.PROPTYPE_FUNCTION | ProposalType.PROPTYPE_VARIABLE, EMPTY_STRING);
case ExpressionType.CLASS_BOOLEAN:
case ExpressionType.CLASS_NUMERIC:
case ExpressionType.CLASS_EXPRESSION:
case ExpressionType.EXPRTYPE_NUMBER:
return new ProposalType(ProposalType.PROPTYPE_OPERATOR, EMPTY_STRING);
case ExpressionType.EXPRTYPE_LITERAL:
if (callStack.theStatus == 1) {
return (new ProposalType(0, EMPTY_STRING));
}
return new ProposalType(ProposalType.PROPTYPE_OPERATOR, EMPTY_STRING);
default:
return (new ProposalType(0, EMPTY_STRING));
}
}
// try suggesting everything
return new ProposalType(ProposalType.PROPTYPE_FUNCTION | ProposalType.PROPTYPE_VARIABLE, EMPTY_STRING);
}
// try last ditch effort
String tempContext = startOfVariable(viewer, offset);
if (tempContext != null) {
return new ProposalType(ProposalType.PROPTYPE_VARIABLE, tempContext);
}
tempContext = startOfFunction(viewer, offset);
if (tempContext != null) {
return new ProposalType(ProposalType.PROPTYPE_FUNCTION, tempContext);
}
return new ProposalType(ProposalType.PROPTYPE_FUNCTION | ProposalType.PROPTYPE_VARIABLE, EMPTY_STRING);
}
/*
* Try best guess to generate proposals based on provided context information
*/
private ICompletionProposal[] generateProposals(ITextViewer viewer, int offset) {
ProposalType propType = determineProposalType2(viewer, offset);
// only toggle if the begins with hasn't changed
boolean toggle = false;
if (theLastBeginsWith.compareTo(propType.theBeginsWith) == 0)
toggle = true;
else
theLastBeginsWith = propType.theBeginsWith;
String funcStart = EMPTY_STRING;
String varlinkStart = EMPTY_STRING;
if (propType.isVariableAndFunction()) {
theToggle = (theToggle+1) % 4;
funcStart = propType.theBeginsWith;
varlinkStart = propType.theBeginsWith;
}
else if (propType.isVariable()) {
if (toggle)
theToggle = (theToggle+1) % 4;
else
theToggle = 0;
funcStart = EMPTY_STRING;
varlinkStart = propType.theBeginsWith;
}
else if (propType.isFunction()) {
if (toggle)
theToggle = (theToggle+1) % 4;
else
theToggle = 1;
funcStart = propType.theBeginsWith;
varlinkStart = EMPTY_STRING;
}
else if (propType.isOperator()) {
if (toggle)
theToggle = (theToggle+1) % 4;
else
theToggle = 2;
funcStart = EMPTY_STRING;
varlinkStart = EMPTY_STRING;
}
switch (theToggle) {
case 0:
theContentAssistant.setStatusMessage(Messages.getString("ExpressionContentAssistProcessor.44")); //$NON-NLS-1$
if (theExpressionContext.compareTo(IEditorConstants.ET_JOIN) == 0) {
return generateLinkProposals(varlinkStart, offset);
}
return generateVariableProposals(varlinkStart, offset);
case 1:
theContentAssistant.setStatusMessage(Messages.getString("ExpressionContentAssistProcessor.45")); //$NON-NLS-1$
return generateFunctionProposals(viewer, funcStart, offset);
case 2:
theContentAssistant.setStatusMessage(Messages.getString("ExpressionContentAssistProcessor.46")); //$NON-NLS-1$
return generateOperatorProposals(EMPTY_STRING, offset);
case 3:
if (theExpressionContext.compareTo(IEditorConstants.ET_JOIN) == 0)
theContentAssistant.setStatusMessage(Messages.getString("ExpressionContentAssistProcessor.48")); //$NON-NLS-1$
else
theContentAssistant.setStatusMessage(Messages.getString("ExpressionContentAssistProcessor.49")); //$NON-NLS-1$
//return generateTemplateProposals(tempStart, offset);
return (fXpathTemplates.computeCompletionProposals(viewer, offset));
}
return null;
}
/**
* From model, determine list of functions the user may want to choose from.
*/
ICompletionProposal[] generateFunctionProposals(ITextViewer viewer, String context, int offset) {
return fFunctionTemplates.computeCompletionProposals(viewer, offset);
}
/*
* Static proposal list of supported operators. Could refine the list
* to group by numeric or boolean operators.
*/
static final String[] OPERATOR_LIST = {AND, OR, EQUAL, NOT_EQUAL, LESS_THAN, GREATER_THAN,
LESS_THAN_EQUAL, GREATER_THAN_EQUAL, PLUS, MINUS, MULTIPLY, DIV, MOD};
ICompletionProposal[] generateOperatorProposals(String context, int offset) {
Image img = BPELUIPlugin.INSTANCE.getImage(IBPELUIConstants.ICON_OPERATION_16);
ICompletionProposal[] proposals = new ICompletionProposal[OPERATOR_LIST.length];
for (int i=0; i<OPERATOR_LIST.length; i++) {
proposals[i] = new CompletionProposal(OPERATOR_LIST[i], offset, 0, OPERATOR_LIST[i].length(), img, OPERATOR_LIST[i], null, null);
}
return proposals;
}
ICompletionProposal[] generateLinkProposals(String context, int offset) {
ArrayList<ICompletionProposal> results = new ArrayList<ICompletionProposal>();
Image linkImg = BPELUIPlugin.INSTANCE.getImage(IBPELUIConstants.ICON_LINK_16);
for( Object next : new LinkContentProvider( LinkContentProvider.INCOMING ).getElements(theModel) ) {
Link link = (Link) next;
String replName = DOLLAR + link.getName();
if (replName.startsWith(context)) {
results.add(new CompletionProposal(replName,
offset - context.length(), context.length(),
replName.length(), linkImg, link.getName() ,
null, null));
}
}
if (results.size() < 1) {
return new ICompletionProposal[] {
new CompletionProposal(EMPTY_STRING, offset, 0,
0, null, Messages.getString("ExpressionContentAssistProcessor.31"), //$NON-NLS-1$
null, null) };
}
return results.toArray(EMPTY_COMPLETION_PROPOSALS);
}
// Bugzilla 320654
String getNamespacePrefix(EObject context, String namespace)
{
String prefix = BPELUtils.getNamespacePrefix(context, namespace);
while ( prefix==null )
{
prefix = BPELUtil.lookupOrCreateNamespacePrefix(context, namespace, null, null);
if ( prefix == null )
throw new IllegalArgumentException("namespace prefix is null");
}
return prefix;
}
/**
* From model, determine list of variables the user may want to choose from.
*/
ICompletionProposal[] generateVariableProposals(String context, int offset) {
boolean seekChildren = false;
String context2;
if ((context.length() > 0) && (context.charAt(0) == '$'))
context2 = context.substring(1);
else
context2 = context;
int slash = context2.indexOf('/');
int dot = context2.indexOf('.');
int at = context2.indexOf('@');
if ((slash > -1) || (dot > -1) || (at > -1))
seekChildren = true;
Variable[] variables = BPELUtil.getVisibleVariables((EObject)theModel);
ArrayList<ICompletionProposal> results = new ArrayList<ICompletionProposal>();
CompletionProposal prop = null;
String name;
Variable currVar = null;
XSDTypeDefinition currXsdType = null;
Message currMsg = null;
XSDElementDeclaration currXsdElem = null;
Image varImg = BPELUIPlugin.INSTANCE.getImage(IBPELUIConstants.ICON_VARIABLE_16);
Image partImg = BPELUIPlugin.INSTANCE.getImage(IBPELUIConstants.ICON_PART_16);
Image elementImg = BPELUIPlugin.INSTANCE.getImage(IBPELUIConstants.ICON_XSD_ELEMENT_DECLARATION_16);
Image attrImg = BPELUIPlugin.INSTANCE.getImage(IBPELUIConstants.ICON_XSD_ATTRIBUTE_DECLARATION_16);
try
{
if (seekChildren) {
// walk down path
int index = 0;
int level = 0;
int token = -1;
char t = 0;
String levelName;
while (index < context2.length()) {
t = context2.charAt(index);
if ((t == '.') || (t == '/')) {
levelName = context2.substring(token+1, index);
/*
// check for namespace
int ns = levelName.indexOf(':');
if (ns > -1) {
levelNSPrefix = levelName.substring(0, ns);
levelName = levelName.substring(ns+1);
}
else
levelNSPrefix = null;
*/
// find variable
if (level == 0) {
for (int i=0; i<variables.length; i++) {
if (levelName.compareTo(variables[i].getName()) == 0) {
currVar = variables[i];
currXsdType = currVar.getType();
currMsg = currVar.getMessageType();
currXsdElem = currVar.getXSDElement();
level++;
break;
}
}
if (currVar == null)
break;
}
// traverse down
else {
boolean childFound = false;
if (context2.charAt(token) == '.') {
if (currMsg != null) {
if (currMsg.getParts() != null) {
for(Object next : currMsg.getParts().values()) {
Part item = (Part) next ;
if (levelName.compareTo(item.getName()) == 0) {
currXsdType = item.getTypeDefinition();
currMsg = item.getEMessage();
currXsdElem = item.getElementDeclaration();
childFound = true;
break;
}
}
}
}
if (!childFound)
break;
}
// search for child objects
else if (context2.charAt(token) == '/') {
if (currXsdType == null) {
if (currXsdElem != null) {
currXsdType = currXsdElem.getTypeDefinition();
}
}
if (currXsdType instanceof XSDComplexTypeDefinition) {
XSDComplexTypeDefinition xsdcomplex = (XSDComplexTypeDefinition)currXsdType;
for(Object next : XSDUtils.getChildElements(xsdcomplex)) {
XSDElementDeclaration elem = ((XSDElementDeclaration)next).getResolvedElementDeclaration();
String elemName = elem.getName();
if (elem.getTargetNamespace() != null) {
String elemNSPrefix = getNamespacePrefix(currVar, elem.getTargetNamespace());
if (elemNSPrefix != null) {
elemName = elemNSPrefix + COLON + elemName;
}
}
if (levelName.compareTo(elemName) == 0) {
currXsdType = elem.getTypeDefinition();
currXsdElem = null;
currMsg = null;
//currXsdElem = elem.getResolvedElementDeclaration();
childFound = true;
break;
}
}
}
else if (currXsdType instanceof XSDSimpleTypeDefinition) {
XSDSimpleTypeDefinition xsdsimple = (XSDSimpleTypeDefinition)currXsdType;
//currXsdType = xsdsimple.getBaseType();
//XSDSimpleTypeDefinition tempsimple = xsdsimple.getBaseTypeDefinition();
//org.eclipse.xsd.XSDParticle temppart = xsdsimple.getComplexType();
//XSDSimpleTypeDefinition tempsimple2 = xsdsimple.getItemTypeDefinition();
String tempname = xsdsimple.getName();
if (levelName.compareTo(tempname) == 0) {
childFound = true;
}
}
if (!childFound)
break;
}
}
token = index;
}
else if (t == '@')
token = index;
index++;
}
// determine if last character is a special character if above is successful
if (index == context2.length()) {
// looking for parts, attributes or elements?
String beginsWith;
if ((index-1) == token)
beginsWith = EMPTY_STRING;
else
beginsWith = context2.substring(token+1);
if ((context2.charAt(token) == '/') || (context2.charAt(token) == '@')) {
if (currXsdType == null) {
if (currXsdElem != null) {
currXsdType = currXsdElem.getTypeDefinition();
}
}
if (currXsdType instanceof XSDComplexTypeDefinition) {
XSDComplexTypeDefinition xsdcomplex = (XSDComplexTypeDefinition)currXsdType;
@SuppressWarnings("rawtypes")
Iterator eaIter;
if (context2.charAt(token) == '/')
eaIter = XSDUtils.getXSDElementsAndAttributes(xsdcomplex).iterator();
else
eaIter = XSDUtils.getChildAttributes(xsdcomplex).iterator();
Image img = null;
String tempReplName = null;
String tempDispName = null;
String namespace = null;
String nsprefix = null;
while (eaIter.hasNext()) {
Object tempEA = eaIter.next();
if (tempEA instanceof XSDAttributeDeclaration) {
XSDAttributeDeclaration attr = (XSDAttributeDeclaration)tempEA;
tempReplName = AT + attr.getName();
tempDispName = attr.getName();
namespace = attr.getTargetNamespace();
if ((namespace != null) && (namespace.length() > 0)) {
nsprefix = getNamespacePrefix(currVar, namespace);
tempReplName = AT + nsprefix + COLON + attr.getName();
tempDispName = nsprefix + COLON + tempDispName;
}
img = attrImg;
}
else if (tempEA instanceof XSDElementDeclaration) {
XSDElementDeclaration elem = ((XSDElementDeclaration)tempEA).getResolvedElementDeclaration();
tempReplName = elem.getName();
tempDispName = tempReplName;
namespace = elem.getTargetNamespace();
if ((namespace != null) && (namespace.length() > 0)) {
nsprefix = getNamespacePrefix(currVar, namespace);
tempReplName = nsprefix + COLON + tempDispName;
tempDispName = tempReplName;
}
img = elementImg;
}
if (tempReplName != null) {
if ((beginsWith.length() == 0) || (tempDispName != null && tempDispName.startsWith(beginsWith))) {
int replOffset = offset-beginsWith.length();
int replLen = beginsWith.length();
if (context2.charAt(token) == '@') {
replOffset--;
replLen++;
}
prop = new CompletionProposal(tempReplName, replOffset, replLen,
tempReplName.length(), img, tempDispName + " " , //$NON-NLS-1$
null, null);
results.add(prop);
}
}
tempReplName = null;
tempDispName = null;
}
}
else if (currXsdType instanceof XSDSimpleTypeDefinition) {
XSDSimpleTypeDefinition simple = (XSDSimpleTypeDefinition)currXsdType;
// do nothing?
}
}
// search for parts
else if (context2.charAt(token) == '.') {
if (currMsg != null) {
if (currMsg.getParts() != null) {
for(Object next : currMsg.getParts().values() ) {
Part item = (Part) next;
if ((beginsWith.length() == 0) || (item.getName().startsWith(beginsWith))) {
prop = new CompletionProposal(item.getName(), offset-beginsWith.length(), beginsWith.length(),
item.getName().length(), partImg, item.getName() + " " , //$NON-NLS-1$
null, null);
results.add(prop);
}
}
}
}
}
}
}
//variables
else {
for (Variable v : variables) {
name = v.getName();
if (name.startsWith(context2)) {
prop = new CompletionProposal(DOLLAR + name, offset-context.length(), context.length(),
name.length()+1, varImg, name + " " , //$NON-NLS-1$
null, null);
results.add(prop);
}
}
}
}
catch(IllegalArgumentException ex)
{
results.toArray(EMPTY_COMPLETION_PROPOSALS);
}
if (results.size() < 1) {
return new ICompletionProposal [] {
new CompletionProposal(EMPTY_STRING, offset, 0,
0, null, Messages.getString("ExpressionContentAssistProcessor.31"), //$NON-NLS-1$
null, null) };
}
return results.toArray(EMPTY_COMPLETION_PROPOSALS);
}
// simple form of determining if variable is located at offset
String startOfVariable(ITextViewer viewer, int offset) {
int startPosition = offset-1;
char currChar;
String context = EMPTY_STRING;
IDocument document = viewer.getDocument();
try {
while (startPosition >= 0) {
currChar = document.getChar(startPosition);
if (currChar == '$')
return context;
if (!(Character.isLetterOrDigit(currChar) || currChar == '/'
|| currChar == '.' || currChar == '@' || currChar == '_'))
return null;
context = currChar + context;
startPosition--;
}
}
catch (Exception e) {
System.out.println(e.toString());
}
return null;
}
// simple form of determing if function is located at offset
String startOfFunction(ITextViewer viewer, int offset) {
int startPosition = offset-1;
char currChar;
String context = EMPTY_STRING;
IDocument document = viewer.getDocument();
try {
while (startPosition >= 0) {
if (Character.isWhitespace(currChar = document.getChar(startPosition)) ||
isReservedOperatorCharacter(currChar) ||
(currChar == '(') || (currChar == '[')) {
if (context.length() > 0) {
return context;
}
return null;
}
context = currChar + context;
startPosition--;
}
}
catch (Exception e) {
System.out.println(e.toString());
}
return context;
}
private boolean isLocationPathCharacter(char c) {
final String LOCATION_CHARS = "./:@-"; //$NON-NLS-1$
if ((LOCATION_CHARS.indexOf(c) > -1) || (Character.isLetterOrDigit(c) || c == '_'))
return true;
return false;
}
final static String RESERVED_OPERATOR_CHARS = "+-*/"; //$NON-NLS-1$
private boolean isReservedOperatorCharacter(char c) {
return RESERVED_OPERATOR_CHARS.indexOf(c) > -1;
}
final static String RESERVED_CLOSING_EXPR_CHARS = ")]"; //$NON-NLS-1$
private boolean isClosingExpressionCharacter(char c) {
return (RESERVED_CLOSING_EXPR_CHARS.indexOf(c) > -1);
}
}