package com.fasterxml.jackson.databind.introspect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
/**
* Tests Scala-style JVM naming patterns for properties.
*
* Scala uses identifiers that are legal JVM names, but not legal Java names:
*
* <ul>
* <li><code>prop␣</code> (trailing space) for fields</li>
* <li><code>prop</code> for getters</li>
* <li><code>prop_=</code> for setters</li>
* </ul>
*
* Scala sources turn property accesses into method calls in most cases; the
* backing field and the particulars of the method names are implementation details.
*
* Since I can't reproduce them in Java, I've substituted legal but uncommonly
* used characters as placeholders.
*/
public class TestScalaLikeImplicitProperties extends BaseMapTest
{
static class NameMangler extends JacksonAnnotationIntrospector
{
private static final long serialVersionUID = 1L;
@Override
public String findImplicitPropertyName(AnnotatedMember member) {
String name = null;
if (member instanceof AnnotatedField) {
name = member.getName();
if (name.endsWith("‿")) {
return name.substring(0, name.length()-1);
}
} else if (member instanceof AnnotatedMethod) {
name = member.getName();
if (name.endsWith("_⁀")) {
return name.substring(0, name.length()-2);
}
if (!name.startsWith("get") && !name.startsWith("set")) {
return name;
}
} else if (member instanceof AnnotatedParameter) {
// A placeholder for legitimate property name detection
// such as what the JDK8 module provides
return "prop";
}
return null;
}
/* Deprecated since 2.9
@Override
public boolean hasCreatorAnnotation(Annotated a) {
return (a instanceof AnnotatedConstructor);
}
*/
@Override
public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
// A placeholder for legitimate creator detection.
// In Scala, all primary constructors should be creators,
// but I can't obtain a reference to the AnnotatedClass from the
// AnnotatedConstructor, so it's simulated here.
return (a instanceof AnnotatedConstructor)
? JsonCreator.Mode.DEFAULT : null;
}
}
static class ValProperty
{
private final String prop‿;
public String prop() { return prop‿; }
public ValProperty(String prop) {
prop‿ = prop;
}
}
static class ValWithBeanProperty
{
private final String prop‿;
public String prop() { return prop‿; }
public String getProp() { return prop‿; }
public ValWithBeanProperty(String prop) {
prop‿ = prop;
}
}
static class VarProperty
{
private String prop‿;
public String prop() { return prop‿; }
public void prop_⁀(String p) { prop‿ = p; }
public VarProperty(String prop) {
prop‿ = prop;
}
}
static class VarWithBeanProperty
{
private String prop‿;
public String prop() { return prop‿; }
public void prop_⁀(String p) { prop‿ = p; }
public String getProp() { return prop‿; }
public void setProp(String p) { prop‿ = p; }
public VarWithBeanProperty(String prop) {
prop‿ = prop;
}
}
static class GetterSetterProperty
{
// Different name to represent an arbitrary implementation, not necessarily local to this class.
private String _prop_impl = "get/set";
public String prop() { return _prop_impl; }
public void prop_⁀(String p) { _prop_impl = p; }
// Getter/Setters are typically not in the constructor because they are implemented
// by the end user, not the compiler. They should be detected similar to 'bean-style'
// getProp/setProp pairs.
}
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
public void testValProperty() throws Exception
{
ObjectMapper m = manglingMapper();
assertEquals("{\"prop\":\"val\"}", m.writeValueAsString(new ValProperty("val")));
}
public void testValWithBeanProperty() throws Exception
{
ObjectMapper m = manglingMapper();
assertEquals("{\"prop\":\"val\"}", m.writeValueAsString(new ValWithBeanProperty("val")));
}
public void testVarProperty() throws Exception
{
ObjectMapper m = manglingMapper();
assertEquals("{\"prop\":\"var\"}", m.writeValueAsString(new VarProperty("var")));
VarProperty result = m.readValue("{\"prop\":\"read\"}", VarProperty.class);
assertEquals("read", result.prop());
}
public void testVarWithBeanProperty() throws Exception
{
ObjectMapper m = manglingMapper();
assertEquals("{\"prop\":\"var\"}", m.writeValueAsString(new VarWithBeanProperty("var")));
VarWithBeanProperty result = m.readValue("{\"prop\":\"read\"}", VarWithBeanProperty.class);
assertEquals("read", result.prop());
}
public void testGetterSetterProperty() throws Exception
{
ObjectMapper m = manglingMapper();
assertEquals("{\"prop\":\"get/set\"}", m.writeValueAsString(new GetterSetterProperty()));
GetterSetterProperty result = m.readValue("{\"prop\":\"read\"}", GetterSetterProperty.class);
assertEquals("read", result.prop());
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
private ObjectMapper manglingMapper()
{
ObjectMapper m = new ObjectMapper();
m.setAnnotationIntrospector(new NameMangler());
return m;
}
}