Skip to main content
summaryrefslogtreecommitdiffstats
blob: 32d83c181202f1a51214be7447f749973a18e910 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/*******************************************************************************
 * Copyright (c) 2015, 2016 Willink Transformations and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   E.D.Willink - Initial API and implementation
 *******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvts2qvti;

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

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableExp;
import org.eclipse.ocl.pivot.ids.IdManager;
import org.eclipse.ocl.pivot.ids.OperationId;
import org.eclipse.ocl.pivot.ids.ParametersId;
import org.eclipse.ocl.pivot.ids.TypeId;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.ClassDatumAnalysis;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.CyclicScheduledRegion;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.NodeConnection;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Region;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtimperative.AddStatement;
import org.eclipse.qvtd.pivot.qvtimperative.ConnectionVariable;
import org.eclipse.qvtd.pivot.qvtimperative.ImperativeDomain;
import org.eclipse.qvtd.pivot.qvtimperative.GuardPattern;
import org.eclipse.qvtd.pivot.qvtimperative.MappingCall;
import org.eclipse.qvtd.pivot.qvtimperative.MappingCallBinding;
import org.eclipse.qvtd.pivot.qvtimperative.MappingStatement;
import org.eclipse.qvtd.pivot.qvtimperative.QVTimperativeFactory;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;

import com.google.common.collect.Iterables;

public class CyclicScheduledRegion2Mapping extends AbstractScheduledRegion2Mapping
{
	/**
	 * RecursionContext coordinates the variables associated with one (of many) recursive connections in a CyclicRegion.
	 */
	private class RecursionContext
	{
		/**
		 * The recursing type.
		 */
		private final @NonNull ClassDatumAnalysis classDatumAnalysis;

		/**
		 * A distinctive number for distinctive auto-generated per-recursion names.
		 */
		private final int index;

		/**
		 * The accumulated output connection for the recursed type.
		 */
		private NodeConnection accumulatedConnection;

		/**
		 * The guard variable for the recursed type.
		 */
		private final @NonNull Variable guardVariable;

		/**
		 * The local accumulation variable for the recursed type. Already processed values may be present.
		 */
		private final @NonNull ConnectionVariable localVariable;

		/**
		 * The filtered local accumulation variable for the recursed type. Already processed values have been removed.
		 */
		private @Nullable ConnectionVariable newVariable;

		/**
		 * The accumulated output variable for the recursed type.
		 */
		private ConnectionVariable accumulatedVariable;

		public RecursionContext(@NonNull Node headNode) {
			this.classDatumAnalysis = headNode.getClassDatumAnalysis();
			this.index = classDatumAnalysis2recursion.size();
			//
			//	Create/locate the domain and guard pattern for the guard.
			//
			GuardPattern guardPattern = getGuardPattern(classDatumAnalysis);
			//
			//	Create the domain guard variable.
			//
			org.eclipse.ocl.pivot.Class elementType = classDatumAnalysis.getCompleteClass().getPrimaryClass();
			guardVariable = PivotUtil.createVariable(getSafeName(headNode), elementType, false, null);
			guardPattern.getVariable().add(guardVariable);

			Iterable<@NonNull NodeConnection> outgoingConnections = headNode.getOutgoingPassedConnections();
			assert Iterables.size(outgoingConnections) == 1;
			NodeConnection outgoingConnection = Iterables.get(outgoingConnections, 0);
			//			Class elementType = classDatumAnalysis.getCompleteClass().getPrimaryClass();
			//			Variable variable = PivotUtil.createVariable(getSafeName(headNode), elementType, false, null);
			//			guardPattern.getVariable().add(variable);
			//			Variable oldVariable = classDatumAnalysis2headVariable.put(classDatumAnalysis, variable);
			//			assert oldVariable == null;
			NodeConnection incomingConnection = headNode.getIncomingPassedConnection();
			assert incomingConnection != null;
			connection2variable.put(incomingConnection, guardVariable);
			//
			//	Create the local accumulation variable.
			//
			//				Iterable<@NonNull NodeConnection> internallyPassedConnections = headNode.getOutgoingPassedConnections();
			//				assert Iterables.size(internallyPassedConnections) == 1;
			//				ClassDatumAnalysis incomingClassDatumAnalysis = incomingConnection.getClassDatumAnalysis();
			//				NodeConnection internallyPassedConnection = Iterables.get(internallyPassedConnections, 0);
			//				ClassDatumAnalysis outgoingClassDatumAnalysis = internallyPassedConnection.getClassDatumAnalysis();
			//				if ((outgoingClassDatumAnalysis == incomingClassDatumAnalysis)
			//				 && !incoming2outgoing.values().contains(internallyPassedConnection)) {	// Multiple should not occur, but line them up pair-wise
			//					incoming2outgoing.put(incomingConnection, internallyPassedConnection);
			Type asType = getConnectionSourcesType(incomingConnection);
			String localName = "«local" + (index > 0 ? Integer.toString(index) : "") + "»";
			localVariable = helper.createConnectionVariable(localName, asType, null);
			mapping.getBottomPattern().getVariable().add(localVariable);
			connection2variable.put(outgoingConnection, localVariable);
			//
			if ((asType instanceof CollectionType) && ((CollectionType)asType).isUnique()) {
				String newName = "«new" + (index > 0 ? Integer.toString(index) : "") + "»";
				ConnectionVariable newVariable2 = newVariable = helper.createConnectionVariable(newName, asType, null);
				mapping.getBottomPattern().getVariable().add(newVariable2);
				connection2variable.put(outgoingConnection, newVariable2);
			}
			else {
				allRecursionsAreUnique = false;
			}
		}

		@SuppressWarnings("unused")
		public @Nullable NodeConnection getAccumulatedConnection() {
			return accumulatedConnection;
		}

		public @Nullable ConnectionVariable getAccumulatedVariable() {
			return accumulatedVariable;
		}

		public @NonNull ClassDatumAnalysis getClassDatumAnalysis() {
			return classDatumAnalysis;
		}

		public @NonNull Variable getGuardVariable() {
			return guardVariable;
		}

		public @NonNull ConnectionVariable getLocalVariable() {
			return localVariable;
		}

		public @Nullable ConnectionVariable getNewVariable() {
			return newVariable;
		}

		public void setAccumulatedConnection(@NonNull NodeConnection accumulatedConnection) {
			this.accumulatedConnection = accumulatedConnection;
			//
			//	Select a/the outgoing recursive intermediate connection.
			//
			NodeConnection intermediateConnection = accumulatedConnection;
			ConnectionVariable accumulatedVariable2 = accumulatedVariable = createConnectionVariable(intermediateConnection);
			mapping.getGuardPattern().getVariable().add(accumulatedVariable2);
			connection2variable.put(intermediateConnection, accumulatedVariable2);
		}
	}

	/**
	 * The recursions.
	 */
	private final @NonNull Map<@NonNull ClassDatumAnalysis, @NonNull RecursionContext> classDatumAnalysis2recursion = new HashMap<@NonNull ClassDatumAnalysis, @NonNull RecursionContext>();

	/**
	 * True if all recursions use Set accumulators allowing the unqiueness to be determined by excliusion rather than
	 * by re-invocation suppression.
	 */
	private boolean allRecursionsAreUnique = false;

	/**
	 * Cache of the domains created for each recursiing typed model
	 */
	private final @NonNull Map<@NonNull TypedModel, @NonNull ImperativeDomain> typedModel2domain = new HashMap<>();

	public CyclicScheduledRegion2Mapping(@NonNull QVTs2QVTiVisitor visitor, @NonNull CyclicScheduledRegion region) {
		super(visitor, region);
		//
		//	Create a guard for each head and place it in a corresponding domain.
		//	Create a local accumulation variable for each head and place it the the middle bottom domain.
		//	Create an outgoing accumulation variable for each head and place it the the middle guard domain.
		//
		for (@NonNull Node headNode : region.getHeadNodes()) {
			//
			//	Create/locate the domain and guard pattern for the guard.
			//
			RecursionContext newRecursion = new RecursionContext(headNode);
			RecursionContext oldRecursion = classDatumAnalysis2recursion.put(newRecursion.getClassDatumAnalysis(), newRecursion);
			assert oldRecursion == null;
		}
		for (@NonNull NodeConnection accumulatedConnection : region.getIntermediateConnections()) {
			ClassDatumAnalysis classDatumAnalysis = accumulatedConnection.getClassDatumAnalysis();
			RecursionContext recursion = classDatumAnalysis2recursion.get(classDatumAnalysis);
			if (recursion != null) {
				recursion.setAccumulatedConnection(accumulatedConnection);
			}
		}
		//
		//	Create any non-recursion connectionVariable guards
		//
		createConnectionGuardVariables();
	}

	/**
	 * Create the guard variables for the intermediate connections that are not recursions.
	 */
	@Override
	protected void createConnectionGuardVariables() {
		List<@NonNull NodeConnection> intermediateConnections = region.getIntermediateConnections();
		for (@NonNull NodeConnection intermediateConnection : intermediateConnections) {
			Variable connectionVariable = connection2variable.get(intermediateConnection);
			if (connectionVariable == null) {
				String name = intermediateConnection.getName();
				assert name != null;
				connectionVariable = helper.createConnectionVariable(name, getConnectionSourcesType(intermediateConnection), null);
				connection2variable.put(intermediateConnection, connectionVariable);
				mapping.getGuardPattern().getVariable().add(connectionVariable);
			}
		}
	}

	@Override
	public @NonNull MappingCall createMappingCall(@NonNull List<@NonNull MappingCallBinding> mappingCallBindings) {
		MappingCall mappingCall = super.createMappingCall(mappingCallBindings);
		if (!allRecursionsAreUnique) {
			mappingCall.setIsInfinite(true);		// FIXME share code
		}
		return mappingCall;
	}

	@Override
	protected @NonNull OCLExpression createSelectByKind(@NonNull Node resultNode) {
		throw new UnsupportedOperationException();
		/*		Variable resultVariable = classDatumAnalysis2headVariable.get(resultNode.getClassDatumAnalysis());
		if (resultVariable == null) {
			OCLExpression asSource = createNullLiteralExp(); //PivotUtil.createVariableExp(getChildrenVariable());
			CompleteClass sourceCompleteClass = resultNode.getCompleteClass();
			CollectionType sourceCollectionType = (CollectionType) sourceCompleteClass.getPrimaryClass();
			Type sourceElementType = sourceCollectionType.getElementType();
			assert sourceElementType != null;
			CompleteClass sourceElementClass = visitor.getEnvironmentFactory().getCompleteModel().getCompleteClass(sourceElementType);
			OCLExpression asTypeExp = createTypeExp(sourceElementClass);
			OCLExpression selectExp = createOperationCallExp(asSource, getSelectByKindOperation(), asTypeExp);
			resultVariable = PivotUtil.createVariable(resultNode.getName(), selectExp);
			mapping.getBottomPattern().getVariable().add(resultVariable);
			classDatumAnalysis2headVariable.put(resultNode.getClassDatumAnalysis(), resultVariable);
		}
		return PivotUtil.createVariableExp(resultVariable); */
	}

	@Override
	public void createStatements() {
		List<@NonNull MappingStatement> mappingStatements = new ArrayList<>();
		for (@NonNull Region callableRegion : region.getCallableChildren()) {
			AbstractRegion2Mapping calledRegion2Mapping = visitor.getRegion2Mapping(callableRegion);
			Map<@NonNull Variable, @NonNull OCLExpression> guardVariable2expression = new HashMap<@NonNull Variable, @NonNull OCLExpression>();
			for (@NonNull Node calledHeadNode : callableRegion.getHeadNodes()) {
				NodeConnection headConnection = calledHeadNode.getIncomingConnection();
				assert headConnection != null;
				Node callingHeadNode = Iterables.get(headConnection.getSourceNodes(), 0);
				Variable callingHeadVariable = getGuardVariable(callingHeadNode);
				Variable calledHeadVariable = calledRegion2Mapping.getGuardVariable(calledHeadNode);
				guardVariable2expression.put(calledHeadVariable, PivotUtil.createVariableExp(callingHeadVariable));
			}
			for (@NonNull NodeConnection intermediateConnection : callableRegion.getIntermediateConnections()) {
				RecursionContext recursion = classDatumAnalysis2recursion.get(intermediateConnection.getClassDatumAnalysis());
				if (recursion != null) {
					Variable callingLocalVariable = recursion.getLocalVariable();
					//					NodeConnection tailConnection = recursion.getAccumulatedConnection();
					//					if (tailConnection != null) {
					Variable calledTailVariable = calledRegion2Mapping.getConnectionVariable(intermediateConnection);
					guardVariable2expression.put(calledTailVariable, PivotUtil.createVariableExp(callingLocalVariable));
					//					}
				}
			}
			mappingStatements.add(createCall(callableRegion, guardVariable2expression));
		}
		//
		//	Create connection assignments to pass local creations to the accumulated result.
		//
		for (@NonNull RecursionContext recursion : classDatumAnalysis2recursion.values()) {
			ConnectionVariable accumulatedVariable = recursion.getAccumulatedVariable();
			if (accumulatedVariable != null) {
				ConnectionVariable localVariable = recursion.getLocalVariable();
				ConnectionVariable newVariable = recursion.getNewVariable();
				if (newVariable != null) {
					ParametersId parametersId = IdManager.getParametersId(TypeId.COLLECTION.getSpecializedId(TypeId.OCL_ANY));
					OperationId operationId = TypeId.COLLECTION.getOperationId(1, "excludingAll", parametersId);
					Operation operation = visitor.getEnvironmentFactory().getIdResolver().getOperation(operationId);
					VariableExp localVariableExp = PivotUtil.createVariableExp(localVariable);
					VariableExp resultVariableExp = PivotUtil.createVariableExp(accumulatedVariable);
					OperationCallExp excludingAllCallExp = PivotUtil.createOperationCallExp(localVariableExp, operation, resultVariableExp);
					excludingAllCallExp.setType(localVariableExp.getType());
					excludingAllCallExp.setIsRequired(localVariableExp.isIsRequired());


					AddStatement addStatement1 = QVTimperativeFactory.eINSTANCE.createAddStatement();
					addStatement1.setTargetVariable(newVariable);
					addStatement1.setValue(excludingAllCallExp);
					mappingStatements.add(addStatement1);
				}
				else {
					newVariable = localVariable;
				}
				//
				AddStatement addStatement2 = QVTimperativeFactory.eINSTANCE.createAddStatement();
				addStatement2.setTargetVariable(accumulatedVariable);
				addStatement2.setValue(PivotUtil.createVariableExp(newVariable));
				mappingStatements.add(addStatement2);
			}
		}
		//
		//	Create the recursive call recursing all the recursive connections.
		//
		Map<@NonNull Variable, @NonNull OCLExpression> guardVariable2expression = new HashMap<@NonNull Variable, @NonNull OCLExpression>();
		for (@NonNull RecursionContext recursion : classDatumAnalysis2recursion.values()) {
			Variable guardVariable = recursion.getGuardVariable();
			Variable newVariable = recursion.getNewVariable();
			if (newVariable == null) {
				newVariable = recursion.getLocalVariable();
			}
			VariableExp localVariableExp = PivotUtil.createVariableExp(newVariable);
			guardVariable2expression.put(guardVariable, localVariableExp);
		}
		mappingStatements.add(createCall(region, guardVariable2expression));
		mapping.getOwnedStatements().addAll(mappingStatements);
	}

	@Override
	public @NonNull List<@NonNull Node> getGuardNodes() {
		return getHeadNodes();
	}

	public @NonNull GuardPattern getGuardPattern(@NonNull ClassDatumAnalysis classDatumAnalysis) {
		//
		//	Create/locate the domain and guard pattern for the guard.
		//
		TypedModel typedModel = visitor.getQVTiTypedModel(classDatumAnalysis.getTypedModel());
		GuardPattern guardPattern;
		if (typedModel != null) {
			ImperativeDomain domain = typedModel2domain.get(typedModel);
			if (domain == null) {
				domain = QVTimperativeUtil.createImperativeDomain(typedModel);
				domain.setIsCheckable(true);
				mapping.getDomain().add(domain);
				typedModel2domain.put(typedModel, domain);
			}
			guardPattern = domain.getGuardPattern();
		}
		else {
			guardPattern = mapping.getGuardPattern();
		}
		assert guardPattern != null;
		return guardPattern;
	}

	@Override
	public @NonNull Variable getGuardVariable(@NonNull Node node) {
		assert getHeadNodes().contains(node);
		ClassDatumAnalysis classDatumAnalysis = node.getClassDatumAnalysis();
		RecursionContext recursion = classDatumAnalysis2recursion.get(classDatumAnalysis);
		assert recursion != null;
		return recursion.getGuardVariable();
	}

	private @NonNull List<@NonNull Node> getHeadNodes() {
		return region.getHeadNodes();
	}
}

Back to the top