Skip to content

Commit

Permalink
Allow fields to be excluded from hashCode() and equals() generation (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
SpaceBison authored and joelittlejohn committed Sep 12, 2017
1 parent 5eeb01e commit 8b8806d
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,7 @@

package org.jsonschema2pojo.rules;

import static org.apache.commons.lang3.StringUtils.*;
import static org.jsonschema2pojo.rules.PrimitiveTypes.*;
import static org.jsonschema2pojo.util.TypeUtil.*;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jsonschema2pojo.AnnotationStyle;
import org.jsonschema2pojo.Schema;
import org.jsonschema2pojo.exception.ClassAlreadyExistsException;
import org.jsonschema2pojo.util.NameHelper;
import org.jsonschema2pojo.util.ParcelableHelper;
import org.jsonschema2pojo.util.SerializableHelper;
import android.os.Parcelable;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -55,7 +35,30 @@
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;

import android.os.Parcelable;
import org.jsonschema2pojo.AnnotationStyle;
import org.jsonschema2pojo.Schema;
import org.jsonschema2pojo.exception.ClassAlreadyExistsException;
import org.jsonschema2pojo.util.NameHelper;
import org.jsonschema2pojo.util.ParcelableHelper;
import org.jsonschema2pojo.util.SerializableHelper;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.apache.commons.lang3.StringUtils.substringAfter;
import static org.apache.commons.lang3.StringUtils.substringBefore;
import static org.jsonschema2pojo.rules.PrimitiveTypes.isPrimitive;
import static org.jsonschema2pojo.rules.PrimitiveTypes.primitiveType;
import static org.jsonschema2pojo.util.TypeUtil.resolveType;

/**
* Applies the generation steps required for schemas of type "object".
Expand Down Expand Up @@ -132,8 +135,8 @@ public JType apply(String nodeName, JsonNode node, JPackage _package, Schema sch
}

if (ruleFactory.getGenerationConfig().isIncludeHashcodeAndEquals()) {
addHashCode(jclass);
addEquals(jclass);
addHashCode(jclass, node);
addEquals(jclass, node);
}

if (ruleFactory.getGenerationConfig().isParcelable()) {
Expand Down Expand Up @@ -403,8 +406,8 @@ private void addToString(JDefinedClass jclass) {
toString.annotate(Override.class);
}

private void addHashCode(JDefinedClass jclass) {
Map<String, JFieldVar> fields = jclass.fields();
private void addHashCode(JDefinedClass jclass, JsonNode node) {
Map<String, JFieldVar> fields = removeFieldsExcludedFromEqualsAndHashCode(jclass.fields(), node);

JMethod hashCode = jclass.method(JMod.PUBLIC, int.class, "hashCode");

Expand All @@ -430,6 +433,37 @@ private void addHashCode(JDefinedClass jclass) {
hashCode.annotate(Override.class);
}

private Map<String, JFieldVar> removeFieldsExcludedFromEqualsAndHashCode(Map<String, JFieldVar> fields, JsonNode node) {
Map<String, JFieldVar> filteredFields = new HashMap<String, JFieldVar>(fields);

JsonNode properties = node.get("properties");

if (properties != null) {
if (node.has("excludedFromEqualsAndHashCode")) {
JsonNode excludedArray = node.get("excludedFromEqualsAndHashCode");

for (Iterator<JsonNode> iterator = excludedArray.elements(); iterator.hasNext(); ) {
String excludedPropertyName = iterator.next().asText();
JsonNode excludedPropertyNode = properties.get(excludedPropertyName);
filteredFields.remove(ruleFactory.getNameHelper().getPropertyName(excludedPropertyName, excludedPropertyNode));
}
}

for (Iterator<Map.Entry<String, JsonNode>> iterator = properties.fields(); iterator.hasNext(); ) {
Map.Entry<String, JsonNode> entry = iterator.next();
String propertyName = entry.getKey();
JsonNode propertyNode = entry.getValue();

if (propertyNode.has("excludedFromEqualsAndHashCode") &&
propertyNode.get("excludedFromEqualsAndHashCode").asBoolean()) {
filteredFields.remove(ruleFactory.getNameHelper().getPropertyName(propertyName, propertyNode));
}
}
}

return filteredFields;
}

private void addConstructors(JDefinedClass jclass, JsonNode node, Schema schema, boolean onlyRequired) {

LinkedHashSet<String> classProperties = getConstructorProperties(node, onlyRequired);
Expand Down Expand Up @@ -522,8 +556,8 @@ private JFieldVar searchClassAndSuperClassesForField(String property, JDefinedCl
return field;
}

private void addEquals(JDefinedClass jclass) {
Map<String, JFieldVar> fields = jclass.fields();
private void addEquals(JDefinedClass jclass, JsonNode node) {
Map<String, JFieldVar> fields = removeFieldsExcludedFromEqualsAndHashCode(jclass.fields(), node);

JMethod equals = jclass.method(JMod.PUBLIC, boolean.class, "equals");
JVar otherObject = equals.param(Object.class, "other");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/**
* Copyright © 2010-2014 Nokia
*
* 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.jsonschema2pojo.integration;

import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat;

public class ExcludedFromEqualsAndHashCodeIT {
@ClassRule public static Jsonschema2PojoRule classSchemaRule = new Jsonschema2PojoRule();

private static Class<?> clazz;

@BeforeClass
public static void generateAndCompileEnum() throws ClassNotFoundException, IllegalAccessException, InstantiationException {

ClassLoader resultsClassLoader = classSchemaRule.generateAndCompile("/schema/excludedFromEqualsAndHashCode/excludedFromEqualsAndHashCode.json", "com.example");

clazz = resultsClassLoader.loadClass("com.example.ExcludedFromEqualsAndHashCode");
}

@Test
public void hashCodeTest() throws IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {

Object instance = clazz.newInstance();

setProperty(instance, "excludedByProperty", "one");
setProperty(instance, "excludedByArray", "two");
setProperty(instance, "notExcluded", "three");
setProperty(instance, "notExcludedByProperty", "four");

int hashCodeBefore;
int hashCodeAfter;

hashCodeBefore = instance.hashCode();
setProperty(instance, "excludedByProperty", "five");
hashCodeAfter = instance.hashCode();

assertThat(hashCodeBefore, is(equalTo(hashCodeAfter)));

hashCodeBefore = hashCodeAfter;
setProperty(instance, "excludedByArray", "six");
hashCodeAfter = instance.hashCode();

assertThat(hashCodeBefore, is(equalTo(hashCodeAfter)));

hashCodeBefore = hashCodeAfter;
setProperty(instance, "notExcluded", "seven");
hashCodeAfter = instance.hashCode();

assertThat(hashCodeBefore, is(not(equalTo(hashCodeAfter))));

hashCodeBefore = hashCodeAfter;
setProperty(instance, "notExcludedByProperty", "eight");
hashCodeAfter = instance.hashCode();

assertThat(hashCodeBefore, is(not(equalTo(hashCodeAfter))));
}

@Test
public void equalsSelf() throws IllegalAccessException, InstantiationException {
Object instance = clazz.newInstance();

assertThat(instance.equals(instance), is(true));
}

@Test
public void exludedByPropertyTest() throws IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {
Object instanceOne = clazz.newInstance();
Object instanceTwo = clazz.newInstance();

setProperty(instanceOne, "excludedByProperty", "one");
setProperty(instanceOne, "excludedByArray", "two");
setProperty(instanceOne, "notExcluded", "three");
setProperty(instanceOne, "notExcludedByProperty", "four");

setProperty(instanceTwo, "excludedByProperty", "differentValue");
setProperty(instanceTwo, "excludedByArray", "two");
setProperty(instanceTwo, "notExcluded", "three");
setProperty(instanceTwo, "notExcludedByProperty", "four");

assertThat(instanceOne.equals(instanceTwo), is(true));
}

@Test
public void exludedByArrayTest() throws IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {
Object instanceOne = clazz.newInstance();
Object instanceTwo = clazz.newInstance();

setProperty(instanceOne, "excludedByProperty", "one");
setProperty(instanceOne, "excludedByArray", "two");
setProperty(instanceOne, "notExcluded", "three");
setProperty(instanceOne, "notExcludedByProperty", "four");

setProperty(instanceTwo, "excludedByProperty", "one");
setProperty(instanceTwo, "excludedByArray", "differentValue");
setProperty(instanceTwo, "notExcluded", "three");
setProperty(instanceTwo, "notExcludedByProperty", "four");

assertThat(instanceOne.equals(instanceTwo), is(true));
}

@Test
public void notExcludedTest() throws IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {
Object instanceOne = clazz.newInstance();
Object instanceTwo = clazz.newInstance();

setProperty(instanceOne, "excludedByProperty", "one");
setProperty(instanceOne, "excludedByArray", "two");
setProperty(instanceOne, "notExcluded", "three");
setProperty(instanceOne, "notExcludedByProperty", "four");

setProperty(instanceTwo, "excludedByProperty", "one");
setProperty(instanceTwo, "excludedByArray", "two");
setProperty(instanceTwo, "notExcluded", "differentValue");
setProperty(instanceTwo, "notExcludedByProperty", "four");

assertThat(instanceOne.equals(instanceTwo), is(false));
}

@Test
public void notExludedByPropertyTest() throws IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {
Object instanceOne = clazz.newInstance();
Object instanceTwo = clazz.newInstance();

setProperty(instanceOne, "excludedByProperty", "one");
setProperty(instanceOne, "excludedByArray", "two");
setProperty(instanceOne, "notExcluded", "three");
setProperty(instanceOne, "notExcludedByProperty", "four");

setProperty(instanceTwo, "excludedByProperty", "one");
setProperty(instanceTwo, "excludedByArray", "two");
setProperty(instanceTwo, "notExcluded", "three");
setProperty(instanceTwo, "notExcludedByProperty", "differentValue");

assertThat(instanceOne.equals(instanceTwo), is(false));
}

private static void setProperty(Object instance, String property, String value) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
new PropertyDescriptor(property, clazz).getWriteMethod().invoke(instance, value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"type": "object",
"excludedFromEqualsAndHashCode" : [ "excludedByArray" ],
"properties": {
"notExcluded" : {
"type" : "string"
},
"excludedByProperty" : {
"type" : "string",
"excludedFromEqualsAndHashCode" : true
},
"notExcludedByProperty" : {
"type" : "string",
"excludedFromEqualsAndHashCode" : false
},
"excludedByArray" : {
"type" : "string"
}
}
}

0 comments on commit 8b8806d

Please sign in to comment.