blob: cad2e8ebd427afd0c08985c77aa8af60cba89470 [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.editors.xpath;
import java.util.ResourceBundle;
import org.eclipse.bpel.ui.BPELUIPlugin;
import org.eclipse.bpel.ui.contentassist.ExpressionContentAssistProcessor;
import org.eclipse.bpel.ui.editors.TextEditor;
import org.eclipse.bpel.ui.expressions.IEditorConstants;
import org.eclipse.bpel.ui.preferences.PreferenceConstants;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.rules.IWhitespaceDetector;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.ContentAssistAction;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
/**
* The XPath Text Editor.
*
* A bunch of the code is borrowed from the Java Text Editor.
*
* @author Michal Chmielewski (michal.chmielewski@oracle.com)
* @date Oct 26, 2006
*/
public class XPathTextEditor extends TextEditor {
/** The editor id, based on the name of our class */
public static final String XPATH_EDITOR_ID = XPathTextEditor.class.getName();
/** Matchable character pairs */
protected final static char[] BRACKETS= { '(', ')', '[', ']' };
/** The editor's bracket maTtcher */
protected XPathCharPairMatcher fBracketMatcher= new XPathCharPairMatcher(BRACKETS);
/** The bracket inserter. */
BracketInserter fBracketInserter= new BracketInserter();
private final ColorManager fColorManager;
IWhitespaceDetector fWhitespaceDetector = new XPathWhitespaceDetector();
VariablePickerAction fVariablePickerAction;
/** Decoration for the content assist */
private ControlDecoration decoration;
/**
*
*/
public XPathTextEditor() {
super();
this.fColorManager = new ColorManager();
setSourceViewerConfiguration(new XPathSourceViewerConfiguration(this.fColorManager) );
}
/**
*
*/
@Override
protected void createActions() {
super.createActions();
ResourceBundle bundle = ResourceBundle.getBundle("bpelexpression"); //$NON-NLS-1$
Action action = new ContentAssistAction(bundle, "ContentAssistProposal.", this); //$NON-NLS-1$
String id = ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS;
action.setActionDefinitionId(id);
setAction("ContentAssistProposal", action); //$NON-NLS-1$
markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$
this.fVariablePickerAction = new VariablePickerAction(XPathEditorMessages.getBundleForConstructedKeys(), "Editor.VariablePicker.", this); //$NON-NLS-1$
// action.setHelpContextId(IAbstractTextEditorHelpContextIds.GOTO_LINE_ACTION);
this.fVariablePickerAction.setActionDefinitionId(IXPathEditorActionDefinitionIds.VARIABLE_PICKER);
setAction(IXPathEditorActionConstants.VARIABLE_PICKER, this.fVariablePickerAction);
}
@Override
protected void doSetInput (IEditorInput input) throws CoreException {
super.doSetInput(input);
ExpressionContentAssistProcessor caproc = (ExpressionContentAssistProcessor)
this.getSourceViewerConfiguration().
getContentAssistant( this.getSourceViewer() ).
getContentAssistProcessor(IDocument.DEFAULT_CONTENT_TYPE);
EObject eObj = (EObject) input.getAdapter(EObject.class);
if (eObj != null) {
caproc.setModelObject( eObj );
}
String expressionContext = (String) input.getAdapter(Integer.class);
if (expressionContext != null) {
caproc.setExpressionContext(expressionContext);
//
if (this.fVariablePickerAction != null) {
this.fVariablePickerAction.setEnabled(expressionContext.indexOf(IEditorConstants.ET_JOIN) < 0);
}
}
}
/**
* @see AbstractTextEditor#createPartControl(Composite)
*/
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
IPreferenceStore preferenceStore= getPreferenceStore();
boolean closeBrackets= preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACKETS);
boolean closeStrings= preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_STRINGS);
this.fBracketInserter.setCloseBracketsEnabled(closeBrackets);
this.fBracketInserter.setCloseStringsEnabled(closeStrings);
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension) {
((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(this.fBracketInserter);
}
}
/**
* @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
* @since 3.1
*/
@Override
protected void editorContextMenuAboutToShow(IMenuManager menu) {
super.editorContextMenuAboutToShow(menu);
IAction variablePicker = getAction(IXPathEditorActionConstants.VARIABLE_PICKER);
menu.add( new Separator() );
menu.add( variablePicker );
// menu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator(ITextEditorActionConstants.GROUP_SETTINGS));
// menu.appendToGroup(ITextEditorActionConstants.GROUP_SETTINGS, preferencesAction);
//
// menu.appendToGroup(ITextEditorActionConstants.GROUP_SAVE, new Separator(ITextEditorActionConstants.GROUP_OPEN));
// MenuManager showInSubMenu= new MenuManager(getShowInMenuLabel());
// showInSubMenu.add(ContributionItemFactory.VIEWS_SHOW_IN.create(getEditorSite().getWorkbenchWindow()));
// menu.appendToGroup(ITextEditorActionConstants.GROUP_OPEN, showInSubMenu);
}
/**
*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#dispose()
*/
@Override
public void dispose()
{
this.fColorManager.dispose();
this.fBracketMatcher.dispose();
ISourceViewer sourceViewer= getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension) {
((ITextViewerExtension) sourceViewer).removeVerifyKeyListener(this.fBracketInserter);
}
super.dispose();
}
@Override
protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
support.setCharacterPairMatcher(this.fBracketMatcher);
support.setMatchingCharacterPainterPreferenceKeys(
PreferenceConstants.EDITOR_MATCHING_BRACKETS,
PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR);
super.configureSourceViewerDecorationSupport(support);
}
@Override
protected void setPreferenceStore (IPreferenceStore store) {
if (store != null) {
PreferenceConstants.initializeDefaultValues( store );
}
super.setPreferenceStore(store);
}
/**
* @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
*/
@Override
protected void handlePreferenceStoreChanged (PropertyChangeEvent event) {
String p = event.getProperty();
if (PreferenceConstants.EDITOR_CLOSE_BRACKETS.equals(p)) {
this.fBracketInserter.setCloseBracketsEnabled(getPreferenceStore().getBoolean(p));
return;
}
if (PreferenceConstants.EDITOR_CLOSE_STRINGS.equals(p)) {
this.fBracketInserter.setCloseStringsEnabled(getPreferenceStore().getBoolean(p));
return;
}
super.handlePreferenceStoreChanged(event);
}
/**
* Enable/disable content assist decoration
* @param enabled
*/
public void setDecoration(boolean enabled) {
if (enabled){
this.decoration = new ControlDecoration(getSourceViewer().getTextWidget(), SWT.TOP | SWT.LEFT);
this.decoration.setMarginWidth(25);
this.decoration.setDescriptionText("Content Assist Available (Ctrl+Space)");
this.decoration.setShowOnlyOnFocus(true);
FieldDecoration dec = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
this.decoration.setImage(dec.getImage());
} else {
this.decoration.dispose();
}
}
// VZ: make this class public and static
public class BracketInserter implements VerifyKeyListener {
private boolean fCloseBrackets = true;
private boolean fCloseStrings = true;
/**
* @param enabled
*/
public void setCloseBracketsEnabled(boolean enabled) {
this.fCloseBrackets= enabled;
}
/**
* @param enabled
*/
public void setCloseStringsEnabled(boolean enabled) {
this.fCloseStrings= enabled ;
}
private boolean hasIdentifierToTheRight(IDocument document, int offset) {
try {
int end= offset;
IRegion endLine= document.getLineInformationOfOffset(end);
int maxEnd= endLine.getOffset() + endLine.getLength();
while (end != maxEnd && XPathTextEditor.this.fWhitespaceDetector.isWhitespace(document.getChar(end)))
++end;
return end != maxEnd && Character.isJavaIdentifierPart(document.getChar(end));
} catch (BadLocationException e) {
// be conservative
return true;
}
}
private boolean hasIdentifierToTheLeft(IDocument document, int offset) {
try {
int start= offset;
IRegion startLine= document.getLineInformationOfOffset(start);
int minStart= startLine.getOffset();
while (start != minStart && XPathTextEditor.this.fWhitespaceDetector.isWhitespace(document.getChar(start - 1)))
--start;
return start != minStart && Character.isJavaIdentifierPart(document.getChar(start - 1));
} catch (BadLocationException e) {
return true;
}
}
private boolean hasCharacterToTheRight(IDocument document, int offset, char character) {
try {
int end= offset;
IRegion endLine= document.getLineInformationOfOffset(end);
int maxEnd= endLine.getOffset() + endLine.getLength();
while (end != maxEnd && XPathTextEditor.this.fWhitespaceDetector.isWhitespace(document.getChar(end)))
++end;
return end != maxEnd && document.getChar(end) == character;
} catch (BadLocationException e) {
// be conservative
return true;
}
}
private boolean hasCharacterAtOffset (IDocument document, int offset, char character) {
try {
return document.getChar(offset) == character ;
} catch (BadLocationException e) {
return false;
}
}
/**
* (non-Javadoc)
* @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
*/
@SuppressWarnings("fallthrough")
public void verifyKey(VerifyEvent event) {
if (!event.doit || getInsertMode() != SMART_INSERT) {
return;
}
ISourceViewer sourceViewer = getSourceViewer();
IDocument document= sourceViewer.getDocument();
final Point selection= sourceViewer.getSelectedRange();
final int offset= selection.x;
final int length= selection.y;
switch (event.character) {
case ']' :
if (!this.fCloseBrackets) {
return ;
}
// Fall through
case ')':
if (hasCharacterAtOffset(document, offset + length, event.character ) ) {
sourceViewer.setSelectedRange(offset + length + 1,0);
event.doit = false;
return ;
}
break;
case '(':
if (hasCharacterToTheRight(document, offset + length, '('))
return;
// fall through
case '[':
if (!this.fCloseBrackets)
return;
if (hasIdentifierToTheRight(document, offset + length))
return;
// fall through
case '\'':
if (event.character == '\'') {
if (!this.fCloseStrings)
return;
if ( hasCharacterAtOffset(document,offset-1,'\\')) {
return ;
}
if ( hasCharacterAtOffset(document,offset + length,'\'') ) {
sourceViewer.setSelectedRange(offset + length + 1,0);
event.doit = false;
return ;
}
if (hasIdentifierToTheLeft(document, offset) || hasIdentifierToTheRight(document, offset + length)) {
return;
}
}
// fall through
case '"':
if (event.character == '"') {
if (!this.fCloseStrings)
return;
if ( hasCharacterAtOffset(document,offset-1,'\\')) {
return ;
}
if ( hasCharacterAtOffset(document,offset+length,'"') ) {
sourceViewer.setSelectedRange(offset+length+1,0);
event.doit = false;
return ;
}
if (hasIdentifierToTheLeft(document, offset) || hasIdentifierToTheRight(document, offset + length))
return;
}
try {
// ITypedRegion partition= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset, true);
//// if (! IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType()) && partition.getOffset() != offset)
// if (! IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType()))
// return;
//
if (!validateEditorInputState()) {
return ;
}
final StringBuffer buffer= new StringBuffer();
buffer.append( event.character );
buffer.append( getPeerCharacter(event.character) );
document.replace(offset, length, buffer.toString());
// move to the right of the inserted element
sourceViewer.setSelectedRange(offset+1,0);
//
// BracketLevel level= new BracketLevel();
// fBracketLevelStack.push(level);
//
// LinkedPositionGroup group= new LinkedPositionGroup();
// group.addPosition(new LinkedPosition(document, offset + 1, 0, LinkedPositionGroup.NO_STOP));
//
// LinkedModeModel model= new LinkedModeModel();
// model.addLinkingListener(this);
// model.addGroup(group);
// model.forceInstall();
//
// level.fOffset= offset;
// level.fLength= 2;
//
// // set up position tracking for our magic peers
// if (fBracketLevelStack.size() == 1) {
// document.addPositionCategory(CATEGORY);
// document.addPositionUpdater(fUpdater);
// }
// level.fFirstPosition= new Position(offset, 1);
// level.fSecondPosition= new Position(offset + 1, 1);
// document.addPosition(CATEGORY, level.fFirstPosition);
// document.addPosition(CATEGORY, level.fSecondPosition);
//
// level.fUI= new EditorLinkedModeUI(model, sourceViewer);
// level.fUI.setSimpleMode(true);
// level.fUI.setExitPolicy(new ExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack));
// level.fUI.setExitPosition(sourceViewer, offset + 2, 0, Integer.MAX_VALUE);
// level.fUI.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
// level.fUI.enter();
//
//
// IRegion newSelection= level.fUI.getSelectedRegion();
// sourceViewer.setSelectedRange(newSelection.getOffset(), newSelection.getLength());
event.doit= false;
} catch (BadLocationException e) {
BPELUIPlugin.log(e);
}
// catch (BadPositionCategoryException e) {
// BPELUIPlugin.log(e);
// }
break;
}
}
}
static char getEscapeCharacter (char character) {
switch (character) {
case '"':
case '\'':
return '\\';
default:
return 0;
}
}
static char getPeerCharacter(char character) {
switch (character) {
case '(':
return ')';
case ')':
return '(';
case '[':
return ']';
case '"':
return character;
case '\'':
return character;
default:
throw new IllegalArgumentException();
}
}
public void addFocusListener(FocusListener focusListener) {
getSourceViewer().getTextWidget().addFocusListener(focusListener);
}
}