/******************************************************************************* * 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.EventResult; 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.StalenessEvent.ChangeType; 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.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 { return internalGetViewMetadataAdapterFactory(context); } /** * @param context * @return the DefaultViewDefnAdapterFactory * */ protected IViewDefnAdapterFactory getDefaultViewMetadataAdapterFactory( final DTFacesContext context) { return internalGetViewMetadataAdapterFactory(context); } private IViewDefnAdapterFactory internalGetViewMetadataAdapterFactory (final DTFacesContext context) { 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"); //$NON-NLS-1$ } // 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", //$NON-NLS-1$ viewId)); } else { JSFCorePlugin .log( IStatus.WARNING, String .format( "Could not get view adapter factory toconstruct design time view root for %s", //$NON-NLS-1$ viewId)); } } catch (final ViewHandlerException e) { JSFCorePlugin.log(e, "While acquiring view defn adapter factory"); //$NON-NLS-1$ // 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(facesContext); } @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; // } } }