jp.terasoluna.fw.beans.JXPathIndexedBeanWrapperImpl.java Source code

Java tutorial

Introduction

Here is the source code for jp.terasoluna.fw.beans.JXPathIndexedBeanWrapperImpl.java

Source

/*
 * Copyright (c) 2007 NTT DATA Corporation
 *
 * 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.
 */

package jp.terasoluna.fw.beans;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import jp.terasoluna.fw.beans.jxpath.BeanPointerFactoryEx;
import jp.terasoluna.fw.beans.jxpath.DynamicPointerFactoryEx;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathException;
import org.apache.commons.jxpath.Pointer;
import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * JXPath??IndexBeanWrapper?
 * <p>
 * JavaBean?Map?DynaBean???????? ???????? ??List???????
 * <h5>?????</h5>
 * <ul>
 * <li></li>
 * <li>??</li>
 * <li>JavaBean</li>
 * <li>JavaBean??List</li>
 * <li>Map</li>
 * </ul>
 * </p>
 * <p>
 * Map????Map??? ??Map??????
 * <ul>
 * <li>/ </li>
 * <li>[ ?????</li>
 * <li>] ????</li>
 * <li>. </li>
 * <li>' </li>
 * <li>" </li>
 * <li>( ?????</li>
 * <li>) ????</li>
 * </ul>
 * </p>
 * <hr>
 * <h4>??</h4>
 * <p>
 * ???Employee?firstName??
 * 
 * <pre>
 * public class Employee {
 *     private String firstName;
 *
 *     public void setFirstName(String firstName) {
 *         this.firstName = firstName;
 *     }
 * 
 *     public String getFirstName() {
 *         return firstName;
 *     }
 * }
 * </pre>
 * </p>
 * <p>
 * <u>??JavaBean?</u>
 * 
 * <pre>
 * // ??Employee
 * Employee emp = new Employee();
 * emp.setFirstName("??");
 * 
 * IndexedBeanWrapper bw = new JXPathIndexedBeanWrapperImpl(emp);
 * </pre>
 * </p>
 * <p>
 * <u>firstName??</u> ?String?????
 * 
 * <pre>
 * Map&lt;String, Object&gt; map = bw.getIndexedPropertyValues(
 *         "<strong>firstName</strong>");
 * </pre>
 * 
 * ??????Map?? ??????????
 * 
 * <pre>
 * System.out.println("Map?Map?");
 * System.out.println("========================");
 * Set&lt;String&gt; keyset = map.keySet();
 * for (String key : keyset) {
 *     System.out.print(key + ":");
 *     System.out.println(map.get(key).toString());
 * }
 * </pre>
 * 
 * ??????
 * 
 * <pre>
 * Map?Map?
 * ========================
 * firstName:??
 * </pre>
 * </p>
 * <hr>
 * <h4>???</h4>
 * <p>
 * ???Address??numbers??
 * 
 * <pre>
 * public class Address {
 *     private int[] numbers;
 *
 *     public void setNumbers(int[] numbers) {
 *         this.numbers = numbers;
 *     }
 * 
 *     public int[] getNumbers() {
 *         return numbers;
 *     }
 * }
 * </pre>
 * </p>
 * <p>
 * <u>??JavaBean?</u>
 * 
 * <pre>
 * // Employee???Address
 * Address address = new Address();
 * address.setNumbers(new int[] { 1, 2, 3 });
 * 
 * IndexedBeanWrapper bw = new JXPathIndexedBeanWrapperImpl(address);
 * </pre>
 * </p>
 * <p>
 * <u>numbers??</u> <em>'numbers[]'????????????
 * ????????????</em>
 * 
 * <pre>
 * Map&lt;String, Object&gt; map = bw.getIndexedPropertyValues(
 *         "<strong>numbers</strong>");
 * </pre>
 * </p>
 * ??????Map?? ??????????
 * 
 * <pre>
 * System.out.println("Map?Map?");
 * System.out.println("========================");
 * Set&lt;String&gt; keyset = map.keySet();
 * for (String key : keyset) {
 *     System.out.print(key + ":");
 *     System.out.println(map.get(key).toString());
 * }
 * </pre>
 * 
 * ??????
 * 
 * <pre>
 * Map?Map?
 * ========================
 * numbers[0]:1
 * numbers[1]:2
 * numbers[2]:3
 * </pre>
 * 
 * List?????????????
 * </p>
 * <hr>
 * <h4>?????</h4>
 * <p>
 * ???Employee?? ???Address?streetNumber??
 * 
 * <pre>
 * public class Employee {
 *     private Address homeAddress;
 *
 *     public void setHomeAddress(Address homeAddress) {
 *         this.homeAddress = homeAddress;
 *     }
 * 
 *     public Address getHomeAddress() {
 *         return homeAddress;
 *     }
 * }
 * 
 * public class Address {
 *     private String streetNumber;
 *
 *     public void setStreetNumber(String streetNumber) {
 *         this.streetNumber = streetNumber;
 *     }
 * 
 *     public String getStreetNumber() {
 *         return streetNumber;
 *     }
 * }
 * </pre>
 * </p>
 * <p>
 * <u>??JavaBean?</u>
 * 
 * <pre>
 * // Employee???Address
 * Address address = new Address();
 * address.setStreetNumber("?");
 * 
 * // Employee
 * Employee emp = new Employee();
 * emp.setHomeAddress(address);
 * 
 * IndexedBeanWrapper bw = new JXPathIndexedBeanWrapperImpl(emp);
 * </pre>
 * </p>
 * <p>
 * <u>Employee?homeAddress????? streetNumber??</u> ??????????'.'? ????
 * 
 * <pre>
 * Map&lt;String, Object&gt; map = bw.getIndexedPropertyValues(
 *         "<strong>homeAddress.streetNumber</strong>");
 * </pre>
 * </p>
 * ??????Map?? ??????????
 * 
 * <pre>
 * System.out.println("Map?Map?");
 * System.out.println("========================");
 * Set&lt;String&gt; keyset = map.keySet();
 * for (String key : keyset) {
 *     System.out.print(key + ":");
 *     System.out.println(map.get(key).toString());
 * }
 * </pre>
 * 
 * ??????
 * 
 * <pre>
 * Map?Map?
 * ========================
 * homeAddress.streetNumber:?
 * </pre>
 * 
 * ?????List?????????????
 * </p>
 * <hr>
 * <h4>Map??</h4>
 * <p>
 * ???Employee?MapaddressMap??
 * 
 * <pre>
 * public class Employee {
 *     private Map addressMap;
 *
 *     public void setAddressMap(Map addressMap) {
 *         this.addressMap = addressMap;
 *     }
 * 
 *     public Map getAddressMap() {
 *         return addressMap;
 *     }
 * }
 * </pre>
 * </p>
 * <p>
 * <u>??JavaBean?</u>
 * 
 * <pre>
 * // Employee???Map
 * Map addressMap = new HashMap();
 * addressMap.put("home", "address1");
 * 
 * // Employee
 * Employee emp = new Employee();
 * emp.setAddressMap(addressMap);
 * 
 * IndexedBeanWrapper bw = new JXPathIndexedBeanWrapperImpl(emp);
 * </pre>
 * </p>
 * <p>
 * <u>Employee?addressMap???home??</u> Map????????????????
 * 
 * <pre>
 * Map&lt;String, Object&gt; map = bw.getIndexedPropertyValues(
 *         "<strong>addressMap(home)</strong>");
 * </pre>
 * </p>
 * ??????Map?? ??????????
 * 
 * <pre>
 * System.out.println("Map?Map?");
 * System.out.println("========================");
 * Set&lt;String&gt; keyset = map.keySet();
 * for (String key : keyset) {
 *     System.out.print(key + ":");
 *     System.out.println(map.get(key).toString());
 * }
 * </pre>
 * 
 * ??????
 * 
 * <pre>
 * Map?Map?
 * ========================
 * addressMap(home):address1
 * </pre>
 * 
 * Map????()?????????
 * </p>
 * <hr>
 * <h4>Map??</h4>
 * <p>
 * ?JavaBean????????Map??????
 * <p>
 * <u>??Map?</u>
 * 
 * <pre>
 * // Employee???Map
 * Map addressMap = new HashMap();
 * addressMap.put("home", "address1");
 * 
 * IndexedBeanWrapper bw = new JXPathIndexedBeanWrapperImpl(addressMap);
 * </pre>
 * </p>
 * <p>
 * <u>addressMap???home??</u>
 * 
 * <pre>
 * Map&lt;String, Object&gt; map = bw.getIndexedPropertyValues(
 *         "<strong>home</strong>");
 * </pre>
 * </p>
 * ??????Map?? ??????????
 * 
 * <pre>
 * System.out.println("Map?Map?");
 * System.out.println("========================");
 * Set&lt;String&gt; keyset = map.keySet();
 * for (String key : keyset) {
 *     System.out.print(key + ":");
 *     System.out.println(map.get(key).toString());
 * }
 * </pre>
 * 
 * ??????
 * 
 * <pre>
 * Map?Map?
 * ========================
 * home:address1
 * </pre>
 * 
 * Map?????List? ?????????
 * </p>
 * <hr>
 * <h4>DynaBean??</h4>
 * <p>
 * ?JavaBean????????DynaBean??????
 * <p>
 * <u>??DynaBean?</u>
 * 
 * <pre>
 * // DynaBean??JavaBean
 * Address address = new Address();
 * address.setStreetNumber("?");
 * 
 * // DynaBean
 * DynaBean dynaBean = new WrapDynaBean(address);
 * 
 * IndexedBeanWrapper bw = new JXPathIndexedBeanWrapperImpl(dynaBean);
 *     
 * --------------------------------------------------------
 * ?????Address??????
 * 
 * public class Address {
 *     private String streetNumber;
 *
 *     public void setStreetNumber(String streetNumber) {
 *         this.streetNumber = streetNumber;
 *     }
 *     public String getStreetNumber() {
 *         return streetNumber;
 *     }
 * }
 * </pre>
 * </p>
 * <p>
 * <u>DynaBean?streetNumber??</u>
 * 
 * <pre>
 * Map&lt;String, Object&gt; map = bw.getIndexedPropertyValues(
 *         "<strong>streetNumber</strong>");
 * </pre>
 * </p>
 * ??????Map?? ??????????
 * 
 * <pre>
 * System.out.println("Map?Map?");
 * System.out.println("========================");
 * Set&lt;String&gt; keyset = map.keySet();
 * for (String key : keyset) {
 *     System.out.print(key + ":");
 *     System.out.println(map.get(key).toString());
 * }
 * </pre>
 * 
 * ??????
 * 
 * <pre>
 * Map?Map?
 * ========================
 * streetNumber:?
 * </pre>
 * </p>
 */
public class JXPathIndexedBeanWrapperImpl implements IndexedBeanWrapper {
    /**
     * 
     */
    private static Log log = LogFactory.getLog(JXPathIndexedBeanWrapperImpl.class);

    /**
     * JXPath
     */
    protected JXPathContext context = null;

    /**
     * ??
     * <p>
     * ??NodePointer? NodePointer?static?????? ?NodePointer????
     * ???NullPointerException?????
     * </p>
     */
    static {
        JXPathContextReferenceImpl.addNodePointerFactory(new BeanPointerFactoryEx());
        JXPathContextReferenceImpl.addNodePointerFactory(new DynamicPointerFactoryEx());
    }

    /**
     * 
     * @param target ?
     */
    public JXPathIndexedBeanWrapperImpl(Object target) {
        // ??JavaBean?Null???
        if (target == null) {
            log.error("TargetBean is null!");
            throw new IllegalArgumentException("TargetBean is null!");
        }
        context = JXPathContext.newContext(target);
    }

    /**
     * ???????
     * @param propertyName ??
     * @return ??????Map??
     */
    @Override
    public Map<String, Object> getIndexedPropertyValues(String propertyName) {

        // ???Null
        if (StringUtils.isEmpty(propertyName)) {
            String message = "PropertyName is empty!";
            log.error(message);
            throw new IllegalArgumentException(message);
        }

        // ?????
        if (StringUtils.indexOfAny(propertyName, new char[] { '/', '"', '\'' }) != -1) {
            String message = "Invalid character has found within property name." + " '" + propertyName + "' "
                    + "Cannot use [ / \" ' ]";
            log.error(message);
            throw new IllegalArgumentException(message);
        }

        // ??[]?[]????
        String stringIndex = extractIndex(propertyName);
        if (stringIndex.length() > 0) {
            try {
                Integer.parseInt(stringIndex);
            } catch (NumberFormatException e) {
                String message = "Invalid character has found within property name." + " '" + propertyName + "' "
                        + "Cannot use [ [] ]";
                log.error(message);
                throw new IllegalArgumentException(message);
            }
        }

        Map<String, Object> result = new LinkedHashMap<String, Object>();
        String requestXpath = toXPath(propertyName);

        // JXPath??
        Iterator<?> ite = null;
        try {
            ite = context.iteratePointers(requestXpath);
        } catch (JXPathException e) {
            // ????
            String message = "Invalid property name. " + "PropertyName: '" + propertyName + "'" + "XPath: '"
                    + requestXpath + "'";
            log.error(message, e);
            throw new IllegalArgumentException(message, e);
        }

        // XPath  Java property
        while (ite.hasNext()) {
            Pointer p = (Pointer) ite.next();
            result.put(this.toPropertyName(p.asPath()), p.getValue());
        }
        return result;
    }

    /**
     * ??XPath?????
     * @param propertyName ?
     * @return XPath?
     */
    protected String toXPath(String propertyName) {
        StringBuilder builder = new StringBuilder("/");
        String[] properties = StringUtils.split(propertyName, '.');

        if (properties == null || properties.length == 0) {
            String message = "Property name is null or blank.";
            log.error(message);
            throw new IllegalArgumentException(message);
        }

        for (String property : properties) {
            // ?
            if (builder.length() > 1) {
                builder.append('/');
            }

            // Map
            if (isMapProperty(property)) {
                builder.append(escapeMapProperty(property));

                // JavaBean ??? Primitive
            } else {
                builder.append(extractAttributeName(property));
            }

            // ?
            builder.append(extractIncrementIndex(property));
        }
        return builder.toString();
    }

    /**
     * ?????
     * @param property Java??
     * @return String XPath???
     */
    protected String extractIncrementIndex(String property) {
        return extractIncrementIndex(property, 1);
    }

    /**
     * ?????
     * @param property ??
     * @param increment ?
     * @return String ???
     */
    protected String extractIncrementIndex(String property, int increment) {
        String stringIndex = extractIndex(property);
        if ("".equals(stringIndex)) {
            return "";
        }

        // ??????????
        try {
            int index = Integer.parseInt(stringIndex);
            return new StringBuilder().append('[').append(index + increment).append(']').toString();
        } catch (NumberFormatException e) {
            // ??[]????
            return "";
        }
    }

    /**
     * ???
     * @param property ??
     * @return ?
     */
    protected String extractIndex(String property) {
        int start = property.lastIndexOf('[');
        int end = property.lastIndexOf(']');

        // []??????????
        if (start == -1 && end == -1) {
            return "";
        }

        // ']aaa[' ???[]????????[]????????
        if (start == -1 || end == -1 || start > end) {
            String message = "Cannot get Index. " + "Invalid property name. '" + property + "'";
            log.error(message);
            throw new IllegalArgumentException(message);
        }
        return property.substring(start + 1, end);
    }

    /**
     * MapXPath???
     * @param property Java??
     * @return String XPath
     */
    protected String escapeMapProperty(String property) {
        // aaa(bbb)  aaa/bbb
        String mapPropertyName = extractMapPropertyName(property);
        String mapKey = extractMapPropertyKey(property);
        return mapPropertyName + "/" + mapKey;
    }

    /**
     * Map?????
     * @param property Java??
     * @return String XPath
     */
    protected String extractMapPropertyName(String property) {
        int pos = property.indexOf('(');

        // '('?????
        if (pos == -1) {
            String message = "Cannot get Map attribute. " + "Invalid property name. '" + property + "'";
            log.error(message);
            throw new IllegalArgumentException(message);
        }
        return property.substring(0, pos);
    }

    /**
     * Map?????
     * @param property Java??
     * @return String XPath
     */
    protected String extractMapPropertyKey(String property) {
        // aaa(bbb)  bbb
        int start = property.indexOf('(');
        int end = property.indexOf(')');

        // '()'???????()???????
        if (start == -1 || end == -1 || start > end) {
            String message = "Cannot get Map key. " + "Invalid property name. '" + property + "'";
            log.error(message);
            throw new IllegalArgumentException(message);
        }
        return property.substring(start + 1, end);
    }

    /**
     * Map?????
     * @param property Java??
     * @return boolean Map??true????false?
     */
    protected boolean isMapProperty(String property) {
        // '()'???Map
        if (property.indexOf('(') != -1 && property.indexOf(')') != -1) {
            return true;
        }
        return false;
    }

    /**
     * XPath???????
     * @param xpath XPath?
     * @return ?
     */
    protected String toPropertyName(String xpath) {
        StringBuilder builder = new StringBuilder("");
        String[] nodes = StringUtils.split(xpath, '/');

        if (nodes == null || nodes.length == 0) {
            String message = "XPath is null or blank.";
            log.error(message);
            throw new IllegalArgumentException(message);
        }

        for (int i = 0; i < nodes.length; i++) {
            String node = nodes[i];

            // Map
            if (i == 0 && isMapObject(node)) {
                builder.append(extractMapKey(node));
                builder.append(extractDecrementIndex(node));
                continue;
            }

            // ?
            if (builder.length() > 0) {
                builder.append('.');
            }

            // Map
            if (isMapAttribute(node)) {
                builder.append(extractMapAttributeName(node));
                builder.append('(');
                builder.append(extractMapKey(node));
                builder.append(')');

                // JavaBean ??? primitive
            } else {
                builder.append(extractAttributeName(node));
            }

            // ?
            builder.append(extractDecrementIndex(node));
        }
        return builder.toString();
    }

    /**
     * ???? ???????
     * @param node XPath?
     * @return ??
     */
    protected String extractAttributeName(String node) {
        int pos = node.lastIndexOf('[');
        if (pos == -1) {
            return node;
        }
        // ????
        return node.substring(0, pos);
    }

    /**
     * Map?????
     * @param node XPath?
     * @return ??
     */
    protected String extractMapAttributeName(String node) {
        // ??'['???Map?????
        int pos = node.indexOf('[');

        // '['?????
        if (pos == -1) {
            String message = "Cannot get Map attribute. " + "Invalid property name. '" + node + "'";
            log.error(message);
            throw new IllegalArgumentException(message);
        }
        return node.substring(0, pos);
    }

    /**
     * Map??
     * @param node XPath?
     * @return ??
     */
    protected String extractMapKey(String node) {
        // aaa[@name='bbb']  bbb
        int start = node.indexOf('[');
        int end = node.indexOf(']');

        // '[]'???????[]???????
        if (start == -1 || end == -1 || start > end) {
            String message = "Cannot get Map key. " + "Invalid property name. '" + node + "'";
            log.error(message);
            throw new IllegalArgumentException(message);
        }
        return node.substring(start + "[@name='".length(), end - "'".length());
    }

    /**
     * ?????
     * @param node XPath?
     * @return ??
     */
    protected String extractDecrementIndex(String node) {
        return extractIncrementIndex(node, -1);
    }

    /**
     * Map???????
     * @param node XPath?
     * @return Map??true????false?
     */
    protected boolean isMapAttribute(String node) {
        // '[@name'???Map
        if (node.indexOf("[@name") != -1) {
            return true;
        }
        return false;
    }

    /**
     * Map?????
     * @param node XPath?
     * @return Map??true????false?
     */
    protected boolean isMapObject(String node) {
        // '.[@name'????Map
        if (node.startsWith(".[@name")) {
            return true;
        }
        return false;
    }
}