| /******************************************************************************* |
| * Copyright (c) 2006, 2012 Oracle. 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 - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.jpa.ui.internal.selection; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.viewers.IPostSelectionProvider; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jpt.common.core.utility.TextRange; |
| import org.eclipse.jpt.common.ui.internal.jface.SelectionChangedAdapter; |
| import org.eclipse.jpt.common.ui.internal.util.SWTUtil; |
| import org.eclipse.jpt.common.ui.internal.utility.PropertyAdapter; |
| import org.eclipse.jpt.common.utility.internal.AbstractTransformer; |
| import org.eclipse.jpt.common.utility.internal.StringTools; |
| import org.eclipse.jpt.common.utility.internal.Transformer; |
| import org.eclipse.jpt.common.utility.internal.model.value.DoublePropertyValueModel; |
| import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; |
| import org.eclipse.jpt.common.utility.internal.model.value.TransformationPropertyValueModel; |
| import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; |
| import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter; |
| import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener; |
| import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; |
| import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; |
| import org.eclipse.jpt.jpa.core.JpaFile; |
| import org.eclipse.jpt.jpa.core.JpaStructureNode; |
| import org.eclipse.jpt.jpa.ui.JpaFileModel; |
| import org.eclipse.jpt.jpa.ui.selection.JpaEditorManager; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IPropertyListener; |
| import org.eclipse.ui.texteditor.ITextEditor; |
| |
| /** |
| * JPA editor manager for a {@link ITextEditor text editor}. |
| * <p> |
| * If the JPA selection model changes, set the text editor's selection |
| * accordingly. If the {@link #textEditor text editor}'s selection changes, |
| * forward the corresponding JPA selection to the {@link #jpaSelectionModel |
| * JPA selection model}. |
| */ |
| class JpaTextEditorManager |
| implements JpaEditorManager |
| { |
| /** |
| * The manager's text editor |
| */ |
| private final ITextEditor textEditor; |
| |
| /** |
| * Listen for the {@link #textEditor text editor}'s input to change. |
| * This can happen if the text editor is re-usable. |
| */ |
| private final IPropertyListener textEditorInputListener = new TextEditorInputListener(); |
| |
| /** |
| * Listen for the {@link #textEditor text editor}'s selection to change. |
| */ |
| private final ISelectionChangedListener textEditorSelectionListener = new TextEditorSelectionListener(); |
| |
| private final ModifiablePropertyValueModel<IFile> fileModel = new SimplePropertyValueModel<IFile>(); |
| |
| /** |
| * We use the JPA file to calculate the JPA selection. |
| * We update the JPA file model's file model whenever the text editor's |
| * file changes. |
| */ |
| private final PropertyValueModel<JpaFile> jpaFileModel; |
| |
| /** |
| * Listen for the model or ourselves (via the file model) to change the |
| * JPA file. |
| */ |
| private final PropertyChangeListener jpaFileListener = new JpaFileListener(); |
| |
| /** |
| * We update the JPA selection model whenever the text editor's selection |
| * changes. |
| */ |
| private final ModifiablePropertyValueModel<JpaStructureNode> jpaSelectionModel = new SimplePropertyValueModel<JpaStructureNode>(); |
| |
| /** |
| * Listen for other views to change the JPA selection. |
| */ |
| private final PropertyChangeListener jpaSelectionListener = new JpaSelectionListener(); |
| |
| |
| // ********** constructor ********** |
| |
| /* CU private */ JpaTextEditorManager(ITextEditor textEditor) { |
| super(); |
| this.textEditor = textEditor; |
| this.textEditor.addPropertyListener(this.textEditorInputListener); |
| this.getTextEditorSelectionProvider().addPostSelectionChangedListener(this.textEditorSelectionListener); |
| |
| this.jpaFileModel = this.buildJpaFileModel(); |
| this.jpaFileModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.jpaFileListener); |
| |
| this.jpaSelectionModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.jpaSelectionListener); |
| |
| // this will trigger a new JPA file |
| // which will trigger us to set the JPA selection |
| this.setFileModel(); |
| } |
| |
| /** |
| * The <em>public</em> JPA selection model will not allow clients to set the |
| * JPA selection to <code>null</code>. Only the editor manager can set the |
| * selection to <code>null</code>. A client may set the JPA selection to |
| * <code>null</code> when it does not have appropriate input. It is OK |
| * for the <em>client's</em> JPA selection be <code>null</code>; but the |
| * editor manager's JPA selection is the <em>master</em> JPA selection for |
| * the workbench page/window and drives the JPA selection for potentially |
| * multiple clients. As a result, clients can only modify the editor |
| * manager's JPA selection to be some non-<code>null</code> value. |
| */ |
| |
| // ********** JPA file ********** |
| |
| public PropertyValueModel<JpaFile> getJpaFileModel() { |
| return this.jpaFileModel; |
| } |
| |
| private PropertyValueModel<JpaFile> buildJpaFileModel() { |
| return new DoublePropertyValueModel<JpaFile>(this.buildJpaFileModelModel()); |
| } |
| |
| private PropertyValueModel<PropertyValueModel<JpaFile>> buildJpaFileModelModel() { |
| return new TransformationPropertyValueModel<IFile, PropertyValueModel<JpaFile>>(this.fileModel, JPA_FILE_MODEL_TRANSFORMER); |
| } |
| |
| private static final Transformer<IFile, PropertyValueModel<JpaFile>> JPA_FILE_MODEL_TRANSFORMER = new JpaFileModelTransformer(); |
| |
| /* CU private */ static class JpaFileModelTransformer |
| extends AbstractTransformer<IFile, PropertyValueModel<JpaFile>> |
| { |
| @Override |
| protected PropertyValueModel<JpaFile> transform_(IFile file) { |
| return (JpaFileModel) file.getAdapter(JpaFileModel.class); |
| } |
| } |
| |
| /* CU private */ class JpaFileListener |
| extends PropertyChangeAdapter |
| { |
| @Override |
| public void propertyChanged(PropertyChangeEvent event) { |
| JpaTextEditorManager.this.jpaFileChanged(); |
| } |
| } |
| |
| /** |
| * Dispatch to the UI thread. |
| */ |
| /* CU private */ void jpaFileChanged() { |
| this.execute(new JpaFileChangedRunnable()); |
| } |
| |
| /* CU private */ class JpaFileChangedRunnable |
| implements Runnable |
| { |
| public void run() { |
| JpaTextEditorManager.this.jpaFileChanged_(); |
| } |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this); |
| } |
| } |
| |
| /** |
| * Pre-condition: executing on the UI thread. |
| */ |
| /* CU private */ void jpaFileChanged_() { |
| this.jpaSelectionModel.setValue(this.getTextEditorJpaSelection()); |
| } |
| |
| |
| // ********** JPA selection ********** |
| |
| public ModifiablePropertyValueModel<JpaStructureNode> getJpaSelectionModel() { |
| return this.jpaSelectionModel; |
| } |
| |
| /* CU private */ class JpaSelectionListener |
| extends PropertyChangeAdapter |
| { |
| @Override |
| public void propertyChanged(PropertyChangeEvent event) { |
| JpaTextEditorManager.this.setTextEditorJpaSelection((JpaStructureNode) event.getNewValue()); |
| } |
| } |
| |
| /* CU private */ void setJpaSelection(ISelection selection) { |
| this.jpaSelectionModel.setValue(this.getTextEditorJpaSelection(selection)); |
| } |
| |
| |
| // ********** text editor selection ********** |
| |
| /* CU private */ class TextEditorSelectionListener |
| extends SelectionChangedAdapter |
| { |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| JpaTextEditorManager.this.setJpaSelection(event.getSelection()); |
| } |
| } |
| |
| /** |
| * Dispatch to the UI thread. |
| */ |
| /* CU private */ void setTextEditorJpaSelection(JpaStructureNode selection) { |
| this.execute(new SetTextEditorSelectionRunnable(selection)); |
| } |
| |
| /* CU private */ class SetTextEditorSelectionRunnable |
| implements Runnable |
| { |
| private final JpaStructureNode selection; |
| |
| SetTextEditorSelectionRunnable(JpaStructureNode selection) { |
| super(); |
| this.selection = selection; |
| } |
| |
| public void run() { |
| JpaTextEditorManager.this.setTextEditorJpaSelection_(this.selection); |
| } |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.selection); |
| } |
| } |
| |
| /** |
| * Pre-condition: executing on the UI thread. |
| * If the new JPA selection is not <code>null</code> and it is different |
| * from the text editor's current JPA selection, modify the text editor's |
| * selection. |
| */ |
| /* CU private */ void setTextEditorJpaSelection_(JpaStructureNode selection) { |
| if ((selection != null) && (selection != this.getTextEditorJpaSelection())) { |
| this.setTextEditorSelection(selection.getSelectionTextRange()); |
| } |
| } |
| |
| private void setTextEditorSelection(TextRange textRange) { |
| if (textRange != null) { |
| // no need for a disposed check... |
| this.textEditor.selectAndReveal(textRange.getOffset(), textRange.getLength()); |
| } |
| } |
| |
| /** |
| * Return the JPA selection corresponding to the text editor's current |
| * selection. |
| * Pre-condition: executing on the UI thread. |
| */ |
| private JpaStructureNode getTextEditorJpaSelection() { |
| // the selection provider can be null if the text editor was disposed |
| // before we get a chance to [asynchronously] handle the event |
| IPostSelectionProvider selProvider = this.getTextEditorSelectionProvider(); |
| return (selProvider == null) ? null : this.getTextEditorJpaSelection(selProvider.getSelection()); |
| } |
| |
| private JpaStructureNode getTextEditorJpaSelection(ISelection selection) { |
| return (selection instanceof ITextSelection) ? this.getTextEditorJpaSelection((ITextSelection) selection) : null; |
| } |
| |
| private JpaStructureNode getTextEditorJpaSelection(ITextSelection selection) { |
| JpaFile jpaFile = this.jpaFileModel.getValue(); |
| return (jpaFile == null) ? null : jpaFile.getStructureNode(selection.getOffset()); |
| } |
| |
| |
| // ********** text editor input ********** |
| |
| /* CU private */ class TextEditorInputListener |
| extends PropertyAdapter |
| { |
| @Override |
| public void propertyChanged(Object source, int propertyID) { |
| if (propertyID == IEditorPart.PROP_INPUT) { |
| JpaTextEditorManager.this.setFileModel(); |
| } |
| } |
| } |
| |
| |
| // ********** misc ********** |
| |
| public IEditorPart getEditor() { |
| return this.textEditor; |
| } |
| |
| /* CU private */ void setFileModel() { |
| this.fileModel.setValue(this.getTextEditorFile()); |
| } |
| |
| private IFile getTextEditorFile() { |
| IEditorInput input = this.textEditor.getEditorInput(); |
| return (input instanceof IFileEditorInput) ? ((IFileEditorInput) input).getFile() : null; |
| } |
| |
| private IPostSelectionProvider getTextEditorSelectionProvider() { |
| return (IPostSelectionProvider) this.textEditor.getSelectionProvider(); |
| } |
| |
| private void execute(Runnable runnable) { |
| SWTUtil.execute(this.textEditor.getSite().getShell().getDisplay(), runnable); |
| } |
| |
| public void dispose() { |
| this.jpaFileModel.removePropertyChangeListener(PropertyValueModel.VALUE, this.jpaFileListener); |
| this.jpaSelectionModel.removePropertyChangeListener(PropertyValueModel.VALUE, this.jpaSelectionListener); |
| this.getTextEditorSelectionProvider().removePostSelectionChangedListener(this.textEditorSelectionListener); |
| this.textEditor.removePropertyListener(this.textEditorInputListener); |
| } |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.textEditor); |
| } |
| } |