blob: f91cafbf3bdd7d974360de495a1d404337c101a4 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2013, 2017 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
*
* </copyright>
*/
package org.eclipse.qvtd.compiler.internal.qvts2qvts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory;
import org.eclipse.ocl.pivot.utilities.FeatureFilter;
import org.eclipse.qvtd.compiler.internal.qvtm2qvts.RegionUtil;
import org.eclipse.qvtd.compiler.internal.qvtm2qvts.ScheduleManager;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtcore.Mapping;
import org.eclipse.qvtd.pivot.qvtcore.analysis.DomainUsage;
import org.eclipse.qvtd.pivot.qvtschedule.ClassDatum;
import org.eclipse.qvtd.pivot.qvtschedule.MappingAction;
import org.eclipse.qvtd.pivot.qvtschedule.MappingRegion;
import org.eclipse.qvtd.pivot.qvtschedule.Node;
import org.eclipse.qvtd.pivot.qvtschedule.Region;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil;
import com.google.common.collect.Iterables;
public class ClassDatumAnalysis implements Adapter
{
public static @Nullable ClassDatumAnalysis find(@NonNull ClassDatum classDatum) {
return ClassUtil.getAdapter(ClassDatumAnalysis.class, classDatum);
}
public static @NonNull ClassDatumAnalysis get(@NonNull Node node) {
ClassDatum classDatum = RegionUtil.getClassDatum(node);
ClassDatumAnalysis adapter = ClassUtil.getAdapter(ClassDatumAnalysis.class, classDatum);
if (adapter == null) {
adapter = RegionUtil.getScheduleManager(RegionUtil.getRegion(node)).getClassDatumAnalysis(classDatum);
}
return adapter;
}
protected final @NonNull ScheduleManager scheduleManager;
protected final @NonNull ClassDatum classDatum;
protected final @NonNull DomainUsage domainUsage;
protected final @NonNull ClassDatum elementalClassDatum;
private @Nullable List<@NonNull Mapping> producedBy = null;
private @Nullable List<@NonNull Mapping> requiredBy = null;
private @Nullable List<@NonNull ClassDatumAnalysis> superClassDatumAnalyses = null;
/**
* The non-to-one properties that may terminate in a given ClassDatum.
*/
private /* @LazyNonNull*/ List<@NonNull Property> multiOpposites = null;
private final @NonNull Map<@NonNull Region, @NonNull List<@NonNull Node>> introducer2assignmentNodes = new HashMap<>();
private final @NonNull Map<@NonNull MappingRegion, @NonNull List<@NonNull Node>> consumer2predicateNodes = new HashMap<>();
private final @NonNull Map<@NonNull Region, @NonNull List<@NonNull Node>> producer2assignmentNodes = new HashMap<>();
public ClassDatumAnalysis(@NonNull ScheduleManager scheduleManager, @NonNull ClassDatum classDatum) {
this.scheduleManager = scheduleManager;
this.classDatum = classDatum;
classDatum.eAdapters().add(this);
TypedModel typedModel = QVTscheduleUtil.getTypedModel(classDatum);
this.domainUsage = scheduleManager.getDomainUsage(typedModel);
Type type = classDatum.getCompleteClass().getPrimaryClass();
Type elementType = type;
while (elementType instanceof CollectionType) {
elementType = ((CollectionType)elementType).getElementType();
}
if ((elementType == null) || (elementType == type) || !(elementType instanceof org.eclipse.ocl.pivot.Class)) {
elementalClassDatum = classDatum;
}
else {
elementalClassDatum = scheduleManager.getClassDatum((org.eclipse.ocl.pivot.Class)elementType, typedModel);
}
}
public void addConsumption(@NonNull MappingRegion consumer, @NonNull Node consumingNode) {
List<@NonNull Node> predicateNodes = consumer2predicateNodes.get(consumer);
if (predicateNodes == null) {
predicateNodes = new ArrayList<@NonNull Node>();
consumer2predicateNodes.put(consumer, predicateNodes);
}
if (!predicateNodes.contains(consumingNode)) { // predicate may consume multiple producers
predicateNodes.add(consumingNode);
}
}
public void addIntroduction(@NonNull Region introducer, @NonNull Node introducingNode) {
List<@NonNull Node> assignmentNodes = introducer2assignmentNodes.get(introducer);
if (assignmentNodes == null) {
assignmentNodes = new ArrayList<@NonNull Node>();
introducer2assignmentNodes.put(introducer, assignmentNodes);
}
assert !assignmentNodes.contains(introducingNode);
assignmentNodes.add(introducingNode);
}
public void addProduction(@NonNull MappingRegion producer, @NonNull Node producingNode) {
List<@NonNull Node> assignmentNodes = producer2assignmentNodes.get(producer);
if (assignmentNodes == null) {
assignmentNodes = new ArrayList<@NonNull Node>();
producer2assignmentNodes.put(producer, assignmentNodes);
}
if (!assignmentNodes.contains(producingNode)) { // assignment may be recursive
assignmentNodes.add(producingNode);
}
}
public @NonNull ClassDatum getClassDatum() {
return classDatum;
}
public @NonNull Iterable<@NonNull Node> getConsumingNodes() {
return Iterables.concat(consumer2predicateNodes.values());
}
public @NonNull List<@NonNull MappingRegion> getConsumingRegions() {
return new ArrayList<>(consumer2predicateNodes.keySet());
}
public @NonNull DomainUsage getDomainUsage() {
return domainUsage;
}
public @NonNull ClassDatum getElementalClassDatum() {
return elementalClassDatum;
}
public @Nullable List<Property> getMultiOpposites() {
List<@NonNull Property> multiOpposites2 = multiOpposites;
if (multiOpposites2 == null) {
EnvironmentFactory environmentFactory = scheduleManager.getEnvironmentFactory();
CompleteClass completeClass = classDatum.getCompleteClass();
assert completeClass != null;
for (@NonNull Property property : completeClass.getProperties((FeatureFilter)null)) {
Property oppositeProperty = property.getOpposite();
if ((oppositeProperty != null) && oppositeProperty.isIsMany() && !oppositeProperty.isIsDerived()) {
Type childrenType = oppositeProperty.getType();
if (childrenType instanceof CollectionType) {
Type childType = ((CollectionType)childrenType).getElementType();
assert childType != null;
CompleteClass childCompleteClass = environmentFactory.getCompleteModel().getCompleteClass(childType);
if (completeClass.conformsTo(childCompleteClass)) { // FIXME bi-conforming types
if (multiOpposites2 == null) {
multiOpposites = multiOpposites2 = new ArrayList<@NonNull Property>();
}
multiOpposites2.add(oppositeProperty);
}
}
}
}
if (multiOpposites2 != null) {
Collections.sort(multiOpposites2, QVTscheduleUtil.MultiOppositeComparator.INSTANCE); // Container first, deterministic order by name later
}
}
return multiOpposites2;
}
public @NonNull List<@NonNull Mapping> getProducedBy() {
List<@NonNull Mapping> producedBy2 = producedBy;
if (producedBy2 == null) {
producedBy = producedBy2 = new ArrayList<>();
for (@NonNull MappingAction producingAction : ClassUtil.nullFree(classDatum.getProducedBy())) {
Mapping mapping = producingAction.getMapping();
assert mapping != null;
producedBy2.add(mapping);
}
}
return producedBy2;
}
public @NonNull Iterable<Node> getProducingNodes() {
return Iterables.concat(consumer2predicateNodes.values());
}
public @NonNull Set<Region> getProducingRegions() {
return producer2assignmentNodes.keySet();
}
public @NonNull List<@NonNull Mapping> getRequiredBy() {
List<@NonNull Mapping> requiredBy2 = requiredBy;
if (requiredBy2 == null) {
requiredBy = requiredBy2 = new ArrayList<>();
for (@NonNull MappingAction consumingAction : ClassUtil.nullFree(classDatum.getRequiredBy())) {
Mapping mapping = consumingAction.getMapping();
assert mapping != null;
requiredBy2.add(mapping);
}
}
return requiredBy2;
}
public @NonNull ScheduleManager getScheduleManager() {
return scheduleManager;
}
public @Nullable Node getSingleProducer() {
Iterator<List<Node>> values = producer2assignmentNodes.values().iterator();
if (!values.hasNext()) {
return null;
}
List<Node> firstProductions = values.next();
return !values.hasNext() && (firstProductions.size() == 1) ? firstProductions.get(0) : null;
}
public List<@NonNull ClassDatumAnalysis> getSuperClassDatumAnalyses() {
List<@NonNull ClassDatumAnalysis> superClassDatumAnalyses2 = superClassDatumAnalyses;
if (superClassDatumAnalyses2 == null) {
superClassDatumAnalyses = superClassDatumAnalyses2 = new ArrayList<>();
CompleteClass completeClass = getClassDatum().getCompleteClass();
for (@NonNull CompleteClass completeSuperClass : completeClass.getSuperCompleteClasses()) {
superClassDatumAnalyses2.add(scheduleManager.getClassDatumAnalysis(completeSuperClass, ClassUtil.nonNullState(domainUsage.getTypedModel(completeClass))));
}
}
return superClassDatumAnalyses2;
}
@Override
public Notifier getTarget() {
return classDatum;
}
public boolean hasNoProducers() {
return producer2assignmentNodes.size() == 0;
}
@Override
public String toString() {
return classDatum.toString();
}
@Override
public boolean isAdapterForType(Object type) {
return type == ClassDatumAnalysis.class;
}
@Override
public void notifyChanged(Notification notification) {}
@Override
public void setTarget(Notifier newTarget) {}
}