| /******************************************************************************* |
| * Copyright (c) 2008, 2017 xored software, Inc. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * xored software, Inc. - initial API and Implementation (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.core.builder; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.builder.IBuildParticipant; |
| import org.eclipse.dltk.core.builder.IBuildParticipantExtension4; |
| import org.eclipse.dltk.core.builder.IBuildParticipantFactory; |
| import org.eclipse.dltk.core.builder.IBuildParticipantFilter; |
| import org.eclipse.dltk.core.builder.IBuildParticipantFilterFactory; |
| import org.eclipse.dltk.internal.core.builder.BuildParticipantManager.FactoryValue; |
| import org.eclipse.dltk.utils.NatureExtensionManager; |
| import org.eclipse.osgi.util.NLS; |
| |
| public class BuildParticipantManager |
| extends NatureExtensionManager<FactoryValue> { |
| |
| private static final String EXT_POINT = DLTKCore.PLUGIN_ID |
| + ".buildParticipant"; //$NON-NLS-1$ |
| |
| public static class FactoryValue<T> { |
| final T factory; |
| |
| public FactoryValue(T factory) { |
| this.factory = factory; |
| } |
| } |
| |
| public static class BuildParticipantFactoryValue |
| extends FactoryValue<IBuildParticipantFactory> { |
| final String id; |
| final String name; |
| public final Set<String> requirements = new HashSet<>(); |
| |
| /** |
| * @param factory |
| */ |
| public BuildParticipantFactoryValue(IBuildParticipantFactory factory, |
| String id, String name) { |
| super(factory); |
| this.id = id != null ? id : factory.getClass().getName(); |
| this.name = name; |
| } |
| |
| } |
| |
| public static class FilterFactoryValue |
| extends FactoryValue<IBuildParticipantFilterFactory> { |
| |
| public FilterFactoryValue(IBuildParticipantFilterFactory factory) { |
| super(factory); |
| } |
| |
| } |
| |
| private BuildParticipantManager() { |
| super(EXT_POINT, FactoryValue.class); |
| } |
| |
| private static final String REQUIRES = "requires"; //$NON-NLS-1$ |
| private static final String REQUIRES_ID = "id"; //$NON-NLS-1$ |
| |
| private static final String PARTICIPANT = "buildParticipant"; |
| private static final String FILTER = "filter"; |
| |
| private static final String ATTR_ID = "id"; //$NON-NLS-1$ |
| private static final String ATTR_NAME = "name"; //$NON-NLS-1$ |
| |
| @Override |
| protected Object createInstanceByDescriptor(Object input) |
| throws CoreException { |
| final IConfigurationElement element = (IConfigurationElement) input; |
| if (PARTICIPANT.equals(element.getName())) { |
| final Object factory = element.createExecutableExtension(classAttr); |
| if (!(factory instanceof IBuildParticipantFactory)) { |
| DLTKCore.warn(NLS.bind( |
| "{0} contributed by {1} must implement {2}", |
| new Object[] { element.getName(), |
| element.getContributor(), |
| IBuildParticipantFactory.class.getName() })); |
| return null; |
| } |
| final BuildParticipantFactoryValue factoryValue = new BuildParticipantFactoryValue( |
| (IBuildParticipantFactory) factory, |
| element.getAttribute(ATTR_ID), |
| element.getAttribute(ATTR_NAME)); |
| final IConfigurationElement[] requires = element |
| .getChildren(REQUIRES); |
| for (int i = 0; i < requires.length; ++i) { |
| final String id = requires[i].getAttribute(REQUIRES_ID); |
| if (id != null) { |
| factoryValue.requirements.add(id); |
| } |
| } |
| return factoryValue; |
| } else if (FILTER.equals(element.getName())) { |
| final Object factory = element.createExecutableExtension(classAttr); |
| if (!(factory instanceof IBuildParticipantFilterFactory)) { |
| DLTKCore.warn( |
| NLS.bind("{0} contributed by {1} must implement {2}", |
| new Object[] { element.getName(), |
| element.getContributor(), |
| IBuildParticipantFilterFactory.class |
| .getName() })); |
| return null; |
| } |
| return new FilterFactoryValue( |
| (IBuildParticipantFilterFactory) factory); |
| } else { |
| DLTKCore.warn(NLS.bind( |
| "Wrong element {0} in {1} extension point contributed by {2}", |
| new Object[] { element.getName(), extensionPoint, |
| element.getContributor() })); |
| return null; |
| } |
| } |
| |
| private static BuildParticipantManager instance = null; |
| |
| private static synchronized BuildParticipantManager getInstance() { |
| if (instance == null) { |
| instance = new BuildParticipantManager(); |
| } |
| return instance; |
| } |
| |
| private static final IBuildParticipant[] NO_PARTICIPANTS = new IBuildParticipant[0]; |
| |
| private static final IBuildParticipantFilter[] NO_PREDICATES = new IBuildParticipantFilter[0]; |
| |
| public static class BuildParticipantResult { |
| // not-null |
| public final IBuildParticipant[] participants; |
| // nullable |
| public final Map<IBuildParticipant, List<IBuildParticipant>> dependencies; |
| |
| public BuildParticipantResult(IBuildParticipant[] participants, |
| Map<IBuildParticipant, List<IBuildParticipant>> dependencies) { |
| this.participants = participants; |
| this.dependencies = dependencies; |
| } |
| } |
| |
| /** |
| * Returns {@link IBuildParticipant} instances of the specified nature. If |
| * there are no build participants then the empty array is returned. |
| * |
| * @param project |
| * @param natureId |
| * @return |
| */ |
| public static BuildParticipantResult getBuildParticipants( |
| IScriptProject project, String natureId) { |
| final FactoryValue<?>[] factories = getInstance() |
| .getInstances(natureId); |
| if (factories == null || factories.length == 0) { |
| return new BuildParticipantResult(NO_PARTICIPANTS, null); |
| } |
| return createParticipants(project, factories); |
| } |
| |
| public static BuildParticipantResult createParticipants( |
| IScriptProject project, FactoryValue<?>[] factories) { |
| final IBuildParticipant[] result = new IBuildParticipant[factories.length]; |
| final Set<String> processed = new HashSet<>(); |
| final Map<String, IBuildParticipant> created = new HashMap<>(); |
| final Map<IBuildParticipant, List<IBuildParticipant>> dependencies = new HashMap<>(); |
| for (;;) { |
| final int iterationStartCount = created.size(); |
| for (int i = 0; i < factories.length; ++i) { |
| if (!(factories[i] instanceof BuildParticipantFactoryValue)) { |
| continue; |
| } |
| final BuildParticipantFactoryValue factory = (BuildParticipantFactoryValue) factories[i]; |
| if (!processed.contains(factory.id) |
| && created.keySet().containsAll(factory.requirements)) { |
| processed.add(factory.id); |
| try { |
| final IBuildParticipant participant = factory.factory |
| .createBuildParticipant(project); |
| if (participant != null) { |
| result[created.size()] = participant; |
| created.put(factory.id, participant); |
| if (!factory.requirements.isEmpty()) { |
| for (String req : factory.requirements) { |
| final IBuildParticipant reqParticipant = created |
| .get(req); |
| List<IBuildParticipant> depList = dependencies |
| .get(reqParticipant); |
| if (depList == null) { |
| depList = new ArrayList<>(); |
| dependencies.put(reqParticipant, |
| depList); |
| } |
| depList.add(participant); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| final String tpl = Messages.BuildParticipantManager_buildParticipantCreateError; |
| DLTKCore.warn(NLS.bind(tpl, factory.id), e); |
| } |
| } |
| } |
| if (iterationStartCount == created.size()) { |
| break; |
| } |
| } |
| if (created.size() != result.length) { |
| if (created.size() == 0) { |
| return new BuildParticipantResult(NO_PARTICIPANTS, null); |
| } |
| final IBuildParticipant[] newResult = new IBuildParticipant[created |
| .size()]; |
| System.arraycopy(result, 0, newResult, 0, created.size()); |
| return new BuildParticipantResult(newResult, dependencies); |
| } else { |
| return new BuildParticipantResult(result, dependencies); |
| } |
| } |
| |
| public static IBuildParticipantFilter[] getFilters(IScriptProject project, |
| String natureId, Object context) { |
| final FactoryValue<?>[] factories = getInstance() |
| .getInstances(natureId); |
| if (factories == null || factories.length == 0) { |
| return NO_PREDICATES; |
| } |
| return createFilters(project, factories, context); |
| } |
| |
| public static IBuildParticipantFilter[] createFilters( |
| IScriptProject project, FactoryValue<?>[] factories, |
| Object context) { |
| final IBuildParticipantFilter[] result = new IBuildParticipantFilter[factories.length]; |
| int created = 0; |
| for (int i = 0; i < factories.length; ++i) { |
| if (!(factories[i] instanceof FilterFactoryValue)) { |
| continue; |
| } |
| final FilterFactoryValue factory = (FilterFactoryValue) factories[i]; |
| try { |
| final IBuildParticipantFilter filter = factory.factory |
| .createPredicate(project, context); |
| if (filter != null) { |
| result[created++] = filter; |
| } |
| } catch (CoreException e) { |
| final String tpl = Messages.BuildParticipantManager_buildParticipantCreateError; |
| DLTKCore.warn( |
| NLS.bind(tpl, factory.factory.getClass().getName()), e); |
| } |
| } |
| if (created != result.length) { |
| if (created == 0) { |
| return NO_PREDICATES; |
| } |
| final IBuildParticipantFilter[] newResult = new IBuildParticipantFilter[created]; |
| System.arraycopy(result, 0, newResult, 0, created); |
| return newResult; |
| } else { |
| return result; |
| } |
| } |
| |
| public static IBuildParticipant[] copyFirst(IBuildParticipant[] array, |
| int length) { |
| if (length == array.length) { |
| return array; |
| } |
| if (length == 0) { |
| return BuildParticipantManager.NO_PARTICIPANTS; |
| } else { |
| IBuildParticipant[] temp = new IBuildParticipant[length]; |
| System.arraycopy(array, 0, temp, 0, length); |
| return temp; |
| } |
| } |
| |
| /** |
| * First removes dangling {@link IBuildParticipant}s from |
| * {@link #dependencies} then notifies participants about their |
| * dependencies. |
| * |
| * @param dependencies |
| * @param participants |
| */ |
| public static void notifyDependents(IBuildParticipant[] participants, |
| Map<IBuildParticipant, List<IBuildParticipant>> dependencies) { |
| if (dependencies == null) { |
| return; |
| } |
| final List<IBuildParticipant> list = Arrays.asList(participants); |
| dependencies.keySet().retainAll(list); |
| for (Iterator<Map.Entry<IBuildParticipant, List<IBuildParticipant>>> i = dependencies |
| .entrySet().iterator(); i.hasNext();) { |
| final Entry<IBuildParticipant, List<IBuildParticipant>> entry = i |
| .next(); |
| entry.getValue().retainAll(list); |
| if (entry.getValue().isEmpty()) { |
| i.remove(); |
| } |
| } |
| if (dependencies.isEmpty()) { |
| return; |
| } |
| for (Map.Entry<IBuildParticipant, List<IBuildParticipant>> entry : dependencies |
| .entrySet()) { |
| if (entry.getKey() instanceof IBuildParticipantExtension4) { |
| final List<IBuildParticipant> dependents = entry.getValue(); |
| ((IBuildParticipantExtension4) entry.getKey()) |
| .notifyDependents(dependents.toArray( |
| new IBuildParticipant[dependents.size()])); |
| } |
| } |
| } |
| |
| } |