blob: e4a17c43e9efc21ba51f169283a35eb5915bacb5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2018 CEA LIST and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink(CEA LIST) - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.examples.codegen.analyzer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.examples.codegen.cgmodel.CGCollectionExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGCollectionPart;
import org.eclipse.ocl.examples.codegen.cgmodel.CGConstantExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGElement;
import org.eclipse.ocl.examples.codegen.cgmodel.CGElementId;
import org.eclipse.ocl.examples.codegen.cgmodel.CGIterator;
import org.eclipse.ocl.examples.codegen.cgmodel.CGMapExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGMapPart;
import org.eclipse.ocl.examples.codegen.cgmodel.CGNamedElement;
import org.eclipse.ocl.examples.codegen.cgmodel.CGShadowPart;
import org.eclipse.ocl.examples.codegen.cgmodel.CGTupleExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGTuplePart;
import org.eclipse.ocl.examples.codegen.cgmodel.CGTypeId;
import org.eclipse.ocl.examples.codegen.cgmodel.CGValuedElement;
import org.eclipse.ocl.examples.codegen.cgmodel.CGVariable;
import org.eclipse.ocl.examples.codegen.cgmodel.CGVariableExp;
import org.eclipse.ocl.examples.codegen.cgmodel.util.AbstractExtendingCGModelVisitor;
import org.eclipse.ocl.examples.codegen.cse.GlobalPlace;
import org.eclipse.ocl.examples.codegen.cse.SimpleAnalysis;
import org.eclipse.ocl.pivot.ids.BindingsId;
import org.eclipse.ocl.pivot.ids.ClassId;
import org.eclipse.ocl.pivot.ids.CollectionTypeId;
import org.eclipse.ocl.pivot.ids.DataTypeId;
import org.eclipse.ocl.pivot.ids.ElementId;
import org.eclipse.ocl.pivot.ids.EnumerationId;
import org.eclipse.ocl.pivot.ids.EnumerationLiteralId;
import org.eclipse.ocl.pivot.ids.IdVisitor;
import org.eclipse.ocl.pivot.ids.LambdaTypeId;
import org.eclipse.ocl.pivot.ids.MapTypeId;
import org.eclipse.ocl.pivot.ids.NestedPackageId;
import org.eclipse.ocl.pivot.ids.NsURIPackageId;
import org.eclipse.ocl.pivot.ids.OclInvalidTypeId;
import org.eclipse.ocl.pivot.ids.OclVoidTypeId;
import org.eclipse.ocl.pivot.ids.OperationId;
import org.eclipse.ocl.pivot.ids.PrimitiveTypeId;
import org.eclipse.ocl.pivot.ids.PropertyId;
import org.eclipse.ocl.pivot.ids.RootPackageId;
import org.eclipse.ocl.pivot.ids.SpecializedId;
import org.eclipse.ocl.pivot.ids.TemplateBinding;
import org.eclipse.ocl.pivot.ids.TemplateParameterId;
import org.eclipse.ocl.pivot.ids.TemplateableTypeId;
import org.eclipse.ocl.pivot.ids.TuplePartId;
import org.eclipse.ocl.pivot.ids.TupleTypeId;
import org.eclipse.ocl.pivot.ids.TypeId;
import org.eclipse.ocl.pivot.ids.UnspecifiedId;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
/**
* Traverses the AST adding any internode dependencies to ensure correct declaration ordering.
*/
public class DependencyVisitor extends AbstractExtendingCGModelVisitor<@Nullable Object, @NonNull CodeGenAnalyzer>
{
private static final int TOUCHED = -1;
protected static final int NOT_AVAILABLE = -2;
private @NonNull Map<@NonNull CGValuedElement, @NonNull Set<@NonNull CGValuedElement>> directDependencies = new HashMap<>();
protected @NonNull Id2DependencyVisitor id2DependencyVisitor = new Id2DependencyVisitor();
protected final @NonNull GlobalPlace globalPlace;
public DependencyVisitor(@NonNull CodeGenAnalyzer analyzer, @NonNull GlobalPlace globalPlace) {
super(analyzer);
this.globalPlace = globalPlace;
}
protected void addDependency(@Nullable CGValuedElement cgElement, @Nullable CGValuedElement dependsOn) {
cgElement = cgElement != null ? cgElement.getThisValue() : null;
dependsOn = dependsOn != null ? dependsOn.getThisValue() : null;
if ((cgElement != null) && (cgElement != dependsOn)) {
if (!cgElement.isGlobal() || (dependsOn == null) || dependsOn.isGlobal()) {
CGValuedElement cgPrimaryElement = getPrimaryElement(cgElement);
Set<@NonNull CGValuedElement> dependencies = directDependencies.get(cgPrimaryElement);
List<@NonNull CGValuedElement> dependsOns = ClassUtil.nullFree(cgPrimaryElement.getDependsOn());
if (dependencies == null) {
dependencies = new HashSet<>();
directDependencies.put(cgPrimaryElement, dependencies);
for (@NonNull CGValuedElement cgDependent : new ArrayList<>(dependsOns)) {
addDependency(cgPrimaryElement, cgDependent);
}
for (@NonNull EStructuralFeature eFeature : ClassUtil.nullFree(cgPrimaryElement.eClass().getEAllStructuralFeatures())) {
if (eFeature instanceof EReference) {
EReference eReference = (EReference)eFeature;
if (!eReference.isDerived() && !eReference.isTransient() && !eReference.isVolatile()) {
Object childOrChildren = cgPrimaryElement.eGet(eReference);
if (eReference.isMany()) {
for (@NonNull Object child : new ArrayList<>((List<@NonNull ?>)childOrChildren)) {
if (child instanceof CGValuedElement) {
addDependency(cgPrimaryElement, (CGValuedElement)child);
}
}
}
else {
if ((childOrChildren instanceof CGValuedElement) && (childOrChildren != cgPrimaryElement.eContainer())) {
addDependency(cgPrimaryElement, (CGValuedElement)childOrChildren);
}
}
}
}
}
}
if (dependsOn != null) {
CGValuedElement cgPrimaryDependsOn = getPrimaryElement(dependsOn);
if (cgPrimaryDependsOn != cgPrimaryElement) {
dependencies.add(cgPrimaryDependsOn);
if (!dependsOns.contains(cgPrimaryDependsOn)) {
dependsOns.add(cgPrimaryDependsOn);
}
}
}
}
}
}
private void addElementIdDependency(@NonNull CGValuedElement cgValuedElement) {
CGTypeId cgTypeId = cgValuedElement.getTypeId();
addDependency(cgValuedElement, cgTypeId);
}
private void addElementIdDependency(@NonNull ElementId elementId, @NonNull ElementId dependsOn) {
CGElementId cgElementId = context.getElementId(elementId);
CGElementId cgDependsOn = context.getElementId(dependsOn);
dependsOn.accept(id2DependencyVisitor);
addDependency(cgElementId, cgDependsOn);
}
private int computeDepths(@NonNull CGValuedElement cgElement, @NonNull Map<CGValuedElement, Integer> dependencyDepths, boolean isGlobal) {
if (isGlobal) {
assert cgElement.isGlobal();
}
@NonNull CGValuedElement cgPrimaryElement = getPrimaryElement(cgElement);
if (isGlobal) {
assert cgPrimaryElement.isGlobal();
}
Set<@NonNull CGValuedElement> dependencies = directDependencies.get(cgPrimaryElement);
if (dependencies == null) {
int depth = getRootDepth(cgPrimaryElement);
dependencyDepths.put(cgPrimaryElement, depth);
return depth;
}
Integer knownDepth = dependencyDepths.get(cgPrimaryElement);
if (knownDepth != null) {
if (knownDepth != TOUCHED) {
return knownDepth;
}
throw new IllegalStateException("Cyclic dependency for " + cgPrimaryElement);
}
dependencyDepths.put(cgPrimaryElement, TOUCHED); // Mark already here
int maxDepth = 0;
for (@NonNull CGValuedElement dependency : dependencies) {
int depth = computeDepths(dependency, dependencyDepths, isGlobal);
if (depth > maxDepth) {
maxDepth = depth;
}
}
int myDepth = maxDepth+1;
dependencyDepths.put(cgPrimaryElement, myDepth);
return myDepth;
}
public @NonNull CGValuedElement getPrimaryElement(@NonNull CGValuedElement cgElement) {
SimpleAnalysis simpleAnalysis = globalPlace.getSimpleAnalysis(cgElement);
if (simpleAnalysis != null) {
return simpleAnalysis.getPrimaryElement();
}
return cgElement;
}
public int getRootDepth(@NonNull CGValuedElement cgElement) {
if (cgElement instanceof CGIterator) {
return NOT_AVAILABLE;
}
return 0;
}
public @NonNull List<@NonNull CGValuedElement> getSortedDependencies(boolean isGlobal) {
final Map<@NonNull CGValuedElement, @NonNull Integer> dependencyDepths = new HashMap<>();
for (@NonNull CGValuedElement cgElement : directDependencies.keySet()) {
computeDepths(cgElement, dependencyDepths, isGlobal);
}
List<@NonNull CGValuedElement> sortedList = new ArrayList<>();
for (@NonNull CGValuedElement cgElement : dependencyDepths.keySet()) {
if (!cgElement.isInlined() && (cgElement.getThisValue() == cgElement)) {
if (isGlobal) {
assert cgElement.isGlobal();
}
sortedList.add(cgElement);
}
}
Collections.sort(sortedList, new Comparator<@NonNull CGValuedElement>()
{
@Override
public int compare(@NonNull CGValuedElement o1, @NonNull CGValuedElement o2) {
Integer d1 = dependencyDepths.get(o1);
Integer d2 = dependencyDepths.get(o2);
assert (d1 != null) && (d2 != null);
if (d1 != d2) {
return d1 - d2;
}
String n1 = String.valueOf(o1.getName());
String n2 = String.valueOf(o2.getName());
int diff = n1.compareTo(n2);
if (diff != 0) {
return diff;
}
String t1 = String.valueOf(o1.getTypeId());
String t2 = String.valueOf(o2.getTypeId());
diff = t1.compareTo(t2);
if (diff != 0) {
return diff;
}
String s1 = String.valueOf(o1);
String s2 = String.valueOf(o2);
return s1.compareTo(s2);
}
});
return sortedList;
}
public void visit(@NonNull CGNamedElement cgElement) {
if (cgElement instanceof CGValuedElement) {
CGValuedElement cgPrimaryElement = getPrimaryElement(((CGValuedElement) cgElement).getThisValue());
addDependency(cgPrimaryElement, null);
cgPrimaryElement.accept(this);
}
else {
cgElement.accept(this);
}
}
public void visitAll(@Nullable Iterable<@NonNull ? extends CGNamedElement> cgElements) {
if (cgElements != null) {
for (@NonNull CGNamedElement cgElement : cgElements) {
visit(cgElement);
}
}
}
@Override
public @Nullable Object visitCGCollectionExp(@NonNull CGCollectionExp cgCollectionExp) {
addElementIdDependency(cgCollectionExp);
for (CGCollectionPart cgCollectionPart : cgCollectionExp.getParts()) {
addDependency(cgCollectionExp, cgCollectionPart);
}
return super.visitCGCollectionExp(cgCollectionExp);
}
@Override
public @Nullable Object visitCGCollectionPart(@NonNull CGCollectionPart cgCollectionPart) {
CGCollectionExp cgCollectionExp = cgCollectionPart.getCollectionExp();
addDependency(cgCollectionExp, cgCollectionPart.getFirst());
addDependency(cgCollectionExp, cgCollectionPart.getLast());
return super.visitCGCollectionPart(cgCollectionPart);
}
@Override
public @Nullable Object visitCGConstantExp(@NonNull CGConstantExp visitCGConstantExp) {
addDependency(visitCGConstantExp, visitCGConstantExp.getNamedValue());
return super.visitCGConstantExp(visitCGConstantExp);
}
@Override
public @Nullable Object visitCGElement(@NonNull CGElement cgElement) {
for (CGElement cgChild : cgElement.getChildren()) {
if ((cgElement instanceof CGValuedElement) && (cgChild instanceof CGValuedElement)) {
addDependency((CGValuedElement)cgElement, (CGValuedElement) cgChild);
}
cgChild.accept(this);
}
return null;
}
@Override
public @Nullable Object visitCGElementId(@NonNull CGElementId cgElementId) {
cgElementId.getElementId().accept(id2DependencyVisitor);
return super.visitCGElementId(cgElementId);
}
@Override
public @Nullable Object visitCGMapExp(@NonNull CGMapExp cgMapExp) {
addElementIdDependency(cgMapExp);
for (CGMapPart cgMapPart : cgMapExp.getParts()) {
addDependency(cgMapExp, cgMapPart);
}
return super.visitCGMapExp(cgMapExp);
}
@Override
public @Nullable Object visitCGMapPart(@NonNull CGMapPart cgMapPart) {
CGMapExp cgMapExp = cgMapPart.getMapExp();
addDependency(cgMapExp, cgMapPart.getKey());
addDependency(cgMapExp, cgMapPart.getValue());
return super.visitCGMapPart(cgMapPart);
}
@Override
public @Nullable Object visitCGTuplePart(@NonNull CGTuplePart cgTuplePart) {
addDependency(cgTuplePart, cgTuplePart.getInit());
return super.visitCGTuplePart(cgTuplePart);
}
@Override
public @Nullable Object visitCGShadowPart(@NonNull CGShadowPart cgShadowPart) {
// CGTupleExp cgTupleExp = cgShadowPart.getTupleExp();
// addDependency(cgTupleExp, cgTuplePart);
addDependency(cgShadowPart, cgShadowPart.getInit());
return super.visitCGShadowPart(cgShadowPart);
}
@Override
public @Nullable Object visitCGTupleExp(@NonNull CGTupleExp cgTupleExp) {
addElementIdDependency(cgTupleExp);
return super.visitCGTupleExp(cgTupleExp);
}
@Override
public @Nullable Object visitCGVariable(@NonNull CGVariable cgVariable) {
CGValuedElement init = cgVariable.getInit();
addDependency(cgVariable, init);
return super.visitCGVariable(cgVariable);
}
@Override
public @Nullable Object visitCGVariableExp(@NonNull CGVariableExp cgVariableExp) {
addDependency(cgVariableExp, cgVariableExp.getReferredVariable());
return super.visitCGVariableExp(cgVariableExp);
}
@Override
public @Nullable Object visiting(@NonNull CGElement visitable) {
throw new UnsupportedOperationException(getClass().getSimpleName() + ": " + visitable.getClass().getSimpleName());
}
public class Id2DependencyVisitor implements IdVisitor<@Nullable Object>
{
@Override
public @Nullable Object visitClassId(final @NonNull ClassId id) {
addElementIdDependency(id, id.getParent());
return null;
}
@Override
public @Nullable Object visitCollectionTypeId(final @NonNull CollectionTypeId id) {
if (id instanceof SpecializedId) {
BindingsId templateBindings = ((SpecializedId)id).getTemplateBindings();
for (int i = 0; i < templateBindings.size(); i++) {
ElementId elementId = ClassUtil.nonNullModel(templateBindings.get(i));
addElementIdDependency(id, elementId);
}
}
return null;
}
@Override
public @Nullable Object visitDataTypeId(final @NonNull DataTypeId id) {
addElementIdDependency(id, id.getParent());
return null;
}
@Override
public @Nullable Object visitEnumerationId(final @NonNull EnumerationId id) {
addElementIdDependency(id, id.getParent());
return null;
}
@Override
public @Nullable Object visitEnumerationLiteralId(final @NonNull EnumerationLiteralId id) {
addElementIdDependency(id, id.getParentId());
return null;
}
@Override
public @Nullable Object visitInvalidId(@NonNull OclInvalidTypeId id) {
return null;
}
@Override
public @Nullable Object visitLambdaTypeId(@NonNull LambdaTypeId id) {
// TODO Auto-generated method stub
return visiting(id);
}
@Override
public @Nullable Object visitMapTypeId(final @NonNull MapTypeId id) {
if (id instanceof SpecializedId) {
BindingsId templateBindings = ((SpecializedId)id).getTemplateBindings();
for (int i = 0; i < templateBindings.size(); i++) {
ElementId elementId = ClassUtil.nonNullModel(templateBindings.get(i));
addElementIdDependency(id, elementId);
}
}
return null;
}
@Override
public @Nullable Object visitNestedPackageId(final @NonNull NestedPackageId id) {
addElementIdDependency(id, id.getParent());
return null;
}
@Override
public @Nullable Object visitNsURIPackageId(final @NonNull NsURIPackageId id) {
return null;
}
@Override
public @Nullable Object visitNullId(@NonNull OclVoidTypeId id) {
return null;
}
@Override
public @Nullable Object visitOperationId(final @NonNull OperationId id) {
addElementIdDependency(id, id.getParent());
for (@NonNull TypeId parameterId : id.getParametersId()) {
addElementIdDependency(id, parameterId);
}
return null;
}
@Override
public @Nullable Object visitPrimitiveTypeId(@NonNull PrimitiveTypeId id) {
return null;
}
@Override
public @Nullable Object visitPropertyId(final @NonNull PropertyId id) {
addElementIdDependency(id, id.getParent());
return null;
}
@Override
public @Nullable Object visitRootPackageId(final @NonNull RootPackageId id) {
return null;
}
@Override
public @Nullable Object visitTemplateBinding(@NonNull TemplateBinding id) {
// TODO Auto-generated method stub
return visiting(id);
}
@Override
public @Nullable Object visitTemplateParameterId(@NonNull TemplateParameterId id) {
return null;
}
@Override
public @Nullable Object visitTemplateableTypeId(@NonNull TemplateableTypeId id) {
// TODO Auto-generated method stub
return visiting(id);
}
@Override
public @Nullable Object visitTuplePartId(final @NonNull TuplePartId id) {
addElementIdDependency(id, id.getTypeId());
return null;
}
@Override
public @Nullable Object visitTupleTypeId(final @NonNull TupleTypeId id) {
for (TuplePartId partId : id.getPartIds()) {
addElementIdDependency(id, partId);
}
return null;
}
@Override
public @Nullable Object visitUnspecifiedId(@NonNull UnspecifiedId id) {
// TODO Auto-generated method stub
return visiting(id);
}
public @Nullable Object visiting(@NonNull ElementId id) {
throw new UnsupportedOperationException(getClass().getSimpleName() + ": " + id.getClass().getName());
}
}
}