Skip to content

Commit

Permalink
Merge pull request #27 from ga4gh/release/0.5.6
Browse files Browse the repository at this point in the history
Release 0.5.6 into main
  • Loading branch information
Jeremy Adams authored Dec 14, 2021
2 parents 4c6d6b1 + 9408b2d commit 3d41729
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 49 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ plugins {
}

group 'org.ga4gh'
version '0.5.5'
version '0.5.6'

repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ public DemoYamlConfigContainer mergedDemoConfigContainer(
@Qualifier("defaultDemoConfigContainer") DemoYamlConfigContainer defaultContainer,
@Qualifier("userDemoConfigContainer") DemoYamlConfigContainer userContainer
) {
DeepObjectMerger.merge(userContainer, defaultContainer);
DeepObjectMerger merger = new DeepObjectMerger();
merger.merge(userContainer, defaultContainer);
return defaultContainer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public static void main(String[] args) {

private static boolean setup(String[] args) {
Options options = new DemoConfiguration().getCommandLineOptions();
return ServerPropertySetter.setServerProperties(DemoYamlConfigContainer.class, args, options, "config");
ServerPropertySetter setter = new ServerPropertySetter();
return setter.setServerProperties(DemoYamlConfigContainer.class, args, options, "config");
}
}
108 changes: 75 additions & 33 deletions src/main/java/org/ga4gh/starterkit/common/util/DeepObjectMerger.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
package org.ga4gh.starterkit.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.util.*;
import org.ga4gh.starterkit.common.config.LogLevel;

public class DeepObjectMerger {

private static final Set<Class<?>> classesToSet = new HashSet<>(Arrays.asList(
List.class,
ArrayList.class,
String.class,
LocalDateTime.class,
private static final Set<Class<?>> DEFAULT_ATOMIC_CLASSES = new HashSet<>(Arrays.asList(
boolean.class,
byte.class,
short.class,
Expand All @@ -20,10 +17,28 @@ public class DeepObjectMerger {
long.class,
float.class,
double.class,
List.class,
ArrayList.class,
String.class,
LocalDateTime.class,
LogLevel.class
));

public static void merge(Object source, Object target) {
private Set<Class<?>> atomicClasses;

public DeepObjectMerger() {
atomicClasses = new HashSet<>();
atomicClasses.addAll(DEFAULT_ATOMIC_CLASSES);
}

public DeepObjectMerger(boolean useDefaults) {
atomicClasses = new HashSet<>();
if (useDefaults) {
atomicClasses.addAll(DEFAULT_ATOMIC_CLASSES);
}
}

public void merge(Object source, Object target) {
// Through reflection, get all fields associated with object to be merged
Class<?> objectClass = source.getClass();
List<Field> fields = new ArrayList<>(Arrays.asList(objectClass.getDeclaredFields()));
Expand All @@ -38,36 +53,63 @@ public static void merge(Object source, Object target) {
}

for (Field field : fields) {
// Get field and set as accessible so it can be written
Class<?> fieldClass = field.getType();
field.setAccessible(true);

try {
Object sourceProperty = field.get(source);
Object targetProperty = field.get(target);

// If the field is a 'primitive' type, then the target field
// can be set with the source field value (provided source
// field value is not null)
if (classesToSet.contains(fieldClass)) {
if (sourceProperty != null) {
field.set(target, sourceProperty);
}
} else {
// If the field is a complex class (i.e. non-primitive),
// and both source and target are non-null, then
// this method is recursively called on the field value
if (sourceProperty != null && targetProperty != null) {
merge(sourceProperty, targetProperty);
}
// Null target field can be set with source field value
else if (sourceProperty != null) {
field.set(target, sourceProperty);
if (!Modifier.isStatic(field.getModifiers())) {

// Get field and set as accessible so it can be written
Class<?> fieldClass = field.getType();
field.setAccessible(true);

try {
Object sourceProperty = field.get(source);
Object targetProperty = field.get(target);

// If the field is a 'primitive' type, then the target field
// can be set with the source field value (provided source
// field value is not null)
if (getAtomicClasses().contains(fieldClass)) {
if (sourceProperty != null) {
field.set(target, sourceProperty);
}
} else {
// If the field is a complex class (i.e. non-primitive),
// and both source and target are non-null, then
// this method is recursively called on the field value
if (sourceProperty != null && targetProperty != null) {
merge(sourceProperty, targetProperty);
}
// Null target field can be set with source field value
else if (sourceProperty != null) {
field.set(target, sourceProperty);
}
}
} catch (IllegalAccessException e) {
System.out.println("illegal access exception");
}
} catch (IllegalAccessException e) {
System.out.println("illegal access exception");
}
}
}

public void setAtomicClasses(Set<Class<?>> atomicClasses) {
this.atomicClasses = atomicClasses;
}

public Set<Class<?>> getAtomicClasses() {
return atomicClasses;
}

public void addAtomicClass(Class<?> atomicClass) {
atomicClasses.add(atomicClass);
}

public void addAtomicClasses(Set<Class<?>> atomicClasses) {
this.atomicClasses.addAll(atomicClasses);
}

public boolean removeAtomicClass(Class<?> atomicClass) {
return this.atomicClasses.remove(atomicClass);
}

public boolean removeAtomicClasses(Set<Class<?>> atomicClasses) {
return this.atomicClasses.removeAll(atomicClasses);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ga4gh.starterkit.common.util.webserver;

import java.util.Properties;
import java.util.Set;
import org.apache.commons.cli.Options;
import org.ga4gh.starterkit.common.config.ContainsServerProps;
import org.ga4gh.starterkit.common.config.ServerProps;
Expand All @@ -11,14 +12,34 @@

public class ServerPropertySetter {

public static <T extends ContainsServerProps> boolean setServerProperties(Class<T> configClass, String[] args, Options options, String optionName) {
private DeepObjectMerger merger;

public ServerPropertySetter() {
merger = new DeepObjectMerger();
}

public ServerPropertySetter(boolean useDefaults) {
merger = new DeepObjectMerger(useDefaults);
}

public ServerPropertySetter(Set<Class<?>> mergerAtomicClasses) {
merger = new DeepObjectMerger();
merger.addAtomicClasses(mergerAtomicClasses);
}

public ServerPropertySetter(boolean useDefaults, Set<Class<?>> mergerAtomicClasses) {
merger = new DeepObjectMerger(useDefaults);
merger.addAtomicClasses(mergerAtomicClasses);
}

public <T extends ContainsServerProps> boolean setServerProperties(Class<T> configClass, String[] args, Options options, String optionName) {
try {
// obtain the final merged configuration object
ApplicationArguments applicationArgs = new DefaultApplicationArguments(args);
T defaultConfig = configClass.getConstructor().newInstance();
T userConfig = CliYamlConfigLoader.load(configClass, applicationArgs, options, optionName);
if (userConfig != null) {
DeepObjectMerger.merge(userConfig, defaultConfig);
merger.merge(userConfig, defaultConfig);
}
T mergedConfig = defaultConfig;
ServerProps serverProps = mergedConfig.getServerProps();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import org.testng.annotations.Test;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

public class DeepObjectMergerTest {
Expand Down Expand Up @@ -59,7 +61,8 @@ public void testNullPrimitiveSourceIgnored() {
Simple target = new Simple("s", dt, false);
Simple source = new Simple(null, null, true);

DeepObjectMerger.merge(source, target);
DeepObjectMerger merger = new DeepObjectMerger();
merger.merge(source, target);

Assert.assertEquals(target.getS(), "s");
Assert.assertEquals(target.getDt(), dt);
Expand All @@ -73,7 +76,8 @@ public void testNonNullPrimitiveSourceCopied() {
Simple target = new Simple("s", null, false);
Simple source = new Simple("t", dt, true);

DeepObjectMerger.merge(source, target);
DeepObjectMerger merger = new DeepObjectMerger();
merger.merge(source, target);

Assert.assertEquals(target.getS(), "t");
Assert.assertEquals(target.getDt(), dt);
Expand All @@ -89,7 +93,8 @@ public void testNullComplexSourceIgnored() {
Complex complexTarget = new Complex(simpleTarget);
Complex complexSource = new Complex(null);

DeepObjectMerger.merge(complexSource, complexTarget);
DeepObjectMerger merger = new DeepObjectMerger();
merger.merge(complexSource, complexTarget);

Simple mergedTarget = complexTarget.getS();

Expand All @@ -108,7 +113,8 @@ public void testNonNullComplexSourceMerged() {
Complex complexTarget = new Complex(simpleTarget);
Complex complexSource = new Complex(simpleSource);

DeepObjectMerger.merge(complexSource, complexTarget);
DeepObjectMerger merger = new DeepObjectMerger();
merger.merge(complexSource, complexTarget);

Simple mergedTarget = complexTarget.getS();

Expand All @@ -134,7 +140,8 @@ public void testCollectionsCopied() {
HasList target = new HasList(Arrays.asList("a", "b"));
HasList source = new HasList(Arrays.asList("c", "d"));

DeepObjectMerger.merge(source, target);
DeepObjectMerger merger = new DeepObjectMerger();
merger.merge(source, target);

Assert.assertEquals(target.getStrings().get(0), "c");
Assert.assertEquals(target.getStrings().get(1), "d");
Expand All @@ -148,10 +155,65 @@ public void testParentClassPropsCopied() {
SimpleChild target = new SimpleChild("s", null, false);
SimpleChild source = new SimpleChild("t", dt, true);

DeepObjectMerger.merge(source, target);
DeepObjectMerger merger = new DeepObjectMerger();
merger.merge(source, target);

Assert.assertEquals(target.getS(), "t");
Assert.assertEquals(target.getDt(), dt);
Assert.assertTrue(target.isB());
}

private List<Complex> atomicClassConstruct() {
LocalDateTime dt = LocalDateTime.now();
SimpleChild source = new SimpleChild(null, null, true);
SimpleChild target = new SimpleChild("s", dt, false);
Complex sourceComplex = new Complex(source);
Complex targetComplex = new Complex(target);
List<Complex> complexes = new ArrayList<>() {{
add(sourceComplex);
add(targetComplex);
}};
return complexes;
}

private void atomicClassAssertions(Complex targetComplex) {
Assert.assertEquals(targetComplex.getS().getS(), null);
Assert.assertEquals(targetComplex.getS().getDt(), null);
Assert.assertTrue(targetComplex.getS().isB());
}

@Test
public void testAddAtomicClass() {
DeepObjectMerger merger = new DeepObjectMerger(true);
merger.addAtomicClass(Simple.class);
merger.addAtomicClass(SimpleChild.class);

List<Complex> complexes = atomicClassConstruct();
merger.merge(complexes.get(0), complexes.get(1));
atomicClassAssertions(complexes.get(1));

merger.removeAtomicClass(Simple.class);
merger.removeAtomicClass(SimpleChild.class);
merger.addAtomicClasses(new HashSet<>() {{
add(Simple.class);
add(SimpleChild.class);
}});

complexes = atomicClassConstruct();
merger.merge(complexes.get(0), complexes.get(1));
atomicClassAssertions(complexes.get(1));

merger.removeAtomicClasses(new HashSet<>() {{
add(Simple.class);
add(SimpleChild.class);
}});
merger.setAtomicClasses(new HashSet<>() {{
add(Simple.class);
add(SimpleChild.class);
}});

complexes = atomicClassConstruct();
merger.merge(complexes.get(0), complexes.get(1));
atomicClassAssertions(complexes.get(1));
}
}
Loading

0 comments on commit 3d41729

Please sign in to comment.