Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 63be3269d2f68822a352c2e9fcfd84549b66650a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*******************************************************************************
 * 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.trace;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.ocl.pivot.utilities.TracingOption;
import org.eclipse.qvtd.compiler.CompilerConstants;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.HeadAnalysis;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.HeadNodeGroup;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.ScheduleManager;
import org.eclipse.qvtd.compiler.internal.utilities.CompilerUtil;
import org.eclipse.qvtd.pivot.qvtschedule.CastEdge;
import org.eclipse.qvtd.pivot.qvtschedule.Edge;
import org.eclipse.qvtd.pivot.qvtschedule.KeyPartEdge;
import org.eclipse.qvtd.pivot.qvtschedule.MappingRegion;
import org.eclipse.qvtd.pivot.qvtschedule.NavigationEdge;
import org.eclipse.qvtd.pivot.qvtschedule.Node;
import org.eclipse.qvtd.pivot.qvtschedule.PredicateEdge;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil;

import com.google.common.collect.Sets;

/**
 * TracedHeadAnalysis supports a multi-directional analysis performed before the trace nodes are synthesized.
 * It facilitates the determination of the unit-oppositeness of trace to model properties.
 */
public class TracedHeadAnalysis extends HeadAnalysis
{
	public static final @NonNull TracingOption TRACED_HEAD_NODE_GROUPS = new TracingOption(CompilerConstants.PLUGIN_ID, "qvtr2qvts/trace/headNodeGroups");
	public static final @NonNull TracingOption TRACED_HEAD_IMMEDIATE_SOURCES = new TracingOption(CompilerConstants.PLUGIN_ID, "qvtr2qvts/trace/headSources");

	/**
	 * Return the head(s) of each TypedModel, mapped to the closure of elements that are transitively and bidirectionally one to one navigable from them.
	 *
	 * This computation has no side effects.
	 */
	public static @NonNull List<@NonNull HeadNodeGroup> computeTraceHeadGroupNodes(@NonNull ScheduleManager scheduleManager, @NonNull MappingRegion mappingRegion) {
		//	String name = mappingRegion.getName();
		TracedHeadAnalysis mappingRegionAnalysis = new TracedHeadAnalysis(mappingRegion);
		Map<@NonNull Node, @NonNull Set<@NonNull Node>> targetFromSources = mappingRegionAnalysis.computeTracedTargetFromSources();
		if (TRACED_HEAD_IMMEDIATE_SOURCES.isActive()) {
			StringBuilder s = new StringBuilder();
			List<@NonNull Node> targets = new ArrayList<>(targetFromSources.keySet());
			Collections.sort(targets, NameUtil.NAMEABLE_COMPARATOR);
			for (@NonNull Node target : targets) {
				s.append("\n  " + target.getName() + ":");
				List<@NonNull Node> sources = new ArrayList<>(targetFromSources.get(target));
				Collections.sort(sources, NameUtil.NAMEABLE_COMPARATOR);
				for (@NonNull Node source : sources) {
					s.append(" " + source.getName());
				}
			}
			TRACED_HEAD_IMMEDIATE_SOURCES.println(s.toString());
		}
		//
		//	Ripple the local target-from-sources to compute the overall target-from-sources closure.
		//
		Map<@NonNull Node, @NonNull Set<@NonNull Node>> targetFromSourcesClosure = CompilerUtil.computeClosure(targetFromSources);
		//
		//	Compute the inverse Set of all source nodes from which a particular target node can be reached by transitive to-one navigation.
		//
		Map<@NonNull Node, @NonNull Set<@NonNull Node>> sourceToTargetsClosure = CompilerUtil.computeInverseClosure(targetFromSourcesClosure);
		//
		//	Locate the head node groups of each to-one navigable sub-region.
		//
		List<@NonNull HeadNodeGroup> headNodeGroups = mappingRegionAnalysis.computeHeadNodeGroups(targetFromSourcesClosure, sourceToTargetsClosure, null);
		Collections.sort(headNodeGroups, NameUtil.NAMEABLE_COMPARATOR);
		if (TRACED_HEAD_NODE_GROUPS.isActive()) {
			StringBuilder s = new StringBuilder();
			s.append(mappingRegion.getName());
			for (@NonNull HeadNodeGroup headNodeGroup : headNodeGroups) {
				s.append("\n\t");
				headNodeGroup.appendTo(s);
			}
			TRACED_HEAD_NODE_GROUPS.println(s.toString());
		}
		return headNodeGroups;
	}

	protected TracedHeadAnalysis(@NonNull MappingRegion mappingRegion) {
		super(mappingRegion);
	}

	private @NonNull Map<@NonNull Node, @NonNull Set<@NonNull Node>> computeTracedTargetFromSources() {
		Map<@NonNull Node, @NonNull Set<@NonNull Node>> targetFromSources = new HashMap<>();
		for (@NonNull Node targetNode : QVTscheduleUtil.getOwnedNodes(mappingRegion)) {
			if (targetNode.isMatched() && !targetNode.isConstant()) {
				Set<@NonNull Node> sources = targetFromSources.get(targetNode);
				if (sources == null) {
					sources = Sets.newHashSet(targetNode);
					targetFromSources.put(targetNode, sources);
				}
				for (@NonNull Edge edge : QVTscheduleUtil.getIncomingEdges(targetNode)) {
					Node sourceNode = edge.getEdgeSource();
					if (sourceNode.isMatched() && !sourceNode.isConstant()) {
						if (edge instanceof NavigationEdge) {
							if (!((NavigationEdge)edge).isPartial()) {
								sources.add(sourceNode);
							}
						}
						else if (edge instanceof CastEdge) {
							sources.add(sourceNode);
						}
						else if (edge instanceof KeyPartEdge) {
							sources.add(sourceNode);
						}
						else if (edge instanceof PredicateEdge) {
							sources.add(sourceNode);
						}
					}
				}
			}
		}
		return targetFromSources;
	}

	@Override
	protected @NonNull HeadNodeGroup createHeadNodeGroup(@NonNull List<@NonNull Node> headNodeGroup) {
		return new TracedHeadNodeGroup(headNodeGroup);
	}
}

Back to the top