• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.introspect;
2 
3 import com.fasterxml.jackson.annotation.JsonCreator;
4 import com.fasterxml.jackson.databind.*;
5 import com.fasterxml.jackson.databind.cfg.MapperConfig;
6 
7 /**
8  * Tests Scala-style JVM naming patterns for properties.
9  *
10  * Scala uses identifiers that are legal JVM names, but not legal Java names:
11  *
12  * <ul>
13  *     <li><code>prop␣</code> (trailing space) for fields</li>
14  *     <li><code>prop</code> for getters</li>
15  *     <li><code>prop_=</code> for setters</li>
16  * </ul>
17  *
18  * Scala sources turn property accesses into method calls in most cases; the
19  * backing field and the particulars of the method names are implementation details.
20  *
21  * Since I can't reproduce them in Java, I've substituted legal but uncommonly
22  * used characters as placeholders.
23  */
24 public class TestScalaLikeImplicitProperties extends BaseMapTest
25 {
26     static class NameMangler extends JacksonAnnotationIntrospector
27     {
28         private static final long serialVersionUID = 1L;
29 
30         @Override
findImplicitPropertyName(AnnotatedMember member)31         public String findImplicitPropertyName(AnnotatedMember member) {
32             String name = null;
33             if (member instanceof AnnotatedField) {
34                 name = member.getName();
35                 if (name.endsWith("‿")) {
36                     return name.substring(0, name.length()-1);
37                 }
38             } else if (member instanceof AnnotatedMethod) {
39                 name = member.getName();
40                 if (name.endsWith("_⁀")) {
41                     return name.substring(0, name.length()-2);
42                 }
43                 if (!name.startsWith("get") && !name.startsWith("set")) {
44                     return name;
45                 }
46             } else if (member instanceof AnnotatedParameter) {
47                 // A placeholder for legitimate property name detection
48                 // such as what the JDK8 module provides
49                 return "prop";
50             }
51             return null;
52         }
53 
54         /* Deprecated since 2.9
55         @Override
56         public boolean hasCreatorAnnotation(Annotated a) {
57             return (a instanceof AnnotatedConstructor);
58         }
59         */
60 
61         @Override
findCreatorAnnotation(MapperConfig<?> config, Annotated a)62         public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
63             // A placeholder for legitimate creator detection.
64             // In Scala, all primary constructors should be creators,
65             // but I can't obtain a reference to the AnnotatedClass from the
66             // AnnotatedConstructor, so it's simulated here.
67             return (a instanceof AnnotatedConstructor)
68                     ? JsonCreator.Mode.DEFAULT : null;
69         }
70     }
71 
72     static class ValProperty
73     {
74         private final String prop‿;
prop()75         public String prop() { return prop‿; }
76 
ValProperty(String prop)77         public ValProperty(String prop) {
78             prop‿ = prop;
79         }
80     }
81 
82     static class ValWithBeanProperty
83     {
84         private final String prop‿;
prop()85         public String prop() { return prop‿; }
getProp()86         public String getProp() { return prop‿; }
87 
ValWithBeanProperty(String prop)88         public ValWithBeanProperty(String prop) {
89             prop‿ = prop;
90         }
91     }
92 
93     static class VarProperty
94     {
95         private String prop‿;
prop()96         public String prop() { return prop‿; }
prop_�(String p)97         public void prop_⁀(String p) { prop‿ = p; }
98 
VarProperty(String prop)99         public VarProperty(String prop) {
100             prop‿ = prop;
101         }
102     }
103 
104     static class VarWithBeanProperty
105     {
106         private String prop‿;
prop()107         public String prop() { return prop‿; }
prop_�(String p)108         public void prop_⁀(String p) { prop‿ = p; }
getProp()109         public String getProp() { return prop‿; }
setProp(String p)110         public void setProp(String p) { prop‿ = p; }
111 
VarWithBeanProperty(String prop)112         public VarWithBeanProperty(String prop) {
113             prop‿ = prop;
114         }
115     }
116 
117     static class GetterSetterProperty
118     {
119         // Different name to represent an arbitrary implementation, not necessarily local to this class.
120         private String _prop_impl = "get/set";
prop()121         public String prop() { return _prop_impl; }
prop_�(String p)122         public void prop_⁀(String p) { _prop_impl = p; }
123 
124         // Getter/Setters are typically not in the constructor because they are implemented
125         // by the end user, not the compiler. They should be detected similar to 'bean-style'
126         // getProp/setProp pairs.
127     }
128 
129     /*
130     /**********************************************************
131     /* Test methods
132     /**********************************************************
133      */
134 
testValProperty()135     public void testValProperty() throws Exception
136     {
137         ObjectMapper m = manglingMapper();
138 
139         assertEquals("{\"prop\":\"val\"}", m.writeValueAsString(new ValProperty("val")));
140     }
141 
testValWithBeanProperty()142     public void testValWithBeanProperty() throws Exception
143     {
144         ObjectMapper m = manglingMapper();
145 
146         assertEquals("{\"prop\":\"val\"}", m.writeValueAsString(new ValWithBeanProperty("val")));
147     }
148 
149 
testVarProperty()150     public void testVarProperty() throws Exception
151     {
152         ObjectMapper m = manglingMapper();
153 
154         assertEquals("{\"prop\":\"var\"}", m.writeValueAsString(new VarProperty("var")));
155         VarProperty result = m.readValue("{\"prop\":\"read\"}", VarProperty.class);
156         assertEquals("read", result.prop());
157     }
158 
159 
testVarWithBeanProperty()160     public void testVarWithBeanProperty() throws Exception
161     {
162         ObjectMapper m = manglingMapper();
163 
164         assertEquals("{\"prop\":\"var\"}", m.writeValueAsString(new VarWithBeanProperty("var")));
165         VarWithBeanProperty result = m.readValue("{\"prop\":\"read\"}", VarWithBeanProperty.class);
166         assertEquals("read", result.prop());
167     }
168 
169 
testGetterSetterProperty()170     public void testGetterSetterProperty() throws Exception
171     {
172         ObjectMapper m = manglingMapper();
173 
174         assertEquals("{\"prop\":\"get/set\"}", m.writeValueAsString(new GetterSetterProperty()));
175         GetterSetterProperty result = m.readValue("{\"prop\":\"read\"}", GetterSetterProperty.class);
176         assertEquals("read", result.prop());
177     }
178 
179     /*
180     /**********************************************************
181     /* Helper methods
182     /**********************************************************
183      */
184 
manglingMapper()185     private ObjectMapper manglingMapper()
186     {
187         ObjectMapper m = new ObjectMapper();
188         m.setAnnotationIntrospector(new NameMangler());
189         return m;
190     }
191 }
192