/* * 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.explorer; import java.util.EventObject; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.ui.model.WorkbenchContentProvider; import org.teiid.core.designer.event.EventObjectListener; import org.teiid.core.designer.event.EventSourceException; import org.teiid.designer.core.ModelerCore; 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.core.workspace.ResourceChangeUtilities; import org.teiid.designer.metamodels.core.ModelAnnotation; import org.teiid.designer.metamodels.core.ModelImport; import org.teiid.designer.ui.PluginConstants; import org.teiid.designer.ui.UiConstants; import org.teiid.designer.ui.UiPlugin; import org.teiid.designer.ui.event.ModelResourceEvent; import org.teiid.designer.ui.viewsupport.ExtendedModelObjectContentProvider; import org.teiid.designer.ui.viewsupport.IExtendedModelObject; import org.teiid.designer.ui.viewsupport.ImportContainer; import org.teiid.designer.ui.viewsupport.ModelObjectContentProvider; import org.teiid.designer.ui.viewsupport.ModelUtilities; /** * ModelExplorerContentProvider * * @since 8.0 */ public class ModelExplorerContentProvider extends WorkbenchContentProvider implements UiConstants, IPropertyChangeListener { private static final Object[] NO_CHILDREN = new Object[0]; private boolean showModelContent = true; // modTODO: control this through preference "Show Model Contents in Model Explorer" private boolean enableModelSorting = false; private boolean sortModelContent = false; private ModelObjectContentProvider modelProvider = ModelObjectContentProvider.getInstance(); private boolean showImportStatements = true; private Map importContainerMap; // key = annotation, value = ImportContainer private EventObjectListener modelListener; private IResourceChangeListener resourceListener; private IResourceDeltaVisitor deltaVisitor; private boolean keepProcessing = true; private ExtendedModelObjectContentProvider extendedContentProvider; /** * Construct an instance of ModelExplorerContentProvider. */ public ModelExplorerContentProvider() { this.importContainerMap = new HashMap(); this.extendedContentProvider = new ExtendedModelObjectContentProvider(); this.deltaVisitor = new IResourceDeltaVisitor() { @Override public boolean visit( IResourceDelta theDelta ) { return handleDeltaVisit(theDelta); } }; this.modelListener = new EventObjectListener() { @Override public void processEvent( EventObject theEvent ) { handleModelEvents(theEvent); } }; try { UiPlugin.getDefault().getEventBroker().addListener(ModelResourceEvent.class, this.modelListener); } catch (EventSourceException theException) { Util.log(theException); } this.resourceListener = new IResourceChangeListener() { @Override public void resourceChanged( IResourceChangeEvent theEvent ) { handleResourceEvent(theEvent); } }; ModelerCore.getWorkspace().addResourceChangeListener(this.resourceListener); this.enableModelSorting = false; this.sortModelContent = this.getSortModelContentsPreferenceBooleanValue(); UiPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); } /** * setter for enableModelSorting flag */ public void setEnableModelSorting( boolean enableSort ) { this.enableModelSorting = enableSort; } /** * getter for enableModelSorting flag */ public boolean isModelSortingEnabled() { return this.enableModelSorting; } /** * handle preference change. This only responds to change in modelContents sort preference. */ @Override public void propertyChange( PropertyChangeEvent e ) { String propStr = e.getProperty(); if (propStr != null && (propStr.equals(PluginConstants.Prefs.General.SORT_MODEL_CONTENTS))) { this.sortModelContent = getSortModelContentsPreferenceBooleanValue(); } } /** * helper method for getting the ModelContents sorting preference * * @return 'true' if sort preference is true, 'false' if not. */ private boolean getSortModelContentsPreferenceBooleanValue() { boolean sortModelConents = false; String sortContentsStr = UiPlugin.getDefault().getPreferenceStore().getString(PluginConstants.Prefs.General.SORT_MODEL_CONTENTS); if (sortContentsStr.equals(MessageDialogWithToggle.ALWAYS)) { sortModelConents = true; } else if (sortContentsStr.equals(MessageDialogWithToggle.NEVER)) { sortModelConents = false; } return sortModelConents; } /** * Handler for when a model is closing. * * @param theEvent the event being processed * @since 4.2 */ void handleModelEvents( EventObject theEvent ) { if (((ModelResourceEvent)theEvent).getType() == ModelResourceEvent.CLOSING) { validateImportCache(((ModelResourceEvent)theEvent).getModelResource().getResource()); } } /** * Handler for <code>IResourceChangeEvent</code>s. * * @param theEvent the event being processed * @since 4.2 */ void handleResourceEvent( IResourceChangeEvent theEvent ) { this.keepProcessing = true; if (ResourceChangeUtilities.isPreClose(theEvent) || ResourceChangeUtilities.isPreDelete(theEvent)) { // project is being closed or deleted validateImportCache(theEvent.getResource()); this.keepProcessing = false; // only validate cache one time } else { // not a project IResourceDelta delta = theEvent.getDelta(); if (delta != null) { try { delta.accept(this.deltaVisitor); } catch (CoreException theException) { Util.log(theException); } } } } /** * Processes each cache entry deleting those whose eResource is null or if they are in a project or model being closed or * deleted. * * @param theProject the project being closed or deleted; or <code>null</code> * @since 4.2 */ private void validateImportCache( IResource theResource ) { Iterator itr = this.importContainerMap.keySet().iterator(); while (itr.hasNext()) { boolean remove = false; ModelAnnotation annotation = (ModelAnnotation)itr.next(); if (annotation.eResource() == null) { remove = true; } else if (theResource != null) { // see if the cache entry is from the project being closed or deleted ModelResource modelResource = ModelerCore.getModelEditor().findModelResource(annotation); if (modelResource == null) { remove = true; } else { IResource model = modelResource.getResource(); if (model == null) { // shouldn't happen remove = true; } else if (theResource instanceof IFile) { // model is closing remove = model.equals(theResource); } else if (theResource instanceof IProject) { // project is closing remove = model.getProject().equals(theResource); } } } if (remove) { itr.remove(); } } } /** * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) * @since 4.2 */ public boolean handleDeltaVisit( IResourceDelta theDelta ) { // only handle delete/remove events and project open/close events if (this.keepProcessing && ResourceChangeUtilities.isRemoved(theDelta)) { boolean process = false; IResource resource = theDelta.getResource(); if (resource == null) { // safety net. should never happen. process = true; } else if (ModelerCore.hasModelNature(resource.getProject())) { // deletes of resources in model projects. // could also check to see if deleted resource was a model process = true; } if (process) { validateImportCache(null); this.keepProcessing = false; // only validate cache one time } } // check children recursively return this.keepProcessing; } /** * @see org.eclipse.ui.model.WorkbenchContentProvider#dispose() * @since 4.2 */ @Override public void dispose() { ModelerCore.getWorkspace().removeResourceChangeListener(this.resourceListener); try { UiPlugin.getDefault().getEventBroker().removeListener(ModelResourceEvent.class, this.modelListener); } catch (EventSourceException theException) { Util.log(theException); } super.dispose(); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) */ @Override public Object[] getChildren( final Object parentElement ) { Object[] children = NO_CHILDREN; try { if (parentElement instanceof EObject) { if (this.enableModelSorting && this.sortModelContent) { return modelProvider.getSortedChildren(parentElement); } return modelProvider.getChildren(parentElement); } if (parentElement instanceof IProject) { if (((IProject)parentElement).isOpen()) return ((IProject)parentElement).members(); return NO_CHILDREN; } if (parentElement instanceof IResource) { IResource resource = (IResource)parentElement; if (ModelUtilities.isModelFile(resource)) { try { ModelResource modelResource = ModelUtil.getModelResource((IFile)resource, true); if (showModelContent && modelResource != null) { if (this.enableModelSorting && this.sortModelContent) { children = modelProvider.getSortedChildren(modelResource); } else { children = modelProvider.getChildren(modelResource); } if (showImportStatements && !ModelUtil.isXsdFile(resource)) { Object[] temp = children != null ? children : new Object[] {}; try { ModelAnnotation annotation = modelResource.getModelAnnotation(); if (annotation == null) { children = temp; } else { children = new Object[temp.length + 1]; children[0] = getImportContainer(annotation); for (int i = 0; i < temp.length; ++i) { children[i + 1] = temp[i]; } } } catch (ModelWorkspaceException theException) { if (!modelResource.hasErrors()) { // No errors, so we should log this exception ... UiConstants.Util.log(IStatus.ERROR, theException, theException.getClass().getName()); } children = temp; } catch (Exception theException) { UiConstants.Util.log(IStatus.ERROR, theException, theException.getClass().getName()); children = temp; } } return children; } return NO_CHILDREN; } catch (CoreException e) { e.printStackTrace(); } } } if (parentElement instanceof ImportContainer) { // parentElement will only be ModelAnnotation when showImportStatements == true return ((ImportContainer)parentElement).getModelAnnotation().getModelImports().toArray(); } if (parentElement instanceof IContainer) { return ((IContainer)parentElement).members(); } if (parentElement instanceof IExtendedModelObject) { return extendedContentProvider.getChildren(parentElement); } return children; } catch (CoreException e) { e.printStackTrace(); } return children; } /** * Obtains the cached <code>ImportContainer</code> for the specified import or creates one if necessary. * * @param theImport the import whose container is being requested * @return the container * @since 4.2 */ private ImportContainer getImportContainer( ModelAnnotation theAnnotation ) { ImportContainer result = (ImportContainer)this.importContainerMap.get(theAnnotation); if (result == null) { result = new ImportContainer(theAnnotation, theAnnotation.eResource()); this.importContainerMap.put(theAnnotation, result); } return result; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) */ @Override public Object getParent( Object element ) { Object parent = null; if (element instanceof ModelImport) { parent = ((ModelImport)element).eContainer(); // ModelImport parent's can either be a ModelAnnotation or a VirtualDatabase. // If ModelAnnotation lookup the ImportContainer. if (parent instanceof ModelAnnotation) { parent = getImportContainer((ModelAnnotation)parent); } } else if (element instanceof ImportContainer) { parent = getParent(((ImportContainer)element).getModelAnnotation()); } else if (element instanceof EObject) { parent = modelProvider.getParent(element); } else if (extendedContentProvider.getParent(element) != null) { parent = extendedContentProvider.getParent(element); } else { parent = super.getParent(element); } return parent; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) */ @Override public boolean hasChildren( Object element ) { if (element instanceof IResource) { IResource resource = (IResource)element; if (ModelUtilities.isModelFile(resource)) { // For model file, return true when showModelContent is true return showModelContent; } } else if (element instanceof EObject) { return modelProvider.hasChildren(element); } else if (element instanceof IExtendedModelObject) { return extendedContentProvider.hasChildren(element); } return super.hasChildren(element); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) */ @Override public Object[] getElements( Object inputElement ) { return getChildren(inputElement); } /** * Set whenter or not to display the content of a model file in this TreeView * * @param resource * @return */ public void setShowModelContent( boolean show ) { this.showModelContent = show; } /** * Control whether to show model dependencies as nodes in the tree * * @param show true to show them, false to hide them. */ public void setShowImportStatements( boolean show ) { this.showImportStatements = show; } }