/******************************************************************************* * Copyright (c) 2009 Red Hat 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: * Red Hat - Initial API and implementation *******************************************************************************/ package org.eclipse.wst.web.internal.deployables; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdapterFactory; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.wst.common.componentcore.ComponentCore; import org.eclipse.wst.common.componentcore.internal.flat.FlatVirtualComponent; import org.eclipse.wst.common.componentcore.internal.flat.FlatVirtualComponent.FlatComponentTaskModel; import org.eclipse.wst.common.componentcore.internal.flat.FlattenParticipantModel; import org.eclipse.wst.common.componentcore.internal.flat.IChildModuleReference; import org.eclipse.wst.common.componentcore.internal.flat.IFlatFile; import org.eclipse.wst.common.componentcore.internal.flat.IFlatFolder; import org.eclipse.wst.common.componentcore.internal.flat.IFlatResource; import org.eclipse.wst.common.componentcore.internal.flat.IFlatVirtualComponent; import org.eclipse.wst.common.componentcore.internal.flat.IFlattenParticipant; import org.eclipse.wst.common.componentcore.internal.util.VirtualReferenceUtilities; import org.eclipse.wst.common.componentcore.resources.IVirtualComponent; import org.eclipse.wst.common.project.facet.core.IFacetedProject; import org.eclipse.wst.common.project.facet.core.IProjectFacet; import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager; import org.eclipse.wst.server.core.IModule; import org.eclipse.wst.server.core.ServerUtil; import org.eclipse.wst.server.core.model.IModuleFile; import org.eclipse.wst.server.core.model.IModuleFolder; import org.eclipse.wst.server.core.model.IModuleResource; import org.eclipse.wst.server.core.model.ModuleDelegate; import org.eclipse.wst.server.core.util.ModuleFile; import org.eclipse.wst.server.core.util.ProjectModule; public abstract class FlatComponentDeployable extends ProjectModule implements IFlatDeployable { public static final String FLATTEN_PARTICIPANTS = "org.eclipse.wst.web.deployables.flatten.participants"; //$NON-NLS-1$ private static final String FLATTEN_PARTICIPANTS_DELIM = ","; //$NON-NLS-1$ /* * Register an adapt IModule to IVirtualComponent */ static { Platform.getAdapterManager().registerAdapters(new IAdapterFactory() { public Class[] getAdapterList() { return new Class[] { IVirtualComponent.class }; } public Object getAdapter(Object adaptableObject, Class adapterType) { if (adaptableObject instanceof IModule) { IModule module = (IModule) adaptableObject; FlatComponentDeployable deployable = (FlatComponentDeployable) module.loadAdapter(FlatComponentDeployable.class, null); if(deployable != null){ IVirtualComponent virtualComponent = deployable.getComponent(); return virtualComponent; } } return null; } }, IModule.class); } protected IVirtualComponent component = null; protected List<IModuleResource> members = new ArrayList<IModuleResource>(); public FlatComponentDeployable(IProject project) { this(project,ComponentCore.createComponent(project)); } public FlatComponentDeployable(IProject project, IVirtualComponent aComponent) { super(project); this.component = aComponent; } public IVirtualComponent getComponent() { return component; } /** * We will cache the flattened piece here, and instead redirect * the module factories to recreate the modules *whenever* there is * a workspace change. This will still be much more efficient than * traversing the tree each time a call to getResources() or getChildModules(). */ private FlatVirtualComponent cacheFlattened = null; public boolean shouldCache() { return false; } public void clearCache() { cacheFlattened = null; } /** * The export model is what does the grunt of the work * @return */ protected IFlatVirtualComponent getFlatComponent() { if( !shouldCache() || cacheFlattened == null ) { FlatComponentTaskModel options = new FlatComponentTaskModel(); options.put(FlatVirtualComponent.PARTICIPANT_LIST, Arrays.asList(getParticipants())); FlatVirtualComponent tmp = new FlatVirtualComponent(component, options); if( shouldCache()) cacheFlattened = tmp; return tmp; } return cacheFlattened; } /** * Subclasses can provide a list of participants who may * be involved in forming the export model * * A deployable with no participant should still properly * consume consumed references and traverse the model appropriately * * @return */ public IFlattenParticipant[] getParticipants() { String[] ids = getParticipantIds(); return getFlattenParticipants(ids); } public String[] getParticipantIds() { // If file exists, load from file String participants = component.getMetaProperties().getProperty(FLATTEN_PARTICIPANTS); // else, get the default ones String[] split = participants == null ? getDefaultFlattenParticipantIDs() : participants.split(FLATTEN_PARTICIPANTS_DELIM); for( int i = 0; i < split.length; i++ ) { split[i] = split[i].trim(); } return split; } protected IFlattenParticipant[] getFlattenParticipants(String[] ids) { ArrayList<IFlattenParticipant> participants = new ArrayList<IFlattenParticipant>(); IFlattenParticipant tmp; for( int i = 0; i < ids.length; i++ ) { tmp = FlattenParticipantModel.getDefault().getParticipant(ids[i]); if( tmp != null ) participants.add(tmp); else { // Log? This is an error somehow } } return participants.toArray(new IFlattenParticipant[participants.size()]); } public void addFlattenParticipant(String id, int position) { String participants = component.getMetaProperties().getProperty(FLATTEN_PARTICIPANTS); String[] split = participants == null ? getDefaultFlattenParticipantIDs() : participants.split(","); //$NON-NLS-1$ ArrayList<String> asList = new ArrayList<String>(); asList.addAll(Arrays.asList(split)); if( !asList.contains(id)) { if( position < asList.size()) asList.add(position, id); else asList.add(id); } String asString = implode(asList.toArray(new String[asList.size()]), FLATTEN_PARTICIPANTS_DELIM); component.setMetaProperty(FLATTEN_PARTICIPANTS, asString); } private String implode(String[] array, String delim) { String retval; if (array.length==0) { retval = ""; //$NON-NLS-1$ } else { StringBuffer sb = new StringBuffer(); sb.append(array[0]); for (int i=1;i<array.length;i++) { sb.append(delim); sb.append(array[i]); } retval = sb.toString(); } return retval; } public void removeFlattenParticipant(String id) { String participants = component.getMetaProperties().getProperty(FLATTEN_PARTICIPANTS); String[] split = participants == null ? getDefaultFlattenParticipantIDs() : participants.split(","); //$NON-NLS-1$ ArrayList<String> asList = new ArrayList<String>(); asList.addAll(Arrays.asList(split)); asList.remove(id); String asString = implode(asList.toArray(new String[asList.size()]), FLATTEN_PARTICIPANTS_DELIM); component.setMetaProperty(FLATTEN_PARTICIPANTS, asString); } /** * Get a list of participant keys that are default for this project type * @return */ public String[] getDefaultFlattenParticipantIDs() { return new String[0]; } public boolean isBinary() { return component == null ? false : component.isBinary(); } @Override public IModuleResource[] members() throws CoreException { if( component.isBinary() ) return LEGACY_binaryMembers(); IFlatVirtualComponent em = getFlatComponent(); IFlatResource[] resources = em.fetchResources(); return convert(resources); } protected IModuleResource[] LEGACY_binaryMembers() { IFile ifile = (IFile)component.getAdapter(IFile.class); File file = (File)component.getAdapter(File.class); ModuleFile mf = ifile != null ? new ModuleFile(ifile, ifile.getName(), new Path("")) //$NON-NLS-1$ : new ModuleFile(file, file.getName(), new Path("")); //$NON-NLS-1$ return new IModuleResource[]{mf}; } /** * Returns the child modules of this module. * * @return org.eclipse.wst.server.core.model.IModule[] */ @Override public IModule[] getChildModules() { return getModules(); } public /* non api */ IChildModuleReference[] getExportModelChildren() throws CoreException { IFlatVirtualComponent em = getFlatComponent(); IChildModuleReference[] children = em.getChildModules(); return children; } public IModule[] getModules() { // Legacy, here in case the old modules are used if( component.isBinary() ) return new IModule[]{}; try { List<IModule> modules = new ArrayList<IModule>(); IChildModuleReference[] children = getExportModelChildren(); for( int i = 0; i < children.length; i++ ) { IModule child = gatherModuleReference(component, children[i]); if( child != null ) modules.add(child); } return modules.toArray(new IModule[modules.size()]); } catch( CoreException ce ) { } return new IModule[]{}; } @Override public String getPath(IModule m) { return getURI(m); } /** * Returns the URI of the given contained CHILD module. * * SOFT requirements (NOT API!!) in use by some adopters * If the passed in module is equal to this module, return our own deployed name * * @param module a module * @return the URI of the given module, or <code>null</code> if the URI could * not be found */ public String getURI(IModule module) { ProjectModule md = (ProjectModule)module.loadAdapter(ProjectModule.class, new NullProgressMonitor()); if( md == this ) { // guess my own name return VirtualReferenceUtilities.INSTANCE.getDefaultProjectArchiveName(this.component); } try { FlatComponentDeployable cd = (FlatComponentDeployable)module.loadAdapter(FlatComponentDeployable.class, new NullProgressMonitor()); if( cd != null ) { IFlatVirtualComponent em = getFlatComponent(); IChildModuleReference[] children = em.getChildModules(); for( int i = 0; i < children.length; i++ ) { IModule child = gatherModuleReference(component, children[i]); if( child != null && child.getId().equals(module.getId())) return children[i].getRelativeURI().toString(); } } } catch( CoreException ce ) { } return null; } /** * If I know how to find an IModule for this child, do so now * * I would love to see this replaced with some API to locate a * possible child module based on a virtual component. * * @param component * @param targetComponent * @return */ protected IModule gatherModuleReference(IVirtualComponent component, IChildModuleReference child) { // Handle workspace project module components // Subclasses should extend IVirtualComponent targetComponent = child.getComponent(); if (targetComponent != null && targetComponent.getProject()!= component.getProject()) { if (!targetComponent.isBinary()) { return filterModuleDelegates(ServerUtil.getModules(targetComponent.getProject())); } } return null; } /** * An extender may wish to override this method in order to control which * delegate is returned in the scenario where more than one exist. By default * the first one found is returned. * * @param IModule[] modules * @return IModule[] */ protected IModule filterModuleDelegates(IModule[] modules) { for (int i = 0; i < modules.length; i++) { ModuleDelegate md = (ModuleDelegate)modules[i].loadAdapter(ModuleDelegate.class, new NullProgressMonitor()); if (md instanceof ProjectModule) { return modules[i]; } } return modules.length > 0 ? modules[0] : null; } /* * Below are STATIC utility classes and methods */ protected static IModuleResource[] convert(IFlatResource[] resources) { ArrayList<IModuleResource> list = new ArrayList<IModuleResource>(); for( int i = 0; i < resources.length; i++ ) { if( resources[i] instanceof IFlatFile) list.add(new ComponentModuleFile(resources[i])); else if( resources[i] instanceof IFlatFolder) list.add(new ComponentModuleFolder(resources[i])); } return list.toArray(new IModuleResource[list.size()]); } public static class ComponentModuleResource { protected IFlatResource delegate; public ComponentModuleResource(IFlatResource resource) { this.delegate = resource; } public long getModificationStamp() { return ((IFlatFile)delegate).getModificationStamp(); } public IPath getModuleRelativePath() { return delegate.getModuleRelativePath(); } public String getName() { return delegate.getName(); } public Object getAdapter(Class adapter) { return delegate.getAdapter(adapter); } public IModuleResource[] members() { IFlatResource[] children = ((IFlatFolder)delegate).members(); return convert(children); } @Override public String toString() { return getName(); } } public static class ComponentModuleFile extends ComponentModuleResource implements IModuleFile{ public ComponentModuleFile(IFlatResource resource) { super(resource); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof IModuleFile)) return false; IModuleFile mf = (IModuleFile) obj; if (!getName().equals(mf.getName())) return false; if (!getModuleRelativePath().equals(mf.getModuleRelativePath())) return false; return true; } @Override public int hashCode() { return getName().hashCode() * 37 + getModuleRelativePath().hashCode(); } } public static class ComponentModuleFolder extends ComponentModuleResource implements IModuleFolder { public ComponentModuleFolder(IFlatResource resource) { super(resource); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof IModuleFolder)) return false; IModuleFolder mf = (IModuleFolder) obj; if (!getName().equals(mf.getName())) return false; if (!getModuleRelativePath().equals(mf.getModuleRelativePath())) return false; return true; } @Override public int hashCode() { return getName().hashCode() * 37 + getModuleRelativePath().hashCode(); } } protected static boolean isProjectOfType(IProject project, String typeID) { IFacetedProject facetedProject = null; try { facetedProject = ProjectFacetsManager.create(project); } catch (CoreException e) { return false; } if (facetedProject !=null && ProjectFacetsManager.isProjectFacetDefined(typeID)) { IProjectFacet projectFacet = ProjectFacetsManager.getProjectFacet(typeID); return projectFacet!=null && facetedProject.hasProjectFacet(projectFacet); } return false; } }