/*
* 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.core.workspace;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
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.ChangeNotifier;
import org.eclipse.emf.edit.provider.INotifyChangedListener;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.IOperation;
import org.teiid.core.designer.util.ResourceNameUtil;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.container.Container;
import org.teiid.designer.core.index.IndexUtil;
import org.teiid.designer.core.search.ModelWorkspaceSearch;
import org.teiid.designer.core.transaction.SourcedNotification;
import org.teiid.designer.core.util.TransactionUtil;
import org.teiid.designer.core.workspace.ModelUtil.XmiHeaderCache;
import org.teiid.designer.core.xmi.XMIHeader;
/**
* ModelWorkspaceManager
*
* @since 8.0
*/
public class ModelWorkspaceManager implements XmiHeaderCache {
public static boolean VERBOSE = false;
public static boolean ZIP_ACCESS_VERBOSE = false;
/**
* The singleton manager
*/
private static ModelWorkspaceManager manager;
/**
* Returns the {@link ModelWorkspaceItem} corresponding to the given file, or <code>null</code> if unable to associate the
* given file with a {@link ModelWorkspaceItem}.
* <p>
* The file must be one of:
* <ul>
* <li>a <code>.mmm</code> file - the item returned is the corresponding {@link ModelResource}</li>
* <li>a <code>.xml</code> file - the item returned is the corresponding {@link ModelResource}</li>
* </ul>
* <p>
* Creating a {@link ModelWorkspaceItem} has the side effect of creating and opening all of the item's parents if they are not
* yet open.
* <p>
*/
public static ModelResource create( final IFile file,
ModelProject project ) {
if (file == null || !ModelerCore.hasModelNature(file.getProject())) return null;
if (project == null) project = manager.getModelWorkspace().getModelProject(file);
if (file.getFileExtension() != null && ModelUtil.isModelFile(file)) try {
ModelResource resource = (ModelResource)project.findModelWorkspaceItem(file);
if (resource != null) return resource;
// Else it was not found, so it must be created ...
resource = (ModelResource) getModelWorkspaceManager().findModelWorkspaceItem(file, true);
return resource;
} catch (final ModelWorkspaceException e) {
return null;
}
return null;
}
/**
* Returns the {@link ModelWorkspaceItem} corresponding to the given folder, or <code>null</code> if unable to associate the
* given file with a {@link ModelWorkspaceItem}. Creating a {@link ModelWorkspaceItem} has the side effect of creating and
* opening all of the item's parents if they are not yet open.
* <p>
*/
public static ModelWorkspaceItem create( final IFolder folder,
ModelProject project ) {
if (project == null) project = manager.getModelWorkspace().getModelProject(folder);
try {
ModelWorkspaceItem folderItem = project.findModelWorkspaceItem(folder);
if (folderItem != null) return folderItem;
// Else it was not found, so it must be created ...
folderItem = getModelWorkspaceManager().findModelWorkspaceItem(folder, true);
return folderItem;
} catch (final ModelWorkspaceException e) {
return null;
}
}
/**
* Returns the {@link ModelWorkspaceItem} corresponding to the given resource, or <code>null</code> if unable to associate the
* given resource with a {@link ModelWorkspaceItem workspace item}.
* <p>
* The resource must be one of:
* <ul>
* <li>a project - the element returned is the corresponding {@link ModelProject}</li>
* <li>a <code>.mmm</code> file - the item returned is the corresponding {@link ModelResource}</li>
* <li>a <code>.xml</code> file - the item returned is the corresponding {@link ModelResource}</li>
* <li>the workspace root resource - the element returned is the {@link ModelWorkspace}</li>
* </ul>
* <p>
* Creating a {@link ModelWorkspaceItem} has the side effect of creating and opening all of the item's parents if they are not
* yet open.
*
* @param resource the given resource
* @param project the model project that the resource exists in, if known
* @return the {@link ModelWorkspaceItem} corresponding to the given resource, or <code>null</code> if unable to associate the
* given resource with a {@link ModelWorkspaceItem workspace item}
*/
public static ModelWorkspaceItem create( final IResource resource,
final ModelProject project ) {
if (resource == null || (resource.getProject() != null && !ModelerCore.hasModelNature(resource.getProject()))) return null;
final int type = resource.getType();
switch (type) {
case IResource.PROJECT:
return ModelerCore.create((IProject)resource);
case IResource.FILE:
return create((IFile)resource, project);
case IResource.FOLDER:
return create((IFolder)resource, project);
case IResource.ROOT:
return ModelerCore.create((IWorkspaceRoot)resource);
default:
return null;
}
}
/**
* Returns the singleton ModelWorkspaceManager
*/
public final static ModelWorkspaceManager getModelWorkspaceManager() {
if (manager == null) {
manager = new ModelWorkspaceManager();
final IWorkspace workspace;
try {
// Start up the ModelWorkspaceManager ...
workspace = ModelerCore.getWorkspace();
workspace.addResourceChangeListener(manager.getDeltaProcessor(), IResourceChangeEvent.PRE_BUILD
| IResourceChangeEvent.POST_BUILD
| IResourceChangeEvent.POST_CHANGE
| IResourceChangeEvent.PRE_DELETE
| IResourceChangeEvent.PRE_CLOSE);
manager.initModelWorkspace(workspace);
} catch (final Throwable t) {
t.printStackTrace();
ModelerCore.Util.log(IStatus.ERROR,
ModelerCore.Util.getString("ModelWorkspaceManager.Error_encountered_starting_ModelWorkspaceManager_1")); //$NON-NLS-1$
}
}
return manager;
}
public final static void shutdown() throws CoreException {
if (manager != null) try {
// Remove the delta processor as a listener of resource change events
final IWorkspace workspace = ModelerCore.getWorkspace();
workspace.removeResourceChangeListener(manager.getDeltaProcessor());
// Shutdown the manager
manager.shutdownManager();
manager.getIndexManager().disposeAll();
} catch (final CoreException e) {
throw e;
} catch (final Throwable t) {
throw new CoreException(
new Status(
IStatus.ERROR,
ModelerCore.PLUGIN_ID,
1,
ModelerCore.Util.getString("ModelWorkspaceManager.Error_encountered_shutting_down_ModelWorkspaceManager_2"), t)); //$NON-NLS-1$
} finally {
manager = null;
}
}
// public static HashSet OptionNames = new HashSet(20);
/**
* Unique handle to the model workspace.
*/
private final ModelWorkspaceImpl modelWorkspace = new ModelWorkspaceImpl();
/**
* Used to convert <code>IResourceDelta</code>s into <code>ModelWorkspaceDelta</code>s.
*/
private final DeltaProcessor deltaProcessor;
/**
* Infos cache.
*/
protected ModelWorkspaceCache cache = new ModelWorkspaceCache();
protected Container container;
/**
* Collection of listeners for workspace change events
*/
private final Collection workspaceListeners = new ArrayList();
/**
* Collection of veto listeners for resource change events; each listener may veto the unloading and reloading of the
* in-memory model.
*/
private final Collection resourceReloadVetoListeners = new ArrayList();
/**
* Table from IProject to PerProjectInfo. NOTE: this object itself is used as a lock to synchronize creation/removal of per
* project infos
*/
protected Map perProjectInfo = new HashMap(5);
/**
* Used to indicate that one or more ModelResource instances have been marked as indexed to the given type.
*/
private int indexType = ModelResource.NOT_INDEXED;
/**
* Used to keep a cache of IMarker-EObject maps to eliminate repetitive UUID/EObject resolving. Part of Defect 22362 to
* improve XML Document editing performance. Improves time to decoration tree & table view items.
*/
private final ModelMarkerManager markerManager = new ModelMarkerManager();
private final Map FileToxmlHeaderMap = Collections.synchronizedMap(new HashMap());
private final ModelWorkspaceSearch modelWorkspaceSearch = new ModelWorkspaceSearch();
private final ModelWorkspaceIndexManager indexManager = new ModelWorkspaceIndexManager();
/**
* Construct an instance of ModelWorkspaceManager.
*/
private ModelWorkspaceManager() {
super();
// Create the delta processor for resource change event
this.deltaProcessor = new DeltaProcessor(this);
// Add a ChangeNotifier to the container so that the workspace
// manager can listen for changes to the EMF Resource instances
// and mark the associated ModelResource as requiring indexing.
try {
addChangeNotifier(ModelerCore.getModelContainer());
} catch (final CoreException e) {
ModelerCore.Util.log(IStatus.ERROR,
e,
ModelerCore.Util.getString("ModelWorkspaceManager.Error_adding_ChangeNotifier_to_the_model_container_{0}_1", e.getMessage())); //$NON-NLS-1$
}
}
/**
* @param resource
* @param project
* @since 4.3
*/
private void add( final IResource resource, ModelProjectImpl project ) {
// there should be no existing item for the resource being added
final ModelWorkspaceItem resourceItem = this.modelWorkspace.getWorkspaceItem(resource.getFullPath(), resource.getType());
if (resourceItem != null) // if there is return, no need to add
return;
if (project == null) {
// We don't have a project yet. If the resource is being created while the project
// is also being created then its expected that the project has not been added.
project = new ModelProjectImpl(resource.getProject(), this.modelWorkspace);
}
// create new ModelWorkspaceItem for resource
ModelWorkspaceItem newResource = null;
ModelWorkspaceItem parentItem = null;
if (resource.getType() == IResource.PROJECT) {
newResource = project;
parentItem = this.modelWorkspace;
} else {
// create the item and update the cache
try {
if (ModelerCore.DEBUG_MODEL_WORKSPACE || ModelerCore.DEBUG_MODEL_WORKSPACE_EVENT) {
final Object[] params = new Object[] {resource};
final String debugMsg = ModelerCore.Util.getString("ModelWorkspaceManager.DEBUG.Creating_ModelResource_instance_for_0_2", params); //$NON-NLS-1$
ModelerCore.Util.log(IStatus.INFO, debugMsg);
}
if (resource.getType() == IResource.FILE && ((IFile)resource).getFileExtension() != null
&& ModelUtil.isModelFile(resource)) {
newResource = project.createModelResource((IFile)resource);
} else if (resource.getType() == IResource.FOLDER) {
newResource = project.createModelFolder((IFolder)resource);
}
parentItem = this.modelWorkspace.getParent(resource);
} catch (final Exception e) {
ModelerCore.Util.log(IStatus.ERROR,
e,
ModelerCore.Util.getString("ModelWorkspaceManager.Error_creating_new_model_workspace_item___{0}_1", e.getMessage())); //$NON-NLS-1$
}
}
if (parentItem == null) {
parentItem = this.modelWorkspace.getParent(resource);
ModelerCore.Util.log(IStatus.ERROR,
ModelerCore.Util.getString("ModelWorkspaceManager.Unable_to_find_parent_item_for_{0})_3", resource.getFullPath())); //$NON-NLS-1$
return;
}
final ModelWorkspaceItemInfo parentInfo = (ModelWorkspaceItemInfo) getInfo(parentItem);
if (parentInfo != null)
parentInfo.addChild(newResource);
}
private void addChangeNotifier( final Container container ) {
if (container != null) {
final ChangeNotifier notifier = container.getChangeNotifier();
if (notifier != null) notifier.addListener(new NotificationProcessor());
}
}
/**
* Add a listener that would listen to and possibly veto the reloading of a ModelResource from the underlying file.
*/
public void addModelResourceReloadVetoListener( final ModelResourceReloadVetoListener listener ) {
CoreArgCheck.isNotNull(listener);
if (this.resourceReloadVetoListeners.contains(listener)) return;
this.resourceReloadVetoListeners.add(listener);
}
/**
* Add a listener that would listen to the workspace change events.
*/
public void addNotificationListener( final ModelWorkspaceNotificationListener listener ) {
CoreArgCheck.isNotNull(listener);
if (this.workspaceListeners.contains(listener)) return;
this.workspaceListeners.add(listener);
}
public boolean canReload( final ModelResource modelResource ) {
final Collection listeners = getModelResourceReloadVetoListeners(); // makes copy
if (listeners.size() != 0) {
// notify all listners about the notification
final Iterator iter = getModelResourceReloadVetoListeners().iterator(); // makes copy
while (iter.hasNext()) {
final ModelResourceReloadVetoListener listener = (ModelResourceReloadVetoListener)iter.next();
final boolean canReload = listener.canReload(modelResource);
if (!canReload) return false;
}
}
return true;
}
/**
* Change the worspace by adding or removing items from it.
*
* @param notification Notification for adding and removing items to workspace
* @throws CoreException
*/
private boolean changeWorkspace( final ModelWorkspaceNotification notification ) throws CoreException {
// get the added/removed/moved/renamed resource
final IResource resource = (IResource)notification.getNotifier();
// do nothing for non-model projects and resources in them
final IProject iProject = resource.getProject();
if (iProject.exists()) // Projects don't have model natures when closed ...
if (iProject.isOpen() && !ModelerCore.hasModelNature(iProject)) return false;
// do nothing for non-model
if (resource.getType() == IResource.FILE) {
if (!ModelUtil.isModelFile(resource, false) && !ModelUtil.isVdbArchiveFile(resource)) return false;
// set the workspace as not indexed for any model file change
this.setIndexType(ModelResource.NOT_INDEXED);
// non-project, non-folder resources
} else if (resource.getType() != IResource.PROJECT && resource.getType() != IResource.FOLDER) return false;
// get the existing resource item for this
ModelProjectImpl project = (ModelProjectImpl)this.modelWorkspace.findModelProject(resource);
final int eventType = ((ModelWorkspaceNotificationImpl)notification).getEventType();
switch (eventType) {
case Notification.ADD:
add(resource, project);
break;
case Notification.REMOVE:
try {
TransactionUtil.executeNonUndoable(new IOperation() {
@Override
public void execute() throws CoreException {
remove(resource);
}
}, this);
} catch (final CoreException err) {
throw err;
} catch (final Exception err) {
ModelerCore.Util.log(err);
}
break;
// remove the old item and create a new one
case Notification.MOVE:
break;
case Notification.SET:
final IPath oldPath = notification.getDelta().getMovedFromPath();
final ModelWorkspaceItem oldItem = this.modelWorkspace.getWorkspaceItem(oldPath, resource.getType());
if (oldItem != null && oldItem.getResource() != null) {
remove(oldItem.getResource());
add(resource, project);
}
break;
case ModelWorkspaceNotification.CHANGE:
// May be null:
if (resource.getType() == IResource.FILE) {
final ModelWorkspaceItem resourceItem = this.modelWorkspace.getWorkspaceItem(resource.getFullPath(),
resource.getType());
if (resourceItem != null && resourceItem instanceof ModelResourceImpl
&& !ModelUtil.isVdbArchiveFile(resourceItem.getCorrespondingResource())) {
final ModelResourceImpl model = (ModelResourceImpl)resourceItem;
return model.processContentsChange(notification);
}
}
break;
case ModelWorkspaceNotification.OPEN:
// when projects are opeaned and they are not part of the workspace yet,
// add them to the workspace as they are opeaned
if (resource instanceof IProject) {
if (project == null) {
project = new ModelProjectImpl((IProject)resource, this.modelWorkspace);
final ModelWorkspaceItemInfo parentInfo = (ModelWorkspaceItemInfo)getInfo(this.modelWorkspace);
if (parentInfo != null) parentInfo.addChild(project);
}
project.open(null);
}
break;
default:
throw new ModelerCoreException(
ModelerCore.Util.getString("ModelWorkspaceManager.Illegal_notification,_notification_type_not_recognized___1") + eventType); //$NON-NLS-1$
}
return false;
}
public void clearCache() {
this.cache.clear();
}
/**
* Deletes all index files associated with the specified <code>IResource</code>.
*
* @param theResource the resource whose indexes are being deleted
* @return <code>true</code> if all indexes have been successfully deleted; <code>false</code> otherwise.
* @throws CoreException if a problem occurs
* @since 5.0.1
*/
public boolean deleteIndexes( final IResource theResource ) throws CoreException {
// delete search indexes for closing project
return deleteIndexes(theResource, new SearchIndexResourceVisitor());
}
/**
* Deletes all index files associated with the specified <code>IResource</code>.
*
* @param theResource the resource whose indexes are being deleted
* @param theVisitor the visitor to use
* @return <code>true</code> if all indexes have been successfully deleted; <code>false</code> otherwise.
* @throws CoreException if a problem occurs
* @since 5.0.1
*/
public boolean deleteIndexes( final IResource theResource,
final SearchIndexResourceVisitor theVisitor ) throws CoreException {
theResource.accept(theVisitor);
final File[] indexFiles = theVisitor.getIndexFiles();
return IndexUtil.deleteIndexFiles(indexFiles, true);
}
/**
* Return the {@link ModelResource} that contains the opened {@link Resource EMF resource}.
*
* @param resource
* @return the ModelResource; null only if the resource is not known to the {@link ModelWorkspace}.
*/
public ModelResource findModelResource( final Resource resource ) {
CoreArgCheck.isNotNull(resource);
return ModelBufferManager.getDefaultBufferManager().getModelResource(resource);
}
/**
* Return the existing {@link ModelWorkspaceItem} that is at the supplied resource path.
*
* @param resourcePath the resource path at the given loacation
* @param resourceType The type of the resource
* @return the ModelResource; null only if the resource is not known to the {@link ModelWorkspace}.
*/
public ModelWorkspaceItem findModelWorkspaceItem( final IPath resourcePath,
final int resourceType ) {
CoreArgCheck.isNotNull(resourcePath);
return modelWorkspace.getWorkspaceItem(resourcePath, resourceType);
}
/**
* Return the existing {@link ModelWorkspaceItem} that represents the supplied resource.
*
* @param resource the resource
* @return the ModelResource; null only if the resource is not known to the {@link ModelWorkspace}.
*/
public ModelWorkspaceItem findModelWorkspaceItem( final IResource resource ) throws ModelWorkspaceException {
CoreArgCheck.isNotNull(resource);
if( resource.getProject().isOpen() ) {
return findModelWorkspaceItem(resource, false);
}
return null;
}
/**
* Return the existing {@link ModelWorkspaceItem} that represents the supplied resource.
*
* @param resource the resource
* @return the ModelResource; null only if the resource is not known to the {@link ModelWorkspace}.
*/
public ModelWorkspaceItem findModelWorkspaceItem( final IResource resource,
final boolean createIfRequired ) throws ModelWorkspaceException {
CoreArgCheck.isNotNull(resource);
// Get the project and the path of the model file relative to the project ...
final IProject proj = resource.getProject();
// check if this is a model project
if (!ModelerCore.hasModelNature(proj)) return null;
final IPath pathInProject = resource.getProjectRelativePath();
// Find the ModelProject
final ModelProject modelProject = this.getModelWorkspace().getModelProject(proj);
// Iterate over the segments, finding the corresponding model folder(s) and model resource
ModelWorkspaceItem parent = modelProject;
int numFolders = pathInProject.segmentCount(); // should be at least 1
if (resource instanceof IFile) {
// See if the file is a model ...
if (!ModelUtil.isModelFile(resource)) return null; // it's a non-model resource
--numFolders;
}
for (int i = 0; i < numFolders; ++i) {
final String folderName = pathInProject.segment(i);
final ModelWorkspaceItem child = parent.getChild(folderName);
if (child == null) {
// get the workspace resource
// i+1 is safe because i is always < numFolders, and uptoSegment takes a count,
// not a segment index or offset or anything.
final IFolder underlyingFolder = proj.getFolder(pathInProject.uptoSegment(i + 1));
CoreArgCheck.isNotNull(underlyingFolder);
final ModelFolder newFolder = new ModelFolderImpl(underlyingFolder, parent);
final Object parentInfo = ((ModelWorkspaceItemImpl)parent).getItemInfo();
if (parentInfo instanceof ModelFolderInfo) {
((ModelFolderInfo)parentInfo).addChild(newFolder);
parent = newFolder;
} else if (parentInfo instanceof ModelProjectInfo) {
((ModelProjectInfo)parentInfo).addChild(newFolder);
parent = newFolder;
}
} else parent = child;
}
if (resource instanceof IFile) {
// Get the ModelResource ...
ModelWorkspaceItem result = parent.getChild(resource);
if (result == null && createIfRequired) {
final String name = resource.getName();
result = new ModelResourceImpl(parent, name);
}
return result;
}
// else return the folder
return parent;
}
/**
* Fire ModelWorkspaceNotification, iteratively make all registered listeners aware of this notification.
*/
public void fire( final ModelWorkspaceNotification notification ) throws CoreException {
// System.out.println("\n[ModelWorkspaceManager.fire] TOP, notification is: " +
// ((ModelWorkspaceNotificationImpl)notification).getNotificationTypePhrase() );
CoreArgCheck.isNotNull(notification);
final IResourceDelta delta = notification.getDelta();
final int eventType = notification.getEventType();
// The delta is null upon project close events
boolean reloadedResource = false;
// change the workspace to add or remove items if needed
if (delta != null && notification.isPostChange()) reloadedResource = changeWorkspace(notification);
// process predelete notifications to cleanup indexes and resources
if (notification.isPreDelete()) reloadedResource = changeWorkspace(notification);
if (eventType == ModelWorkspaceNotification.CLOSING) // notifier should be a project but check to be sure
if (notification.isProject() && (notification.getNotifier() instanceof IProject)) // delete search indexes for closing
// project
deleteIndexes((IProject)notification.getNotifier());
// notify all listners about the notification
final Iterator listenIter = getNotificationListeners().iterator();
while (listenIter.hasNext()) {
final ModelWorkspaceNotificationListener listener = (ModelWorkspaceNotificationListener)listenIter.next();
switch (eventType) {
case Notification.ADD: {
listener.notifyAdd(notification);
}
break;
case Notification.REMOVE: {
listener.notifyRemove(notification);
}
break;
case Notification.MOVE: {
listener.notifyMove(notification);
}
break;
case Notification.SET: {
listener.notifyRename(notification);
}
break;
case ModelWorkspaceNotification.CHANGE: {
listener.notifyChanged(notification);
if (reloadedResource) {
final ModelWorkspaceNotification reloadedNotification = new ModelWorkspaceNotificationImpl(
ModelWorkspaceNotification.RELOADED,
notification.getDelta(),
((ModelWorkspaceNotificationImpl)notification).getChangeEvent());
listener.notifyReloaded(reloadedNotification);
}
}
break;
case ModelWorkspaceNotification.OPEN: {
listener.notifyOpen(notification);
}
break;
case ModelWorkspaceNotification.CLOSING: {
listener.notifyClosing(notification);
}
break;
default:
throw new ModelerCoreException(
ModelerCore.Util.getString("ModelWorkspaceManager.Illegal_notification,_notification_type_not_recognized___1") + eventType); //$NON-NLS-1$
}
}
}
@Override
public XMIHeader getCachedXmiHeader( final File resource ) {
final XMIHeaderCachedObject headerCachedObject = (XMIHeaderCachedObject)FileToxmlHeaderMap.get(resource.getAbsolutePath());
if (headerCachedObject != null) {
if (!headerCachedObject.isModified(resource)) return headerCachedObject.getXMIHeader();
FileToxmlHeaderMap.remove(resource.getAbsolutePath());
}
return null;
}
/**
* Return the reference to the resource change listener used by the ModelWorkspace manager for processing events.
*
* @return
*/
public DeltaProcessor getDeltaProcessor() {
return this.deltaProcessor;
}
/**
* Returns the {@link ModelWorkspaceItem} represented by the <code>String</code> memento.
*/
public ModelWorkspaceItem getHandleFromMemento( final String memento ) {
if (memento == null) return null;
final ModelWorkspace modelWorkspace = getModelWorkspace();
if (memento.equals("")) return modelWorkspace; //$NON-NLS-1$
return null;
}
public ModelWorkspaceIndexManager getIndexManager() {
return this.indexManager;
}
/**
* Returns the info for the element.
* @param item
* @return item info
*/
public ModelWorkspaceItemInfo getInfo( final ModelWorkspaceItem item ) {
return this.cache.getInfo(item);
}
/**
* Convenience method to get access the ModelMarkerManager
*
* @return
* @since 5.0
*/
public ModelMarkerManager getMarkerManager() {
return this.markerManager;
}
/**
* @return
*/
public Container getModelContainer() throws CoreException {
if (container == null) container = ModelerCore.getModelContainer();
return container;
}
/**
* Return the listeners
*/
Collection getModelResourceReloadVetoListeners() {
return new ArrayList(this.resourceReloadVetoListeners); // create a copy to prevent concurrent modifications
}
/**
* @return
*/
public ModelWorkspace getModelWorkspace() {
return this.modelWorkspace;
}
public ModelWorkspaceSearch getModelWorkspaceSearch() {
return this.modelWorkspaceSearch;
}
/**
* Find all {@link org.eclipse.core.resources.IResource}s whose {@link org.teiid.designer.core.workspace.ModelResource}s
* are not indexed to the given index type.
*
* @param indexType The indexType of the ModelResource.
* @return The collection of IResource objects
* @throws CoreException
*/
public Collection getNonIndexedResources( final int indexType ) throws CoreException {
class ResourceVisitor implements IResourceVisitor {
Collection resources = new ArrayList();
public Collection getResources() {
return resources;
}
@Override
public boolean visit( final IResource resource ) throws CoreException {
if (resource != null && resource.getType() == IResource.FILE) {
final ModelWorkspaceItem modelWorkspaceItem = findModelWorkspaceItem(resource);
if (modelWorkspaceItem != null && modelWorkspaceItem instanceof ModelResource) {
final ModelResource mResource = (ModelResource)modelWorkspaceItem;
final int modelIndexType = mResource.getIndexType();
switch (indexType) {
case ModelResource.INDEXED:
if (modelIndexType != ModelResource.INDEXED) resources.add(mResource.getResource());
break;
case ModelResource.METADATA_INDEXED:
if (modelIndexType != ModelResource.INDEXED && modelIndexType != ModelResource.METADATA_INDEXED) resources.add(mResource.getResource());
break;
case ModelResource.SEARCH_INDEXED:
if (modelIndexType != ModelResource.INDEXED && modelIndexType != ModelResource.SEARCH_INDEXED) resources.add(mResource.getResource());
break;
default:
break;
}
}
}
return true;
}
}
// If there are no resources that require indexing then return immediately
if (this.indexType == ModelResource.INDEXED || this.indexType == indexType) return Collections.EMPTY_LIST;
// assuming when some one gets non-indexed resources they would build them
setIndexType(indexType);
final ResourceVisitor visitor = new ResourceVisitor();
// collect all IResources for model files
final ModelProject[] projects = ModelerCore.getModelWorkspace().getModelProjects();
for (final ModelProject mProject : projects)
if (mProject != null && mProject.isOpen()) mProject.getProject().accept(visitor);
return visitor.getResources();
}
/**
* Return the listeners
*/
Collection getNotificationListeners() {
return new ArrayList(this.workspaceListeners); // create a copy to prevent concurrent modifications
}
private void initModelWorkspace( final IWorkspace workspace ) throws CoreException {
CoreArgCheck.isNotNull(workspace);
// collect all resources in open projects
final ModelIResourceCollectorVisitor resourceVisitor = new ModelIResourceCollectorVisitor();
workspace.getRoot().accept(resourceVisitor);
// sort the resources, in order (IRoot, IPRoject, IFolder and IFile)
final List resources = resourceVisitor.getResources();
final IResourceComparator comparator = new IResourceComparator();
Collections.sort(resources, comparator);
// create model workspaceitems for each of these resources
final Iterator resourceIter = resources.iterator();
while (resourceIter.hasNext()) {
final IResource resource = (IResource)resourceIter.next();
final ModelWorkspaceItem workspaceItem = create(resource, null);
if (resource.getType() == IResource.PROJECT || resource.getType() == IResource.ROOT) if (workspaceItem instanceof Openable) ((Openable)workspaceItem).open(null);
}
ModelUtil.setModelWorkspaceManagerInitialized();
}
/**
* Indicates if the resource is a model and is open.
*
* @param theResource the resource being checked
* @return <code>true</code>if a model and is open; <code>false</code> otherwise.
* @throws IllegalArgumentException if resource is <code>null</code>
* @since 4.2
*/
public boolean isModelOpen( final IResource theResource ) {
CoreArgCheck.isNotNull(theResource);
boolean result = false;
if (ModelUtil.isModelFile(theResource, true)) try {
final ModelWorkspaceItem item = findModelWorkspaceItem(theResource, false);
if (item != null) result = (getInfo(item) != null);
} catch (final ModelWorkspaceException theException) {
}
return result;
}
public boolean modelExists(String containerPath, String modelName) {
if (containerPath == null) {
return false;
}
IPath modelPath = new Path(containerPath).append(modelName);
if (!modelPath.toString().toLowerCase().endsWith(ResourceNameUtil.XMI_FILE_EXTENSION)) {
modelPath = modelPath.addFileExtension(ResourceNameUtil.XMI_FILE_EXTENSION);
}
ModelWorkspaceItem item = findModelWorkspaceItem(modelPath, IResource.FILE);
if (item != null) {
return true;
}
return false;
}
/**
* Notify all workspace listeners that a clean has been initiated on the provided project
*
* @param project the project on which the clean has been initiated.
*/
public void notifyClean( final IProject project ) {
final Collection listeners = getNotificationListeners();
final Iterator iter = listeners.iterator();
while (iter.hasNext()) {
final ModelWorkspaceNotificationListener listener = (ModelWorkspaceNotificationListener)iter.next();
listener.notifyClean(project);
}
}
/**
* Returns the info for this element without disturbing the cache ordering.
*/
// TODO: should be synchronized, could answer uninitialized info or if cache is in middle of rehash, could even answer
// distinct
// element info
protected Object peekAtInfo( final ModelWorkspaceItem item ) {
return this.cache.peekAtInfo(item);
}
/**
* @param key
* @param value
*/
public void putInfo( final ModelWorkspaceItem item,
final ModelWorkspaceItemInfo info ) {
this.cache.putInfo(item, info);
}
/**
* @param resource
* @throws CoreException
* @since 4.3
*/
void remove( final IResource resource ) throws CoreException {
// May be null:
final ModelWorkspaceItem resourceItem = this.modelWorkspace.getWorkspaceItem(resource.getFullPath(), resource.getType());
// clean up indexes and resources for all model children
removeResourcesRecursively(resource);
if (resourceItem instanceof Openable) ((Openable)resourceItem).close();
final ModelWorkspaceItem parentItem = this.modelWorkspace.getParent(resource);
if (parentItem != null) {
final ModelWorkspaceItemInfo parentInfo = (ModelWorkspaceItemInfo)getInfo(parentItem);
// Info will be null if the parentItem is not open... so just return
if (parentInfo != null) parentInfo.removeChild(resourceItem);
}
// parentItem will be null if the remove event for the parent is processed first
}
/**
* Remove all listeners that would listen to and possibly veto the reloading of a ModelResource from the underlying file.
*/
public void removeAllModelResourceReloadVetoListeners() {
this.resourceReloadVetoListeners.clear();
}
/**
* Remove a listener that would listen to the workspace change events.
*/
public void removeAllNotificationListeners() {
this.workspaceListeners.clear();
}
/**
* @param item
*/
public void removeInfo( final ModelWorkspaceItem item ) {
this.cache.removeInfo(item);
}
/**
* Remove a listener that would listen to and possibly veto the reloading of a ModelResource from the underlying file.
*/
public void removeModelResourceReloadVetoListener( final ModelResourceReloadVetoListener listener ) {
CoreArgCheck.isNotNull(listener);
this.resourceReloadVetoListeners.remove(listener);
}
/**
* Remove a listener that would listen to the workspace change events.
*/
public void removeNotificationListener( final ModelWorkspaceNotificationListener listener ) {
this.workspaceListeners.remove(listener);
}
/**
* Navigate the given IResource, find children that are models and clean up their index files and emf resources.
*
* @param resource The IResource to navigate
* @throws CoreException
* @since 4.2
*/
private void removeResourcesRecursively( final IResource resource ) throws CoreException {
CoreArgCheck.isNotNull(resource);
// type of resource
final int resourceType = resource.getType();
// Per defect 10957, resourceItem may be null, so can't use it to clean up index files
if (resource.getLocation() != null && resourceType == IResource.FILE) {
// Remove the runtime index file associated with the resource being removed
final String runtimeIndexFileName = IndexUtil.getRuntimeIndexFileName(resource);
final File runtimeIndexFile = new File(IndexUtil.INDEX_PATH, runtimeIndexFileName);
if (runtimeIndexFile.exists()) getIndexManager().disposeIndex(runtimeIndexFileName);
// Remove the search index file associated with the resource being removed
final String searchIndexFileName = IndexUtil.getSearchIndexFileName(resource);
final File searchIndexFile = new File(IndexUtil.INDEX_PATH, searchIndexFileName);
if (searchIndexFile.exists()) getIndexManager().disposeIndex(searchIndexFileName);
// Remove the underlying Emf resource
final ModelWorkspaceItem resourceItem = this.modelWorkspace.getWorkspaceItem(resource.getFullPath(), IResource.FILE);
if (resourceItem != null && resourceItem instanceof ModelResourceImpl) ((ModelResourceImpl)resourceItem).removeEmfResource();
// remove XMIHeader from cache
this.FileToxmlHeaderMap.remove(ModelUtil.getLocation(resource).toOSString());
}
if (resource.exists() && resource.isAccessible() && resourceType != IResource.FILE) {
class ResourceVisitor implements IResourceVisitor {
List resources = new ArrayList();
public List getFileResources() {
return resources;
}
private boolean isIncludedResource( final IResource resource ) {
if (resource == null || !resource.exists()) return false;
if (ModelUtil.isModelFile(resource) || ModelUtil.isXsdFile(resource) || ModelUtil.isVdbArchiveFile(resource)) return true;
return false;
}
@Override
public boolean visit( final IResource resource ) {
if (isIncludedResource(resource)) resources.add(resource);
return true;
}
}
final ResourceVisitor visitor = new ResourceVisitor();
// collect all IResources for model files
resource.accept(visitor);
// for each model clean up index files and emf resoures
final Collection childResources = visitor.getFileResources();
for (final Iterator rscIter = childResources.iterator(); rscIter.hasNext();) {
final IResource child = (IResource)rscIter.next();
removeResourcesRecursively(child);
}
}
}
/**
* Set the type indicating one or more ModelResources in the workspace have been indexed to the given type.
*/
void setIndexType( final int type ) {
this.indexType = type;
}
@Override
public void setXmiHeaderToCache( final File resource,
final XMIHeader header ) {
final XMIHeaderCachedObject headerCachedObject = new XMIHeaderCachedObject(header, resource.lastModified());
FileToxmlHeaderMap.put(resource.getAbsolutePath(), headerCachedObject);
}
/**
* This method can be called to release all resources. Currently, this simply shuts down the {@link #getModelContainer() model
* container}, which can be reinitialized after this method is called with the {@link #getModelContainer()} method.
*
* @throws CoreException
*/
private void shutdownManager() throws CoreException {
if (container != null) try {
if (ModelerCore.DEBUG_MODEL_WORKSPACE) ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("ModelWorkspaceManager.DEBUG.Shutting_down_model_container")); //$NON-NLS-1$
container.shutdown();
if (ModelerCore.DEBUG_MODEL_WORKSPACE) ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("ModelWorkspaceManager.DEBUG.Completed_shuting_down_model_container")); //$NON-NLS-1$
} catch (final ModelerCoreException e) {
throw new CoreException(
new Status(
IStatus.ERROR,
ModelerCore.PLUGIN_ID,
1,
ModelerCore.Util.getString("ModelWorkspaceManager.Error_shutting_down_the_model_container_2"), e)); //$NON-NLS-1$
} finally {
container = null;
}
if (workspaceListeners != null) workspaceListeners.clear();
/*
* Need to tell the markerManager to dispose so it's cache of eObject/Marker maps can be cleand up.
*/
if (markerManager != null) markerManager.dispose();
}
/**
* Compare IResources in a collection and order them as IRoot, IProject, IFolder and IFile.
*/
private class IResourceComparator implements Comparator {
public IResourceComparator() {
super();
}
@Override
public int compare( final Object rsc1,
final Object rsc2 ) {
CoreArgCheck.isInstanceOf(IResource.class, rsc1);
CoreArgCheck.isInstanceOf(IResource.class, rsc2);
final IResource resource1 = (IResource)rsc1;
final IResource resource2 = (IResource)rsc2;
return resource2.getType() - resource1.getType();
}
@Override
public boolean equals( final Object anObject ) {
if (this == anObject) return true;
if (anObject == this) return true;
if (anObject == null || anObject.getClass() != this.getClass()) return false;
return true;
}
}
/**
* Visitor that collects IResources for which ModelResources need to be created, these include IProjects, IFolders and
* IFiles(model, XSD and VDB files.)
*/
class ModelIResourceCollectorVisitor implements IResourceVisitor {
List resources = new ArrayList();
public List getResources() {
return resources;
}
private boolean isIncludedResource( final IResource resource ) {
if (resource == null || !resource.exists()) return false;
if (resource.getType() == IResource.PROJECT) return ModelerCore.hasModelNature((IProject)resource);
else if ((resource.getType() == IResource.FILE)) {
if (ModelUtil.isModelFile(resource) || ModelUtil.isXsdFile(resource) || ModelUtil.isVdbArchiveFile(resource)) return true;
return false;
}
return true;
}
@Override
public boolean visit( final IResource resource ) {
if (isIncludedResource(resource)) {
resources.add(resource);
if (resource.getType() != IResource.FILE) return true;
}
return false;
}
}
class NotificationProcessor implements INotifyChangedListener {
private void checkResourceForIndexing( final Notification notification ) {
final Object target = notification.getNotifier();
if (notification.isTouch()) return;
switch (notification.getEventType()) {
case Notification.ADD:
case Notification.ADD_MANY:
case Notification.REMOVE:
case Notification.REMOVE_MANY:
case Notification.SET:
case Notification.UNSET:
case Notification.MOVE:
refreshResourceIndexType(target);
break;
default:
// do nothing
}
}
@Override
public void notifyChanged( final Notification notification ) {
// If the notification is just a touch, don't do anything.
if (notification.isTouch()) return;
if (notification instanceof SourcedNotification) {
final Collection chain = ((SourcedNotification)notification).getNotifications();
for (final Iterator iter = chain.iterator(); iter.hasNext();) {
final Notification n = (Notification)iter.next();
checkResourceForIndexing(n);
}
} else checkResourceForIndexing(notification);
}
private void refreshResourceIndexType( final Object obj ) {
if (obj == null) return;
if (obj instanceof Resource && ((Resource)obj).isModified()) {
final ModelResource mResource = ModelWorkspaceManager.this.findModelResource((Resource)obj);
if (mResource != null) {
if (mResource.getIndexType() == ModelResource.NOT_INDEXED) return;
mResource.refreshIndexType();
ModelWorkspaceManager.this.setIndexType(ModelResource.NOT_INDEXED);
}
} else if (obj instanceof EObject && ((EObject)obj).eResource() != null) {
final Resource eResource = ((EObject)obj).eResource();
if (eResource.isModified()) {
final ModelResource mResource = ModelWorkspaceManager.this.findModelResource(eResource);
if (mResource != null) {
if (mResource.getIndexType() == ModelResource.NOT_INDEXED) return;
mResource.setIndexType(ModelResource.NOT_INDEXED);
ModelWorkspaceManager.this.setIndexType(ModelResource.NOT_INDEXED);
}
}
}
}
}
private class XMIHeaderCachedObject {
private final XMIHeader header;
private final long lastModified;
XMIHeaderCachedObject( final XMIHeader header,
final long lastModified ) {
this.header = header;
this.lastModified = lastModified;
}
XMIHeader getXMIHeader() {
return header;
}
boolean isModified( final File file ) {
return file.lastModified() != this.lastModified;
}
}
}