| /******************************************************************************* |
| * 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.util.Collections; |
| import java.util.Map; |
| 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.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.jst.jsf.common.internal.resource.ImmutableLifecycleListener; |
| import org.eclipse.jst.jsf.common.runtime.internal.model.component.ComponentTypeInfo; |
| import org.eclipse.jst.jsf.context.symbol.ISymbol; |
| 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.StalenessListener; |
| import org.eclipse.jst.jsf.designtime.internal.view.DTUIViewRoot.VersionStamp; |
| import org.eclipse.jst.jsf.designtime.internal.view.IDTViewHandler.ViewHandlerException.Cause; |
| import org.eclipse.wst.common.componentcore.ComponentCore; |
| |
| /** |
| * All IDTViewHandler's must sub-class this abstract class. |
| * |
| * @author cbateman |
| * |
| */ |
| public abstract class AbstractDTViewHandler implements IDTViewHandler |
| { |
| private final AtomicBoolean _isDisposed = new AtomicBoolean(false); |
| |
| /** |
| * the path separator |
| */ |
| public static final String PATH_SEPARATOR = "/"; //$NON-NLS-1$ |
| |
| public abstract String calculateLocale(DTFacesContext context) |
| throws ViewHandlerException; |
| |
| public final DTUIViewRoot createView(final DTFacesContext facesContext, |
| final String viewId) throws ViewHandlerException |
| { |
| if (_isDisposed.get()) |
| { |
| throw new IllegalStateException("View handler is disposed"); //$NON-NLS-1$ |
| } |
| |
| final DTUIViewRoot viewRoot = internalCreateView(facesContext, viewId); |
| |
| if (viewRoot == null) |
| { |
| throw new ViewHandlerException("Problem in createView", Cause.UNABLE_TO_CREATE_VIEW); //$NON-NLS-1$ |
| } |
| viewRoot.setViewId(viewId); |
| final VersionStamp versionStamp = createVersionStamp(facesContext, |
| viewId); |
| if (versionStamp == null) |
| { |
| throw new ViewHandlerException(new Throwable( |
| "Bad version stamp detected"), Cause.BAD_VERSION_STAMP); //$NON-NLS-1$ |
| } |
| viewRoot.setVersionStamp(versionStamp); |
| |
| final StalenessAdvisor advisor = createStalenessAdvisor(viewRoot, |
| facesContext, viewId); |
| |
| if (advisor == null) |
| { |
| throw new ViewHandlerException(new Throwable( |
| "Bad staleness advisor"), Cause.BAD_STALENESS_ADVISOR); //$NON-NLS-1$ |
| } |
| viewRoot.setStalenessAdvisor(advisor); |
| |
| // lock down the tree |
| viewRoot.setSubtreeProtected(); |
| |
| registerView(viewRoot, facesContext, viewId); |
| |
| return viewRoot; |
| } |
| |
| /** |
| * @param facesContext |
| * @param viewId |
| * @return internal construction of the view root. |
| */ |
| protected abstract DTUIViewRoot internalCreateView( |
| final DTFacesContext facesContext, final String viewId); |
| |
| public abstract IResource getActionDefinition(DTFacesContext context, |
| String viewId) throws ViewHandlerException; |
| |
| public abstract IPath getActionURL(DTFacesContext context, |
| IResource resource, IPath requestPath) throws ViewHandlerException; |
| |
| public abstract IPath getRelativeActionPath(DTFacesContext context, |
| String relativeToViewId, String uri) throws ViewHandlerException; |
| |
| public abstract IViewDefnAdapterFactory getViewMetadataAdapterFactory( |
| DTFacesContext context) throws ViewHandlerException; |
| |
| public abstract boolean supportsViewDefinition(IFile file); |
| |
| |
| public abstract void setLifecycleListener(ImmutableLifecycleListener listener); |
| |
| public final void dispose() |
| { |
| if (_isDisposed.compareAndSet(false, true)) |
| { |
| doDispose(); |
| } |
| } |
| |
| /** |
| * Sub-classes should override to add disposal tasks |
| */ |
| protected void doDispose() |
| { |
| // do nothing by default |
| } |
| |
| /** |
| * IMPORTANT: this method must not return null. All view creation will fail |
| * for this handler when createVersionStamp() returns null. create() will |
| * throw ViewHandlerException. |
| * |
| * @param facesContext |
| * @param viewId |
| * @return a new version stamp for the facesContext/viewId. The version |
| * stamp needs to be unique for all instances of viewId only, not |
| * all instances of component trees everywhere in the system. |
| */ |
| protected abstract VersionStamp createVersionStamp( |
| final DTFacesContext facesContext, final String viewId); |
| |
| /** |
| * IMPORTANT: this method must not return null. All view creation will fail |
| * for this handler when createStalenessAdvisor returns null. |
| * |
| * @param viewRoot |
| * @param facesContext |
| * @param viewId |
| * @return a new staleness advisor. |
| */ |
| protected abstract StalenessAdvisor createStalenessAdvisor( |
| final DTUIViewRoot viewRoot, DTFacesContext facesContext, |
| String viewId); |
| |
| /** |
| * Called by createView immediately before returning. Provides sub-class |
| * a chance to register a fully initialized view root with any stateful |
| * activities such as automatic change tracking and caching. |
| * |
| * @param viewRoot |
| * @param context |
| * @param viewId |
| */ |
| protected void registerView(DTUIViewRoot viewRoot, final DTFacesContext context, final String viewId) |
| { |
| // do nothing by default |
| } |
| |
| public String getViewId(final DTFacesContext context, final IResource res) |
| { |
| // TODO: sync with WebrootUtil? |
| String strWebrootPath = ""; //$NON-NLS-1$ |
| final IProject project = res.getProject(); |
| final IPath path = res.getFullPath(); |
| final IPath webContentPath = getWebContentPath(project); |
| if (webContentPath != null && webContentPath.isPrefixOf(path)) |
| { |
| final int start = path.matchingFirstSegments(webContentPath); |
| final String[] segments = path.segments(); |
| for (int i = start, n = path.segmentCount(); i < n; i++) |
| { |
| strWebrootPath = strWebrootPath + PATH_SEPARATOR + segments[i]; |
| } |
| } |
| return strWebrootPath; |
| } |
| |
| private IPath getWebContentPath(final IProject project) |
| { |
| if (project != null) |
| { |
| return ComponentCore.createComponent(project).getRootFolder() |
| .getUnderlyingFolder().getFullPath(); |
| } |
| return null; |
| } |
| |
| /** |
| * A default DTUIViewRoot that represents an uncreatable or uninitializable |
| * view root. |
| * |
| */ |
| protected static final class NullViewRoot extends DTUIViewRoot |
| { |
| /** |
| * |
| */ |
| private static final long serialVersionUID = 1187035338610719171L; |
| |
| /** |
| */ |
| protected NullViewRoot() |
| { |
| super(null, null, new ComponentTypeInfo("", "", "","")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| } |
| |
| @Override |
| public IAdaptable getServices() |
| { |
| return new IAdaptable() |
| { |
| public Object getAdapter(Class adapter) |
| { |
| // no services |
| return null; |
| } |
| }; |
| } |
| |
| @Override |
| protected Map<String, ISymbol> doGetMapForScope(int scopeMask) { |
| return Collections.emptyMap(); |
| } |
| |
| } |
| |
| /** |
| * Provides a simple time-based stamp that makes almost impossible to create |
| * the same timestamp twice. To do so, two threads would need to get the |
| * same system simulataneously and get same number back from the same |
| * gaussian distribution. |
| * |
| * @author cbateman |
| * |
| */ |
| protected static class TimeBasedVersionStamp extends VersionStamp |
| { |
| /** |
| * |
| */ |
| private static final long serialVersionUID = 5557828245936568977L; |
| private final long timestamp; |
| private final long randomStamp; |
| |
| /** |
| * |
| */ |
| public TimeBasedVersionStamp() |
| { |
| super(); |
| // use the millisecond time, since it is offset from a known |
| // point on all platform, whereas no guarantee is made about |
| // the nano time value. |
| this.timestamp = System.currentTimeMillis(); |
| // this protects against two threads constructing two time |
| // stamp objects "simulataneously" and having the same time based |
| // stamp. Math.random is thread-safe. |
| this.randomStamp = (long) (Long.MAX_VALUE * Math.random()); |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| return (int) (timestamp ^ randomStamp); |
| } |
| |
| @Override |
| protected boolean isEqual(final VersionStamp other) |
| { |
| return other instanceof TimeBasedVersionStamp |
| && (timestamp == ((TimeBasedVersionStamp) other).timestamp) |
| && (randomStamp == ((TimeBasedVersionStamp) other).randomStamp); |
| } |
| } |
| |
| /** |
| * Returns a default staleness advisor that always returns false. |
| * |
| * @author cbateman |
| * |
| */ |
| protected static final class NullStalenessAdvisor extends StalenessAdvisor |
| { |
| /** |
| * |
| */ |
| private static final long serialVersionUID = 5946420948320047613L; |
| |
| @Override |
| public boolean isStale() |
| { |
| return false; |
| } |
| |
| @Override |
| public void addListener(final StalenessListener listener) |
| { |
| // do nothing |
| } |
| |
| @Override |
| public void removeListener(final StalenessListener listener) |
| { |
| // do nothing |
| } |
| |
| public boolean isAccessible() |
| { |
| return true; |
| } |
| } |
| } |