/******************************************************************************* * 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); } }