| /******************************************************************************* |
| * Copyright (c) 2015, 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.qvtb2qvts; |
| |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Deque; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.pivot.utilities.NameUtil; |
| import org.eclipse.ocl.pivot.utilities.Nameable; |
| import org.eclipse.qvtd.pivot.qvtschedule.Edge; |
| import org.eclipse.qvtd.pivot.qvtschedule.KeyPartEdge; |
| import org.eclipse.qvtd.pivot.qvtschedule.Node; |
| import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil; |
| |
| import com.google.common.collect.Iterables; |
| |
| /** |
| * A HeadNodeGroup identifies a set of mutually one to one navigable nodes from which many nodes can be navigated. |
| * |
| * If necessary a HeadNodeGroup supports a determination that all nodes in one head node group can be derived from |
| * those in another. |
| */ |
| public abstract class HeadNodeGroup implements Nameable |
| { |
| /** |
| * The mutually to-one navigable nodes that define the head group. |
| */ |
| private final @NonNull List<@NonNull Node> headGroupNodes; |
| |
| /** |
| * The object nodes currently known to be to-one navigable from the head. |
| */ |
| private Set<@NonNull Node> uniqueNodes = null; |
| |
| public HeadNodeGroup(@NonNull List<@NonNull Node> headGroupNodes) { |
| assert !headGroupNodes.isEmpty(); |
| this.headGroupNodes = headGroupNodes; |
| } |
| |
| private boolean accumulateReachableTargets(@NonNull Deque<@NonNull Node> workList) { |
| Node sourceNode = workList.removeFirst(); |
| assert sourceNode.isMatched(); |
| boolean gotOne = false; |
| for (@NonNull Edge source2targetEdge : QVTscheduleUtil.getOutgoingEdges(sourceNode)) { |
| Node targetNode = QVTscheduleUtil.getTargetNode(source2targetEdge); |
| if (targetNode.isMatched() && canBeSameGroup(sourceNode, source2targetEdge) && !uniqueNodes.contains(targetNode)) { |
| if (source2targetEdge.isCast()) { // Can happen when analyzing traced heads |
| uniqueNodes.add(targetNode); |
| workList.add(targetNode); |
| gotOne = true; |
| } |
| else if (source2targetEdge.isPredicate()) { |
| uniqueNodes.add(targetNode); |
| gotOne = true; |
| } |
| else if (source2targetEdge.isNavigation()) { |
| if (!source2targetEdge.isPartial()) { |
| uniqueNodes.add(targetNode); |
| workList.add(targetNode); |
| gotOne = true; |
| } |
| } |
| else if (source2targetEdge.isComputation()) { |
| boolean allArgumentsReachable = true; |
| for (@NonNull Edge argumentEdge : QVTscheduleUtil.getIncomingEdges(targetNode)) { |
| if ((argumentEdge != source2targetEdge) && argumentEdge.isComputation()) { |
| Node argumentNode = QVTscheduleUtil.getSourceNode(argumentEdge); |
| if (argumentNode.isMatched()) { |
| if (!argumentNode.isConstant() && !uniqueNodes.contains(argumentNode)) { |
| allArgumentsReachable = false; |
| break; |
| } |
| } |
| } |
| } |
| if (allArgumentsReachable) { |
| uniqueNodes.add(targetNode); |
| workList.add(targetNode); |
| gotOne = true; |
| } |
| } |
| } |
| } |
| for (@NonNull Edge target2sourceEdge : QVTscheduleUtil.getIncomingEdges(sourceNode)) { |
| if (target2sourceEdge instanceof KeyPartEdge) { // FIXME KeyPartEdge is bidirectional |
| Node targetNode = QVTscheduleUtil.getSourceNode(target2sourceEdge); |
| uniqueNodes.add(targetNode); |
| if (targetNode.isMatched()) { |
| workList.add(targetNode); |
| } |
| gotOne = true; |
| } |
| } |
| return gotOne; |
| } |
| |
| private void accumulateReachables() { |
| Deque<@NonNull Node> workList = new ArrayDeque<>(headGroupNodes); |
| uniqueNodes = new HashSet<>(headGroupNodes); |
| while (!workList.isEmpty()) { |
| accumulateReachableTargets(workList); |
| } |
| } |
| |
| public void appendTo(@NonNull StringBuilder s) { |
| s.append("heads:"); |
| List<@NonNull Node> nodeList1 = new ArrayList<>(headGroupNodes); |
| Collections.sort(nodeList1, NameUtil.NAMEABLE_COMPARATOR); |
| for (@NonNull Node node : nodeList1) { |
| s.append(" "); |
| s.append(node.getName()); |
| } |
| if (uniqueNodes != null) { |
| List<@NonNull Node> nodeList2 = new ArrayList<>(uniqueNodes); |
| nodeList2.removeAll(headGroupNodes); |
| if (nodeList2.size() > 0) { |
| Collections.sort(nodeList2, NameUtil.NAMEABLE_COMPARATOR); |
| s.append(", to-ones:"); |
| for (@NonNull Node node : nodeList2) { |
| s.append(" "); |
| s.append(node.getName()); |
| } |
| } |
| } |
| } |
| |
| protected abstract boolean canBeSameGroup(@NonNull Node sourceNode, @NonNull Edge source2targetEdge); |
| |
| public @NonNull Iterable<@NonNull Node> getHeadNodes() { |
| return headGroupNodes; |
| } |
| |
| public @NonNull Node getPreferredHeadNode(@Nullable Iterable<@NonNull Node> preferredHeadNodes) { |
| if (preferredHeadNodes != null) { |
| for (@NonNull Node node : headGroupNodes) { |
| if (Iterables.contains(preferredHeadNodes, node)) { |
| return node; |
| } |
| } |
| } |
| return headGroupNodes.get(0); |
| } |
| |
| private @NonNull Set<@NonNull Node> getToOneSet() { |
| Set<@NonNull Node> toOneSet2 = uniqueNodes; |
| if (toOneSet2 == null) { |
| accumulateReachables(); |
| toOneSet2 = uniqueNodes; |
| assert toOneSet2 != null; |
| } |
| return toOneSet2; |
| } |
| |
| /** |
| * Return true if all of the headNodes of this HeadNodeGroup are reachable as to-one nodess of thatHeadNodeGroup. |
| */ |
| public boolean isDeriveableFrom(@NonNull HeadNodeGroup thatHeadNodeGroup) { |
| return thatHeadNodeGroup.getToOneSet().containsAll(headGroupNodes); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder s = new StringBuilder(); |
| s.append(headGroupNodes.iterator().next().getOwningRegion()); |
| s.append("\n\theads:"); |
| for (@NonNull Node node : headGroupNodes) { |
| s.append("\n\t\t"); |
| s.append(node); |
| } |
| if (uniqueNodes != null) { |
| s.append("\n\tto-ones:"); |
| for (@NonNull Node node : uniqueNodes) { |
| s.append("\n\t\t"); |
| s.append(node); |
| } |
| } |
| return s.toString(); |
| } |
| } |