/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.core.internal.builder;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.DartCoreDebug;
import com.google.dart.tools.core.builder.BuildParticipant;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.core.runtime.Status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
/**
* Each {@link BuildParticipantDeclaration} represents one type of build participant and is used to
* instantiate that type of build participant.
*
* @coverage dart.tools.core.builder
*/
public class BuildParticipantDeclaration {
private static final String PARTICIPANT_EXTENSION_POINT = "buildParticipants"; //$NON-NLS-1$
private static final String PARTICIPANT_CONTRIBUTION = "buildParticipant"; //$NON-NLS-1$
private static final String PARTICIPANT_CLASS_ATTR = "class"; //$NON-NLS-1$
private static final String PARTICIPANT_PRIORITY_ATTR = "priority"; //$NON-NLS-1$
private static final int DEFAULT_PRIORITY = 50;
private static final Object lock = new Object();
/**
* The {@link BuildParticipantDeclaration}s or {@code null} if not yet initialized. Synchronize
* against {@link #lock} before accessing this field.
*/
private static ArrayList<BuildParticipantDeclaration> declarations;
/**
* Answer build participants for the specified project in the order in which they should be
* called. The result is a mixture of both {@link BuildParticipant} and
* {@link DartBuildParticipant}. Instances of {@link DartBuildParticipant} are shared across all
* builders while instances of {@link BuildParticipant} are created one per project and wrapped in
* new instances of {@link DartBuildParticipant}. It is the responsibility of the caller to cache
* the result as this method returns new instances of {@link BuildParticipant} each time it is
* called.
*
* @param project the project for which participants are to be created (not {@code null})
* @return an array of participants (not {@code null} , contains no {@code null}s)
*/
public static BuildParticipant[] participantsFor(IProject project) {
synchronized (lock) {
if (declarations == null) {
// Get the extensions declaring build participants
IExtensionRegistry registry = RegistryFactory.getRegistry();
IExtensionPoint extensionPoint = registry.getExtensionPoint(
DartCore.PLUGIN_ID,
PARTICIPANT_EXTENSION_POINT);
// Create build participant declarations
declarations = new ArrayList<BuildParticipantDeclaration>();
for (IExtension extension : extensionPoint.getExtensions()) {
for (IConfigurationElement element : extension.getConfigurationElements()) {
if (element.getName().equals(BuildParticipantDeclaration.PARTICIPANT_CONTRIBUTION)) {
try {
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
if (!element.getAttribute("id").equals(
"com.google.dart.tools.core.buildParticipant.analysis.engine")) {
declarations.add(new BuildParticipantDeclaration(element));
}
} else {
declarations.add(new BuildParticipantDeclaration(element));
}
} catch (CoreException e1) {
DartCore.logError(
"Exception creating build participant declaration\n from plugin "
+ element.getNamespaceIdentifier(),
e1);
}
}
}
}
// Sort the build declarations by priority
Collections.sort(declarations, new Comparator<BuildParticipantDeclaration>() {
@Override
public int compare(BuildParticipantDeclaration d1, BuildParticipantDeclaration d2) {
return d1.getPriority() - d2.getPriority();
}
});
}
// Construct one new participant for each declaration
ArrayList<BuildParticipant> participants = new ArrayList<BuildParticipant>();
Iterator<BuildParticipantDeclaration> iter = declarations.iterator();
while (iter.hasNext()) {
BuildParticipantDeclaration declaration = iter.next();
try {
participants.add(declaration.newParticipant(project));
} catch (CoreException e) {
DartCore.logError("Exception instantiating build participant\n from plugin "
+ declaration.getPluginId(), e);
iter.remove();
}
}
return participants.toArray(new BuildParticipant[participants.size()]);
}
}
private final IConfigurationElement configElement;
private final int priority;
/**
* Construct a new instance representing the specified type of build participant.
*
* @param element the element (not {@code null})
*/
private BuildParticipantDeclaration(IConfigurationElement element) throws CoreException {
// Extract the priority from the element if defined
String priorityAttr = element.getAttribute(BuildParticipantDeclaration.PARTICIPANT_PRIORITY_ATTR);
int priorityInt;
if (priorityAttr != null) {
try {
priorityInt = Integer.parseInt(priorityAttr);
} catch (NumberFormatException e) {
throw new CoreException(new Status(
IStatus.ERROR,
DartCore.PLUGIN_ID,
"Expected priority to be an integer, but found" + priorityAttr));
}
} else {
priorityInt = BuildParticipantDeclaration.DEFAULT_PRIORITY;
}
this.configElement = element;
this.priority = priorityInt;
}
/**
* Answer the identifer of the plugin declaring this participant.
*/
private String getPluginId() {
return configElement.getNamespaceIdentifier();
}
/**
* Answer the priority for build participants created using this declaration.
*
* @return the priority where a lower number indicates a higher priority
*/
private int getPriority() {
return priority;
}
/**
* Answer a new build participant for the specified project. It is the responsibility of the
* caller to cache the result as this method may return a new instance each time it is called.
*
* @param project the project associated with the new participant (not {@code null})
*/
private BuildParticipant newParticipant(IProject project) throws CoreException {
Object object = configElement.createExecutableExtension(PARTICIPANT_CLASS_ATTR);
if (object instanceof BuildParticipant) {
return (BuildParticipant) object;
}
throw new CoreException(new Status(
IStatus.ERROR,
DartCore.PLUGIN_ID,
"Expected build participant to be an instance of\n " + BuildParticipant.class.getName()
+ "\n but was " + (object != null ? object.getClass().getName() : "null")));
}
}