/**
* Copyright (c) 2011 Cloudsmith Inc. and other contributors, as listed below.
* 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:
* Cloudsmith
*
*/
package org.cloudsmith.geppetto.pp.dsl.ui.pptp;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.apache.log4j.Logger;
import org.cloudsmith.geppetto.common.util.BundleAccess;
import org.cloudsmith.geppetto.pp.dsl.target.PptpResourceUtil;
import org.cloudsmith.geppetto.pp.dsl.target.PuppetTarget;
import org.cloudsmith.geppetto.pp.dsl.ui.PPUiConstants;
import org.cloudsmith.geppetto.pp.dsl.ui.preferences.PPPreferencesHelper;
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.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.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.util.URI;
import org.eclipse.xtext.ui.XtextProjectHelper;
import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* Handler of the hidden puppet target project.
* Is used to check the state of the workspace and all projects with puppet nature.
*
*/
@Singleton
public class PptpTargetProjectHandler {
@Inject
PPPreferencesHelper preferenceHelper;
@Inject
IPreferenceStoreAccess storeAccess;
@Inject
BundleAccess bundleAccess;
private final static Logger log = Logger.getLogger(PptpTargetProjectHandler.class);
/**
* Ensures that all projects with the PuppetNature has a dependency on the project
* defining the target platform.
* Calls {@link #ensureTargetProjectConfiguration()} if the target project is not created.
*/
public void ensureStateOfPuppetProjects(IProgressMonitor monitor) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IProject targetProject = workspace.getRoot().getProject(PPUiConstants.PPTP_TARGET_PROJECT_NAME);
ensureTargetProjectConfiguration(monitor);
IProject[] projects = workspace.getRoot().getProjects();
SubMonitor ticker = SubMonitor.convert(monitor, projects.length);
PROJECTS: for(IProject p : projects) {
ticker.worked(1);
// skip the target project itself, do not want to create a circular dependency
if(p.equals(targetProject))
continue;
if(!p.isAccessible())
continue;
try {
if(p.hasNature(PPUiConstants.PUPPET_NATURE_ID)) {
IProjectDescription desc = p.getDescription();
IProject[] dynamicReferences = desc.getDynamicReferences();
for(int i = 0; i < dynamicReferences.length; i++) {
if(dynamicReferences[i].equals(targetProject))
continue PROJECTS;
}
IProject[] newDynamicReferences = new IProject[dynamicReferences.length + 1];
System.arraycopy(dynamicReferences, 0, newDynamicReferences, 1, dynamicReferences.length);
newDynamicReferences[0] = targetProject;
desc.setDynamicReferences(newDynamicReferences);
p.setDescription(desc, monitor);
}
}
catch(CoreException e) {
log.error("Could not determine nature of project: '" + p.getName() + "'", e);
}
}
}
public void ensureTargetProjectConfiguration(IProgressMonitor monitor) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
// While developing, keep this here to enable removal of hidden project in safe way
IProject oldTargetProject = workspace.getRoot().getProject(PPUiConstants.OLD_PPTP_TARGET_PROJECT_NAME);
if(oldTargetProject.exists())
try {
oldTargetProject.delete(true, monitor);
}
catch(CoreException e) {
// ignore
}
IProject targetProject = workspace.getRoot().getProject(PPUiConstants.PPTP_TARGET_PROJECT_NAME);
// make sure project exists
if(!targetProject.exists()) {
try {
// TODO: Aaaargh: hidden projects are not built, needs to be hidden by using a name starting with "."
// create (and hide it), and set natures
// If not hidden this way, it may be displayed (and there is a race with setting it hidden
// using setHidden(true). This avoids the race.
// targetProject.create(null, IResource.HIDDEN, monitor);
targetProject.create(monitor);
}
catch(CoreException e) {
log.error("Could not create target platform project.", e);
return;
}
}
// make sure project is open
try {
if(!targetProject.isAccessible())
targetProject.open(monitor);
}
catch(CoreException e) {
log.error("Could not open target project!", e);
}
// make sure it is configured with the correct natures
try {
targetProject.setHidden(false);
IProjectDescription desc = targetProject.getDescription();
desc.setNatureIds(new String[] { XtextProjectHelper.NATURE_ID, PPUiConstants.PUPPET_NATURE_ID });
targetProject.setDescription(desc, monitor);
}
catch(CoreException e) {
log.error("Failed to configure target project", e);
}
URI uri;
try {
uri = PuppetTarget.forLiteral(preferenceHelper.getPptpVersion()).getPlatformURI();
}
catch(IllegalArgumentException e) {
log.error(e.getMessage());
uri = PuppetTarget.getDefault().getPlatformURI();
}
sync(targetProject, uri, "puppet-", monitor);
sync(targetProject, PptpResourceUtil.getFacter_1_6(), "facter-", monitor);
}
public void initializePuppetTargetProject() {
Job job = new Job("Checking Puppet Projects") {
@Override
protected IStatus run(IProgressMonitor monitor) {
monitor.beginTask("Checking Puppet Projects ...", 100);
ensureStateOfPuppetProjects(monitor);
monitor.done();
return Status.OK_STATUS;
}
};
job.setSystem(false);
job.setPriority(Job.INTERACTIVE);
job.schedule();
}
public void initializePuppetWorkspace() {
Job job = new Job("Initializing Puppet Workspace") {
@Override
protected IStatus run(IProgressMonitor monitor) {
monitor.beginTask("Checking Puppet Projects ...", 100);
// ensure that store is initialized before doing anything else
preferenceHelper.initialize(storeAccess);
ensureStateOfPuppetProjects(monitor);
monitor.done();
return Status.OK_STATUS;
}
};
job.setSystem(false);
job.setPriority(Job.INTERACTIVE);
job.schedule();
}
private void sync(IProject targetProject, URI uri, String prefix, IProgressMonitor monitor) {
// get a handle to the wanted target platform (.pptp) file from preferences
//
try {
targetProject.refreshLocal(IFile.DEPTH_INFINITE, monitor);
File pptpFile = bundleAccess.getResourceAsFile(new URL(uri.toString()));
IFile targetFile = targetProject.getFile(pptpFile.getName());
if(targetFile.exists()) {
if(pptpFile.lastModified() > targetFile.getLocalTimeStamp()) {
targetFile.delete(IFile.FORCE | IFile.ALWAYS_DELETE_PROJECT_CONTENT, monitor);
targetFile.refreshLocal(IFile.DEPTH_ZERO, monitor);
targetFile.create(new FileInputStream(pptpFile), true, monitor);
}
}
else {
// delete all prefix-* resources already there (either none, or some other/older .pptp)
// this makes it possible to keep several that are not managed
for(IResource r : targetProject.members()) {
if(r.getName().startsWith(prefix))
r.delete(IFile.FORCE | IFile.ALWAYS_DELETE_PROJECT_CONTENT, monitor);
}
InputStream inputStream = new FileInputStream(pptpFile);
targetFile.create(inputStream, true, monitor);
}
}
catch(IOException e) {
log.error("Could not get .pptp default for copying.", e);
}
catch(CoreException e) {
log.error("Could not perform operation on target .pptp file.", e);
}
}
}