| /******************************************************************************* |
| * Copyright (c) 2016, 2018 Willink Transformations and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * E.D.Willink - Initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.qvtd.compiler.internal.utilities; |
| |
| import java.io.IOException; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.emf.codegen.ecore.genmodel.GenPackage; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.ECollections; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature.Setting; |
| import org.eclipse.emf.ecore.impl.BasicEObjectImpl; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.util.Diagnostician; |
| import org.eclipse.emf.ecore.util.EcoreUtil.UnresolvedProxyCrossReferencer; |
| import org.eclipse.emf.ecore.xmi.XMLResource; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.examples.codegen.dynamic.JavaClasspath; |
| import org.eclipse.ocl.examples.codegen.dynamic.JavaFileUtil; |
| import org.eclipse.ocl.examples.codegen.generator.CodeGenerator; |
| import org.eclipse.ocl.pivot.Element; |
| import org.eclipse.ocl.pivot.NamedElement; |
| import org.eclipse.ocl.pivot.OCLExpression; |
| import org.eclipse.ocl.pivot.OperationCallExp; |
| import org.eclipse.ocl.pivot.PivotPackage; |
| import org.eclipse.ocl.pivot.Variable; |
| import org.eclipse.ocl.pivot.VariableExp; |
| import org.eclipse.ocl.pivot.ids.OperationId; |
| import org.eclipse.ocl.pivot.utilities.ClassUtil; |
| import org.eclipse.ocl.pivot.utilities.LabelUtil; |
| import org.eclipse.ocl.pivot.utilities.NameUtil; |
| import org.eclipse.ocl.pivot.utilities.Nameable; |
| import org.eclipse.ocl.pivot.utilities.PivotUtil; |
| import org.eclipse.ocl.pivot.utilities.StringUtil; |
| import org.eclipse.qvtd.compiler.CompilerChainException; |
| import org.eclipse.qvtd.compiler.CompilerProblem; |
| import org.eclipse.qvtd.compiler.internal.qvtb2qvts.PartitionProblem; |
| import org.eclipse.qvtd.compiler.internal.qvtb2qvts.RegionProblem; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.Concurrency; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.CompositePartitionAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.PartitionAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.TransformationPartitioner; |
| import org.eclipse.qvtd.pivot.qvtcore.QVTcorePackage; |
| import org.eclipse.qvtd.pivot.qvtcore.VariableAssignment; |
| import org.eclipse.qvtd.pivot.qvtschedule.DispatchRegion; |
| import org.eclipse.qvtd.pivot.qvtschedule.Partition; |
| import org.eclipse.qvtd.pivot.qvtschedule.Region; |
| import org.eclipse.qvtd.pivot.qvtschedule.RuleRegion; |
| import org.eclipse.qvtd.pivot.qvtschedule.VerdictRegion; |
| import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil; |
| |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| |
| public class CompilerUtil extends QVTscheduleUtil |
| { |
| public final static @NonNull Map<Object, Object> defaultSavingOptions; |
| |
| // FIXME use a better default strategy for the saving options |
| static { |
| defaultSavingOptions = new HashMap<Object, Object>(); |
| defaultSavingOptions.put(XMLResource.OPTION_ENCODING, "UTF-8"); |
| defaultSavingOptions.put(XMLResource.OPTION_LINE_DELIMITER, "\n"); |
| defaultSavingOptions.put(XMLResource.OPTION_SCHEMA_LOCATION, Boolean.TRUE); |
| defaultSavingOptions.put(XMLResource.OPTION_SCHEMA_LOCATION_IMPLEMENTATION, Boolean.TRUE); |
| defaultSavingOptions.put(XMLResource.OPTION_LINE_WIDTH, Integer.valueOf(132)); |
| |
| } |
| |
| /** |
| * PartialRegion definds the necessary partial-region functionality for a computeImmediatePredecessors participant. |
| */ |
| public interface PartialRegion<PR, TC, TP> extends Nameable |
| { |
| @Nullable Iterable<@NonNull TC> getConsumedTraceClassAnalyses(); |
| @Nullable Iterable<@NonNull TP> getConsumedTracePropertyAnalyses(); |
| @Nullable Iterable<@NonNull PR> getExplicitPredecessors(); |
| } |
| |
| /** |
| * PartialRegion definds the necessary trace-class functionality for a computeImmediatePredecessors participant. |
| */ |
| public interface TraceClass<PR, TC, TP> extends Nameable |
| { |
| @NonNull Iterable<@NonNull PR> getProducers(); |
| @NonNull Iterable<@NonNull TC> getSubTraceClassAnalyses(); |
| } |
| |
| /** |
| * PartialRegion definds the necessary trace-property functionality for a computeImmediatePredecessors participant. |
| */ |
| public interface TraceProperty<PR, TC, TP> extends Nameable |
| { |
| @NonNull Iterable<@NonNull PR> getProducers(); |
| } |
| |
| /** |
| * Add the URI to the list of classpath elememnts. |
| * |
| * @throws MalformedURLException |
| */ |
| @Deprecated /* Promote to JavaClasspath */ |
| public static void addURI(@NonNull JavaClasspath classpath, @NonNull URI uri) throws MalformedURLException { |
| classpath.addURL(new URL(uri.isFile() ? uri.toString() : uri.toFileString())); |
| } |
| |
| |
| public static void assertNoResourceErrors(@NonNull String prefix, @NonNull Resource resource) { |
| String message = PivotUtil.formatResourceDiagnostics(resource.getErrors(), prefix, "\n\t"); |
| if (message != null) |
| assert false : message; |
| } |
| |
| public static void assertNoResourceSetErrors(@NonNull String prefix, @NonNull Resource resource) { |
| ResourceSet resourceSet = resource.getResourceSet(); |
| assert resourceSet != null : prefix + " no ResourceSet for " + resource; |
| for (Resource aResource : resourceSet.getResources()) { |
| if (aResource != null) { |
| assertNoResourceErrors(prefix, aResource); |
| } |
| } |
| } |
| |
| public static void assertNoUnresolvedProxies(String message, Resource resource) { |
| Map<EObject, Collection<Setting>> unresolvedProxies = UnresolvedProxyCrossReferencer.find(resource); |
| if (unresolvedProxies.size() > 0) { |
| StringBuilder s = new StringBuilder(); |
| s.append(unresolvedProxies.size()); |
| s.append(" unresolved proxies in '" + resource.getURI() + "' "); |
| s.append(message); |
| for (Map.Entry<EObject, Collection<Setting>> unresolvedProxy : unresolvedProxies.entrySet()) { |
| s.append("\n"); |
| BasicEObjectImpl key = (BasicEObjectImpl) unresolvedProxy.getKey(); |
| s.append(key.eProxyURI()); |
| for (Setting setting : unresolvedProxy.getValue()) { |
| s.append("\n\t"); |
| EObject eObject = setting.getEObject(); |
| s.append(eObject.toString()); |
| } |
| } |
| assert false : s.toString(); |
| } |
| } |
| |
| public static void assertNoValidationErrors(@NonNull String prefix, @NonNull Resource resource) { |
| for (EObject eObject : resource.getContents()) { |
| assertNoValidationErrors(prefix + " of '" + resource.getURI() + "'", eObject); |
| } |
| } |
| |
| public static void assertNoValidationErrors(@NonNull String string, EObject eObject) { |
| Map<Object, Object> validationContext = LabelUtil.createDefaultContext(Diagnostician.INSTANCE); |
| Diagnostic diagnostic = Diagnostician.INSTANCE.validate(eObject, validationContext); |
| List<Diagnostic> children = diagnostic.getChildren(); |
| if (children.size() <= 0) { |
| return; |
| } |
| StringBuilder s = new StringBuilder(); |
| s.append(string + ": " + children.size() + " validation errors"); |
| for (Diagnostic child : children){ |
| s.append("\n\t"); |
| if (child.getData().size() > 0) { |
| Object data = child.getData().get(0); |
| if (data instanceof Element) { |
| for (EObject eScope = (Element)data; eScope instanceof Element; eScope = eScope.eContainer()) { |
| /* ModelElementCS csElement = ElementUtil.getCsElement((Element)eScope); |
| if (csElement != null) { |
| ICompositeNode node = NodeModelUtils.getNode(csElement); |
| if (node != null) { |
| Resource eResource = csElement.eResource(); |
| if (eResource != null) { |
| s.append(eResource.getURI().lastSegment() + ":"); |
| } |
| int startLine = node.getStartLine(); |
| s.append(startLine + ":"); |
| } |
| s.append(((Element)data).eClass().getName() + ": "); |
| break; |
| } */ |
| } |
| } |
| } |
| s.append(child.getMessage()); |
| } |
| assert false : s.toString(); |
| } |
| |
| /** |
| * Return a new list of exemplar classes whose projects need to be on the class path. |
| * @throws IOException |
| */ |
| public static @NonNull JavaClasspath createDefaultQVTiClasspath() { |
| JavaClasspath classpath = JavaFileUtil.createDefaultOCLClasspath(); |
| classpath.addClass(org.eclipse.qvtd.runtime.evaluation.Invocation.class); |
| return classpath; |
| } |
| |
| /** |
| * Return the classpath folders corresponding to the projectNames, except that the projectName uses an explicit and |
| * possibly irregular classFilePath allowing the current project to use a non-standard 'bin' folder to avoid confusion |
| * between the test compilation and the auto-build. |
| * |
| public static @NonNull List<@NonNull Class<?>> createClassPathProjectList(@NonNull URIConverter uriConverter, @NonNull Class<?> projectName, |
| @NonNull Class<?> classFilePath, @Nullable Iterable<@NonNull Class<?>> projectNames) { |
| assert EcorePlugin.IS_ECLIPSE_RUNNING; |
| if (projectNames == null) { |
| projectNames = CompilerUtil.createClasspathProjectNameList(projectName); |
| } |
| List<@NonNull Class<?>> projectNames2 = Lists.newArrayList(projectNames); |
| projectNames2.remove(projectName); |
| List<@NonNull Class<?>> classpathProjects = JavaFileUtil.createClassPathProjectList(uriConverter, projectNames2); |
| classpathProjects.add(0, classFilePath); |
| return classpathProjects; |
| } */ |
| |
| // |
| // Ripple the direct from-2-tos to compute and return the transitive from-2-tos closure. |
| // |
| public static <T> @NonNull Map<T, @NonNull Set<T>> computeClosure(@NonNull Map<T, @NonNull Set<T>> from2tos) { |
| Iterable<T> tos = from2tos.keySet(); |
| Map<T, @NonNull Set<T>> from2tosClosure = new HashMap<>(); |
| for (T from : tos) { |
| from2tosClosure.put(from, new HashSet<>(from2tos.get(from))); |
| } |
| boolean isChanged = true; |
| while (isChanged) { |
| isChanged = false; |
| for (T from : from2tos.keySet()) { |
| Set<T> fromTos = from2tosClosure.get(from); |
| assert fromTos != null; |
| for (T fromTo : new ArrayList<>(fromTos)) { |
| Set<T> fromToTos = from2tosClosure.get(fromTo); |
| assert fromToTos != null; |
| if (fromTos.addAll(fromToTos)) { |
| isChanged = true; |
| } |
| } |
| } |
| } |
| return from2tosClosure; |
| } |
| |
| |
| /** |
| * Return a map of the PartialRegions that may execute immediately before each PartialRegion. |
| * |
| * This is a simple type-conformance exercise. Find the sources that are supertypes of the target. |
| */ |
| public static <@NonNull PR extends PartialRegion<PR, TC, TP>, @NonNull TC extends TraceClass<PR, TC, TP>, @NonNull TP extends TraceProperty<PR, TC, TP>> @NonNull Map<@NonNull PR, @NonNull Set<@NonNull PR>> computeImmediatePredecessors(@NonNull Iterable<@NonNull PR> partialRegionAnalyses) { |
| Map<@NonNull PR, @NonNull Set<@NonNull PR>> consumer2producers = new HashMap<>(); |
| Map<@NonNull PR, @NonNull Map<@NonNull Object, @NonNull Set<@NonNull PR>>> consumer2consumedElement2producers = new HashMap<>(); |
| for (@NonNull PR partialRegionAnalysis : partialRegionAnalyses) { |
| consumer2producers.put(partialRegionAnalysis, new HashSet<>()); |
| } |
| for (@NonNull PR consumer : partialRegionAnalyses) { |
| Map<@NonNull Object, @NonNull Set<@NonNull PR>> consumedElement2producers = null; |
| if (Iterables.contains(partialRegionAnalyses, consumer)) { |
| consumedElement2producers = new HashMap<>(); |
| consumer2consumedElement2producers.put(consumer, consumedElement2producers); |
| } |
| Iterable<@NonNull PR> explicitPredecessors = consumer.getExplicitPredecessors(); // Used by no-success QVTc trace |
| if (explicitPredecessors != null) { |
| for (@NonNull PR explicitPredecessor : explicitPredecessors) { |
| Set<@NonNull PR> producers = consumer2producers.get(consumer); |
| assert producers != null; |
| producers.add(explicitPredecessor); |
| } |
| } |
| Iterable<@NonNull TC> consumedTraceClassAnalyses = consumer.getConsumedTraceClassAnalyses(); |
| if (consumedTraceClassAnalyses != null) { |
| for (@NonNull TC consumedTraceClassAnalysis : consumedTraceClassAnalyses) { |
| for (@NonNull TC subConsumedTraceClass : consumedTraceClassAnalysis.getSubTraceClassAnalyses()) { |
| for (@NonNull PR producer : subConsumedTraceClass.getProducers()) { |
| Set<@NonNull PR> producers = consumer2producers.get(consumer); |
| assert producers != null; |
| producers.add(producer); |
| if ((consumedElement2producers != null) && Iterables.contains(partialRegionAnalyses, producer)) { |
| Set<@NonNull PR> producers2 = consumedElement2producers.get(subConsumedTraceClass); |
| if (producers2 == null) { |
| producers2 = new HashSet<>(); |
| consumedElement2producers.put(subConsumedTraceClass, producers2); |
| } |
| producers2.add(producer); |
| } |
| } |
| } |
| } |
| } |
| Iterable<@NonNull TP> consumedTracePropertyAnalyses = consumer.getConsumedTracePropertyAnalyses(); |
| if (consumedTracePropertyAnalyses != null) { |
| for (@NonNull TP consumedTracePropertyAnalysis : consumedTracePropertyAnalyses) { |
| for (@NonNull PR producer : consumedTracePropertyAnalysis.getProducers()) { |
| Set<@NonNull PR> producers = consumer2producers.get(consumer); |
| assert producers != null; |
| producers.add(producer); |
| if ((consumedElement2producers != null) && Iterables.contains(partialRegionAnalyses, producer)) { |
| Set<@NonNull PR> producers2 = consumedElement2producers.get(consumedTracePropertyAnalysis); |
| if (producers2 == null) { |
| producers2 = new HashSet<>(); |
| consumedElement2producers.put(consumedTracePropertyAnalysis, producers2); |
| } |
| producers2.add(producer); |
| } |
| } |
| } |
| } |
| } |
| return consumer2producers; |
| } |
| |
| // |
| // Compute and return the inverse per-to closure of all froms given the from to tos closure. |
| // |
| public static <T> @NonNull Map<T, @NonNull Set<T>> computeInverseClosure(@NonNull Map<T, @NonNull Set<T>> from2tos) { |
| Map<T, @NonNull Set<T>> to2froms = new HashMap<>(); |
| Set<T> froms = from2tos.keySet(); |
| for (T to : froms) { |
| to2froms.put(to, new HashSet<>()); |
| } |
| for (T from : froms) { |
| Set<T> fromTos = from2tos.get(from); |
| assert fromTos != null; |
| for (T fromTo : fromTos) { |
| Set<T> fromToFroms = to2froms.get(fromTo); |
| assert fromToFroms != null; |
| fromToFroms.add(from); |
| } |
| } |
| return to2froms; |
| } |
| |
| /** |
| * Return a sequence of concurrencies, where each concurrency is a set of Partitions that may be executed |
| * concurrently, after their preceding concurrencies and before their subsequent concurrencies. The index in the |
| * sequence corresponds to the critical path length/depth from the earliest predecessor to the concurrent element. |
| */ |
| public static @NonNull List<@NonNull Concurrency> computeParallelSchedule( // FIXME this can be much faster with bit masks |
| @NonNull Map<@NonNull PartitionAnalysis, @NonNull Set<@NonNull PartitionAnalysis>> partitionAnalysis2predecessors) { |
| Map<@NonNull PartitionAnalysis, @NonNull Set<@NonNull PartitionAnalysis>> partition2successors = CompilerUtil.computeTransitiveSuccessors(partitionAnalysis2predecessors); |
| // |
| // Loop over the candidates to select those with no unscheduled predecessors. On the first iteration |
| // all partitions are considered, on subsequent iterations only successors of just scheduled partitions |
| // are reconsidered. |
| // |
| List<@NonNull Concurrency> parallelSchedule = new ArrayList<>(); |
| Set<@NonNull PartitionAnalysis> scheduledPartitionAnalyses = new HashSet<>(); |
| Set<@NonNull PartitionAnalysis> scheduleCandidates = new HashSet<>(partitionAnalysis2predecessors.keySet()); |
| while (!scheduleCandidates.isEmpty()) { |
| Concurrency nextConcurrency = new Concurrency(); |
| Set<@NonNull PartitionAnalysis> nextScheduleCandidates = new HashSet<>(); |
| for (@NonNull PartitionAnalysis partitionAnalysis : scheduleCandidates) { |
| Set<@NonNull PartitionAnalysis> predecessors = partitionAnalysis2predecessors.get(partitionAnalysis); |
| assert predecessors != null; |
| Set<@NonNull PartitionAnalysis> unscheduledPredecessors = new HashSet<>(predecessors); |
| // assert unscheduledPredecessors != null; |
| unscheduledPredecessors.remove(partitionAnalysis); |
| unscheduledPredecessors.removeAll(scheduledPartitionAnalyses); |
| if (unscheduledPredecessors.isEmpty()) { |
| nextConcurrency.add(partitionAnalysis); |
| Set<@NonNull PartitionAnalysis> unscheduledSuccessors = partition2successors.get(partitionAnalysis); |
| assert unscheduledSuccessors != null; |
| nextScheduleCandidates.addAll(unscheduledSuccessors); |
| } |
| } |
| if (nextConcurrency.size() <= 0) { // If scheduling a hopeless recursion - everything uses everything |
| Set<@NonNull PartitionAnalysis> minUnscheduledPredecessors = null; |
| for (@NonNull PartitionAnalysis partitionAnalysis : scheduleCandidates) { |
| Set<@NonNull PartitionAnalysis> predecessors = partitionAnalysis2predecessors.get(partitionAnalysis); |
| assert predecessors != null; |
| Set<@NonNull PartitionAnalysis> unscheduledPredecessors = new HashSet<>(predecessors); |
| // assert unscheduledPredecessors != null; |
| // unscheduledPredecessors.remove(partitionAnalysis); |
| unscheduledPredecessors.removeAll(scheduledPartitionAnalyses); |
| if ((minUnscheduledPredecessors == null) || (unscheduledPredecessors.size() < minUnscheduledPredecessors.size())) { |
| minUnscheduledPredecessors = unscheduledPredecessors; |
| } |
| } |
| assert minUnscheduledPredecessors != null; |
| for (@NonNull PartitionAnalysis partitionAnalysis : minUnscheduledPredecessors) { |
| nextConcurrency.add(partitionAnalysis); |
| } |
| } |
| assert nextConcurrency.size() > 0; |
| Concurrency nextNonCompositeConcurrency = null; |
| List<@NonNull CompositePartitionAnalysis> nextCompositePartitionAnalyses = null; |
| for (@NonNull PartitionAnalysis partitionAnalysis : nextConcurrency) { |
| nextScheduleCandidates.remove(partitionAnalysis); |
| scheduledPartitionAnalyses.add(partitionAnalysis); |
| if (partitionAnalysis instanceof CompositePartitionAnalysis) { |
| if (nextCompositePartitionAnalyses == null) { |
| nextCompositePartitionAnalyses = new ArrayList<>(); |
| } |
| nextCompositePartitionAnalyses.add((CompositePartitionAnalysis) partitionAnalysis); |
| } |
| else { |
| if (nextNonCompositeConcurrency == null) { |
| nextNonCompositeConcurrency = new Concurrency(); |
| } |
| nextNonCompositeConcurrency.add(partitionAnalysis); |
| } |
| } |
| if (nextNonCompositeConcurrency != null) { |
| parallelSchedule.add(nextNonCompositeConcurrency); |
| } |
| if (nextCompositePartitionAnalyses != null) { |
| for (CompositePartitionAnalysis compositePartitionAnalysis : nextCompositePartitionAnalyses) { |
| Concurrency nextCompositeConcurrency = new Concurrency(); |
| nextCompositeConcurrency.add(compositePartitionAnalysis); |
| parallelSchedule.add(nextCompositeConcurrency); |
| } |
| } |
| scheduleCandidates = nextScheduleCandidates; |
| } |
| return parallelSchedule; |
| } |
| |
| /** |
| * Return a map of the RAs that may execute before each PR. |
| * |
| * This is a simple type-conformance exercise. Find the sources that are supertypes of the target. |
| */ |
| public static <@NonNull PR extends PartialRegion<PR, TC, TP>, @NonNull TC extends TraceClass<PR, TC, TP>, @NonNull TP extends TraceProperty<PR, TC, TP>> @NonNull Map<@NonNull PR, @NonNull Set<@NonNull PR>> computeTransitivePredecessors(@NonNull Iterable<@NonNull PR> partialRegionAnalyses) { |
| Map<@NonNull PR, @NonNull Set<@NonNull PR>> consumer2producers = computeImmediatePredecessors(partialRegionAnalyses); |
| List<@NonNull PR> successors = Lists.newArrayList(partialRegionAnalyses); |
| Collections.sort(successors, NameUtil.NAMEABLE_COMPARATOR); |
| if (TransformationPartitioner.PREDECESSORS.isActive()) { |
| for (@NonNull PR successor : successors) { |
| StringBuilder s = new StringBuilder(); |
| s.append(successor + ":"); |
| List<@NonNull PR> producers = new ArrayList<>(consumer2producers.get(successor)); |
| Collections.sort(producers, NameUtil.NAMEABLE_COMPARATOR); |
| for (@NonNull PR producer : producers) { |
| s.append(" " + producer); |
| } |
| TransformationPartitioner.PREDECESSORS.println(s.toString()); |
| } |
| } |
| Map<@NonNull PR, @NonNull Set<@NonNull PR>> consumer2producersClosure = CompilerUtil.computeClosure(consumer2producers); |
| if (TransformationPartitioner.PREDECESSORS.isActive()) { |
| for (@NonNull PR successor : successors) { |
| StringBuilder s = new StringBuilder(); |
| s.append(successor + ":"); |
| List<@NonNull PR> producers = new ArrayList<>(consumer2producersClosure.get(successor)); |
| Collections.sort(producers, NameUtil.NAMEABLE_COMPARATOR); |
| for (@NonNull PR producer : producers) { |
| s.append(" " + producer); |
| } |
| TransformationPartitioner.PREDECESSORS.println(s.toString()); |
| } |
| } |
| return consumer2producersClosure; |
| } |
| |
| /** |
| * Return a map of the RAs that may execute after each PR. |
| * |
| * This is an inconvenient type-conformance exercise. Find the sources that are subtypes of the target. We therefore |
| * require the inverse computeTransitivePredecessors's result to be provided for inversion. |
| */ |
| public static <@NonNull PR extends Nameable/*PartialRegionAnalysis<@NonNull PR>*/> @NonNull Map<@NonNull PR, @NonNull Set<@NonNull PR>> computeTransitiveSuccessors(@NonNull Map<@NonNull PR, @NonNull Set<@NonNull PR>> partialRegion2predecessors) { |
| Map<@NonNull PR, @NonNull Set<@NonNull PR>> producer2consumersClosure = new HashMap<>(); |
| Iterable<@NonNull PR> partialRegions = partialRegion2predecessors.keySet(); |
| for (@NonNull PR predecessor : partialRegions) { |
| producer2consumersClosure.put(predecessor, new HashSet<>()); |
| } |
| for (@NonNull PR successor : partialRegions) { |
| Iterable<@NonNull PR> predecessors = partialRegion2predecessors.get(successor); |
| assert predecessors != null; |
| for (@NonNull PR predecessor : predecessors) { |
| Set<@NonNull PR> successors = producer2consumersClosure.get(predecessor); |
| assert successors != null; |
| successors.add(successor); |
| } |
| } |
| /* for (@NonNull PR regionAnalysis : regionAnalyses) { |
| producer2consumers.put(regionAnalysis, new HashSet<>()); |
| } |
| for (@NonNull PR producer : regionAnalyses) { |
| Iterable<@NonNull PR> successors = producer.getSuccessors(); // Used by no-success QVTc trace |
| if (successors != null) { |
| for (@NonNull PR successor : successors) { |
| Set<@NonNull PR> consumers = producer2consumers.get(producer); |
| assert consumers != null; |
| consumers.add(successor); |
| } |
| } |
| Iterable<@NonNull TC> producedTraceClassAnalyses = producer.getProducedTraceClassAnalyses(); |
| if (producedTraceClassAnalyses != null) { |
| for (@NonNull TC producedTraceClassAnalysis : producedTraceClassAnalyses) { |
| for (@NonNull TC superProducedTraceClassAnalysis : producedTraceClassAnalysis.getSuperTraceClassAnalyses()) { |
| for (@NonNull PR consumer : superProducedTraceClassAnalysis.getConsumers()) { |
| Set<@NonNull PR> consumers = producer2consumers.get(producer); |
| assert consumers != null; |
| consumers.add(consumer); |
| } |
| } |
| } |
| } |
| Iterable<@NonNull TracePropertyAnalysis<@NonNull PR>> producedTracePropertyAnalyses = producer.getProducedTracePropertyAnalyses(); |
| if (producedTracePropertyAnalyses != null) { |
| for (@NonNull TracePropertyAnalysis<@NonNull PR> producedTracePropertyAnalysis : producedTracePropertyAnalyses) { |
| for (@NonNull PR consumer : producedTracePropertyAnalysis.getConsumers()) { |
| Set<@NonNull PR> consumers = producer2consumers.get(producer); |
| assert consumers != null; |
| consumers.add(consumer); |
| } |
| } |
| } |
| } |
| if (TransformationPartitioner.SUCCESSORS.isActive()) { |
| for (@NonNull PR successor : regionAnalyses) { |
| StringBuilder s = new StringBuilder(); |
| s.append(successor + ":"); |
| List<@NonNull PR> consumers = new ArrayList<>(producer2consumers.get(successor)); |
| Collections.sort(consumers, NameUtil.NAMEABLE_COMPARATOR); |
| for (@NonNull PR consumer : consumers) { |
| s.append(" " + consumer); |
| } |
| TransformationPartitioner.SUCCESSORS.println(s.toString()); |
| } |
| } |
| Map<@NonNull PR, @NonNull Set<@NonNull PR>> producer2consumersClosure = CompilerUtil.computeClosure(producer2consumers); */ |
| if (TransformationPartitioner.SUCCESSORS.isActive()) { |
| List<@NonNull PR> predecessors = Lists.newArrayList(partialRegions); |
| Collections.sort(predecessors, NameUtil.NAMEABLE_COMPARATOR); |
| for (@NonNull PR predecessor : predecessors) { |
| StringBuilder s = new StringBuilder(); |
| s.append(predecessor + ":"); |
| List<@NonNull PR> consumers = new ArrayList<>(producer2consumersClosure.get(predecessor)); |
| Collections.sort(consumers, NameUtil.NAMEABLE_COMPARATOR); |
| for (@NonNull PR consumer : consumers) { |
| s.append(" " + consumer); |
| } |
| TransformationPartitioner.SUCCESSORS.println(s.toString()); |
| } |
| } |
| return producer2consumersClosure; |
| } |
| |
| public static @NonNull PartitionProblem createPartitionError(@NonNull Partition partition, @NonNull String messageTemplate, Object... bindings) { |
| String boundMessage = StringUtil.bind(messageTemplate, bindings); |
| return new PartitionProblem(CompilerProblem.Severity.ERROR, partition, boundMessage); |
| } |
| |
| public static @NonNull PartitionProblem createPartitionWarning(@NonNull Partition partition, @NonNull String messageTemplate, Object... bindings) { |
| String boundMessage = StringUtil.bind(messageTemplate, bindings); |
| return new PartitionProblem(CompilerProblem.Severity.WARNING, partition, boundMessage); |
| } |
| |
| public static @NonNull RegionProblem createRegionError(@NonNull Region region, @NonNull String messageTemplate, Object... bindings) { |
| String boundMessage = StringUtil.bind(messageTemplate, bindings); |
| return new RegionProblem(CompilerProblem.Severity.ERROR, region, boundMessage); |
| } |
| |
| public static @NonNull RegionProblem createRegionWarning(@NonNull Region region, @NonNull String messageTemplate, Object... bindings) { |
| String boundMessage = StringUtil.bind(messageTemplate, bindings); |
| return new RegionProblem(CompilerProblem.Severity.WARNING, region, boundMessage); |
| } |
| |
| public static @NonNull List<@NonNull PartitionAnalysis> gatherPartitionAnalyses(@NonNull CompositePartitionAnalysis rootPartitionAnalysis, @NonNull List<@NonNull PartitionAnalysis> allPartitionAnalyses) { |
| for (@NonNull PartitionAnalysis partitionAnalysis : rootPartitionAnalysis.getPartitionAnalyses()) { |
| assert !allPartitionAnalyses.contains(partitionAnalysis); |
| if (partitionAnalysis instanceof CompositePartitionAnalysis) { |
| gatherPartitionAnalyses((CompositePartitionAnalysis)partitionAnalysis, allPartitionAnalyses); |
| } |
| else { |
| allPartitionAnalyses.add(partitionAnalysis); |
| } |
| } |
| return allPartitionAnalyses; |
| } |
| |
| public static @NonNull GenPackage getGenPackage(@NonNull ResourceSet resourceSet, @NonNull String resourcePath, @Nullable String fragment) { |
| URI uri = URI.createPlatformResourceURI(resourcePath, false); |
| if (fragment != null) { |
| uri = uri.appendFragment(fragment); |
| } |
| return ClassUtil.nonNullState((GenPackage)resourceSet.getEObject(uri, true)); |
| } |
| |
| public static void indent(@NonNull StringBuilder s, int depth) { |
| for (int i = 0; i < depth; i++) { |
| s.append(" "); |
| } |
| } |
| |
| public static boolean isAbstract(@NonNull Partition partition) { |
| Region originalRegion = partition.getRegion(); |
| if (originalRegion instanceof RuleRegion) { |
| if (originalRegion instanceof DispatchRegion) { |
| return false; |
| } |
| if (originalRegion instanceof VerdictRegion) { |
| return false; |
| } |
| return ((RuleRegion)originalRegion).getReferredRule().isIsAbstract(); |
| } |
| if (originalRegion instanceof DispatchRegion) { |
| return false; |
| } |
| return false; |
| } |
| |
| /** |
| * Normalize a list of ENamedElement by sorting according to their name. |
| * |
| public static void normalize(@NonNull List<@NonNull ? extends ENamedElement> nameables) { |
| if (nameables instanceof EList<?>) { |
| ECollections.sort((EList<? extends ENamedElement>)nameables, NameUtil.ENamedElementComparator.INSTANCE); |
| } |
| else { |
| Collections.sort(nameables, NameUtil.ENamedElementComparator.INSTANCE); |
| } |
| } */ |
| |
| /** |
| * Normalize a list of Nameable by sorting according to their name. |
| */ |
| public static void normalizeNameables(@NonNull List<@NonNull ? extends Nameable> nameables) { |
| if (nameables instanceof EList<?>) { |
| ECollections.sort((EList<? extends Nameable>)nameables, NameUtil.NAMEABLE_COMPARATOR); |
| } |
| else { |
| Collections.sort(nameables, NameUtil.NAMEABLE_COMPARATOR); |
| } |
| } |
| |
| /** |
| * Return the variable name associated with oclExpression, or null if none found. |
| * This enables the user's choice of name to be used for the expression node that implements it. |
| */ |
| public static @Nullable String recoverVariableName(@NonNull NamedElement namedElement) { |
| EObject eContainer = namedElement.eContainer(); |
| EReference eContainmentFeature = namedElement.eContainmentFeature(); |
| if ((eContainmentFeature == PivotPackage.Literals.VARIABLE__OWNED_INIT) && (eContainer instanceof Variable)) { |
| return ((Variable)eContainer).getName(); |
| } |
| else if ((eContainmentFeature == QVTcorePackage.Literals.ASSIGNMENT__VALUE) && (eContainer instanceof VariableAssignment)) { |
| return ((VariableAssignment)eContainer).getTargetVariable().getName(); |
| } |
| else if ((eContainmentFeature == PivotPackage.Literals.CALL_EXP__OWNED_SOURCE) && (eContainer instanceof OperationCallExp)) { |
| OperationCallExp operationCallExp = (OperationCallExp)eContainer; |
| if (PivotUtil.isSameOperation(operationCallExp.getReferredOperation().getOperationId(), OperationId.OCLANY_EQUALS)) { |
| OCLExpression argument = PivotUtil.getOwnedArgument(operationCallExp, 0); |
| if (argument instanceof VariableExp) { |
| return PivotUtil.getReferredVariable((VariableExp)argument).getName(); |
| } |
| } |
| } |
| else if ((eContainmentFeature == PivotPackage.Literals.OPERATION_CALL_EXP__OWNED_ARGUMENTS) && (eContainer instanceof OperationCallExp)) { |
| OperationCallExp operationCallExp = (OperationCallExp)eContainer; |
| if (PivotUtil.isSameOperation(operationCallExp.getReferredOperation().getOperationId(), OperationId.OCLANY_EQUALS)) { |
| OCLExpression source = PivotUtil.getOwnedSource(operationCallExp); |
| if (source instanceof VariableExp) { |
| return PivotUtil.getReferredVariable((VariableExp)source).getName(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static <T> void removeAll(@NonNull Collection<T> removeFrom, @NonNull Iterable<T> elementsToTemove) { |
| for (T element : elementsToTemove) { |
| removeFrom.remove(element); |
| } |
| } |
| |
| public static void throwExceptionWithProblems(@NonNull CodeGenerator codeGenerator, @NonNull Exception e) throws Exception { |
| List<@NonNull Exception> problems = codeGenerator.getProblems(); |
| if (problems != null) { |
| StringBuilder s = new StringBuilder(); |
| for (@NonNull Exception ex : problems) { |
| s.append(ex.toString() + "\n"); |
| } |
| s.append(e.toString()); |
| throw new CompilerChainException(e, s.toString()); |
| } |
| else { |
| throw e; |
| } |
| } |
| } |