/* * Copyright (c) 2014, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.core.pub; import com.google.common.base.Objects; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.core.utilities.yaml.PubYamlUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Maintains a list of all the used packages (all the versions) used by all of the workspace * projects. * * @coverage dart.tools.core.pub */ public class PubCacheManager_NEW { /** * Information about a referenced package. */ public static class PackageInfo { public final String name; public final String version; private IProject project; public PackageInfo(String name, String version, IProject project) { this.name = name; this.version = version; this.project = project; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof PackageInfo) { PackageInfo other = (PackageInfo) obj; return Objects.equal(other.name, name) && Objects.equal(other.version, version); } return false; } public IProject getProject() { return project; } @Override public int hashCode() { return Objects.hashCode(name, version); } @Override public String toString() { return "PackageInfo(" + name + ", " + version + ", " + project + ")"; } } /** * Information about a package and version. */ public static class PackageVersion { public final String name; public final String version; public PackageVersion(String name, String version) { this.name = name; this.version = version; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof PackageVersion) { PackageVersion other = (PackageVersion) obj; return Objects.equal(other.name, name) && Objects.equal(other.version, version); } return false; } @Override public int hashCode() { return Objects.hashCode(name, version); } @Override public String toString() { return "PackageVersion(" + name + ", " + version + ")"; } } private class FillPubCacheList extends Job { public FillPubCacheList() { super("Update references packages"); } @Override public IStatus run(IProgressMonitor monitor) { Map<PackageVersion, InstalledPackage> installedPackages = readInstalledPackages(); // prepare old map Map<PackageInfo, PackageInfo> oldPackageMap = Maps.newHashMap(); for (PackageInfo pkg : packages) { oldPackageMap.put(pkg, pkg); } // Set<PackageInfo> newPackages = prepareUsedPackages(); for (Iterator<PackageInfo> I = newPackages.iterator(); I.hasNext();) { PackageInfo newPackage = I.next(); PackageInfo oldPackage = oldPackageMap.remove(newPackage); // old package, copy project if (oldPackage != null) { newPackage.project = oldPackage.project; continue; } // new package, try to create project { String projectName = newPackage.name + "_" + newPackage.version; InstalledPackage installedPackage = installedPackages.get(new PackageVersion( newPackage.name, newPackage.version)); if (installedPackage != null) { String projectLocation = installedPackage.location; newPackage.project = createExternalProject(projectName, projectLocation); } } // if still no project, remove it if (newPackage.project == null) { I.remove(); } } // remove package projects that are not used anymore for (PackageInfo oldPackage : oldPackageMap.keySet()) { IProject project = oldPackage.project; try { project.delete(true, null); } catch (CoreException e) { } } // done packages = newPackages; PubManager.getInstance().notifyListeners((Map<String, Object>) null); return Status.OK_STATUS; } } private static class InstalledPackage { public final String name; public final String version; public final String location; public InstalledPackage(String name, String version, String location) { this.name = name; this.version = version; this.location = location; } } static final QualifiedName PACKAGE_CACHE_PROJECT = new QualifiedName( DartCore.PLUGIN_ID, "packageCacheProject"); private static PubCacheManager_NEW instance; public static final synchronized PubCacheManager_NEW getInstance() { if (instance == null) { instance = new PubCacheManager_NEW(); } return instance; } public static boolean isPubCacheProject(IProject project) { try { return project.getPersistentProperty(PACKAGE_CACHE_PROJECT) != null; } catch (CoreException e) { return false; } } public static boolean isPubCacheResource(IResource resource) { if (resource != null) { IProject project = resource.getProject(); return isPubCacheProject(project); } return false; } private static IProject createExternalProject(String name, String location) { try { IWorkspace workspace = ResourcesPlugin.getWorkspace(); // prepare project IProject project = workspace.getRoot().getProject(name); if (project.exists()) { return project; } // prepare description IProjectDescription description = workspace.newProjectDescription(name); description.setLocation(new Path(location)); // create the project if (!project.exists()) { project.create(description, null); } project.open(null); project.setPersistentProperty(PACKAGE_CACHE_PROJECT, "TRUE"); // done return project; } catch (CoreException e) { DartCore.logError("Unable to create project " + name + " at " + location, e); return null; } } private static IProject[] getProjects() { try { return ResourcesPlugin.getWorkspace().getRoot().getProjects(); } catch (IllegalStateException e) { // The workspace is shutting down so return an empty list return new IProject[] {}; } } private static String getPubCacheList() { RunPubCacheListJob job = new RunPubCacheListJob(); return job.run(new NullProgressMonitor()).getMessage(); } private Set<PackageInfo> packages = Sets.newHashSet(); public PubCacheManager_NEW() { updatePackagesList(2000); } public PackageInfo[] getPackages() { return packages.toArray(new PackageInfo[packages.size()]); } public void updatePackagesList(int delay) { new FillPubCacheList().schedule(delay); } /** * Parses the given "lockFile" and adds new {@link PackageVersion}s. */ private void addUsedPackages(Set<PackageInfo> usedPackages, IFile lockFile) { Map<String, String> versionMap = PubYamlUtils.getPackageVersionMap(lockFile); Set<Entry<String, String>> entrySet = versionMap.entrySet(); for (Entry<String, String> entry : entrySet) { String name = entry.getKey(); String version = entry.getValue(); usedPackages.add(new PackageInfo(name, version, null)); } } /** * Returns the {@link Set} of packages used by the workspace projects. */ private Set<PackageInfo> prepareUsedPackages() { final Set<PackageInfo> packages = Sets.newHashSet(); IProject[] projects = getProjects(); for (IProject project : projects) { // ignore artificial package projects if (isPubCacheProject(project)) { continue; } // scan and parse all pubspec.lock files try { project.accept(new IResourceVisitor() { @Override public boolean visit(IResource resource) throws CoreException { if (resource instanceof IFile) { IFile file = (IFile) resource; if (file.getName().equals(DartCore.PUBSPEC_LOCK_FILE_NAME)) { addUsedPackages(packages, file); } return false; } return true; } }); } catch (Throwable e) { } } return packages; } private Map<PackageVersion, InstalledPackage> readInstalledPackages() { Map<PackageVersion, InstalledPackage> installedPackages = Maps.newHashMap(); // ask Pub String message = getPubCacheList(); if (message.startsWith("{\"packages")) { try { Map<String, Object> object = PubYamlUtils.parsePubspecYamlToMap(message); @SuppressWarnings("unchecked") Map<String, Object> rawMap = (Map<String, Object>) object.get(PubspecConstants.PACKAGES); for (Entry<String, Object> pkgEntry : rawMap.entrySet()) { String name = pkgEntry.getKey(); if (pkgEntry.getValue() instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> rawVersionsMap = (Map<String, Object>) pkgEntry.getValue(); for (Entry<String, Object> versionEntry : rawVersionsMap.entrySet()) { String version = versionEntry.getKey(); @SuppressWarnings("unchecked") Map<String, Object> rawPropertiesMap = (Map<String, Object>) versionEntry.getValue(); String location = (String) rawPropertiesMap.get(PubspecConstants.LOCATION); installedPackages.put(new PackageVersion(name, version), new InstalledPackage( name, version, location)); } } } } catch (Throwable e) { DartCore.logError("Error while parsing pub cache list", e); } } else { DartCore.logError(message); } // done return installedPackages; } }