| /********************************************************************* |
| * Copyright (c) 2009 - 2012 SpringSource, a division of VMware, Inc. and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| **********************************************************************/ |
| |
| package org.eclipse.virgo.ide.facet.core; |
| |
| import java.io.BufferedReader; |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Queue; |
| import java.util.Set; |
| import java.util.concurrent.ArrayBlockingQueue; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceVisitor; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.common.util.WrappedException; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; |
| import org.eclipse.emf.ecore.xmi.PackageNotFoundException; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| import org.eclipse.virgo.ide.facet.internal.core.Plan; |
| import org.eclipse.virgo.ide.facet.internal.core.PlanReader; |
| import org.eclipse.virgo.ide.facet.internal.core.PlanReference; |
| import org.eclipse.virgo.ide.par.Bundle; |
| import org.eclipse.virgo.ide.par.Par; |
| import org.eclipse.virgo.ide.par.ParPackage; |
| import org.eclipse.wst.common.project.facet.core.FacetedProjectFramework; |
| |
| /** |
| * Utility to check if the given {@link IResource} belongs to a project that has the par or bundle facet. |
| * |
| * @author Christian Dupuis |
| * @author Leo Dos Santos |
| * @author GianMaria Romanato |
| * @since 1.0.0 |
| */ |
| public class FacetUtils { |
| |
| /** |
| * Checks if a given {@link IResource} has the bundle facet. |
| */ |
| public static boolean isBundleProject(IResource resource) { |
| return hasNature(resource, JavaCore.NATURE_ID) && hasProjectFacet(resource, FacetCorePlugin.BUNDLE_FACET_ID); |
| } |
| |
| /** |
| * Checks if a given {@link IResource} has the par facet. |
| */ |
| public static boolean isParProject(IResource resource) { |
| return hasProjectFacet(resource, FacetCorePlugin.PAR_FACET_ID); |
| } |
| |
| /** |
| * Checks if a given {@link IResource} has the par facet. |
| */ |
| public static boolean isPlanProject(IResource resource) { |
| return hasProjectFacet(resource, FacetCorePlugin.PLAN_FACET_ID); |
| } |
| |
| /** |
| * Checks if a {@link IResource} has a given project facet. |
| */ |
| public static boolean hasProjectFacet(IResource resource, String facetId) { |
| if (resource != null && resource.isAccessible()) { |
| try { |
| return FacetedProjectFramework.hasProjectFacet(resource.getProject(), facetId); |
| } catch (CoreException e) { |
| StatusManager.getManager().handle( |
| new Status(IStatus.ERROR, FacetCorePlugin.PLUGIN_ID, "An error occurred inspecting project facet", e)); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Checks if a {@link IResource} has a given project nature. |
| */ |
| public static boolean hasNature(IResource resource, String natureId) { |
| if (resource != null && resource.isAccessible()) { |
| IProject project = resource.getProject(); |
| if (project != null) { |
| try { |
| return project.hasNature(natureId); |
| } catch (CoreException e) { |
| StatusManager.getManager().handle( |
| new Status(IStatus.ERROR, FacetCorePlugin.PLUGIN_ID, "An error occurred inspecting project nature", e)); |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns all bundle project in the current workspace regardless weather they are open or closed. |
| */ |
| public static IProject[] getBundleProjects() { |
| List<IProject> bundles = new ArrayList<IProject>(); |
| IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); |
| for (IProject candidate : projects) { |
| if (FacetUtils.isBundleProject(candidate)) { |
| bundles.add(candidate); |
| } |
| } |
| return bundles.toArray(new IProject[bundles.size()]); |
| } |
| |
| public static IProject[] getParProjects(IProject project) { |
| Set<IProject> bundles = new HashSet<IProject>(); |
| IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); |
| for (IProject candidate : projects) { |
| if (FacetUtils.isParProject(candidate)) { |
| if (Arrays.asList(getBundleProjects(candidate)).contains(project)) { |
| bundles.add(candidate); |
| } |
| } |
| } |
| return bundles.toArray(new IProject[bundles.size()]); |
| } |
| |
| public static IProject[] getBundleProjects(IProject parProject) { |
| Set<IProject> bundles = new HashSet<IProject>(); |
| if (isParProject(parProject)) { |
| Par par = getParDefinition(parProject); |
| if (par != null && par.getBundle() != null) { |
| for (Bundle bundle : par.getBundle()) { |
| IProject bundleProject = ResourcesPlugin.getWorkspace().getRoot().getProject(bundle.getSymbolicName()); |
| if (FacetUtils.isBundleProject(bundleProject)) { |
| bundles.add(bundleProject); |
| } |
| } |
| } |
| } |
| return bundles.toArray(new IProject[bundles.size()]); |
| } |
| |
| public static Par getParDefinition(IProject project) { |
| // Create a resource set to hold the resources. |
| ResourceSet resourceSet = new ResourceSetImpl(); |
| // Register the package to ensure it is available during loading. |
| resourceSet.getPackageRegistry().put(ParPackage.eNS_URI, ParPackage.eINSTANCE); |
| |
| File parFile = new File(new File(project.getLocation().toString() + File.separatorChar + ".settings"), |
| "org.eclipse.virgo.ide.runtime.core.par.xml"); |
| if (parFile.exists()) { |
| URI fileUri = URI.createFileURI(parFile.toString()); |
| Resource resource = null; |
| try { |
| resource = resourceSet.getResource(fileUri, true); |
| } catch (WrappedException e) { |
| if (e.getCause() instanceof PackageNotFoundException) { |
| // Handle case where we need to update old par file format. |
| try { |
| BufferedReader br = new BufferedReader(new FileReader(parFile)); |
| StringBuilder sb = new StringBuilder(); |
| String next = br.readLine(); |
| do { |
| next = next.replaceAll("http:///com/springsource/server/ide/par.ecore", "http://eclipse.org/virgo/par.ecore"); |
| next = next.replaceAll("com\\.springsource\\.server", "org.eclipse.virgo"); |
| sb.append(next + "\n"); |
| next = br.readLine(); |
| } while (next != null); |
| br.close(); |
| BufferedWriter bw = new BufferedWriter(new FileWriter(parFile)); |
| bw.write(sb.toString()); |
| bw.close(); |
| project.refreshLocal(IResource.DEPTH_INFINITE, null); |
| resource = resourceSet.getResource(fileUri, true); |
| } catch (IOException e1) { |
| throw new RuntimeException(e1); |
| } catch (CoreException e2) { |
| throw new RuntimeException(e2); |
| } |
| } |
| } |
| return (Par) resource.getContents().iterator().next(); |
| } |
| |
| return null; |
| } |
| |
| public static IFile getParFile(IProject project) { |
| IResource resource = project.findMember(new Path(".settings").append("org.eclipse.virgo.ide.runtime.core.par.xml")); |
| if (resource instanceof IFile) { |
| return (IFile) resource; |
| } |
| return null; |
| } |
| |
| /** |
| * Gets all the plan files found in the given project. |
| * |
| * @param project |
| * @return |
| */ |
| public static Collection<IFile> getPlansInPlanProject(IProject project) { |
| if (!isPlanProject(project)) { |
| return Collections.emptyList(); |
| } |
| |
| final List<IFile> planFiles = new ArrayList<IFile>(); |
| |
| // Collect output locations if java project |
| final Set<IPath> outputLocations = new HashSet<IPath>(); |
| try { |
| if (FacetUtils.hasNature(project, JavaCore.NATURE_ID)) { |
| IJavaProject je = JavaCore.create(project); |
| try { |
| outputLocations.add(je.getOutputLocation()); |
| for (IClasspathEntry entry : je.getRawClasspath()) { |
| if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { |
| if (entry.getOutputLocation() != null) { |
| outputLocations.add(entry.getOutputLocation()); |
| } |
| } |
| } |
| } catch (JavaModelException e) { |
| // safe to ignore |
| } |
| } |
| project.accept(new IResourceVisitor() { |
| |
| public boolean visit(IResource resource) throws CoreException { |
| if (resource.isTeamPrivateMember() || resource.isDerived()) { |
| return false; |
| } |
| if (resource instanceof IFile && "plan".equals(resource.getFileExtension())) { |
| planFiles.add((IFile) resource); |
| } else if (resource instanceof IContainer) { |
| IPath path = ((IContainer) resource).getFullPath(); |
| for (IPath outputLocation : outputLocations) { |
| if (outputLocation.isPrefixOf(path)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| return true; |
| } |
| }); |
| } catch (CoreException e) { |
| // TODO CD log exception |
| } |
| |
| return planFiles; |
| } |
| |
| /** |
| * Returns all the plans in the workspace as a map project to list of plan files |
| * |
| * @return |
| */ |
| private static Map<IProject, Collection<IFile>> getPlansInWorkspace() { |
| Map<IProject, Collection<IFile>> plans = new HashMap<IProject, Collection<IFile>>(); |
| IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); |
| for (IProject iProject : projects) { |
| if (iProject.isOpen() && isPlanProject(iProject)) { |
| Collection<IFile> ps = getPlansInPlanProject(iProject); |
| plans.put(iProject, ps); |
| } |
| } |
| return plans; |
| } |
| |
| /** |
| * Returns the list of nested plans for a given plan file, including the given plan file as the first one. The look |
| * only in the planFile project, unless the project is also a Java project. In such case, the project classpath is |
| * used to look for plans in required projects. |
| * |
| * @param planFile the plan file |
| * @param recurse whether the method should return only direct children of the file (false) or recurse and collect |
| * all descendants (true) |
| * @return |
| */ |
| public static List<IFile> getNestedPlanFiles(IFile planFile, boolean recurse) { |
| if (!isPlanProject(planFile)) { |
| return Collections.emptyList(); |
| } |
| |
| // parse the top level plan file |
| PlanReader reader = new PlanReader(); |
| Plan topLevelPlan; |
| try { |
| topLevelPlan = reader.read(planFile); |
| } catch (Exception e1) { |
| return Collections.emptyList(); |
| } |
| |
| List<PlanReference> nestedReferences = topLevelPlan.getNestedPlans(); |
| |
| if (nestedReferences.isEmpty()) { |
| return Collections.emptyList(); |
| } |
| |
| List<IFile> nestedPlanFiles = new ArrayList<IFile>(); |
| |
| /* |
| * examine the containing Java project class path (if a Java projecT) and find all the required Java projects. |
| * Nested plans will be searched not only in the current project but also in its dependencies |
| */ |
| List<IProject> orderedProjects = getOrderedProjectDependencies(planFile.getProject()); |
| orderedProjects.add(0, planFile.getProject()); |
| |
| Map<IProject, Collection<IFile>> allPlans = FacetUtils.getPlansInWorkspace(); |
| |
| // used for searching a nested plan that is referred only by name (no version) |
| Map<String, Plan> name2PlanLookup = new HashMap<String, Plan>(); |
| |
| // used for searching a nested plan that is referred by name and version |
| Map<PlanReference, Plan> ref2Plan = new HashMap<PlanReference, Plan>(); |
| |
| // plan to related file |
| Map<PlanReference, IFile> ref2File = new HashMap<PlanReference, IFile>(); |
| |
| // loop over the list of ordered projects and search for plans |
| for (IProject iProject : orderedProjects) { |
| Collection<IFile> candidates = allPlans.get(iProject); |
| if (candidates != null) { |
| for (IFile iFile : candidates) { |
| // ignore self |
| if (!planFile.equals(iFile)) { |
| try { |
| Plan p = reader.read(iFile); |
| PlanReference r = p.asRefence(); |
| |
| /* |
| * in case of duplicate plans (same name and version) first found in classpath wins and |
| * found is assumed to be the right one |
| */ |
| if (!ref2Plan.containsKey(r)) { |
| // add for name+version lookup |
| ref2Plan.put(r, p); |
| |
| /* |
| * in case of duplicates plans with the same name and different version if an outer plan |
| * is referring to a nested plan via name only, the first found in classpath wins |
| */ |
| if (!name2PlanLookup.containsKey(r.getName())) { |
| name2PlanLookup.put(r.getName(), p); |
| } |
| |
| ref2File.put(r, iFile); |
| |
| } |
| } catch (Exception e) { |
| // ignore |
| } |
| } |
| } |
| } |
| } |
| |
| // finally compute the list of nested plans. Use a queue instead of recursion. |
| Queue<PlanReference> toBeProcessed = new ArrayBlockingQueue<PlanReference>(ref2Plan.size() + 1); |
| Set<PlanReference> alreadyProcessed = new HashSet<PlanReference>(); |
| toBeProcessed.addAll(nestedReferences); |
| |
| while (toBeProcessed.peek() != null) { |
| PlanReference planReference = toBeProcessed.poll(); |
| alreadyProcessed.add(planReference); |
| |
| // search for exact match name + version |
| Plan nestedPlan = ref2Plan.get(planReference); // search for exact match name + version |
| if (nestedPlan == null && planReference.getVersion() == null) { |
| nestedPlan = name2PlanLookup.get(planReference.getName()); |
| } |
| |
| if (nestedPlan != null) { |
| IFile nestedFile = ref2File.get(nestedPlan.asRefence()); |
| |
| nestedPlanFiles.add(nestedFile); |
| |
| for (PlanReference aRef : nestedPlan.getNestedPlans()) { |
| if (recurse && !alreadyProcessed.contains(aRef)) { |
| toBeProcessed.add(aRef); |
| } |
| } |
| } |
| } |
| |
| return nestedPlanFiles; |
| } |
| |
| /** |
| * Returns the ordered list of project dependencies for the given Java project or an empty list if the project is |
| * not a Java project. |
| * |
| * @param project the Java project |
| * @return the list of required projects |
| */ |
| private static List<IProject> getOrderedProjectDependencies(IProject project) { |
| LinkedHashSet<IProject> projects = new LinkedHashSet<IProject>(); |
| if (FacetUtils.hasNature(project, JavaCore.NATURE_ID)) { |
| IJavaProject je = JavaCore.create(project); |
| String[] names; |
| try { |
| names = je.getRequiredProjectNames(); |
| for (String prjName : names) { |
| IProject prj = ResourcesPlugin.getWorkspace().getRoot().getProject(prjName); |
| if (prj.exists() && prj.isOpen() && isPlanProject(prj)) { |
| projects.add(prj); |
| projects.addAll(getOrderedProjectDependencies(prj)); |
| } |
| } |
| } catch (JavaModelException e) { |
| } |
| } |
| return new ArrayList<IProject>(projects); |
| } |
| |
| } |