diff options
author | Ed Willink | 2016-10-08 18:34:56 +0000 |
---|---|---|
committer | Ed Willink | 2016-10-30 11:37:12 +0000 |
commit | 1202cf4479dc5f2575608cba34d8236c9ed77d38 (patch) | |
tree | 994e37b2c676730c2a115336b39aa70202357f0d | |
parent | a86d3b30f8df7ae9a2e2e8b5d7c32040ed7d3eac (diff) | |
download | org.eclipse.qvtd-1202cf4479dc5f2575608cba34d8236c9ed77d38.tar.gz org.eclipse.qvtd-1202cf4479dc5f2575608cba34d8236c9ed77d38.tar.xz org.eclipse.qvtd-1202cf4479dc5f2575608cba34d8236c9ed77d38.zip |
[500962] Introduce Connection without use
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 |