Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEd Willink2016-10-08 18:34:56 +0000
committerEd Willink2016-10-30 11:37:12 +0000
commit1202cf4479dc5f2575608cba34d8236c9ed77d38 (patch)
tree994e37b2c676730c2a115336b39aa70202357f0d
parenta86d3b30f8df7ae9a2e2e8b5d7c32040ed7d3eac (diff)
downloadorg.eclipse.qvtd-1202cf4479dc5f2575608cba34d8236c9ed77d38.tar.gz
org.eclipse.qvtd-1202cf4479dc5f2575608cba34d8236c9ed77d38.tar.xz
org.eclipse.qvtd-1202cf4479dc5f2575608cba34d8236c9ed77d38.zip
[500962] Introduce Connection without use
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractExecutionVisitor.java12
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractTransformer.java1
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Connection.java83
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/EnforcedConnection.java31
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/ExecutionVisitor.java2
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Interval.java2
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Invoker.java28
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/UnenforcedConnection.java31
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractConnectionInternal.java208
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractEnforcedConnectionInternal.java133
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractIntervalInternal.java8
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractInvocationInternal.java4
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractUnenforcedConnectionInternal.java99
-rw-r--r--plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/ConnectionLinkage.java18
14 files changed, 658 insertions, 2 deletions
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractExecutionVisitor.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractExecutionVisitor.java
index 556880538..faab92923 100644
--- a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractExecutionVisitor.java
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractExecutionVisitor.java
@@ -24,7 +24,7 @@ public /*abstract*/ class AbstractExecutionVisitor<R> implements ExecutionVisito
* is available.
*/
public R visiting(@NonNull ExecutionVisitable visitable) {
- throw new UnsupportedOperationException("No " + getClass().getSimpleName() + " suupport for a " + visitable.getClass().getSimpleName());
+ throw new UnsupportedOperationException("No " + getClass().getSimpleName() + " support for a " + visitable.getClass().getSimpleName());
}
@Override
@@ -33,6 +33,11 @@ public /*abstract*/ class AbstractExecutionVisitor<R> implements ExecutionVisito
}
@Override
+ public R visitConnection(@NonNull Connection object) {
+ return visiting(object);
+ }
+
+ @Override
public R visitInterval(@NonNull Interval object) {
return visiting(object);
}
@@ -48,6 +53,11 @@ public /*abstract*/ class AbstractExecutionVisitor<R> implements ExecutionVisito
}
@Override
+ public R visitInvoker(@NonNull Invoker object) {
+ return visiting(object);
+ }
+
+ @Override
public R visitObjectManager(@NonNull ObjectManager object) {
return visiting(object);
}
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractTransformer.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractTransformer.java
index 6b2546489..7fffbaa34 100644
--- a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractTransformer.java
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/AbstractTransformer.java
@@ -25,6 +25,7 @@ import org.eclipse.qvtd.runtime.internal.evaluation.AbstractTransformerInternal;
public abstract class AbstractTransformer extends AbstractTransformerInternal
{
public static final @NonNull String PLUGIN_ID = "org.eclipse.qvtd.runtime";
+ public static final @NonNull TracingOption APPENDS = new TracingOption(PLUGIN_ID, "tx/appends");
public static final @NonNull TracingOption EXCEPTIONS = new TracingOption(PLUGIN_ID, "tx/exceptions");
public static final @NonNull TracingOption INVOCATIONS = new TracingOption(PLUGIN_ID, "tx/invocations");
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Connection.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Connection.java
new file mode 100644
index 000000000..455e6249d
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Connection.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * 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.evaluation;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ocl.pivot.utilities.Nameable;
+
+/**
+ * A Connection 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.
+ *
+ * @noimplement clients should derive from AbstractConnection
+ */
+public interface Connection extends ExecutionVisitable, Nameable
+{
+ void addConsumer(@NonNull Invoker consumingInvoker);
+
+ void addProducer(@NonNull Invoker producingInvoker);
+
+ /**
+ * Append aValue to the contents, enforcing uniqueness if necessary, and waking up the overall
+ * connection manager to schedule a propagate() to consumers when convenient.
+ */
+ @NonNull Object append(@NonNull Object aValue);
+
+ void check();
+
+ /**
+ * Remove the revoked entries and update the internal indexes accordingly.
+ */
+ void cleanup();
+
+ void consume(int elementIndex, @NonNull Invocation mapping);
+
+ int getCapacity();
+
+ @NonNull Iterable<@NonNull Invoker> getConsumers();
+
+ // @NonNull Iterable<@NonNull Invocation> getConsumers(int i);
+
+ @NonNull Iterable<@NonNull Invoker> getProducers();
+
+ @Nullable Object getValue(int i);
+
+ int getValues();
+
+ void propagate();
+
+ /**
+ * Replace the old value at connectionKey by newValue.
+ *
+ * If the old value is a multiple value in a unique value connection, the multi-value count is decremented
+ * and a new entry created for the newValue by delegating to append to enforce uniqueness of the newValue.
+ *
+ * Otherwise the old value is removed, its consumingInvocations are invalidated
+ * so that they recompute with the newValue which replaces the old.
+ */
+ @NonNull Object replace(@NonNull Object connectionKey, @NonNull Object newValue);
+
+ /**
+ * Revoke, inverse append, the old value at connectionKey.
+ *
+ * If the old value is a multiple value in a unique value connection, the multi-value count is decremented.
+ *
+ * Otherwise the old value is removed, its consumingInvocations are revoked
+ * so that their appends are also revoked.
+ */
+ void revoke(@NonNull Object connectionKey);
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/EnforcedConnection.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/EnforcedConnection.java
new file mode 100644
index 000000000..25c6da625
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/EnforcedConnection.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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.evaluation;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.ocl.pivot.ids.CollectionTypeId;
+import org.eclipse.qvtd.runtime.internal.evaluation.AbstractEnforcedConnectionInternal;
+
+/**
+ * An EnforcedConnection maintains the unqiue values between one or more sources,
+ * typically Mappings, that invoke append() and one or more consumers that consume each value.
+ * Uniqueness on the internal values on behalf of an overall application that is unable to do so automatically.
+ *
+ * Incremental update is supported by a revoke() or an append(), or a replace() of an appended value.
+ *
+ * Incremental update is supported by a revoke() or an append(), or a replace() of an appended value.
+ */
+public class EnforcedConnection extends AbstractEnforcedConnectionInternal
+{
+ public EnforcedConnection(@NonNull Interval interval, @NonNull String name, @NonNull CollectionTypeId typeId) {
+ super(interval, name, typeId);
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/ExecutionVisitor.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/ExecutionVisitor.java
index 3b8b2d8b6..c1db83d2b 100644
--- a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/ExecutionVisitor.java
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/ExecutionVisitor.java
@@ -26,9 +26,11 @@ public interface ExecutionVisitor<R>
// R visiting(@NonNull ExecutionVisitable visitable);
R visitComputation(@NonNull Computation object);
+ R visitConnection(@NonNull Connection object);
R visitInterval(@NonNull Interval object);
R visitInvocation(@NonNull Invocation object);
R visitInvocationManager(@NonNull InvocationManager object);
+ R visitInvoker(@NonNull Invoker object);
R visitObjectManager(@NonNull ObjectManager object);
R visitSlotState(@NonNull SlotState object);
R visitTransformer(@NonNull Transformer object);
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Interval.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Interval.java
index bcc0f674b..aa81794a5 100644
--- a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Interval.java
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Interval.java
@@ -23,6 +23,8 @@ public interface Interval extends ExecutionVisitable, Nameable
{
boolean flush();
+ int getIndex();
+
@NonNull InvocationManager getInvocationManager();
@Override
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Invoker.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Invoker.java
new file mode 100644
index 000000000..3ddc218a3
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/Invoker.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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.evaluation;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.ocl.pivot.utilities.Nameable;
+
+/**
+ * An Invoker marshalls the arguments to invoke a mapping Invocation.
+ *
+ * @noimplement clients should derive from AbstractInvoker
+ */
+public interface Invoker extends ExecutionVisitable, Nameable
+{
+ @NonNull Interval getInterval();
+
+ @NonNull Iterable<@NonNull Invocation> getInvocations();
+
+ void propagate();
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/UnenforcedConnection.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/UnenforcedConnection.java
new file mode 100644
index 000000000..1f5d06015
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/evaluation/UnenforcedConnection.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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.evaluation;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.ocl.pivot.ids.CollectionTypeId;
+import org.eclipse.qvtd.runtime.internal.evaluation.AbstractUnenforcedConnectionInternal;
+
+/**
+ * An UnenforcedConnection maintains the values between one or more sources, typically Mappings, that
+ * invoke append() and one or more consumers that consume each value.
+ *
+ * It is assumed that the overall application enforces unqiueness externally if uniqueness of values is required.
+ * Use EnforcedConnection to enforce uniqueness internally.
+ *
+ * Incremental update is supported by a revoke() or an append(), or a replace() of an appended value.
+ */
+public class UnenforcedConnection extends AbstractUnenforcedConnectionInternal
+{
+ public UnenforcedConnection(@NonNull Interval interval, @NonNull String name, @NonNull CollectionTypeId typeId) {
+ super(interval, name, typeId);
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractConnectionInternal.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractConnectionInternal.java
new file mode 100644
index 000000000..d5927662e
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractConnectionInternal.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * 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.List;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ocl.pivot.ids.CollectionTypeId;
+import org.eclipse.qvtd.runtime.evaluation.AbstractTransformer;
+import org.eclipse.qvtd.runtime.evaluation.Connection;
+import org.eclipse.qvtd.runtime.evaluation.ExecutionVisitor;
+import org.eclipse.qvtd.runtime.evaluation.Interval;
+import org.eclipse.qvtd.runtime.evaluation.Invocation;
+import org.eclipse.qvtd.runtime.evaluation.Invoker;
+
+/**
+ * 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 AbstractConnectionInternal extends ConnectionLinkage implements Connection
+{
+ protected static final int VALUE_INDEX = 0;
+ protected static final int INDEX_INDEX = 1;
+ protected static final int COUNT_INDEX = 2;
+
+ protected final boolean debugAppends = AbstractTransformer.APPENDS.isActive();
+ protected final @NonNull Interval interval;
+ protected final @NonNull String name;
+ protected final @NonNull CollectionTypeId typeId;
+
+ /**
+ * The consumers of each appended value.
+ */
+ protected final @NonNull List<@NonNull Invoker> consumers = new ArrayList<>();
+
+ /**
+ * The producers of values.
+ */
+ protected final @NonNull List<@NonNull Invoker> producers = new ArrayList<>();
+
+ /**
+ * The earlist interval at which a consumer can execute.
+ */
+ // private /*@LazyNonNull*/ Interval earliestInterval = null;
+
+ /**
+ * 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 AbstractConnectionInternal(@NonNull Interval interval, @NonNull String name, @NonNull CollectionTypeId typeId) {
+ this.interval = interval;
+ this.name = name;
+ this.typeId = typeId;
+ }
+
+ @Override
+ public <R> R accept(@NonNull ExecutionVisitor<R> visitor) {
+ return visitor.visitConnection(this);
+ }
+
+ @Override
+ public void addConsumer(@NonNull Invoker consumer) {
+ // assert listOfValueAndConsumingInvocations.isEmpty() || listOfValueAndConsumingInvocations.get(0).;
+ if (!consumers.contains(consumer)) {
+ consumers.add(consumer);
+ if (!listOfValueAndConsumingInvocations.isEmpty()) {
+ queue();
+ }
+ }
+ }
+
+ @Override
+ public void addProducer(@NonNull Invoker producer) {
+ if (!producers.contains(producer)) {
+ producers.add(producer);
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ @Override
+ public int getCapacity() { // not getSize() since some entries may be null.
+ return listOfValueAndConsumingInvocations.size();
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull Invoker> getConsumers() {
+ return consumers;
+ }
+
+ @Override
+ public @NonNull String getName() {
+ return name;
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull Invoker> getProducers() {
+ return producers;
+ }
+
+ @Override
+ public @Nullable Object getValue(int i) {
+ List<@NonNull Object> valueAndConsumingInvocations = listOfValueAndConsumingInvocations.get(i);
+ return valueAndConsumingInvocations != null ? valueAndConsumingInvocations.get(VALUE_INDEX) : null;
+ }
+
+ /* @Override
+ public void insertAfter(@NonNull Interval predecessor) {
+ @NonNull AbstractIntervalInternal castPredecessor = (AbstractIntervalInternal)predecessor;
+ @NonNull AbstractIntervalInternal successor = castPredecessor.next;
+ successor.prev = this;
+ next = successor;
+ castPredecessor.next = this;
+ prev = castPredecessor;
+ } */
+
+ /* @Override
+ public void remove() {
+ prev.next = next;
+ next.prev = prev;
+ prev = this;
+ next = this;
+ } */
+
+ @Override
+ public int getValues() {
+ return listOfValueAndConsumingInvocations.size();
+ }
+
+ @Override
+ public void propagate() {
+ for (@NonNull Invoker consumer : consumers) {
+ consumer.propagate();
+ }
+ }
+
+ protected void queue() {
+ throw new UnsupportedOperationException();
+ // if (nextConnection == this) {
+ // interval.queue(this);
+ // }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append(interval.getIndex());
+ s.append(": ");
+ s.append(name);
+ s.append(" : ");
+ s.append(typeId);
+ s.append(": ");
+ int i = 0;
+ for (@NonNull ConnectionLinkage nextConnectionLinkage = nextConnection; nextConnectionLinkage != this; nextConnectionLinkage = nextConnectionLinkage.nextConnection) {
+ i++;
+ }
+ s.append(i);
+ return s.toString();
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractEnforcedConnectionInternal.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractEnforcedConnectionInternal.java
new file mode 100644
index 000000000..efbd493e9
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractEnforcedConnectionInternal.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * 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.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.ocl.pivot.ids.CollectionTypeId;
+import org.eclipse.qvtd.runtime.evaluation.AbstractInvocation;
+import org.eclipse.qvtd.runtime.evaluation.AbstractTransformer;
+import org.eclipse.qvtd.runtime.evaluation.Interval;
+import org.eclipse.qvtd.runtime.evaluation.Invocation;
+
+/**
+ * An AbstractEnforcedConnectionInternal maintains the unqiue values between one or more sources,
+ * typically Mappings, that invoke append() and one or more consumers that consume each value.
+ * Uniqueness on the internal values on behalf of an overall application that 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 AbstractEnforcedConnectionInternal extends AbstractConnectionInternal
+{
+ /**
+ * Map from each unique value to the entry for that value.
+ */
+ private final @NonNull Map<@NonNull Object, @NonNull List<@NonNull Object>> uniqueValues2valueAndConsumingInvocations = new HashMap<>();
+
+ protected AbstractEnforcedConnectionInternal(@NonNull Interval interval, @NonNull String name, @NonNull CollectionTypeId typeId) {
+ super(interval, name, typeId);
+ }
+
+ /**
+ * Append aValue to the contents, enforcing uniqueness if necessary, and waking up the overall
+ * connection manager to schedule a propagate() to consumers when convenient.
+ */
+ @Override
+ public synchronized @NonNull Object append(@NonNull Object aValue) {
+ if (debugAppends) {
+ AbstractTransformer.APPENDS.println(this + " - " + aValue);
+ }
+ List<@NonNull Object> valueAndConsumingInvocations = uniqueValues2valueAndConsumingInvocations.get(aValue);
+ if (valueAndConsumingInvocations == null) {
+ valueAndConsumingInvocations = new ArrayList<>();
+ valueAndConsumingInvocations.add(aValue); // VALUE_INDEX
+ valueAndConsumingInvocations.add(listOfValueAndConsumingInvocations.size()); // INDEX_INDEX
+ valueAndConsumingInvocations.add(1); // COUNT_INDEX
+ listOfValueAndConsumingInvocations.add(valueAndConsumingInvocations);
+ uniqueValues2valueAndConsumingInvocations.put(aValue, valueAndConsumingInvocations);
+ queue();
+ }
+ else {
+ Integer count = (Integer) valueAndConsumingInvocations.get(COUNT_INDEX);
+ valueAndConsumingInvocations.set(COUNT_INDEX, count+1);
+ }
+ return valueAndConsumingInvocations;
+ }
+
+ @Override
+ public void check() {
+ for (int i = 0; i < listOfValueAndConsumingInvocations.size(); i++) {
+ List<@NonNull Object> valueAndConsumingInvocations = listOfValueAndConsumingInvocations.get(i);
+ if (valueAndConsumingInvocations != null) {
+ assert valueAndConsumingInvocations.get(INDEX_INDEX) == Integer.valueOf(i);
+ assert valueAndConsumingInvocations.contains(valueAndConsumingInvocations.get(VALUE_INDEX));
+ }
+ }
+ }
+
+ /**
+ * Replace the old value at connectionKey by newValue.
+ *
+ * If the old value is a multiple value in a unique value connection, the multi-value count is decremented
+ * and a new entry created for the newValue by delegating to append to enforce uniqueness of the newValue.
+ *
+ * Otherwise the old value is removed, its consumingInvocations are invalidated
+ * so that they recompute with the newValue which replaces the old.
+ */
+ @Override
+ public synchronized @NonNull Object replace(@NonNull Object connectionKey, @NonNull Object newValue) {
+ @SuppressWarnings("unchecked") List<@NonNull Object> valueAndConsumingInvocations = (List<@NonNull Object>) connectionKey;
+ Object oldValue = valueAndConsumingInvocations.get(VALUE_INDEX);
+ if (newValue != oldValue) { // FIXME ?? equals/oclEquals ??
+ Integer count = (Integer) valueAndConsumingInvocations.get(COUNT_INDEX);
+ if (count > 1) {
+ valueAndConsumingInvocations.set(COUNT_INDEX, count-1);
+ return append(newValue);
+ }
+ valueAndConsumingInvocations.set(VALUE_INDEX, newValue);
+ int iMax = valueAndConsumingInvocations.size();
+ for (int i = COUNT_INDEX+1; i < iMax; i++) {
+ Invocation consumer = (Invocation) valueAndConsumingInvocations.get(i);
+ interval.queue(consumer);
+ }
+ }
+ return connectionKey;
+ }
+
+ /**
+ * Revoke, inverse append, the old value at connectionKey.
+ *
+ * If the old value is a multiple value in a unique value connection, the multi-value count is decremented.
+ *
+ * Otherwise the old value is removed, its consumingInvocations are revoked
+ * so that their appends are also revoked.
+ */
+ @Override
+ public synchronized void revoke(@NonNull Object connectionKey) {
+ @SuppressWarnings("unchecked") List<@NonNull Object> valueAndConsumingInvocations = (List<@NonNull Object>) connectionKey;
+ int valueIndex = (int) valueAndConsumingInvocations.get(INDEX_INDEX);
+ Integer count = (Integer) valueAndConsumingInvocations.get(COUNT_INDEX);
+ if (count > 1) {
+ valueAndConsumingInvocations.set(COUNT_INDEX, count-1);
+ return;
+ }
+ listOfValueAndConsumingInvocations.set(valueIndex, null); // Do not disrupt index equivalence.
+ int iMax = valueAndConsumingInvocations.size();
+ for (int i = COUNT_INDEX+1; i < iMax; i++) {
+ AbstractInvocation consumingInvocation = (AbstractInvocation) valueAndConsumingInvocations.get(i);
+ consumingInvocation.revoke();
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractIntervalInternal.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractIntervalInternal.java
index 69b3e3e05..3b3c35287 100644
--- a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractIntervalInternal.java
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractIntervalInternal.java
@@ -23,7 +23,7 @@ import org.eclipse.qvtd.runtime.evaluation.SlotState;
/**
* AbstractIntervalInternal provides the shared implementation of the intrusive blocked/waiting linked list functionality.
*/
-public abstract class AbstractIntervalInternal implements Interval
+public abstract class AbstractIntervalInternal extends ConnectionLinkage implements Interval
{
protected final boolean debugInvocations = AbstractTransformer.INVOCATIONS.isActive();
@@ -108,6 +108,12 @@ public abstract class AbstractIntervalInternal implements Interval
}
}
+
+ @Override
+ public int getIndex() {
+ return intervalIndex;
+ }
+
@Override
public @NonNull InvocationManager getInvocationManager() {
return invocationManager;
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractInvocationInternal.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractInvocationInternal.java
index f5f20660c..03e10f5f3 100644
--- a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractInvocationInternal.java
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractInvocationInternal.java
@@ -54,6 +54,10 @@ public abstract class AbstractInvocationInternal implements Invocation
next = this;
}
+ public void revoke() {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public String toString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this));
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractUnenforcedConnectionInternal.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractUnenforcedConnectionInternal.java
new file mode 100644
index 000000000..018dc3097
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/AbstractUnenforcedConnectionInternal.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * 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.List;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.ocl.pivot.ids.CollectionTypeId;
+import org.eclipse.qvtd.runtime.evaluation.AbstractInvocation;
+import org.eclipse.qvtd.runtime.evaluation.AbstractTransformer;
+import org.eclipse.qvtd.runtime.evaluation.Interval;
+import org.eclipse.qvtd.runtime.evaluation.Invocation;
+
+/**
+ * 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 AbstractUnenforcedConnectionInternal extends AbstractConnectionInternal
+{
+ protected AbstractUnenforcedConnectionInternal(@NonNull Interval interval, @NonNull String name, @NonNull CollectionTypeId typeId) {
+ super(interval, name, typeId);
+ }
+
+ /**
+ * Append aValue to the contents, and waking up the overall
+ * connection manager to schedule a propagate() to consumers when convenient.
+ */
+ @Override
+ public synchronized @NonNull Object append(@NonNull Object aValue) {
+ if (debugAppends) {
+ AbstractTransformer.APPENDS.println(this + " - " + aValue);
+ }
+ List<@NonNull Object> valueAndConsumingInvocations = new ArrayList<>();
+ valueAndConsumingInvocations.add(aValue); // VALUE_INDEX
+ valueAndConsumingInvocations.add(listOfValueAndConsumingInvocations.size()); // INDEX_INDEX
+ listOfValueAndConsumingInvocations.add(valueAndConsumingInvocations);
+ queue();
+ return valueAndConsumingInvocations;
+ }
+
+ @Override
+ public void check() {
+ for (int i = 0; i < listOfValueAndConsumingInvocations.size(); i++) {
+ List<@NonNull Object> valueAndConsumingInvocations = listOfValueAndConsumingInvocations.get(i);
+ if (valueAndConsumingInvocations != null) {
+ assert valueAndConsumingInvocations.get(INDEX_INDEX) == Integer.valueOf(i);
+ }
+ }
+ }
+
+ /**
+ * Replace the old value at connectionKey by newValue.The old value is removed,
+ * its consumingInvocations are invalidated so that they recompute with the newValue which replaces the old.
+ */
+ @Override
+ public synchronized @NonNull Object replace(@NonNull Object connectionKey, @NonNull Object newValue) {
+ @SuppressWarnings("unchecked") List<@NonNull Object> valueAndConsumingInvocations = (List<@NonNull Object>) connectionKey;
+ Object oldValue = valueAndConsumingInvocations.get(VALUE_INDEX);
+ if (newValue != oldValue) { // FIXME ?? equals/oclEquals ??
+ valueAndConsumingInvocations.set(VALUE_INDEX, newValue);
+ int iMax = valueAndConsumingInvocations.size();
+ for (int i = INDEX_INDEX+1; i < iMax; i++) {
+ Invocation consumer = (Invocation) valueAndConsumingInvocations.get(i);
+ interval.queue(consumer);
+ }
+ }
+ return connectionKey;
+ }
+
+ /**
+ * Revoke, inverse append, the old value at connectionKey. The old value is removed,
+ * its consumingInvocations are revoked so that their appends are also revoked.
+ */
+ @Override
+ public synchronized void revoke(@NonNull Object connectionKey) {
+ @SuppressWarnings("unchecked") List<@NonNull Object> valueAndConsumingInvocations = (List<@NonNull Object>) connectionKey;
+ int valueIndex = (int) valueAndConsumingInvocations.get(INDEX_INDEX);
+ listOfValueAndConsumingInvocations.set(valueIndex, null); // Do not disrupt index equivalence.
+ int iMax = valueAndConsumingInvocations.size();
+ for (int i = INDEX_INDEX+1; i < iMax; i++) {
+ AbstractInvocation consumingInvocation = (AbstractInvocation) valueAndConsumingInvocations.get(i);
+ consumingInvocation.revoke();
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/ConnectionLinkage.java b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/ConnectionLinkage.java
new file mode 100644
index 000000000..f1ecc3817
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.runtime/src/org/eclipse/qvtd/runtime/internal/evaluation/ConnectionLinkage.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * 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 org.eclipse.jdt.annotation.NonNull;
+
+class ConnectionLinkage
+{
+ @NonNull ConnectionLinkage nextConnection = this;
+} \ No newline at end of file

Back to the top