Skip to content

Commit

Permalink
GH-643 - Make sure all kinds of enums are correctly identified.
Browse files Browse the repository at this point in the history
This fixes #643.
  • Loading branch information
michael-simons authored Jul 3, 2019
1 parent bfcadad commit bab8271
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 15 deletions.
26 changes: 26 additions & 0 deletions api/src/main/java/org/neo4j/ogm/support/ClassUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,32 @@ public static ClassLoader getDefaultClassLoader() {
return cl;
}

/**
* See https://github.com/neo4j/neo4j-ogm/issues/643. An enum instance that overrides methods of the enum itself
* is realized as an anonymous inner class for which {@link Class#isEnum()} returns false.
*
* @param clazz The class to check whether it is an enum or not.
* @return True, if {@code clazz} is an enum.
*/
public static boolean isEnum(Class<?> clazz) {

return clazz.isEnum() || Enum.class.isAssignableFrom(clazz);
}

/**
* @param object
* @return True, if the object is an enum instance.
* @see #isEnum(Class)
*/
public static boolean isEnum(Object object) {

if (object == null) {
return false;
}

return isEnum(object.getClass());
}

private ClassUtils() {
}
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/neo4j/ogm/metadata/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public ClassInfo(Class<?> cls) {
final int modifiers = cls.getModifiers();
this.isInterface = Modifier.isInterface(modifiers);
this.isAbstract = Modifier.isAbstract(modifiers);
this.isEnum = cls.isEnum();
this.isEnum = org.neo4j.ogm.support.ClassUtils.isEnum(cls);
this.className = cls.getName();

if (cls.getSuperclass() != null) {
Expand Down
13 changes: 6 additions & 7 deletions core/src/main/java/org/neo4j/ogm/metadata/DomainInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.neo4j.ogm.metadata;

import static java.util.Comparator.*;
import static org.neo4j.ogm.support.ClassUtils.*;

import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult;
Expand Down Expand Up @@ -379,13 +380,11 @@ private void registerDefaultFieldConverters(ClassInfo classInfo, FieldInfo field
}
}

if (!enumConverterSet) {
if (fieldType.isEnum()) {
LOGGER.debug(
"Setting default enum converter for unscanned class " + classInfo.name() + ", field: "
+ fieldInfo.getName());
setEnumFieldConverter(fieldInfo, fieldType);
}
if (!enumConverterSet && isEnum(fieldType)) {
LOGGER.debug(
"Setting default enum converter for unscanned class " + classInfo.name() + ", field: "
+ fieldInfo.getName());
setEnumFieldConverter(fieldInfo, fieldType);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
package org.neo4j.ogm.metadata.reflect;

import static org.neo4j.ogm.support.ClassUtils.*;

import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
Expand Down Expand Up @@ -204,7 +206,7 @@ private static Object stringToCharacterIterable(Object value, Class parameterTyp
return characters;
}

if (value.getClass().isArray() && (elementType == String.class || elementType.isEnum())) {
if (value.getClass().isArray() && (elementType == String.class || isEnum(elementType))) {
String[] strings = (String[]) value;
return Arrays.asList(strings);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import static java.util.Collections.*;
import static java.util.stream.Collectors.*;
import static org.neo4j.ogm.support.ClassUtils.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
Expand Down Expand Up @@ -192,7 +193,7 @@ private String keyInstanceToString(Object propertyKey) {

if (propertyKey instanceof String) {
return (String) propertyKey;
} else if (propertyKey.getClass().isEnum()) {
} else if (isEnum(propertyKey)) {
return ((Enum) propertyKey).name();
}

Expand All @@ -206,7 +207,7 @@ private Object keyInstanceFromString(String propertyKey, Class<?> keyType) {
return propertyKey;
} else if (keyType.equals(String.class)) {
return propertyKey;
} else if (keyType.isEnum()) {
} else if (isEnum(keyType)) {
try {
return keyType.getDeclaredMethod("valueOf", String.class).invoke(keyType, propertyKey);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

/**
* @author Frantisek Hartman
* @author Michael J. Simons
*/
@NodeEntity(label = "User")
public class UserWithEnumMap {
Expand Down Expand Up @@ -70,7 +71,15 @@ public void putMyProperty(UserProperties key, Object value) {
public enum UserProperties {

CITY,
ZIP_CODE,
ZIP_CODE {
@Override
void doSomething() {

}
};

void doSomething() {

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@

/**
* @author Frantisek Hartman
* @author Michael J. Simons
*/
public class EnumMapPropertiesTest extends MultiDriverTestClass {

private static Session session;

@BeforeClass
public static void init() throws IOException {
public static void init() {
session = new SessionFactory(driver,
UserWithEnumMap.class.getName())
.openSession();
}

@Before
public void setUp() throws Exception {
public void setUp() {
session.purgeDatabase();
}

Expand All @@ -74,7 +75,7 @@ public void shouldMapMapAttributeToProperties() throws Exception {
}

@Test
public void shouldMapNodePropertiesToPropertiesAttribute() throws Exception {
public void shouldMapNodePropertiesToPropertiesAttribute() {
session.query(
"CREATE (u:User {`name`:'Frantisek', `myProperties.CITY`:'London', `myProperties.ZIP_CODE`:'SW1A 1AA'})",
emptyMap());
Expand Down
72 changes: 72 additions & 0 deletions test/src/test/java/org/neo4j/ogm/support/ClassUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.neo4j.ogm.support;

import static org.assertj.core.api.Assertions.*;

import org.junit.Test;

/**
* @author Michael J. Simons
*/
public class ClassUtilsTest {

enum YourFriendlyEnumMostPeopleUse {
THING1, THING2
}

enum YesEnumsCanBeMuchMore {
THING1, THING2 {
@Override
public void something() {
}
};

@SuppressWarnings({ "unused" })
void something() {
}
}

interface SomeInterface {
}

enum Singleton implements SomeInterface {
THE_INSTANCE
}

@Test
public void shouldWorkWithPlainEnum() {
assertThat(ClassUtils.isEnum(YourFriendlyEnumMostPeopleUse.class)).isTrue();
assertThat(ClassUtils.isEnum(YourFriendlyEnumMostPeopleUse.THING1)).isTrue();
assertThat(ClassUtils.isEnum(YourFriendlyEnumMostPeopleUse.THING2)).isTrue();
}

@Test
public void shouldWorkWithExtendedEnum() {
assertThat(ClassUtils.isEnum(YesEnumsCanBeMuchMore.class)).isTrue();
assertThat(ClassUtils.isEnum(YesEnumsCanBeMuchMore.THING1)).isTrue();
assertThat(ClassUtils.isEnum(YesEnumsCanBeMuchMore.THING2)).isTrue();
}

@Test
public void shouldWorkWithInterfaceEnum() {
assertThat(ClassUtils.isEnum(Singleton.class)).isTrue();
assertThat(ClassUtils.isEnum(Singleton.THE_INSTANCE)).isTrue();
}
}

0 comments on commit bab8271

Please sign in to comment.