BV2: utility classes
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/fad43eb8 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/fad43eb8 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/fad43eb8 Branch: refs/heads/bv2 Commit: fad43eb892b66592606fa76d70f67f8d016db373 Parents: 5c09f0d Author: Matt Benson <[email protected]> Authored: Wed Feb 21 14:31:22 2018 -0600 Committer: Matt Benson <[email protected]> Committed: Wed Feb 21 14:44:27 2018 -0600 ---------------------------------------------------------------------- .../java/org/apache/bval/util/BValVersion.java | 12 +- .../java/org/apache/bval/util/Exceptions.java | 125 +++++++ .../main/java/org/apache/bval/util/Lazy.java | 62 ++++ .../main/java/org/apache/bval/util/LazyInt.java | 49 +++ .../java/org/apache/bval/util/ObjectUtils.java | 20 +- .../org/apache/bval/util/ObjectWrapper.java | 50 +++ .../java/org/apache/bval/util/StringUtils.java | 67 ++-- .../java/org/apache/bval/util/Validate.java | 29 +- .../apache/bval/util/reflection/Reflection.java | 173 +++++++-- .../apache/bval/util/reflection/TypeUtils.java | 65 ++-- .../bval/jsr/util/AnnotationsManager.java | 359 +++++++++++++++++++ .../org/apache/bval/jsr/util/ClassHelper.java | 10 +- ...ementNodeBuilderCustomizableContextImpl.java | 77 ++++ ...nerElementNodeBuilderDefinedContextImpl.java | 65 ++++ .../ContainerElementNodeContextBuilderImpl.java | 85 +++++ .../main/java/org/apache/bval/jsr/util/IOs.java | 11 +- .../java/org/apache/bval/jsr/util/LRUCache.java | 41 +++ .../LeafNodeBuilderCustomizableContextImpl.java | 23 +- .../java/org/apache/bval/jsr/util/Methods.java | 45 +++ .../NodeBuilderCustomizableContextImpl.java | 55 ++- .../jsr/util/NodeBuilderDefinedContextImpl.java | 32 +- .../bval/jsr/util/NodeContextBuilderImpl.java | 52 +-- .../java/org/apache/bval/jsr/util/NodeImpl.java | 118 +++--- .../java/org/apache/bval/jsr/util/PathImpl.java | 91 +++-- .../apache/bval/jsr/util/PathNavigation.java | 102 ++++-- .../java/org/apache/bval/jsr/util/Proxies.java | 2 +- .../apache/bval/jsr/util/ToUnmodifiable.java | 34 ++ 27 files changed, 1503 insertions(+), 351 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/BValVersion.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/BValVersion.java b/bval-core/src/main/java/org/apache/bval/util/BValVersion.java index b0c451f..13d1fa3 100644 --- a/bval-core/src/main/java/org/apache/bval/util/BValVersion.java +++ b/bval-core/src/main/java/org/apache/bval/util/BValVersion.java @@ -54,21 +54,17 @@ public class BValVersion { static { Properties revisionProps = new Properties(); - try { - InputStream in = BValVersion.class.getResourceAsStream("/META-INF/org.apache.bval.revision.properties"); + try (InputStream in = BValVersion.class.getResourceAsStream("/META-INF/org.apache.bval.revision.properties")) { if (in != null) { - try { - revisionProps.load(in); - } finally { - in.close(); - } + revisionProps.load(in); } } catch (IOException ioe) { } String vers = revisionProps.getProperty("project.version"); - if (vers == null || "".equals(vers.trim())) + if (vers == null || "".equals(vers.trim())) { vers = "0.0.0"; + } VERSION_NUMBER = vers; StringTokenizer tok = new StringTokenizer(VERSION_NUMBER, ".-"); http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/Exceptions.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/Exceptions.java b/bval-core/src/main/java/org/apache/bval/util/Exceptions.java new file mode 100644 index 0000000..9487cde --- /dev/null +++ b/bval-core/src/main/java/org/apache/bval/util/Exceptions.java @@ -0,0 +1,125 @@ +/* + * 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.bval.util; + +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * Utility class for the creation and throwing of Exceptions. + */ +public class Exceptions { + + public static <E extends Exception> E create(Function<? super String, ? extends E> fn, String format, + Object... args) { + return create(fn, () -> String.format(format, args)); + } + + public static <E extends Exception, C extends Throwable> E create( + BiFunction<? super String, ? super C, ? extends E> fn, C cause, String format, Object... args) { + return create(fn, cause, () -> String.format(format, args)); + } + + public static <E extends Exception> E create(Function<? super String, ? extends E> fn, Supplier<String> message) { + return elideStackTrace(fn.apply(message.get())); + } + + public static <E extends Exception, C extends Throwable> E create( + BiFunction<? super String, ? super C, ? extends E> fn, C cause, Supplier<String> message) { + return elideStackTrace(fn.apply(message.get(), cause)); + } + + public static <E extends Exception, R> R raise(Function<? super String, ? extends E> fn, String format, + Object... args) throws E { + throw create(fn, format, args); + } + + public static <E extends Exception> void raiseIf(boolean condition, Function<? super String, ? extends E> fn, + String format, Object... args) throws E { + if (condition) { + raise(fn, format, args); + } + } + + public static <E extends Exception> void raiseUnless(boolean condition, Function<? super String, ? extends E> fn, + String format, Object... args) throws E { + raiseIf(!condition, fn, format, args); + } + + public static <E extends Exception, R> R raise(Function<? super String, ? extends E> fn, Supplier<String> message) + throws E { + throw create(fn, message); + } + + public static <E extends Exception> void raiseIf(boolean condition, Function<? super String, ? extends E> fn, + Supplier<String> message) throws E { + if (condition) { + raise(fn, message); + } + } + + public static <E extends Exception> void raiseUnless(boolean condition, Function<? super String, ? extends E> fn, + Supplier<String> message) throws E { + raiseIf(!condition, fn, message); + } + + public static <E extends Exception, C extends Throwable, R> R raise( + BiFunction<? super String, ? super C, ? extends E> fn, C cause, String format, Object... args) throws E { + throw create(fn, cause, format, args); + } + + public static <E extends Exception, C extends Throwable> void raiseIf(boolean condition, + BiFunction<? super String, ? super C, ? extends E> fn, C cause, String format, Object... args) throws E { + if (condition) { + raise(fn, cause, format, args); + } + } + + public static <E extends Exception, C extends Throwable> void raiseUnless(boolean condition, + BiFunction<? super String, ? super C, ? extends E> fn, C cause, String format, Object... args) throws E { + raiseIf(!condition, fn, cause, format, args); + } + + public static <E extends Exception, C extends Throwable, R> R raise( + BiFunction<? super String, ? super C, ? extends E> fn, C cause, Supplier<String> message) throws E { + throw create(fn, cause, message); + } + + public static <E extends Exception, C extends Throwable> void raiseIf(boolean condition, + BiFunction<? super String, ? super C, ? extends E> fn, C cause, Supplier<String> message) throws E { + if (condition) { + raise(fn, cause, message); + } + } + + public static <E extends Exception, C extends Throwable> void raiseUnless(boolean condition, + BiFunction<? super String, ? super C, ? extends E> fn, C cause, Supplier<String> message) throws E { + raiseIf(!condition, fn, cause, message); + } + + private static <T extends Throwable> T elideStackTrace(T t) { + final StackTraceElement[] stackTrace = t.fillInStackTrace().getStackTrace(); + t.setStackTrace(Stream.of(stackTrace).filter(e -> !Exceptions.class.getName().equals(e.getClassName())) + .toArray(StackTraceElement[]::new)); + return t; + } + + private Exceptions() { + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/Lazy.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/Lazy.java b/bval-core/src/main/java/org/apache/bval/util/Lazy.java new file mode 100644 index 0000000..4796de3 --- /dev/null +++ b/bval-core/src/main/java/org/apache/bval/util/Lazy.java @@ -0,0 +1,62 @@ +/* + * 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.bval.util; + +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * @since 2.0 + * + * @param <T> + */ +public class Lazy<T> implements Supplier<T> { + private T value; + private volatile Supplier<T> init; + + public Lazy(Supplier<T> init) { + reset(init); + } + + public Lazy<T> reset(Supplier<T> init) { + this.init = Validate.notNull(init); + return this; + } + + @Override + public T get() { + if (init != null) { + synchronized (this) { + if (init != null) { + value = init.get(); + init = null; + } + } + } + return value; + } + + public Optional<T> optional() { + return Optional.ofNullable(value); + } + + public <U> Consumer<U> consumer(BiConsumer<? super T, ? super U> delegate) { + return u -> delegate.accept(get(), u); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/LazyInt.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/LazyInt.java b/bval-core/src/main/java/org/apache/bval/util/LazyInt.java new file mode 100644 index 0000000..44e09dd --- /dev/null +++ b/bval-core/src/main/java/org/apache/bval/util/LazyInt.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.bval.util; + +import java.util.OptionalInt; +import java.util.function.IntSupplier; + +/** + * @since 2.0 + */ +public class LazyInt implements IntSupplier { + private int value; + private IntSupplier init; + + public LazyInt(IntSupplier init) { + this.init = Validate.notNull(init); + } + + @Override + public int getAsInt() { + if (init != null) { + synchronized (this) { + if (init != null) { + value = init.getAsInt(); + init = null; + } + } + } + return value; + } + + public synchronized OptionalInt optional() { + return init == null ? OptionalInt.of(value) : OptionalInt.empty(); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java b/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java index 0464eeb..b7f2fac 100644 --- a/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java +++ b/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java @@ -18,6 +18,8 @@ package org.apache.bval.util; import java.lang.annotation.Annotation; import java.lang.reflect.Array; +import java.util.function.Predicate; +import java.util.stream.Stream; public final class ObjectUtils { public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0]; @@ -44,7 +46,7 @@ public final class ObjectUtils { * @return {@code object} if it is not {@code null}, defaultValue otherwise */ public static <T> T defaultIfNull(final T object, final T defaultValue) { - return object != null ? object : defaultValue; + return object == null ? defaultValue : object; } public static <T> boolean isNotEmpty(final T[] array) { @@ -68,29 +70,19 @@ public final class ObjectUtils { if (array == null) { return false; } - for (Object o : array) { - if (o.equals(objectToFind)) { - return true; - } - } - return false; + return Stream.of(array).anyMatch(Predicate.isEqual(objectToFind)); } public static <T> T[] arrayAdd(T[] array, T objectToAdd) { - Class<?> type; - if (array != null) { - type = array.getClass().getComponentType(); - } else if (objectToAdd != null) { - type = objectToAdd.getClass(); - } else { + if (array == null && objectToAdd == null) { throw new IllegalArgumentException("Arguments cannot both be null"); } final int arrayLength = Array.getLength(array); + @SuppressWarnings("unchecked") T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(), arrayLength + 1); System.arraycopy(array, 0, newArray, 0, arrayLength); newArray[newArray.length - 1] = objectToAdd; return newArray; - } } http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/ObjectWrapper.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/ObjectWrapper.java b/bval-core/src/main/java/org/apache/bval/util/ObjectWrapper.java new file mode 100644 index 0000000..8483745 --- /dev/null +++ b/bval-core/src/main/java/org/apache/bval/util/ObjectWrapper.java @@ -0,0 +1,50 @@ +/* + * 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.bval.util; + +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class ObjectWrapper<T> implements Consumer<T>, Supplier<T> { + private T value; + + public ObjectWrapper() { + this(null); + } + + public ObjectWrapper(T value) { + super(); + this.value = value; + } + + @Override + public void accept(T value) { + this.value = value; + } + + @Override + public T get() { + return value; + } + + public Optional<T> optional() { + return Optional.ofNullable(value); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/StringUtils.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/StringUtils.java b/bval-core/src/main/java/org/apache/bval/util/StringUtils.java index f7add27..6b9c25d 100644 --- a/bval-core/src/main/java/org/apache/bval/util/StringUtils.java +++ b/bval-core/src/main/java/org/apache/bval/util/StringUtils.java @@ -17,8 +17,6 @@ package org.apache.bval.util; import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; import java.util.List; public final class StringUtils { @@ -93,49 +91,24 @@ public final class StringUtils { return true; } - public static String join(Collection<?> values, String joinToken) { - if (values == null) { - return null; - } - if (values.size() == 0) { - return ""; - } - if (values.size() == 1) { - return values.iterator().next().toString(); - } - if (joinToken == null) { - joinToken = "null"; // backward compat with commons-lang StringUtils... - } - - StringBuilder sb = new StringBuilder(values.size() * (16 + joinToken.length())); - Iterator<?> it = values.iterator(); - sb.append(it.next()); - while (it.hasNext()) { - sb.append(joinToken).append(it.next()); - } - return sb.toString(); - } - - public static String joinArray(Object[] values, String joinToken) { - if (values == null) { - return null; - } - if (values.length == 0) { - return ""; - } - if (values.length == 1) { - return values[0].toString(); - } - if (joinToken == null) { - joinToken = "null"; // backward compat with commons-lang StringUtils... - } - - StringBuilder sb = new StringBuilder(values.length * (16 + joinToken.length())); - sb.append(values[0]); - for (int i = 1; i < values.length; i++) { - sb.append(joinToken).append(values[i]); - } - return sb.toString(); + /** + * Taken from commons-lang3. + * <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p> + * + * <pre> + * StringUtils.isNotBlank(null) = false + * StringUtils.isNotBlank("") = false + * StringUtils.isNotBlank(" ") = false + * StringUtils.isNotBlank("bob") = true + * StringUtils.isNotBlank(" bob ") = true + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is + * not empty and not null and not whitespace + */ + public static boolean isNotBlank(final CharSequence cs) { + return !isBlank(cs); } /** @@ -149,12 +122,12 @@ public final class StringUtils { * <p>Splits the provided text into an array, separator is whitespace. */ public static String[] split(String str, Character token) { - if (str == null || str.length() == 0) { + if (str == null || str.isEmpty()) { return ObjectUtils.EMPTY_STRING_ARRAY; } // split on token - List<String> ret = new ArrayList<String>(); + List<String> ret = new ArrayList<>(); StringBuilder sb = new StringBuilder(str.length()); for (int pos = 0; pos < str.length(); pos++) { char c = str.charAt(pos); http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/Validate.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/Validate.java b/bval-core/src/main/java/org/apache/bval/util/Validate.java index f0e0611..042dc1b 100644 --- a/bval-core/src/main/java/org/apache/bval/util/Validate.java +++ b/bval-core/src/main/java/org/apache/bval/util/Validate.java @@ -16,9 +16,10 @@ */ package org.apache.bval.util; +import java.util.function.Function; + /** - * Some used Validate from commons. - * + * Some used validations from commons. */ public final class Validate { private Validate() { @@ -29,16 +30,30 @@ public final class Validate { } public static <T> T notNull(final T object, final String message, final Object... values) { - if (object == null) { - throw new NullPointerException(String.format(message, values)); - } + return notNull(object, NullPointerException::new, message, values); + } + + public static <E extends Exception, T> T notNull(final T object, Function<? super String, ? extends E> fn, + final String message, final Object... values) throws E { + Exceptions.raiseIf(object == null, fn, message, values); return object; } public static void isTrue(final boolean expression, final String message, final Object... values) { - if (expression == false) { - throw new IllegalArgumentException(String.format(message, values)); + Exceptions.raiseUnless(expression, IllegalArgumentException::new, message, values); + } + + public static <T> T[] noNullElements(final T[] array, final String message, final Object... values) { + Validate.notNull(array); + + for (int i = 0; i < array.length; i++) { + Exceptions.raiseIf(array[i] == null, IllegalArgumentException::new, message, + ObjectUtils.arrayAdd(values, Integer.valueOf(i))); } + return array; } + public static void validState(final boolean expression, final String message, final Object... values) { + Exceptions.raiseUnless(expression, IllegalStateException::new, message, values); + } } http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java b/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java index 674cf94..221a277 100644 --- a/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java +++ b/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java @@ -24,8 +24,16 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; import org.apache.commons.weaver.privilizer.Privilizing; @@ -33,36 +41,44 @@ import org.apache.commons.weaver.privilizer.Privilizing; * Security-agnostic "blueprint" class for reflection-related operations. Intended for use by Apache BVal code. */ public class Reflection { + /** + * Inclusivity literals for {@link #hierarchy(Class, Interfaces)}. + * Taken from commons-lang3. + */ + public enum Interfaces { + INCLUDE, EXCLUDE + } + private static final Object[][] NATIVE_CODES = new Object[][]{ - {byte.class, "byte", "B"}, - {char.class, "char", "C"}, - {double.class, "double", "D"}, - {float.class, "float", "F"}, - {int.class, "int", "I"}, - {long.class, "long", "J"}, - {short.class, "short", "S"}, - {boolean.class, "boolean", "Z"}, - {void.class, "void", "V"} + { byte.class, "byte", "B" }, + { char.class, "char", "C" }, + { double.class, "double", "D" }, + { float.class, "float", "F" }, + { int.class, "int", "I" }, + { long.class, "long", "J" }, + { short.class, "short", "S" }, + { boolean.class, "boolean", "Z" }, + { void.class, "void", "V" } }; /** * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}. */ - private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP = new HashMap<Class<?>, Class<?>>(); + private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP; static { - PRIMITIVE_WRAPPER_MAP.put(Boolean.TYPE, Boolean.class); - PRIMITIVE_WRAPPER_MAP.put(Byte.TYPE, Byte.class); - PRIMITIVE_WRAPPER_MAP.put(Character.TYPE, Character.class); - PRIMITIVE_WRAPPER_MAP.put(Short.TYPE, Short.class); - PRIMITIVE_WRAPPER_MAP.put(Integer.TYPE, Integer.class); - PRIMITIVE_WRAPPER_MAP.put(Long.TYPE, Long.class); - PRIMITIVE_WRAPPER_MAP.put(Double.TYPE, Double.class); - PRIMITIVE_WRAPPER_MAP.put(Float.TYPE, Float.class); - PRIMITIVE_WRAPPER_MAP.put(Void.TYPE, Void.TYPE); + final Map<Class<?>, Class<?>> m = new HashMap<>(); + m.put(Boolean.TYPE, Boolean.class); + m.put(Byte.TYPE, Byte.class); + m.put(Character.TYPE, Character.class); + m.put(Short.TYPE, Short.class); + m.put(Integer.TYPE, Integer.class); + m.put(Long.TYPE, Long.class); + m.put(Double.TYPE, Double.class); + m.put(Float.TYPE, Float.class); + m.put(Void.TYPE, Void.TYPE); + PRIMITIVE_WRAPPER_MAP = Collections.unmodifiableMap(m); } - - /** * <p>Converts the specified primitive Class object to its corresponding * wrapper Class object.</p> @@ -129,8 +145,7 @@ public class Reflection { return cl == null ? clazz.getClassLoader() : cl; } - public static Class<?> toClass(String className) throws ClassNotFoundException - { + public static Class<?> toClass(String className) throws ClassNotFoundException { ClassLoader cl = getClassLoader(Reflection.class); return toClass(className, cl); } @@ -142,7 +157,7 @@ public class Reflection { * * @throws RuntimeException on load error */ - public static Class toClass(String className, ClassLoader loader) throws ClassNotFoundException { + public static Class<?> toClass(String className, ClassLoader loader) throws ClassNotFoundException { return toClass(className, false, loader); } @@ -153,7 +168,7 @@ public class Reflection { * * @throws RuntimeException on load error */ - public static Class toClass(String className, boolean resolve, ClassLoader loader) throws ClassNotFoundException { + public static Class<?> toClass(String className, boolean resolve, ClassLoader loader) throws ClassNotFoundException { if (className == null) { throw new NullPointerException("className == null"); } @@ -171,7 +186,7 @@ public class Reflection { for (int i = 0; !primitive && (i < NATIVE_CODES.length); i++) { if (NATIVE_CODES[i][1].equals(className)) { if (dims == 0) { - return (Class) NATIVE_CODES[i][0]; + return (Class<?>) NATIVE_CODES[i][0]; } className = (String) NATIVE_CODES[i][2]; primitive = true; @@ -296,6 +311,22 @@ public class Reflection { } /** + * Perform a search against the class hierarchy. + * @param clazz + * @param search + * @return T or {@code null} + */ + public static <T> T find(final Class<?> clazz, Function<Class<?>, T> search) { + for (Class<?> t : hierarchy(clazz, Interfaces.INCLUDE)) { + final T value = search.apply(t); + if (value != null) { + return value; + } + } + return null; + } + + /** * Construct a new instance of {@code cls} using its default constructor. * @param cls * @return T @@ -333,4 +364,94 @@ public class Reflection { return true; } + /** + * Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order. + * Taken from commons-lang3. + * + * @param type the type to get the class hierarchy from + * @param interfacesBehavior switch indicating whether to include or exclude interfaces + * @return Iterable an Iterable over the class hierarchy of the given class + */ + public static Iterable<Class<?>> hierarchy(final Class<?> type, final Interfaces interfacesBehavior) { + if (type == null) { + return Collections.emptySet(); + } + final Iterable<Class<?>> classes = new Iterable<Class<?>>() { + + @Override + public Iterator<Class<?>> iterator() { + return new Iterator<Class<?>>() { + Optional<Class<?>> next; + { + next = Optional.of(type); + } + + @Override + public boolean hasNext() { + return next.isPresent(); + } + + @Override + public Class<?> next() { + final Class<?> result = next.orElseThrow(NoSuchElementException::new); + next = Optional.ofNullable(result.getSuperclass()); + return result; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + if (interfacesBehavior != Interfaces.INCLUDE) { + return classes; + } + return new Iterable<Class<?>>() { + + @Override + public Iterator<Class<?>> iterator() { + final Set<Class<?>> seenInterfaces = new HashSet<Class<?>>(); + final Iterator<Class<?>> wrapped = classes.iterator(); + + return new Iterator<Class<?>>() { + Iterator<Class<?>> interfaces = Collections.emptyIterator(); + + @Override + public boolean hasNext() { + return interfaces.hasNext() || wrapped.hasNext(); + } + + @Override + public Class<?> next() { + if (interfaces.hasNext()) { + final Class<?> nextInterface = interfaces.next(); + seenInterfaces.add(nextInterface); + return nextInterface; + } + final Class<?> nextSuperclass = wrapped.next(); + final Set<Class<?>> currentInterfaces = new LinkedHashSet<>(); + walkInterfaces(currentInterfaces, nextSuperclass); + interfaces = currentInterfaces.iterator(); + return nextSuperclass; + } + + private void walkInterfaces(final Set<Class<?>> addTo, final Class<?> c) { + for (final Class<?> iface : c.getInterfaces()) { + if (!seenInterfaces.contains(iface)) { + addTo.add(iface); + } + walkInterfaces(addTo, iface); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } } http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java ---------------------------------------------------------------------- diff --git a/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java b/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java index 4734906..b8b044d 100644 --- a/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java +++ b/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java @@ -28,8 +28,11 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; +import org.apache.bval.util.ObjectUtils; import org.apache.bval.util.Validate; /** @@ -148,13 +151,7 @@ public class TypeUtils { */ @Override public int hashCode() { - int result = 71 << 4; - result |= raw.hashCode(); - result <<= 4; - result |= useOwner == null ? 0 : useOwner.hashCode(); - result <<= 8; - result |= Arrays.hashCode(typeArguments); - return result; + return Objects.hash(raw, useOwner, typeArguments); } } @@ -162,7 +159,8 @@ public class TypeUtils { * WildcardType implementation class. */ private static final class WildcardTypeImpl implements WildcardType { - private static final Type[] EMPTY_BOUNDS = new Type[0]; + private static final Type[] EMPTY_UPPER_BOUNDS = { Object.class }; + private static final Type[] EMPTY_LOWER_BOUNDS = new Type[0]; private final Type[] upperBounds; private final Type[] lowerBounds; @@ -173,8 +171,8 @@ public class TypeUtils { * @param lowerBounds of this type */ private WildcardTypeImpl(final Type[] upperBounds, final Type[] lowerBounds) { - this.upperBounds = upperBounds != null ? upperBounds : EMPTY_BOUNDS; - this.lowerBounds = lowerBounds != null ? lowerBounds : EMPTY_BOUNDS; + this.upperBounds = ObjectUtils.isEmpty(upperBounds) ? EMPTY_UPPER_BOUNDS : upperBounds; + this.lowerBounds = lowerBounds == null ? EMPTY_LOWER_BOUNDS : lowerBounds; } /** @@ -214,11 +212,7 @@ public class TypeUtils { */ @Override public int hashCode() { - int result = 73 << 8; - result |= Arrays.hashCode(upperBounds); - result <<= 8; - result |= Arrays.hashCode(lowerBounds); - return result; + return Objects.hash(upperBounds, lowerBounds); } } @@ -320,19 +314,13 @@ public class TypeUtils { if (type instanceof TypeVariable<?>) { // if any of the bounds are assignable to the class, then the // type is assignable to the class. - for (final Type bound : ((TypeVariable<?>) type).getBounds()) { - if (isAssignable(bound, toClass)) { - return true; - } - } - - return false; + return Stream.of(((TypeVariable<?>) type).getBounds()).anyMatch(bound -> isAssignable(bound, toClass)); } // the only classes to which a generic array type can be assigned // are class Object and array classes if (type instanceof GenericArrayType) { - return toClass.equals(Object.class) + return Object.class.equals(toClass) || toClass.isArray() && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass .getComponentType()); @@ -554,25 +542,15 @@ public class TypeUtils { if (type instanceof WildcardType) { // so long as one of the upper bounds is assignable, it's good - for (final Type bound : getImplicitUpperBounds((WildcardType) type)) { - if (isAssignable(bound, toGenericArrayType)) { - return true; - } - } - - return false; + return Stream.of(getImplicitUpperBounds((WildcardType) type)) + .anyMatch(bound -> isAssignable(bound, toGenericArrayType)); } if (type instanceof TypeVariable<?>) { // probably should remove the following logic and just return false. // type variables cannot specify arrays as bounds. - for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) { - if (isAssignable(bound, toGenericArrayType)) { - return true; - } - } - - return false; + return Stream.of(getImplicitBounds((TypeVariable<?>) type)) + .anyMatch(bound -> isAssignable(bound, toGenericArrayType)); } if (type instanceof ParameterizedType) { @@ -871,8 +849,7 @@ public class TypeUtils { getRawType(parameterizedOwnerType), subtypeVarAssigns); } else { // no owner, prep the type variable assignments map - typeVarAssigns = subtypeVarAssigns == null ? new HashMap<TypeVariable<?>, Type>() - : new HashMap<TypeVariable<?>, Type>(subtypeVarAssigns); + typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns); } // get the subject parameterized type's arguments @@ -917,7 +894,7 @@ public class TypeUtils { if (toClass.isPrimitive()) { // dealing with widening here. No type arguments to be // harvested with these two types. - return new HashMap<TypeVariable<?>, Type>(); + return new HashMap<>(); } // work with wrapper the wrapper class instead of the primitive @@ -925,8 +902,8 @@ public class TypeUtils { } // create a copy of the incoming map, or an empty one if it's null - final HashMap<TypeVariable<?>, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<TypeVariable<?>, Type>() - : new HashMap<TypeVariable<?>, Type>(subtypeVarAssigns); + final Map<TypeVariable<?>, Type> typeVarAssigns = + subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns); // has target class been reached? if (toClass.equals(cls)) { @@ -1030,7 +1007,7 @@ public class TypeUtils { return bounds; } - final Set<Type> types = new HashSet<Type>(bounds.length); + final Set<Type> types = new HashSet<>(bounds.length); for (final Type type1 : bounds) { boolean subtypeFound = false; @@ -1243,7 +1220,7 @@ public class TypeUtils { if (p.getOwnerType() == null) { parameterizedTypeArguments = typeArguments; } else { - parameterizedTypeArguments = new HashMap<TypeVariable<?>, Type>(typeArguments); + parameterizedTypeArguments = new HashMap<>(typeArguments); parameterizedTypeArguments.putAll(TypeUtils.getTypeArguments(p)); } final Type[] args = p.getActualTypeArguments(); http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java new file mode 100644 index 0000000..b53b513 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java @@ -0,0 +1,359 @@ +/* + * 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.bval.jsr.util; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.Constraint; +import javax.validation.ConstraintDefinitionException; +import javax.validation.ConstraintTarget; +import javax.validation.OverridesAttribute; +import javax.validation.Payload; +import javax.validation.ValidationException; +import javax.validation.constraintvalidation.ValidationTarget; + +import org.apache.bval.jsr.ApacheValidatorFactory; +import org.apache.bval.jsr.ConfigurationImpl; +import org.apache.bval.jsr.ConstraintAnnotationAttributes; +import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo; +import org.apache.bval.jsr.groups.Group; +import org.apache.bval.jsr.groups.Groups; +import org.apache.bval.jsr.groups.GroupsComputer; +import org.apache.bval.jsr.metadata.Metas; +import org.apache.bval.jsr.xml.AnnotationProxyBuilder; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; +import org.apache.bval.util.StringUtils; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.Reflection; +import org.apache.commons.weaver.privilizer.Privilizing; +import org.apache.commons.weaver.privilizer.Privilizing.CallTo; + +/** + * Manages (constraint) annotations according to the BV spec. + * + * @since 2.0 + */ +@Privilizing(@CallTo(Reflection.class)) +public class AnnotationsManager { + private static final class OverriddenAnnotationSpecifier { + final Class<? extends Annotation> annotationType; + final boolean impliesSingleComposingConstraint; + final int constraintIndex; + + OverriddenAnnotationSpecifier(OverridesAttribute annotation) { + this(annotation.constraint(), annotation.constraintIndex()); + } + + OverriddenAnnotationSpecifier(Class<? extends Annotation> annotationType, int constraintIndex) { + super(); + this.annotationType = annotationType; + this.impliesSingleComposingConstraint = constraintIndex < 0; + this.constraintIndex = Math.max(constraintIndex, 0); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(getClass())) { + return false; + } + final OverriddenAnnotationSpecifier other = (OverriddenAnnotationSpecifier) obj; + return Objects.equals(annotationType, other.annotationType) && constraintIndex == other.constraintIndex; + } + + @Override + public int hashCode() { + return Objects.hash(annotationType, constraintIndex); + } + } + + private static class Composition { + final Lazy<Map<OverriddenAnnotationSpecifier, Map<String, String>>> overrides = new Lazy<>(HashMap::new); + final Annotation[] components; + + Composition(Class<? extends Annotation> annotationType) { + // TODO detect recursion + components = getDeclaredConstraints(annotationType); + + if (!isComposed()) { + return; + } + final Map<Class<? extends Annotation>, AtomicInteger> constraintCounts = new HashMap<>(); + for (Annotation a : components) { + constraintCounts.computeIfAbsent(a.annotationType(), k -> new AtomicInteger()).incrementAndGet(); + } + // create a map of overridden constraints to overridden attributes: + for (Method m : Reflection.getDeclaredMethods(annotationType)) { + final String from = m.getName(); + for (OverridesAttribute overridesAttribute : m.getDeclaredAnnotationsByType(OverridesAttribute.class)) { + final String to = + Optional.of(overridesAttribute.name()).filter(StringUtils::isNotBlank).orElse(from); + + final OverriddenAnnotationSpecifier spec = new OverriddenAnnotationSpecifier(overridesAttribute); + final int count = constraintCounts.get(spec.annotationType).get(); + + if (spec.impliesSingleComposingConstraint) { + Exceptions.raiseUnless(count == 1, ConstraintDefinitionException::new, + "Expected a single composing %s constraint", spec.annotationType); + } else { + Exceptions.raiseUnless(count > spec.constraintIndex, ConstraintDefinitionException::new, + "Expected at least %s composing %s constraints", spec.constraintIndex + 1, + spec.annotationType); + } + final Map<String, String> attributeMapping = + overrides.get().computeIfAbsent(spec, k -> new HashMap<>()); + + Exceptions.raiseIf(attributeMapping.containsKey(to), ConstraintDefinitionException::new, + "Attempt to override %s#%s() index %d from multiple sources", overridesAttribute.constraint(), + to, overridesAttribute.constraintIndex()); + + attributeMapping.put(to, from); + } + } + } + + boolean isComposed() { + return components.length > 0; + } + + Annotation[] getComponents(Annotation source) { + final Class<?>[] groups = + ConstraintAnnotationAttributes.GROUPS.analyze(source.annotationType()).read(source); + + final Class<? extends Payload>[] payload = + ConstraintAnnotationAttributes.PAYLOAD.analyze(source.annotationType()).read(source); + + final Optional<ConstraintTarget> constraintTarget = + Optional.of(source.annotationType()).map(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO::analyze) + .filter(ConstraintAnnotationAttributes.Worker::isValid).map(w -> w.read(source)); + + final Map<Class<? extends Annotation>, AtomicInteger> constraintCounts = new HashMap<>(); + + return Stream.of(components).map(c -> { + final int index = + constraintCounts.computeIfAbsent(c.annotationType(), k -> new AtomicInteger()).getAndIncrement(); + + final AnnotationProxyBuilder<Annotation> proxyBuilder = new AnnotationProxyBuilder<>(c); + + proxyBuilder.setGroups(groups); + proxyBuilder.setPayload(payload); + constraintTarget.ifPresent(proxyBuilder::setValidationAppliesTo); + + overrides.optional().map(o -> o.get(new OverriddenAnnotationSpecifier(c.annotationType(), index))) + .ifPresent(m -> { + final Map<String, Object> sourceAttributes = readAttributes(source); + m.forEach((k, v) -> proxyBuilder.setValue(k, sourceAttributes.get(v))); + }); + return proxyBuilder.isChanged() ? proxyBuilder.createAnnotation() : c; + }).toArray(Annotation[]::new); + } + } + + public static Map<String, Object> readAttributes(Annotation a) { + final Lazy<Map<String, Object>> result = new Lazy<>(LinkedHashMap::new); + + Stream.of(Reflection.getDeclaredMethods(a.annotationType())).filter(m -> m.getParameterCount() == 0) + .forEach(m -> { + final boolean mustUnset = Reflection.setAccessible(m, true); + try { + result.get().put(m.getName(), m.invoke(a)); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + Exceptions.raise(ValidationException::new, e, "Caught exception reading attributes of %s", a); + } finally { + if (mustUnset) { + Reflection.setAccessible(m, false); + } + } + }); + return result.optional().map(Collections::unmodifiableMap).orElseGet(Collections::emptyMap); + } + + /** + * Meta-annotation aware. + * + * @param e + * @param t + * @return {@code boolean} + * @see AnnotatedElement#isAnnotationPresent(Class) + */ + public static boolean isAnnotationPresent(AnnotatedElement e, Class<? extends Annotation> t) { + if (e.isAnnotationPresent(t)) { + return true; + } + return Stream.of(e.getAnnotations()).map(Annotation::annotationType).anyMatch(a -> isAnnotationPresent(a, t)); + } + + /** + * Get declared annotations with a particular meta-annotation. + * + * @param e + * @param meta + * @return {@link Annotation}[] + */ + public static Annotation[] getDeclared(AnnotatedElement e, Class<? extends Annotation> meta) { + return Stream.of(e.getDeclaredAnnotations()).filter(ann -> isAnnotationPresent(ann.annotationType(), meta)) + .toArray(Annotation[]::new); + } + + /** + * Accounts for {@link Constraint} meta-annotation AND {@link Repeatable} + * constraint annotations. + * + * @param meta + * @return Annotation[] + */ + public static Annotation[] getDeclaredConstraints(Metas<?> meta) { + final Annotation[] result = getDeclaredConstraints(meta.getHost()); + final Class<?> dc = meta.getDeclaringClass(); + if (dc.isInterface()) { + final GroupsComputer groupsComputer = new GroupsComputer(); + // ensure interface group is implied by Default group: + Stream.of(result).map(c -> { + final Groups groups = groupsComputer + .computeGroups(ConstraintAnnotationAttributes.GROUPS.analyze(c.annotationType()).read(c)); + if (groups.getGroups().stream().anyMatch(Group::isDefault)) { + final Set<Class<?>> groupClasses = groups.getGroups().stream().map(Group::getGroup) + .collect(Collectors.toCollection(LinkedHashSet::new)); + if (groupClasses.add(dc)) { + final AnnotationProxyBuilder<?> proxyBuilder = new AnnotationProxyBuilder<>(c); + proxyBuilder.setGroups(groupClasses.toArray(new Class[groupClasses.size()])); + return proxyBuilder.createAnnotation(); + } + } + return c; + }).toArray(n -> result); + } + return result; + } + + private static Annotation[] getDeclaredConstraints(AnnotatedElement e) { + return Stream.of(e.getDeclaredAnnotations()).flatMap((Function<Annotation, Stream<Annotation>>) a -> { + final ConstraintAnnotationAttributes.Worker<? extends Annotation> analyzer = + ConstraintAnnotationAttributes.VALUE.analyze(a.annotationType()); + if (analyzer.isValid()) { + return Stream.of(analyzer.<Annotation[]> read(a)); + } + return Stream.of(a); + }).filter(a -> a.annotationType().isAnnotationPresent(Constraint.class)).toArray(Annotation[]::new); + } + + public static boolean declaresAttribute(Class<? extends Annotation> annotationType, String name) { + try { + annotationType.getDeclaredMethod(name); + return true; + } catch (NoSuchMethodException | SecurityException e) { + return false; + } + } + + private final ApacheValidatorFactory validatorFactory; + private final LRUCache<Class<? extends Annotation>, Composition> compositions; + + public AnnotationsManager(ApacheValidatorFactory validatorFactory) { + super(); + this.validatorFactory = Validate.notNull(validatorFactory); + final String cacheSize = + validatorFactory.getProperties().get(ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE); + try { + compositions = new LRUCache<>(Integer.parseInt(cacheSize)); + } catch (NumberFormatException e) { + throw Exceptions.create(IllegalStateException::new, e, + "Cannot parse value %s for configuration property %s", cacheSize, + ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE); + } + } + + /** + * Retrieve the composing constraints for the specified constraint + * {@link Annotation}. + * + * @param a + * @return {@link Annotation}[] + */ + public Annotation[] getComposingConstraints(Annotation a) { + return getComposition(a.annotationType()).getComponents(a); + } + + /** + * Learn whether {@code a} is composed. + * + * @param a + * @return {@code boolean} + */ + public boolean isComposed(Annotation a) { + return getComposition(a.annotationType()).isComposed(); + } + + /** + * Get the supported targets for {@code constraintType}. + * + * @param constraintType + * @return {@link Set} of {@link ValidationTarget} + */ + public <A extends Annotation> Set<ValidationTarget> supportedTargets(Class<A> constraintType) { + final Set<ConstraintValidatorInfo<A>> constraintValidatorInfo = + validatorFactory.getConstraintsCache().getConstraintValidatorInfo(constraintType); + final Stream<Set<ValidationTarget>> s; + if (constraintValidatorInfo.isEmpty()) { + // must be for composition: + s = Stream.of(new Composition(constraintType).components).map(Annotation::annotationType) + .map(this::supportedTargets); + } else { + s = constraintValidatorInfo.stream().map(ConstraintValidatorInfo::getSupportedTargets); + } + return s.flatMap(Collection::stream) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(ValidationTarget.class))); + } + + private Composition getComposition(Class<? extends Annotation> annotationType) { + return compositions.computeIfAbsent(annotationType, ct -> { + final Set<ValidationTarget> composedTargets = supportedTargets(annotationType); + final Composition result = new Composition(annotationType); + Stream.of(result.components).map(Annotation::annotationType).forEach(at -> { + final Set<ValidationTarget> composingTargets = supportedTargets(at); + Exceptions.raiseIf(Collections.disjoint(composingTargets, composedTargets), + ConstraintDefinitionException::new, + "Attempt to compose %s of %s but validator types are incompatible", annotationType.getName(), + at.getName()); + }); + return result; + }); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java index 9d3bd85..73c82a6 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java @@ -20,7 +20,11 @@ package org.apache.bval.jsr.util; import java.io.Serializable; import java.security.AccessController; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Common operations on classes that do not require an {@link AccessController}. @@ -28,6 +32,7 @@ import java.util.List; * @author Carlos Vara */ public class ClassHelper { + private static final Set<Class<?>> IGNORED_TYPES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(null,Object.class,Serializable.class,Cloneable.class))); private ClassHelper() { // No instances please @@ -42,10 +47,7 @@ public class ClassHelper { * @param clazz */ public static List<Class<?>> fillFullClassHierarchyAsList(List<Class<?>> allClasses, Class<?> clazz) { - if (clazz == null || clazz == Object.class || clazz == Serializable.class || clazz == Cloneable.class) { - return allClasses; - } - if (allClasses.contains(clazz)) { + if (IGNORED_TYPES.contains(clazz) || allClasses.contains(clazz)) { return allClasses; } allClasses.add(clazz); http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java new file mode 100644 index 0000000..c0cff10 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java @@ -0,0 +1,77 @@ +/* + * 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.bval.jsr.util; + +import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeContextBuilder; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext; + +import org.apache.bval.jsr.job.ConstraintValidatorContextImpl; + +public class ContainerElementNodeBuilderCustomizableContextImpl + implements ContainerElementNodeBuilderCustomizableContext { + private final ConstraintValidatorContextImpl<?> context; + private final String template; + private final PathImpl path; + private NodeImpl node; + + public ContainerElementNodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl<?> context, String template, + PathImpl path, String name, Class<?> containerType, Integer typeArgumentIndex) { + super(); + this.context = context; + this.path = path; + this.template = template; + this.node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex); + } + + @Override + public ContainerElementNodeContextBuilder inIterable() { + node.setInIterable(true); + return new ContainerElementNodeContextBuilderImpl(context, template, path, node); + } + + @Override + public NodeBuilderCustomizableContext addPropertyNode(String name) { + path.addNode(node); + return new NodeBuilderCustomizableContextImpl(context, template, path, name); + } + + @Override + public LeafNodeBuilderCustomizableContext addBeanNode() { + path.addNode(node); + return new LeafNodeBuilderCustomizableContextImpl(context, template, path); + } + + @Override + public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType, + Integer typeArgumentIndex) { + path.addNode(node); + node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex); + return this; + } + + @Override + public ConstraintValidatorContext addConstraintViolation() { + context.addError(template, path); + return context; + } + +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java new file mode 100644 index 0000000..6077d87 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java @@ -0,0 +1,65 @@ +/* + * 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.bval.jsr.util; + +import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderDefinedContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext; + +import org.apache.bval.jsr.job.ConstraintValidatorContextImpl; + +public class ContainerElementNodeBuilderDefinedContextImpl implements ContainerElementNodeBuilderDefinedContext { + private final ConstraintValidatorContextImpl<?> context; + private final String template; + private final PathImpl path; + + ContainerElementNodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl<?> context, String template, + PathImpl path) { + super(); + this.context = context; + this.template = template; + this.path = path; + } + + @Override + public NodeBuilderCustomizableContext addPropertyNode(String name) { + return new NodeBuilderCustomizableContextImpl(context, template, path, name); + } + + @Override + public LeafNodeBuilderCustomizableContext addBeanNode() { + return new LeafNodeBuilderCustomizableContextImpl(context, template, path); + } + + @Override + public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType, + Integer typeArgumentIndex) { + return new ContainerElementNodeBuilderCustomizableContextImpl(context, name, path, name, containerType, + typeArgumentIndex); + } + + @Override + public ConstraintValidatorContext addConstraintViolation() { + context.addError(template, path); + return context; + } + +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java new file mode 100644 index 0000000..f05ef76 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java @@ -0,0 +1,85 @@ +/* + * 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.bval.jsr.util; + +import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderDefinedContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeContextBuilder; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext; + +import org.apache.bval.jsr.job.ConstraintValidatorContextImpl; + +public class ContainerElementNodeContextBuilderImpl implements ContainerElementNodeContextBuilder { + private final ConstraintValidatorContextImpl<?> context; + private final String template; + private final PathImpl path; + private final NodeImpl node; + + ContainerElementNodeContextBuilderImpl(ConstraintValidatorContextImpl<?> context, String template, + PathImpl path, NodeImpl node) { + super(); + this.context = context; + this.template = template; + this.path = path; + this.node = node; + } + + @Override + public ContainerElementNodeBuilderDefinedContext atKey(Object key) { + node.setKey(key); + path.addNode(node); + return new ContainerElementNodeBuilderDefinedContextImpl(context, template, path); + } + + @Override + public ContainerElementNodeBuilderDefinedContext atIndex(Integer index) { + node.setIndex(index); + path.addNode(node); + return new ContainerElementNodeBuilderDefinedContextImpl(context, template, path); + } + + @Override + public NodeBuilderCustomizableContext addPropertyNode(String name) { + path.addNode(node); + return new NodeBuilderCustomizableContextImpl(context, name, path, name); + } + + @Override + public LeafNodeBuilderCustomizableContext addBeanNode() { + path.addNode(node); + return new LeafNodeBuilderCustomizableContextImpl(context, template, path); + } + + @Override + public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType, + Integer typeArgumentIndex) { + path.addNode(node); + return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType, + typeArgumentIndex); + } + + @Override + public ConstraintValidatorContext addConstraintViolation() { + context.addError(template, path); + return context; + } + +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java index 611a9d6..57f7cf4 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java @@ -33,25 +33,20 @@ public class IOs { if (stream == null) { return null; } - - // force ByteArrayOutputStream since we close the stream ATM - /*if (stream.markSupported()) { - return stream; - } else {*/ - try { + try (InputStream in = stream) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final byte[] buffer = new byte[1024]; int length; - while ((length = stream.read(buffer)) != -1) { + while ((length = in.read(buffer)) != -1) { baos.write(buffer, 0, length); } return new ByteArrayInputStream(baos.toByteArray()); } catch (final IOException e) { throw new RuntimeException(e); } - /*}*/ } + //TODO see if needed public static void closeQuietly(Closeable closeable) { if (closeable != null) { try { http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java new file mode 100644 index 0000000..48fcd7d --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java @@ -0,0 +1,41 @@ +/* + * 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.bval.jsr.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class LRUCache<K, V> extends LinkedHashMap<K, V> { + private static final long serialVersionUID = 1L; + + private final int maximumCapacity; + + public LRUCache(int maximumCapacity) { + super(16, 0.75f, true); + if (maximumCapacity < 1) { + throw new IllegalArgumentException("maximumCapacity must be > 0"); + } + this.maximumCapacity = maximumCapacity; + } + + @Override + protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { + return super.removeEldestEntry(eldest) || size() >= maximumCapacity; + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java index efa9aeb..99305be 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java @@ -18,9 +18,10 @@ */ package org.apache.bval.jsr.util; -import org.apache.bval.jsr.ConstraintValidatorContextImpl; +import org.apache.bval.jsr.job.ConstraintValidatorContextImpl; import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext; import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderDefinedContext; import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeContextBuilder; @@ -43,8 +44,7 @@ public class LeafNodeBuilderCustomizableContextImpl } @Override - public LeafNodeBuilderDefinedContext atIndex( - Integer index) { + public LeafNodeBuilderDefinedContext atIndex(Integer index) { node.setIndex(index); return definedContext; } @@ -55,16 +55,16 @@ public class LeafNodeBuilderCustomizableContextImpl } } - private final ConstraintValidatorContextImpl context; + private final ConstraintValidatorContextImpl<?> context; private final PathImpl path; private final String template; private final NodeImpl node; - public LeafNodeBuilderCustomizableContextImpl(final ConstraintValidatorContextImpl parent, String messageTemplate, - PathImpl propertyPath) { - context = parent; - template = messageTemplate; - path = propertyPath; + public LeafNodeBuilderCustomizableContextImpl(final ConstraintValidatorContextImpl<?> context, String template, + PathImpl path) { + this.context = context; + this.template = template; + this.path = path; node = new NodeImpl.BeanNodeImpl(); } @@ -81,4 +81,9 @@ public class LeafNodeBuilderCustomizableContextImpl return context; } + @Override + public LeafNodeBuilderCustomizableContext inContainer(Class<?> containerType, Integer typeArgumentIndex) { + node.inContainer(containerType, typeArgumentIndex); + return this; + } } http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java new file mode 100644 index 0000000..9f98311 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java @@ -0,0 +1,45 @@ +/* + * 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.bval.jsr.util; + +import java.beans.Introspector; +import java.lang.reflect.Method; + +import org.apache.bval.util.Validate; + +public final class Methods { + public static boolean isGetter(Method m) { + if (m.getParameterCount() > 0) { + return false; + } + // TODO look for capital letter after verb? + if (Boolean.TYPE.equals(m.getReturnType()) && m.getName().startsWith("is")) { + return true; + } + return !Void.TYPE.equals(m.getReturnType()) && m.getName().startsWith("get"); + } + + public static String propertyName(Method getter) { + Validate.isTrue(isGetter(getter), "%s is not a getter", getter); + final String name = getter.getName(); + final String suffix = name.startsWith("is") ? name.substring(2) : name.substring(3); + return Introspector.decapitalize(suffix); + } + + private Methods() { + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java index ca058fc..6ec977c 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java @@ -18,38 +18,40 @@ */ package org.apache.bval.jsr.util; -import org.apache.bval.jsr.ConstraintValidatorContextImpl; - import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext; import javax.validation.ElementKind; +import org.apache.bval.jsr.job.ConstraintValidatorContextImpl; + /** * Description: implementation of {@link javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext}.<br/> */ public final class NodeBuilderCustomizableContextImpl implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext { - private final ConstraintValidatorContextImpl parent; - private final String messageTemplate; - private final PathImpl propertyPath; + private final ConstraintValidatorContextImpl<?> context; + private final String template; + private final PathImpl path; private NodeImpl node; /** * Create a new NodeBuilderCustomizableContextImpl instance. - * @param contextImpl + * @param context * @param template * @param path * @param name */ - public NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl contextImpl, String template, PathImpl path, + public NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl<?> context, String template, PathImpl path, String name) { - parent = contextImpl; - messageTemplate = template; - propertyPath = path; + this.context = context; + this.template = template; + this.path = path; - if (propertyPath.isRootPath() || propertyPath.getLeafNode().getKind() != null) { + if (path.isRootPath() || path.getLeafNode().getKind() != null) { node = new NodeImpl.PropertyNodeImpl(name); } else { - node = propertyPath.removeLeafNode(); + node = path.removeLeafNode(); node.setName(name); node.setKind(ElementKind.PROPERTY); // enforce it } @@ -61,7 +63,7 @@ public final class NodeBuilderCustomizableContextImpl @Override public ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder inIterable() { node.setInIterable(true); - return new NodeContextBuilderImpl(parent, messageTemplate, propertyPath, node); + return new NodeContextBuilderImpl(context, template, path, node); } /** @@ -75,15 +77,15 @@ public final class NodeBuilderCustomizableContextImpl @Override public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode( String name) { - propertyPath.addNode(node); + path.addNode(node); node = new NodeImpl.PropertyNodeImpl(name); return this; } @Override public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() { - propertyPath.addNode(node); - return new LeafNodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath); + path.addNode(node); + return new LeafNodeBuilderCustomizableContextImpl(context, template, path); } /** @@ -91,10 +93,25 @@ public final class NodeBuilderCustomizableContextImpl */ @Override public ConstraintValidatorContext addConstraintViolation() { - propertyPath.addNode(node); + path.addNode(node); node = null; - parent.addError(messageTemplate, propertyPath); - return parent; + context.addError(template, path); + return context; + } + + @Override + public NodeBuilderCustomizableContext inContainer(Class<?> containerClass, Integer typeArgumentIndex) { + path.getLeafNode().inContainer(containerClass, typeArgumentIndex); + return this; + } + + @Override + public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType, + Integer typeArgumentIndex) { + path.addNode(node); + node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex); + return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType, + typeArgumentIndex); } } http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java index 5ce20b5..f695e84 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java @@ -18,18 +18,19 @@ */ package org.apache.bval.jsr.util; -import org.apache.bval.jsr.ConstraintValidatorContextImpl; - import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext; + +import org.apache.bval.jsr.job.ConstraintValidatorContextImpl; /** * Description: Implementation of {@link NodeBuilderDefinedContext}.<br/> */ public final class NodeBuilderDefinedContextImpl implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext { - private final ConstraintValidatorContextImpl parent; - private final String messageTemplate; - private final PathImpl propertyPath; + private final ConstraintValidatorContextImpl context; + private final String template; + private final PathImpl path; /** * Create a new NodeBuilderDefinedContextImpl instance. @@ -38,9 +39,9 @@ public final class NodeBuilderDefinedContextImpl * @param path */ public NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl contextImpl, String template, PathImpl path) { - parent = contextImpl; - messageTemplate = template; - propertyPath = path; + this.context = contextImpl; + this.template = template; + this.path = path; } /** @@ -54,12 +55,12 @@ public final class NodeBuilderDefinedContextImpl @Override public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode( String name) { - return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath, name); + return new NodeBuilderCustomizableContextImpl(context, template, path, name); } @Override public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() { - return new LeafNodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath); + return new LeafNodeBuilderCustomizableContextImpl(context, template, path); } /** @@ -67,7 +68,14 @@ public final class NodeBuilderDefinedContextImpl */ @Override public ConstraintValidatorContext addConstraintViolation() { - parent.addError(messageTemplate, propertyPath); - return parent; + context.addError(template, path); + return context; + } + + @Override + public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType, + Integer typeArgumentIndex) { + return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType, + typeArgumentIndex); } }
