You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by ra...@apache.org on 2018/10/12 15:00:51 UTC
svn commit: r1843674 [13/22] - in /tomee/deps/branches/bval-2: ./ bundle/
bundle/src/ bundle/src/main/ bundle/src/main/appended-resources/
bundle/src/main/appended-resources/META-INF/ bval-extras/ bval-extras/src/
bval-extras/src/main/ bval-extras/src/...
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,543 @@
+/*
+ * 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 static java.util.Comparator.comparing;
+import static java.util.Comparator.naturalOrder;
+import static java.util.Comparator.nullsFirst;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import javax.validation.ElementKind;
+import javax.validation.Path;
+import javax.validation.Path.BeanNode;
+import javax.validation.Path.ConstructorNode;
+import javax.validation.Path.ContainerElementNode;
+import javax.validation.Path.MethodNode;
+import javax.validation.Path.Node;
+import javax.validation.Path.ParameterNode;
+import javax.validation.Path.PropertyNode;
+
+import org.apache.bval.util.Comparators;
+import org.apache.bval.util.Exceptions;
+
+public abstract class NodeImpl implements Path.Node, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Comparator for any path {@link Node}. For iterable nodes with no, or {@code null}, key and index values
+ * the left operand is always treated as less than the right.
+ */
+ public static final Comparator<Path.Node> NODE_COMPARATOR =
+ nullsFirst(comparing(Node::getName, nullsFirst(naturalOrder())).thenComparing(NodeImpl::compareIterability)
+ .thenComparing(NodeImpl::compareSpecificNodeInfo));
+
+ private static final Comparator<Path.Node> NODE_EQUALITY_COMPARATOR =
+ nullsFirst(comparing(Node::getName, nullsFirst(naturalOrder()))
+ .thenComparing((o1, o2) -> NodeImpl.compareIterability(o1, o2, false))
+ .thenComparing(NodeImpl::compareSpecificNodeInfo));
+
+ private static final Comparator<Class<?>> CLASS_COMPARATOR = Comparator.nullsFirst(
+ Comparator.<Class<?>, Boolean> comparing(Class::isPrimitive).reversed().thenComparing(Class::getName));
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static final Comparator<Object> KEY_COMPARATOR = nullsFirst(((Comparator<Object>) (quid, quo) -> {
+ if (quid instanceof Comparable<?> && quo instanceof Comparable<?>) {
+ try {
+ return Comparator.<Comparable> naturalOrder().compare((Comparable) quid, (Comparable) quo);
+ } catch (Exception e) {
+ // apparently not mutually comparable
+ }
+ }
+ if (quid instanceof Class<?> && quo instanceof Class<?>) {
+ return CLASS_COMPARATOR.compare((Class<?>) quid, (Class<?>) quo);
+ }
+ return 0;
+ }).thenComparing((Function<Object, String>) Objects::toString));
+
+ private static final char INDEX_OPEN = '[';
+ private static final char INDEX_CLOSE = ']';
+
+ private static <T extends Path.Node> Optional<T> optional(Class<T> type, Object o) {
+ return Optional.ofNullable(o).filter(type::isInstance).map(type::cast);
+ }
+
+ /**
+ * Append a Node to the specified StringBuilder.
+ *
+ * @param node
+ * @param to
+ * @return to
+ */
+ public static StringBuilder appendNode(Node node, StringBuilder to) {
+ if (node.isInIterable()) {
+ to.append(INDEX_OPEN);
+ if (node.getIndex() != null) {
+ to.append(node.getIndex());
+ } else if (node.getKey() != null) {
+ to.append(node.getKey());
+ }
+ to.append(INDEX_CLOSE);
+ }
+ if (node.getName() != null) {
+ if (to.length() > 0) {
+ to.append(PathImpl.PROPERTY_PATH_SEPARATOR);
+ }
+ to.append(node.getName());
+ }
+ return to;
+ }
+
+ /**
+ * Get a NodeImpl indexed from the preceding node (or root).
+ *
+ * @param index
+ * @return NodeImpl
+ */
+ public static NodeImpl atIndex(Integer index) {
+ final NodeImpl result = new NodeImpl.PropertyNodeImpl((String) null);
+ result.setIndex(index);
+ return result;
+ }
+
+ /**
+ * Get a NodeImpl keyed from the preceding node (or root).
+ *
+ * @param key
+ * @return NodeImpl
+ */
+ public static NodeImpl atKey(Object key) {
+ final NodeImpl result = new NodeImpl.PropertyNodeImpl((String) null);
+ result.setKey(key);
+ return result;
+ }
+
+ private static int compareIterability(Node quid, Node quo) {
+ final boolean strict = true;
+ return compareIterability(quid, quo, strict);
+ }
+
+ private static int compareIterability(Node quid, Node quo, boolean strict) {
+ if (quid.isInIterable()) {
+ if (quo.isInIterable()) {
+ if (quid.getKey() != null) {
+ return Comparator.comparing(Node::getKey, KEY_COMPARATOR).compare(quid, quo);
+ }
+ if (quo.getKey() != null) {
+ return -1;
+ }
+ if (quid.getIndex() == null) {
+ if (strict) {
+ // this method cannot consistently order iterables without key or index; the first argument is
+ // always assumed to be less:
+ return -1;
+ }
+ return quo.getIndex() == null ? 0 : -1;
+ }
+ return quo.getIndex() == null ? 1 : quid.getIndex().compareTo(quo.getIndex());
+ }
+ return 1;
+ }
+ return quo.isInIterable() ? -1 : 0;
+ }
+
+ private static int compareSpecificNodeInfo(Node quid, Node quo) {
+ final ElementKind kind = quid.getKind();
+ final int k = kind.compareTo(quo.getKind());
+ if (k != 0) {
+ return k;
+ }
+ final Comparator<Node> cmp;
+ switch (kind) {
+ case BEAN:
+ cmp = comparing(to(BeanNode.class), comparing(BeanNode::getContainerClass, CLASS_COMPARATOR)
+ .thenComparing(BeanNode::getTypeArgumentIndex, nullsFirst(naturalOrder())));
+ break;
+ case PROPERTY:
+ cmp = comparing(to(PropertyNode.class), comparing(PropertyNode::getContainerClass, CLASS_COMPARATOR)
+ .thenComparing(PropertyNode::getTypeArgumentIndex, nullsFirst(naturalOrder())));
+ break;
+ case CONTAINER_ELEMENT:
+ cmp = comparing(to(ContainerElementNode.class),
+ comparing(ContainerElementNode::getContainerClass, CLASS_COMPARATOR)
+ .thenComparing(ContainerElementNode::getTypeArgumentIndex, nullsFirst(naturalOrder())));
+ break;
+ case CONSTRUCTOR:
+ cmp = comparing(to(ConstructorNode.class).andThen(ConstructorNode::getParameterTypes),
+ Comparators.comparingIterables(CLASS_COMPARATOR));
+ break;
+ case METHOD:
+ cmp = comparing(to(MethodNode.class).andThen(MethodNode::getParameterTypes),
+ Comparators.comparingIterables(CLASS_COMPARATOR));
+ break;
+ case PARAMETER:
+ cmp = comparing(to(ParameterNode.class).andThen(ParameterNode::getParameterIndex));
+ break;
+ default:
+ return 0;
+ }
+ return cmp.compare(quid, quo);
+ }
+
+ private static <T> Function<Object, T> to(Class<T> type) {
+ return type::cast;
+ }
+
+ private String name;
+ private boolean inIterable;
+ private Integer index;
+ private int parameterIndex;
+ private Object key;
+ private List<Class<?>> parameterTypes;
+ private Class<?> containerType;
+ private Integer typeArgumentIndex;
+
+ /**
+ * Create a new NodeImpl instance.
+ *
+ * @param name
+ */
+ private NodeImpl(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Create a new NodeImpl instance.
+ *
+ * @param node
+ */
+ NodeImpl(Path.Node node) {
+ this(node.getName());
+ this.inIterable = node.isInIterable();
+ this.index = node.getIndex();
+ this.key = node.getKey();
+
+ if (node instanceof NodeImpl) {
+ final NodeImpl n = (NodeImpl) node;
+ this.parameterIndex = n.parameterIndex;
+ this.parameterTypes = n.parameterTypes;
+ this.containerType = n.containerType;
+ this.typeArgumentIndex = n.typeArgumentIndex;
+ }
+ }
+
+ <T extends Path.Node> NodeImpl(Path.Node node, Class<T> nodeType, Consumer<T> handler) {
+ this(node);
+ Optional.of(node).filter(nodeType::isInstance).map(nodeType::cast).ifPresent(handler);
+ }
+
+ private NodeImpl() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name
+ * the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isInIterable() {
+ return inIterable;
+ }
+
+ /**
+ * Set whether this node represents a contained value of an {@link Iterable} or {@link Map}.
+ *
+ * @param inIterable
+ */
+ public void setInIterable(boolean inIterable) {
+ this.inIterable = inIterable;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Integer getIndex() {
+ return index;
+ }
+
+ /**
+ * Set the index of this node, implying <code>inIterable</code>.
+ *
+ * @param index
+ */
+ public void setIndex(Integer index) {
+ inIterable = true;
+ this.index = index;
+ this.key = null;
+ }
+
+ public void setParameterIndex(final Integer parameterIndex) {
+ this.parameterIndex = parameterIndex;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object getKey() {
+ return key;
+ }
+
+ /**
+ * Set the map key of this node, implying <code>inIterable</code>.
+ *
+ * @param key
+ */
+ public void setKey(Object key) {
+ inIterable = true;
+ this.key = key;
+ this.index = null;
+ }
+
+ @Override
+ public <T extends Node> T as(final Class<T> nodeType) {
+ Exceptions.raiseUnless(nodeType.isInstance(this), ClassCastException::new, "Type %s not supported by %s",
+ f -> f.args(nodeType, getClass()));
+ return nodeType.cast(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return appendNode(this, new StringBuilder()).toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || !getClass().equals(o.getClass())) {
+ return false;
+ }
+ return NODE_EQUALITY_COMPARATOR.compare(this, (NodeImpl) o) == 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, Boolean.valueOf(inIterable), index, key, getKind());
+ }
+
+ public int getParameterIndex() {
+ return parameterIndex;
+ }
+
+ public List<Class<?>> getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public void setParameterTypes(final List<Class<?>> parameterTypes) {
+ this.parameterTypes = parameterTypes;
+ }
+
+ public Class<?> getContainerClass() {
+ return containerType;
+ }
+
+ public Integer getTypeArgumentIndex() {
+ return typeArgumentIndex;
+ }
+
+ public NodeImpl inIterable() {
+ setInIterable(true);
+ return this;
+ }
+
+ public NodeImpl inContainer(Class<?> containerType, Integer typeArgumentIndex) {
+ this.containerType = containerType;
+ this.typeArgumentIndex = typeArgumentIndex;
+ return this;
+ }
+
+ @SuppressWarnings("serial")
+ public static class ParameterNodeImpl extends NodeImpl implements Path.ParameterNode {
+ public ParameterNodeImpl(final Node cast) {
+ super(cast);
+ optional(Path.ParameterNode.class, cast).ifPresent(n -> setParameterIndex(n.getParameterIndex()));
+ }
+
+ public ParameterNodeImpl(final String name, final int idx) {
+ super(name);
+ setParameterIndex(idx);
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.PARAMETER;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class ConstructorNodeImpl extends NodeImpl implements Path.ConstructorNode {
+ public ConstructorNodeImpl(final Node cast) {
+ super(cast);
+ optional(Path.ConstructorNode.class, cast).ifPresent(n -> setParameterTypes(n.getParameterTypes()));
+ }
+
+ public ConstructorNodeImpl(final String simpleName, List<Class<?>> paramTypes) {
+ super(simpleName);
+ setParameterTypes(paramTypes);
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.CONSTRUCTOR;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class CrossParameterNodeImpl extends NodeImpl implements Path.CrossParameterNode {
+ public CrossParameterNodeImpl() {
+ super("<cross-parameter>");
+ }
+
+ public CrossParameterNodeImpl(final Node cast) {
+ super(cast);
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.CROSS_PARAMETER;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class MethodNodeImpl extends NodeImpl implements Path.MethodNode {
+ public MethodNodeImpl(final Node cast) {
+ super(cast);
+ optional(Path.MethodNode.class, cast).ifPresent(n -> setParameterTypes(n.getParameterTypes()));
+ }
+
+ public MethodNodeImpl(final String name, final List<Class<?>> classes) {
+ super(name);
+ setParameterTypes(classes);
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.METHOD;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class ReturnValueNodeImpl extends NodeImpl implements Path.ReturnValueNode {
+ public ReturnValueNodeImpl(final Node cast) {
+ super(cast);
+ }
+
+ public ReturnValueNodeImpl() {
+ super("<return value>");
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.RETURN_VALUE;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class PropertyNodeImpl extends NodeImpl implements Path.PropertyNode {
+ public PropertyNodeImpl(final String name) {
+ super(name);
+ }
+
+ public PropertyNodeImpl(final Node cast) {
+ super(cast);
+ optional(Path.PropertyNode.class, cast)
+ .ifPresent(n -> inContainer(n.getContainerClass(), n.getTypeArgumentIndex()));
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.PROPERTY;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class BeanNodeImpl extends NodeImpl implements Path.BeanNode {
+ public BeanNodeImpl() {
+ // no-op
+ }
+
+ public BeanNodeImpl(final Node cast) {
+ super(cast);
+ optional(Path.BeanNode.class, cast)
+ .ifPresent(n -> inContainer(n.getContainerClass(), n.getTypeArgumentIndex()));
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.BEAN;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class ContainerElementNodeImpl extends NodeImpl implements Path.ContainerElementNode {
+
+ public ContainerElementNodeImpl(String name) {
+ super(name);
+ }
+
+ public ContainerElementNodeImpl(String name, Class<?> containerType, Integer typeArgumentIndex) {
+ this(name);
+ inContainer(containerType, typeArgumentIndex);
+ }
+
+ public ContainerElementNodeImpl(final Node cast) {
+ super(cast);
+ optional(Path.ContainerElementNode.class, cast)
+ .ifPresent(n -> inContainer(n.getContainerClass(), n.getTypeArgumentIndex()));
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.CONTAINER_ELEMENT;
+ }
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,354 @@
+/*
+ * 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.io.Serializable;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+import javax.validation.Path;
+
+import org.apache.bval.util.Comparators;
+import org.apache.bval.util.Exceptions;
+
+/**
+ * Description: object holding the property path as a list of nodes. (Implementation partially based on reference
+ * implementation) <br/>
+ * This class is not synchronized.
+ *
+ * @version $Rev: 1498347 $ $Date: 2013-07-01 12:06:18 +0200 (lun., 01 juil. 2013) $
+ */
+public class PathImpl implements Path, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @see NodeImpl#NODE_COMPARATOR
+ */
+ public static final Comparator<Path> PATH_COMPARATOR = Comparators.comparingIterables(NodeImpl.NODE_COMPARATOR);
+
+ static final String PROPERTY_PATH_SEPARATOR = ".";
+
+ /**
+ * Builds non-root paths from expressions.
+ */
+ public static class Builder implements PathNavigation.Callback<PathImpl> {
+ private final PathImpl result = PathImpl.create();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleProperty(String name) {
+ result.addProperty(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleIndexOrKey(String value) {
+ // with no context to guide us, we can only parse ints and fall back to String keys:
+ NodeImpl node;
+ try {
+ node = NodeImpl.atIndex(Integer.parseInt(value));
+ } catch (NumberFormatException e) {
+ node = NodeImpl.atKey(value);
+ }
+ result.addNode(node);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PathImpl result() {
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleGenericInIterable() {
+ result.addNode(NodeImpl.atIndex(null));
+ }
+ }
+
+ /**
+ * Returns a {@code Path} instance representing the path described by the given string. To create a root node the
+ * empty string should be passed. Note: This signature is to maintain pluggability with the RI impl.
+ *
+ * @param propertyPath
+ * the path as string representation.
+ * @return a {@code Path} instance representing the path described by the given string.
+ */
+ public static PathImpl createPathFromString(String propertyPath) {
+ if (propertyPath == null || propertyPath.isEmpty()) {
+ return create();
+ }
+ return PathNavigation.navigateAndReturn(propertyPath, new Builder());
+ }
+
+ /**
+ * Create a {@link PathImpl} instance representing the specified path.
+ *
+ * @return PathImpl
+ */
+ public static PathImpl create() {
+ final PathImpl path = new PathImpl();
+ final NodeImpl node = new NodeImpl.BeanNodeImpl();
+ path.addNode(node);
+ return path;
+ }
+
+ /**
+ * Copy another Path.
+ *
+ * @param path
+ * @return new {@link PathImpl}
+ */
+ public static PathImpl copy(Path path) {
+ return path == null ? null : new PathImpl(path);
+ }
+
+ public static PathImpl of(Path path) {
+ return path instanceof PathImpl ? (PathImpl) path : copy(path);
+ }
+
+ private static NodeImpl newNode(final Node cast) {
+ if (BeanNode.class.isInstance(cast)) {
+ return new NodeImpl.BeanNodeImpl(cast);
+ }
+ if (MethodNode.class.isInstance(cast)) {
+ return new NodeImpl.MethodNodeImpl(cast);
+ }
+ if (ConstructorNode.class.isInstance(cast)) {
+ return new NodeImpl.ConstructorNodeImpl(cast);
+ }
+ if (ReturnValueNode.class.isInstance(cast)) {
+ return new NodeImpl.ReturnValueNodeImpl(cast);
+ }
+ if (ParameterNode.class.isInstance(cast)) {
+ return new NodeImpl.ParameterNodeImpl(cast);
+ }
+ if (CrossParameterNode.class.isInstance(cast)) {
+ return new NodeImpl.CrossParameterNodeImpl(cast);
+ }
+ if (ContainerElementNode.class.isInstance(cast)) {
+ return new NodeImpl.ContainerElementNodeImpl(cast);
+ }
+ return new NodeImpl.PropertyNodeImpl(cast);
+ }
+
+ private static boolean isAwaitingPropertyName(NodeImpl n) {
+ return n != null && n.getName() == null && (n.isInIterable() || n.getContainerClass() != null);
+ }
+
+ private final LinkedList<NodeImpl> nodeList = new LinkedList<>();
+
+ private PathImpl() {
+ }
+
+ private PathImpl(Iterable<? extends Node> nodes) {
+ nodes.forEach(n -> nodeList.add(newNode(n)));
+ }
+
+ /**
+ * Learn whether this {@link PathImpl} points to the root of its graph.
+ *
+ * @return true if no child nodes
+ */
+ // our implementation stores a nameless root node.
+ public boolean isRootPath() {
+ if (nodeList.size() != 1) {
+ return false;
+ }
+ final Path.Node first = nodeList.peekFirst();
+ return !first.isInIterable() && first.getName() == null;
+ }
+
+ /**
+ * Add a node to this {@link PathImpl}.
+ *
+ * @param node
+ * to add
+ * @return {@code this}, fluently
+ */
+ public PathImpl addNode(Node node) {
+ final NodeImpl impl = node instanceof NodeImpl ? (NodeImpl) node : newNode(node);
+ if (isRootPath()) {
+ nodeList.pop();
+ }
+ nodeList.add(impl);
+ return this;
+ }
+
+ /**
+ * Encapsulate the node manipulations needed to add a named property to this path.
+ *
+ * @param name
+ * @return {@code this}, fluently
+ */
+ public PathImpl addProperty(String name) {
+ if (!nodeList.isEmpty()) {
+ NodeImpl leaf = getLeafNode();
+ if (isAwaitingPropertyName(leaf)) {
+ if (!PropertyNode.class.isInstance(leaf)) {
+ final NodeImpl tmp = new NodeImpl.PropertyNodeImpl(leaf);
+ removeLeafNode();
+ addNode(tmp);
+ leaf = tmp;
+ }
+ leaf.setName(name);
+ return this;
+ }
+ }
+ return addNode(new NodeImpl.PropertyNodeImpl(name));
+ }
+
+ public PathImpl addBean() {
+ final NodeImpl.BeanNodeImpl node;
+ if (!nodeList.isEmpty() && isAwaitingPropertyName(getLeafNode())) {
+ node = new NodeImpl.BeanNodeImpl(removeLeafNode());
+ } else {
+ node = new NodeImpl.BeanNodeImpl();
+ }
+ return addNode(node);
+ }
+
+ /**
+ * Trim the leaf node from this {@link PathImpl}.
+ *
+ * @return the node removed
+ * @throws IllegalStateException
+ * if no nodes are found
+ */
+ public NodeImpl removeLeafNode() {
+ Exceptions.raiseIf(isRootPath() || nodeList.isEmpty(), IllegalStateException::new, "No nodes in path!");
+
+ try {
+ return nodeList.removeLast();
+ } finally {
+ if (nodeList.isEmpty()) {
+ nodeList.add(new NodeImpl.BeanNodeImpl());
+ }
+ }
+ }
+
+ /**
+ * Get the leaf node (if any) from this {@link PathImpl}
+ *
+ * @return {@link NodeImpl}
+ */
+ public NodeImpl getLeafNode() {
+ if (nodeList.isEmpty()) {
+ return null;
+ }
+ return nodeList.peekLast();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator<Path.Node> iterator() {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final Iterator<Path.Node> result = ((List) nodeList).iterator();
+ return result;
+ }
+
+ /**
+ * Learn whether <code>path</code> is a parent to <code>this</code>.
+ *
+ * @param path
+ * @return <code>true</code> if our nodes begin with nodes equal to those found in <code>path</code>
+ */
+ public boolean isSubPathOf(Path path) {
+ if (path instanceof PathImpl && ((PathImpl) path).isRootPath()) {
+ return true;
+ }
+ final Iterator<Node> pathIter = path.iterator();
+ final Iterator<Node> thisIter = iterator();
+ while (pathIter.hasNext()) {
+ final Node pathNode = pathIter.next();
+ if (!thisIter.hasNext()) {
+ return false;
+ }
+ final Node thisNode = thisIter.next();
+ if (pathNode.isInIterable()) {
+ if (!thisNode.isInIterable()) {
+ return false;
+ }
+ if (pathNode.getIndex() != null && !pathNode.getIndex().equals(thisNode.getIndex())) {
+ return false;
+ }
+ if (pathNode.getKey() != null && !pathNode.getKey().equals(thisNode.getKey())) {
+ return false;
+ }
+ } else if (thisNode.isInIterable()) {
+ // in this case we have shown that the proposed parent is not
+ // indexed, and we are, thus the paths cannot match
+ return false;
+ }
+ if (pathNode.getName() == null || pathNode.getName().equals(thisNode.getName())) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ for (Path.Node node : this) {
+ NodeImpl.appendNode(node, builder);
+ }
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || !getClass().equals(o.getClass())) {
+ return false;
+ }
+ return Objects.equals(nodeList, ((PathImpl) o).nodeList);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(nodeList);
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,374 @@
+/*
+ * 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 static org.apache.bval.util.Escapes.unescapeJava;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.validation.ValidationException;
+
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
+
+/**
+ * Defines a path navigation algorithm and a means of interacting with same.
+ *
+ * @version $Rev: 1136233 $ $Date: 2011-06-15 17:49:27 -0500 (Wed, 15 Jun 2011) $
+ */
+public class PathNavigation {
+
+ /**
+ * Path traversal callback function interface.
+ */
+ public interface Callback<T> {
+ /**
+ * Handle a .-delimited property.
+ *
+ * @param name
+ */
+ void handleProperty(String name);
+
+ /**
+ * Handle an index or key embedded in [].
+ *
+ * @param value
+ */
+ void handleIndexOrKey(String value);
+
+ /**
+ * Handle contiguous [].
+ */
+ void handleGenericInIterable();
+
+ /**
+ * Return a result. Called after navigation is complete.
+ *
+ * @return result
+ */
+ T result();
+ }
+
+ /**
+ * Callback "procedure" that always returns null.
+ */
+ public static abstract class CallbackProcedure implements Callback<Void> {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final Void result() {
+ complete();
+ return null;
+ }
+
+ /**
+ * Complete this CallbackProcedure. Default implementation is noop.
+ */
+ protected void complete() {
+ }
+ }
+
+ public static class CompositeCallbackProcedure extends CallbackProcedure {
+ private final List<Callback<?>> delegates;
+
+ public CompositeCallbackProcedure(Callback<?>... delegates) {
+ this(new ArrayList<>(Arrays.asList(delegates)));
+ }
+
+ public CompositeCallbackProcedure(List<Callback<?>> delegates) {
+ super();
+ this.delegates = Validate.notNull(delegates);
+ }
+
+ @Override
+ public void handleProperty(String name) {
+ delegates.forEach(d -> d.handleProperty(name));
+ }
+
+ @Override
+ public void handleIndexOrKey(String value) {
+ delegates.forEach(d -> d.handleIndexOrKey(value));
+ }
+
+ @Override
+ public void handleGenericInIterable() {
+ delegates.forEach(Callback::handleGenericInIterable);
+ }
+ }
+
+ private static class QuotedStringParser {
+ String parseQuotedString(CharSequence path, PathPosition pos) throws Exception {
+ final int len = path.length();
+ final int start = pos.getIndex();
+ if (start < len) {
+ final char quote = path.charAt(start);
+ pos.next();
+ final StringWriter w = new StringWriter();
+ while (pos.getIndex() < len) {
+ final int here = pos.getIndex();
+ // look for matching quote
+ if (path.charAt(here) == quote) {
+ pos.next();
+ return w.toString();
+ }
+ handleNextChar(path, pos, w);
+ }
+ // if reached, reset due to no ending quote found
+ pos.setIndex(start);
+ }
+ return null;
+ }
+
+ protected void handleNextChar(CharSequence path, PathPosition pos, Writer target) throws IOException {
+ final int codePoints = unescapeJava(path, pos.getIndex(), target);
+ if (codePoints == 0) {
+ target.write(Character.toChars(Character.codePointAt(path, pos.getIndex())));
+ pos.next();
+ } else {
+ for (int i = 0; i < codePoints; i++) {
+ pos.plus(Character.charCount(Character.codePointAt(path, pos.getIndex())));
+ }
+ }
+ }
+ }
+
+ private static final QuotedStringParser QUOTED_STRING_PARSER = new QuotedStringParser();
+
+ /**
+ * Create a new PathNavigation instance.
+ */
+ private PathNavigation() {
+ }
+
+ /**
+ * Navigate a path using the specified callback, returning its result.
+ *
+ * @param <T>
+ * @param propertyPath
+ * , null is assumed empty/root
+ * @param callback
+ * @return T result
+ */
+ public static <T> T navigateAndReturn(CharSequence propertyPath, Callback<? extends T> callback) {
+ try {
+ parse(propertyPath == null ? "" : propertyPath, new PathPosition(callback));
+ } catch (ValidationException | IllegalArgumentException ex) {
+ throw ex;
+ } catch (Exception e) {
+ Exceptions.raise(ValidationException::new, e, "invalid property: %s", propertyPath);
+ }
+ return callback.result();
+ }
+
+ /**
+ * Navigate a path using the specified callback.
+ *
+ * @param propertyPath
+ * @param callback
+ */
+ public static void navigate(CharSequence propertyPath, Callback<?> callback) {
+ navigateAndReturn(propertyPath, callback);
+ }
+
+ private static void parse(CharSequence path, PathPosition pos) throws Exception {
+ int len = path.length();
+ boolean sep = true;
+ while (pos.getIndex() < len) {
+ int here = pos.getIndex();
+ char c = path.charAt(here);
+ switch (c) {
+ case ']':
+ Exceptions.raise(IllegalStateException::new, "Position %s: unexpected '%s'", here, c);
+ case '[':
+ handleIndex(path, pos.next());
+ break;
+ case '.':
+ Exceptions.raiseIf(sep, IllegalStateException::new,
+ "Position %s: expected property, index/key, or end of expression", here);
+
+ sep = true;
+ pos.next();
+ // fall through:
+ default:
+ Exceptions.raiseUnless(sep, IllegalStateException::new,
+ "Position %s: expected property path separator, index/key, or end of expression", here);
+
+ pos.handleProperty(parseProperty(path, pos));
+ }
+ sep = false;
+ }
+ }
+
+ private static String parseProperty(CharSequence path, PathPosition pos) throws Exception {
+ final int len = path.length();
+ final int start = pos.getIndex();
+ loop: while (pos.getIndex() < len) {
+ switch (path.charAt(pos.getIndex())) {
+ case '[':
+ case ']':
+ case '.':
+ break loop;
+ }
+ pos.next();
+ }
+ Exceptions.raiseIf(pos.getIndex() == start, IllegalStateException::new, "Position %s: expected property",
+ start);
+
+ return path.subSequence(start, pos.getIndex()).toString();
+ }
+
+ /**
+ * Handles an index/key. If the text contained between [] is surrounded by a pair of " or ', it will be treated as a
+ * string which may contain Java escape sequences.
+ *
+ * @param path
+ * @param pos
+ * @throws Exception
+ */
+ private static void handleIndex(CharSequence path, PathPosition pos) throws Exception {
+ final int len = path.length();
+ final int start = pos.getIndex();
+ if (start < len) {
+ final char first = path.charAt(pos.getIndex());
+ if (first == '"' || first == '\'') {
+ final String s = QUOTED_STRING_PARSER.parseQuotedString(path, pos);
+ if (s != null && path.charAt(pos.getIndex()) == ']') {
+ pos.handleIndexOrKey(s);
+ pos.next();
+ return;
+ }
+ }
+ // no quoted string; match ] greedily
+ while (pos.getIndex() < len) {
+ final int here = pos.getIndex();
+ try {
+ if (path.charAt(here) == ']') {
+ if (here == start) {
+ pos.handleGenericInIterable();
+ } else {
+ pos.handleIndexOrKey(path.subSequence(start, here).toString());
+ }
+ return;
+ }
+ } finally {
+ pos.next();
+ }
+ }
+ }
+ Exceptions.raise(IllegalStateException::new, "Position %s: unparsable index", start);
+ }
+
+ /**
+ * ParsePosition/Callback
+ */
+ private static class PathPosition extends ParsePosition implements Callback<Void> {
+ final Callback<?> delegate;
+
+ /**
+ * Create a new {@link PathPosition} instance.
+ *
+ * @param delegate
+ */
+ private PathPosition(Callback<?> delegate) {
+ super(0);
+ this.delegate = delegate;
+ }
+
+ /**
+ * Increment and return this.
+ *
+ * @return this
+ */
+ public PathPosition next() {
+ return plus(1);
+ }
+
+ /**
+ * Increase position and return this.
+ *
+ * @param addend
+ * @return this
+ */
+ public PathPosition plus(int addend) {
+ setIndex(getIndex() + addend);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleProperty(String name) {
+ delegate.handleProperty(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleIndexOrKey(String value) {
+ delegate.handleIndexOrKey(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleGenericInIterable() {
+ delegate.handleGenericInIterable();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Void result() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ /*
+ * Override equals to make findbugs happy; would simply ignore but doesn't seem to be possible at the inner
+ * class level without attaching the filter to the containing class.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ /*
+ * Override hashCode to make findbugs happy in the presence of overridden #equals :P
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,55 @@
+/*
+ * 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.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public final class Proxies {
+ private static final Set<String> KNOWN_PROXY_CLASSNAMES;
+
+ static {
+ final Set<String> s = new HashSet<>();
+ s.add("org.jboss.weld.bean.proxy.ProxyObject");
+ KNOWN_PROXY_CLASSNAMES = Collections.unmodifiableSet(s);
+ }
+
+ // get rid of proxies which probably contains wrong annotation metamodel
+ public static <T> Class<?> classFor(final Class<?> clazz) { // TODO: do we want a SPI with impl for guice, owb, openejb, ...?
+ if (isProxyClass(clazz)) {
+ final Class<?> parent = clazz.getSuperclass();
+ if (parent != null) {
+ return classFor(clazz.getSuperclass());
+ }
+ }
+ return clazz;
+ }
+
+ public static boolean isProxyClass(Class<?> clazz) {
+ if (KNOWN_PROXY_CLASSNAMES.contains(clazz.getName())) {
+ return true;
+ }
+ return clazz.getSimpleName().contains("$$");// a lot of proxies use this convention to avoid conflicts with inner/anonymous classes
+ }
+
+ private Proxies() {
+ // no-op
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/ToUnmodifiable.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/ToUnmodifiable.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/ToUnmodifiable.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/util/ToUnmodifiable.java Fri Oct 12 15:00:48 2018
@@ -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 java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+/**
+ * Utility {@link Collector} definitions.
+ */
+public class ToUnmodifiable {
+
+ /**
+ * Collector to unmodifiable {@link Set} with custom backing implementation.
+ *
+ * @param set
+ * {@link Supplier}
+ * @return {@link Collector}
+ */
+ public static <T> Collector<T, ?, Set<T>> set(Supplier<Set<T>> set) {
+ return Collectors.collectingAndThen(Collectors.toCollection(set),
+ t -> t.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(t));
+ }
+
+ /**
+ * Collector to unmodifiable {@link Set} (maintains insertion order).
+ *
+ * @return {@link Collector}
+ */
+ public static <T> Collector<T, ?, Set<T>> set() {
+ return set(LinkedHashSet::new);
+ }
+
+ /**
+ * Collector to unmodifiable {@link List}.
+ *
+ * @return {@link Collector}
+ */
+ public static <T> Collector<T, ?, List<T>> list() {
+ return Collectors.collectingAndThen(Collectors.toList(),
+ t -> t.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(t));
+ }
+
+ /**
+ * Collector to unmodifiable {@link Map}.
+ *
+ * @param keyMapper
+ * @param valueMapper
+ * @return {@link Collector}
+ */
+ public static <T, K, U> Collector<T, ?, Map<K, U>> map(Function<? super T, ? extends K> keyMapper,
+ Function<? super T, ? extends U> valueMapper) {
+ return Collectors.collectingAndThen(Collectors.toMap(keyMapper, valueMapper),
+ t -> t.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(t));
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,109 @@
+/*
+ * 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.valueextraction;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.validation.ValidationException;
+import javax.validation.valueextraction.ValueExtractor;
+
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.metadata.ContainerElementKey;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+
+/**
+ * Utility class to extract values from a {@link GraphContext} using a {@link ValueExtractor}.
+ */
+public final class ExtractValues {
+
+ private static class Receiver implements ValueExtractor.ValueReceiver {
+ private final GraphContext context;
+ private final ContainerElementKey containerElementKey;
+ private final Lazy<List<GraphContext>> result = new Lazy<>(ArrayList::new);
+
+ Receiver(GraphContext context, ContainerElementKey containerElementKey) {
+ super();
+ this.context = context;
+ this.containerElementKey = containerElementKey;
+ }
+
+ @Override
+ public void value(String nodeName, Object object) {
+ addChild(new NodeImpl.ContainerElementNodeImpl(nodeName), object);
+ }
+
+ @Override
+ public void iterableValue(String nodeName, Object object) {
+ final NodeImpl node = new NodeImpl.ContainerElementNodeImpl(nodeName);
+ node.setInIterable(true);
+ addChild(node, object);
+ }
+
+ @Override
+ public void indexedValue(String nodeName, int i, Object object) {
+ final NodeImpl node = new NodeImpl.ContainerElementNodeImpl(nodeName);
+ node.setIndex(Integer.valueOf(i));
+ addChild(node, object);
+ }
+
+ @Override
+ public void keyedValue(String nodeName, Object key, Object object) {
+ final NodeImpl node = new NodeImpl.ContainerElementNodeImpl(nodeName);
+ node.setKey(key);
+ addChild(node, object);
+ }
+
+ private void addChild(NodeImpl node, Object value) {
+ final PathImpl path = context.getPath();
+ path.addNode(
+ node.inContainer(containerElementKey.getContainerClass(), containerElementKey.getTypeArgumentIndex()));
+ result.get().add(context.child(path, value));
+ }
+ }
+
+ private ExtractValues() {
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static List<GraphContext> extract(GraphContext context, ContainerElementKey containerElementKey,
+ ValueExtractor<?> valueExtractor) {
+ Validate.notNull(context, "context");
+ Validate.notNull(containerElementKey, "containerElementKey");
+ if (valueExtractor != null) {
+ Exceptions.raiseIf(context.getValue() == null, IllegalStateException::new,
+ "Cannot extract values from null");
+ final Receiver receiver = new Receiver(context, containerElementKey);
+ try {
+ ((ValueExtractor) valueExtractor).extractValues(context.getValue(), receiver);
+ } catch (ValidationException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ValidationException(e);
+ }
+ return receiver.result.optional().orElse(Collections.emptyList());
+ }
+ return Collections.singletonList(context);
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,96 @@
+/*
+ * 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.valueextraction;
+
+import java.util.Optional;
+import java.util.function.BooleanSupplier;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.UnwrapByDefault;
+import javax.validation.valueextraction.ValueExtractor;
+
+import org.apache.bval.util.reflection.Reflection;
+
+import javafx.beans.property.ReadOnlyListProperty;
+import javafx.beans.property.ReadOnlyMapProperty;
+import javafx.beans.property.ReadOnlySetProperty;
+import javafx.beans.value.ObservableValue;
+
+@SuppressWarnings("restriction")
+public abstract class FxExtractor {
+ public static class Activation implements BooleanSupplier {
+
+ @Override
+ public boolean getAsBoolean() {
+ try {
+ return Reflection.toClass("javafx.beans.Observable") != null;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+ }
+
+ @UnwrapByDefault
+ public static class ForObservableValue implements ValueExtractor<ObservableValue<@ExtractedValue ?>> {
+
+ @Override
+ public void extractValues(ObservableValue<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ receiver.value(null, originalValue.getValue());
+ }
+ }
+
+ public static class ForListProperty implements ValueExtractor<ReadOnlyListProperty<@ExtractedValue ?>> {
+
+ @Override
+ public void extractValues(ReadOnlyListProperty<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ Optional.ofNullable(originalValue.getValue()).ifPresent(l -> {
+ for (int i = 0, sz = l.size(); i < sz; i++) {
+ receiver.indexedValue("<list element>", i, l.get(i));
+ }
+ });
+ }
+ }
+
+ public static class ForSetProperty implements ValueExtractor<ReadOnlySetProperty<@ExtractedValue ?>> {
+
+ @Override
+ public void extractValues(ReadOnlySetProperty<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ Optional.ofNullable(originalValue.getValue())
+ .ifPresent(s -> s.forEach(e -> receiver.iterableValue("<iterable element>", e)));
+ }
+ }
+
+ public static class ForMapPropertyKey implements ValueExtractor<ReadOnlyMapProperty<@ExtractedValue ?, ?>> {
+
+ @Override
+ public void extractValues(ReadOnlyMapProperty<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ Optional.ofNullable(originalValue.getValue())
+ .ifPresent(m -> m.keySet().forEach(k -> receiver.keyedValue("<map key>", k, k)));
+ }
+ }
+
+ public static class ForMapPropertyValue implements ValueExtractor<ReadOnlyMapProperty<?, @ExtractedValue ?>> {
+
+ @Override
+ public void extractValues(ReadOnlyMapProperty<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ Optional.ofNullable(originalValue.getValue()).ifPresent(
+ m -> m.entrySet().forEach(e -> receiver.keyedValue("<map value>", e.getKey(), e.getValue())));
+ }
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,30 @@
+/*
+ * 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.valueextraction;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.ValueExtractor;
+
+public class IterableElementExtractor implements ValueExtractor<Iterable<@ExtractedValue ?>> {
+
+ @Override
+ public void extractValues(Iterable<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ originalValue.forEach(v -> receiver.iterableValue("<iterable element>", v));
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,34 @@
+/*
+ * 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.valueextraction;
+
+import java.util.List;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.ValueExtractor;
+
+public class ListElementExtractor implements ValueExtractor<List<@ExtractedValue ?>> {
+
+ @Override
+ public void extractValues(List<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ for (int i = 0, sz = originalValue.size(); i < sz; i++) {
+ receiver.indexedValue("<list element>", i, originalValue.get(i));
+ }
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,42 @@
+/*
+ * 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.valueextraction;
+
+import java.util.Map;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.ValueExtractor;
+
+public abstract class MapExtractor {
+ public static class ForKey implements ValueExtractor<Map<@ExtractedValue ?, ?>> {
+
+ @Override
+ public void extractValues(Map<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ originalValue.keySet().forEach(k -> receiver.keyedValue("<map key>", k, k));
+ }
+ }
+
+ public static class ForValue implements ValueExtractor<Map<?, @ExtractedValue ?>> {
+
+ @Override
+ public void extractValues(Map<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ originalValue.entrySet().forEach(e -> receiver.keyedValue("<map value>", e.getKey(), e.getValue()));
+ }
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java Fri Oct 12 15:00:48 2018
@@ -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.valueextraction;
+
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.UnwrapByDefault;
+import javax.validation.valueextraction.ValueExtractor;
+
+public abstract class OptionalExtractor {
+ public static class ForObject implements ValueExtractor<Optional<@ExtractedValue ?>> {
+
+ @Override
+ public void extractValues(Optional<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+ receiver.value(null, originalValue.orElse(null));
+ }
+ }
+
+ @UnwrapByDefault
+ public static class ForInt implements ValueExtractor<@ExtractedValue(type = Integer.class) OptionalInt> {
+
+ @Override
+ public void extractValues(OptionalInt originalValue, ValueExtractor.ValueReceiver receiver) {
+ receiver.value(null, originalValue.isPresent() ? Integer.valueOf(originalValue.getAsInt()) : null);
+ }
+ }
+
+ @UnwrapByDefault
+ public static class ForLong implements ValueExtractor<@ExtractedValue(type = Long.class) OptionalLong> {
+
+ @Override
+ public void extractValues(OptionalLong originalValue, ValueExtractor.ValueReceiver receiver) {
+ receiver.value(null, originalValue.isPresent() ? Long.valueOf(originalValue.getAsLong()) : null);
+ }
+ }
+
+ @UnwrapByDefault
+ public static class ForDouble implements ValueExtractor<@ExtractedValue(type = Double.class) OptionalDouble> {
+
+ @Override
+ public void extractValues(OptionalDouble originalValue, ValueExtractor.ValueReceiver receiver) {
+ receiver.value(null, originalValue.isPresent() ? Double.valueOf(originalValue.getAsDouble()) : null);
+ }
+ }
+}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java?rev=1843674&view=auto
==============================================================================
--- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java (added)
+++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java Fri Oct 12 15:00:48 2018
@@ -0,0 +1,341 @@
+/*
+ * 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.valueextraction;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.metadata.ValidateUnwrappedValue;
+import javax.validation.valueextraction.UnwrapByDefault;
+import javax.validation.valueextraction.ValueExtractor;
+import javax.validation.valueextraction.ValueExtractorDeclarationException;
+import javax.validation.valueextraction.ValueExtractorDefinitionException;
+
+import org.apache.bval.jsr.metadata.ContainerElementKey;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.StringUtils;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.bval.util.reflection.Reflection.Interfaces;
+import org.apache.bval.util.reflection.TypeUtils;
+
+/**
+ * {@link ValueExtractor} collection of some level of a bean validation hierarchy.
+ */
+public class ValueExtractors {
+ public enum OnDuplicateContainerElementKey {
+ EXCEPTION, OVERWRITE;
+ }
+
+ public static class UnwrappingInfo {
+ public final ContainerElementKey containerElementKey;
+ public final ValueExtractor<?> valueExtractor;
+
+ private UnwrappingInfo(ContainerElementKey containerElementKey, ValueExtractor<?> valueExtractor) {
+ super();
+ this.containerElementKey = containerElementKey;
+ this.valueExtractor = valueExtractor;
+ }
+
+ UnwrappingInfo inTermsOf(Class<?> containerClass) {
+ final Class<?> keyContainer = containerElementKey.getContainerClass();
+ if (keyContainer.equals(containerClass)) {
+ return this;
+ }
+ Validate.validState(keyContainer.isAssignableFrom(containerClass), "Cannot render %s in terms of %s",
+ containerElementKey, containerClass);
+
+ final ContainerElementKey key;
+
+ if (containerElementKey.getTypeArgumentIndex() == null) {
+ key = new ContainerElementKey(containerClass, null);
+ } else {
+ Integer typeArgumentIndex = null;
+ final Map<TypeVariable<?>, Type> typeArguments =
+ TypeUtils.getTypeArguments(containerClass, keyContainer);
+ Type t = typeArguments
+ .get(keyContainer.getTypeParameters()[containerElementKey.getTypeArgumentIndex().intValue()]);
+ while (t instanceof TypeVariable<?>) {
+ final TypeVariable<?> var = (TypeVariable<?>) t;
+ if (containerClass.equals(var.getGenericDeclaration())) {
+ typeArgumentIndex =
+ Integer.valueOf(ObjectUtils.indexOf(containerClass.getTypeParameters(), var));
+ break;
+ }
+ t = typeArguments.get(t);
+ }
+ key = new ContainerElementKey(containerClass, typeArgumentIndex);
+ }
+ return new UnwrappingInfo(key, valueExtractor);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s:%s", containerElementKey, valueExtractor);
+ }
+ }
+
+ public static final ValueExtractors EMPTY =
+ new ValueExtractors(null, OnDuplicateContainerElementKey.EXCEPTION, Collections.emptyMap());
+
+ public static final ValueExtractors DEFAULT;
+ static {
+ final Properties defaultExtractors = new Properties();
+ try {
+ defaultExtractors.load(ValueExtractors.class.getResourceAsStream("DefaultExtractors.properties"));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ final Map<ContainerElementKey, ValueExtractor<?>> m = new TreeMap<>();
+ final Consumer<ValueExtractor<?>> put = ve -> m.put(ContainerElementKey.forValueExtractor(ve), ve);
+
+ split(defaultExtractors.getProperty(ValueExtractor.class.getName())).map(cn -> {
+ try {
+ @SuppressWarnings("unchecked")
+ final Class<? extends ValueExtractor<?>> result =
+ (Class<? extends ValueExtractor<?>>) Reflection.toClass(cn).asSubclass(ValueExtractor.class);
+ return result;
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }).map(ValueExtractors::newInstance).forEach(put);
+
+ split(defaultExtractors.getProperty(ValueExtractor.class.getName() + ".container"))
+ .flatMap(ValueExtractors::loadValueExtractors).forEach(put);
+
+ DEFAULT = new ValueExtractors(null, OnDuplicateContainerElementKey.EXCEPTION, Collections.unmodifiableMap(m));
+ }
+
+ public static Class<?> getExtractedType(ValueExtractor<?> extractor, Type target) {
+ final ContainerElementKey key = ContainerElementKey.forValueExtractor(extractor);
+ Type result = key.getAnnotatedType().getType();
+ if (result instanceof WildcardType && key.getTypeArgumentIndex() != null) {
+ result = TypeUtils.getTypeArguments(target, key.getContainerClass())
+ .get(key.getContainerClass().getTypeParameters()[key.getTypeArgumentIndex().intValue()]);
+ }
+ Exceptions.raiseUnless(result instanceof Class<?>, ValueExtractorDefinitionException::new,
+ "%s did not resolve to a %s relative to %s", f -> f.args(key, Class.class.getName(), target));
+ return (Class<?>) result;
+ }
+
+ public static boolean isUnwrapByDefault(ValueExtractor<?> valueExtractor) {
+ if (valueExtractor != null) {
+ for (Class<?> t : Reflection.hierarchy(valueExtractor.getClass(), Interfaces.INCLUDE)) {
+ if (t.isAnnotationPresent(UnwrapByDefault.class)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static Stream<String> split(String s) {
+ return Stream.of(StringUtils.split(s, ','));
+ }
+
+ private static <T> T newInstance(Class<T> t) {
+ try {
+ return t.getConstructor().newInstance();
+ } catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalStateException(e.getTargetException());
+ }
+ }
+
+ private static Stream<ValueExtractor<?>> loadValueExtractors(String containerClassName) {
+ try {
+ final Class<? extends BooleanSupplier> activation =
+ Reflection.toClass(containerClassName + "$Activation").asSubclass(BooleanSupplier.class);
+ if (!newInstance(activation).getAsBoolean()) {
+ return Stream.empty();
+ }
+ } catch (ClassNotFoundException e) {
+ // always active
+ }
+ final Class<?> containerClass;
+ try {
+ containerClass = Reflection.toClass(containerClassName);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ return Stream.of(containerClass.getClasses()).filter(ValueExtractor.class::isAssignableFrom).map(c -> {
+ @SuppressWarnings("unchecked")
+ final Class<? extends ValueExtractor<?>> result =
+ (Class<? extends ValueExtractor<?>>) c.asSubclass(ValueExtractor.class);
+ return result;
+ }).map(ValueExtractors::newInstance);
+ }
+
+ private static <T> Optional<T> maximallySpecific(Collection<T> candidates, Function<? super T, Class<?>> toType) {
+ final Collection<T> result;
+ if (candidates.size() > 1) {
+ result = new HashSet<>();
+ for (T candidate : candidates) {
+ final Class<?> candidateType = toType.apply(candidate);
+ if (candidates.stream().filter(Predicate.isEqual(candidate).negate()).map(toType)
+ .allMatch(t -> t.isAssignableFrom(candidateType))) {
+ result.add(candidate);
+ }
+ }
+ } else {
+ result = candidates;
+ }
+ return result.size() == 1 ? Optional.of(result.iterator().next()) : Optional.empty();
+ }
+
+ private final ValueExtractors parent;
+ private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> valueExtractors = new Lazy<>(TreeMap::new);
+ private final Lazy<Set<ValueExtractors>> children = new Lazy<>(HashSet::new);
+ private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> searchCache = new Lazy<>(HashMap::new);
+ private final OnDuplicateContainerElementKey onDuplicateContainerElementKey;
+
+ public ValueExtractors() {
+ this(OnDuplicateContainerElementKey.EXCEPTION);
+ }
+
+ public ValueExtractors(OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
+ this(DEFAULT, Validate.notNull(onDuplicateContainerElementKey));
+ }
+
+ private ValueExtractors(ValueExtractors parent, OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
+ this.parent = parent;
+ this.onDuplicateContainerElementKey = onDuplicateContainerElementKey;
+ }
+
+ private ValueExtractors(ValueExtractors parent, OnDuplicateContainerElementKey onDuplicateContainerElementKey,
+ Map<ContainerElementKey, ValueExtractor<?>> backingMap) {
+ this(parent, onDuplicateContainerElementKey);
+ this.valueExtractors.reset(backingMap);
+ }
+
+ public ValueExtractors createChild() {
+ return createChild(OnDuplicateContainerElementKey.EXCEPTION);
+ }
+
+ public ValueExtractors createChild(OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
+ final ValueExtractors child = new ValueExtractors(this, onDuplicateContainerElementKey);
+ children.get().add(child);
+ return child;
+ }
+
+ public void add(ValueExtractor<?> extractor) {
+ Validate.notNull(extractor);
+ final ContainerElementKey key = ContainerElementKey.forValueExtractor(extractor);
+ if (key == null) {
+ Exceptions.raise(IllegalStateException::new, "Computed null %s for %s",
+ ContainerElementKey.class.getSimpleName(), extractor);
+ }
+ final Map<ContainerElementKey, ValueExtractor<?>> m = valueExtractors.get();
+ if (onDuplicateContainerElementKey == OnDuplicateContainerElementKey.EXCEPTION) {
+ synchronized (this) {
+ if (m.containsKey(key)) {
+ Exceptions.raise(ValueExtractorDeclarationException::new,
+ "Multiple context-level %ss specified for %s", ValueExtractor.class.getSimpleName(), key);
+ }
+ m.put(key, extractor);
+ }
+ } else {
+ m.put(key, extractor);
+ }
+ children.optional().ifPresent(s -> s.stream().forEach(ValueExtractors::clearCache));
+ }
+
+ public Map<ContainerElementKey, ValueExtractor<?>> getValueExtractors() {
+ final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> result = new Lazy<>(HashMap::new);
+ populate(result);
+ return result.optional().orElseGet(Collections::emptyMap);
+ }
+
+ public ValueExtractor<?> find(ContainerElementKey key) {
+ final Optional<ValueExtractor<?>> cacheHit = searchCache.optional().map(m -> m.get(key));
+ if (cacheHit.isPresent()) {
+ return cacheHit.get();
+ }
+ final Map<ContainerElementKey, ValueExtractor<?>> allValueExtractors = getValueExtractors();
+ if (allValueExtractors.containsKey(key)) {
+ return allValueExtractors.get(key);
+ }
+ final Map<ValueExtractor<?>, ContainerElementKey> candidates = Stream
+ .concat(Stream.of(key), key.getAssignableKeys().stream()).filter(allValueExtractors::containsKey).collect(
+ Collectors.toMap(allValueExtractors::get, Function.identity(), (quid, quo) -> quo, LinkedHashMap::new));
+
+ final Optional<ValueExtractor<?>> result =
+ maximallySpecific(candidates.keySet(), ve -> candidates.get(ve).getContainerClass());
+ if (result.isPresent()) {
+ searchCache.get().put(key, result.get());
+ return result.get();
+ }
+ throw Exceptions.create(ConstraintDeclarationException::new, "Could not determine %s for %s",
+ ValueExtractor.class.getSimpleName(), key);
+ }
+
+ public Optional<UnwrappingInfo> findUnwrappingInfo(Class<?> containerClass,
+ ValidateUnwrappedValue valueUnwrapping) {
+ if (valueUnwrapping == ValidateUnwrappedValue.SKIP) {
+ return Optional.empty();
+ }
+ final Map<ContainerElementKey, ValueExtractor<?>> allValueExtractors = getValueExtractors();
+
+ final Set<UnwrappingInfo> unwrapping = allValueExtractors.entrySet().stream()
+ .filter(e -> e.getKey().getContainerClass().isAssignableFrom(containerClass))
+ .filter(e -> valueUnwrapping == ValidateUnwrappedValue.UNWRAP || isUnwrapByDefault(e.getValue()))
+ .map(e -> new UnwrappingInfo(e.getKey(), e.getValue())).collect(Collectors.toSet());
+
+ final Optional<UnwrappingInfo> result =
+ maximallySpecific(unwrapping, u -> u.containerElementKey.getContainerClass())
+ .map(u -> u.inTermsOf(containerClass));
+
+ if (!result.isPresent() && valueUnwrapping == ValidateUnwrappedValue.UNWRAP) {
+ Exceptions.raise(ConstraintDeclarationException::new, "Could not determine %s for %s",
+ ValueExtractor.class.getSimpleName(), containerClass);
+ }
+ return result;
+ }
+
+ private void populate(Supplier<Map<ContainerElementKey, ValueExtractor<?>>> target) {
+ Optional.ofNullable(parent).ifPresent(p -> p.populate(target));
+ valueExtractors.optional().ifPresent(m -> target.get().putAll(m));
+ }
+
+ private void clearCache() {
+ searchCache.optional().ifPresent(Map::clear);
+ }
+}