Search in sources :

Example 1 with ApiImplMapping

use of org.eclipse.n4js.compare.ApiImplMapping in project n4js by eclipse.

the class ChooseImplementationHelper method chooseImplementationIfRequired.

/**
 * Choose an implementation if needed
 *
 * @param runnerId
 *            the runner ID, e.g. NODEJS
 * @param moduleToRun
 *            the emf-full URI to the module to run
 * @return the selected implementation Id
 */
public String chooseImplementationIfRequired(String runnerId, URI moduleToRun) {
    // first see if we need to supply an implementationId
    // (we need one if there are API projects among the dependencies of the moduleToRun AND there exist 2 or more
    // implementations for them)
    final ApiUsage apiUsage = runnerHelper.getProjectExtendedDeps(runnerId, moduleToRun);
    final ApiImplMapping apiImplMapping = apiUsage.apiImplMapping;
    final List<String> availableImplIds = apiImplMapping.getAllImplIds();
    if (apiImplMapping.isEmpty())
        // no API projects among the dependencies -> no need to bother the user
        return null;
    if (availableImplIds.isEmpty())
        // no implementations available -> error will be shown somewhere else
        return null;
    if (availableImplIds.size() == 1)
        // exactly 1 implementation -> use that, no need to bother the user
        return availableImplIds.get(0);
    // make user choose:
    // We have to open the dialog on the UI-thread
    // See:
    // http://stackoverflow.com/questions/354796/whats-the-best-way-to-get-a-return-value-out-of-an-asyncexec-in-eclipse
    final AtomicReference<String> result = new AtomicReference<>();
    final ChooseImplementationDialog dlg = new ChooseImplementationDialog(null, apiImplMapping);
    Display.getDefault().syncExec(new Runnable() {

        @Override
        public void run() {
            if (dlg.open() == Window.OK) {
                result.set((String) dlg.getResult()[0]);
            } else {
                result.set(CANCEL);
            }
        }
    });
    return result.get();
}
Also used : ApiUsage(org.eclipse.n4js.runner.RunnerHelper.ApiUsage) ApiImplMapping(org.eclipse.n4js.compare.ApiImplMapping) AtomicReference(java.util.concurrent.atomic.AtomicReference)

Example 2 with ApiImplMapping

use of org.eclipse.n4js.compare.ApiImplMapping in project n4js by eclipse.

the class RunnerHelper method getProjectExtendedDepsAndApiImplMapping.

/**
 * Returns a mapping from all API projects among <code>dependencies</code> to their corresponding implementation
 * project for implementation ID <code>implementationId</code>.
 * <p>
 * Special cases: if there are no API projects among the dependencies, this method will return an empty map in
 * {@link ApiUsage#concreteApiImplProjectMapping} and {@link ApiUsage#implementationIdRequired}<code>==false</code>;
 * otherwise, if <code>implementationId</code> is <code>null</code>, then this method will assert that there exists
 * exactly one implementation for the API projects among the dependencies and use that (stored in
 * {@link ApiUsage#implementationId}).
 * <p>
 * Throws exception in case of error given <code>throwOnError==true</code>, never returns null.
 *
 * @param runtimeEnvironment
 *            active runtime environment.
 * @param moduleToRun
 *            what to run.
 * @param implementationId
 *            might be <code>null</code>
 * @param throwOnError
 *            if true fast fail in erroneous situations. Otherwise tries to proceed in a meaningful way. State can
 *            then be queried on the ApiUsage instance.
 *
 * @return result wrapped in an {@link ApiUsage} instance.
 */
// TODO this methods could require some cleanup after the concepts of API-Implementation mappings stabilized...
public ApiUsage getProjectExtendedDepsAndApiImplMapping(RuntimeEnvironment runtimeEnvironment, URI moduleToRun, String implementationId, boolean throwOnError) {
    final LinkedHashSet<IN4JSProject> deps = new LinkedHashSet<>();
    // 1) add project containing the moduleToRun and its direct AND indirect dependencies
    final Optional<? extends IN4JSProject> project = n4jsCore.findProject(moduleToRun);
    if (project.isPresent() == false) {
        throw new RuntimeException("can't obtain containing project for moduleToRun: " + moduleToRun);
    }
    recursiveDependencyCollector(project.get(), deps, new RecursionGuard<>());
    // TODO need to add not only REs but also RLs they provide
    // 2) add the runtime environment project, REs it extends and RLs provided
    final Optional<IN4JSProject> reProject = getCustomRuntimeEnvironmentProject(runtimeEnvironment);
    if (reProject.isPresent()) {
        IN4JSProject re = reProject.get();
        recursiveExtendedREsCollector(re, deps);
    } else {
    // IDE-1359: don't throw exception to make runners work without user-defined runtime environment
    // (will be changed later when library manager is available!)
    // throw new RuntimeException("can't obtain runtime environment project for " + moduleToRun2);
    }
    // TODO actually we would like to return the dependencies in load order:
    // - RuntimeEnvironment boot
    // - all RuntimeLibrary boots (take deps2 between RLs into account)
    // - all other deps2 (order does not matter they just needs to be on path later)
    // - project to be called
    // maybe some in order DFS or something above?
    // manually transform to list, or is this one ok?
    final ApiImplMapping apiImplMapping = ApiImplMapping.of(deps, n4jsCore.findAllProjects());
    if (apiImplMapping.hasErrors()) {
        if (throwOnError)
            throw new IllegalStateException("the workspace setup contains errors related to API / implementation projects (check manifests of related projects):\n    " + Joiner.on("\n    ").join(apiImplMapping.getErrorMessages()));
    }
    if (apiImplMapping.isEmpty()) {
        return ApiUsage.of(new ArrayList<>(deps), Collections.<IN4JSProject, IN4JSProject>emptyMap(), apiImplMapping);
    }
    // there must be exactly one implementation for the API projects in the workspace
    if (implementationId == null) {
        final List<String> allImplIds = apiImplMapping.getAllImplIds();
        if (allImplIds.size() != 1) {
            if (throwOnError) {
                throw new IllegalStateException("no implementationId specified while several are available in the workspace: " + allImplIds);
            } else {
                // back out, further processing not possible without implementations id.
                return ApiUsage.of(new ArrayList<>(deps), Collections.emptyMap(), apiImplMapping, true);
            }
        } else {
            implementationId = allImplIds.get(0);
        }
    }
    final Map<IN4JSProject, IN4JSProject> apiImplProjectMapping = new LinkedHashMap<>();
    // projectIds of projects without an implementation
    final List<String> missing = new ArrayList<>();
    for (IN4JSProject dep : deps) {
        if (dep != null) {
            final String depId = dep.getProjectId();
            if (depId != null && apiImplMapping.isApi(depId)) {
                // so: dep is an API project ...
                final IN4JSProject impl = apiImplMapping.getImpl(depId, implementationId);
                if (impl != null) {
                    // so: impl is the implementation project for dep for implementation ID 'implementationId'
                    apiImplProjectMapping.put(dep, impl);
                } else {
                    // bad: no implementation for dep for implementation ID 'implementationId'
                    missing.add(depId);
                }
            }
        }
    }
    if (!missing.isEmpty()) {
        if (throwOnError) {
            throw new IllegalStateException("no implementation for implementation ID \"" + implementationId + "\" found for the following projects: " + Joiner.on(", ").join(missing));
        }
    }
    // / #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    // IDEBUG-506 look for additional dependencies, pulled in from api-impl project not yet processed:
    HashSet<IN4JSProject> processedDepProjects = deps;
    LinkedHashSet<IN4JSProject> tobeInspectedApiImplProjects = new LinkedHashSet<>();
    apiImplProjectMapping.entrySet().forEach(p -> {
        IN4JSProject v = p.getValue();
        if (!processedDepProjects.contains(v))
            tobeInspectedApiImplProjects.add(v);
    });
    // Collect transitive mappings. Populate with original ones.
    final Map<IN4JSProject, IN4JSProject> joinedApiImplProjectMapping = new LinkedHashMap<>(apiImplProjectMapping);
    while (!tobeInspectedApiImplProjects.isEmpty()) {
        // compute dependencies if necessary
        LinkedHashSet<IN4JSProject> batchedPivotDependencies = new LinkedHashSet<>();
        RecursionGuard<URI> guard = new RecursionGuard<>();
        for (IN4JSProject pivotProject : tobeInspectedApiImplProjects) {
            recursiveDependencyCollector(pivotProject, batchedPivotDependencies, guard);
        }
        tobeInspectedApiImplProjects.clear();
        List<IN4JSProject> batchedPivotNewDepList = batchedPivotDependencies.stream().filter(p -> null != p && !processedDepProjects.contains(p)).collect(Collectors.toList());
        // new Api-mapping
        apiImplMapping.enhance(batchedPivotNewDepList, n4jsCore.findAllProjects());
        // go over new dependencies and decide:
        for (IN4JSProject pivNewDep : batchedPivotNewDepList) {
            final String depId = pivNewDep.getProjectId();
            if (apiImplMapping.isApi(depId)) {
                // API-mapping
                if (joinedApiImplProjectMapping.containsKey(pivNewDep)) {
                // already done.
                } else {
                    // put new entry
                    IN4JSProject pivImpl = apiImplMapping.getImpl(depId, implementationId);
                    if (null != pivImpl) {
                        joinedApiImplProjectMapping.put(pivNewDep, pivImpl);
                        tobeInspectedApiImplProjects.add(pivImpl);
                    } else {
                        missing.add(depId);
                    }
                }
            } else {
                // no API.
                if (!processedDepProjects.contains(pivNewDep)) {
                    // add to deps
                    processedDepProjects.add(pivNewDep);
                }
            }
        }
    }
    if (!missing.isEmpty()) {
        if (throwOnError) {
            throw new IllegalStateException("no implementation for implementation ID \"" + implementationId + "\" found for the following projects: " + Joiner.on(", ").join(missing));
        }
    }
    return ApiUsage.of(implementationId, new ArrayList<>(deps), apiImplProjectMapping, apiImplMapping, missing);
}
Also used : LinkedHashSet(java.util.LinkedHashSet) BootstrapModule(org.eclipse.n4js.n4mf.BootstrapModule) URI(org.eclipse.emf.common.util.URI) Inject(com.google.inject.Inject) ResourceNameComputer(org.eclipse.n4js.utils.ResourceNameComputer) IN4JSCore(org.eclipse.n4js.projectModel.IN4JSCore) IN4JSSourceContainerAware(org.eclipse.n4js.projectModel.IN4JSSourceContainerAware) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) Strings(com.google.common.base.Strings) RunnerRegistry(org.eclipse.n4js.runner.extension.RunnerRegistry) N4JSGlobals(org.eclipse.n4js.N4JSGlobals) Optional(com.google.common.base.Optional) Map(java.util.Map) RecursionGuard(org.eclipse.n4js.utils.RecursionGuard) IRunnerDescriptor(org.eclipse.n4js.runner.extension.IRunnerDescriptor) RuntimeEnvironment(org.eclipse.n4js.runner.extension.RuntimeEnvironment) ApiImplMapping(org.eclipse.n4js.compare.ApiImplMapping) Path(java.nio.file.Path) LinkedHashSet(java.util.LinkedHashSet) Collections.emptyList(java.util.Collections.emptyList) Collection(java.util.Collection) IN4JSProject(org.eclipse.n4js.projectModel.IN4JSProject) Set(java.util.Set) Collectors(java.util.stream.Collectors) File(java.io.File) ProjectType(org.eclipse.n4js.n4mf.ProjectType) List(java.util.List) Lists.newArrayList(com.google.common.collect.Lists.newArrayList) IN4JSArchive(org.eclipse.n4js.projectModel.IN4JSArchive) AbstractSubGenerator(org.eclipse.n4js.generator.AbstractSubGenerator) Collections(java.util.Collections) FindArtifactHelper(org.eclipse.n4js.utils.FindArtifactHelper) Joiner(com.google.common.base.Joiner) ApiImplMapping(org.eclipse.n4js.compare.ApiImplMapping) ArrayList(java.util.ArrayList) Lists.newArrayList(com.google.common.collect.Lists.newArrayList) RecursionGuard(org.eclipse.n4js.utils.RecursionGuard) URI(org.eclipse.emf.common.util.URI) LinkedHashMap(java.util.LinkedHashMap) IN4JSProject(org.eclipse.n4js.projectModel.IN4JSProject)

Aggregations

ApiImplMapping (org.eclipse.n4js.compare.ApiImplMapping)2 Joiner (com.google.common.base.Joiner)1 Optional (com.google.common.base.Optional)1 Strings (com.google.common.base.Strings)1 Lists.newArrayList (com.google.common.collect.Lists.newArrayList)1 Inject (com.google.inject.Inject)1 File (java.io.File)1 Path (java.nio.file.Path)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 Collections (java.util.Collections)1 Collections.emptyList (java.util.Collections.emptyList)1 HashSet (java.util.HashSet)1 LinkedHashMap (java.util.LinkedHashMap)1 LinkedHashSet (java.util.LinkedHashSet)1 List (java.util.List)1 Map (java.util.Map)1 Set (java.util.Set)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 Collectors (java.util.stream.Collectors)1