Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ public void literal(String name) {
appendln(name);
}

public void clazz(String name, Runnable content) {
block("class " + name, content);
public void clazz(String name, boolean isAbstract, Runnable content) {
block((isAbstract ? "abstract " : "") + "class " + name, content);
}

public void attribute(String name, String type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,16 @@ public String generate() {
// show selected target classes
selected.selectedClasses.stream()
.sorted(Comparator.comparing(EClassifier::getName))
.forEach(this::targetClazz);
// class might have already been generated as super class of another class

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, actually not, because that part is not implemented yet :p

.forEach(this::targetClazzIfNew);
} else {
// show all target classes
targetMetaModel.pack().getEClassifiers().stream()
.sorted(Comparator.comparing(EClassifier::getName))
.forEach(classifier -> {
if (classifier instanceof EClass clazz) {
targetClazz(clazz);
// class might have already been generated as super class of another class
targetClazzIfNew(clazz);
} else if (classifier instanceof EEnum eEnum) {
// enum might have already been generated from a class attribute
enumerationIfNew(eEnum);
Expand Down Expand Up @@ -195,6 +197,12 @@ private void packages() {
out.pack("\"Target: %s\" as %s".formatted(targetName, targetName), Empty);
}

private void targetClazzIfNew(EClass target) {
if (!seenClazzes.contains(target)) {
targetClazz(target);
}
}

private void targetClazz(EClass target) {
clazz(target);

Expand Down Expand Up @@ -228,7 +236,7 @@ private void clazz(EClass clazz) {

// class with attributes
out.clazz(
qualifiedName, () -> {
qualifiedName, clazz.isAbstract(), () -> {
clazz.getEAttributes().forEach(attr -> {
var type = getQualifiedName(attr.getEType());
out.attribute(attr.getName(), type);
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ protected EPackage createPackage() {
protected EClass createClass(AQRTargetClass targetClass) {
EClass target = Ecore.createEClass();
target.setName(targetClass.name());
target.setAbstract(targetClass.isAbstract());

trace.targetToAqr().put(target, targetClass);
trace.aqrToTarget().put(targetClass, target);
Expand All @@ -105,7 +106,11 @@ protected EClass createClass(AQRTargetClass targetClass) {

private void populateClass(AQRTargetClass targetClass) {
var target = Objects.requireNonNull(trace.aqrToTarget().get(targetClass));
var features = targetClass.features().stream().map(this::createFeature).toList();

var superTypes = targetClass.superClasses().stream().map(superClass -> trace.aqrToTarget().get(superClass)).toList();
target.getESuperTypes().addAll(superTypes);

var features = targetClass.features().stream().filter(feature -> !(feature.kind() instanceof AQRFeature.Kind.Override)).map(this::createFeature).toList();
target.getEStructuralFeatures().addAll(features);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package tools.vitruv.neojoin.jvmmodel;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmOperation;
Expand All @@ -17,7 +15,8 @@
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder;
import org.jspecify.annotations.Nullable;
import tools.vitruv.neojoin.Constants;
import tools.vitruv.neojoin.ast.Body;
import tools.vitruv.neojoin.ast.ConcreteBody;
import tools.vitruv.neojoin.ast.ConcreteMainQuery;
import tools.vitruv.neojoin.ast.From;
import tools.vitruv.neojoin.ast.MainQuery;
import tools.vitruv.neojoin.ast.Source;
Expand Down Expand Up @@ -77,14 +76,16 @@ public void infer() {

viewType.eResource().getContents().add(root); // otherwise type resolution fails
for (MainQuery q : viewType.getQueries()) {
inferMainQuery(q);
if (q instanceof ConcreteMainQuery c) {
inferConcreteMainQuery(c);
}
}
viewType.eResource().getContents().remove(root);

acceptor.accept(root);
}

private void inferMainQuery(MainQuery mainQuery) {
private void inferConcreteMainQuery(ConcreteMainQuery mainQuery) {
var targetName = AstUtils.getTargetName(mainQuery);

if (mainQuery.getSource() != null) {
Expand Down Expand Up @@ -128,15 +129,15 @@ private void inferMainQuery(MainQuery mainQuery) {
}

if (mainQuery.getBody() != null) {
inferBody(
inferConcreteBody(
mainQuery.getBody(),
targetName,
paramsForSource(mainQuery.getSource(), AstUtils.isGrouping(mainQuery.getSource()), null)
);
}
}

private void inferBody(Body body, String name, Consumer<JvmOperation> addParams) {
private void inferConcreteBody(ConcreteBody body, String name, Consumer<JvmOperation> addParams) {
Utils.forEachIndexed(
body.getFeatures(), (feature, index) -> {
var exprName = name + "_feature_" + index;
Expand All @@ -147,7 +148,7 @@ private void inferBody(Body body, String name, Consumer<JvmOperation> addParams)
var featureType = inferEClassFromExpressionOrNull(feature.getExpression());
if (featureType != null) {
var subQueryName = AstUtils.getTargetName(feature.getSubQuery(), featureType);
inferBody(
inferConcreteBody(
feature.getSubQuery().getBody(),
subQueryName,
paramsForClass(featureType, feature.getSubQuery())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public IScope getScope(EObject context, EReference reference) {
var from = join.getFrom();
if (source != null && from != null) {
return createJoinConditionOtherScope(source, from);
} else {
return IScope.NULLSCOPE;
}
} else if (reference == AstPackage.Literals.JOIN_FEATURE_CONDITION__FEATURES) {
var condition = (JoinFeatureCondition) context;
Expand All @@ -77,14 +79,16 @@ public IScope getScope(EObject context, EReference reference) {
var right = join.getFrom().getClazz();
if (left != null && right != null) {
return createJoinConditionFieldsScope(left, right);
} else {
return IScope.NULLSCOPE;
}
} else if (reference == AstPackage.Literals.MAIN_QUERY__SUPER_CLASSES) {
return createSuperClassScope(AstUtils.getViewType(context));
} else if (reference == AstPackage.Literals.FEATURE__TYPE) {
return createFeatureTypeScope(AstUtils.getViewType(context));
} else {
return super.getScope(context, reference);
}

return IScope.NULLSCOPE;
}

/**
Expand Down Expand Up @@ -137,6 +141,14 @@ private IScope createJoinConditionFieldsScope(EClass left, EClass right) {
return new SimpleScope(IScope.NULLSCOPE, candidates);
}

private IScope createSuperClassScope(ViewTypeDefinition viewType) {
var queryCandidates = AstUtils.getAllQueries(viewType)
.map(query -> EObjectDescription.create(AstUtils.getTargetName(query, expressionHelper), query))
.toList();

return new SimpleScope(queryCandidates);
}

/**
* Scope for available types when specifying an explicit type for a feature.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
import org.eclipse.xtext.EcoreUtil2;
import org.jspecify.annotations.Nullable;
import tools.vitruv.neojoin.aqr.AQRFeatureOptionsBuilder;
import tools.vitruv.neojoin.ast.AbstractBody;
import tools.vitruv.neojoin.ast.AbstractMainQuery;
import tools.vitruv.neojoin.ast.Body;
import tools.vitruv.neojoin.ast.ConcreteBody;
import tools.vitruv.neojoin.ast.ConcreteFeature;
import tools.vitruv.neojoin.ast.ConcreteMainQuery;
import tools.vitruv.neojoin.ast.Feature;
import tools.vitruv.neojoin.ast.From;
import tools.vitruv.neojoin.ast.Import;
Expand All @@ -20,6 +26,7 @@
import tools.vitruv.neojoin.jvmmodel.ExpressionHelper;
import tools.vitruv.neojoin.jvmmodel.TypeResolutionException;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
Expand All @@ -43,10 +50,12 @@ public static String getTargetName(MainQuery mainQuery) {
return mainQuery.getName();
}

if (mainQuery.getSource() != null) {
var source = mainQuery.getSource().getFrom().getClazz();
if (source != null && source.getName() != null) {
return source.getName();
if (mainQuery instanceof ConcreteMainQuery concreteMainQuery) {
if (concreteMainQuery.getSource() != null) {
var source = concreteMainQuery.getSource().getFrom().getClazz();
if (source != null && source.getName() != null) {
return source.getName();
}
}
}

Expand Down Expand Up @@ -101,7 +110,7 @@ public static String getTargetName(Query query, ExpressionHelper expressionHelpe
SubQuery subQuery,
ExpressionHelper expressionHelper
) {
if (subQuery.eContainer() instanceof Feature feature) {
if (subQuery.eContainer() instanceof ConcreteFeature feature) {
try {
var typeInfo = expressionHelper.inferEType(feature.getExpression());
if (typeInfo != null && typeInfo.classifier() instanceof EClass clazz) {
Expand Down Expand Up @@ -227,6 +236,15 @@ public static boolean isManyMultiplicity(MultiplicityExpr multiplicity) {
return isManyMultiplicity(AQRFeatureOptionsBuilder.normalizeMultiplicity(multiplicity));
}

public static boolean isManyMultiplicityDefinition(Feature feature) {
var explicitMultiplicity = AstUtils.findMultiplicityExpression(feature);
if (explicitMultiplicity != null) {
return AstUtils.isManyMultiplicity(explicitMultiplicity);
} else {
return false; // should we try to infer this from the declared type?
}
}

/**
* Get all queries (main and sub queries) contained in the given view type.
*/
Expand All @@ -236,14 +254,42 @@ public static Stream<Query> getAllQueries(ViewTypeDefinition viewType) {
}

private static Stream<Query> getAllQueriesImpl(Query query) {
if (query.getBody() == null) {
var body = getBody(query);
if (body == null) {
return Stream.of(query);
} else {
var subQueries = query.getBody().getFeatures().stream()
.map(Feature::getSubQuery)
} else if (body instanceof ConcreteBody concreteBody) {
var subQueries = concreteBody.getFeatures().stream()
.map(ConcreteFeature::getSubQuery)
.filter(Objects::nonNull)
.flatMap(AstUtils::getAllQueriesImpl);
return Stream.concat(Stream.of(query), subQueries);
} else if (body instanceof AbstractBody) {
// abstract features cannot have sub queries
return Stream.of(query);
} else {
return fail("A query body must be either an abstract or a concrete body");
}
}

public static Body getBody(Query query) {
if (query instanceof ConcreteMainQuery concreteMainQuery) {
return concreteMainQuery.getBody();
} else if (query instanceof AbstractMainQuery abstractMainQuery) {
return abstractMainQuery.getBody();
} else if (query instanceof SubQuery subQuery) {
return subQuery.getBody();
} else {
return fail("Query must be either a concrete main query, an abstract main query, or a sub query");
}
}

public static List<Feature> getFeatures(Body body) {
if (body instanceof ConcreteBody concreteBody) {
return concreteBody.getFeatures().stream().map(e -> (Feature) e).toList();
} else if (body instanceof AbstractBody abstractBody) {
return abstractBody.getFeatures().stream().map(e -> (Feature) e).toList();
} else {
return fail("Body must be either a concrete query body or an abstract query body");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.jspecify.annotations.Nullable;
import tools.vitruv.neojoin.ast.AstPackage;
import tools.vitruv.neojoin.ast.BooleanModifier;
import tools.vitruv.neojoin.ast.ConcreteFeature;
import tools.vitruv.neojoin.ast.Feature;
import tools.vitruv.neojoin.ast.Modifier;
import tools.vitruv.neojoin.ast.MultiplicityBounds;
Expand Down Expand Up @@ -83,9 +84,9 @@ void checkApplicability(Feature feature) {
private @Nullable Boolean isAttribute(Feature feature) {
if (feature.getType() != null) {
return feature.getType() instanceof EDataType;
} else {
} else if (feature instanceof ConcreteFeature concreteFeature) {
try {
var inferredType = expressionHelper.inferEType(feature.getExpression());
var inferredType = expressionHelper.inferEType(concreteFeature.getExpression());
if (inferredType != null) {
return inferredType.classifier() instanceof EDataType;
}
Expand Down Expand Up @@ -145,26 +146,28 @@ void checkMultiplicity(Feature feature) {
}

var explicitIsMany = AstUtils.isManyMultiplicity(explicitMultiplicity);
try {
var inferredType = expressionHelper.inferEType(feature.getExpression());
if (inferredType != null) {
var inferredIsMany = inferredType.isMany();
if (explicitIsMany && !inferredIsMany) {
error(
"Cannot assign a single value to a multi-valued feature",
feature,
AstPackage.Literals.FEATURE__EXPRESSION
);
} else if (!explicitIsMany && inferredIsMany) {
error(
"Cannot assign multiple values to a single-valued feature",
feature,
AstPackage.Literals.FEATURE__EXPRESSION
);
if (feature instanceof ConcreteFeature concreteFeature) {
try {
var inferredType = expressionHelper.inferEType(concreteFeature.getExpression());
if (inferredType != null) {
var inferredIsMany = inferredType.isMany();
if (explicitIsMany && !inferredIsMany) {
error(
"Cannot assign a single value to a multi-valued feature",
concreteFeature,
AstPackage.Literals.CONCRETE_FEATURE__EXPRESSION
);
} else if (!explicitIsMany && inferredIsMany) {
error(
"Cannot assign multiple values to a single-valued feature",
concreteFeature,
AstPackage.Literals.CONCRETE_FEATURE__EXPRESSION
);
}
}
} catch (TypeResolutionException e) {
// ignore: will be handled by type checking
}
} catch (TypeResolutionException e) {
// ignore: will be handled by type checking
}
}
}
Expand Down
Loading
Loading