| /******************************************************************************* |
| * 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.common.componentcore.internal.flat; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.wst.common.componentcore.internal.DependencyType; |
| import org.eclipse.wst.common.componentcore.internal.flat.VirtualComponentFlattenUtility.ShouldIncludeUtilityCallback; |
| import org.eclipse.wst.common.componentcore.resources.IVirtualComponent; |
| import org.eclipse.wst.common.componentcore.resources.IVirtualFolder; |
| import org.eclipse.wst.common.componentcore.resources.IVirtualReference; |
| |
| public class FlatVirtualComponent implements IFlatVirtualComponent, ShouldIncludeUtilityCallback { |
| |
| public static class FlatComponentTaskModel extends HashMap<Object, Object> { |
| private static final long serialVersionUID = 1L; |
| } |
| |
| /** |
| * The datamodel, which may contain preferences, settings, or other data |
| * used by the various participants to determine how to properly |
| * traverse this component. |
| */ |
| private FlatComponentTaskModel dataModel; |
| |
| /** |
| * The root component being flattened. |
| */ |
| private IVirtualComponent component; |
| |
| /** |
| * The list of participants to engage in the flattening process. |
| */ |
| private IFlattenParticipant[] participants; |
| |
| /** |
| * The list of member resources for this component |
| */ |
| private List<IFlatResource> members = null; |
| |
| /** |
| * The list of child modules for this component |
| */ |
| private List<IChildModuleReference> children = null; |
| |
| |
| public FlatVirtualComponent(IVirtualComponent component) { |
| this(component, new FlatComponentTaskModel()); |
| } |
| |
| public FlatVirtualComponent(IVirtualComponent component, FlatComponentTaskModel dataModel) { |
| this.component = component; |
| this.dataModel = dataModel; |
| participants = setParticipants(); |
| dataModel.put(EXPORT_MODEL, this); |
| } |
| |
| /** |
| * Set the list of participants for this virtual component. |
| * This is pulled from the datamodel. |
| */ |
| protected IFlattenParticipant[] setParticipants() { |
| Object o = dataModel.get(PARTICIPANT_LIST); |
| if( o != null ) { |
| if( o instanceof IFlattenParticipant ) |
| return new IFlattenParticipant[] { (IFlattenParticipant)o}; |
| if( o instanceof IFlattenParticipant[]) |
| return (IFlattenParticipant[])o; |
| if( o instanceof List ) { |
| List<IFlattenParticipant> l = (List<IFlattenParticipant>)o; |
| return (IFlattenParticipant[]) l |
| .toArray(new IFlattenParticipant[l.size()]); |
| } |
| } |
| return new IFlattenParticipant[]{}; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.wst.common.componentcore.internal.flat.IFlatVirtualComponent#fetchResources() |
| */ |
| public IFlatResource[] fetchResources() throws CoreException { |
| if( members == null) |
| cacheResources(); |
| return (FlatResource[]) members.toArray(new FlatResource[members.size()]); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.wst.common.componentcore.internal.flat.IFlatVirtualComponent#getChildModules() |
| */ |
| public IChildModuleReference[] getChildModules() throws CoreException { |
| if( members == null ) |
| cacheResources(); |
| return (ChildModuleReference[]) children.toArray(new ChildModuleReference[children.size()]); |
| } |
| |
| |
| protected void cacheResources() throws CoreException { |
| runInitializations(); |
| if( canOptimize()) { |
| optimize(members, children); |
| } else { |
| treeWalk(); |
| runFinalizations(members); |
| } |
| } |
| |
| protected void runInitializations() { |
| members = new ArrayList<IFlatResource>(); |
| children = new ArrayList<IChildModuleReference>(); |
| for( int i = 0; i < participants.length; i++ ) { |
| participants[i].initialize(component, dataModel, members); |
| } |
| } |
| |
| protected boolean canOptimize() { |
| for( int i = 0; i < participants.length; i++ ) { |
| if( participants[i].canOptimize(component, dataModel)) |
| return true; |
| } |
| return false; |
| } |
| |
| protected void optimize(List<IFlatResource> resources, List<IChildModuleReference> children) { |
| for( int i = 0; i < participants.length; i++ ) { |
| if( participants[i].canOptimize(component, dataModel)) { |
| participants[i].optimize(component, dataModel, resources, children); |
| return; |
| } |
| } |
| } |
| |
| protected void runFinalizations(List<IFlatResource> resources) { |
| for( int i = 0; i < participants.length; i++ ) { |
| participants[i].finalize(component, dataModel, resources); |
| } |
| } |
| |
| protected void treeWalk() throws CoreException { |
| if (component != null) { |
| VirtualComponentFlattenUtility util = new VirtualComponentFlattenUtility(members, this); |
| IVirtualFolder vFolder = component.getRootFolder(); |
| |
| // actually walk the tree |
| util.addMembers(component, vFolder, Path.EMPTY); |
| |
| //addRelevantOutputFolders(); // to be done in a participant later |
| |
| addConsumedReferences(util, component, new Path("")); |
| addUsedReferences(util, component, new Path("")); |
| } |
| } |
| |
| /** |
| * Consumed references are, by definition, consumed, and should not |
| * be eligible to be exposed as child modules. They are consumed |
| * directly into the module tree. |
| * |
| * The reference in question may have references of its own, both |
| * used and consumed. References of the child will be treated |
| * as references of the parent, whether consumed or used. |
| * |
| * A key difference in the handling of non-child USED references |
| * as compared to CONSUMES is that CONSUMED references have their |
| * archiveName *ignored*, and its child members are directly consumed. |
| * In contrast, a USED non-child keeps its archiveName as the folder name. |
| * |
| * @param vc |
| */ |
| protected void addConsumedReferences(VirtualComponentFlattenUtility util, IVirtualComponent vc, IPath root) throws CoreException { |
| List consumableMembers = new ArrayList(); |
| Map<String, Object> options = new HashMap<String, Object>(); |
| options.put(IVirtualComponent.REQUESTED_REFERENCE_TYPE, IVirtualComponent.FLATTENABLE_REFERENCES); |
| IVirtualReference[] refComponents = vc.getReferences(options); |
| for (int i = 0; i < refComponents.length; i++) { |
| IVirtualReference reference = refComponents[i]; |
| if (reference != null && reference.getDependencyType()==IVirtualReference.DEPENDENCY_TYPE_CONSUMES) { |
| consumeComponent(util, root, reference); |
| } |
| } |
| } |
| |
| protected void consumeComponent(VirtualComponentFlattenUtility util, IPath root, IVirtualReference reference) throws CoreException { |
| IVirtualComponent consumedComponent = reference.getReferencedComponent(); |
| if (consumedComponent.getRootFolder()!=null) { |
| IVirtualFolder vFolder = consumedComponent.getRootFolder(); |
| util.addMembers(consumedComponent, vFolder, root.append(reference.getRuntimePath().makeRelative())); |
| addConsumedReferences(util, consumedComponent, root.append(reference.getRuntimePath().makeRelative())); |
| addUsedReferences(util, consumedComponent, root.append(reference.getRuntimePath().makeRelative())); |
| } |
| } |
| |
| /** |
| * This checks to see if any exportable file is actually a child module. |
| * Children modules will be exposed via the getChildModules() method. |
| */ |
| public boolean shouldAddComponentFile(IVirtualComponent current, IFlatFile file) { |
| for( int i = 0; i < participants.length; i++ ) { |
| if( participants[i].isChildModule(component, dataModel, file)) { |
| ChildModuleReference child = new ChildModuleReference(current.getProject(), file); |
| children.add(child); |
| return false; |
| } else if( !participants[i].shouldAddExportableFile(component, current, dataModel, file)) |
| return false; |
| } |
| return true; |
| } |
| |
| protected void addUsedReferences(VirtualComponentFlattenUtility util, IVirtualComponent vc, IPath root) throws CoreException { |
| Map<String, Object> options = new HashMap<String, Object>(); |
| options.put(IVirtualComponent.REQUESTED_REFERENCE_TYPE, IVirtualComponent.FLATTENABLE_REFERENCES); |
| IVirtualReference[] allReferences = vc.getReferences(options); |
| for (int i = 0; i < allReferences.length; i++) { |
| IVirtualReference reference = allReferences[i]; |
| IVirtualComponent virtualComp = reference.getReferencedComponent(); |
| if (reference.getDependencyType() == DependencyType.USES ) { |
| if( shouldIgnoreReference(reference)) |
| continue; |
| |
| if( !isChildModule(reference)) { |
| addNonChildUsedReference(util, vc, reference, root.append(reference.getRuntimePath())); |
| } else { |
| ChildModuleReference cm = new ChildModuleReference(reference, root); |
| List<IChildModuleReference> duplicates = new ArrayList(); |
| for( IChildModuleReference tmp : children ) { |
| if(tmp.getRelativeURI().equals(cm.getRelativeURI())) |
| duplicates.add(tmp); |
| } |
| children.removeAll(duplicates); |
| children.add(cm); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Should we expose this used reference as a member file? |
| * |
| * @param currentComponent the current component we're traversing |
| * @return true if it's a member file, false if it's a child module |
| */ |
| protected boolean isChildModule(IVirtualReference referencedComponent) { |
| for( int i = 0; i < participants.length; i++ ) { |
| if( participants[i].isChildModule(component, referencedComponent, dataModel)) |
| return true; |
| } |
| return false; |
| } |
| |
| protected boolean shouldIgnoreReference(IVirtualReference referencedComponent) { |
| for( int i = 0; i < participants.length; i++ ) { |
| if( participants[i].shouldIgnoreReference(component, referencedComponent, dataModel)) |
| return true; |
| } |
| return false; |
| } |
| |
| protected void addNonChildUsedReference(VirtualComponentFlattenUtility util, IVirtualComponent parent, |
| IVirtualReference reference, IPath runtimePath) throws CoreException { |
| if( reference.getReferencedComponent().isBinary()) { |
| handleNonChildUsedBinaryReference(util, parent, reference, runtimePath); |
| } else /* !virtualComp.isBinary() */ { |
| /* |
| * used references to non-binary components that are NOT child modules. |
| * These should be 'consumed' but maintain their name |
| * As of now I don't believe there are any such instances of this. |
| * I also believe in most cases, this probably is a child module that |
| * the parent just doesn't know about. |
| * |
| * Example: Ear Project consumes ESB project, Ear project does not |
| * recognize ESB project as a child However, if the server adapter |
| * can use nested exploded deployments (folders instead of zips), |
| * then this will still work. |
| * |
| * TODO Investigate / Discuss |
| */ |
| util.addMembers(reference.getReferencedComponent(), reference.getReferencedComponent().getRootFolder(), |
| runtimePath.append(reference.getArchiveName())); |
| } |
| } |
| |
| protected void handleNonChildUsedBinaryReference(VirtualComponentFlattenUtility util, IVirtualComponent parent, |
| IVirtualReference reference, IPath runtimePath) throws CoreException { |
| // Binary used references must be added as a single file unless they're child modules |
| final String archiveName2 = reference.getArchiveName(); |
| final String archiveName = new Path(archiveName2).lastSegment(); |
| final IVirtualComponent virtualComp = reference.getReferencedComponent(); |
| FlatFile mf = null; |
| IFile ifile = (IFile)virtualComp.getAdapter(IFile.class); |
| if( ifile != null ) { |
| String name = null != archiveName ? archiveName : ifile.getName(); |
| mf = new FlatFile(ifile, name, runtimePath.makeRelative()); |
| } else { |
| File extFile = (File)virtualComp.getAdapter(File.class); |
| if( extFile != null ) { |
| String name = null != archiveName ? archiveName : extFile.getName(); |
| mf = new FlatFile(extFile, name, runtimePath.makeRelative()); |
| } |
| } |
| |
| if( mf != null ) { |
| IFlatResource moduleParent = VirtualComponentFlattenUtility.getExistingModuleResource(members, mf.getModuleRelativePath()); |
| if (moduleParent != null && moduleParent instanceof IFlatFolder) { |
| IFlatResource[] mf_members = ((IFlatFolder)moduleParent).members(); |
| for (int i = 0; i < mf_members.length; i++) { |
| if (mf_members[i].getName().equals(mf.getName())) |
| return; |
| } |
| VirtualComponentFlattenUtility.addMembersToModuleFolder((IFlatFolder)moduleParent, new FlatResource[]{mf}); |
| } else { |
| if( shouldAddExportableFile(virtualComp, mf)) { |
| if (mf.getModuleRelativePath().isEmpty()) { |
| for( IFlatResource tmp : members) |
| if( tmp.getName().equals(mf.getName())) |
| return; |
| members.add(mf); |
| } else { |
| if (moduleParent == null) { |
| moduleParent = VirtualComponentFlattenUtility.ensureParentExists(members, mf.getModuleRelativePath(), (IContainer)parent.getRootFolder().getUnderlyingResource()); |
| } |
| VirtualComponentFlattenUtility.addMembersToModuleFolder((IFlatFolder)moduleParent, new FlatResource[] {mf}); |
| } |
| } else { |
| // Automatically added to children if it needed to be |
| } |
| } |
| } |
| } |
| |
| protected boolean shouldAddExportableFile(IVirtualComponent current, FlatFile file) { |
| for( int i = 0; i < participants.length; i++ ) { |
| if ( !participants[i].shouldAddExportableFile(component, current, dataModel, file)) |
| return false; |
| } |
| return true; |
| } |
| |
| public IVirtualComponent getComponent() { |
| return component; |
| } |
| } |