package org.eclipse.jst.jsf.designtime.internal.resources; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.jst.jsf.common.internal.componentcore.AbstractVirtualComponentQuery; import org.eclipse.jst.jsf.common.internal.finder.AbstractMatcher.AlwaysMatcher; import org.eclipse.jst.jsf.common.internal.finder.VisitorMatcher; import org.eclipse.jst.jsf.common.internal.finder.acceptor.FileMatchingAcceptor; import org.eclipse.jst.jsf.common.internal.resource.ContentTypeResolver; import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent; import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.ReasonType; import org.eclipse.jst.jsf.common.internal.resource.ResourceManager; import org.eclipse.jst.jsf.common.internal.resource.ResourceTracker; import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; import org.eclipse.jst.jsf.designtime.internal.resources.JSFResourceChangeListener.JSFResourceChangedEvent; import org.eclipse.jst.jsf.designtime.internal.resources.JSFResourceChangeListener.JSFResourceChangedEvent.CHANGE_TYPE; import org.eclipse.jst.jsf.designtime.internal.resources.ResourceIdentifierFactory.InvalidIdentifierException; import org.eclipse.wst.common.componentcore.resources.IVirtualFolder; /** * Resource Manager that tracks JSF resources in a workspace. * * @author cbateman * */ public class WorkspaceResourceManager extends ResourceManager<IResource> { private final AbstractVirtualComponentQuery _vcQuery; private final IProject _project; private final AbstractJSFResourceLocator _locator; private final ContentTypeResolver _contentTypeResolver; private final ResourceIdentifierFactory _factory; /** * @param project * @param vcQuery * @param locator * @param contentTypeResolver */ public WorkspaceResourceManager(final IProject project, final AbstractVirtualComponentQuery vcQuery, final AbstractJSFResourceLocator locator, final ContentTypeResolver contentTypeResolver) { super(project.getWorkspace()); _project = project; _vcQuery = vcQuery; _locator = locator; _contentTypeResolver = contentTypeResolver; _factory = new ResourceIdentifierFactory(); } @Override protected JSFResourceTracker createNewInstance(final IResource resource) { final boolean isJSFResource = !getRootResourceFolder().getParent() .equals(resource); return new JSFResourceTracker(resource, isJSFResource); } @Override public void initResources() { final IFolder folder = getRootResourceFolder(); if (folder != null) { try { track(folder.getParent()); track(folder); } catch (final Exception e1) { JSFCorePlugin .log(e1, "While trying to locate JSF resources in the workspace"); //$NON-NLS-1$ } if (folder.isAccessible()) { trackAllInFolder(folder); } } } private List<IWorkspaceJSFResourceFragment> trackAllInFolder( final IContainer folder) { final VisitorMatcher<IContainer, IResource, String> matcher = new VisitorMatcher<IContainer, IResource, String>( "", "", //$NON-NLS-1$ //$NON-NLS-2$ new FileMatchingAcceptor(), Collections.singletonList(new AlwaysMatcher())); final List<IWorkspaceJSFResourceFragment> newFragments = new ArrayList<IWorkspaceJSFResourceFragment>(); try { final Collection<? extends IResource> foundResources = matcher .find(folder); for (final IResource res : foundResources) { try { newFragments.add(track(res)); } catch (final InvalidIdentifierException e) { // Bug 377405: this call can get expensive for a project with many things on the Facelet classpath // and all it does is remote informational logging. // JSFCorePlugin.log(IStatus.INFO, // "Ignoring invalid JSF resource: "+res); //$NON-NLS-1$ } catch (final ManagedObjectException e) { JSFCorePlugin.log(e, "Problem adding managed object for resource: "+res); //$NON-NLS-1$ } } } catch (final Exception e) { JSFCorePlugin.log(e, "While trying to locate JSF resources in the workspace"); //$NON-NLS-1$ } return newFragments; } private IWorkspaceJSFResourceFragment track(final IResource res) throws ManagedObjectException, InvalidIdentifierException { final IPath fullPath = res.getFullPath().makeRelativeTo( getRootResourceFolder().getFullPath()); // cause the resource to get tracked final JSFResourceTracker tracker = (JSFResourceTracker) getInstance(res); IWorkspaceJSFResourceFragment jsfRes = null; if (res.getType() == IResource.FILE) { try { jsfRes = new WorkspaceJSFResource( _factory.createLibraryResource(fullPath.toString()), res, _contentTypeResolver); } catch (InvalidIdentifierException e) { // ensure that the tracker gets released before we throw the exception. unmanageResource(res); tracker.dispose(); throw e; } } else { jsfRes = new WorkspaceJSFResourceContainer( _factory.createLibraryFragment(fullPath.toString()), (IContainer) res); } tracker.setJsfResource(jsfRes); addLifecycleEventListener(tracker); return jsfRes; } /** * @return the root folder for resources in the workspace. */ public IFolder getRootResourceFolder() { final IVirtualFolder webContentFolder = _vcQuery .getWebContentFolder(_project); if (webContentFolder != null && webContentFolder.getUnderlyingFolder().isAccessible()) { return webContentFolder.getUnderlyingFolder().getFolder( new Path("resources")); //$NON-NLS-1$ } return null; } @Override public List<IResource> getResources() { return new ArrayList(getManagedResources()); } /** * @return the jsf resource currently known. List is a copy but the * contained JSFResource references are not. */ public List<IJSFResourceFragment> getJSFResources() { final List<IJSFResourceFragment> jsfResources = new ArrayList<IJSFResourceFragment>(); final Map<IResource, ManagedResourceObject<ResourceTracker<IResource>>> jsfResourceTrackers = getPerResourceObjects(); for (final Map.Entry<IResource, ManagedResourceObject<ResourceTracker<IResource>>> entry : jsfResourceTrackers .entrySet()) { final JSFResourceTracker jsfResourceTracker = (JSFResourceTracker) entry .getValue().getManagedObject(); if (jsfResourceTracker.isJSFResource()) { jsfResources.add(jsfResourceTracker.getJsfResource()); } } return jsfResources; } private class JSFResourceTracker extends ResourceTracker<IResource> { private IWorkspaceJSFResourceFragment _jsfResource; private final boolean _isJSFResource; /** * @param resource * @param isJSFResource * if false, indicates that the resource being tracked is * related to JSF resources but not an actual JSF resource * (i.e. web root). */ public JSFResourceTracker(final IResource resource, final boolean isJSFResource) { super(resource); _isJSFResource = isJSFResource; } public boolean isJSFResource() { return _isJSFResource; } @Override protected void fireResourceInAccessible( final IResource affectedResource, final ReasonType reasonType) { if (reasonType == ReasonType.RESOURCE_DELETED_FROM_CONTAINER || reasonType == ReasonType.RESOURCE_MOVED_CONTAINER) { try { final List<JSFResourceTracker> trackers = getTrackers(affectedResource); for (final JSFResourceTracker tracker : trackers) { final IWorkspaceJSFResourceFragment jsfResource = tracker .getJsfResource(); removeLifecycleEventListener(tracker); _locator.fireChangeEvent(new JSFResourceChangedEvent( _locator, jsfResource, null, CHANGE_TYPE.REMOVED)); } } catch (final ManagedObjectException e) { JSFCorePlugin.log(e, "Processing an inaccessible resource event"); //$NON-NLS-1$ } } } private List<JSFResourceTracker> getTrackers(final IResource res) throws ManagedObjectException { final JSFResourceTracker tracker = (JSFResourceTracker) getInstance(res); final List<JSFResourceTracker> allChildren = new ArrayList<JSFResourceTracker>(); allChildren.add(tracker); if (res instanceof IContainer) { final IPath parentPath = res.getFullPath(); for (final Entry<IResource, ManagedResourceObject<ResourceTracker<IResource>>> trackerEntry : getPerResourceObjects() .entrySet()) { final IPath trackerPath = trackerEntry.getKey() .getFullPath(); if (parentPath.isPrefixOf(trackerPath) && !parentPath.equals(trackerPath)) { allChildren.add((JSFResourceTracker) trackerEntry .getValue().getManagedObject()); } } } return allChildren; } @Override protected void fireResourceChanged(final IResource affectedResource, final ReasonType reasonType) { _locator.fireChangeEvent(new JSFResourceChangedEvent(_locator, _jsfResource, _jsfResource, CHANGE_TYPE.CHANGED)); } @Override protected void fireResourceAdded(final IResource affectedResource, final ReasonType reasonType) { if (reasonType == ReasonType.RESOURCE_ADDED_TO_CONTAINER || reasonType == ReasonType.RESOURCE_MOVED_CONTAINER) { final IContainer parent = affectedResource.getParent(); if (parent != null && parent.isAccessible()) { try { final IWorkspaceJSFResourceFragment newJsfRes = track(affectedResource); fireNewJSFResourceEvent(newJsfRes); if (reasonType == ReasonType.RESOURCE_MOVED_CONTAINER && affectedResource instanceof IContainer) { final List<IWorkspaceJSFResourceFragment> newFragments = trackAllInFolder((IContainer) affectedResource); for (final IWorkspaceJSFResourceFragment frag : newFragments) { fireNewJSFResourceEvent(frag); } } } catch (final ManagedObjectException e) { JSFCorePlugin .log(e, "While adding new resource " + affectedResource); //$NON-NLS-1$ } catch (final InvalidIdentifierException e) { JSFCorePlugin .log(IStatus.INFO, "While adding new resource " + affectedResource); //$NON-NLS-1$ } } } } private void fireNewJSFResourceEvent( final IWorkspaceJSFResourceFragment newJsfRes) { final JSFResourceChangedEvent event = new JSFResourceChangedEvent( _locator, null, newJsfRes, CHANGE_TYPE.ADDED); _locator.fireChangeEvent(event); } @Override protected boolean isInteresting(final ResourceLifecycleEvent event) { boolean isInteresting = false; final ReasonType reasonType = event.getReasonType(); switch (event.getEventType()) { case RESOURCE_ADDED: { final IResource resource = getResource(); isInteresting = (reasonType == ReasonType.RESOURCE_ADDED_TO_CONTAINER || reasonType == ReasonType.RESOURCE_MOVED_CONTAINER) && event.getAffectedResource().getParent() .equals(resource); // ignore if my resource is web content and this is trying // to add something other than resources folder if (resource.equals(_vcQuery.getWebContentFolder(_project) .getUnderlyingFolder())) { isInteresting &= event.getAffectedResource().equals( getRootResourceFolder()); } } break; case RESOURCE_INACCESSIBLE: { final IResource resource = getResource(); // if the resource made inaccessible was deleted or moved // from its container // and it's parent is me, then it is interesting. isInteresting = (reasonType == ReasonType.RESOURCE_DELETED_FROM_CONTAINER || reasonType == ReasonType.RESOURCE_MOVED_CONTAINER) && event.getAffectedResource().getParent() .equals(resource); // ignore if my resource is web content and this is trying // to add something other than resources folder if (resource.equals(_vcQuery.getWebContentFolder(_project) .getUnderlyingFolder())) { isInteresting &= event.getAffectedResource().equals( getRootResourceFolder()); } // isInteresting &= (_isJSFResource || (resource.getType() == IResource.FOLDER && "resources".equals(resource.getName()))); //$NON-NLS-1$ // or if the resource being delted or moved is the root // folder and that is my resource. // isInteresting |= (reasonType == // ReasonType.RESOURCE_DELETED || reasonType == // ReasonType.RESOURCE_MOVED_CONTAINER) // && (resource.equals(event.getAffectedResource()) && // resource.equals(getRootResourceFolder())); } break; default: isInteresting = super.isInteresting(event); } return isInteresting; } public final IWorkspaceJSFResourceFragment getJsfResource() { return _jsfResource; } public final void setJsfResource( final IWorkspaceJSFResourceFragment jsfResource) { _jsfResource = jsfResource; } } }