Skip to content

Commit e8f3e31

Browse files
committed
Added field access capabilities (work in progress)
1 parent d504ebc commit e8f3e31

File tree

4 files changed

+212
-39
lines changed

4 files changed

+212
-39
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package de.danielbechler.diff.introspection
2+
3+
import de.danielbechler.diff.access.PropertyAwareAccessor
4+
import spock.lang.Specification
5+
6+
abstract class AbstractIntrospectorSpecification extends Specification {
7+
8+
abstract Introspector getIntrospector()
9+
10+
def 'private field accessible via getter and setter'() {
11+
given:
12+
def testType = new IntrospectorTestType()
13+
14+
when:
15+
def typeInfo = introspector.introspect(IntrospectorTestType)
16+
def accessor = getAccessorByName(typeInfo.accessors, 'readWriteProperty')
17+
18+
then:
19+
accessor != null
20+
21+
and:
22+
accessor.fieldAnnotations.size() == 1
23+
def fieldAnnotation = accessor.fieldAnnotations.first() as ObjectDiffProperty
24+
fieldAnnotation.categories().first() == 'field'
25+
26+
and:
27+
accessor.readMethodAnnotations.size() == 1
28+
def getterAnnotation = accessor.readMethodAnnotations.first() as ObjectDiffProperty
29+
getterAnnotation.categories().first() == 'getter'
30+
31+
and:
32+
accessor.categoriesFromAnnotation == ['getter'] as Set
33+
34+
when:
35+
def value1 = UUID.randomUUID().toString()
36+
accessor.set(testType, value1)
37+
38+
then:
39+
testType.getReadWriteProperty() == value1
40+
41+
when:
42+
def value2 = UUID.randomUUID().toString()
43+
testType.setReadWriteProperty(value2)
44+
45+
then:
46+
accessor.get(testType) == value2
47+
}
48+
49+
def 'private field accessible via getter and ambiguous setters'() {
50+
given:
51+
def testType = new IntrospectorTestType()
52+
53+
when:
54+
def typeInfo = introspector.introspect(IntrospectorTestType)
55+
def accessor = getAccessorByName(typeInfo.accessors, 'ambiguousSetterProperty')
56+
57+
then:
58+
accessor != null
59+
60+
when:
61+
def value1 = Integer.valueOf(10)
62+
accessor.set(testType, value1)
63+
64+
then:
65+
testType.getAmbiguousSetterProperty() == value1
66+
67+
when:
68+
def value2 = Math.random()
69+
testType.setAmbiguousSetterProperty(value2 as Number)
70+
71+
then:
72+
accessor.get(testType) == value2
73+
}
74+
75+
def 'private field only accessible via getter'() {
76+
given:
77+
def testType = new IntrospectorTestType()
78+
79+
when:
80+
def typeInfo = introspector.introspect(IntrospectorTestType)
81+
def accessor = getAccessorByName(typeInfo.accessors, 'readOnlyProperty')
82+
83+
then:
84+
accessor != null
85+
86+
and:
87+
accessor.fieldAnnotations.size() == 1
88+
def fieldAnnotation = accessor.fieldAnnotations.first() as ObjectDiffProperty
89+
fieldAnnotation.categories().first() == 'field'
90+
91+
and:
92+
accessor.readMethodAnnotations.size() == 1
93+
def getterAnnotation = accessor.readMethodAnnotations.first() as ObjectDiffProperty
94+
getterAnnotation.categories().first() == 'getter'
95+
96+
when:
97+
def initialReadOnlyPropertyValue = testType.getReadOnlyProperty()
98+
def newReadOnlyPropertyValue = UUID.randomUUID().toString()
99+
accessor.set(testType, newReadOnlyPropertyValue)
100+
101+
then:
102+
testType.getReadOnlyProperty() == initialReadOnlyPropertyValue
103+
104+
expect:
105+
accessor.get(testType) == testType.getReadOnlyProperty()
106+
}
107+
108+
static PropertyAwareAccessor getAccessorByName(Collection<PropertyAwareAccessor> accessors, String propertyName) {
109+
return accessors.find { it.propertyName == propertyName }
110+
}
111+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package de.danielbechler.diff.introspection
2+
3+
import spock.lang.Subject
4+
5+
class DefaultIntrospectorTest extends AbstractIntrospectorSpecification {
6+
7+
@Subject
8+
DefaultIntrospector introspector = new DefaultIntrospector()
9+
10+
def 'public field and accessors of the same name'() {
11+
given:
12+
introspector.returnFields = true
13+
14+
when:
15+
def typeInfo = introspector.introspect(IntrospectorTestType)
16+
def accessors = typeInfo.accessors.grep {
17+
it.propertyName == 'publicFieldWithAccessors'
18+
}
19+
20+
then: 'only one accessor with that name should be returned'
21+
accessors.size() == 1
22+
23+
and: 'the getter-setter-accessor should win'
24+
accessors.first() instanceof PropertyAccessor
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
package de.danielbechler.diff.introspection;
22

3+
import java.util.UUID;
4+
35
public class IntrospectorTestType
46
{
57
public static final String constantField = "constant_field";
68
private String privateField;
79
String packageProtectedField;
810
protected String protectedField;
9-
public String publicField;
1011
@ObjectDiffProperty
11-
public String annotatedPublicField;
12+
public String publicField;
1213
public final String publicFinalField = "public_final_field";
14+
@ObjectDiffProperty(categories = {"field"})
1315
private String readWriteProperty;
14-
@ObjectDiffProperty
15-
private String fieldAnnotatedReadOnlyProperty;
16+
@ObjectDiffProperty(categories = {"field"})
17+
private String readOnlyProperty;
18+
private Number ambiguousSetterProperty;
19+
public String publicFieldWithAccessors;
1620

17-
@ObjectDiffProperty
21+
public IntrospectorTestType()
22+
{
23+
readOnlyProperty = UUID.randomUUID().toString();
24+
}
25+
26+
@ObjectDiffProperty(categories = {"getter"})
1827
public String getReadWriteProperty()
1928
{
2029
return readWriteProperty;
@@ -25,8 +34,34 @@ public void setReadWriteProperty(final String readWriteProperty)
2534
this.readWriteProperty = readWriteProperty;
2635
}
2736

28-
public String getFieldAnnotatedReadOnlyProperty()
37+
@ObjectDiffProperty(categories = {"getter"})
38+
public String getReadOnlyProperty()
39+
{
40+
return readOnlyProperty;
41+
}
42+
43+
public Number getAmbiguousSetterProperty()
44+
{
45+
return ambiguousSetterProperty;
46+
}
47+
48+
public void setAmbiguousSetterProperty(final Integer ambiguousSetterProperty)
49+
{
50+
throw new UnsupportedOperationException();
51+
}
52+
53+
public void setAmbiguousSetterProperty(final Number ambiguousSetterProperty)
54+
{
55+
this.ambiguousSetterProperty = ambiguousSetterProperty;
56+
}
57+
58+
public String getPublicFieldWithAccessors()
59+
{
60+
return publicFieldWithAccessors;
61+
}
62+
63+
public void setPublicFieldWithAccessors(final String publicFieldWithAccessors)
2964
{
30-
return fieldAnnotatedReadOnlyProperty;
65+
this.publicFieldWithAccessors = publicFieldWithAccessors;
3166
}
3267
}

src/test/java/de/danielbechler/diff/introspection/StandardIntrospectorTest.groovy

+33-32
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@ package de.danielbechler.diff.introspection
1818

1919
import de.danielbechler.diff.access.PropertyAwareAccessor
2020
import de.danielbechler.diff.mock.ObjectWithString
21-
import spock.lang.Specification
21+
import spock.lang.Subject
2222

2323
import java.beans.BeanInfo
2424
import java.beans.IntrospectionException
2525

2626
/**
2727
* @author Daniel Bechler
2828
*/
29-
public class StandardIntrospectorTest extends Specification {
29+
class StandardIntrospectorTest extends AbstractIntrospectorSpecification {
3030

31-
def introspector = new StandardIntrospector()
31+
@Subject
32+
StandardIntrospector introspector = new StandardIntrospector()
3233

3334
private Map<String, PropertyAwareAccessor> introspect(Class<?> type) {
3435
introspector.introspect(type).accessors.collectEntries {
@@ -38,74 +39,74 @@ public class StandardIntrospectorTest extends Specification {
3839

3940
def 'should return proper accessor for property'() {
4041
when:
41-
def accessor = introspect(TypeWithOnlyOneProperty).get('value')
42+
def accessor = introspect(TypeWithOnlyOneProperty).get('value')
4243
then:
43-
accessor.propertyName == 'value'
44+
accessor.propertyName == 'value'
4445
and:
45-
def target = new TypeWithOnlyOneProperty()
46-
accessor.get(target) == null
46+
def target = new TypeWithOnlyOneProperty()
47+
accessor.get(target) == null
4748
and:
48-
accessor.set(target, 'bar')
49-
accessor.get(target) == 'bar'
49+
accessor.set(target, 'bar')
50+
accessor.get(target) == 'bar'
5051
and:
51-
accessor.excludedByAnnotation == false
52+
accessor.excludedByAnnotation == false
5253
and:
53-
accessor.categoriesFromAnnotation.isEmpty()
54+
accessor.categoriesFromAnnotation.isEmpty()
5455
}
5556

5657
def 'should return PropertyAwareAccessors for each property of the given class'() {
5758
when:
58-
def accessors = introspect(TypeWithTwoProperties)
59+
def accessors = introspect(TypeWithTwoProperties)
5960
then:
60-
accessors.size() == 2
61-
accessors.get('foo') != null
62-
accessors.get('bar') != null
61+
accessors.size() == 2
62+
accessors.get('foo') != null
63+
accessors.get('bar') != null
6364
}
6465

6566
def 'should apply categories of ObjectDiffProperty annotation to accessor'() {
6667
when:
67-
def accessor = introspect(TypeWithPropertyAnnotation).get('value')
68+
def accessor = introspect(TypeWithPropertyAnnotation).get('value')
6869
then:
69-
accessor.categoriesFromAnnotation.size() == 2
70-
accessor.categoriesFromAnnotation.containsAll(['category1', 'category2'])
70+
accessor.categoriesFromAnnotation.size() == 2
71+
accessor.categoriesFromAnnotation.containsAll(['category1', 'category2'])
7172
}
7273

7374
def 'should apply exclusion of ObjectDiffProperty annotation to accessor'() {
7475
when:
75-
def accessor = introspect(TypeWithPropertyAnnotation).get('value')
76+
def accessor = introspect(TypeWithPropertyAnnotation).get('value')
7677
then:
77-
accessor.excludedByAnnotation == true
78+
accessor.excludedByAnnotation == true
7879
}
7980

8081
def 'should throw exception when invoked without type'() {
8182
when:
82-
introspector.introspect(null)
83+
introspector.introspect(null)
8384
then:
84-
thrown(IllegalArgumentException)
85+
thrown(IllegalArgumentException)
8586
}
8687

8788
def 'should skip default class properties'() {
8889
expect:
89-
introspect(TypeWithNothingButDefaultProperties).isEmpty()
90+
introspect(TypeWithNothingButDefaultProperties).isEmpty()
9091
}
9192

9293
def 'should skip properties without getter'() {
9394
expect:
94-
introspect(TypeWithPropertyWithoutGetter).isEmpty()
95+
introspect(TypeWithPropertyWithoutGetter).isEmpty()
9596
}
9697

9798
def 'should wrap IntrospectionException with RuntimeException'() {
9899
given:
99-
introspector = new StandardIntrospector() {
100-
@Override
101-
protected BeanInfo getBeanInfo(final Class<?> type) throws IntrospectionException {
102-
throw new IntrospectionException(type.getCanonicalName());
103-
}
104-
};
100+
introspector = new StandardIntrospector() {
101+
@Override
102+
protected BeanInfo getBeanInfo(final Class<?> type) throws IntrospectionException {
103+
throw new IntrospectionException(type.getCanonicalName());
104+
}
105+
};
105106
when:
106-
introspector.introspect(ObjectWithString.class);
107+
introspector.introspect(ObjectWithString.class);
107108
then:
108-
thrown(RuntimeException)
109+
thrown(RuntimeException)
109110
}
110111

111112
private class TypeWithNothingButDefaultProperties {

0 commit comments

Comments
 (0)