/*******************************************************************************
* Copyright (c) 2004, 2005
* Thomas Hallgren, Kenneth Olwing, Mitch Sonies
* Pontus Rydin, Nils Unden, Peer Torngren
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the individual
* copyright holders listed above, as Initial Contributors under such license.
* The text of such license is available at www.eclipse.org.
*******************************************************************************/
package org.eclipse.buckminster.core.build;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.buckminster.core.CorePlugin;
import org.eclipse.buckminster.core.Messages;
import org.eclipse.buckminster.runtime.Logger;
import org.eclipse.buckminster.runtime.MonitorUtils;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
/**
* @author kolwing
*/
public abstract class AbstractBuckminsterBuilder extends IncrementalProjectBuilder implements IResourceChangeListener {
public static final String ARG_REFRESH_RESOURCE = "refresh.resource"; //$NON-NLS-1$
public static final String ARG_DERIVED_RESOURCE = "derived.resource"; //$NON-NLS-1$
public static final String ARG_GIVEN_NAME_KEY = "given.name"; //$NON-NLS-1$
public static final String ARG_DISABLED_KEY = "disabled"; //$NON-NLS-1$
public static final String ARG_DELTA_RESOURCE_KEY = "delta.resource"; //$NON-NLS-1$
public static final String ARG_AUTO_PRINTSTREAM_KEY = "auto.printstream"; //$NON-NLS-1$
public static final String ARG_CLEAN_PRINTSTREAM_KEY = "clean.printstream"; //$NON-NLS-1$
public static final String ARG_FULL_PRINTSTREAM_KEY = "full.printstream"; //$NON-NLS-1$
public static final String ARG_INCREMENTAL_PRINTSTREAM_KEY = "incremental.printstream"; //$NON-NLS-1$
public static String bestNameForBuilder(String givenName, IConfigurationElement ce) {
StringBuilder sb = new StringBuilder();
if (givenName != null)
sb.append(givenName).append(" ("); //$NON-NLS-1$
String s = ce.getDeclaringExtension().getLabel().trim();
if (s.length() == 0)
s = ce.getDeclaringExtension().getUniqueIdentifier();
sb.append(s);
if (givenName != null)
sb.append(")"); //$NON-NLS-1$
return sb.toString();
}
public static String getValue(Map<String, String> args, String key) {
String v = args.get(key);
if (v != null) {
v = v.trim();
if (v.length() == 0)
v = null;
}
return v;
}
public static boolean isDeltaMatching(Map<String, String> args, IProject project, IResourceDelta delta, IResource[] notifyOnChangedResources)
throws CoreException {
// if there's no delta available, just go on
//
if (delta == null)
return true;
// if there is no delta resource configured, everything matches
//
String deltaResource = getValue(args, ARG_DELTA_RESOURCE_KEY);
if (deltaResource == null)
return true;
// try to find the configured delta resource
//
if (delta.findMember(new Path(deltaResource)) != null)
return true;
// anything the builder wishes change notifications about are
// implicitly delta resources
// note: delta checks are for auto/incremental, notifications for any
// change, so they are otherwise different usecases
if (notifyOnChangedResources != null) {
for (IResource r : notifyOnChangedResources)
if (delta.findMember(r.getFullPath()) != null)
return true;
}
return false;
}
public static boolean isDisabled(Map<String, String> args) {
return Boolean.parseBoolean(getValue(args, ARG_DISABLED_KEY));
}
public static boolean isPrintingEnabledForKind(Map<String, String> args, int kind) {
String key = null;
switch (kind) {
case AUTO_BUILD:
key = ARG_AUTO_PRINTSTREAM_KEY;
break;
case CLEAN_BUILD:
key = ARG_CLEAN_PRINTSTREAM_KEY;
break;
case FULL_BUILD:
key = ARG_FULL_PRINTSTREAM_KEY;
break;
case INCREMENTAL_BUILD:
key = ARG_INCREMENTAL_PRINTSTREAM_KEY;
break;
}
if (key == null)
return true;
// an absent value indicates 'true'
//
String value = getValue(args, key);
if (value == null)
return true;
return Boolean.parseBoolean(value);
}
public static String kindToString(int kind) {
if (kind == AUTO_BUILD)
return "AUTO"; //$NON-NLS-1$
if (kind == CLEAN_BUILD)
return "CLEAN"; //$NON-NLS-1$
if (kind == FULL_BUILD)
return "FULL"; //$NON-NLS-1$
if (kind == INCREMENTAL_BUILD)
return "INCREMENTAL"; //$NON-NLS-1$
return "NONE"; //$NON-NLS-1$
}
public static void setDisabled(Map<String, String> args, boolean disabled) {
if (disabled)
args.put(ARG_DISABLED_KEY, Boolean.TRUE.toString());
else
args.remove(ARG_DISABLED_KEY);
}
public static void setPrintingEnabledForKind(Map<String, String> args, int kind, boolean enabled) {
String key = null;
switch (kind) {
case AUTO_BUILD:
key = ARG_AUTO_PRINTSTREAM_KEY;
break;
case CLEAN_BUILD:
key = ARG_CLEAN_PRINTSTREAM_KEY;
break;
case FULL_BUILD:
key = ARG_FULL_PRINTSTREAM_KEY;
break;
case INCREMENTAL_BUILD:
key = ARG_INCREMENTAL_PRINTSTREAM_KEY;
break;
}
if (key != null) {
if (enabled)
args.remove(key);
else
args.put(key, Boolean.FALSE.toString());
}
}
private IConfigurationElement config;
private Object data;
private String propertyName;
private PrintStream errStream = null;
private PrintStream outStream = null;
private IResource[] notifyOnChangedResources;
// regardless of if delta is matching or not, always do a build
// the very first time the builder is called (unless disabled, of course)
// this catches cases where otherwise the builder hasn't had the opportunity
// to set resource notifications, for example
private boolean initialBuildDone = false;
@Override
public void resourceChanged(IResourceChangeEvent event) {
if (notifyOnChangedResources != null && event.getType() == IResourceChangeEvent.POST_CHANGE) {
// don't instantiate a list until we know it's needed
List<IResource> changedResources = null;
for (IResource r : notifyOnChangedResources) {
IResourceDelta delta = event.getDelta().findMember(r.getFullPath());
if (delta != null) {
if (changedResources == null)
changedResources = new ArrayList<IResource>();
changedResources.add(r);
}
}
if (changedResources != null)
resourcesChangeNotification(changedResources.toArray(new IResource[0]));
}
}
@Override
public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
super.setInitializationData(config, propertyName, data);
this.config = config;
this.propertyName = propertyName;
this.data = data;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int,
* java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
*/
@SuppressWarnings("rawtypes")
@Override
final protected IProject[] build(int kind, Map rawArgs, IProgressMonitor monitor) throws CoreException {
@SuppressWarnings("unchecked")
Map<String, String> args = rawArgs;
if (args == null)
args = new HashMap<String, String>();
boolean disabled = isDisabled(args);
if (disabled)
return null;
boolean isDeltaMatching = ((kind != AUTO_BUILD && kind != INCREMENTAL_BUILD) ? true : isDeltaMatching(args, getProject(),
getDelta(getProject()), notifyOnChangedResources));
if (!isDeltaMatching && initialBuildDone)
return null;
IProject[] projects = null;
initialBuildDone = true;
boolean needsPrintStream = isPrintingEnabledForKind(args, kind);
MonitorUtils.begin(monitor, 10);
Logger logger = CorePlugin.getLogger();
try {
if (needsPrintStream) {
outStream = Logger.getOutStream();
errStream = Logger.getErrStream();
} else {
outStream = System.out;
errStream = System.err;
}
if (logger.isDebugEnabled())
logger.debug("[start AntBuilder(%s)] : %s - %s", kindToString(kind), getBestName(args), //$NON-NLS-1$
getProject().getName());
projects = doBuild(kind, args, MonitorUtils.subMonitor(monitor, 8));
String refreshResource = getValue(args, ARG_REFRESH_RESOURCE);
if (refreshResource != null) {
IResource resource = getProject().findMember(new Path(refreshResource));
if (resource != null)
resource.refreshLocal(IResource.DEPTH_INFINITE, MonitorUtils.subMonitor(monitor, 1));
}
String derivedResource = getValue(args, ARG_DERIVED_RESOURCE);
if (derivedResource != null) {
IResource resource = getProject().findMember(new Path(derivedResource));
if (resource != null) {
if (refreshResource == null || !refreshResource.equals(derivedResource))
resource.refreshLocal(IResource.DEPTH_INFINITE, MonitorUtils.subMonitor(monitor, 1));
resource.setDerived(true, MonitorUtils.subMonitor(monitor, 1));
}
}
} finally {
MonitorUtils.done(monitor);
if (logger.isDebugEnabled())
logger.debug(String.format("[end AntBuilder(%s)]", kindToString(kind))); //$NON-NLS-1$
}
return projects;
}
@Override
protected final void clean(IProgressMonitor monitor) throws CoreException {
// make all go the same way
//
build(CLEAN_BUILD, getCommand().getArguments(), monitor);
}
protected IProject[] doAutoBuild(Map<String, String> args, IProgressMonitor monitor) throws CoreException {
return null;
}
protected IProject[] doBuild(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
if (kind == AUTO_BUILD)
return doAutoBuild(args, monitor);
if (kind == CLEAN_BUILD)
return doCleanBuild(args, monitor);
if (kind == FULL_BUILD)
return doFullBuild(args, monitor);
if (kind == INCREMENTAL_BUILD)
return doIncrementalBuild(args, monitor);
throw new CoreException(new Status(IStatus.ERROR, CorePlugin.CORE_NAMESPACE, 0, Messages.Unknown_kind, null));
}
protected IProject[] doCleanBuild(Map<String, String> args, IProgressMonitor monitor) throws CoreException {
return null;
}
protected IProject[] doFullBuild(Map<String, String> args, IProgressMonitor monitor) throws CoreException {
return null;
}
protected IProject[] doIncrementalBuild(Map<String, String> args, IProgressMonitor monitor) throws CoreException {
return null;
}
protected void doStartupOnInitialize() {
// noop
}
protected String getBestName(Map<String, String> args) {
return bestNameForBuilder(getGivenName(args), config);
}
protected IConfigurationElement getConfig() {
return config;
}
protected Object getData() {
return data;
}
protected PrintStream getErrStream() {
return errStream;
}
protected String getGivenName(Map<String, String> args) {
return getValue(args, ARG_GIVEN_NAME_KEY);
}
protected PrintStream getOutStream() {
return outStream;
}
protected String getPropertyName() {
return propertyName;
}
protected void notifyOnChangedResources(IResource[] resources) {
notifyOnChangedResources = resources;
}
protected void resourcesChangeNotification(IResource[] changedResources) {
// if someone has requested notification and then doesn't listen to it,
// they deserve to be punished
throw new IllegalStateException(Messages.Method_not_overridden);
}
@Override
protected void startupOnInitialize() {
super.startupOnInitialize();
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
doStartupOnInitialize();
}
}