| /******************************************************************************* |
| * 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 ); |
| } |
| |
| Integer value = (Integer) input.getAdapter(Integer.class); |
| if (value != null) { |
| String expressionContext = value.toString(); |
| 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); |
| } |
| |
| } |