/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.ui.views;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.provider.INotifyChangedListener;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.texteditor.ITextEditorExtension2;
import org.eclipse.ui.views.properties.PropertySheet;
import org.eclipse.xsd.XSDConcreteComponent;
import org.eclipse.xsd.util.XSDResourceImpl;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.notification.util.NotificationUtilities;
import org.teiid.designer.core.transaction.SourcedNotification;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.core.workspace.ModelWorkspaceException;
import org.teiid.designer.metamodels.core.Annotation;
import org.teiid.designer.metamodels.core.AnnotationContainer;
import org.teiid.designer.metamodels.core.ModelAnnotation;
import org.teiid.designer.metamodels.xsd.XsdUtil;
import org.teiid.designer.ui.PluginConstants;
import org.teiid.designer.ui.UiConstants;
import org.teiid.designer.ui.UiPlugin;
import org.teiid.designer.ui.common.eventsupport.SelectionUtilities;
import org.teiid.designer.ui.common.text.StyledTextEditor;
import org.teiid.designer.ui.common.util.UiUtil;
import org.teiid.designer.ui.editors.ModelEditorManager;
import org.teiid.designer.ui.util.DiagramProxy;
import org.teiid.designer.ui.viewsupport.ModelObjectUtilities;
import org.teiid.designer.ui.viewsupport.ModelUtilities;
/**
* DescriptionView displays a read-only text viewer of textual content of a selected model or model object.
*
* Editing (or clearing) the text is done via Edit and Clear actions located on the toolbar or via a context menu.
*
*
*
* @since 8.0
*/
public class DescriptionView extends ModelerView
implements ISelectionListener, INotifyChangedListener, IMenuListener,
ITextEditorExtension2 {
private static final String DESCRIPTION_TXN_LABEL = UiConstants.Util.getString("DescriptionView.setDescriptionTransactionLabel"); //$NON-NLS-1$
/**
* The object whose description is being shown. Will either be an {@link EObject}, {@link ModelResource}, or <code>null</code>
* .
*/
private Object currentObject;
private ModelResource currentModel;
private StyledTextEditor textViewerPanel;
// ---------- Description View actions --------------------
private Action editDescriptionAction;
private Action clearDescriptionAction;
private void addListeners() {
IWorkbenchWindow window = UiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow();
window.getSelectionService().addSelectionListener(this);
ModelUtilities.addNotifyChangedListener(this);
this.textViewerPanel.addMenuListener(this);
}
void clear( ) {
if (!this.textViewerPanel.isDisposed()) {
setText(CoreStringUtil.Constants.EMPTY_STRING);
}
}
private void clearDescription() {
boolean cancelled = openEditorIfNeeded();
if( !cancelled ) {
saveChangedObjectDescription(CoreStringUtil.Constants.EMPTY_STRING);
}
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalToolBar(bars.getToolBarManager());
}
/**
* @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createPartControl( Composite parent ) {
super.createPartControl(parent);
GridData gd = new GridData(GridData.FILL_BOTH);
parent.setLayoutData(gd);
parent.setLayout(new GridLayout(2, false));
// Create a Text Viewer
this.textViewerPanel = new StyledTextEditor(parent, SWT.MULTI | SWT.V_SCROLL | SWT.WRAP | SWT.BORDER);
this.textViewerPanel.setAllowCut(false);
this.textViewerPanel.setAllowPaste(false);
this.textViewerPanel.setAllowUndoRedo(false);
this.textViewerPanel.setAllowFind(true);
GridData tvGD = new GridData(GridData.FILL_BOTH);
tvGD.horizontalSpan = 2;
this.textViewerPanel.setLayoutData(tvGD);
this.textViewerPanel.setEditable(false);
Color newColor = UiUtil.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
this.textViewerPanel.getTextWidget().setBackground(newColor);
contributeToActionBars();
addListeners();
}
/**
* @see org.teiid.designer.ui.views.ModelerView#dispose()
* @since 5.5
*/
@Override
public void dispose() {
this.textViewerPanel.addMenuListener(this);
IWorkbenchWindow workbenchWindow = getSite().getWorkbenchWindow();
workbenchWindow.getSelectionService().removeSelectionListener(this);
ModelUtilities.removeNotifyChangedListener(this);
this.textViewerPanel.dispose();
super.dispose();
}
private void editDescription() {
boolean cancelled = openEditorIfNeeded();
if( !cancelled ) {
Shell shell = UiPlugin.getDefault().getCurrentWorkbenchWindow().getShell();
EditDescriptionDialog dialog = new EditDescriptionDialog(shell, getCurrentObjectName(), textViewerPanel.getText());
// try {
// UiPlugin.getDefault().getEventBroker().addListener(ModelResourceEvent.class, this);
// } catch (EventSourceException e) {
// UiConstants.Util.log(IStatus.ERROR, e, e.getMessage());
// }
if (dialog.open() == Window.OK) {
String newDescription = dialog.getChangedDescription();
saveChangedObjectDescription(newDescription);
refresh();
}
}
}
private void fillLocalToolBar( IToolBarManager manager ) {
this.editDescriptionAction = new Action(null) {
@Override
public void run() {
editDescription();
}
};
this.editDescriptionAction.setImageDescriptor(UiPlugin.getDefault().getImageDescriptor(PluginConstants.Images.EDIT_DOCUMENT_ICON));
this.editDescriptionAction.setToolTipText(UiConstants.Util.getString("DescriptionView.edit.tooltip")); //$NON-NLS-1$
this.editDescriptionAction.setText(UiConstants.Util.getString("DescriptionView.edit.label")); //$NON-NLS-1$
manager.add(editDescriptionAction);
clearDescriptionAction = new Action(null) {
@Override
public void run() {
clearDescription();
}
};
this.clearDescriptionAction.setImageDescriptor(UiPlugin.getDefault().getImageDescriptor(PluginConstants.Images.CLEAR_DOCUMENT_ICON));
this.clearDescriptionAction.setToolTipText(UiConstants.Util.getString("DescriptionView.clear.tooltip")); //$NON-NLS-1$
this.clearDescriptionAction.setText(UiConstants.Util.getString("DescriptionView.clear.label")); //$NON-NLS-1$
manager.add(clearDescriptionAction);
}
/**
* @see org.teiid.designer.ui.views.ModelerView#getAdapter(java.lang.Class)
* @since 5.5
*/
@Override
public Object getAdapter( Class key ) {
if (key.equals(IFindReplaceTarget.class)) {
return this.textViewerPanel.getTextViewer().getFindReplaceTarget();
}
return super.getAdapter(key);
}
/**
* @return the currently selected {@link EObject}'s or {@link ModelResource}'s description
* @since 5.5
*/
String getCurrentObjectDescription() {
if (this.currentObject instanceof EObject) {
return ModelObjectUtilities.getDescription((EObject)this.currentObject);
} else if (this.currentObject instanceof ModelResource) {
return ModelUtilities.getModelDescription(this.currentModel);
} else {
// TODO: Check for VDB SELECTION
}
return null;
}
String getCurrentObjectName() {
if (this.currentObject instanceof EObject) {
return ModelerCore.getModelEditor().getName((EObject)this.currentObject);
} else if (this.currentObject instanceof ModelResource) {
return this.currentModel.getItemName();
} else {
// TODO: Check for VDB SELECTION
}
return null;
}
/**Shell shell = UiPlugin.getDefault().getCurrentWorkbenchWindow().getShell();
* @param obj1 the first object being compared
* @param obj2 the second object being compared
* @return <code>true</code> if the objects are equal and both not <code>null</code>
* @since 5.5.3
*/
private boolean haveSameState( Object obj1,
Object obj2 ) {
if (obj1 == obj2) {
return ((obj1 != null) && (obj2 != null));
}
if (obj1 == null) {
return (obj2 == null);
}
if (obj2 == null) {
return false;
}
return obj1.equals(obj2);
}
/**
* @return <code>true</code> if the model editor is open for the current object
* @since 5.5.3
*/
private boolean isEditorOpen() {
if ((this.currentObject != null) && (this.currentModel != null)) {
IFile modelFile = (IFile)this.currentModel.getResource();
return (ModelEditorManager.isOpen(modelFile));
}
return false;
}
@Override
public void menuAboutToShow(IMenuManager manager) {
manager.add(new Separator());
manager.add(editDescriptionAction);
manager.add(clearDescriptionAction);
}
/**
* @see org.eclipse.emf.edit.provider.INotifyChangedListener#notifyChanged(org.eclipse.emf.common.notify.Notification)
* @since 5.5
*/
@Override
public void notifyChanged( Notification notification ) {
boolean descriptionChanged = false;
// if the target of the notification is this object's annotation, refresh the display
if (notification instanceof SourcedNotification) {
Collection nList = ((SourcedNotification)notification).getNotifications();
Notification nextNotification = null;
EObject eObj = null;
for (Iterator iter = nList.iterator(); iter.hasNext();) {
nextNotification = (Notification)iter.next();
eObj = NotificationUtilities.getEObject(nextNotification);
if (eObj == null) {
Resource resource = NotificationUtilities.getResource(nextNotification);
if ((resource != null) && (this.currentModel != null)) {
try {
if (this.currentModel.getEmfResource().equals(resource)) {
descriptionChanged = true;
break;
}
} catch (ModelWorkspaceException e) {
UiConstants.Util.log(IStatus.ERROR, e, e.getClass().getName());
}
}
} else if (eObj instanceof Annotation) {
EObject target = ((Annotation)eObj).getAnnotatedObject();
if ((target != null) && target.equals(this.currentObject)) {
descriptionChanged = true;
break;
}
} else if (eObj instanceof AnnotationContainer) {
if (NotificationUtilities.isAdded(nextNotification)) {
EObject[] newChildren = NotificationUtilities.getAddedChildren(nextNotification);
for (int iChild = 0; iChild < newChildren.length; iChild++) {
if (newChildren[iChild] instanceof Annotation) {
EObject target = ((Annotation)newChildren[iChild]).getAnnotatedObject();
if ((target != null) && target.equals(this.currentObject)) {
descriptionChanged = true;
break;
}
}
}
} else if (NotificationUtilities.isRemoved(nextNotification)) {
EObject[] oldChildren = NotificationUtilities.getRemovedChildren(nextNotification);
for (int iChild = 0; iChild < oldChildren.length; iChild++) {
if (oldChildren[iChild] instanceof Annotation) {
EObject target = ((Annotation)oldChildren[iChild]).getAnnotatedObject();
if (target != null && target.equals(this.currentObject)) {
descriptionChanged = true;
break;
}
}
}
}
} else if( eObj instanceof ModelAnnotation && ((SourcedNotification)notification).getSource() == this ) {
descriptionChanged = true;
}
if (descriptionChanged) {
break;
}
}
} else {
EObject target = NotificationUtilities.getEObject(notification);
if (target instanceof Annotation) {
target = ((Annotation)target).getAnnotatedObject();
if ((target != null) && target.equals(this.currentObject)) {
descriptionChanged = true;
}
}
}
if (descriptionChanged) {
refresh();
}
}
/**
* Should only be called if current object and model are not <code>null</code>.
*
* @since 5.5.3
*/
private boolean openEditorIfNeeded() {
boolean openEditorCancelled = false;
// we only need to worry about the readonly status if the file is not currently open,
// and its underlying IResource is not read only
if( this.currentModel == null ) {
} else if (!isEditorOpen() && !this.currentModel.getResource().getResourceAttributes().isReadOnly()) {
final IFile modelFile = (IFile)this.currentModel.getResource();
Shell shell = UiPlugin.getDefault().getCurrentWorkbenchWindow().getShell();
// may want to change these text strings eventually:
if (MessageDialog.openQuestion(shell,
ModelEditorManager.OPEN_EDITOR_TITLE,
ModelEditorManager.OPEN_EDITOR_MESSAGE)) {
// load and activate, not async (to prevent multiple dialogs from coming up):
// Changed to use method that insures Object editor mode is on
ModelEditorManager.openInEditMode(modelFile, true, UiConstants.ObjectEditor.IGNORE_OPEN_EDITOR);
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
setFocus();
}
});
} else {
openEditorCancelled = true;
}
}
return openEditorCancelled;
}
/**
* Refreshes the enabled state and content of the GUI controls.
*
* @since 5.5.3
*/
void refresh() {
Runnable work = new Runnable() {
@Override
public void run() {
setText(getCurrentObjectDescription());
updateActions();
}
};
UiUtil.runInSwtThread(work, true);
}
/**
* Saves the text editor content to the {@link EObject}'s description if necessary.
*
* @since 5.5
*/
private void saveChangedObjectDescription(String changedDescription) {
// nothing to save if no current object
if (this.currentObject == null) {
return;
}
String currentDescription = changedDescription; //this.textEditor.getText();
String savedDescription = null;
if (this.currentObject instanceof EObject) {
savedDescription = ModelObjectUtilities.getDescription((EObject)this.currentObject);
if (!haveSameState(currentDescription, savedDescription)) {
setDescription((EObject)this.currentObject, currentDescription, this);
}
} else if (this.currentObject instanceof ModelResource) {
savedDescription = ModelUtilities.getModelDescription((ModelResource)this.currentObject);
if (!haveSameState(currentDescription, savedDescription)) {
setDescription((ModelResource)this.currentObject, currentDescription, this);
}
} else if (this.currentObject != null) {
// TODO: - support selection of a VDB
}
}
/**
* @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart,
* org.eclipse.jface.viewers.ISelection)
* @since 5.5
*/
@Override
public void selectionChanged( IWorkbenchPart part,
ISelection selection ) {
if ((part != this) && !(part instanceof PropertySheet)) {
setCurrentObject(SelectionUtilities.getSelectedObject(selection));
updateActions();
}
}
private void setCurrentObject( Object object ) {
this.currentObject = null;
this.currentModel = null;
try {
if (object instanceof EObject) {
EObject eObj = (EObject)object;
if (ModelObjectUtilities.supportsDescription(eObj)) {
this.currentObject = object;
this.currentModel = ModelUtilities.getModelResourceForModelObject(eObj);
}
} else if ((object instanceof IFile) && ModelUtilities.supportsModelDescription((IResource)object)) {
this.currentModel = ModelUtil.getModelResource((IFile)object, false);
this.currentObject = this.currentModel;
} else {
// TODO: - support selection of a VDB
}
} catch (Exception e) {
UiConstants.Util.log(IStatus.ERROR, e, e.getClass().getName());
}
refresh();
}
/*
* Set up a transaction to set description on a model object
*/
private void setDescription( EObject eObject, String description, Object eventSource ) {
if (!ModelObjectUtilities.isReadOnly(eObject)) {
boolean requiredStart = ModelerCore.startTxn(true, true, DESCRIPTION_TXN_LABEL, eventSource);
boolean succeeded = false;
try {
if (eObject.eResource() instanceof XSDResourceImpl) {
if (eObject instanceof XSDConcreteComponent) {
XsdUtil.addUserInfoAttribute((XSDConcreteComponent)eObject, description);
}
} else {
ModelerCore.getModelEditor().setDescription(eObject, description);
}
succeeded = true;
} catch (ModelerCoreException ex) {
String message = UiConstants.Util.getString("DescriptionView.errorSetDescription", eObject.toString()); //$NON-NLS-1$
UiConstants.Util.log(IStatus.ERROR, ex, message);
} finally {
if (requiredStart) {
if (succeeded) {
ModelerCore.commitTxn();
} else {
ModelerCore.rollbackTxn();
}
}
}
}
}
/*
* Set up a transaction to set description on a ModelResource
*/
private void setDescription(ModelResource modelResource, String description,
Object eventSource) {
if (!ModelUtilities.isReadOnly(modelResource)) {
boolean requiredStart = ModelerCore.startTxn(true, true, DESCRIPTION_TXN_LABEL, eventSource);
boolean succeeded = false;
try {
ModelAnnotation annotation = modelResource.getModelAnnotation();
if (annotation != null) {
annotation.setDescription(description);
} else {
String message =
UiConstants.Util.getString("DescriptionView.nullModelAnnotation", modelResource.getPath().toString()); //$NON-NLS-1$)
UiConstants.Util.log(IStatus.ERROR, message);
}
succeeded = true;
} catch (ModelWorkspaceException ex) {
String message = UiConstants.Util.getString("DescriptionView.errorSetDescriptionModel", modelResource.toString()); //$NON-NLS-1$
UiConstants.Util.log(IStatus.ERROR, ex, message);
} finally {
if (requiredStart) {
if (succeeded) {
ModelerCore.commitTxn();
} else {
ModelerCore.rollbackTxn();
}
}
}
}
}
void setText( String newDescription ) {
if (!this.textViewerPanel.isDisposed()) {
String newText = (newDescription == null ? CoreStringUtil.Constants.EMPTY_STRING : newDescription);
// change the text if necessary
if (!newText.equals(this.textViewerPanel.getText())) {
this.textViewerPanel.setText(newText);
}
}
}
void updateActions() {
boolean enable = true;
// Check Read-only state
if (this.currentModel != null) {
enable = !this.currentModel.isReadOnly();//ModelUtilities.isReadOnly(this.currentModel);
} else if (this.currentObject != null) {
// TODO: - support selection of a VDB
} else {
enable = false;
}
// Check for Transient Diagrams
if( enable && this.currentObject instanceof DiagramProxy) {
enable = false;
}
this.editDescriptionAction.setEnabled(enable);
this.clearDescriptionAction.setEnabled(enable);
}
/**
* @see org.eclipse.ui.texteditor.ITextEditorExtension2#isEditorInputModifiable()
* @since 5.5.3
*/
@Override
public boolean isEditorInputModifiable() {
return false; //this.textEditor.isEditable();
}
/**
* @see org.eclipse.ui.texteditor.ITextEditorExtension2#validateEditorInputState()
* @since 5.5.3
*/
@Override
public boolean validateEditorInputState() {
return false;
}
@Override
public void setFocus() {
// DO NOTHING
}
}