/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* 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:
* Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
/*
* Created on Nov 1, 2005
*/
package org.eclipse.imp.builder;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.imp.runtime.IPluginLog;
public abstract class ProjectNatureBase implements IProjectNature {
private IProject fProject;
public ProjectNatureBase() {}
public abstract String getNatureID();
public abstract String getBuilderID();
public abstract IPluginLog getLog();
/**
* Refresh the preferences, to make sure the project settings are up to date.
* Derived classes must implement.
*/
protected abstract void refreshPrefs();
/**
* Returns the ID of the builder that processes the artifacts that this
* nature's builder produces. If there is no such dependency, returns null.
*/
// TODO this should be a property of the builder itself...
protected String getDownstreamBuilderID() {
return null; // by default, no such dependency
}
/**
* Returns the ID of the builder that produces artifacts that this nature's
* builder consumes. If there is no such dependency, returns null.
*/
// TODO this should be a property of the builder itself...
protected String getUpstreamBuilderID() {
return null; // by default, no such dependency
}
public void addToProject(IProject project) {
String natureID= getNatureID();
refreshPrefs();
// SMS 16 Apr 2007
// You can find log null here if the plugin hasn't been started yet.
// It is intended that the manifest file for the IDE plugin should have autostart
// set to true, in which case the log should not come back null, but if the
// manifest file has been corrupted (which can happen if you regenerate services
// over prior implementations) then the autostart property may be lost and the
// pluin may not be started yet.
IPluginLog log = getLog();
log.maybeWriteInfoMsg("Attempting to add nature " + natureID);
try {
IProjectDescription description= project.getDescription();
String[] natures= description.getNatureIds();
String[] newNatures= new String[natures.length + 1];
System.arraycopy(natures, 0, newNatures, 0, natures.length);
newNatures[natures.length]= natureID;
description.setNatureIds(newNatures);
project.setDescription(description, null);
// At this point, this nature's builder should be in the project description,
// but since the description holds only nature ID's, the Eclipse framework ends
// up instantiating the nature itself and calling configure() on that instance.
// It uses the default (no-arg) ctor to do this, so that instance won't have
// enough info to properly populate the builder arguments, if any. So: we need
// to find the builder now and set its arguments using getBuilderArguments().
// N.B.: As an added twist, we have to ask Eclipse for the project description
// again, rather than using the one we got above, since the latter won't have
// the builder in it.
IProjectDescription newDesc= project.getDescription();
ICommand[] builders= newDesc.getBuildSpec();
String builderID= getBuilderID();
for(int i= 0; i < builders.length; i++) {
if (builders[i].getBuilderName().equals(builderID)) {
builders[i].setArguments(getBuilderArguments());
}
}
newDesc.setBuildSpec(builders);
project.setDescription(newDesc, null);
getLog().maybeWriteInfoMsg("Added nature " + natureID);
} catch (CoreException e) {
// Something went wrong
getLog().writeErrorMsg("Failed to add nature " + natureID + ": " + e.getMessage());
}
}
public void configure() throws CoreException {
IProjectDescription desc= getProject().getDescription();
String builderID= getBuilderID();
ICommand[] cmds= desc.getBuildSpec();
// Check: is the builder already in this project?
for(int i=0; i < cmds.length; i++) {
if (cmds[i].getBuilderName().equals(builderID))
return; // relevant command is already in there...
}
int beforeWhere= cmds.length;
String downstreamBuilderID= getDownstreamBuilderID();
if (downstreamBuilderID != null) {
// Since this builder produces artifacts that another one will
// post-process, it needs to run *before* that one.
// So, find the right spot (in front of that builder) to put this one.
for(beforeWhere--; beforeWhere >= 0; beforeWhere--) {
if (cmds[beforeWhere].getBuilderName().equals(downstreamBuilderID))
break; // found it
}
if (beforeWhere < 0) {
getLog().writeErrorMsg("Unable to find downstream builder '" + downstreamBuilderID + "' for builder '" + builderID + "'.");
beforeWhere= 0; // be safe
}
}
int afterWhere= -1;
String upstreamBuilderID= getUpstreamBuilderID();
if (upstreamBuilderID != null) {
// This builder consumes artifacts that another one will produce,
// so it needs to run *after* that one.
// So, find the right spot (after that builder) to put this one.
for(afterWhere= 0; afterWhere < cmds.length; afterWhere++) {
if (cmds[afterWhere].getBuilderName().equals(upstreamBuilderID))
break; // found it
}
if (afterWhere == cmds.length) {
getLog().writeErrorMsg("Unable to find upstream builder '" + upstreamBuilderID + "' for builder '" + builderID + "'.");
afterWhere= cmds.length - 1;
}
}
if (beforeWhere <= afterWhere)
getLog().writeErrorMsg("Error: builder '" + builderID + "' needs to be before downstream builder '" + downstreamBuilderID + "' but after builder " + upstreamBuilderID + ", but " + downstreamBuilderID + " comes after " + upstreamBuilderID + "!");
if (beforeWhere == cmds.length && afterWhere >= 0)
beforeWhere= afterWhere + 1;
ICommand compilerCmd= desc.newCommand();
compilerCmd.setBuilderName(builderID);
// RMF 8/9/2006 - Don't bother trying to set the builder arguments here; this instance of
// the nature will have been constructed with the no-arg ctor (by the Eclipse framework),
// and won't have enough info to compute the builder arguments. Do it later, in addToProject(),
// which will hopefully be executed by a nature instance that does have the necessary info.
// compilerCmd.setArguments(getBuilderArguments());
ICommand[] newCmds= new ICommand[cmds.length+1];
System.arraycopy(cmds, 0, newCmds, 0, beforeWhere);
newCmds[beforeWhere] = compilerCmd;
System.arraycopy(cmds, beforeWhere, newCmds, beforeWhere+1, cmds.length-beforeWhere);
desc.setBuildSpec(newCmds);
getProject().setDescription(desc, null);
}
protected Map getBuilderArguments() {
return new HashMap();
}
public void deconfigure() throws CoreException {
IProjectDescription desc= getProject().getDescription();
String builderID= getBuilderID();
ICommand[] cmds= desc.getBuildSpec();
for(int i=0; i < cmds.length; ++i) {
if (cmds[i].getBuilderName().equals(builderID)) {
ICommand[] newCmds= new ICommand[cmds.length - 1];
System.arraycopy(cmds, 0, newCmds, 0, i);
System.arraycopy(cmds, i + 1, newCmds, i, cmds.length - i - 1);
desc.setBuildSpec(newCmds);
getProject().setDescription(desc, null);
return;
}
}
}
public IProject getProject() {
return fProject;
}
public void setProject(IProject project) {
fProject= project;
}
}