package com.dubture.composer.core.model; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; 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.DLTKCore; import org.eclipse.dltk.core.IBuildpathContainer; import org.eclipse.dltk.core.IBuildpathEntry; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.internal.core.ModelManager; import org.eclipse.dltk.internal.core.util.Util; import org.eclipse.php.internal.core.project.PHPNature; import org.eclipse.ui.statushandlers.StatusManager; import org.osgi.service.prefs.BackingStoreException; import com.dubture.composer.core.ComposerBuildpathContainerInitializer; import com.dubture.composer.core.ComposerNature; import com.dubture.composer.core.ComposerPlugin; import com.dubture.composer.core.log.Logger; @SuppressWarnings("restriction") 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#"; public final static String BP_PROJECT_BUILDPATH_DEV_PREFIX = ComposerPlugin.ID + ".projectDevPackages#"; 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("#"); 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) { StatusManager.getManager().handle(e.getStatus()); } 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("Updating composer buildpath"); installedPath = new Path("vendor/composer/installed.json"); installedDevPath = new Path("vendor/composer/installed_dev.json"); } public void setProject(IProject project) { this.project = project; } private void installLocalPackage(InstalledPackage installedPackage, IProject project) { IPath path = new Path("vendor").append(installedPackage.name); Logger.debug("Installing local version of " + installedPackage.getFullName()); 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 " + installedPackage.getLocalFile().getAbsolutePath()); installedPackage.getLocalFile().mkdirs(); FileUtils.copyDirectory(file, installedPackage.getLocalFile()); } catch (IOException e) { Logger.logException(e); } } } else { Logger.debug("Unable to find folder in project for path " + path.toString()); } } @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 " + 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(); IOUtils.copy(file.getContents(), writer); String propertyValue = writer.toString(); 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()); for (InstalledPackage installedPackage : packages) { if (!installedPackage.isLocalVersionAvailable()) { installLocalPackage(installedPackage, project); } else { Logger.debug(installedPackage.getFullName() + " is already installed locally"); } } } @Override protected IStatus run(IProgressMonitor monitor) { Logger.debug("Running buildpath job"); running = true; monitor.setTaskName("Updating composer buildpath..."); 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"); throw new InterruptedException(); } if (!project.hasNature(ComposerNature.NATURE_ID)) { Logger.debug("Buildpath not running on project without composer nature " + project.getName()); return; } IFile installed = (IFile) project.findMember(installedPath); if (installed == null) { Logger.debug("Unabled to find installed.json in project " + project.getName()); 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); } } }