Repository: johnzon Updated Branches: refs/heads/master b28c4baa0 -> b3e496efc
JOHNZON-89 JOHNZON-88 switching default access mode to field+method + fixing this mode + upgrading tomee for websocket tests Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/b3e496ef Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/b3e496ef Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/b3e496ef Branch: refs/heads/master Commit: b3e496efc6afead36a115979ef4c478a1cae4a53 Parents: b28c4ba Author: Romain manni-Bucau <[email protected]> Authored: Sat Jul 2 21:21:54 2016 +0200 Committer: Romain manni-Bucau <[email protected]> Committed: Sat Jul 2 21:21:54 2016 +0200 ---------------------------------------------------------------------- .../apache/johnzon/jsonb/JsonbAccessMode.java | 2 +- .../apache/johnzon/mapper/MapperBuilder.java | 6 +- .../mapper/access/FieldAndMethodAccessMode.java | 140 +++++++++++++++++-- .../johnzon/mapper/access/MethodAccessMode.java | 16 ++- .../johnzon/mapper/GetterSetterRespectTest.java | 49 +++++++ .../johnzon/mapper/JohnzonAnyMappingTest.java | 2 + johnzon-websocket/pom.xml | 18 ++- .../src/test/resources/arquillian.xml | 1 - src/site/markdown/index.md | 2 + 9 files changed, 210 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java index 25bdf12..28c7211 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java @@ -127,7 +127,7 @@ public class JsonbAccessMode implements AccessMode, Closeable { this.order = orderValue; this.visibility = visibilityStrategy; this.caseSensitive = caseSensitive; - this.delegate = new FieldAndMethodAccessMode(true, true); + this.delegate = new FieldAndMethodAccessMode(true, true, false); this.defaultConverters = defaultConverters; this.factory = factory; this.parserFactory = parserFactory; http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java index 944ed9b..b3e57eb 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java @@ -167,10 +167,10 @@ public class MapperBuilder { accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, true); } else if ("strict-method".equalsIgnoreCase(accessModeName)) { accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, false); - } else if ("both".equalsIgnoreCase(accessModeName)) { - accessMode = new FieldAndMethodAccessMode(supportConstructors, supportHiddenAccess); + } else if ("both".equalsIgnoreCase(accessModeName) || accessModeName == null) { + accessMode = new FieldAndMethodAccessMode(supportConstructors, supportHiddenAccess, useGetterForCollections); } else { - accessMode = new MethodAccessMode(supportConstructors, supportHiddenAccess, useGetterForCollections); + throw new IllegalArgumentException("Unsupported access mode: " + accessModeName); } } if (!ignoredForFields.isEmpty()) { http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAndMethodAccessMode.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAndMethodAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAndMethodAccessMode.java index 0e4d9e7..607bf2d 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAndMethodAccessMode.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAndMethodAccessMode.java @@ -19,52 +19,174 @@ package org.apache.johnzon.mapper.access; import org.apache.johnzon.mapper.Adapter; +import org.apache.johnzon.mapper.JohnzonIgnore; +import org.apache.johnzon.mapper.JohnzonProperty; import org.apache.johnzon.mapper.ObjectConverter; +import java.beans.Introspector; import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; -// methods override fields +// annotated entity overrides the other one, methods are used instead of field if both are there public class FieldAndMethodAccessMode extends BaseAccessMode { private final FieldAccessMode fields; private final MethodAccessMode methods; - public FieldAndMethodAccessMode(final boolean useConstructor, final boolean acceptHiddenConstructor) { + public FieldAndMethodAccessMode(final boolean useConstructor, final boolean acceptHiddenConstructor, + final boolean useGettersAsWriter) { super(useConstructor, acceptHiddenConstructor); this.fields = new FieldAccessMode(useConstructor, acceptHiddenConstructor); - this.methods = new MethodAccessMode(useConstructor, acceptHiddenConstructor, false); + this.methods = new MethodAccessMode(useConstructor, acceptHiddenConstructor, useGettersAsWriter); } @Override public Map<String, Reader> doFindReaders(final Class<?> clazz) { - final Map<String, Reader> readers = new HashMap<String, Reader>(fields.findReaders(clazz)); - for (final Map.Entry<String, Reader> entry : methods.findReaders(clazz).entrySet()) { + final Map<String, Reader> fieldsReaders = this.fields.findReaders(clazz); + final Map<String, Reader> methodReaders = this.methods.findReaders(clazz); + + final Map<String, Reader> readers = new HashMap<String, Reader>(); + + for (final Map.Entry<String, Reader> entry : fieldsReaders.entrySet()) { + final String key = entry.getKey(); + Method m = getMethod("get" + Character.toUpperCase(key.charAt(0)) + (key.length() > 1 ? key.substring(1) : ""), clazz); + if (m == null && (boolean.class == entry.getValue().getType() || Boolean.class == entry.getValue().getType())) { + m = getMethod("is" + Character.toUpperCase(key.charAt(0)) + (key.length() > 1 ? key.substring(1) : ""), clazz); + } + boolean skip = false; + if (m != null) { + for (final Reader w : methodReaders.values()) { + if (MethodAccessMode.MethodDecoratedType.class.cast(w).getMethod().equals(m)) { + if (w.getAnnotation(JohnzonProperty.class) != null || w.getAnnotation(JohnzonIgnore.class) != null) { + skip = true; + } + break; + } + } + } + if (skip) { + continue; + } + readers.put(entry.getKey(), entry.getValue()); + } + + for (final Map.Entry<String, Reader> entry : methodReaders.entrySet()) { + final Method mr = MethodAccessMode.MethodDecoratedType.class.cast(entry.getValue()).getMethod(); + final String fieldName = Introspector.decapitalize(mr.getName().startsWith("is") ? mr.getName().substring(2) : mr.getName().substring(3)); + final Field f = getField(fieldName, clazz); + boolean skip = false; + if (f != null) { + for (final Reader w : fieldsReaders.values()) { + if (FieldAccessMode.FieldDecoratedType.class.cast(w).getField().equals(f)) { + if (w.getAnnotation(JohnzonProperty.class) != null || w.getAnnotation(JohnzonIgnore.class) != null) { + skip = true; + } + break; + } + } + } + if (skip) { + continue; + } + final Reader existing = readers.get(entry.getKey()); if (existing == null) { readers.put(entry.getKey(), entry.getValue()); } else { - readers.put(entry.getKey(), new CompositeReader(existing, entry.getValue())); + readers.put(entry.getKey(), new CompositeReader(entry.getValue(), existing)); } } + return readers; } + private Method getMethod(final String methodName, final Class<?> type, final Class<?>... args) { + try { + return type.getMethod(methodName, args); + } catch (final NoSuchMethodException e) { + return null; + } + } + + private Field getField(final String fieldName, final Class<?> type) { + Class<?> t = type; + while (t != Object.class && t != null) { + try { + return t.getDeclaredField(fieldName); + } catch (final NoSuchFieldException e) { + // no-op + } + t = t.getSuperclass(); + } + return null; + } + @Override public Map<String, Writer> doFindWriters(final Class<?> clazz) { - final Map<String, Writer> writers = new HashMap<String, Writer>(fields.findWriters(clazz)); - for (final Map.Entry<String, Writer> entry : methods.findWriters(clazz).entrySet()) { + final Map<String, Writer> fieldWriters = this.fields.findWriters(clazz); + final Map<String, Writer> metodWriters = this.methods.findWriters(clazz); + + final Map<String, Writer> writers = new HashMap<String, Writer>(); + + for (final Map.Entry<String, Writer> entry : fieldWriters.entrySet()) { + final String key = entry.getKey(); + final Method m = getMethod("set" + Character.toUpperCase(key.charAt(0)) + (key.length() > 1 ? key.substring(1) : ""), clazz, toType(entry.getValue().getType())); + boolean skip = false; + if (m != null) { + for (final Writer w : metodWriters.values()) { + if (MethodAccessMode.MethodDecoratedType.class.cast(w).getMethod().equals(m)) { + if (w.getAnnotation(JohnzonProperty.class) != null) { + skip = true; + } + break; + } + } + } + if (skip) { + continue; + } + writers.put(entry.getKey(), entry.getValue()); + } + + for (final Map.Entry<String, Writer> entry : metodWriters.entrySet()) { + final Method mr = MethodAccessMode.MethodDecoratedType.class.cast(entry.getValue()).getMethod(); + final String fieldName = Introspector.decapitalize(mr.getName().startsWith("is") ? mr.getName().substring(2) : mr.getName().substring(3)); + final Field f = getField(fieldName, clazz); + boolean skip = false; + if (f != null) { + for (final Writer w : fieldWriters.values()) { + if (FieldAccessMode.FieldDecoratedType.class.cast(w).getField().equals(f)) { + if (w.getAnnotation(JohnzonProperty.class) != null) { + skip = true; + } + break; + } + } + } + if (skip) { + continue; + } + final Writer existing = writers.get(entry.getKey()); if (existing == null) { writers.put(entry.getKey(), entry.getValue()); } else { - writers.put(entry.getKey(), new CompositeWriter(existing, entry.getValue())); + writers.put(entry.getKey(), new CompositeWriter(entry.getValue(), existing)); } } return writers; } + private Class<?> toType(final Type type) { + return Class.class.isInstance(type) ? Class.class.cast(type) : + (ParameterizedType.class.isInstance(type) ? toType(ParameterizedType.class.cast(type).getRawType()) : + Object.class /*fallback*/); + } + public static abstract class CompositeDecoratedType implements DecoratedType { protected final DecoratedType type1; private final DecoratedType type2; http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java index 302b4f4..c81c070 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java @@ -52,7 +52,7 @@ public class MethodAccessMode extends BaseAccessMode { if (isIgnored(descriptor.getName()) || Meta.getAnnotation(readMethod, JohnzonAny.class) != null) { continue; } - readers.put(extractKey(descriptor), new MethodReader(readMethod, fixType(clazz, readMethod.getGenericReturnType()))); + readers.put(extractKey(descriptor.getName(), readMethod, null), new MethodReader(readMethod, fixType(clazz, readMethod.getGenericReturnType()))); } } return readers; @@ -68,20 +68,24 @@ public class MethodAccessMode extends BaseAccessMode { } final Method writeMethod = descriptor.getWriteMethod(); if (writeMethod != null) { - writers.put(extractKey(descriptor), new MethodWriter(writeMethod, fixType(clazz, writeMethod.getGenericParameterTypes()[0]))); + writers.put(extractKey(descriptor.getName(), writeMethod, descriptor.getReadMethod()), + new MethodWriter(writeMethod, fixType(clazz, writeMethod.getGenericParameterTypes()[0]))); } else if (supportGetterAsWritter && Collection.class.isAssignableFrom(descriptor.getPropertyType()) && descriptor.getReadMethod() != null) { final Method readMethod = descriptor.getReadMethod(); - writers.put(extractKey(descriptor), new MethodGetterAsWriter(readMethod, fixType(clazz, readMethod.getGenericReturnType()))); + writers.put(extractKey(descriptor.getName(), readMethod, null), new MethodGetterAsWriter(readMethod, fixType(clazz, readMethod.getGenericReturnType()))); } } return writers; } - private String extractKey(final PropertyDescriptor f) { - final JohnzonProperty property = f.getReadMethod() == null ? null : Meta.getAnnotation(f.getReadMethod(), JohnzonProperty.class); - return property != null ? property.value() : f.getName(); + private String extractKey(final String name, final Method from, final Method or) { + JohnzonProperty property = Meta.getAnnotation(from, JohnzonProperty.class); + if (property == null && or != null) { + property = Meta.getAnnotation(or, JohnzonProperty.class); + } + return property != null ? property.value() : name; } protected boolean isIgnored(final String name) { http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GetterSetterRespectTest.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GetterSetterRespectTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GetterSetterRespectTest.java new file mode 100644 index 0000000..a129e11 --- /dev/null +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/GetterSetterRespectTest.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.johnzon.mapper; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class GetterSetterRespectTest { + @Test + public void run() { + final Mapper mapper = new MapperBuilder().build(); + assertEquals("ok", Mapped.class.cast(mapper.readObject("{\"name_\":\"ok\"}", Mapped.class)).name); + + final Mapped mapped = new Mapped(); + mapped.name = "ok"; + assertEquals("{\"_name\":\"ok\"}", mapper.writeObjectAsString(mapped)); + } + + public static class Mapped { + private String name; + + @JohnzonProperty("_name") + public String getName() { + return name; + } + + @JohnzonProperty("name_") + public void setName(final String name) { + this.name = name; + } + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonAnyMappingTest.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonAnyMappingTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonAnyMappingTest.java index cb52d74..6ec0daf 100644 --- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonAnyMappingTest.java +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonAnyMappingTest.java @@ -58,6 +58,8 @@ public class JohnzonAnyMappingTest { public static class AnyMe { private String name; + + @JohnzonIgnore private Map<String, Object> any = new TreeMap<String, Object>(); public String getName() { http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/johnzon-websocket/pom.xml ---------------------------------------------------------------------- diff --git a/johnzon-websocket/pom.xml b/johnzon-websocket/pom.xml index b319669..661ab59 100644 --- a/johnzon-websocket/pom.xml +++ b/johnzon-websocket/pom.xml @@ -29,8 +29,8 @@ <name>Johnzon :: WebSocket</name> <properties> - <tomcat.version>7.0.59</tomcat.version> - <tomee.version>1.7.4</tomee.version> + <tomcat.version>8.5.3</tomcat.version> + <tomee.version>7.0.1</tomee.version> <staging.directory>${project.parent.reporting.outputDirectory}</staging.directory> </properties> @@ -61,7 +61,7 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.apache.openejb</groupId> + <groupId>org.apache.tomee</groupId> <artifactId>arquillian-tomee-remote</artifactId> <version>${tomee.version}</version> <scope>test</scope> @@ -87,16 +87,22 @@ </exclusions> </dependency> <dependency> - <groupId>org.apache.openejb</groupId> + <groupId>org.apache.tomee</groupId> <artifactId>apache-tomee</artifactId> <version>${tomee.version}</version> <type>zip</type> - <classifier>jaxrs</classifier> + <classifier>webprofile</classifier> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> - <artifactId>tomcat7-websocket</artifactId> + <artifactId>tomcat-websocket</artifactId> + <version>${tomcat.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.tomcat</groupId> + <artifactId>tomcat-api</artifactId> <version>${tomcat.version}</version> <scope>test</scope> </dependency> http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/johnzon-websocket/src/test/resources/arquillian.xml ---------------------------------------------------------------------- diff --git a/johnzon-websocket/src/test/resources/arquillian.xml b/johnzon-websocket/src/test/resources/arquillian.xml index c86d7a3..1f2d9d0 100644 --- a/johnzon-websocket/src/test/resources/arquillian.xml +++ b/johnzon-websocket/src/test/resources/arquillian.xml @@ -24,7 +24,6 @@ http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> <container qualifier="tomee" default="true"> <configuration> - <property name="classifier">jaxrs</property> <property name="httpsPort">-1</property> <property name="httpPort">-1</property> <property name="stopPort">-1</property> http://git-wip-us.apache.org/repos/asf/johnzon/blob/b3e496ef/src/site/markdown/index.md ---------------------------------------------------------------------- diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 8c97a99..87fe66b 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -189,6 +189,7 @@ If you don't fully know you model but want to handle all keys you can use @Johnz <pre class="prettyprint linenums"><