nl.ucan.navigate.NestedPath.java Source code

Java tutorial

Introduction

Here is the source code for nl.ucan.navigate.NestedPath.java

Source

package nl.ucan.navigate;

import org.apache.commons.beanutils.*;
import org.apache.commons.beanutils.expression.Resolver;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.GenericTypeResolver;

import java.lang.reflect.*;
import java.beans.PropertyDescriptor;
import java.beans.IntrospectionException;
import java.util.*;

/*
 * Copyright 2007-2008 the original author or authors.
 *
 * Licensed 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.
 *
 * author : Arnold Reuser
 * since  : 0.2.5
 */
public class NestedPath {
    private static Log log = LogFactory.getLog(NestedPath.class);
    private PropertyUtilsBean pub;
    private PropertyInstance propertyInstance;
    private PropertyValue propertyValue;
    private IndexPointer indexPointer;

    public static void setNested(char nested) {
        ResolverImpl.setNested(nested);
    }

    public static void setMappedStart(char mappedStart) {
        ResolverImpl.setMappedStart(mappedStart);
    }

    public static void setMappedEnd(char mappedEnd) {
        ResolverImpl.setMappedEnd(mappedEnd);
    }

    public static void setIndexedStart(char indexedStart) {
        ResolverImpl.setIndexedStart(indexedStart);
    }

    public static void setIndexedEnd(char indexedEnd) {
        ResolverImpl.setIndexedEnd(indexedEnd);
    }

    public NestedPath setPropertyInstance(PropertyInstance propertyInstance) {
        this.propertyInstance = propertyInstance;
        return this;
    }

    public NestedPath setPropertyValue(PropertyValue propertyValue) {
        this.propertyValue = propertyValue;
        return this;
    }

    public NestedPath setIndexPointer(IndexPointer indexPointer) {
        this.indexPointer = indexPointer;
        return this;
    }

    public static NestedPath getInstance() {
        return new NestedPath();
    }

    private NestedPath() {
        this.propertyInstance = new PropertyInstance() {
            public Object indexed(Object bean, String property, int index, Object value) {
                log.info("created indexed property " + property + " at " + index + " of bean " + bean
                        + " and will be set to " + value);
                return value;
            }

            public Object simple(Object bean, String property, Object value) {
                log.info("created simple property " + property + " of bean " + bean + " and will be set to "
                        + value);
                return value;
            }
        };
        this.propertyValue = new PropertyValue() {
            public Object indexed(Object bean, String property, int index, Object value) {
                log.info("value of indexed property " + property + " at " + index + " of bean " + bean
                        + " will be set to " + value);
                return value;
            }

            public Object mapped(Object bean, String property, Object key, Object value) {
                log.info("value of mapped property " + property + " at " + key + " of bean " + bean
                        + " will be set to " + value);
                return value;
            }

            public Object simple(Object bean, String property, Object value) {
                log.info("value of simple property " + property + " of bean " + bean + " will be set to " + value);
                return value;
            }

            public Object valueOf(Class clasz, String property, String value) {
                log.info("value of valueOf " + property + " will be set to " + value);
                return value;
            }
        };
        this.indexPointer = new IndexPointer() {
            public int size(Object bean) {
                return CollectionUtils.size(bean);
            }

            public Object get(Object bean, int idx) {
                return CollectionUtils.get(bean, idx);
            }

            public int firstIndexOf(Object bean, String undeterminedIndex)
                    throws IllegalAccessException, InvocationTargetException, NoSuchMethodException,
                    InstantiationException, IntrospectionException {
                this.setUndeterminedIndex(undeterminedIndex);
                for (int idx = 0; idx < size(bean); idx++) {
                    Object object = get(bean, idx);
                    if (object != null) {
                        if (evaluate(object, this.getUndeterminedIndex()))
                            return idx;
                    }
                }
                return -1;
            }

            public void setIndexAsProperty(Object bean, String undeterminedIndex)
                    throws IllegalAccessException, InvocationTargetException, NoSuchMethodException,
                    InstantiationException, IntrospectionException {
                this.setUndeterminedIndex(undeterminedIndex);
                Object value = propertyValue.simple(bean, this.getProperty(), this.getValue());
                pub.setProperty(bean, this.getProperty(), value);
            }

            private String undeterminedIndex;

            private void setUndeterminedIndex(String undeterminedIndex) {
                this.undeterminedIndex = undeterminedIndex;
            }

            private String getUndeterminedIndex() {
                return this.undeterminedIndex;
            }

            private boolean evaluate(Object bean, String undeterminedIndex)
                    throws IllegalAccessException, InvocationTargetException, NoSuchMethodException,
                    InstantiationException, IntrospectionException {
                this.setUndeterminedIndex(undeterminedIndex);
                String property = getProperty();
                String valueOfIndex = getValue();
                Object valueOfBean = pub.getProperty(bean, property);
                return ObjectUtils.equals(valueOfIndex, valueOfBean);
            }

            private String getProperty() {
                Map.Entry<String, String> entry = getNamedIndex(this.getUndeterminedIndex());
                return entry.getKey();
            }

            private String getValue() {
                Map.Entry<String, String> entry = getNamedIndex(this.getUndeterminedIndex());
                return entry.getValue();
            }

            private Map.Entry<String, String> getNamedIndex(String value) {
                final String SEP = "=";
                Map<String, String> keyValuePair = new HashMap<String, String>();
                if (StringUtils.indexOf(value, SEP) == -1)
                    return null;
                keyValuePair.put(StringUtils.substringBefore(value, SEP), StringUtils.substringAfter(value, SEP));
                return keyValuePair.entrySet().iterator().next();
            }
        };
        this.pub = BeanUtilsBean.getInstance().getPropertyUtils();
        this.pub.setResolver(new ResolverImpl());
    }

    public void populate(Object bean, String path, Object value) throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, InstantiationException, IntrospectionException {
        PathContext pathContext = acquirePathContext(bean, path);
        setProperty(pathContext, value);
    }

    public Object extract(Object bean, String path) throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException, InstantiationException, IntrospectionException {
        PathContext pathContext = acquirePathContext(bean, path);
        return pub.getProperty(bean, pathContext.toString());
    }

    private Object getLastInstance(PathContext pathContext) throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, InstantiationException, IntrospectionException {
        if (pathContext.getPaths().size() <= 1) {
            return pathContext.getBean();
        } else {
            List subListOfPaths = pathContext.getPaths().subList(0, pathContext.getPaths().size() - 1);
            return pub.getProperty(pathContext.getBean(), pathContext.toString(subListOfPaths));
        }
    }

    private String getLastElement(PathContext pathContext) {
        return pathContext.getPaths().get(pathContext.getPaths().size() - 1);
    }

    private void setProperty(PathContext pathContext, Object value) throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, InstantiationException, IntrospectionException {
        if (pathContext.getPaths().size() > 0) {
            String lastPathElement = getLastElement(pathContext);
            Object lastPathInstance = getLastInstance(pathContext);
            Resolver resolver = pub.getResolver();
            String prop = resolver.getProperty(lastPathElement);
            if (resolver.isMapped(lastPathElement)) {
                //
                // mapped property
                String key = resolver.getKey(lastPathElement);
                value = propertyValue.mapped(lastPathInstance, prop, key, value);
            } else if (resolver.isIndexed(lastPathElement)) {
                //
                // indexed property
                int idx = resolver.getIndex(lastPathElement);
                value = propertyValue.indexed(lastPathInstance, prop, idx, value);
            } else {
                //
                // simple property
                value = propertyValue.simple(lastPathInstance, lastPathElement, value);
            }
            pub.setProperty(lastPathInstance, lastPathElement, value);
        }
    }

    public PathContext acquirePathContext(Object bean, String path) throws IllegalAccessException,
            InvocationTargetException, InstantiationException, IntrospectionException, NoSuchMethodException {
        PathContext pathContext = new PathContext(ResolverImpl.getNested());
        Object instance = bean;
        DynaBean dynaBean = getDynaBean(instance);
        pub.copyProperties(instance, bean);
        Resolver resolver = pub.getResolver();
        for (; StringUtils.isNotBlank(path); path = resolver.remove(path)) {
            String prop = resolver.getProperty(path);
            if (resolver.isIndexed(path)) {
                String undeterminedIdx = getUndeterminedIndex(path);
                int positionalIdx = getPositionalIndex(undeterminedIdx);
                if (positionalIdx == -1) {
                    Object object = pub.getProperty(dynaBean, prop);
                    int idx = indexPointer.firstIndexOf(object, undeterminedIdx);
                    if (idx == -1) {
                        Object nestedBean = (pathContext.noPathSpecified() ? bean
                                : pub.getProperty(bean, pathContext.toString()));
                        instance = createInstanceOfCollection(instance.getClass(), prop);
                        indexPointer.setIndexAsProperty(instance, undeterminedIdx);
                        instance = propertyInstance.indexed(nestedBean, prop, indexPointer.size(object), instance);
                        String replace = namedToPositioned(resolver.next(path), indexPointer.size(object));
                        pub.setIndexedProperty(dynaBean, replace, instance); // create object at required position
                        pub.setProperty(bean, (StringUtils.isBlank(pathContext.toString()) ? prop
                                : pathContext.toString() + ResolverImpl.getNested() + prop), object);
                        pathContext.addPart(replace);
                    } else {
                        String replace = namedToPositioned(resolver.next(path), idx);
                        pathContext.addPart(replace);
                        instance = indexPointer.get(object, idx);
                    }
                } else {
                    Object tmp = pub.getIndexedProperty(dynaBean, prop, positionalIdx); // create object at required position
                    if (tmp == null) {
                        Object nestedBean = (pathContext.noPathSpecified() ? bean
                                : pub.getProperty(bean, pathContext.toString()));
                        growCollection(instance, prop, positionalIdx);
                        instance = createInstanceOfCollection(instance.getClass(), prop);
                        instance = propertyInstance.indexed(nestedBean, prop, positionalIdx, instance);
                        pathContext.addPart(resolver.next(path));
                        pub.setProperty(bean, pathContext.toString(), instance);
                    } else {
                        pathContext.addPart(resolver.next(path));
                        instance = tmp;
                    }
                }
            } else {
                instance = pub.getProperty(instance, prop);
                if (instance == null) {
                    instance = pub.getProperty(dynaBean, prop);
                    Object nestedBean = (pathContext.noPathSpecified() ? bean
                            : pub.getProperty(bean, pathContext.toString()));
                    instance = propertyInstance.simple(nestedBean, prop, instance);
                    pathContext.addPart(resolver.next(path));
                    String tmp = pathContext.toString();
                    pub.setProperty(bean, tmp, instance);
                } else {
                    pathContext.addPart(resolver.next(path));
                }
            }
            if (resolver.hasNested(path))
                dynaBean = getDynaBean(instance);
        }
        pathContext.setBean(bean);
        return pathContext;
    }

    private void growCollection(Object instance, String prop, int positionalIdx) throws NoSuchMethodException,
            IntrospectionException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Object propInstance = pub.getProperty(instance, prop);
        if (propInstance != null) {
            if (propInstance.getClass().isArray()) {
                Object[] elementData = (Object[]) propInstance;
                int oldCapacity = elementData.length;
                int minCapacity = positionalIdx + 1;
                if (minCapacity > oldCapacity) {
                    Object oldData[] = elementData;
                    int newCapacity = (oldCapacity * 3) / 2 + 1;
                    if (newCapacity < minCapacity)
                        newCapacity = minCapacity;
                    elementData = Arrays.copyOf(elementData, newCapacity);
                    pub.setProperty(instance, prop, elementData);
                }
                if (elementData[positionalIdx] == null) {
                    Class clasz = elementData.getClass().getComponentType();
                    Constructor constructor = ConstructorUtils.getAccessibleConstructor(clasz, new Class[] {});
                    elementData[positionalIdx] = constructor.newInstance();
                }

            }
        }
    }

    private static String namedToPositioned(String next, int position) {
        String substr = StringUtils.substringBetween(next, "" + ResolverImpl.getIndexedStart(),
                "" + ResolverImpl.getIndexedEnd());
        return StringUtils.replace(next, substr, "" + position);
    }

    private static DynaBean getDynaBean(Object instance)
            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        WrapDynaClass dynaClass = WrapDynaClass.createDynaClass(instance.getClass());
        LazyDynaBean lazyBean = new LazyDynaBean(dynaClass);
        PropertyUtils.copyProperties(lazyBean, instance);
        return lazyBean;
    }

    private static Object createInstanceOfCollection(Class clasz, String prop) throws IntrospectionException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        Class type = getCollectionReturnType(prop, clasz);
        Constructor constructor = ConstructorUtils.getAccessibleConstructor(type, new Class[] {});
        return constructor.newInstance();
    }

    private static Class getCollectionReturnType(String property, Class clasz) throws IntrospectionException {
        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(property, clasz);
        Method method = propertyDescriptor.getReadMethod();
        Class klass = GenericCollectionTypeResolver.getCollectionReturnType(method);
        if (klass == null) {
            klass = GenericTypeResolver.resolveReturnType(method, clasz);
            if (klass.isArray()) {
                return klass.getComponentType();
            } else {
                throw new IllegalStateException("unsupported return type");
            }
        } else
            return klass;
    }

    private static String getUndeterminedIndex(String expression) {
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (c == ResolverImpl.getNested() || c == ResolverImpl.getMappedStart()) {
                return null;
            } else if (c == ResolverImpl.getIndexedStart()) {
                int end = expression.indexOf(ResolverImpl.getIndexedEnd(), i);
                if (end < 0) {
                    throw new IllegalArgumentException("Missing End Delimiter");
                }
                String value = expression.substring(i + 1, end);
                if (value.length() == 0) {
                    throw new IllegalArgumentException("No Index Value");
                }
                return value;
            }
        }
        return null;
    }

    private static int getPositionalIndex(String value) {
        int index = 0;
        try {
            index = Integer.parseInt(value, 10);
        } catch (Exception e) {
            return -1;
        }
        return index;
    }

}