/*******************************************************************************
* Copyright (c) 2012, 2016, 2017 PDT Extension Group 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:
* PDT Extension Group - initial API and implementation
* Kaloyan Raev - [501269] externalize strings
*******************************************************************************/
package org.eclipse.php.composer.core.model;
import java.io.*;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.internal.core.ModelManager;
import org.eclipse.dltk.internal.core.util.Util;
import org.eclipse.php.composer.core.ComposerBuildpathContainerInitializer;
import org.eclipse.php.composer.core.ComposerPlugin;
import org.eclipse.php.composer.core.facet.FacetManager;
import org.eclipse.php.composer.core.log.Logger;
import org.eclipse.php.internal.core.project.PHPNature;
import org.osgi.service.prefs.BackingStoreException;
public class PackageManager {
private Map<String, BuildpathPackage> packages;
/**
* Maps project to installed local packages
*/
private Map<String, List<InstalledPackage>> installedPackages;
private Map<String, List<InstalledPackage>> installedDevPackages;
public final static String BP_COMPOSERPACKAGE_PREFERENCES_PREFIX = ComposerPlugin.ID + ".composerPackage."; //$NON-NLS-1$
public final static String BP_PROJECT_BUILDPATH_PREFIX = ComposerPlugin.ID + ".projectPackages#"; //$NON-NLS-1$
public final static String BP_PROJECT_BUILDPATH_DEV_PREFIX = ComposerPlugin.ID + ".projectDevPackages#"; //$NON-NLS-1$
private BuildpathJob buildpathJob;
public PackageManager() {
initialize();
}
private void reloadPackages() {
IEclipsePreferences instancePreferences = ConfigurationScope.INSTANCE.getNode(ComposerPlugin.ID);
String[] propertyNames;
try {
propertyNames = instancePreferences.keys();
} catch (BackingStoreException e) {
Util.log(e, "Exception while initializing user libraries"); //$NON-NLS-1$
return;
}
for (int i = 0, length = propertyNames.length; i < length; i++) {
String propertyName = propertyNames[i];
if (propertyName.startsWith(BP_PROJECT_BUILDPATH_PREFIX)) {
String propertyValue = instancePreferences.get(propertyName, null);
if (propertyValue != null) {
try {
List<InstalledPackage> packages = InstalledPackage.deserialize(propertyValue);
installedPackages.put(unpackProjectName(propertyName), packages);
} catch (IOException e) {
Logger.logException(e);
}
}
} else if (propertyName.startsWith(BP_PROJECT_BUILDPATH_DEV_PREFIX)) {
String propertyValue = instancePreferences.get(propertyName, null);
if (propertyValue != null) {
try {
List<InstalledPackage> packages = InstalledPackage.deserialize(propertyValue);
installedDevPackages.put(unpackProjectName(propertyName), packages);
} catch (IOException e) {
Logger.logException(e);
}
}
}
}
}
private String unpackProjectName(String propertyName) {
String[] strings = propertyName.split("#"); //$NON-NLS-1$
return strings[1];
}
private void initialize() {
packages = new HashMap<String, BuildpathPackage>();
installedPackages = new HashMap<String, List<InstalledPackage>>();
installedDevPackages = new HashMap<String, List<InstalledPackage>>();
IEclipsePreferences instancePreferences = ConfigurationScope.INSTANCE.getNode(ComposerPlugin.ID);
String[] propertyNames;
try {
propertyNames = instancePreferences.keys();
} catch (BackingStoreException e) {
Util.log(e, "Exception while initializing user libraries"); //$NON-NLS-1$
return;
}
boolean preferencesNeedFlush = false;
for (int i = 0, length = propertyNames.length; i < length; i++) {
String propertyName = propertyNames[i];
if (propertyName.startsWith(BP_COMPOSERPACKAGE_PREFERENCES_PREFIX)) {
String propertyValue = instancePreferences.get(propertyName, null);
if (propertyValue != null) {
String libName = propertyName.substring(BP_COMPOSERPACKAGE_PREFERENCES_PREFIX.length());
StringReader reader = new StringReader(propertyValue);
BuildpathPackage library;
try {
library = BuildpathPackage.createFromString(reader);
} catch (Exception e) {
Util.log(e, "Exception while initializing user library " + libName); //$NON-NLS-1$
instancePreferences.remove(propertyName);
preferencesNeedFlush = true;
continue;
}
packages.put(libName, library);
}
}
}
if (preferencesNeedFlush) {
try {
instancePreferences.flush();
} catch (BackingStoreException e) {
Util.log(e, "Exception while flusing instance preferences"); //$NON-NLS-1$
}
}
buildpathJob = new BuildpathJob();
reloadPackages();
}
public synchronized void setPackage(String name, IBuildpathEntry[] buildpathEntries, boolean isSystemLibrary) {
IEclipsePreferences prefs = ConfigurationScope.INSTANCE.getNode(ComposerPlugin.ID);
String propertyName = BP_COMPOSERPACKAGE_PREFERENCES_PREFIX + makePackageName(name);
try {
String propertyValue = BuildpathPackage.serialize(buildpathEntries, isSystemLibrary);
prefs.put(propertyName, propertyValue);
prefs.flush();
} catch (BackingStoreException e) {
Logger.logException(e);
} catch (IOException e) {
Logger.logException(e);
}
}
public void removePackage(String name) {
try {
IEclipsePreferences preferences = ConfigurationScope.INSTANCE.getNode(ComposerPlugin.ID);
String propertyName = BP_COMPOSERPACKAGE_PREFERENCES_PREFIX + makePackageName(name);
preferences.remove(propertyName);
preferences.flush();
} catch (BackingStoreException e) {
Util.log(e, "Exception while removing user library " + name); //$NON-NLS-1$
}
}
public BuildpathPackage getPackage(String packageName) {
if (!packages.containsKey(packageName)) {
return null;
}
return (BuildpathPackage) packages.get(makePackageName(packageName));
}
private Object makePackageName(String packageName) {
return PHPNature.ID + "#" + packageName; //$NON-NLS-1$
}
private String getPackageName(String key) {
int pos = key.indexOf("#"); //$NON-NLS-1$
if (pos != -1) {
return key.substring(pos + 1);
}
return key;
}
public synchronized String[] getPackageNames() {
Set<String> set = packages.keySet();
Set<String> result = new HashSet<String>();
for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
result.add(getPackageName(key));
}
return (String[]) result.toArray(new String[result.size()]);
}
public PackagePath[] getPackagePaths(IScriptProject project) {
List<PackagePath> packagePaths = new ArrayList<PackagePath>();
try {
IBuildpathContainer container = ModelManager.getModelManager()
.getBuildpathContainer(new Path(ComposerBuildpathContainerInitializer.CONTAINER), project);
for (IBuildpathEntry entry : container.getBuildpathEntries()) {
packagePaths.add(new PackagePath(entry, project));
}
} catch (ModelException e) {
Logger.logException(e.getStatus().getMessage(), e);
}
return packagePaths.toArray(new PackagePath[packagePaths.size()]);
}
public void updateBuildpath() {
if (buildpathJob == null) {
return;
}
synchronized (buildpathJob) {
buildpathJob.cancel();
buildpathJob.setPriority(Job.LONG);
buildpathJob.schedule(1000);
}
}
public void updateBuildpath(IProject project) {
if (buildpathJob == null) {
return;
}
synchronized (buildpathJob) {
buildpathJob.cancel();
buildpathJob.setPriority(Job.LONG);
buildpathJob.setProject(project);
buildpathJob.schedule(1000);
}
}
private class BuildpathJob extends Job {
private IPath installedPath;
private IPath installedDevPath;
private IProject project;
private boolean running;
public BuildpathJob() {
super(Messages.PackageManager_BuildPathJobName);
installedPath = new Path("vendor/composer/installed.json"); //$NON-NLS-1$
installedDevPath = new Path("vendor/composer/installed_dev.json"); //$NON-NLS-1$
}
public void setProject(IProject project) {
this.project = project;
}
private void installLocalPackage(InstalledPackage installedPackage, IProject project) {
IPath path = new Path("vendor").append(installedPackage.name); //$NON-NLS-1$
Logger.debug("Installing local version of " + installedPackage.getFullName()); //$NON-NLS-1$
IResource resource = project.findMember(path);
if (resource instanceof IFolder) {
IFolder folder = (IFolder) resource;
File file = folder.getRawLocation().makeAbsolute().toFile();
if (file != null && file.exists() && installedPackage.getLocalFile() != null) {
try {
Logger.debug("Installing local package " + installedPackage.name + " to " //$NON-NLS-1$//$NON-NLS-2$
+ installedPackage.getLocalFile().getAbsolutePath());
installedPackage.getLocalFile().mkdirs();
final java.nio.file.Path sourcePath = file.toPath();
final java.nio.file.Path packagePath = installedPackage.getLocalFile().toPath();
SimpleFileVisitor<java.nio.file.Path> n = new SimpleFileVisitor<java.nio.file.Path>() {
@Override
public FileVisitResult preVisitDirectory(java.nio.file.Path dirPath,
BasicFileAttributes attrs) throws IOException {
java.nio.file.Path targetPath = packagePath.resolve(sourcePath.relativize(dirPath));
if (!Files.exists(targetPath)) {
Files.createDirectory(targetPath);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(java.nio.file.Path filePath, BasicFileAttributes attrs)
throws IOException {
Files.copy(filePath, packagePath.resolve(sourcePath.relativize(filePath)),
StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
};
Files.walkFileTree(file.toPath(), n);
} catch (IOException e) {
Logger.logException(e);
}
}
} else {
Logger.debug("Unable to find folder in project for path " + path.toString()); //$NON-NLS-1$
}
}
@Override
protected void canceling() {
super.canceling();
running = false;
}
private void handleDevPackages(IProject project) throws Exception {
handlePackages(project, BP_PROJECT_BUILDPATH_DEV_PREFIX + project.getName(), installedDevPath);
}
private void handleProdPackages(IProject project) throws Exception {
handlePackages(project, BP_PROJECT_BUILDPATH_PREFIX + project.getName(), installedPath);
}
private void handlePackages(IProject project, String propertyName, IPath path) throws Exception {
IFile installed = (IFile) project.findMember(path);
if (installed == null) {
Logger.debug("Unable to find '" + path.lastSegment() + "' in " + project.getName() + " using path " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ path.toString());
return;
}
List<InstalledPackage> json = InstalledPackage.deserialize(installed.getContents());
installPackages(json, project);
persist(propertyName, installed);
}
private void persist(String key, IFile file) throws IOException, CoreException, BackingStoreException {
IEclipsePreferences prefs = ConfigurationScope.INSTANCE.getNode(ComposerPlugin.ID);
StringWriter writer = new StringWriter();
InputStream contents = file.getContents();
Scanner s = new Scanner(contents).useDelimiter("\\A"); //$NON-NLS-1$
String propertyValue = s.hasNext() ? s.next() : ""; //$NON-NLS-1$
contents.close();
prefs.put(key, propertyValue);
prefs.flush();
writer.close();
}
private void installPackages(List<InstalledPackage> packages, IProject project) {
Logger.debug("Installing local packages for project " + project.getName()); //$NON-NLS-1$
for (InstalledPackage installedPackage : packages) {
if (!installedPackage.isLocalVersionAvailable()) {
installLocalPackage(installedPackage, project);
} else {
Logger.debug(installedPackage.getFullName() + " is already installed locally"); //$NON-NLS-1$
}
}
}
@Override
protected IStatus run(IProgressMonitor monitor) {
Logger.debug("Running buildpath job"); //$NON-NLS-1$
running = true;
monitor.setTaskName(Messages.PackageManager_BuildPathTaskName);
if (project != null) {
updateProject(project);
monitor.worked(1);
} else {
for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
updateProject(project);
monitor.worked(1);
}
}
reloadPackages();
return Status.OK_STATUS;
}
protected void updateProject(IProject project) {
try {
if (!running) {
Logger.debug("Job cancelled"); //$NON-NLS-1$
throw new InterruptedException();
}
if (!FacetManager.hasComposerFacet(project)) {
Logger.debug("Buildpath not running on project without composer nature " + project.getName()); //$NON-NLS-1$
return;
}
IFile installed = (IFile) project.findMember(installedPath);
if (installed == null) {
Logger.debug("Unabled to find installed.json in project " + project.getName()); //$NON-NLS-1$
return;
}
handleProdPackages(project);
handleDevPackages(project);
DLTKCore.refreshBuildpathContainers(DLTKCore.create(project));
} catch (Exception e) {
Logger.logException(e);
}
}
}
public List<InstalledPackage> getInstalledPackages(IScriptProject project) {
if (installedPackages.containsKey(project.getProject().getName())) {
return installedPackages.get(project.getProject().getName());
}
return null;
}
public List<InstalledPackage> getInstalledDevPackages(IScriptProject project) {
if (installedDevPackages.containsKey(project.getProject().getName())) {
return installedDevPackages.get(project.getProject().getName());
}
return null;
}
public List<InstalledPackage> getAllPackages(IScriptProject project) {
List<InstalledPackage> allPackages = new ArrayList<InstalledPackage>();
if (!installedPackages.containsKey(project.getProject().getName())) {
return allPackages;
}
for (InstalledPackage pack : installedPackages.get(project.getProject().getName())) {
pack.isDev = false;
allPackages.add(pack);
}
if (installedDevPackages.containsKey(project.getProject().getName())) {
for (InstalledPackage pack : installedDevPackages.get(project.getProject().getName())) {
pack.isDev = true;
allPackages.add(pack);
}
}
return allPackages;
}
public void removeProject(IProject project) {
try {
String name = project.getName();
String propertyName = BP_PROJECT_BUILDPATH_PREFIX + name;
IEclipsePreferences instancePreferences = ConfigurationScope.INSTANCE.getNode(ComposerPlugin.ID);
instancePreferences.remove(propertyName);
instancePreferences.flush();
if (installedPackages.containsKey(name)) {
installedPackages.remove(name);
}
} catch (BackingStoreException e) {
Logger.logException(e);
}
}
}