Java tutorial
/* * Copyright (c) 2013 ITOCHU Techno-Solutions 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.co.ctc_g.jfw.core.util; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.regex.Matcher; import java.util.regex.Pattern; import jp.co.ctc_g.jfw.core.internal.InternalException; import jp.co.ctc_g.jfw.core.internal.InternalMessages; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; /** * <p> * ????JavaBeans????????? * </p> * <p> * ?????????pseudoProperty??? * ?JavaBean???????????? * </p> * @author ITOCHU Techno-Solutions Corporation. */ public final class Beans { private Beans() { } private static final ResourceBundle R = InternalMessages.getBundle(Beans.class); private static final Log L = LogFactory.getLog(Beans.class); private static final Pattern ACCESSOR_PATTERN = Pattern.compile("(get|set|is)[A-Z]{1}[a-zA-Z0-9_]*"); private static final Pattern WRITER_PATTERN = Pattern.compile("set[A-Z]{1}[a-zA-Z0-9_]*"); private static final Pattern READER_PATTERN = Pattern.compile("(get|is)[A-Z]{1}[a-zA-Z0-9_]*"); private static final Pattern FIRST_UPPERCASE_PATTERN = Pattern.compile("^[a-z0-9]+[A-Z]"); private static final Pattern DOUBLE_UPPERCASE_SEQUENCE_PATTERN = Pattern.compile("^[A-Z][A-Z][a-zA-Z0-9_]*"); private static final Pattern ARRAY_PROPERTY_PATTERN = Pattern .compile("^([a-zA-Z][a-zA-Z0-9_]*)\\[([0-9]+)\\]$"); /** * ??????? * ???{@link Strings#isEmpty(CharSequence) }??? * ???????????? * @param capitalizee ?? * @return ????? */ public static String capitalize(String capitalizee) { if (Strings.isEmpty(capitalizee)) return capitalizee; if (capitalizee.length() > 1) { if (Character.isUpperCase(capitalizee.charAt(1))) { return capitalizee; } else { return new StringBuilder().append(capitalizee.substring(0, 1).toUpperCase()) .append(capitalizee.substring(1, capitalizee.length())).toString(); } } else { return capitalizee.toUpperCase(); } } /** * ???????? * ???{@link Strings#isEmpty(CharSequence) }??? * ???????????? * @param decapitalizee ??? * @return ?????? */ public static String decapitalize(String decapitalizee) { return decapitalize(decapitalizee, true); } /** * ???????? * ???{@link Strings#isEmpty(CharSequence) }??? * ???????????? * @param decapitalizee ??? * @param doubleUppercaseEnable true??2???????? * @return ?????? */ public static String decapitalize(String decapitalizee, boolean doubleUppercaseEnable) { if (Strings.isEmpty(decapitalizee)) return decapitalizee; if (decapitalizee.length() > 1) { if (doubleUppercaseEnable && DOUBLE_UPPERCASE_SEQUENCE_PATTERN.matcher(decapitalizee).matches()) { return decapitalizee; } else { return new StringBuilder().append(decapitalizee.substring(0, 1).toLowerCase()) .append(decapitalizee.substring(1, decapitalizee.length())).toString(); } } else { return decapitalizee.toLowerCase(); } } /** * ???getter????????? * ???<code>getFoo</code><code>isBar</code>??? * @param suspect ? * @return ???true */ public static boolean isPropertyReader(String suspect) { if (suspect == null) return false; if ("getClass".equals(suspect)) return false; return READER_PATTERN.matcher(suspect).matches(); } /** * ???setter????????? * ???<code>setFoo</code>??? * @param suspect ? * @return ???true */ public static boolean isPropertyWriter(String suspect) { if (suspect == null) return false; return WRITER_PATTERN.matcher(suspect).matches(); } /** * ???getter???setter????????? * ???<code>getFoo</code><code>isBar</code>?<code>setFoo</code>??? * @param suspect ? * @return ???true */ public static boolean isPropertyAccessor(String suspect) { if (suspect == null) return false; return ACCESSOR_PATTERN.matcher(suspect).matches(); } /** * ????????????? * ???<code>getFoo</code><code>foo</code>? * <code>isBar</code><code>bar</code>?<code>setFoo</code><code>foo</code>??????? * @param accessorName ??????? * @return ?? */ public static String generatePropertyNameFor(String accessorName) { if (Strings.isEmpty(accessorName)) return accessorName; if (Beans.isPropertyAccessor(accessorName)) { Matcher matcher = FIRST_UPPERCASE_PATTERN.matcher(accessorName); return matcher.lookingAt() ? Beans.decapitalize(accessorName.substring(matcher.end() - 1)) : accessorName; } else { return accessorName; } } /** * ??????? * ???{@code null}????? * @param <T> ?? * @param clazz * @return ??? * @see jp.co.ctc_g.jfw.core.util.Reflects#make */ public static <T> T make(Class<T> clazz) { Args.checkNotNull(clazz); return Reflects.make(clazz); } /** * ???????? * @param clazz * @return */ public static PropertyDescriptor[] findPropertyDescriptorsFor(Class<?> clazz) { Args.checkNotNull(clazz); return BeanUtils.getPropertyDescriptors(clazz); } /** * ??????????????? * @param clazz * @param propertyName ?? * @return */ public static PropertyDescriptor findPropertyDescriptorFor(Class<?> clazz, String propertyName) { Args.checkNotNull(clazz); return BeanUtils.getPropertyDescriptor(clazz, propertyName); } /** * ?????????? * ??? * <pre class="brush:java"> * public class FooBean { * private String bar = "propertyValue"; * public String getBar() {return bar;} * public void setBar(String bar) {this.bar = bar;} * } * * FooBean b = new FooBean(); * String value = Beans.readPropertyValueNamed("bar", b); * assert value.equals("propertyValue"); * </pre> * ???? * ??????????????? * <pre class="brush:java"> * public class BuzBean { * private FooBean fooBean = new FooBean(); * public FooBean getFooBean() {return fooBean;} * public void setFooBean(FooBean fooBean) {this.fooBean = fooBean} * } * * BuzBean b = new BuzBean(); * String value = Beans.readPropertyValueNamed("fooBean.bar", b); * assert value.equals("propertyValue"); * </pre> * ????????? * <pre class="brush:java"> * public class ArrayBean { * private String[] qux = {"1", "2", "3"}; * public String[] getQux() {return this.qux;} * public void setQux(String[]) {this.qux = qux;} * } * * ArrayBean b = new ArrayBean(); * String value = Beans.readPropertyValueNamed("qux[1]", b); * assert value.equals("2"); * </pre> * @param propertyName ?? * @param bean ? * @return */ public static Object readPropertyValueNamed(String propertyName, Object bean) { Args.checkNotBlank(propertyName); Args.checkNotNull(bean); Object temporaryPropertyValue = bean; String[] properties = propertyName.split("\\."); for (String property : properties) { Matcher matcher = ARRAY_PROPERTY_PATTERN.matcher(property); if (matcher.matches()) { String arrayPropertyName = matcher.group(1); Integer arrayPropertyIndex = Integer.valueOf(matcher.group(2)); Object array = readPropertyValueNamed0(arrayPropertyName, temporaryPropertyValue); temporaryPropertyValue = readElementAt(arrayPropertyIndex, array); } else { temporaryPropertyValue = readPropertyValueNamed0(property, temporaryPropertyValue); } } return temporaryPropertyValue; } @SuppressWarnings("unchecked") private static Object readElementAt(int index, Object arrayOrList) { Args.checkPositiveOrZero(index); Object contained = null; Class<?> type = arrayOrList.getClass(); if (type.isArray()) { Object[] array = (Object[]) arrayOrList; contained = readElementFromArrayAt(index, array); } else if (List.class.isAssignableFrom(type)) { List<Object> container = (List<Object>) arrayOrList; contained = readElementFromListAt(index, container); } else { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); L.debug(Strings.substitute(R.getString("D-UTIL#0003"), replace)); } } return contained; } private static Object readElementFromArrayAt(int index, Object[] array) { if (array == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); L.debug(Strings.substitute(R.getString("D-UTIL#0011"), replace)); } return null; } if (array.length <= index) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); replace.put("size", array.length); L.debug(Strings.substitute(R.getString("D-UTIL#0012"), replace)); } return false; } return array[index]; } private static Object readElementFromListAt(int index, List<Object> container) { if (container == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); L.debug(Strings.substitute(R.getString("D-UTIL#0013"), replace)); } return false; } if (container.size() <= index) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); replace.put("size", container.size()); L.debug(Strings.substitute(R.getString("D-UTIL#0014"), replace)); } return false; } return container.get(index); } private static Object readPropertyValueNamed0(String propertyName, Object bean) { PropertyDescriptor pd = findPropertyDescriptorFor(bean.getClass(), propertyName); if (pd == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("class", bean.getClass().getName()); replace.put("property", propertyName); L.debug(Strings.substitute(R.getString("D-UTIL#0015"), replace)); } return null; } Method reader = pd.getReadMethod(); if (reader == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("class", bean.getClass().getName()); replace.put("property", propertyName); L.debug(Strings.substitute(R.getString("D-UTIL#0016"), replace)); } return null; } return Reflects.invoke(reader, bean); } /** * ?????????? * ??? * <pre class="brush:java"> * public class FooBean { * private String bar; * public String getBar() {return bar;} * public void setBar(String bar) {this.bar = bar;} * } * * FooBean b = new FooBean(); * Beans.writePropertyValueNamed("bar", b, "propertyValue"); * assert b.getBar().equals("propertyValue"); * </pre> * ???? * ????????????????? * <pre class="brush:java"> * public class BuzBean { * private FooBean fooBean = new FooBean(); * public FooBean getFooBean() {return fooBean;} * public void setFooBean(FooBean fooBean) {this.fooBean = fooBean} * } * * BuzBean b = new BuzBean(); * Beans.writePropertyValueNamed("fooBean.bar", b, "propertyValue"); * assert b.getBar().equals("propertyValue"); * </pre> * ????????? * <pre class="brush:java"> * public class ArrayBean { * private String[] qux = new String[3]; * public String[] getQux() {return this.qux;} * public void setQux(String[]) {this.qux = qux;} * } * * ArrayBean b = new ArrayBean(); * Beans.writePropertyValueNamed("qux[1]", b, "propertyValue"); * assert b.getQux()[1].equals("propertyValue"); * </pre> * ?????{@link PopulationSafeList}??? * {@link IndexOutOfBoundsException}?????????? * @param propertyName ?? * @param bean ? * @param newValue ? * @see PopulationSafeList */ public static void writePropertyValueNamed(String propertyName, Object bean, Object newValue) { Args.checkNotBlank(propertyName); Args.checkNotNull(bean); String[] properties = propertyName.split("\\."); String propertyToWrite = propertyName; Object writable = bean; if (properties.length > 1) { writable = readPropertyValueNamed( Strings.joinBy(".", Arrays.slice(properties, 0, properties.length - 1)), bean); propertyToWrite = properties[properties.length - 1]; } Matcher matcher = ARRAY_PROPERTY_PATTERN.matcher(propertyToWrite); if (matcher.matches()) { String arrayPropertyName = matcher.group(1); Integer arrayPropertyIndex = Integer.valueOf(matcher.group(2)); Object array = readPropertyValueNamed(arrayPropertyName, writable); writeElementAt(arrayPropertyIndex, array, newValue); } else { writePropertyValueNamed0(propertyToWrite, writable, newValue); } } private static boolean writePropertyValueNamed0(String propertyName, Object bean, Object newValue) { PropertyDescriptor pd = findPropertyDescriptorFor(bean.getClass(), propertyName); if (pd == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("class", bean.getClass().getName()); replace.put("property", propertyName); L.debug(Strings.substitute(R.getString("D-UTIL#0017"), replace)); } return false; } Method writer = pd.getWriteMethod(); if (writer == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("class", bean.getClass().getName()); replace.put("property", propertyName); L.debug(Strings.substitute(R.getString("D-UTIL#0018"), replace)); } return false; } Reflects.invoke(writer, bean, newValue); return true; } /** * ??????????? * {@link #readPropertyValueNamed(String, Object)}???????? * ????????????? * ???????????????? * ?????? * @param propertyName ?? * @param bean ?? * @return */ public static Object readPseudoPropertyValueNamed(String propertyName, Object bean) { Args.checkNotBlank(propertyName); Args.checkNotNull(bean); Object temporaryPropertyValue = bean; String[] properties = propertyName.split("\\."); for (String property : properties) { Matcher matcher = ARRAY_PROPERTY_PATTERN.matcher(property); if (matcher.matches()) { String arrayPropertyName = matcher.group(1); Integer arrayPropertyIndex = Integer.valueOf(matcher.group(2)); Object array = readPseudoPropertyValueNamed0(arrayPropertyName, temporaryPropertyValue); temporaryPropertyValue = readElementAt(arrayPropertyIndex, array); } else { temporaryPropertyValue = readPseudoPropertyValueNamed0(property, temporaryPropertyValue); } } return temporaryPropertyValue; } private static Object readPseudoPropertyValueNamed0(String propertyName, Object bean) { Object value = null; try { PropertyDescriptor pd = findPropertyDescriptorFor(bean.getClass(), propertyName); // ??????? if (pd != null) { Method reader = pd.getReadMethod(); if (reader != null) { reader.setAccessible(true); value = reader.invoke(bean); } else { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(1); replace.put("property", propertyName); L.debug(Strings.substitute(R.getString("E-UTIL#0012"), replace)); } } // ??????? } else { Field f = bean.getClass().getField(propertyName); if (f != null && !Modifier.isStatic(f.getModifiers())) { f.setAccessible(true); value = f.get(bean); } else { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(1); replace.put("property", propertyName); L.debug(Strings.substitute(R.getString("D-UTIL#0019"), replace)); } } } } catch (SecurityException e) { throw new InternalException(Beans.class, "E-UTIL#0010", e); } catch (NoSuchFieldException e) { Map<String, String> replace = new HashMap<String, String>(1); replace.put("property", propertyName); throw new InternalException(Beans.class, "E-UTIL#0011", replace, e); } catch (IllegalArgumentException e) { Map<String, String> replace = new HashMap<String, String>(1); replace.put("property", propertyName); throw new InternalException(Beans.class, "E-UTIL#0012", replace, e); } catch (IllegalAccessException e) { throw new InternalException(Beans.class, "E-UTIL#0013", e); } catch (InvocationTargetException e) { Map<String, String> replace = new HashMap<String, String>(2); replace.put("class", bean.getClass().getName()); replace.put("property", propertyName); throw new InternalException(Beans.class, "E-UTIL#0014", replace, e); } return value; } /** * ?????????? * {@link #writePropertyValueNamed(String, Object, Object)}???????? * ?????????????? * ???????????????? * ??????? * @param propertyName ?? * @param bean ???? * @param newValue ? */ public static void writePseudoPropertyValueNamed(String propertyName, Object bean, Object newValue) { Args.checkNotBlank(propertyName); Args.checkNotNull(bean); String[] properties = propertyName.split("\\."); String propertyToWrite = propertyName; Object writable = bean; if (properties.length > 1) { writable = readPseudoPropertyValueNamed( Strings.joinBy(".", Arrays.slice(properties, 0, properties.length - 1)), bean); propertyToWrite = properties[properties.length - 1]; } Matcher matcher = ARRAY_PROPERTY_PATTERN.matcher(propertyToWrite); if (matcher.matches()) { String arrayPropertyName = matcher.group(1); Integer arrayPropertyIndex = Integer.valueOf(matcher.group(2)); Object array = readPseudoPropertyValueNamed0(arrayPropertyName, writable); writeElementAt(arrayPropertyIndex, array, newValue); } else { writePseudoPropertyValueNamed0(propertyToWrite, writable, newValue); } } @SuppressWarnings("unchecked") private static boolean writeElementAt(int index, Object arrayOrList, Object newValue) { Args.checkPositiveOrZero(index); Class<?> type = arrayOrList.getClass(); if (type.isArray()) { Object[] array = (Object[]) arrayOrList; return writeElementToArrayAt(index, array, newValue); } else if (List.class.isAssignableFrom(type)) { List<Object> container = (List<Object>) arrayOrList; return writeElementToListAt(index, container, newValue); } else { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); replace.put("value", newValue); L.debug(Strings.substitute(R.getString("D-UTIL#0004"), replace)); } return false; } } private static boolean writeElementToArrayAt(int index, Object[] array, Object newValue) { if (array == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); replace.put("value", newValue); L.debug(Strings.substitute(R.getString("D-UTIL#0005"), replace)); } return false; } if (array.length <= index) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); replace.put("value", newValue); replace.put("size", array.length); L.debug(Strings.substitute(R.getString("D-UTIL#0006"), replace)); } return false; } array[index] = newValue; return true; } private static boolean writeElementToListAt(int index, List<Object> container, Object newValue) { if (container == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); replace.put("value", newValue); L.debug(Strings.substitute(R.getString("D-UTIL#0007"), replace)); } return false; } if (container.size() <= index) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(2); replace.put("index", index); replace.put("value", newValue); replace.put("size", container.size()); L.debug(Strings.substitute(R.getString("D-UTIL#0008"), replace)); } return false; } container.set(index, newValue); return true; } private static boolean writePseudoPropertyValueNamed0(String propertyName, Object bean, Object newValue) { try { PropertyDescriptor pd = findPropertyDescriptorFor(bean.getClass(), propertyName); // ??????? if (pd != null) { Method writer = pd.getWriteMethod(); if (writer != null) { writer.setAccessible(true); writer.invoke(bean, newValue); return true; } else { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(1); replace.put("property", propertyName); L.debug(Strings.substitute(R.getString("D-UTIL#0009"), replace)); } return false; } // ??????? } else { Field f = bean.getClass().getField(propertyName); if (f != null) { f.setAccessible(true); f.set(bean, newValue); return true; } else { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(1); replace.put("property", propertyName); L.debug(Strings.substitute(R.getString("D-UTIL#0010"), replace)); } return false; } } } catch (SecurityException e) { throw new InternalException(Beans.class, "E-UTIL#0010", e); } catch (NoSuchFieldException e) { Map<String, String> replace = new HashMap<String, String>(1); replace.put("property", propertyName); throw new InternalException(Beans.class, "E-UTIL#0011", replace, e); } catch (IllegalArgumentException e) { Map<String, String> replace = new HashMap<String, String>(1); replace.put("property", propertyName); throw new InternalException(Beans.class, "E-UTIL#0012", replace, e); } catch (IllegalAccessException e) { throw new InternalException(Beans.class, "E-UTIL#0013", e); } catch (InvocationTargetException e) { Map<String, String> replace = new HashMap<String, String>(1); replace.put("property", propertyName); replace.put("class", bean.getClass().getName()); throw new InternalException(Beans.class, "E-UTIL#0014", replace, e); } } /** * ??? * @param clazz * @return ?? */ public static String[] listPseudoPropertyNames(Class<?> clazz) { List<String> names = new ArrayList<String>(); for (PropertyDescriptor pd : Beans.findPropertyDescriptorsFor(clazz)) { names.add(pd.getName()); } for (Field f : clazz.getFields()) { String n = f.getName(); if (!names.contains(n)) names.add(n); } return names.toArray(new String[0]); } /** * ??????????? * @param propertyName ?? * @param target * @return ???? */ public static Class<?> detectDeclaredPropertyType(String propertyName, Class<?> target) { Args.checkNotBlank(propertyName); Args.checkNotNull(target); String[] properties = propertyName.split("\\."); Class<?> result = target; for (String property : properties) { PropertyDescriptor pd = findPropertyDescriptorFor(result, property); if (pd == null) { if (L.isDebugEnabled()) { Map<String, Object> replace = new HashMap<String, Object>(1); replace.put("property", propertyName); replace.put("class", target.getClass().getName()); L.debug(Strings.substitute(R.getString("D-UTIL#0015"), replace)); } return null; } result = pd.getPropertyType(); } return result; } }