You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by rm...@apache.org on 2013/08/26 15:59:20 UTC
svn commit: r1517540 [9/15] - in /bval/branches/bval-11/bval-jsr: ./ src/
src/main/ src/main/appended-resources/
src/main/appended-resources/META-INF/ src/main/java/ src/main/java/org/
src/main/java/org/apache/ src/main/java/org/apache/bval/ src/main/j...
Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,369 @@
+/*
+ * 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.ElementKind;
+import javax.validation.Path;
+import javax.validation.Path.Node;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+public class NodeImpl implements Path.Node, Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private static final String INDEX_OPEN = "[";
+ private static final String INDEX_CLOSE = "]";
+ private List<Class<?>> parameterTypes;
+
+ /**
+ * 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) {
+ NodeImpl result = new NodeImpl();
+ 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) {
+ NodeImpl result = new NodeImpl();
+ result.setKey(key);
+ return result;
+ }
+
+ private String name;
+ private boolean inIterable;
+ private Integer index;
+ private int parameterIndex;
+ private Object key;
+ private ElementKind kind;
+
+ /**
+ * Create a new NodeImpl instance.
+ * @param name
+ */
+ public NodeImpl(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Create a new NodeImpl instance.
+ * @param node
+ */
+ NodeImpl(Path.Node node) {
+ this.name = node.getName();
+ this.inIterable = node.isInIterable();
+ this.index = node.getIndex();
+ this.key = node.getKey();
+ this.kind = node.getKind();
+ }
+
+ private NodeImpl() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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}
+ */
+ 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}
+ */
+ 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;
+ }
+
+ public ElementKind getKind() {
+ return kind;
+ }
+
+ public void setKind(ElementKind kind) {
+ this.kind = kind;
+ }
+
+ public <T extends Node> T as(final Class<T> nodeType) {
+ if (nodeType.isInstance(this)) {
+ return nodeType.cast(this);
+ }
+ throw new ClassCastException("Type " + nodeType + " not supported");
+ }
+
+ /**
+ * {@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() != o.getClass()) {
+ return false;
+ }
+
+ NodeImpl node = (NodeImpl) o;
+
+ if (inIterable != node.inIterable) {
+ return false;
+ }
+ if (index != null ? !index.equals(node.index) : node.index != null) {
+ return false;
+ }
+ if (key != null ? !key.equals(node.key) : node.key != null) {
+ return false;
+ }
+ if (name != null ? !name.equals(node.name) : node.name != null) {
+ return false;
+ }
+ if (kind != null ? !kind.equals(node.kind) : node.kind != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (inIterable ? 1 : 0);
+ result = 31 * result + (index != null ? index.hashCode() : 0);
+ result = 31 * result + (key != null ? key.hashCode() : 0);
+ result = 31 * result + (kind != null ? kind.hashCode() : 0);
+ return result;
+ }
+
+ public int getParameterIndex() {
+ return parameterIndex;
+ }
+
+ public List<Class<?>> getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public void setParameterTypes(final List<Class<?>> parameterTypes) {
+ this.parameterTypes = parameterTypes;
+ }
+
+ public static class ParameterNodeImpl extends NodeImpl implements Path.ParameterNode {
+ public ParameterNodeImpl(final Node cast) {
+ super(cast);
+ if (ParameterNodeImpl.class.isInstance(cast)) {
+ setParameterIndex(ParameterNodeImpl.class.cast(cast).getParameterIndex());
+ }
+ }
+
+ public ParameterNodeImpl(final String name, final int idx) {
+ super(name);
+ setParameterIndex(idx);
+ }
+
+ public ElementKind getKind() {
+ return ElementKind.PARAMETER;
+ }
+ }
+
+ public static class ConstructorNodeImpl extends NodeImpl implements Path.ConstructorNode {
+ public ConstructorNodeImpl(final Node cast) {
+ super(cast);
+ if (NodeImpl.class.isInstance(cast)) {
+ setParameterTypes(NodeImpl.class.cast(cast).parameterTypes);
+ }
+ }
+
+ public ConstructorNodeImpl(final String simpleName, List<Class<?>> paramTypes) {
+ super(simpleName);
+ setParameterTypes(paramTypes);
+ }
+
+ public ElementKind getKind() {
+ return ElementKind.CONSTRUCTOR;
+ }
+ }
+
+ public static class CrossParameterNodeImpl extends NodeImpl implements Path.CrossParameterNode {
+ public CrossParameterNodeImpl() {
+ super("<cross-parameter>");
+ }
+
+ public CrossParameterNodeImpl(final Node cast) {
+ super(cast);
+ }
+
+ public ElementKind getKind() {
+ return ElementKind.CROSS_PARAMETER;
+ }
+ }
+
+ public static class MethodNodeImpl extends NodeImpl implements Path.MethodNode {
+ public MethodNodeImpl(final Node cast) {
+ super(cast);
+ if (MethodNodeImpl.class.isInstance(cast)) {
+ setParameterTypes(MethodNodeImpl.class.cast(cast).getParameterTypes());
+ }
+ }
+
+ public MethodNodeImpl(final String name, final List<Class<?>> classes) {
+ super(name);
+ setParameterTypes(classes);
+ }
+
+ public ElementKind getKind() {
+ return ElementKind.METHOD;
+ }
+ }
+
+ public static class ReturnValueNodeImpl extends NodeImpl implements Path.ReturnValueNode {
+ public ReturnValueNodeImpl(final Node cast) {
+ super(cast);
+ }
+
+ public ReturnValueNodeImpl() {
+ super("<return value>");
+ }
+
+ public ElementKind getKind() {
+ return ElementKind.RETURN_VALUE;
+ }
+ }
+
+ public static class PropertyNodeImpl extends NodeImpl implements Path.PropertyNode {
+ public PropertyNodeImpl(final String name) {
+ super(name);
+ }
+
+ public PropertyNodeImpl(final Node cast) {
+ super(cast);
+ }
+
+ public ElementKind getKind() {
+ return ElementKind.PROPERTY;
+ }
+ }
+
+ public static class BeanNodeImpl extends NodeImpl implements Path.BeanNode {
+ public BeanNodeImpl() {
+ // no-op
+ }
+
+ public BeanNodeImpl(final Node cast) {
+ super(cast);
+ }
+
+ public ElementKind getKind() {
+ return ElementKind.BEAN;
+ }
+ }
+}
Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,362 @@
+/*
+ * 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.Path;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 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;
+
+ static final String PROPERTY_PATH_SEPARATOR = ".";
+
+ /**
+ * Builds non-root paths from expressions.
+ */
+ private static class PathImplBuilder implements PathNavigation.Callback<PathImpl> {
+ PathImpl result = new PathImpl();
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleProperty(String name) {
+ result.addProperty(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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}
+ */
+ public PathImpl result() {
+ if (result.nodeList.isEmpty()) {
+ throw new IllegalStateException();
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleGenericInIterable() {
+ result.addNode(NodeImpl.atIndex(null));
+ }
+
+ }
+
+ private final List<Node> nodeList;
+
+ /**
+ * 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.length() == 0) {
+ return create();
+ }
+ return PathNavigation.navigateAndReturn(propertyPath, new PathImplBuilder());
+ }
+
+ /**
+ * 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);
+ }
+
+ private PathImpl(Path path) {
+ this.nodeList = new ArrayList<Node>();
+ for (final Object aPath : path) {
+ nodeList.add(newNode(Node.class.cast(aPath)));
+ }
+ }
+
+ private static Node newNode(final Node cast) {
+ if (PropertyNode.class.isInstance(cast)) {
+ return new NodeImpl.PropertyNodeImpl(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 (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);
+ }
+ return new NodeImpl(cast);
+ }
+
+ private PathImpl() {
+ nodeList = new ArrayList<Node>();
+ }
+
+ private PathImpl(List<Node> nodeList) {
+ this.nodeList = new ArrayList<Node>();
+ for (Node node : nodeList) {
+ this.nodeList.add(new NodeImpl(node));
+ }
+ }
+
+ /**
+ * 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;
+ }
+ Path.Node first = nodeList.get(0);
+ return !first.isInIterable() && first.getName() == null;
+ }
+
+ /**
+ * Return a new {@link PathImpl} that represents <code>this</code> minus its leaf node (if present).
+ *
+ * @return PathImpl
+ */
+ public PathImpl getPathWithoutLeafNode() {
+ List<Node> nodes = new ArrayList<Node>(nodeList);
+ PathImpl path = null;
+ if (nodes.size() > 1) {
+ nodes.remove(nodes.size() - 1);
+ path = new PathImpl(nodes);
+ }
+ return path;
+ }
+
+ /**
+ * Add a node to this {@link PathImpl}.
+ *
+ * @param node
+ * to add
+ */
+ public void addNode(Node node) {
+ if (isRootPath()) {
+ nodeList.set(0, node);
+ } else {
+ nodeList.add(node);
+ }
+ }
+
+ /**
+ * Encapsulate the node manipulations needed to add a named property to this path.
+ *
+ * @param name
+ */
+ public void addProperty(String name) {
+ if (!nodeList.isEmpty()) {
+ NodeImpl leaf = getLeafNode();
+ if (leaf != null && leaf.isInIterable() && leaf.getName() == null) { // TODO: avoid to be here
+ if (!PropertyNode.class.isInstance(leaf)) {
+ final NodeImpl tmp = new NodeImpl.PropertyNodeImpl(leaf);
+ removeLeafNode();
+ addNode(tmp);
+ leaf = tmp;
+ }
+ leaf.setName(name);
+ return;
+ }
+ }
+
+ final NodeImpl node;
+ if ("<cross-parameter>".equals(name)) {
+ node = new NodeImpl.CrossParameterNodeImpl();
+ } else {
+ node = new NodeImpl.PropertyNodeImpl(name);
+ }
+ addNode(node);
+
+ }
+
+ /**
+ * Trim the leaf node from this {@link PathImpl}.
+ *
+ * @return the node removed
+ * @throws IllegalStateException
+ * if no nodes are found
+ */
+ public Node removeLeafNode() {
+ if (isRootPath() || nodeList.size() == 0) {
+ throw new IllegalStateException("No nodes in path!");
+ }
+ try {
+ return nodeList.remove(nodeList.size() - 1);
+ } finally {
+ if (nodeList.isEmpty()) {
+ nodeList.add(new NodeImpl((String) null));
+ }
+ }
+ }
+
+ /**
+ * Get the leaf node (if any) from this {@link PathImpl}
+ *
+ * @return {@link NodeImpl}
+ */
+ public NodeImpl getLeafNode() {
+ if (nodeList.size() == 0) {
+ return null;
+ }
+ return (NodeImpl) nodeList.get(nodeList.size() - 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<Path.Node> iterator() {
+ return nodeList.iterator();
+ }
+
+ /**
+ * 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;
+ }
+ Iterator<Node> pathIter = path.iterator();
+ Iterator<Node> thisIter = iterator();
+ while (pathIter.hasNext()) {
+ Node pathNode = pathIter.next();
+ if (!thisIter.hasNext()) {
+ return false;
+ }
+ 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() {
+ 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() != o.getClass()) {
+ return false;
+ }
+
+ PathImpl path = (PathImpl) o;
+ return !(nodeList != null && !nodeList.equals(path.nodeList)) && !(nodeList == null && path.nodeList != null);
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return nodeList != null ? nodeList.hashCode() : 0;
+ }
+
+}
Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,326 @@
+/*
+ * 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 org.apache.commons.lang3.StringEscapeUtils;
+
+import javax.validation.ValidationException;
+import java.io.StringWriter;
+import java.text.ParsePosition;
+
+/**
+ * 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<Object> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public final Object result() {
+ complete();
+ return null;
+ }
+
+ /**
+ * Complete this CallbackProcedure. Default implementation is noop.
+ */
+ protected void complete() {
+ }
+ }
+
+ /**
+ * 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 ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ValidationException(String.format("invalid property: %s", propertyPath), ex);
+ }
+ 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 ']':
+ throw new IllegalStateException(String.format("Position %s: unexpected '%s'", here, c));
+ case '[':
+ handleIndex(path, pos.next());
+ break;
+ case '.':
+ if (sep) {
+ throw new IllegalStateException(String.format("Position %s: expected property, index/key, or end of expression", here));
+ }
+ sep = true;
+ pos.next();
+ // fall through:
+ default:
+ if (!sep) {
+ throw new IllegalStateException(String.format("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 {
+ int len = path.length();
+ int start = pos.getIndex();
+ loop: while (pos.getIndex() < len) {
+ switch (path.charAt(pos.getIndex())) {
+ case '[':
+ case ']':
+ case '.':
+ break loop;
+ }
+ pos.next();
+ }
+ if (pos.getIndex() > start) {
+ return path.subSequence(start, pos.getIndex()).toString();
+ }
+ throw new IllegalStateException(String.format("Position %s: expected property", start));
+ }
+
+ /**
+ * 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 {
+ int len = path.length();
+ int start = pos.getIndex();
+ if (start < len) {
+ char first = path.charAt(pos.getIndex());
+ if (first == '"' || first == '\'') {
+ String s = 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) {
+ 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();
+ }
+ }
+ }
+ throw new IllegalStateException(String.format("Position %s: unparsable index", start));
+ }
+
+ private static String parseQuotedString(CharSequence path, PathPosition pos) throws Exception {
+ int len = path.length();
+ int start = pos.getIndex();
+ if (start < len) {
+ char quote = path.charAt(start);
+ pos.next();
+ StringWriter w = new StringWriter();
+ while (pos.getIndex() < len) {
+ int here = pos.getIndex();
+ // look for matching quote
+ if (path.charAt(here) == quote) {
+ pos.next();
+ return w.toString();
+ }
+ int codePoints = StringEscapeUtils.UNESCAPE_JAVA.translate(path, here, w);
+ if (codePoints == 0) {
+ w.write(Character.toChars(Character.codePointAt(path, here)));
+ pos.next();
+ } else {
+ for (int i = 0; i < codePoints; i++) {
+ pos.plus(Character.charCount(Character.codePointAt(path, pos.getIndex())));
+ }
+ }
+ }
+ // if reached, reset due to no ending quote found
+ pos.setIndex(start);
+ }
+ return null;
+ }
+
+ /**
+ * ParsePosition/Callback
+ */
+ private static class PathPosition extends ParsePosition implements Callback<Object> {
+ 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}
+ */
+ public void handleProperty(String name) {
+ delegate.handleProperty(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleIndexOrKey(String value) {
+ delegate.handleIndexOrKey(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleGenericInIterable() {
+ delegate.handleGenericInIterable();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object 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: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+public final class Proxies {
+ // 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 (!clazz.getSimpleName().contains("$$")) { // a lot of proxies use this convention to avoid conflicts with inner/anonymous classes
+ return clazz;
+ }
+ return classFor(clazz.getSuperclass());
+ }
+
+ private Proxies() {
+ // no-op
+ }
+}
Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/ValidationContextTraversal.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/ValidationContextTraversal.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/ValidationContextTraversal.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/util/ValidationContextTraversal.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,204 @@
+/*
+ * 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 org.apache.bval.DynamicMetaBean;
+import org.apache.bval.jsr.JsrMetaBeanFactory;
+import org.apache.bval.jsr.UnknownPropertyException;
+import org.apache.bval.jsr.util.PathNavigation.CallbackProcedure;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.IndexedAccess;
+import org.apache.bval.util.KeyedAccess;
+import org.apache.bval.util.PropertyAccess;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+import java.lang.reflect.Type;
+
+/**
+ * {@link ValidationContext} traversal {@link CallbackProcedure}.
+ *
+ * @version $Rev: 1137074 $ $Date: 2011-06-17 18:20:30 -0500 (Fri, 17 Jun 2011) $
+ */
+public class ValidationContextTraversal extends CallbackProcedure {
+ private static class NullSafePropertyAccess extends PropertyAccess {
+
+ /**
+ * Create a new NullSafePropertyAccess instance.
+ *
+ * @param clazz
+ * @param propertyName
+ */
+ public NullSafePropertyAccess(Class<?> clazz, String propertyName) {
+ super(clazz, propertyName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(Object bean) {
+ return bean == null ? null : super.get(bean);
+ }
+ }
+
+ private final ValidationContext<?> validationContext;
+ private Type type;
+ private Class<?> rawType;
+
+ /**
+ * Create a new {@link ValidationContextTraversal} instance.
+ *
+ * @param validationContext
+ */
+ public ValidationContextTraversal(ValidationContext<?> validationContext) {
+ this.validationContext = validationContext;
+ init();
+ }
+
+ /**
+ * Initialize from {@link ValidationContext}.
+ */
+ public void init() {
+ this.rawType = validationContext.getMetaBean().getBeanClass();
+ this.type = this.rawType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleIndexOrKey(String token) {
+ moveDownIfNecessary();
+
+ AccessStrategy access;
+ if (IndexedAccess.getJavaElementType(type) != null) {
+ try {
+ Integer index = token == null ? null : Integer.valueOf(token);
+ access = new IndexedAccess(type, index);
+ validationContext.setCurrentIndex(index);
+ } catch (NumberFormatException e) {
+ throw new UnknownPropertyException(String.format("Cannot parse %s as an array/iterable index", token),
+ e);
+ }
+ } else if (KeyedAccess.getJavaElementType(type) != null) {
+ access = new KeyedAccess(type, token);
+ validationContext.setCurrentKey(token);
+ } else {
+ throw new UnknownPropertyException(String.format("Cannot determine index/key type for %s", type));
+ }
+ Object value = validationContext.getBean();
+ Object child = value == null ? null : access.get(value);
+ setType(child == null ? access.getJavaType() : child.getClass());
+ validationContext.setBean(child,
+ validationContext.getMetaBean().resolveMetaBean(child == null ? rawType : child));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleProperty(String token) {
+ moveDownIfNecessary();
+
+ MetaBean metaBean = validationContext.getMetaBean();
+
+ if (metaBean instanceof DynamicMetaBean) {
+ metaBean = metaBean.resolveMetaBean(ObjectUtils.defaultIfNull(validationContext.getBean(), rawType));
+ validationContext.setMetaBean(metaBean);
+ }
+ MetaProperty mp = metaBean.getProperty(token);
+ if (mp == null) {
+ // TODO this could indicate a property hosted on a superclass; should we shunt the context traversal down a path based on that type?
+
+ PropertyAccess access = new PropertyAccess(rawType, token);
+ if (access.isKnown()) {
+ // add heretofore unknown, but valid, property on the fly:
+ mp = JsrMetaBeanFactory.addMetaProperty(metaBean, access);
+ } else {
+ throw new UnknownPropertyException("unknown property '" + token + "' in " + metaBean.getId());
+ }
+ }
+ validationContext.setMetaProperty(mp);
+ setType(mp.getType());
+ }
+
+ /**
+ * If we currently have a property, navigate the context such that the property becomes the bean, in preparation for
+ * another property.
+ *
+ * @param validationContext
+ */
+ public void moveDownIfNecessary() {
+ MetaProperty mp = validationContext.getMetaProperty();
+ if (mp != null) {
+ if (mp.getMetaBean() == null) {
+ throw new UnknownPropertyException(String.format("Property %s.%s is not cascaded", mp
+ .getParentMetaBean().getId(), mp.getName()));
+ }
+ validationContext.moveDown(mp, new NullSafePropertyAccess(validationContext.getMetaBean().getBeanClass(),
+ mp.getName()));
+ }
+ }
+
+ /**
+ * Set the type of the expression processed thus far.
+ *
+ * @param type
+ */
+ protected void setType(Type type) {
+ this.rawType = TypeUtils.getRawType(type, this.type);
+ this.type = type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleGenericInIterable() {
+ throw new UnsupportedOperationException("Cannot navigate a ValidationContext to []");
+ }
+
+ /**
+ * @return the type
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * @return the rawType
+ */
+ public Class<?> getRawType() {
+ return rawType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void complete() {
+ super.complete();
+ if (validationContext.getMetaProperty() != null) {
+ return;
+ }
+ if (validationContext.getMetaBean() instanceof DynamicMetaBean) {
+ validationContext.setMetaBean(validationContext.getMetaBean().resolveMetaBean(
+ ObjectUtils.defaultIfNull(validationContext.getBean(), rawType)));
+ }
+ }
+}
\ No newline at end of file
Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationIgnores.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationIgnores.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationIgnores.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationIgnores.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,200 @@
+/*
+ * 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.xml;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Description: This class instantiated during the parsing of the XML configuration
+ * data and keeps track of the annotations which should be ignored.<br/>
+ */
+public final class AnnotationIgnores {
+
+ private static final Logger log = Logger.getLogger(AnnotationIgnores.class.getName());
+
+ /**
+ * Keeps track whether the 'ignore-annotations' flag is set on bean level in the
+ * xml configuration.
+ * If 'ignore-annotations' is not specified: default = true
+ */
+ private final Map<Class<?>, Boolean> ignoreAnnotationDefaults = new HashMap<Class<?>, Boolean>();
+
+ /**
+ * Keeps track of explicitly excluded members (fields and properties) for a given class.
+ * If a member appears in
+ * the list mapped to a given class 'ignore-annotations' was explicitly set to
+ * <code>true</code> in the configuration
+ * for this class.
+ */
+ private final Map<Class<?>, Map<Member, Boolean>> ignoreAnnotationOnMember = new HashMap<Class<?>, Map<Member, Boolean>>();
+
+ private final Map<Class<?>, Boolean> ignoreAnnotationOnClass = new HashMap<Class<?>, Boolean>();
+
+ private final Map<Class<?>, Map<Member, Map<Integer, Boolean>>> ignoreAnnotationOnParameter = new HashMap<Class<?>, Map<Member, Map<Integer, Boolean>>>();
+ private final Map<Member, Boolean> ignoreAnnotationOnReturn = new HashMap<Member, Boolean>();
+ private final Map<Member, Boolean> ignoreAnnotationOnCrossParameter = new HashMap<Member, Boolean>();
+
+ /**
+ * Record the ignore state for a particular annotation type.
+ * @param clazz
+ * @param b, default true if null
+ */
+ public void setDefaultIgnoreAnnotation(Class<?> clazz, Boolean b) {
+ ignoreAnnotationDefaults.put(clazz, b == null || b.booleanValue());
+ }
+
+ /**
+ * Learn whether the specified annotation type should be ignored.
+ * @param clazz
+ * @return boolean
+ */
+ public boolean getDefaultIgnoreAnnotation(Class<?> clazz) {
+ return ignoreAnnotationDefaults.containsKey(clazz)
+ && ignoreAnnotationDefaults.get(clazz);
+ }
+
+ /**
+ * Ignore annotations on a particular {@link Member} of a class.
+ * @param member
+ */
+ public void setIgnoreAnnotationsOnMember(Member member, boolean value) {
+ Class<?> beanClass = member.getDeclaringClass();
+ Map<Member, Boolean> memberList = ignoreAnnotationOnMember.get(beanClass);
+ if (memberList == null) {
+ memberList = new HashMap<Member, Boolean>();
+ ignoreAnnotationOnMember.put(beanClass, memberList);
+ }
+ memberList.put(member, value);
+ }
+
+ /**
+ * Learn whether annotations should be ignored on a particular {@link Member} of a class.
+ * @param member
+ * @return boolean
+ */
+ public boolean isIgnoreAnnotations(final Member member) {
+ final Class<?> clazz = member.getDeclaringClass();
+ final Map<Member, Boolean> ignoreAnnotationForMembers = ignoreAnnotationOnMember.get(clazz);
+ if (ignoreAnnotationForMembers != null && ignoreAnnotationForMembers.containsKey(member)) {
+ final boolean value = ignoreAnnotationForMembers.get(member);
+ if (value) {
+ logMessage(member, clazz);
+ }
+ return value;
+ }
+
+ final boolean ignoreAnnotation = getDefaultIgnoreAnnotation(clazz);
+ if (ignoreAnnotation) {
+ logMessage(member, clazz);
+ }
+ return ignoreAnnotation;
+ }
+
+ public void setIgnoreAnnotationsOnParameter(final Member method, final int i, final boolean value) {
+ final Class<?> beanClass = method.getDeclaringClass();
+ Map<Member, Map<Integer, Boolean>> memberList = ignoreAnnotationOnParameter.get(beanClass);
+ if (memberList == null) {
+ memberList = new HashMap<Member, Map<Integer, Boolean>>();
+ ignoreAnnotationOnParameter.put(beanClass, memberList);
+ }
+ Map<Integer, Boolean> indexes = memberList.get(method);
+ if (indexes == null) {
+ indexes = new HashMap<Integer, Boolean>();
+ memberList.put(method, indexes);
+ }
+ indexes.put(i, value);
+ }
+
+ public boolean isIgnoreAnnotationOnParameter(final Member m, final int i) {
+ final Map<Member, Map<Integer, Boolean>> members = ignoreAnnotationOnParameter.get(m.getDeclaringClass());
+ if (members != null) {
+ final Map<Integer, Boolean> indexes = members.get(m);
+ if (indexes != null && indexes.containsKey(i)) {
+ return indexes.get(i);
+ }
+ }
+ return false;
+ }
+
+ private void logMessage(Member member, Class<?> clazz) {
+ String type;
+ if (member instanceof Field) {
+ type = "Field";
+ } else {
+ type = "Property";
+ }
+ log.log(Level.FINEST, String.format("%s level annotations are getting ignored for %s.%s", type, clazz.getName(), member.getName()));
+ }
+
+ /**
+ * Record the ignore state of a particular class.
+ * @param clazz
+ * @param b
+ */
+ public void setIgnoreAnnotationsOnClass(Class<?> clazz, boolean b) {
+ ignoreAnnotationOnClass.put(clazz, b);
+ }
+
+ /**
+ * Learn whether annotations should be ignored for a given class.
+ * @param clazz to check
+ * @return boolean
+ */
+ public boolean isIgnoreAnnotations(Class<?> clazz) {
+ boolean ignoreAnnotation;
+ if (ignoreAnnotationOnClass.containsKey(clazz)) {
+ ignoreAnnotation = ignoreAnnotationOnClass.get(clazz);
+ } else {
+ ignoreAnnotation = getDefaultIgnoreAnnotation(clazz);
+ }
+ if (ignoreAnnotation) {
+ log.log(Level.FINEST, String.format("Class level annotation are getting ignored for %s", clazz.getName()));
+ }
+ return ignoreAnnotation;
+ }
+
+ public void setIgnoreAnnotationOnReturn(final Member method, final boolean value) {
+ ignoreAnnotationOnReturn.put(method, value);
+ }
+
+ public boolean isIgnoreAnnotationOnReturn(final Member m) {
+ final Boolean value = ignoreAnnotationOnReturn.get(m);
+ if (value != null) {
+ return value;
+ }
+ return false;
+ }
+
+ public void setIgnoreAnnotationOnCrossParameter(final Member method, final boolean value) {
+ ignoreAnnotationOnCrossParameter.put(method, value);
+ }
+
+ public boolean isIgnoreAnnotationOnCrossParameter(final Member m) {
+ final Boolean value = ignoreAnnotationOnCrossParameter.get(m);
+ if (value != null) {
+ return value;
+ }
+ return false;
+ }
+}
Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,113 @@
+/*
+ * 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.xml;
+
+import javax.validation.Valid;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Description: <br/>
+ * InvocationHandler implementation of <code>Annotation</code> that pretends it
+ * is a "real" source code annotation.
+ * <p/>
+ */
+class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ private final Class<? extends Annotation> annotationType;
+ private final Map<String, Object> values;
+
+ /**
+ * Create a new AnnotationProxy instance.
+ *
+ * @param <A>
+ * @param descriptor
+ */
+ public <A extends Annotation> AnnotationProxy(AnnotationProxyBuilder<A> descriptor) {
+ this.annotationType = descriptor.getType();
+ values = getAnnotationValues(descriptor);
+ }
+
+ private <A extends Annotation> Map<String, Object> getAnnotationValues(AnnotationProxyBuilder<A> descriptor) {
+ final Map<String, Object> result = new HashMap<String, Object>();
+ int processedValuesFromDescriptor = 0;
+ for (final Method m : descriptor.getMethods()) {
+ if (descriptor.contains(m.getName())) {
+ result.put(m.getName(), descriptor.getValue(m.getName()));
+ processedValuesFromDescriptor++;
+ } else if (m.getDefaultValue() != null) {
+ result.put(m.getName(), m.getDefaultValue());
+ } else {
+ throw new IllegalArgumentException("No value provided for " + m.getName());
+ }
+ }
+ if (processedValuesFromDescriptor != descriptor.size()
+ && !Valid.class.equals(annotationType)) {
+ throw new RuntimeException("Trying to instanciate " + annotationType + " with unknown paramters.");
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (values.containsKey(method.getName())) {
+ return values.get(method.getName());
+ }
+ return method.invoke(this, args);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Class<? extends Annotation> annotationType() {
+ return annotationType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append('@').append(annotationType().getName()).append('(');
+ boolean comma = false;
+ for (String m : getMethodsSorted()) {
+ if (comma)
+ result.append(", ");
+ result.append(m).append('=').append(values.get(m));
+ comma = true;
+ }
+ result.append(")");
+ return result.toString();
+ }
+
+ private SortedSet<String> getMethodsSorted() {
+ SortedSet<String> result = new TreeSet<String>();
+ result.addAll(values.keySet());
+ return result;
+ }
+}
Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,256 @@
+/*
+ * 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.xml;
+
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.util.reflection.Reflection;
+
+import javax.validation.Payload;
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.groups.ConvertGroup;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Description: Holds the information and creates an annotation proxy during xml
+ * parsing of validation mapping constraints. <br/>
+ */
+// TODO move this guy up to org.apache.bval.jsr or
+// org.apache.bval.jsr.model
+final public class AnnotationProxyBuilder<A extends Annotation> {
+ private static final ConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new ConcurrentHashMap<Class<?>, Method[]>();
+
+ private final Class<A> type;
+ private final Map<String, Object> elements = new HashMap<String, Object>();
+ private final Method[] methods;
+
+ /**
+ * Create a new AnnotationProxyBuilder instance.
+ *
+ * @param annotationType
+ */
+ public AnnotationProxyBuilder(final Class<A> annotationType) {
+ this.type = annotationType;
+ this.methods = findMethods(annotationType);
+ }
+
+ public static <A> Method[] findMethods(final Class<A> annotationType) {
+ if (annotationType.getName().startsWith("javax.validation.constraints.")) { // cache built-in constraints only to avoid mem leaks
+ Method[] mtd = METHODS_CACHE.get(annotationType);
+ if (mtd == null) {
+ final Method[] value = Reflection.INSTANCE.getDeclaredMethods(annotationType);
+ mtd = METHODS_CACHE.putIfAbsent(annotationType, value);
+ if (mtd == null) {
+ mtd = value;
+ }
+ }
+ return mtd;
+ }
+ return Reflection.INSTANCE.getDeclaredMethods(annotationType);
+ }
+
+ /**
+ * Create a new AnnotationProxyBuilder instance.
+ *
+ * @param annotationType
+ * @param elements
+ */
+ public AnnotationProxyBuilder(Class<A> annotationType, Map<String, Object> elements) {
+ this(annotationType);
+ for (Map.Entry<String, Object> entry : elements.entrySet()) {
+ this.elements.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Create a builder initially configured to create an annotation equivalent
+ * to <code>annot</code>.
+ *
+ * @param annot Annotation to be replicated.
+ */
+ @SuppressWarnings("unchecked")
+ public AnnotationProxyBuilder(A annot) {
+ this((Class<A>) annot.annotationType());
+ // Obtain the "elements" of the annotation
+ for (Method m : methods) {
+ if (!m.isAccessible()) {
+ m.setAccessible(true);
+ }
+ try {
+ Object value = m.invoke(annot);
+ this.elements.put(m.getName(), value);
+ } catch (IllegalArgumentException e) {
+ // No args, so should not happen
+ throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+ } catch (IllegalAccessException e) {
+ throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+ } catch (InvocationTargetException e) {
+ throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+ }
+ }
+ }
+
+ public Method[] getMethods() {
+ return methods;
+ }
+
+ /**
+ * Add an element to the configuration.
+ *
+ * @param elementName
+ * @param value
+ */
+ public void putValue(String elementName, Object value) {
+ elements.put(elementName, value);
+ }
+
+ /**
+ * Get the specified element value from the current configuration.
+ *
+ * @param elementName
+ * @return Object value
+ */
+ public Object getValue(String elementName) {
+ return elements.get(elementName);
+ }
+
+ /**
+ * Learn whether a given element has been configured.
+ *
+ * @param elementName
+ * @return <code>true</code> if an <code>elementName</code> element is found
+ * on this annotation
+ */
+ public boolean contains(String elementName) {
+ return elements.containsKey(elementName);
+ }
+
+ /**
+ * Get the number of configured elements.
+ *
+ * @return int
+ */
+ public int size() {
+ return elements.size();
+ }
+
+ /**
+ * Get the configured Annotation type.
+ *
+ * @return Class<A>
+ */
+ public Class<A> getType() {
+ return type;
+ }
+
+ /**
+ * Configure the well-known JSR303 "message" element.
+ *
+ * @param message
+ */
+ public void setMessage(String message) {
+ ConstraintAnnotationAttributes.MESSAGE.put(elements, message);
+ }
+
+ /**
+ * Configure the well-known JSR303 "groups" element.
+ *
+ * @param groups
+ */
+ public void setGroups(Class<?>[] groups) {
+ ConstraintAnnotationAttributes.GROUPS.put(elements, groups);
+ }
+
+ /**
+ * Configure the well-known JSR303 "payload" element.
+ *
+ * @param payload
+ */
+ public void setPayload(Class<? extends Payload>[] payload) {
+ ConstraintAnnotationAttributes.PAYLOAD.put(elements, payload);
+ }
+
+ /**
+ * Create the annotation represented by this builder.
+ *
+ * @return {@link Annotation}
+ */
+ public A createAnnotation() {
+ ClassLoader classLoader = Reflection.INSTANCE.getClassLoader(getType());
+ @SuppressWarnings("unchecked")
+ final Class<A> proxyClass = (Class<A>) Proxy.getProxyClass(classLoader, getType());
+ final InvocationHandler handler = new AnnotationProxy(this);
+ if (System.getSecurityManager() == null) {
+ return doCreateAnnotation(proxyClass, handler);
+ }
+ return AccessController.doPrivileged(new PrivilegedAction<A>() {
+ public A run() {
+ return doCreateAnnotation(proxyClass, handler);
+ }
+ });
+ }
+
+ private A doCreateAnnotation(final Class<A> proxyClass, final InvocationHandler handler) {
+ try {
+ Constructor<A> constructor = proxyClass.getConstructor(InvocationHandler.class);
+ return constructor.newInstance(handler);
+ } catch (Exception e) {
+ throw new ValidationException("Unable to create annotation for configured constraint", e);
+ }
+ }
+
+ public static final class ValidAnnotation implements Valid {
+ public static final ValidAnnotation INSTANCE = new ValidAnnotation();
+
+ public Class<? extends Annotation> annotationType() {
+ return Valid.class;
+ }
+ }
+
+ public static final class ConverGroupAnnotation implements ConvertGroup {
+ private final Class<?> from;
+ private final Class<?> to;
+
+ public ConverGroupAnnotation(final Class<?> from, final Class<?> to) {
+ this.from = from;
+ this.to = to;
+ }
+
+ public Class<? extends Annotation> annotationType() {
+ return ConvertGroup.class;
+ }
+
+ public Class<?> from() {
+ return from;
+ }
+
+ public Class<?> to() {
+ return to;
+ }
+ }
+}
Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/MetaConstraint.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/MetaConstraint.java?rev=1517540&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/MetaConstraint.java (added)
+++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/xml/MetaConstraint.java Mon Aug 26 13:59:15 2013
@@ -0,0 +1,124 @@
+/*
+ * 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.xml;
+
+
+import org.apache.bval.ConstructorAccess;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.FieldAccess;
+import org.apache.bval.util.MethodAccess;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+/**
+ * Description: hold parsed information from xml to complete MetaBean later<br/>
+ */
+//TODO move this guy up to org.apache.bval.jsr or org.apache.bval.jsr.model
+//to decouple ApacheValidatorFactory from xml package and allow others to consume MetaConstraint
+public class MetaConstraint<T, A extends Annotation> {
+
+ /** The member the constraint was defined on. */
+ private final Member member;
+
+ /** The class of the bean hosting this constraint. */
+ private final Class<T> beanClass;
+
+ /** constraint annotation (proxy) */
+ private final A annotation;
+
+ private Integer index; // for parameters
+
+ private final AccessStrategy accessStrategy;
+
+ /**
+ * Create a new MetaConstraint instance.
+ * @param beanClass The class in which the constraint is defined on
+ * @param member The member on which the constraint is defined on, {@code null} if it is a class constraint}
+ * @param annotation
+ */
+ public MetaConstraint(Class<T> beanClass, Member member, A annotation) {
+ this.member = member;
+ this.beanClass = beanClass;
+ this.annotation = annotation;
+ if (member != null) {
+ accessStrategy = createAccessStrategy(member);
+ /*TODO: see if can really be removed
+ if (accessStrategy == null || accessStrategy.getPropertyName() == null) { // can happen if method does not follow the bean convention
+ throw new ValidationException("Annotated method does not follow the JavaBeans naming convention: " + member);
+ }
+ */
+ } else {
+ this.accessStrategy = null;
+ }
+ }
+
+ private static AccessStrategy createAccessStrategy(Member member) {
+ if (member instanceof Method) {
+ return new MethodAccess((Method) member);
+ } else if (member instanceof Field) {
+ return new FieldAccess((Field) member);
+ } else if (member instanceof Constructor<?>) {
+ return new ConstructorAccess((Constructor<?>) member);
+ } else {
+ return null; // class level
+ }
+ }
+
+ /**
+ * Get the bean class of this constraint.
+ * @return Class
+ */
+ public Class<T> getBeanClass() {
+ return beanClass;
+ }
+
+ /**
+ * Get the member to which this constraint applies.
+ * @return Member
+ */
+ public Member getMember() {
+ return member;
+ }
+
+ /**
+ * Get the annotation that defines this constraint.
+ * @return Annotation
+ */
+ public A getAnnotation() {
+ return annotation;
+ }
+
+ /**
+ * Get the access strategy used for the associated property.
+ * @return {@link AccessStrategy}
+ */
+ public AccessStrategy getAccessStrategy() {
+ return accessStrategy;
+ }
+
+ public Integer getIndex() {
+ return index;
+ }
+
+ public void setIndex(final int index) {
+ this.index = index;
+ }
+}