Skip to main content
summaryrefslogtreecommitdiffstats
blob: 0afe7171b9aea2cb37123de86f7769ecc05060d2 (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
/*******************************************************************************
 * Copyright (c) 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.runtime.internal.evaluation;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.ids.CollectionTypeId;
import org.eclipse.ocl.pivot.utilities.LabelUtil;
import org.eclipse.qvtd.runtime.evaluation.AbstractConnection;
import org.eclipse.qvtd.runtime.evaluation.AbstractTransformer;
import org.eclipse.qvtd.runtime.evaluation.Connection;
import org.eclipse.qvtd.runtime.evaluation.Interval;
import org.eclipse.qvtd.runtime.evaluation.Invocation;
import org.eclipse.qvtd.runtime.evaluation.InvocationConstructor;

/**
 * An AbstractConnection maintains the values between one or more sources, typically Mappings, that
 * invoke append() and one or more consumers that consume each value.
 *
 * The AbstractConnection may optionally enforce uniqueness on the internal values where the overall
 * application is unable to do so automatically.
 *
 * Incremental update is supported by a revoke() or an append(), or a replace() of an appended value.
 */
public abstract class AbstractIncrementalConnectionInternal extends AbstractConnection implements Connection.Incremental
{
	protected final class ValueIterator<T> implements Iterator<T> {

		private final int size = listOfValueAndConsumingInvocations.size();

		private int cursor = next(0);

		@Override
		public boolean hasNext() {
			return cursor < size;
		}

		@Override
		public @NonNull T next() {
			List<@NonNull Object> valueAndConsumingInvocations = listOfValueAndConsumingInvocations.get(cursor);
			if (valueAndConsumingInvocations == null) {
				throw new NoSuchElementException();
			}
			cursor = next(cursor+1);
			return (T) valueAndConsumingInvocations.get(VALUE_INDEX);
		}

		private int next(int i) {
			while (i < size) {
				if (listOfValueAndConsumingInvocations.get(i) != null) {
					return i;
				}
				i++;
			}
			return size;
		}
	}

	protected static final int VALUE_INDEX = 0;
	protected static final int INDEX_INDEX = 1;
	protected static final int COUNT_INDEX = 2;

	protected final boolean debugConsumes = AbstractTransformer.CONSUMES.isActive();

	/**
	 * The appenders of values.
	 */
	protected final @NonNull List<@NonNull InvocationConstructor> appenders = new ArrayList<>();

	/**
	 * First VALUE_INDEX entry of each list is the @NonNull Object element value.
	 * Second INDEX_INDEX entry of each list is the @NonNull Integer index within the outer list.
	 * If unique values are maintained the third COUNT_INDEX entry of each list is the number of copies of this value.
	 * Subsequent entries are @NonNull AbstractMapping consumers of the value.
	 * The entry is returned opaquely as a connectionKey to enable append() to return a
	 * key that may subsequently be used by remove() or replace().
	 * Revoked entries are set to null in order to preserve index validity until a cleanup.
	 */
	protected final @NonNull List<@Nullable List<@NonNull Object>> listOfValueAndConsumingInvocations = new ArrayList<>();

	protected AbstractIncrementalConnectionInternal(@NonNull Interval interval, @NonNull String name, @NonNull CollectionTypeId typeId) {
		super(interval, name, typeId);
	}

	@Override
	public void addAppender(@NonNull InvocationConstructor appender) {
		if (!appenders.contains(appender)) {
			appenders.add(appender);
		}
	}

	@Override
	public boolean addConsumer(@NonNull InvocationConstructor consumer) {
		//		assert listOfValueAndConsumingInvocations.isEmpty() || listOfValueAndConsumingInvocations.get(0).;
		if (!super.addConsumer(consumer)) {
			return false;
		}
		if (!listOfValueAndConsumingInvocations.isEmpty()) {
			queue();
		}
		return true;
	}

	/**
	 * Remove the revoked entries and update the internal indexes accordingly.
	 */
	@Override
	public synchronized void cleanup() {
		int iWrite = 0;
		for (int iRead = 0; iRead < listOfValueAndConsumingInvocations.size(); iRead++) {
			List<@NonNull Object> valueAndConsumingInvocations = listOfValueAndConsumingInvocations.get(iRead);
			if (valueAndConsumingInvocations != null) {
				if (iWrite != iRead) {
					listOfValueAndConsumingInvocations.set(iWrite, valueAndConsumingInvocations);
				}
				valueAndConsumingInvocations.set(INDEX_INDEX, iWrite);
				iWrite++;
			}
		}
	}

	@Override
	public void consume(int elementIndex, @NonNull Invocation invocation) {
		List<@NonNull Object> valueAndConsumingInvocations = listOfValueAndConsumingInvocations.get(elementIndex);
		assert valueAndConsumingInvocations != null;
		assert !valueAndConsumingInvocations.contains(invocation);		// Earlier indexes cannot be the invocation, so no need for a sub-list
		valueAndConsumingInvocations.add(invocation);
		// FIXME empty status if all consumers at final index
		// invocationManager.dequeue(this);
		if (debugConsumes) {
			AbstractTransformer.CONSUMES.println(this + " => " + LabelUtil.getLabel(valueAndConsumingInvocations.get(VALUE_INDEX)));
		}
	}

	@Override
	public @NonNull Iterable<@NonNull InvocationConstructor> getAppenders() {
		return appenders;
	}

	@Override
	public int getCapacity() {		// not getSize() since some entries may be null.
		return listOfValueAndConsumingInvocations.size();
	}

	@Override
	public @Nullable Object getValue(int i) {
		List<@NonNull Object> valueAndConsumingInvocations = listOfValueAndConsumingInvocations.get(i);
		return valueAndConsumingInvocations != null ? valueAndConsumingInvocations.get(VALUE_INDEX) : null;
	}

	@Override
	public int getValues() {
		return listOfValueAndConsumingInvocations.size();
	}


	@Override
	public <@NonNull T> @NonNull Iterable<T> typedIterable(Class<T> elementClass) {
		return new Iterable<T>()
		{
			@Override
			public Iterator<@NonNull T> iterator() {
				return new ValueIterator<T>();
			}
		};
	}
}

Back to the top