All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.bval.jsr303.NestedMetaProperty Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 * 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.jsr303;


import org.apache.bval.model.MetaBean;
import org.apache.bval.model.MetaProperty;
import org.apache.bval.util.PropertyAccess;
import org.apache.commons.lang.StringUtils;

import javax.validation.ValidationException;
import java.lang.reflect.*;
import java.util.*;

/**
 * Description: handle nested property paths 
*/ final class NestedMetaProperty { private MetaBean metaBean; private MetaProperty metaProperty; private final String propertyPath; private boolean nested; private Object value; /** * Create a new NestedMetaProperty instance. * @param path * @param value */ public NestedMetaProperty(String path, Object value) { this.propertyPath = path; this.value = value; } /** * Parse this {@link NestedMetaProperty}. */ void parse() { try { StringTokenizer tokens = new StringTokenizer(propertyPath, ".[]", true); while (tokens.hasMoreTokens()) { String token = tokens.nextToken(); if ("[".equals(token)) { String sindex = tokens.nextToken(); int idx = Integer.parseInt(sindex); token = tokens.nextToken(); if (!"]".equals(token)) { throw new ValidationException( "']' missing, invalid property format: " + propertyPath); } useIndexedValue(idx); resolveMetaBean(); } else if (!".".equals(token)) { // it is a property name MetaProperty mp = getMetaBean().getProperty(token); if (mp == null) { throw new UnknownPropertyException( "unknown property '" + token + "' in " + getMetaBean().getId()); } if (getValue() != null) { setValue(PropertyAccess.getProperty(getValue(), token)); } setMetaProperty(mp); resolveMetaBean(); } } } catch (ValidationException ex) { throw ex; // route exception } catch (Exception ex) { // wrap exception throw new ValidationException( "invalid property format: " + propertyPath, ex); } } /** * Get the resolved MetaProperty. * @return MetaProperty */ public MetaProperty getMetaProperty() { return metaProperty; } /** * Get the property path. * @return String path */ public String getPropertyPath() { return propertyPath; } /** * Learn whether the {@link MetaProperty} represented is indeed nested. * @return boolean */ public boolean isNested() { return nested; } /** * Set the MetaProperty directly * @param aMetaProperty to set */ public void setMetaProperty(MetaProperty aMetaProperty) { if (this.metaProperty != null) { this.nested = true; } this.metaProperty = aMetaProperty; } /** * Get the resolved MetaBean. * @return MetaBean */ public MetaBean getMetaBean() { return metaBean; } /** * Set the MetaBean directly * @param metaBean to set */ public void setMetaBean(MetaBean metaBean) { this.metaBean = metaBean; } /** * Get the property value referenced. * @return Object */ public Object getValue() { return value; } /** * Set the property value directly. * @param value to set */ public void setValue(Object value) { this.value = value; } private void useIndexedValue(int idx) { setValue(getAtIndex(getValue(), idx)); } private Object getAtIndex(Object value, int idx) { if (value == null) return null; if (value instanceof Iterable) { Iterator iter = ((Iterable) value).iterator(); for (int i = 0; i <= idx; i++) { value = iter.next(); } return value; } else if (value.getClass().isArray()) { return getAtIndex(Arrays.asList(value), idx); } else { throw new ValidationException("cannot access indexed value from " + value); } } /** * Get the declared type of a Member. * @param member * @return generic type */ static Type typeOf(Member member) { if (member instanceof Field) { return ((Field) member).getGenericType(); } if (member instanceof Method) { return ((Method) member).getGenericReturnType(); } throw new IllegalArgumentException("Member " + member + " is neither a field nor a method"); } /** * Get the component type of an indexed type. * @param type * @return Type */ static Type getIndexedType(Type type) { Type indexedType = type; if (isCollection(type) && type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType) type; Class collectionClass = getCollectionClass(type); if (Collection.class.isAssignableFrom(collectionClass)) { indexedType = paramType.getActualTypeArguments()[0]; } else if (Map.class.isAssignableFrom(collectionClass)) { indexedType = paramType.getActualTypeArguments()[1]; } } else if (isArray(type) && type instanceof GenericArrayType) { GenericArrayType arrayTye = (GenericArrayType) type; indexedType = arrayTye.getGenericComponentType(); } return indexedType; } /** * Resolve the raw type of a Collection. * @param type * @return Class if found */ @SuppressWarnings("unchecked") static Class> getCollectionClass(Type type) { if (type instanceof Class && isCollectionClass((Class) type)) { return (Class>) type; } if (type instanceof ParameterizedType) { return getCollectionClass(((ParameterizedType) type).getRawType()); } if (type instanceof WildcardType) { Type[] upperBounds = ((WildcardType) type).getUpperBounds(); if (upperBounds.length == 0) { return null; } return getCollectionClass(upperBounds[0]); } return null; } /** * Learn whether a particular type represents an array. * @param type * @return boolean */ static boolean isArray(Type type) { if (type instanceof Class) { return ((Class) type).isArray(); } return type instanceof GenericArrayType; } /** * Learn whether type is a {@link Collection} type. * @param type the type to check. * @return Returns true if type is a collection type or false otherwise. */ static boolean isCollection(Type type) { return getCollectionClass(type) != null; } /** * Learn whether clazz implements either {@link Collection} or {@link Map}. * @param clazz * @return boolean */ //TODO should all these Collection references be Iterable? static boolean isCollectionClass(Class clazz) { return Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz); } // enhancement: HACK ALERT! improve support for nested property types, this is not correct in all cases private void resolveMetaBean() { if (metaProperty.getMetaBean() == null) { return; } if (getValue() != null) { metaBean = metaProperty.getMetaBean().resolveMetaBean(getValue()); } else { Member member = null; try { member = metaBean.getBeanClass().getDeclaredField(metaProperty.getName()); } catch (NoSuchFieldException e) { String getter = "get" + StringUtils.capitalize(metaProperty.getName()); try { member = metaBean.getBeanClass().getDeclaredMethod(getter); } catch (NoSuchMethodException e1) { try { member = metaBean.getBeanClass().getField(metaProperty.getName()); } catch (NoSuchFieldException e2) { try { member = metaBean.getBeanClass().getMethod(getter); } catch (NoSuchMethodException e3) { } } } } if (member != null) { Type type = getIndexedType(typeOf(member)); if (type != null) { metaBean = metaProperty.getMetaBean().resolveMetaBean(type); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy