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