| /******************************************************************************* |
| * Copyright (c) 2001, 2008 Oracle Corporation and others. |
| * 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 Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.jsf.designtime.internal.view; |
| |
| import java.io.Serializable; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| 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.IWorkspace; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jst.jsf.common.internal.JSPUtil; |
| import org.eclipse.jst.jsf.common.internal.resource.IResourceLifecycleListener; |
| import org.eclipse.jst.jsf.common.internal.resource.ImmutableLifecycleListener; |
| import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent; |
| import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.ReasonType; |
| import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; |
| import org.eclipse.jst.jsf.designtime.context.DTFacesContext; |
| import org.eclipse.jst.jsf.designtime.internal.view.DTUIViewRoot.StalenessAdvisor; |
| import org.eclipse.jst.jsf.designtime.internal.view.DTUIViewRoot.StalenessEvent; |
| import org.eclipse.jst.jsf.designtime.internal.view.DTUIViewRoot.StalenessListener; |
| import org.eclipse.jst.jsf.designtime.internal.view.DTUIViewRoot.VersionStamp; |
| import org.eclipse.jst.jsf.designtime.internal.view.DTUIViewRoot.StalenessEvent.ChangeType; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry; |
| import org.w3c.dom.Node; |
| |
| /** |
| * A default implementation of the design time view handler meant to parallel |
| * the default runtime ViewHandler required by the JSF spec. |
| * |
| */ |
| public class DefaultDTViewHandler extends AbstractDTViewHandler |
| { |
| private final MyLifecycleManager _lifecycleManager = new MyLifecycleManager(); |
| |
| @Override |
| public String calculateLocale(final DTFacesContext context) |
| throws ViewHandlerException |
| { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public IResource getActionDefinition(final DTFacesContext context, |
| final String viewId) throws ViewHandlerException |
| { |
| // TODO: this seems like a bit of a cop out... |
| return context.adaptContextObject(); |
| } |
| |
| @Override |
| public IPath getActionURL(final DTFacesContext context, |
| final IResource resource, final IPath requestPath) |
| throws ViewHandlerException |
| { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public IPath getRelativeActionPath(final DTFacesContext context, |
| final String relativeToViewId, final String uri) |
| throws ViewHandlerException |
| { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public IViewDefnAdapterFactory getViewMetadataAdapterFactory( |
| final DTFacesContext context) throws ViewHandlerException |
| { |
| final IResource res = context.adaptContextObject(); |
| |
| if (res instanceof IFile) |
| { |
| return new DefaultViewDefnAdapterFactory(this); |
| } |
| |
| return null; |
| } |
| |
| @Override |
| protected VersionStamp createVersionStamp( |
| final DTFacesContext facesContext, final String viewId) |
| { |
| return new TimeBasedVersionStamp(); |
| } |
| |
| static class DefaultViewDefnAdapterFactory extends |
| AbstractViewDefnAdapterFactory |
| { |
| private final DefaultDTViewHandler _myViewHandler; |
| |
| DefaultViewDefnAdapterFactory(final DefaultDTViewHandler viewHandler) |
| { |
| _myViewHandler = viewHandler; |
| } |
| |
| @Override |
| public IViewDefnAdapter<Node, IDocument> createAdapter( |
| final DTFacesContext context, final String viewId) |
| { |
| try |
| { |
| final IResource res = _myViewHandler.getActionDefinition( |
| context, viewId); |
| |
| if (res instanceof IFile) |
| { |
| final IFile srcFile = (IFile) res; |
| final ITagRegistry registry = findTagRegistry(srcFile); |
| if (JSPUtil.isJSPContentType(srcFile) && registry != null) |
| { |
| // if we have a jsp file, then return the default |
| // adapter |
| return new JSPViewDefnAdapter(registry); |
| } |
| } |
| } |
| catch (final ViewHandlerException vhe) |
| { |
| JSFCorePlugin.log(vhe, "While acquiring view adapter"); |
| } |
| |
| // not found or failed |
| return null; |
| } |
| } |
| |
| @Override |
| protected DTUIViewRoot internalCreateView( |
| final DTFacesContext facesContext, final String viewId) |
| { |
| IViewDefnAdapterFactory factory; |
| try |
| { |
| factory = getViewMetadataAdapterFactory(facesContext); |
| if (factory != null) |
| { |
| final IViewDefnAdapter<?, ?> adapter = factory.createAdapter( |
| facesContext, viewId); |
| if (adapter instanceof XMLViewDefnAdapter) |
| { |
| final IResource res = facesContext.adaptContextObject(); |
| final DTUIViewRoot root = newView(facesContext, viewId); |
| final XMLComponentTreeConstructionStrategy constructionStrategy = createTreeConstructionStrategy( |
| (XMLViewDefnAdapter) adapter, res.getProject()); |
| |
| constructionStrategy |
| .createComponentTree(facesContext, root); |
| return root; |
| } |
| JSFCorePlugin |
| .log( |
| IStatus.WARNING, |
| String |
| .format( |
| "Could not get view adapter to construct design time view root for %s", |
| viewId)); |
| } |
| else |
| { |
| JSFCorePlugin |
| .log( |
| IStatus.WARNING, |
| String |
| .format( |
| "Could not get view adapter factory toconstruct design time view root for %s", |
| viewId)); |
| } |
| } |
| catch (final ViewHandlerException e) |
| { |
| JSFCorePlugin.log(e, "While acquiring view defn adapter factory"); |
| // fall-through |
| } |
| |
| return new NullViewRoot(); |
| } |
| |
| /** |
| * By default, returns DefaultDTUIViewRoot. |
| * |
| * @param facesContext |
| * @param viewId |
| * @return a new instance of a view. Override to change the implementation |
| * or configuration of DTUIViewRoot that is used by |
| * internalCreateView. |
| */ |
| protected DTUIViewRoot newView(final DTFacesContext facesContext, |
| final String viewId) |
| { |
| return new DefaultDTUIViewRoot(); |
| } |
| |
| @Override |
| protected void registerView(final DTUIViewRoot viewRoot, |
| final DTFacesContext facesContext, final String viewId) |
| { |
| final IResource res = facesContext.adaptContextObject(); |
| _lifecycleManager.addViewInfo(viewId, res); |
| } |
| |
| /** |
| * Sub-classes may override to provide a different strategy. |
| * |
| * @param adapter |
| * @param project |
| * @return the construction strategy used to create this view's component |
| * tree |
| */ |
| protected XMLComponentTreeConstructionStrategy createTreeConstructionStrategy( |
| final XMLViewDefnAdapter adapter, final IProject project) |
| { |
| return new XMLComponentTreeConstructionStrategy(adapter, project); |
| } |
| |
| @Override |
| public boolean supportsViewDefinition(final IFile file) |
| { |
| // currently only JSP content type is supported |
| return (JSPUtil.isJSPContentType(file)); |
| } |
| |
| @Override |
| protected StalenessAdvisor createStalenessAdvisor( |
| final DTUIViewRoot viewRoot, final DTFacesContext facesContext, |
| final String viewId) |
| { |
| final IResource res = facesContext.adaptContextObject(); |
| // if the view root is null or the res is null fall through |
| // and use the null staleness advisor |
| if (!(viewRoot instanceof NullViewRoot) && res != null) |
| { |
| return new ResourceModStampStalenessAdvisor(viewRoot, res, viewId); |
| } |
| return new NullStalenessAdvisor(); |
| } |
| |
| /** |
| * Measures the staleness of a view by comparing the modification stamp on |
| * the resource at construction with the current value when isStale is |
| * called. |
| * |
| * @author cbateman |
| * |
| */ |
| protected final class ResourceModStampStalenessAdvisor extends |
| StalenessAdvisor implements Serializable |
| { |
| /** |
| * version id |
| */ |
| private static final long serialVersionUID = -4982206388722638735L; |
| private final long _modificationStamp; |
| |
| private transient final IResource _res; |
| private transient final AtomicBoolean _forcedStale; |
| private transient final StalenessListener _myListener; |
| |
| /** |
| * @param viewRoot |
| * @param file |
| * @param viewId |
| */ |
| public ResourceModStampStalenessAdvisor(final DTUIViewRoot viewRoot, |
| final IResource file, final String viewId) |
| { |
| _res = file; |
| _modificationStamp = file.getModificationStamp(); |
| _forcedStale = new AtomicBoolean(false); |
| _myListener = new StalenessListener() |
| { |
| @Override |
| protected void stalenessChanged(final StalenessEvent event) |
| { |
| if (event.getChangeType() == ChangeType.PROJECT_CLEANED) |
| { |
| if (_forcedStale.compareAndSet(false, true)) |
| { |
| _lifecycleManager.removeListener(_res, _myListener); |
| } |
| } |
| } |
| }; |
| _lifecycleManager.addViewInfo(viewId, _res); |
| _lifecycleManager.addListener(_res, _myListener); |
| } |
| |
| @Override |
| public boolean isStale() |
| { |
| if (!_forcedStale.get()) |
| { |
| final long curStamp = _res.getModificationStamp(); |
| return curStamp != _modificationStamp; |
| } |
| // else forced stale |
| return true; |
| } |
| |
| @Override |
| public void addListener(final StalenessListener listener) |
| { |
| _lifecycleManager.addListener(_res, listener); |
| } |
| |
| @Override |
| public void removeListener(final StalenessListener listener) |
| { |
| _lifecycleManager.removeListener(_res, listener); |
| } |
| |
| @Override |
| public boolean isAccessible() |
| { |
| return _res.isAccessible(); |
| } |
| } |
| |
| @Override |
| public final void setLifecycleListener(final ImmutableLifecycleListener listener) |
| { |
| _lifecycleManager.update(listener); |
| } |
| |
| @Override |
| protected final void doDispose() |
| { |
| _lifecycleManager.dispose(); |
| } |
| |
| private class MyLifecycleManager implements IResourceLifecycleListener |
| { |
| private ImmutableLifecycleListener _listener; |
| private final Map<IResource, ViewInfo> _stalenessListeners; |
| private final IResourceChangeListener _buildListener; |
| |
| public MyLifecycleManager() |
| { |
| _stalenessListeners = new HashMap<IResource, ViewInfo>(); |
| _buildListener = new IResourceChangeListener() |
| { |
| // on a clean build request, fire staleness for all project-related |
| // resources. |
| public void resourceChanged(final IResourceChangeEvent event) |
| { |
| if (event.getType() == IResourceChangeEvent.PRE_BUILD) |
| { |
| if (event.getBuildKind() == IncrementalProjectBuilder.CLEAN_BUILD) |
| { |
| if (event.getSource() instanceof IProject) |
| { |
| cleanProject((IProject) event.getSource()); |
| } |
| else if (event.getSource() instanceof IWorkspace) |
| { |
| cleanAll(); |
| } |
| } |
| } |
| } |
| }; |
| ResourcesPlugin.getWorkspace().addResourceChangeListener(_buildListener, |
| IResourceChangeEvent.PRE_BUILD); |
| } |
| |
| public void addListener(final IResource res, final StalenessListener listener) |
| { |
| final ViewInfo viewInfo = getViewInfo(res); |
| viewInfo.getListeners().addIfAbsent(listener); |
| } |
| |
| public void removeListener(final IResource res, final StalenessListener listener) |
| { |
| final ViewInfo viewInfo = getViewInfo(res); |
| viewInfo.getListeners().remove(listener); |
| } |
| |
| public EventResult acceptEvent(final ResourceLifecycleEvent event) |
| { |
| switch (event.getEventType()) |
| { |
| case RESOURCE_CHANGED: |
| { |
| return handleContentChangeEvent(event); |
| } |
| |
| case RESOURCE_INACCESSIBLE: |
| { |
| return handleInaccessibleChangeEvent(event); |
| } |
| |
| default: |
| // do nothing with other types |
| } |
| |
| return EventResult.getDefaultEventResult(); |
| } |
| |
| private EventResult handleContentChangeEvent( |
| final ResourceLifecycleEvent event) |
| { |
| if (event.getReasonType() != ReasonType.RESOURCE_CHANGED_CONTENTS) |
| { |
| return EventResult.getDefaultEventResult(); |
| } |
| |
| final IResource res = event.getAffectedResource(); |
| final List<StalenessListener> stalenessListeners = getListeners(res); |
| |
| if (stalenessListeners != null) |
| { |
| for (final StalenessListener listener : stalenessListeners) |
| { |
| listener.stalenessChanged(new StalenessEvent( |
| ChangeType.VIEW_DEFN_CHANGED)); |
| } |
| } |
| return EventResult.getDefaultEventResult(); |
| } |
| |
| private EventResult handleInaccessibleChangeEvent( |
| final ResourceLifecycleEvent event) |
| { |
| final IResource res = event.getAffectedResource(); |
| final ReasonType reasonType = event.getReasonType(); |
| ChangeType changeType = null; |
| if (reasonType == ReasonType.RESOURCE_PROJECT_CLOSED) |
| { |
| changeType = ChangeType.VIEW_DEFN_PROJECT_CLOSED; |
| } |
| else if (reasonType == ReasonType.RESOURCE_DELETED) |
| { |
| changeType = ChangeType.VIEW_DEFN_DELETED; |
| } |
| else |
| { |
| return EventResult.getDefaultEventResult(); |
| } |
| |
| final List<StalenessListener> listeners = getListeners(res); |
| |
| if (listeners != null) |
| { |
| for (final StalenessListener listener : listeners) |
| { |
| listener.stalenessChanged(new StalenessEvent( |
| changeType)); |
| } |
| } |
| return EventResult.getDefaultEventResult(); |
| } |
| |
| private void cleanAll() |
| { |
| final StalenessEvent event = new StalenessEvent(ChangeType.PROJECT_CLEANED); |
| for (final Map.Entry<IResource, ViewInfo> entry : _stalenessListeners.entrySet()) |
| { |
| final ViewInfo info = entry.getValue(); |
| for (final StalenessListener listener : info.getListeners()) |
| { |
| listener.stalenessChanged(event); |
| } |
| } |
| } |
| |
| private void cleanProject(final IProject project) |
| { |
| final StalenessEvent event = new StalenessEvent(ChangeType.PROJECT_CLEANED); |
| for (final Map.Entry<IResource, ViewInfo> entry : _stalenessListeners.entrySet()) |
| { |
| final IResource res = entry.getKey(); |
| |
| if (res.getProject().equals(project)) |
| { |
| final ViewInfo info = entry.getValue(); |
| for (final StalenessListener listener : info.getListeners()) |
| { |
| listener.stalenessChanged(event); |
| } |
| } |
| } |
| } |
| |
| private List<StalenessListener> getListeners(final IResource res) |
| { |
| List<StalenessListener> stalenessListeners = null; |
| |
| synchronized (this) |
| { |
| final ViewInfo viewInfo = _stalenessListeners.get(res); |
| |
| if (viewInfo != null) |
| { |
| stalenessListeners = viewInfo.getListeners(); |
| } |
| } |
| return stalenessListeners; |
| } |
| |
| public synchronized void addViewInfo(final String viewId, |
| final IResource res) |
| { |
| ViewInfo viewInfo = _stalenessListeners.get(res); |
| |
| if (viewInfo == null) |
| { |
| viewInfo = new ViewInfo(viewId); |
| _stalenessListeners.put(res, viewInfo); |
| } |
| } |
| |
| public synchronized ViewInfo getViewInfo(final IResource res) |
| { |
| return _stalenessListeners.get(res); |
| } |
| |
| public synchronized void dispose() |
| { |
| // updating with null effectively deregisters any existing listener |
| // and doesn't register a new one. |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener(_buildListener); |
| update(null); |
| } |
| |
| public synchronized void update( |
| final ImmutableLifecycleListener listener) |
| { |
| if (listener == _listener) |
| { |
| return; |
| } |
| |
| final ImmutableLifecycleListener oldListener = _listener; |
| |
| if (oldListener != null) |
| { |
| oldListener.removeListener(this); |
| } |
| |
| _listener = listener; |
| |
| if (_listener != null) |
| { |
| _listener.addListener(this); |
| } |
| } |
| } |
| |
| private static class ViewInfo |
| { |
| private final CopyOnWriteArrayList<StalenessListener> _listeners; |
| private final String _viewId; |
| |
| /** |
| * @param listeners |
| * @param res |
| */ |
| private ViewInfo(final String viewId) |
| { |
| super(); |
| _listeners = new CopyOnWriteArrayList<StalenessListener>(); |
| _viewId = viewId; |
| } |
| |
| protected final CopyOnWriteArrayList<StalenessListener> getListeners() |
| { |
| return _listeners; |
| } |
| |
| protected final String getViewId() |
| { |
| return _viewId; |
| } |
| } |
| } |