[OLINGO-508] Fixed OneToMany self reference
diff --git a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProvider.java b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProvider.java
index 8b6e7f6..81546a7 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProvider.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProvider.java
@@ -374,9 +374,13 @@
}
EdmNavigationProperty enp = field.getAnnotation(EdmNavigationProperty.class);
if (enp != null) {
- final NavigationProperty navProperty = createNavigationProperty(namespace, enp, field);
+ Class<?> fromClass = field.getDeclaringClass();
+ Class<?> toClass = ClassHelper.getFieldType(field);
+ AnnotationHelper.AnnotatedNavInfo info = ANNOTATION_HELPER.getCommonNavigationInfo(fromClass, toClass);
+
+ final NavigationProperty navProperty = createNavigationProperty(namespace, field, info);
navProperties.add(navProperty);
- Association association = createAssociation(field, navProperty);
+ Association association = createAssociation(info);
associations.add(association);
}
EdmMediaResourceContent emrc = field.getAnnotation(EdmMediaResourceContent.class);
@@ -498,17 +502,15 @@
return cp;
}
- private NavigationProperty createNavigationProperty(final String namespace, final EdmNavigationProperty enp,
- final Field field) {
+ private NavigationProperty createNavigationProperty(final String namespace, Field field,
+ AnnotationHelper.AnnotatedNavInfo navInfo) {
NavigationProperty navProp = new NavigationProperty();
navProp.setName(ANNOTATION_HELPER.getPropertyName(field));
- String fromRole = ANNOTATION_HELPER.extractFromRoleName(enp, field);
+ String fromRole = navInfo.getFromRoleName();
navProp.setFromRole(fromRole);
+ navProp.setToRole(navInfo.getToRoleName());
- String toRole = ANNOTATION_HELPER.extractToRoleName(enp, field);
- navProp.setToRole(toRole);
-
- String relationshipName = ANNOTATION_HELPER.extractRelationshipName(enp, field);
+ String relationshipName = navInfo.getRelationshipName();
navProp.setRelationship(new FullQualifiedName(namespace, relationshipName));
return navProp;
@@ -600,25 +602,22 @@
return ANNOTATION_HELPER.extractEntityTypeFqn(baseEntityClass);
}
- private Association createAssociation(final Field field, final NavigationProperty navProperty) {
+ private Association createAssociation(final AnnotationHelper.AnnotatedNavInfo info) {
Association association = new Association();
- EdmNavigationProperty navigation = field.getAnnotation(EdmNavigationProperty.class);
AssociationEnd fromEnd = new AssociationEnd();
- fromEnd.setRole(navProperty.getFromRole());
- String typeName = ANNOTATION_HELPER.extractEntityTypeName(field.getDeclaringClass());
- fromEnd.setType(new FullQualifiedName(namespace, typeName));
- fromEnd.setMultiplicity(EdmMultiplicity.ONE);
+ fromEnd.setRole(info.getFromRoleName());
+ fromEnd.setType(new FullQualifiedName(namespace, info.getFromTypeName()));
+ fromEnd.setMultiplicity(info.getFromMultiplicity());
association.setEnd1(fromEnd);
AssociationEnd toEnd = new AssociationEnd();
- toEnd.setRole(navProperty.getToRole());
- String toTypeName = ANNOTATION_HELPER.extractEntitTypeName(navigation, field);
- toEnd.setType(new FullQualifiedName(namespace, toTypeName));
- toEnd.setMultiplicity(ANNOTATION_HELPER.getMultiplicity(navigation, field));
+ toEnd.setRole(info.getToRoleName());
+ toEnd.setType(new FullQualifiedName(namespace, info.getToTypeName()));
+ toEnd.setMultiplicity(info.getToMultiplicity());
association.setEnd2(toEnd);
- String associationName = navProperty.getRelationship().getName();
+ String associationName = info.getRelationshipName();
association.setName(associationName);
return association;
}
diff --git a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelper.java b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelper.java
index 0d2ff11..d0f072a 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelper.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelper.java
@@ -103,22 +103,18 @@
private boolean isEqual(final Object firstKey, final Object secondKey) {
if (firstKey == null) {
- if (secondKey == null) {
- return true;
- } else {
- return secondKey.equals(firstKey);
- }
+ return secondKey == null || secondKey.equals(firstKey);
} else {
return firstKey.equals(secondKey);
}
}
- public String extractEntitTypeName(final EdmNavigationProperty enp, final Class<?> fallbackClass) {
+ public String extractEntityTypeName(final EdmNavigationProperty enp, final Class<?> fallbackClass) {
Class<?> entityTypeClass = enp.toType();
return extractEntityTypeName(entityTypeClass == Object.class ? fallbackClass : entityTypeClass);
}
- public String extractEntitTypeName(final EdmNavigationProperty enp, final Field field) {
+ public String extractEntityTypeName(final EdmNavigationProperty enp, final Field field) {
Class<?> entityTypeClass = enp.toType();
if (entityTypeClass == Object.class) {
Class<?> toClass = field.getType();
@@ -199,8 +195,7 @@
}
public String generateNamespace(final Class<?> annotatedClass) {
- String packageName = annotatedClass.getPackage().getName();
- return packageName;
+ return annotatedClass.getPackage().getName();
}
/**
@@ -263,40 +258,53 @@
return propertyName;
}
- public String extractFromRoleName(final EdmNavigationProperty enp, final Field field) {
- return getCanonicalRole(field.getDeclaringClass());
- }
-
public String extractToRoleName(final EdmNavigationProperty enp, final Field field) {
String role = enp.toRole();
if (role.isEmpty()) {
- role = getCanonicalRole(
- field.getType().isArray() || Collection.class.isAssignableFrom(field.getType()) ?
- (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0] : field.getType());
+ role = getCanonicalRoleName(field.getName());
}
return role;
}
- public String getCanonicalRole(final Class<?> fallbackClass) {
- String toRole = extractEntityTypeName(fallbackClass);
- return "r_" + toRole;
+ public String extractFromRoleEntityName(final Field field) {
+ return extractEntityTypeName(field.getDeclaringClass());
+ }
+
+ public String extractToRoleEntityName(final EdmNavigationProperty enp, final Field field) {
+ Class<?> clazz = enp.toType();
+ if (clazz == Object.class) {
+ if(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType())) {
+ clazz = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+ } else {
+ clazz = field.getType();
+ }
+ }
+ return extractEntityTypeName(clazz);
+ }
+
+ public String getCanonicalRoleName(String name) {
+ return "r_" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
}
public String extractRelationshipName(final EdmNavigationProperty enp, final Field field) {
String relationshipName = enp.association();
if (relationshipName.isEmpty()) {
- final String fromRole = extractFromRoleName(enp, field);
- final String toRole = extractToRoleName(enp, field);
- if (fromRole.compareTo(toRole) > 0) {
- relationshipName = toRole + "-" + fromRole;
- } else {
- relationshipName = fromRole + "-" + toRole;
- }
+ final String fromRole = extractFromRoleEntityName(field);
+ final String toRole = extractToRoleEntityName(enp, field);
+ return createCanonicalRelationshipName(fromRole, toRole);
}
return relationshipName;
}
- public EdmMultiplicity getMultiplicity(final EdmNavigationProperty enp, final Field field) {
+ public String createCanonicalRelationshipName(String fromRole, String toRole) {
+ if (fromRole.compareTo(toRole) > 0) {
+ return toRole + "-" + fromRole;
+ } else {
+ return fromRole + "-" + toRole;
+ }
+ }
+
+ public EdmMultiplicity extractMultiplicity(final EdmNavigationProperty enp, final Field field) {
EdmMultiplicity multiplicity = mapMultiplicity(enp.toMultiplicity());
final boolean isCollectionType = field.getType().isArray() || Collection.class.isAssignableFrom(field.getType());
@@ -357,48 +365,131 @@
return fromField;
}
+ public String getFromRoleName() {
+ if(isBiDirectional()) {
+ return extractFromRoleEntityName(toField);
+ }
+ return extractToRoleName(toNavigation, toField);
+ }
+
public Field getToField() {
return toField;
}
+ public String getToRoleName() {
+ if(isBiDirectional()) {
+ return extractToRoleName(toNavigation, toField);
+ }
+ return extractToRoleName(fromNavigation, fromField);
+ }
+
public EdmMultiplicity getFromMultiplicity() {
- return getMultiplicity(toNavigation, toField);
+ if(isBiDirectional()) {
+ return EdmMultiplicity.ONE;
+ }
+ return extractMultiplicity(toNavigation, toField);
}
public EdmMultiplicity getToMultiplicity() {
- return getMultiplicity(fromNavigation, fromField);
+ if(isBiDirectional()) {
+ return extractMultiplicity(toNavigation, toField);
+ }
+ return extractMultiplicity(fromNavigation, fromField);
}
public boolean isBiDirectional() {
- return toNavigation != null;
+ return fromNavigation == null;
+ }
+
+ public String getRelationshipName() {
+ String toAssociation = toNavigation.association();
+ String fromAssociation = "";
+ if(!isBiDirectional()) {
+ fromAssociation = fromNavigation.association();
+ }
+
+ if(fromAssociation.isEmpty() && fromAssociation.equals(toAssociation)) {
+ return createCanonicalRelationshipName(getFromRoleName(), getToRoleName());
+ } else if(toAssociation.isEmpty()) {
+ return fromAssociation;
+ } else if(!toAssociation.equals(fromAssociation)) {
+ throw new AnnotationRuntimeException("Invalid associations for navigation properties '" +
+ this.toString() + "'");
+ }
+ return toAssociation;
+ }
+
+ public String getFromTypeName() {
+ if(isBiDirectional()) {
+ return extractEntityTypeName(toField.getDeclaringClass());
+ }
+ return extractEntityTypeName(fromField.getDeclaringClass());
+ }
+
+ public String getToTypeName() {
+ if(isBiDirectional()) {
+ return extractEntityTypeName(ClassHelper.getFieldType(toField));
+ }
+ return extractEntityTypeName(toField.getDeclaringClass());
+ }
+
+ @Override
+ public String toString() {
+ if(isBiDirectional()) {
+ return "AnnotatedNavInfo{biDirectional = true" +
+ ", toField=" + toField.getName() +
+ ", toNavigation=" + toNavigation.name() + '}';
+ }
+ return "AnnotatedNavInfo{" +
+ "fromField=" + fromField.getName() +
+ ", toField=" + toField.getName() +
+ ", fromNavigation=" + fromNavigation.name() +
+ ", toNavigation=" + toNavigation.name() + '}';
}
}
+
public AnnotatedNavInfo getCommonNavigationInfo(final Class<?> sourceClass, final Class<?> targetClass) {
List<Field> sourceFields = getAnnotatedFields(sourceClass, EdmNavigationProperty.class);
List<Field> targetFields = getAnnotatedFields(targetClass, EdmNavigationProperty.class);
+ if(sourceClass == targetClass) {
+ // special case, actual handled as bi-directional
+ return getCommonNavigationInfoBiDirectional(sourceClass, targetClass);
+ }
+
// first try via association name to get full navigation information
for (Field sourceField : sourceFields) {
- final EdmNavigationProperty sourceNav = sourceField.getAnnotation(EdmNavigationProperty.class);
- final String sourceAssociation = extractRelationshipName(sourceNav, sourceField);
- for (Field targetField : targetFields) {
- final EdmNavigationProperty targetNav = targetField.getAnnotation(EdmNavigationProperty.class);
- final String targetAssociation = extractRelationshipName(targetNav, targetField);
- if (sourceAssociation.equals(targetAssociation)) {
- return new AnnotatedNavInfo(sourceField, targetField, sourceNav, targetNav);
+ if(ClassHelper.getFieldType(sourceField) == targetClass) {
+ final EdmNavigationProperty sourceNav = sourceField.getAnnotation(EdmNavigationProperty.class);
+ final String sourceAssociation = extractRelationshipName(sourceNav, sourceField);
+ for (Field targetField : targetFields) {
+ if(ClassHelper.getFieldType(targetField) == sourceClass) {
+ final EdmNavigationProperty targetNav = targetField.getAnnotation(EdmNavigationProperty.class);
+ final String targetAssociation = extractRelationshipName(targetNav, targetField);
+ if (sourceAssociation.equals(targetAssociation)) {
+ return new AnnotatedNavInfo(sourceField, targetField, sourceNav, targetNav);
+ }
+ }
}
}
}
- // if nothing was found assume none bi-directinal navigation
+ // if nothing was found assume/guess none bi-directional navigation
+ return getCommonNavigationInfoBiDirectional(sourceClass, targetClass);
+ }
+
+ private AnnotatedNavInfo getCommonNavigationInfoBiDirectional(final Class<?> sourceClass,
+ final Class<?> targetClass) {
+ List<Field> sourceFields = getAnnotatedFields(sourceClass, EdmNavigationProperty.class);
+
String targetEntityTypeName = extractEntityTypeName(targetClass);
for (Field sourceField : sourceFields) {
final EdmNavigationProperty sourceNav = sourceField.getAnnotation(EdmNavigationProperty.class);
- final String navTargetEntityName = extractEntitTypeName(sourceNav, sourceField);
+ final String navTargetEntityName = extractEntityTypeName(sourceNav, sourceField);
if (navTargetEntityName.equals(targetEntityTypeName)) {
- return new AnnotatedNavInfo(sourceField, null, sourceNav, null);
+ return new AnnotatedNavInfo(null, sourceField, null, sourceNav);
}
}
@@ -576,7 +667,6 @@
/**
*
- * @param instance
* @param resultClass
* @param annotation
* @param inherited
diff --git a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/ClassHelper.java b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/ClassHelper.java
index 24082ff..a5e9eb9 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/ClassHelper.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/ClassHelper.java
@@ -23,6 +23,7 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -180,6 +181,21 @@
}
}
+ /**
+ * Get the type of the field. For arrays and collections the type of the array or collection is returned.
+ *
+ * @param field field for which the type is extracted
+ * @return type of the field (also for arrays or collections)
+ */
+ public static Class<?> getFieldType(Field field) {
+ if(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType())) {
+ return (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+ } else {
+ return field.getType();
+ }
+ }
+
+
public static Object getFieldValue(final Object instance, final Field field) {
try {
synchronized (field) {
diff --git a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProviderTest.java b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProviderTest.java
index 44187d4..50a1520 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProviderTest.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProviderTest.java
@@ -222,7 +222,7 @@
assertEquals("Buildings", asBuildingRooms.getEnd1().getEntitySet());
assertEquals("r_Building", asBuildingRooms.getEnd1().getRole());
assertEquals("Rooms", asBuildingRooms.getEnd2().getEntitySet());
- assertEquals("r_Room", asBuildingRooms.getEnd2().getRole());
+ assertEquals("r_Rooms", asBuildingRooms.getEnd2().getRole());
}
@Test
@@ -252,7 +252,7 @@
assertEquals(6, entitySets.size());
List<Association> associations = schema.getAssociations();
- assertEquals(4, associations.size());
+ assertEquals(5, associations.size());
for (Association association : associations) {
assertNotNull(association.getName());
validateAssociation(association);
@@ -265,22 +265,26 @@
private void validateAssociation(final Association association) {
String name = association.getName();
- if (name.equals("r_Employee-r_Room")) {
+ if (name.equals("r_Employees-r_Room")) {
validateAssociation(association,
"r_Room", EdmMultiplicity.ONE, defaultFqn("Room"),
- "r_Employee", EdmMultiplicity.MANY, defaultFqn("Employee"));
+ "r_Employees", EdmMultiplicity.MANY, defaultFqn("Employee"));
} else if (name.equals("BuildingRooms")) {
validateAssociation(association,
"r_Building", EdmMultiplicity.ONE, defaultFqn("Building"),
- "r_Room", EdmMultiplicity.MANY, defaultFqn("Room"));
+ "r_Rooms", EdmMultiplicity.MANY, defaultFqn("Room"));
} else if (name.equals("ManagerEmployees")) {
validateAssociation(association,
"r_Manager", EdmMultiplicity.ONE, defaultFqn("Manager"),
- "r_Employee", EdmMultiplicity.MANY, defaultFqn("Employee"));
+ "r_Employees", EdmMultiplicity.MANY, defaultFqn("Employee"));
} else if (name.equals("TeamEmployees")) {
validateAssociation(association,
"r_Team", EdmMultiplicity.ONE, defaultFqn("Team"),
- "r_Employee", EdmMultiplicity.MANY, defaultFqn("Employee"));
+ "r_Employees", EdmMultiplicity.MANY, defaultFqn("Employee"));
+ } else if (name.equals("Team-r_SubTeam")) {
+ validateAssociation(association,
+ "Team", EdmMultiplicity.ONE, defaultFqn("Team"),
+ "r_SubTeam", EdmMultiplicity.ONE, defaultFqn("Team"));
} else {
fail("Got unknown association to validate with name '" + name + "'.");
}
@@ -333,11 +337,11 @@
for (NavigationProperty navigationProperty : employee.getNavigationProperties()) {
if (navigationProperty.getName().equals("ne_Manager")) {
- validateNavProperty(navigationProperty, "ManagerEmployees", "r_Employee", "r_Manager");
+ validateNavProperty(navigationProperty, "ManagerEmployees", "r_Employees", "r_Manager");
} else if (navigationProperty.getName().equals("ne_Team")) {
- validateNavProperty(navigationProperty, "TeamEmployees", "r_Employee", "r_Team");
+ validateNavProperty(navigationProperty, "TeamEmployees", "r_Employees", "r_Team");
} else if (navigationProperty.getName().equals("ne_Room")) {
- validateNavProperty(navigationProperty, "r_Employee-r_Room", "r_Employee", "r_Room");
+ validateNavProperty(navigationProperty, "r_Employees-r_Room", "r_Employees", "r_Room");
} else {
fail("Got unexpected navigation property with name '" + navigationProperty.getName() + "'.");
}
@@ -376,9 +380,11 @@
assertEquals(ModelSharedConstants.NAMESPACE_1, team.getBaseType().getNamespace());
assertEquals(1, team.getProperties().size());
- assertEquals(1, team.getNavigationProperties().size());
- NavigationProperty navigationProperty = team.getNavigationProperties().get(0);
- validateNavProperty(navigationProperty, "TeamEmployees", "r_Team", "r_Employee");
+ assertEquals(2, team.getNavigationProperties().size());
+ NavigationProperty navPropTeamEmployess = team.getNavigationProperties().get(0);
+ validateNavProperty(navPropTeamEmployess, "TeamEmployees", "r_Team", "r_Employees");
+ NavigationProperty navPropTeamTeam = team.getNavigationProperties().get(1);
+ validateNavProperty(navPropTeamTeam, "Team-r_SubTeam", "Team", "r_SubTeam");
}
@Test
@@ -459,9 +465,9 @@
for (NavigationProperty navigationProperty : navigationProperties) {
if (navigationProperty.getName().equals("nr_Employees")) {
- validateNavProperty(navigationProperty, "r_Employee-r_Room", "r_Room", "r_Employee");
+ validateNavProperty(navigationProperty, "r_Employees-r_Room", "r_Room", "r_Employees");
} else if (navigationProperty.getName().equals("nr_Building")) {
- validateNavProperty(navigationProperty, "BuildingRooms", "r_Room", "r_Building");
+ validateNavProperty(navigationProperty, "BuildingRooms", "r_Rooms", "r_Building");
} else {
fail("Got unexpected navigation property with name '" + navigationProperty.getName() + "'.");
}
diff --git a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/model/Team.java b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/model/Team.java
index 8e5e794..23371a6 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/model/Team.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/model/Team.java
@@ -38,6 +38,8 @@
private Boolean isScrumTeam;
@EdmNavigationProperty(name = "nt_Employees", association = "TeamEmployees", toMultiplicity = Multiplicity.MANY)
private List<Employee> employees = new ArrayList<Employee>();
+ @EdmNavigationProperty
+ private Team subTeam;
public Team() {
super(-1, null);
@@ -63,6 +65,10 @@
return employees;
}
+ public Team getSubTeam() {
+ return subTeam;
+ }
+
@Override
public int hashCode() {
return id;
diff --git a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelperTest.java b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelperTest.java
index 18d7df2..478a1b7 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelperTest.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelperTest.java
@@ -17,6 +17,7 @@
import java.lang.reflect.Field;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import junit.framework.Assert;
@@ -160,26 +161,36 @@
}
@Test
- public void extractEntitTypeNameViaNavigation() throws Exception {
+ public void extractEntityTypeNameViaNavigation() throws Exception {
Field field = NavigationAnnotated.class.getDeclaredField("navigationPropertySimpleEntity");
EdmNavigationProperty enp = field.getAnnotation(EdmNavigationProperty.class);
- String name = annotationHelper.extractEntitTypeName(enp, SimpleEntity.class);
+ String name = annotationHelper.extractEntityTypeName(enp, SimpleEntity.class);
Assert.assertEquals("SimpleEntity", name);
}
@Test
- public void extractEntitTypeNameViaNavigationField() throws Exception {
+ public void extractEntityTypeNameViaNavigationField() throws Exception {
Field field = NavigationAnnotated.class.getDeclaredField("navigationPropertyDefault");
EdmNavigationProperty enp = field.getAnnotation(EdmNavigationProperty.class);
- String name = annotationHelper.extractEntitTypeName(enp, field);
+ String name = annotationHelper.extractEntityTypeName(enp, field);
Assert.assertEquals("SimpleEntity", name);
}
@Test
+ public void selfReferencedEntityTypeNameViaNavigationField() throws Exception {
+ Field field = NavigationAnnotated.class.getDeclaredField("selfReferencedNavigation");
+ EdmNavigationProperty enp = field.getAnnotation(EdmNavigationProperty.class);
+
+ String name = annotationHelper.extractToRoleName(enp, field);
+
+ Assert.assertEquals("r_SelfReferencedNavigation", name);
+ }
+
+ @Test
public void getFieldTypeForPropertyNullInstance() throws Exception {
Object result = annotationHelper.getFieldTypeForProperty(null, "");
Assert.assertNull(result);
@@ -247,11 +258,14 @@
}
}
+ @EdmEntityType
private class NavigationAnnotated {
@EdmNavigationProperty(toType = SimpleEntity.class)
SimpleEntity navigationPropertySimpleEntity;
@EdmNavigationProperty
SimpleEntity navigationPropertyDefault;
+ @EdmNavigationProperty
+ List<NavigationAnnotated> selfReferencedNavigation;
}
private class ConversionProperty {
diff --git a/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/AnnotationRefServiceFactory.java b/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/AnnotationRefServiceFactory.java
index 5a3766f..6bf3588 100644
--- a/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/AnnotationRefServiceFactory.java
+++ b/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/AnnotationRefServiceFactory.java
@@ -126,7 +126,7 @@
teamDs.create(createTeam("Team Beta", false));
teamDs.create(createTeam("Team Gamma", false));
teamDs.create(createTeam("Team Omega", true));
- teamDs.create(createTeam("Team Zeta", true));
+ teamDs.create(createTeam("Team Zeta", true, createTeam("SubTeamOne", false)));
DataStore<Building> buildingsDs = getDataStore(Building.class);
Building redBuilding = createBuilding("Red Building");
@@ -179,9 +179,14 @@
}
private static Team createTeam(final String teamName, final boolean isScrumTeam) {
+ return createTeam(teamName, isScrumTeam, null);
+ }
+
+ private static Team createTeam(final String teamName, final boolean isScrumTeam, Team subTeam) {
Team team = new Team();
team.setName(teamName);
team.setScrumTeam(isScrumTeam);
+ subTeam.setSubTeam(subTeam);
return team;
}
diff --git a/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/model/Team.java b/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/model/Team.java
index 7f48f66..9e3d38c 100644
--- a/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/model/Team.java
+++ b/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/model/Team.java
@@ -38,6 +38,8 @@
private Boolean isScrumTeam;
@EdmNavigationProperty(name = "nt_Employees", association = "TeamEmployees", toMultiplicity = Multiplicity.MANY)
private List<Employee> employees = new ArrayList<Employee>();
+ @EdmNavigationProperty
+ private Team subTeam;
public Boolean isScrumTeam() {
return isScrumTeam;
@@ -55,6 +57,14 @@
return employees;
}
+ public void setSubTeam(Team subTeam) {
+ this.subTeam = subTeam;
+ }
+
+ public Team getSubTeam() {
+ return subTeam;
+ }
+
@Override
public int hashCode() {
return super.hashCode();
diff --git a/odata2-annotation-processor/annotation-processor-ref/src/test/java/org/apache/olingo/odata2/annotation/processor/ref/MetadataTest.java b/odata2-annotation-processor/annotation-processor-ref/src/test/java/org/apache/olingo/odata2/annotation/processor/ref/MetadataTest.java
index 5114594..0068869 100644
--- a/odata2-annotation-processor/annotation-processor-ref/src/test/java/org/apache/olingo/odata2/annotation/processor/ref/MetadataTest.java
+++ b/odata2-annotation-processor/annotation-processor-ref/src/test/java/org/apache/olingo/odata2/annotation/processor/ref/MetadataTest.java
@@ -101,17 +101,17 @@
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Employee' and" +
" @m:HasStream='true']/edm:NavigationProperty[@Name='ne_Manager' and " +
- "@Relationship='RefScenario.ManagerEmployees' and @FromRole='r_Employee' and @ToRole='r_Manager']",
+ "@Relationship='RefScenario.ManagerEmployees' and @FromRole='r_Employees' and @ToRole='r_Manager']",
payload);
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Employee' and" +
" @m:HasStream='true']/edm:NavigationProperty[@Name='ne_Team' and " +
- "@Relationship='RefScenario.TeamEmployees' and @FromRole='r_Employee' and @ToRole='r_Team']",
+ "@Relationship='RefScenario.TeamEmployees' and @FromRole='r_Employees' and @ToRole='r_Team']",
payload);
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Employee' and " +
"@m:HasStream='true']/edm:NavigationProperty[@Name='ne_Room' and " +
- "@Relationship='RefScenario.r_Employee-r_Room' and @FromRole='r_Employee' and @ToRole='r_Room']",
+ "@Relationship='RefScenario.r_Employees-r_Room' and @FromRole='r_Employees' and @ToRole='r_Room']",
payload);
// Team
@@ -126,7 +126,7 @@
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Team' and " +
"@BaseType='RefScenario.Base']/edm:NavigationProperty[@Name='nt_Employees' and " +
- "@Relationship='RefScenario.TeamEmployees' and @FromRole='r_Team' and @ToRole='r_Employee']",
+ "@Relationship='RefScenario.TeamEmployees' and @FromRole='r_Team' and @ToRole='r_Employees']",
payload);
// Room
@@ -144,12 +144,12 @@
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Room' and" +
" @BaseType='RefScenario.Base']/edm:NavigationProperty[@Name='nr_Employees' and " +
- "@Relationship='RefScenario.r_Employee-r_Room' and @FromRole='r_Room' and @ToRole='r_Employee']",
+ "@Relationship='RefScenario.r_Employees-r_Room' and @FromRole='r_Room' and @ToRole='r_Employees']",
payload);
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Room' and " +
"@BaseType='RefScenario.Base']/edm:NavigationProperty[@Name='nr_Building' and " +
- "@Relationship='RefScenario.BuildingRooms' and @FromRole='r_Room' and @ToRole='r_Building']",
+ "@Relationship='RefScenario.BuildingRooms' and @FromRole='r_Rooms' and @ToRole='r_Building']",
payload);
// Manager
@@ -157,7 +157,7 @@
"@BaseType='RefScenario.Employee']", payload);
assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Manager' and " +
"@BaseType='RefScenario.Employee']/edm:NavigationProperty[@Name='nm_Employees' and " +
- "@Relationship='RefScenario.ManagerEmployees' and @FromRole='r_Manager' and @ToRole='r_Employee']",
+ "@Relationship='RefScenario.ManagerEmployees' and @FromRole='r_Manager' and @ToRole='r_Employees']",
payload);
// Building
@@ -174,7 +174,7 @@
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Building']" +
"/edm:NavigationProperty[@Name='nb_Rooms' and @Relationship='RefScenario.BuildingRooms' " +
- "and @FromRole='r_Building' and @ToRole='r_Room']", payload);
+ "and @FromRole='r_Building' and @ToRole='r_Rooms']", payload);
// Base
assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Base' and @Abstract='true']",
@@ -219,22 +219,22 @@
// ManagerEmployees
assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='ManagerEmployees']", payload);
assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='ManagerEmployees']" +
- "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employee']", payload);
+ "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employees']", payload);
assertXpathExists( "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='ManagerEmployees']" +
"/edm:End[@Type='RefScenario.Manager' and @Multiplicity='1' and @Role='r_Manager']", payload);
// TeamEmployees
assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='TeamEmployees']", payload);
assertXpathExists( "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='TeamEmployees']" +
- "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employee']", payload);
+ "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employees']", payload);
assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='TeamEmployees']" +
"/edm:End[@Type='RefScenario.Team' and @Multiplicity='1' and @Role='r_Team']", payload);
// RoomEmployees
- assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employee-r_Room']", payload);
- assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employee-r_Room']" +
- "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employee']", payload);
- assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employee-r_Room']" +
+ assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employees-r_Room']", payload);
+ assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employees-r_Room']" +
+ "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employees']", payload);
+ assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employees-r_Room']" +
"/edm:End[@Type='RefScenario.Room' and @Multiplicity='1' and @Role='r_Room']", payload);
// BuildingRooms
@@ -242,7 +242,7 @@
assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='BuildingRooms']" +
"/edm:End[@Type='RefScenario.Building' and @Multiplicity='1' and @Role='r_Building']", payload);
assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='BuildingRooms']" +
- "/edm:End[@Type='RefScenario.Room' and @Multiplicity='*' and @Role='r_Room']", payload);
+ "/edm:End[@Type='RefScenario.Room' and @Multiplicity='*' and @Role='r_Rooms']", payload);
}
@Test
@@ -291,7 +291,7 @@
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
"@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='ManagerEmployees' and " +
- "@Association='RefScenario.ManagerEmployees']/edm:End[@EntitySet='Employees' and @Role='r_Employee']",
+ "@Association='RefScenario.ManagerEmployees']/edm:End[@EntitySet='Employees' and @Role='r_Employees']",
payload);
assertXpathExists(
@@ -307,23 +307,23 @@
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
"@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='TeamEmployees' and " +
- "@Association='RefScenario.TeamEmployees']/edm:End[@EntitySet='Employees' and @Role='r_Employee']",
+ "@Association='RefScenario.TeamEmployees']/edm:End[@EntitySet='Employees' and @Role='r_Employees']",
payload);
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
- "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employee-r_Room' and " +
- "@Association='RefScenario.r_Employee-r_Room']",
+ "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employees-r_Room' and " +
+ "@Association='RefScenario.r_Employees-r_Room']",
payload);
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
- "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employee-r_Room' and " +
- "@Association='RefScenario.r_Employee-r_Room']/edm:End[@EntitySet='Rooms' and @Role='r_Room']",
+ "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employees-r_Room' and " +
+ "@Association='RefScenario.r_Employees-r_Room']/edm:End[@EntitySet='Rooms' and @Role='r_Room']",
payload);
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
- "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employee-r_Room' and " +
- "@Association='RefScenario.r_Employee-r_Room']/edm:End[@EntitySet='Employees' and @Role='r_Employee']",
+ "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employees-r_Room' and " +
+ "@Association='RefScenario.r_Employees-r_Room']/edm:End[@EntitySet='Employees' and @Role='r_Employees']",
payload);
assertXpathExists(
@@ -339,7 +339,7 @@
assertXpathExists(
"/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
"@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='BuildingRooms' and " +
- "@Association='RefScenario.BuildingRooms']/edm:End[@EntitySet='Rooms' and @Role='r_Room']",
+ "@Association='RefScenario.BuildingRooms']/edm:End[@EntitySet='Rooms' and @Role='r_Rooms']",
payload);
}
}