/** * Copyright 2011-2015 John Ericksen * * 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.androidtransfuse.processor; import org.androidtransfuse.bootstrap.Bootstrap; import org.androidtransfuse.bootstrap.Bootstraps; import org.androidtransfuse.model.Identified; import org.androidtransfuse.model.Mergeable; import org.junit.Before; import org.junit.Test; import javax.inject.Inject; import javax.inject.Provider; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; /** * @author John Ericksen */ @Bootstrap public class MergerTest { public static class MergeableRoot extends Mergeable implements Identified { String id; String dontMerge; String stringValue; int intValue; List<SubMergeable> subMergeables = new ArrayList<SubMergeable>(); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getDontMerge() { return dontMerge; } public void setDontMerge(String dontMerge) { this.dontMerge = dontMerge; } @Merge("v") public String getStringValue() { return stringValue; } public void setStringValue(String stringValue) { this.stringValue = stringValue; } @Merge("i") public int getIntValue() { return intValue; } public void setIntValue(int intValue) { this.intValue = intValue; } @MergeCollection(collectionType = ArrayList.class, type = SubMergeable.class) public List<SubMergeable> getSubMergeables() { return subMergeables; } public void setSubMergeables(List<SubMergeable> subMergeables) { this.subMergeables = subMergeables; } @Override public String getIdentifier() { return id; } } public static class SubMergeable extends Mergeable implements Identified { String value; String dontMergeValue; String id; @Merge("v") public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getDontMergeValue() { return dontMergeValue; } public void setDontMergeValue(String dontMergeValue) { this.dontMergeValue = dontMergeValue; } @Merge("i") public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public String getIdentifier() { return id; } } public static class OneContainer extends Mergeable{ private List<One> ones = new ArrayList<One>(); @MergeCollection(collectionType = ArrayList.class, type = One.class) public List<One> getOnes() { return ones; } public void setOnes(List<One> ones) { this.ones = ones; } } public static class One extends Mergeable implements Identified{ private String name; private List<Two> two = new ArrayList<Two>(); @Merge("n") public String getName() { return name; } public void setName(String name) { this.name = name; } @MergeCollection(collectionType = ArrayList.class, type = Two.class) public List<Two> getTwo() { return two; } public void setTwo(List<Two> two) { this.two = two; } @Override public String getIdentifier() { return name; } } //non identified, should merge on order public static class Two extends Mergeable{ private List<Three> three = new ArrayList<Three>(); @MergeCollection(collectionType = ArrayList.class, type = Three.class) public List<Three> getThree() { return three; } public void setThree(List<Three> three) { this.three = three; } } public static class Three extends Mergeable implements Identified{ private String name; private String value; @Merge("n") public String getName() { return name; } public void setName(String name) { this.name = name; } @Merge("v") public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public String getIdentifier() { return name; } } @Inject private Provider<SubMergeable> subMergeableProvider; @Inject private Provider<MergeableRoot> mergeableRootProvider; @Inject private Merger merger; @Before public void setup() { Bootstraps.inject(this); } @Test public void testMerge() throws MergerException { List<SubMergeable> subMergeablesOne = new ArrayList<SubMergeable>(); List<SubMergeable> subMergeablesTwo = new ArrayList<SubMergeable>(); subMergeablesOne.add(buildSubMergeable("1", "five", "six", true)); subMergeablesTwo.add(buildSubMergeable("1", "seven", "eight", true)); MergeableRoot one = buildMergeableRoot("2", "one", "two", 5, subMergeablesOne, true); MergeableRoot two = buildMergeableRoot("2", "three", "four", 6, subMergeablesTwo, true); MergeableRoot merged = merger.merge(MergeableRoot.class, one, two); assertEquals("one", merged.getDontMerge()); assertEquals("four", merged.getStringValue()); assertEquals(6, merged.getIntValue()); assertEquals(1, merged.getSubMergeables().size()); SubMergeable subMergeable = merged.getSubMergeables().iterator().next(); assertEquals("seven", subMergeable.getValue()); assertEquals("six", subMergeable.getDontMergeValue()); } @Test public void testNonMatchingMerge() throws MergerException { List<SubMergeable> subMergeablesOne = new ArrayList<SubMergeable>(); List<SubMergeable> subMergeablesTwo = new ArrayList<SubMergeable>(); subMergeablesOne.add(buildSubMergeable("1", "five", "six", true)); subMergeablesTwo.add(buildSubMergeable("2", "seven", "eight", true)); MergeableRoot one = buildMergeableRoot("3", "one", "two", 5, subMergeablesOne, true); MergeableRoot two = buildMergeableRoot("4", "three", "four", 6, subMergeablesTwo, true); MergeableRoot merged = merger.merge(MergeableRoot.class, one, two); assertEquals("one", merged.getDontMerge()); assertEquals("four", merged.getStringValue()); assertEquals(6, merged.getIntValue()); assertEquals(1, merged.getSubMergeables().size()); SubMergeable subMergeableTwo = buildSubMergeableMap(merged.getSubMergeables()).get("2"); assertEquals("seven", subMergeableTwo.getValue()); assertEquals("eight", subMergeableTwo.getDontMergeValue()); } @Test public void testNullMerge() throws MergerException { List<SubMergeable> subMergeablesOne = new ArrayList<SubMergeable>(); List<SubMergeable> subMergeablesTwo = new ArrayList<SubMergeable>(); subMergeablesOne.add(buildSubMergeable("1", "seven", "eight", true)); subMergeablesTwo.add(buildSubMergeable("1", null, null, true)); MergeableRoot one = buildMergeableRoot("3", "one", "two", 5, subMergeablesOne, true); MergeableRoot two = buildMergeableRoot("3", null, null, 6, subMergeablesTwo, true); MergeableRoot merged = merger.merge(MergeableRoot.class, one, two); assertEquals("one", merged.getDontMerge()); assertNull(merged.getStringValue()); assertEquals(6, merged.getIntValue()); assertEquals(1, merged.getSubMergeables().size()); SubMergeable subMergeable = merged.getSubMergeables().iterator().next(); assertNull(subMergeable.getValue()); assertEquals(2, subMergeable.getMergeTagSize()); assertEquals("eight", subMergeable.getDontMergeValue()); } @Test public void testNullCollection() throws MergerException { List<SubMergeable> subMergeablesTwo = new ArrayList<SubMergeable>(); subMergeablesTwo.add(buildSubMergeable("1", "seven", "eight", true)); MergeableRoot one = buildMergeableRoot("3", "one", "two", 5, null, true); MergeableRoot two = buildMergeableRoot("3", null, null, 6, subMergeablesTwo, true); MergeableRoot merged = merger.merge(MergeableRoot.class, one, two); assertEquals("one", merged.getDontMerge()); assertNull(merged.getStringValue()); assertEquals(6, merged.getIntValue()); assertEquals(1, merged.getSubMergeables().size()); SubMergeable subMergeable = merged.getSubMergeables().iterator().next(); assertEquals("seven", subMergeable.getValue()); assertEquals("eight", subMergeable.getDontMergeValue()); } @Test public void testPreviouslyUntagWritten() throws MergerException { List<SubMergeable> subMergeablesOne = new ArrayList<SubMergeable>(); List<SubMergeable> subMergeablesTwo = new ArrayList<SubMergeable>(); subMergeablesOne.add(buildSubMergeable("1", "five", "six", false)); subMergeablesOne.add(buildSubMergeable("2", "nine", "ten", false)); subMergeablesTwo.add(buildSubMergeable("1", "seven", "eight", true)); MergeableRoot one = buildMergeableRoot("2", "one", "two", 5, subMergeablesOne, false); MergeableRoot two = buildMergeableRoot("2", "three", "four", 6, subMergeablesTwo, true); MergeableRoot merged = merger.merge(MergeableRoot.class, one, two); assertEquals("one", merged.getDontMerge()); assertEquals("two", merged.getStringValue()); assertEquals(5, merged.getIntValue()); assertEquals(2, merged.getSubMergeables().size()); SubMergeable subMergeable = buildSubMergeableMap(merged.getSubMergeables()).get("1"); assertEquals("five", subMergeable.getValue()); assertEquals("six", subMergeable.getDontMergeValue()); } @Test public void testEmptyCollection() throws MergerException { List<SubMergeable> subMergeablesOne = new ArrayList<SubMergeable>(); List<SubMergeable> subMergeablesTwo = new ArrayList<SubMergeable>(); subMergeablesOne.add(buildSubMergeable("1", "five", "six", true)); MergeableRoot one = buildMergeableRoot("2", "one", "two", 5, subMergeablesOne, true); MergeableRoot two = buildMergeableRoot("2", "three", "four", 6, subMergeablesTwo, true); MergeableRoot merged = merger.merge(MergeableRoot.class, one, two); assertEquals("one", merged.getDontMerge()); assertEquals("four", merged.getStringValue()); assertEquals(6, merged.getIntValue()); assertEquals(0, merged.getSubMergeables().size()); } @Test public void testUnidentified() throws MergerException { One before = buildOne("1", buildTwo(buildThree("3", "before"))); One after = buildOne("1", buildTwo(buildThree("3", "after"))); One merged = merger.merge(One.class, before, after); assertEquals(1, merged.getTwo().size()); Two mergedTwo = merged.getTwo().get(0); assertEquals(1, mergedTwo.getThree().size()); Three mergedThree = mergedTwo.getThree().get(0); assertEquals("after", mergedThree.getValue()); } @Test public void testUnidentifiedList() throws MergerException { OneContainer before = new OneContainer(); OneContainer after = new OneContainer(); before.getOnes().add(buildOne("1", buildTwo(buildThree("3", "before")))); before.getOnes().add(buildOne("2")); after.getOnes().add(buildOne("1")); after.getOnes().add(buildOne("2", buildTwo(buildThree("3", "after")))); OneContainer merged = merger.merge(OneContainer.class, before, after); assertEquals(2, merged.getOnes().size()); One first = merged.getOnes().get(0); One second = merged.getOnes().get(1); //assertEquals(0, first.getTwo().size()); assertEquals(1, second.getTwo().size()); Two mergedTwo = second.getTwo().get(0); assertEquals(1, mergedTwo.getThree().size()); Three mergedThree = mergedTwo.getThree().get(0); assertEquals("after", mergedThree.getValue()); } private One buildOne(String name, Two... twos) throws MergerException { One one = new One(); one.setName(name); if(twos != null){ for (Two two : twos) { one.getTwo().add(two); } } return updateMergeTags(one); } private Two buildTwo(Three... threes) throws MergerException { Two two = new Two(); if(threes != null){ for (Three three : threes) { two.getThree().add(three); } } return updateMergeTags(two); } private Three buildThree(String name, String value) throws MergerException { Three three = new Three(); three.setName(name); three.setValue(value); return updateMergeTags(three); } private Map<String, SubMergeable> buildSubMergeableMap(List<SubMergeable> subMergeables) { Map<String, SubMergeable> subMergeableMap = new HashMap<String, SubMergeable>(); for (SubMergeable subMergeable : subMergeables) { subMergeableMap.put(subMergeable.getIdentifier(), subMergeable); } return subMergeableMap; } private MergeableRoot buildMergeableRoot(String id, String dontMerge, String stringValue, int intValue, List<SubMergeable> subMergeables, boolean generateTag) throws MergerException { MergeableRoot mergeableRoot = mergeableRootProvider.get(); mergeableRoot.setId(id); mergeableRoot.setDontMerge(dontMerge); mergeableRoot.setStringValue(stringValue); mergeableRoot.setIntValue(intValue); mergeableRoot.setSubMergeables(subMergeables); if (generateTag) { updateMergeTags(mergeableRoot); } return mergeableRoot; } private SubMergeable buildSubMergeable(String id, String value, String dontMergeValue, boolean generateTag) throws MergerException { SubMergeable subMergeable = subMergeableProvider.get(); subMergeable.setId(id); subMergeable.setValue(value); subMergeable.setDontMergeValue(dontMergeValue); if (generateTag) { updateMergeTags(subMergeable); } return subMergeable; } private <T extends Mergeable> T updateMergeTags(T mergeable) throws MergerException { try { mergeable.setGenerated(true); BeanInfo beanInfo = Introspector.getBeanInfo(mergeable.getClass()); for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) { Method readMethod = propertyDescriptor.getReadMethod(); Method writeMethod = propertyDescriptor.getWriteMethod(); Merge mergeAnnotation = findAnnotation(Merge.class, writeMethod, readMethod); if (mergeAnnotation != null) { mergeable.addMergeTag(mergeAnnotation.value()); } } } catch (IntrospectionException e) { throw new MergerException(e); } return mergeable; } private <T extends Annotation> T findAnnotation(Class<T> annotationClass, Method... methods) { T annotation = null; if (methods != null) { for (Method method : methods) { if (annotation == null && method != null && method.isAnnotationPresent(annotationClass)) { annotation = method.getAnnotation(annotationClass); } } } return annotation; } }