package com.gh.mygreen.xlsmapper; import java.awt.Point; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.poi.ss.formula.FormulaParseException; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellReference; import com.gh.mygreen.xlsmapper.annotation.XlsColumn; import com.gh.mygreen.xlsmapper.annotation.XlsConverter; import com.gh.mygreen.xlsmapper.annotation.XlsFormula; import com.gh.mygreen.xlsmapper.annotation.XlsMapColumns; import com.gh.mygreen.xlsmapper.annotation.XlsNestedRecords; import com.gh.mygreen.xlsmapper.cellconvert.ConversionException; import com.gh.mygreen.xlsmapper.cellconvert.DefaultItemConverter; import com.gh.mygreen.xlsmapper.cellconvert.ItemConverter; import com.gh.mygreen.xlsmapper.fieldprocessor.CellNotFoundException; import com.gh.mygreen.xlsmapper.fieldprocessor.FieldAdaptor; import com.gh.mygreen.xlsmapper.validation.SheetBindingErrors; import com.gh.mygreen.xlsmapper.xml.AnnotationReader; /** * ユーティリティクラス。 * * @version 1.5 * @author T.TSUCHIE * @author Naoki Takezoe * @author Mitsuyoshi Hasegawa * */ public class Utils { private static final ItemConverter ITEM_CONVERTER = new DefaultItemConverter(); /** * 配列の要素を指定した区切り文字で繋げて1つの文字列とする。 * @param arrays 処理対象の配列。 * @param separator 区切り文字。 * @param ignoreEmptyItem 空、nullの要素を無視するかどうか。 * @param trim トリムをするかどうか。 * @param itemConverter 要素を変換するクラス。 * @return */ public static String join(final Object[] arrays, final String separator, final boolean ignoreEmptyItem, final boolean trim, final ItemConverter itemConverter) { return join(Arrays.asList(arrays), separator, ignoreEmptyItem, trim, itemConverter); } /** * コレクションの要素を指定した区切り文字で繋げて1つの文字列とする。 * @param col 処理対象のコレクション。 * @param separator 区切り文字。 * @param ignoreEmptyItem 空、nullの要素を無視するかどうか。 * @param trim トリムをするかどうか。 * @param itemConverter 要素を変換するクラス。 * @return */ public static String join(final Collection<?> col, final String separator, final boolean ignoreEmptyItem, final boolean trim, final ItemConverter itemConverter) { final List<Object> list = new ArrayList<Object>(); for(Object item : col) { if(item == null) { continue; } Object value = item; if(item instanceof String) { String str = (String) item; if(ignoreEmptyItem && isEmpty(str)) { continue; } else if(trim) { value = str.trim(); } } else if(item instanceof Character && isEmpty(item.toString())) { String str = item.toString(); if(ignoreEmptyItem && isEmpty(str)) { continue; } else if(trim) { value = str.trim().charAt(0); } } else if(char.class.isAssignableFrom(item.getClass())) { String str = item.toString(); if(ignoreEmptyItem && isEmpty(str)) { continue; } else if(trim) { value = str.trim().charAt(0); } } list.add(value); } return join(list, separator, itemConverter); } /** * 配列の要素を指定した区切り文字で繋げて1つの文字列とする。 * @param arrays * @param separator * @return */ public static String join(final Object[] arrays, final String separator) { return join(arrays, separator, ITEM_CONVERTER); } /** * 配列の要素を指定した区切り文字で繋げて1つの文字列とする。 * @param arrays * @param separator * @param itemConverter * @return */ public static String join(final Object[] arrays, final String separator, final ItemConverter itemConverter) { final int len = arrays.length; if(arrays == null || len == 0) { return ""; } StringBuilder sb = new StringBuilder(); for(int i=0; i < len; i++) { final Object item = arrays[i]; sb.append(itemConverter.convertToString(item)); if(separator != null && (i < len-1)) { sb.append(separator); } } return sb.toString(); } /** * Collectionの要素を指定した区切り文字で繋げて1つの文字列とする。 * @param col * @param separator * @return */ public static String join(final Collection<?> col, final String separator) { return join(col, separator, ITEM_CONVERTER); } /** * Collectionの要素を指定した区切り文字で繋げて1つの文字列とする。 * @param col * @param separator * @param itemConverter * @return */ public static String join(final Collection<?> col, final String separator, final ItemConverter itemConverter) { final int size = col.size(); if(col == null || size == 0) { return ""; } StringBuilder sb = new StringBuilder(); for(Iterator<?> itr = col.iterator(); itr.hasNext();) { final Object item = itr.next(); String text = itemConverter.convertToString(item); sb.append(text); if(separator != null && itr.hasNext()) { sb.append(separator); } } return sb.toString(); } /** * 先頭の文字を大文字にする。 * <pre> * Utils.capitalize(null) = null * Utils.capitalize("") = "" * Utils.capitalize("cat") = "Cat" * Utils.capitalize("cAt") = "CAt" * </pre> * @param str * @return 引数がnull、空文字の場合、そのまま返す。 */ public static String capitalize(final String str) { final int strLen; if(str == null || (strLen = str.length()) == 0) { return str; } return new StringBuilder(strLen) .append(String.valueOf(str.charAt(0)).toUpperCase()) .append(str.substring(1)) .toString(); } /** * 先頭の文字を小文字にする。 * @param str * @return 引数がnull、空文字の場合、そのまま返す。 */ public static String uncapitalize(final String str) { final int strLen; if(str == null || (strLen = str.length()) == 0) { return str; } return new StringBuilder(strLen) .append(String.valueOf(str.charAt(0)).toLowerCase()) .append(str.substring(1)) .toString(); } /** * システム設定に従いラベルを比較する。 * <p>正規表現や正規化を行い指定する。 * * @since 1.1 * @param text1 セルのラベル * @param text2 アノテーションに指定されているラベル。 * {@literal /<ラベル>/}と指定する場合、正規表現による比較を行う。 * @param config システム設定 * @return true:ラベルが一致する。 */ public static boolean matches(final String text1, final String text2, final XlsMapperConfig config){ if(config.isRegexLabelText() && text2.startsWith("/") && text2.endsWith("/")){ return normalize(text1, config).matches(text2.substring(1, text2.length() - 1)); } else { return normalize(text1, config).equals(normalize(text2, config)); } } /** * システム設定に従いラベルを正規化する。 * @since 1.1 * @param text セルのラベル * @param config システム設定 * @return true:ラベルが一致する。 */ private static String normalize(final String text, final XlsMapperConfig config){ if(text != null && config.isNormalizeLabelText()){ return text.trim().replaceAll("[\n\r]", "").replaceAll("[\t  ]+", " "); } return text; } /** * 指定したフィールド名に対するGetterメソッドを取得する。 * * @param clazz * @param fieldName * @return 見つからない場合はnullを返す。 */ public static Method getGetter(final Class<?> clazz, final String fieldName) { final String methodName = "get" + capitalize(fieldName); // public method try { final Method method = clazz.getMethod(methodName, Void.class); return method; } catch (SecurityException | NoSuchMethodException e) { } // private / protected method try { final Method method = clazz.getDeclaredMethod(methodName, Void.class); method.setAccessible(true); return method; } catch (SecurityException | NoSuchMethodException e) { } return null; } /** * 指定してたメソッドがBooleanのGetterかどうかチェックする。 * @param method * @return メソッド名がisから始まり、戻り値が booleanの場合trueを返す。 */ public static boolean isBooleanGetterMethod(final Method method) { if(!method.getName().startsWith("is")) { return false; } final Class<?> returnType = method.getReturnType(); return isPrimitiveBoolean(returnType); } /** * 指定してたメソッドがBooleanのGetterでないかどうかチェックする。 * @param method * @return メソッド名がisから始まり、戻り値が booleanの場合falseを返す。 */ public static boolean isNotBooleanGetterMethod(final Method method) { return !isBooleanGetterMethod(method); } public static boolean isPrimitiveBoolean(final Class<?> clazzType) { if(clazzType.isPrimitive() && boolean.class.isAssignableFrom(clazzType)) { return true; } return false; } /** * フィールドのタイプがboolean型がどうかチェックする。 * @param field * @return プリミティブ型のboolean型の場合trueを返す。 */ public static boolean isBooleanField(final Field field) { final Class<?> clazzType = field.getType(); return isPrimitiveBoolean(clazzType); } /** * 指定したフィールド名に対するbooleanのメソッドを取得する * * @param clazz * @param fieldName * @return */ public static Method getBooleanGetter(final Class<?> clazz, final String fieldName) { final String methodName = "is" + capitalize(fieldName); // private / protected method try { final Method method = clazz.getDeclaredMethod(methodName, Void.class); method.setAccessible(true); return method; } catch (SecurityException | NoSuchMethodException e) { } return null; } /** * 指定したフィールド名に対するSetterメソッドを取得する。 * * @param clazz * @param fieldName * @return 見つからない場合はnullを返す。 */ public static Method getSetter(final Class<?> clazz, final String fieldName, final Class<?>... fieldClass) { final String methodName = "set" + capitalize(fieldName); // private / protected method try { final Method method = clazz.getDeclaredMethod(methodName, fieldClass); method.setAccessible(true); return method; } catch (SecurityException | NoSuchMethodException e) { } return null; } /** * 指定した名前のフィールドを取得する。 * @param clazz * @param fieldName * @return 見つからない場合はnullを返す。 */ public static Field getField(final Class<?> clazz, final String fieldName) { try { final Field field = clazz.getDeclaredField(fieldName); return field; } catch (NoSuchFieldException | SecurityException e) { } return null; } /** * セルの位置を設定する。 * <p>「set + 'フィールド名' + Position」のsetterか「'フィールド名' + Position」というフィールド名で決める。 * <p>フィールド「Map<String, Point> positions」に、設定する。 * @param x 列のインデックス番号 * @param y 行のインデックス番号 * @param obj メソッドが定義されているオブジェクト * @param fieldName フィールド名 */ @SuppressWarnings("unchecked") public static void setPosition(final int x, final int y, final Object obj, final String fieldName) { final Class<?> clazz = obj.getClass(); final String positionFieldName = fieldName + "Position"; // フィールド positionsの場合 final String positionMapFieldName = "positions"; try { Field positionMapField = clazz.getDeclaredField(positionMapFieldName); positionMapField.setAccessible(true); if(Map.class.isAssignableFrom(positionMapField.getType())) { Object positionMapValue = positionMapField.get(obj); if(positionMapValue == null) { positionMapValue = new HashMap<String, Point>(); positionMapField.set(obj, positionMapValue); } ((Map<String, Point>) positionMapValue).put(fieldName, new Point(x, y)); return; } } catch (NoSuchFieldException | SecurityException e) { // フィールドが見つからない場合は何もしない。 } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set '%s' field", positionMapFieldName), e); } // メソッドの場合(引数が int, intの場合) final Method positionMethod1 = getSetter(clazz, positionFieldName, Integer.TYPE, Integer.TYPE); if(positionMethod1 != null) { try { positionMethod1.invoke(obj, x, y); return; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException( String.format("fail set position with '%s' method", positionMethod1.getName()), e); } } // メソッドの場合(引数が Pointの場合) final Method positionMethod2 = getSetter(clazz, positionFieldName, Point.class); if(positionMethod2 != null) { try { positionMethod2.invoke(obj, new Point(x, y)); return; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException( String.format("fail set position with '%s' method", positionMethod2.getName()), e); } } // フィールドの場合 final Field positionField = getField(clazz, positionFieldName); if(positionField != null) { try { positionField.setAccessible(true); positionField.set(obj, new Point(x, y)); } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set position with '%s' field", positionField.getName()), e); } } } /** * セルの位置を取得する * <p>「get + 'フィールド名' + Position」のgetterか「'フィールド名' + Position」というフィールド名で決める。 * <p>フィールド「Map<String, Point> positions」に、設定する。 * @param obj メソッドが定義されているオブジェクト * @param fieldName フィールド名 * @return 座標が取得できない場合はnullを返す。 */ @SuppressWarnings("unchecked") public static Point getPosition(final Object obj, final String fieldName) { final Class<?> clazz = obj.getClass(); final String positionFieldName = fieldName + "Position"; // フィールド positionsの場合 final String positionMapFieldName = "positions"; try { Field positionMapField = clazz.getDeclaredField(positionMapFieldName); positionMapField.setAccessible(true); if(Map.class.isAssignableFrom(positionMapField.getType())) { Object positionMapValue = positionMapField.get(obj); if(positionMapValue == null) { return null; } return ((Map<String, Point>) positionMapValue).get(fieldName); } } catch (NoSuchFieldException | SecurityException e) { // フィールドが見つからない場合は何もしない。 } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set '%s' field", positionMapFieldName), e); } // メソッドの場合 final Method positionMethod1 = getGetter(clazz, positionFieldName); if(positionMethod1 != null) { try { final Object positionValue = positionMethod1.invoke(obj); if(Point.class.isAssignableFrom(positionMethod1.getReturnType())) { return (Point) positionValue; } else { throw new RuntimeException( String.format("method '%s' return type not '%s'", positionMethod1.getName(), Point.class.getName())); } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException( String.format("fail set position with '%s' method", positionMethod1.getName()), e); } } // フィールドの場合 final Field positionField = getField(clazz, positionFieldName); if(positionField != null) { try { positionField.setAccessible(true); final Object positionValue = positionField.get(obj); if(Point.class.isAssignableFrom(positionValue.getClass())) { return (Point) positionValue; } else { throw new RuntimeException( String.format("field '%s' return type not '%s'", positionField.getName(), Point.class.getName())); } } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set position with '%s' field", positionField.getName()), e); } } return null; } /** * MapColumn形式の場合のセルの位置を設定する。 * <p>「set + 'フィールド名' + Position」のsetterか「'フィールド名' + Position」というフィールド名で決める。 * <p>フィールド「Map<String, Point> positions」に、設定する。 * @param x 列のインデックス * @param y 行のインデックス * @param obj メソッドが定義されているオブジェクト * @param fieldName フィールド名 * @param key */ @SuppressWarnings("unchecked") public static void setPositionWithMapColumn(final int x, final int y, final Object obj, final String fieldName, final String key) { final Class<?> clazz = obj.getClass(); final String positionFieldName = fieldName + "Position"; // フィールド positionsの場合 final String positionMapFieldName = "positions"; try { Field positionMapField = clazz.getDeclaredField(positionMapFieldName); positionMapField.setAccessible(true); if(Map.class.isAssignableFrom(positionMapField.getType())) { Object positionMapValue = positionMapField.get(obj); if(positionMapValue == null) { positionMapValue = new HashMap<String, Point>(); positionMapField.set(obj, positionMapValue); } final String mapKey = String.format("%s[%s]", fieldName, key); ((Map<String, Point>) positionMapValue).put(mapKey, new Point(x, y)); return; } } catch (NoSuchFieldException | SecurityException e) { // フィールドが見つからない場合は何もしない。 } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set '%s' field", positionMapFieldName), e); } // メソッドの場合(引数が String, int, intの場合) final Method positionMethod1 = getSetter(clazz, positionFieldName, String.class, Integer.TYPE, Integer.TYPE); if(positionMethod1 != null) { try { positionMethod1.invoke(obj, key, x, y); return; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException( String.format("fail set map position with '%s' method", positionMethod1.getName()), e); } } // メソッドの場合(引数が String, Pointの場合) final Method positionMethod2 = getSetter(clazz, positionFieldName, String.class, Point.class); if(positionMethod2 != null) { try { positionMethod2.invoke(obj, key, new Point(x, y)); return; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException( String.format("fail set map position with '%s' method", positionMethod2.getName()), e); } } // フィールドの場合(Map<String, Point>)の場合 final Field positionField = getField(clazz, positionFieldName); if(positionField != null) { try { positionField.setAccessible(true); if(Map.class.isAssignableFrom(positionField.getType())) { Object positionValue = positionField.get(obj); if(positionValue == null) { positionValue = new HashMap<String, Point>(); positionField.set(obj, positionValue); } ((Map<String, Point>) positionValue).put(key, new Point(x, y)); } } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set position with '%s' field", positionField.getName()), e); } } } /** * セルの見出しを設定する。 * <p>「set + 'フィールド名' + Label」のsetterか「'フィールド名' + Label」というフィールド名で決める。 * <p>フィールド「Map<String, String> labels」に、設定する。 * @param label 設定する見出し * @param obj メソッドが定義されているオブジェクト * @param fieldName フィールド名 */ @SuppressWarnings("unchecked") public static void setLabel(final String label, final Object obj, final String fieldName) { final Class<?> clazz = obj.getClass(); final String labelFieldName = fieldName + "Label"; // フィールド labelsの場合 final String labelMapFieldName = "labels"; try { Field labelMapField = clazz.getDeclaredField(labelMapFieldName); labelMapField.setAccessible(true); if(Map.class.isAssignableFrom(labelMapField.getType())) { Object labelMapValue = labelMapField.get(obj); if(labelMapValue == null) { labelMapValue = new HashMap<String, Point>(); labelMapField.set(obj, labelMapValue); } ((Map<String, String>) labelMapValue).put(fieldName, label); return; } } catch (NoSuchFieldException | SecurityException e) { // フィールドが見つからない場合は何もしない。 } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set '%s' field", labelMapFieldName), e); } // メソッドの場合(引数が String の場合) final Method labelMethod1 = getSetter(clazz, labelFieldName, String.class); if(labelMethod1 != null) { try { labelMethod1.invoke(obj, label); return; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException( String.format("fail set label with '%s' method", labelMethod1.getName()), e); } } // フィールドの場合 final Field labelField = getField(clazz, labelFieldName); if(labelField != null) { try { labelField.setAccessible(true); labelField.set(obj, label); return; } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set label with '%s' field", labelField.getName()), e); } } } /** * セルの見出しを取得する。 * <p>「get + 'フィールド名' + Label」のgetterか「'フィールド名' + Label」というフィールド名で決める。 * <p>フィールド「Map<String, String> labels」に、設定する。 * @param obj メソッドが定義されているオブジェクト * @param fieldName フィールド名 * @return セルの見出し。 */ @SuppressWarnings("unchecked") public static String getLabel(final Object obj, final String fieldName) { final Class<?> clazz = obj.getClass(); final String labelFieldName = fieldName + "Label"; // フィールド labelsの場合 final String labelMapFieldName = "labels"; try { Field labelMapField = clazz.getDeclaredField(labelMapFieldName); labelMapField.setAccessible(true); if(Map.class.isAssignableFrom(labelMapField.getType())) { Object labelMapValue = labelMapField.get(obj); if(labelMapValue == null) { return null; } return ((Map<String, String>) labelMapValue).get(fieldName); } } catch (NoSuchFieldException | SecurityException e) { // フィールドが見つからない場合は何もしない。 } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set '%s' field", labelMapFieldName), e); } // メソッドの場合 final Method labelMethod1 = getSetter(clazz, labelFieldName); if(labelMethod1 != null) { try { final Object labelValue = labelMethod1.invoke(obj); if(Point.class.isAssignableFrom(labelMethod1.getReturnType())) { return (String) labelValue; } else { throw new RuntimeException( String.format("method '%s' return type not '%s'", labelMethod1.getName(), String.class.getName())); } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException( String.format("fail set label with '%s' method", labelMethod1.getName()), e); } } // フィールドの場合 final Field labelField = getField(clazz, labelFieldName); if(labelField != null) { try { labelField.setAccessible(true); final Object labelValue = labelField.get(obj); if(Point.class.isAssignableFrom(labelField.getType())) { return (String) labelValue; } else { throw new RuntimeException( String.format("field '%s' type not '%s'", labelField.getName(), String.class.getName())); } } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set label with '%s' field", labelField.getName()), e); } } return null; } /** * セルの見出しを設定する。 * <p>「set + 'フィールド名' + Label」のsetterか「'フィールド名' + Label」というフィールド名で決める。 * <p>フィールド「Map<String, String> labels」に、設定する。 * @param label 設定する見出し * @param obj メソッドが定義されているオブジェクト * @param fieldName フィールド名 * @param key */ @SuppressWarnings("unchecked") public static void setLabelWithMapColumn(final String label, final Object obj, final String fieldName, final String key) { final Class<?> clazz = obj.getClass(); final String labelFieldName = fieldName + "Label"; // フィールド labelsの場合 final String labelMapFieldName = "labels"; try { Field labelMapField = clazz.getDeclaredField(labelMapFieldName); labelMapField.setAccessible(true); if(Map.class.isAssignableFrom(labelMapField.getType())) { Object labelMapValue = labelMapField.get(obj); if(labelMapValue == null) { labelMapValue = new HashMap<String, String>(); labelMapField.set(obj, labelMapValue); } final String mapKey = String.format("%s[%s]", fieldName, key); ((Map<String, String>) labelMapValue).put(mapKey, label); return; } } catch (NoSuchFieldException | SecurityException e) { // フィールドが見つからない場合は何もしない。 } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set '%s' field", labelMapFieldName), e); } // メソッドの場合(引数が String, String の場合) final Method labelMethod1 = getSetter(clazz, labelFieldName, String.class, String.class); if(labelMethod1 != null) { try { labelMethod1.invoke(obj, key, label); return; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException( String.format("fail set label with '%s' method", labelMethod1.getName()), e); } } // フィールドの場合 final Field labelField = getField(clazz, labelFieldName); if(labelField != null) { try { labelField.setAccessible(true); if(Map.class.isAssignableFrom(labelField.getType())) { Object labelValue = labelField.get(obj); if(labelValue == null) { labelValue = new HashMap<String, Point>(); labelField.set(obj, labelValue); } ((Map<String, String>) labelValue).put(key, label); } return; } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException( String.format("fail set label with '%s' field", labelField.getName()), e); } } } /** * 文字列が空文字か判定する。 * @param str * @return */ public static boolean isEmpty(final String str) { if(str == null || str.isEmpty()) { return true; } if(str.length() == 1) { return str.charAt(0) == '\u0000'; } return false; } /** * 文字列が空文字でないか判定する。 * @param str * @return */ public static boolean isNotEmpty(final String str) { return !isEmpty(str); } /** * コレクションが空か判定する。 * @param collection * @return nullまたはサイズが0のときにtrueを返す。 */ public static boolean isEmpty(final Collection<?> collection) { if(collection == null || collection.isEmpty()) { return true; } return false; } public static boolean isNotEmpty(final Collection<?> collection) { return !isEmpty(collection); } /** * 配列がが空か判定する。 * @param arrays * @return nullまたは、配列のサイズが0のときにtrueを返す。 */ public static boolean isEmpty(final Object[] arrays) { if(arrays == null || arrays.length == 0) { return true; } return false; } /** * 配列が空でないか判定する * @param arrays * @return */ public static boolean isNotEmpty(final Object[] arrays) { return !isEmpty(arrays); } private static final Pattern PATTERN_CELL_ADREESS = Pattern.compile("^([a-zA-Z]+)([0-9]+)$"); /** * Excelのアドレス形式'A1'を、Pointに変換する。 * @param address * @return 変換できない場合は、nullを返す。 */ public static Point parseCellAddress(final String address) { if(isEmpty(address)) { return null; } final Matcher matcher = PATTERN_CELL_ADREESS.matcher(address); if(!matcher.matches()) { return null; } final CellReference ref = new CellReference(address.toUpperCase()); return new Point(ref.getCol(), ref.getRow()); } /** * 座標をExcelのアドレス形式'A1'などに変換する * @param rowIndex 行インデックス * @param colIndex 列インデックス * @return */ public static String formatCellAddress(final int rowIndex, final int colIndex) { return POIUtils.formatCellAddress(rowIndex, colIndex); } /** * 座標をExcelのアドレス形式'A1'になどに変換する。 * @param cellAddress * @return * @throws IllegalArgumentException address == null. */ public static String formatCellAddress(final Point cellAddress) { return POIUtils.formatCellAddress(cellAddress); } /** * セルのアドレス'A1'を取得する。 * @param cell * @return IllegalArgumentException cell == null. */ public static String formatCellAddress(final Cell cell) { return POIUtils.formatCellAddress(cell); } /** * 指定したラベル(値を持つ)セルを検索し、取得する。 * <p>見つからない場合は、例外{@link CellNotFoundException}をスローする。 * @param sheet 検索対象のシート。 * @param label 検索するセルの値 * @param from 検索開始位置の列 * @param config システム設定 * @return 引数labelで指定した値を持つセル。 * @throws CellNotFoundException シート中に引数'label'を持つセルが存在しない場合。 */ public static Cell getCell(final Sheet sheet, final String label, final int from, final XlsMapperConfig config) throws CellNotFoundException { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(label, "label"); ArgUtils.notMin(from, 0, "from"); ArgUtils.notNull(config, "config"); return getCell(sheet, label, from, true, config); } /** * 指定したラベル(値を持つ)セルを検索し、取得する。 * @param sheet 検索対象のシート。 * @param label 検索するセルの値 * @param from 検索開始位置の列 * @param throwableWhenNotFound セルが見つからない場合例外をスローするかどうか。falseの場合、nullを返す。 * @return 引数labelで指定した値を持つセル。見つからに場合は、nullを返す。 * @throws CellNotFoundException シート中に引数'label'を持つセルが存在しない場合。 */ public static Cell getCell(final Sheet sheet, final String label, final int from, boolean throwableWhenNotFound, final XlsMapperConfig config) throws CellNotFoundException { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(label, "label"); ArgUtils.notMin(from, 0, "from"); ArgUtils.notNull(config, "config"); final int rows = POIUtils.getColumns(sheet); for(int i=0; i < rows; i++) { final Cell[] columns = POIUtils.getColumn(sheet, i); for(int j=from; j < columns.length; j++) { final String cellValue = POIUtils.getCellContents(columns[j], config.getCellFormatter()); if(matches(cellValue, label, config)) { return columns[j]; } } } if(throwableWhenNotFound) { throw new CellNotFoundException(sheet.getSheetName(), label); } return null; } /** * 指定したラベル(値を持つ)セルを検索し、取得する。 * * @since 0.5 * @param sheet 検索対象のシート。 * @param label 検索するセルの値 * @param fromCol 検索開始位置の列のインデックス * @param fromRow 検索開始位置の行のインデックス * @return 引数labelで指定した値を持つセル。見つからに場合は、nullを返す。 * @throws CellNotFoundException シート中に引数'label'を持つセルが存在しない場合。 */ public static Cell getCell(final Sheet sheet, final String label, final int fromCol, final int fromRow, final XlsMapperConfig config) throws CellNotFoundException { return getCell(sheet, label, fromCol, fromRow, true, config); } /** * 指定したラベル(値を持つ)セルを検索し、取得する。 * * @since 0.5 * @param sheet 検索対象のシート。 * @param label 検索するセルの値 * @param fromCol 検索開始位置の列のインデックス * @param fromRow 検索開始位置の行のインデックス * @param throwableWhenNotFound セルが見つからない場合例外をスローするかどうか。falseの場合、nullを返す。 * @return 引数labelで指定した値を持つセル。見つからに場合は、nullを返す。 * @throws CellNotFoundException シート中に引数'label'を持つセルが存在しない場合。ただし、引数throwableWhenNotFound=trueの場合のみ。 */ public static Cell getCell(final Sheet sheet, final String label, final int fromCol, final int fromRow, final boolean throwableWhenNotFound, final XlsMapperConfig config) throws CellNotFoundException { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(label, "label"); ArgUtils.notMin(fromCol, 0, "fromCol"); ArgUtils.notMin(fromRow, 0, "fromRow"); ArgUtils.notNull(config, "config"); final int maxRow = POIUtils.getRows(sheet); for(int i=fromRow; i < maxRow; i++) { final Row row = sheet.getRow(i); if(row == null) { continue; } final int maxCol = row.getLastCellNum();; for(int j=fromCol; j < maxCol; j++) { final Cell cell = row.getCell(j, Row.CREATE_NULL_AS_BLANK); final String cellValue = POIUtils.getCellContents(cell, config.getCellFormatter()); if(matches(cellValue, label, config)) { return cell; } } } if(throwableWhenNotFound) { throw new CellNotFoundException(sheet.getSheetName(), label); } return null; } /** * Return cell object by using first argument sheet. * This cell will be found by label name in Excel sheet. * * NOTICE: When the cell object is specified for the third argument, * a lower right cell is scanned from the cell. * * @param sheet JExcel Api sheet object. * @param label Target cell label. * @param after A lower right cell is scanned from the cell object. * @param includeAfter Is the third argument cell object scanned? * * @return Target JExcel Api cell object. * @throws CellNotFoundException This occures when the cell is not found. */ public static Cell getCell(final Sheet sheet, final String label, final Cell after, final boolean includeAfter, final XlsMapperConfig config) throws CellNotFoundException { return getCell(sheet, label, after, includeAfter, true, config); } /** * Return cell object by using first argument sheet. * This cell will be found by label name in Excel sheet. * * NOTICE: When the cell object is specified for the third argument, * a lower right cell is scanned from the cell. * * @param sheet JExcel Api sheet object. * @param label Target cell label. * @param after A lower right cell is scanned from the cell object. * @param includeAfter Is the third argument cell object scanned? * @param throwableWhenNotFound If this argument is true, throws XLSBeansException when we can't find target cell. * * @return Target JExcel Api cell object. * @throws CellNotFoundException This occures when the cell is not found. */ public static Cell getCell(final Sheet sheet, final String label, final Cell after, boolean includeAfter, boolean throwableWhenNotFound, final XlsMapperConfig config) throws CellNotFoundException { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(label, "label"); if (after == null) { // Call XLSBeans#getCell() - method if third argument is null. return Utils.getCell(sheet, label, 0, 0, throwableWhenNotFound, config); } //[TO POI] int columnStart = after.getColumnIndex(); int rowStart = after.getRowIndex(); final int maxRow = POIUtils.getRows(sheet); for(int i=rowStart; i < maxRow; i++) { final Row row = sheet.getRow(i); if(row == null) { continue; } final int maxCol = row.getLastCellNum();; for(int j=columnStart; j < maxCol; j++) { if(!includeAfter && i == rowStart && j == columnStart) { continue; } final Cell cell = row.getCell(j, Row.CREATE_NULL_AS_BLANK); final String cellValue = POIUtils.getCellContents(cell, config.getCellFormatter()); if(matches(cellValue, label, config)) { return cell; } } } if(throwableWhenNotFound) { throw new CellNotFoundException(sheet.getSheetName(), label); } return null; } /** * Return cell object by using first argument sheet. * This cell will be found by label name in Excel sheet. * * NOTICE: When the cell object is specified for the third argument, * a lower right cell is scanned from the cell. * * @param sheet JExcel Api sheet object. * @param label Target cell label. * @param after A lower right cell is scanned from this cell object. * @param config api configuration. * @return Target JExcel Api cell object. * @throws XlsMapperException This occures when the cell is not found. */ public static Cell getCell(final Sheet sheet, final String label, final Cell after, final XlsMapperConfig config) throws CellNotFoundException { return getCell(sheet, label, after, false, config); } /** * Setterメソッドか判定を行う。 * <ol>判定基準は次の通り。 * <li>メソッド名が'set'から始まる。 * <li>メソッドの引数が1つのみ。 * * @param method * @return * @throws IllegalArgumentException arg 'method' == null. */ public static boolean isSetterMethod(final Method method) { ArgUtils.notNull(method, "method"); method.setAccessible(true); if(!method.getName().startsWith("set")) { return false; } if(method.getParameterTypes().length != 1) { return false; } return true; } /** * Setterメソッドでないか判定を行う。 * {@link #isSetterMethod(Method)}の否定。 * @param method * @return */ public static boolean isNotSetterMethod(final Method method) { return !isSetterMethod(method); } /** * Getterメソッドか判定を行う。 * <ol>判定基準は次の通り。 * <li>メソッド名が'get'から始まる。 * <li>メソッドの引数が0個。 * * @param method * @return * @throws IllegalArgumentException arg 'method' == null. */ public static boolean isGetterMethod(final Method method) { ArgUtils.notNull(method, "method"); method.setAccessible(true); if(!method.getName().startsWith("get")) { return false; } if(method.getParameterTypes().length != 0) { return false; } return true; } /** * Getterメソッドでないか判定を行う。 * {@link #isGetterMethod(Method)}の否定。 * @param method * @return */ public static boolean isNotGetterMethod(final Method method) { return !isGetterMethod(method); } /** * アノテーション{@link XlsColumn}が付与されている読み込み系の指定したオブジェクトのメソッド(Setter)とフィールド情報を取得する。 * <p>フィールドは、public以外の全てのメソッドを対象とする。 * * @param clazz * @param name 属性columnNameと比較する値。名前がない場合はnullを指定する。 * @param reader * @param config * @return */ public static List<FieldAdaptor> getLoadingColumnProperties(final Class<?> clazz, final String name, final AnnotationReader reader, final XlsMapperConfig config) { ArgUtils.notNull(clazz, "clazz"); ArgUtils.notNull(reader, "reader"); ArgUtils.notNull(config, "config"); final List<FieldAdaptor> list = new ArrayList<>(); for(FieldAdaptor adaptor : getSetterColumnMethods(clazz, name, reader, config)) { list.add(adaptor); } for(FieldAdaptor adaptor : getColumnFields(clazz, name, reader, config)) { if(list.contains(adaptor)) { continue; } list.add(adaptor); } return list; } /** * アノテーション{@link XlsColumn}が付与されている書き込み系の指定したオブジェクトのメソッド(Setter)とフィールド情報を取得する。 * <p>フィールドは、public以外の全てのメソッドを対象とする。 * * @param clazz * @param name 属性columnNameと比較する値。名前がない場合はnullを指定する。 * @param reader * @param config * @return */ public static List<FieldAdaptor> getSavingColumnProperties(final Class<?> clazz, final String name, final AnnotationReader reader, final XlsMapperConfig config) { ArgUtils.notNull(clazz, "clazz"); ArgUtils.notNull(reader, "reader"); ArgUtils.notNull(config, "config"); final List<FieldAdaptor> list = new ArrayList<>(); for(FieldAdaptor adaptor : getGetterColumnMethods(clazz, name, reader, config)) { list.add(adaptor); } for(FieldAdaptor adaptor : getColumnFields(clazz, name, reader, config)) { if(list.contains(adaptor)) { continue; } list.add(adaptor); } return list; } /** * アノテーション{@link XlsColumn}が付与されているSetterメソッドを取得する。 * さらに、引数nameで指定した値と属性columnNameと一致するものを取得する。 * ただし、引数nameがnullの場合は、一致するものを判断する。 * <p>publicメソッドを取得する。 * @param clazz * @param name 属性columnNameと比較する値。名前がない場合はnullを指定する。 * @param reader * @param config * @return */ public static FieldAdaptor[] getSetterColumnMethods(final Class<?> clazz, final String name, final AnnotationReader reader, final XlsMapperConfig config) { ArgUtils.notNull(clazz, "clazz"); //TODO: 例外のスロー内容を見直す。 final List<FieldAdaptor> result = new ArrayList<>(); for(Method method : clazz.getMethods()) { if(isNotSetterMethod(method)) { continue; } final FieldAdaptor adaptor = new FieldAdaptor(clazz, method, reader); final XlsColumn column = adaptor.getLoadingAnnotation(XlsColumn.class); if(column == null) { continue; } final String columnName = column.columnName(); if(name == null) { result.add(adaptor); } else if(Utils.matches(name, columnName, config)) { result.add(adaptor); } } return result.toArray(new FieldAdaptor[result.size()]); } /** * アノテーション{@link XlsColumn}が付与されているGetterメソッドを取得する。 * さらに、引数nameで指定した値と属性columnNameと一致するものを取得する。 * ただし、引数nameがnullの場合は、一致するものを判断する。 * <p>publicメソッドを取得する。 * @param clazz * @param name 属性columnNameと比較する値。名前がない場合はnullを指定する。 * @param reader * @param config * @return */ public static FieldAdaptor[] getGetterColumnMethods(final Class<?> clazz, final String name, final AnnotationReader reader, final XlsMapperConfig config) { ArgUtils.notNull(clazz, "clazz"); //TODO: 例外のスロー内容を見直す。 final List<FieldAdaptor> result = new ArrayList<>(); for(Method method : clazz.getMethods()) { if(isNotGetterMethod(method) && isNotBooleanGetterMethod(method)) { continue; } final FieldAdaptor adaptor = new FieldAdaptor(clazz, method, reader); final XlsColumn column = adaptor.getSavingAnnotation(XlsColumn.class); if(column == null) { continue; } final String columnName = column.columnName(); if(name == null) { result.add(adaptor); } else if(matches(name, columnName, config)) { result.add(adaptor); } } return result.toArray(new FieldAdaptor[result.size()]); } /** * アノテーション{@link XlsColumn}が付与されているフィールドを取得する。 * さらに、引数nameで指定した値と属性columnNameと一致するものを取得する。 * ただし、引数nameがnullの場合は、一致するものを判断する。 * <p>publicメソッド以外も対象とする。 * @param clazz * @param name 名前を指定しない場合はnullを設定。 * @param reader * @param config * @return */ public static FieldAdaptor[] getColumnFields(final Class<?> clazz, final String name, final AnnotationReader reader, final XlsMapperConfig config) { ArgUtils.notNull(clazz, "clazz"); final List<FieldAdaptor> result = new ArrayList<>(); for(Field field : clazz.getDeclaredFields()) { field.setAccessible(true); FieldAdaptor adaptor = new FieldAdaptor(clazz, field, reader); final XlsColumn column = adaptor.getLoadingAnnotation(XlsColumn.class); if(column == null) { continue; } final String columnName = column.columnName(); if(name == null) { result.add(adaptor); } else if(Utils.matches(name, columnName, config)) { result.add(adaptor); } } return result.toArray(new FieldAdaptor[result.size()]); } /** * アノテーション{@link XlsMapColumns}が付与されている読み込み系の指定したオブジェクトのメソッド(Setter)とフィールド情報を取得する。 * <p>フィールドは、public以外の全てのメソッドを対象とする。 * * @param clazz * @param reader * @return */ public static List<FieldAdaptor> getLoadingMapColumnProperties(final Class<?> clazz, final AnnotationReader reader) { ArgUtils.notNull(clazz, "clazz"); final List<FieldAdaptor> list = new ArrayList<>(); for(FieldAdaptor adaptor : getSetterMethodsWithAnnotation(clazz, reader, XlsMapColumns.class)) { list.add(adaptor); } for(FieldAdaptor adaptor : getFieldsWithAnnotation(clazz, reader, XlsMapColumns.class)) { if(list.contains(adaptor)) { continue; } list.add(adaptor); } return list; } /** * アノテーション{@link XlsMapColumns}が付与されている書き込み系の指定したオブジェクトのメソッド(Getter)とフィールド情報を取得する。 * <p>フィールドは、public以外の全てのメソッドを対象とする。 * * @param clazz * @param reader * @return */ public static List<FieldAdaptor> getSavingMapColumnProperties(final Class<?> clazz, final AnnotationReader reader) { ArgUtils.notNull(clazz, "clazz"); final List<FieldAdaptor> list = new ArrayList<>(); for(FieldAdaptor adaptor : getGetterMethodsWithAnnotation(clazz, reader, XlsMapColumns.class)) { list.add(adaptor); } for(FieldAdaptor adaptor : getFieldsWithAnnotation(clazz, reader, XlsMapColumns.class)) { if(list.contains(adaptor)) { continue; } list.add(adaptor); } return list; } /** * アノテーション{@link XlsNestedRecords}が付与されている読み込み系の指定したオブジェクトのメソッド(Setter)とフィールド情報を取得する。 * <p>フィールドは、public以外の全てのメソッドを対象とする。 * * @since 1.4 * @param clazz レコードのクラス情報 * @param reader アノテーションリーダ。 * @return */ public static List<FieldAdaptor> getLoadingNestedRecordsProperties(final Class<?> clazz, final AnnotationReader reader) { ArgUtils.notNull(clazz, "clazz"); final List<FieldAdaptor> list = new ArrayList<>(); for(FieldAdaptor adaptor : getSetterMethodsWithAnnotation(clazz, reader, XlsNestedRecords.class)) { list.add(adaptor); } for(FieldAdaptor adaptor : getFieldsWithAnnotation(clazz, reader, XlsNestedRecords.class)) { if(list.contains(adaptor)) { continue; } list.add(adaptor); } return list; } /** * アノテーション{@link XlsNestedRecords}が付与されている書き込み系の指定したオブジェクトのメソッド(Setter)とフィールド情報を取得する。 * <p>フィールドは、public以外の全てのメソッドを対象とする。 * * @since 1.4 * @param clazz レコードのクラス情報 * @param reader アノテーションリーダ。 * @return */ public static List<FieldAdaptor> getSavingNestedRecordsProperties(final Class<?> clazz, final AnnotationReader reader) { ArgUtils.notNull(clazz, "clazz"); final List<FieldAdaptor> list = new ArrayList<>(); for(FieldAdaptor adaptor : getGetterMethodsWithAnnotation(clazz, reader, XlsNestedRecords.class)) { list.add(adaptor); } for(FieldAdaptor adaptor : getFieldsWithAnnotation(clazz, reader, XlsNestedRecords.class)) { if(list.contains(adaptor)) { continue; } list.add(adaptor); } return list; } /** * 指定したアノテーションを持つ読み込み系メソッド(Setter)とフィールド情報を取得する。 * @param clazz * @param reader * @param annoClass * @return */ public static List<FieldAdaptor> getLoadingPropertiesWithAnnotation(final Class<?> clazz, final AnnotationReader reader, final Class<? extends Annotation> annoClass) { ArgUtils.notNull(clazz, "clazz"); ArgUtils.notNull(annoClass, "annoClass"); final List<FieldAdaptor> list = new ArrayList<>(); for(FieldAdaptor adaptor : getSetterMethodsWithAnnotation(clazz, reader, annoClass)) { list.add(adaptor); } for(FieldAdaptor adaptor : getFieldsWithAnnotation(clazz, reader, annoClass)) { if(list.contains(adaptor)) { continue; } list.add(adaptor); } return list; } /** * 指定したアノテーションを持つ読み込み系メソッド(Setter)とフィールド情報を取得する。 * @param clazz * @param reader * @param annoClass * @return */ public static List<FieldAdaptor> getSavingPropertiesWithAnnotation(final Class<?> clazz, final AnnotationReader reader, final Class<? extends Annotation> annoClass) { ArgUtils.notNull(clazz, "clazz"); ArgUtils.notNull(annoClass, "annoClass"); final List<FieldAdaptor> list = new ArrayList<>(); for(FieldAdaptor adaptor : getGetterMethodsWithAnnotation(clazz, reader, annoClass)) { list.add(adaptor); } for(FieldAdaptor adaptor : getFieldsWithAnnotation(clazz, reader, annoClass)) { if(list.contains(adaptor)) { continue; } list.add(adaptor); } return list; } /** * 指定したアノテーションが付与されたSetterメソッド情報を取得する。 * @param clazz * @param reader * @param annoClass * @return */ public static FieldAdaptor[] getSetterMethodsWithAnnotation(final Class<?> clazz, final AnnotationReader reader, final Class<? extends Annotation> annoClass) { ArgUtils.notNull(clazz, "clazz"); ArgUtils.notNull(annoClass, "annoClass"); final List<FieldAdaptor> result = new ArrayList<>(); for(Method method : clazz.getMethods()) { if(isNotSetterMethod(method)) { continue; } final FieldAdaptor adaptor = new FieldAdaptor(clazz, method, reader); if(adaptor.hasLoadingAnnotation(annoClass)) { result.add(adaptor); } } return result.toArray(new FieldAdaptor[result.size()]); } /** * 指定したアノテーションが付与されたGetterメソッド情報を取得する。 * @param clazz * @param reader * @param annoClass * @return */ public static FieldAdaptor[] getGetterMethodsWithAnnotation(final Class<?> clazz, final AnnotationReader reader, final Class<? extends Annotation> annoClass) { ArgUtils.notNull(clazz, "clazz"); ArgUtils.notNull(annoClass, "annoClass"); final List<FieldAdaptor> result = new ArrayList<>(); for(Method method : clazz.getMethods()) { if(isNotGetterMethod(method) && isNotBooleanGetterMethod(method)) { continue; } final FieldAdaptor adaptor = new FieldAdaptor(clazz, method, reader); if(adaptor.hasSavingAnnotation(annoClass)) { result.add(adaptor); } } return result.toArray(new FieldAdaptor[result.size()]); } /** * 指定したアノテーションが付与されたフィールド情報を取得する。 * @param clazz * @param reader * @param annoClass * @return */ public static FieldAdaptor[] getFieldsWithAnnotation(final Class<?> clazz, final AnnotationReader reader, final Class<? extends Annotation> annoClass) { ArgUtils.notNull(clazz, "clazz"); ArgUtils.notNull(annoClass, "annoClass"); final List<FieldAdaptor> result = new ArrayList<>(); for(Field field : clazz.getDeclaredFields()) { field.setAccessible(true); final FieldAdaptor adaptor = new FieldAdaptor(clazz, field, reader); if(adaptor.hasLoadingAnnotation(annoClass)) { result.add(adaptor); } } return result.toArray(new FieldAdaptor[result.size()]); } /** * オブジェクトの比較を行う。 * <p>値がnullの場合を考慮する。 * @param obj1 * @param obj2 * @return */ public static boolean equals(final Object obj1, final Object obj2) { if(obj1 == null && obj2 == null) { return true; } if(obj1 == null) { return false; } if(obj2 == null) { return false; } return obj1.equals(obj2); } public static boolean notEquals(final Object obj1, final Object obj2) { return !equals(obj1, obj2); } /** * オブジェクトを文字列に変換する。 * <p>nullの場合、文字列として "null"を返す。 * <p>単純に、{@link Object#toString()}を呼び出す。 * @param value * @return */ public static String convertToString(final Object value) { if(value == null) { return "null"; } return value.toString(); } /** * アノテーションの属性trimに従い、文字列をトリムする。 * @param value * @param converterAnno * @return */ public static String trim(final String value, final XlsConverter converterAnno) { if(converterAnno == null || !converterAnno.trim() || value == null) { return value; } return value.trim(); } /** * アノテーションの属性trimに従い、文字列をトリムする。 * @param value * @param converterAnno * @return */ public static String trim(final Character value, final XlsConverter converterAnno) { if(value == null) { return ""; } if(converterAnno == null || !converterAnno.trim()) { return String.valueOf(value); } return String.valueOf(value).trim(); } /** * 文字列をトリムする。値がnullの場合は、空文字にする。 * @param value * @return */ public static String trimToEmpty(final String value) { return value == null ? "" : value.trim(); } /** * デフォルト値がアノテーションに設定されているかどうか。 * @param converterAnno * @return */ public static boolean hasDefaultValue(final XlsConverter converterAnno) { if(converterAnno == null || isEmpty(converterAnno.defaultValue())) { return false; } return true; } /** * デフォルト値がアノテーションに設定されていないかどうか。 * @param converterAnno * @return */ public static boolean hasNotDefaultValue(final XlsConverter converterAnno) { return !hasDefaultValue(converterAnno); } /** * デフォルト値がアノテーションに設定されているかどうか。 * @param converterAnno * @return */ public static String getDefaultValue(final XlsConverter converterAnno) { if(converterAnno == null || isEmpty(converterAnno.defaultValue())) { return null; } return converterAnno.defaultValue(); } /** * 引数「value」がnullまたは空文字であれば初期値を取得する。 * @param value * @param converterAnno * @return */ public static String getDefaultValueIfEmpty(final String value, final XlsConverter converterAnno) { if(isNotEmpty(value)) { return value; } if(hasDefaultValue(converterAnno)) { return converterAnno.defaultValue(); } return value; } /** * アノテーションXlsConverterの属性のtrimの値を取得する。 * @param converterAnno * @return 引数converterAnnoの値がnullの場合、falseを返す。 */ public static boolean getTrimValue(final XlsConverter converterAnno) { if(converterAnno == null) { return false; } return converterAnno.trim(); } /** * PostProcessなどのメソッドを実行する。 * <p>メソッドの引数が既知のものであれば、インスタンスを設定する。 * * @param processObj 実行対象の処理が埋め込まれているオブジェクト。 * @param method 実行対象のメソッド情報 * @param beanObj 処理対象のBeanオブジェクト。 * @param sheet シート情報 * @param config 共通設定 * @param errors エラー情報 * @throws XlsMapperException */ public static void invokeNeedProcessMethod(final Object processObj, final Method method, final Object beanObj, final Sheet sheet, final XlsMapperConfig config, final SheetBindingErrors errors) throws XlsMapperException { final Class<?>[] paramTypes = method.getParameterTypes(); final Object[] paramValues = new Object[paramTypes.length]; for(int i=0; i < paramTypes.length; i++) { if(Sheet.class.isAssignableFrom(paramTypes[i])) { paramValues[i] = sheet; } else if(XlsMapperConfig.class.isAssignableFrom(paramTypes[i])) { paramValues[i] = config; } else if(SheetBindingErrors.class.isAssignableFrom(paramTypes[i])) { paramValues[i] = errors; } else if(paramTypes[i].isAssignableFrom(beanObj.getClass())) { paramValues[i] = beanObj; } else if(paramTypes[i].equals(Object.class)) { paramValues[i] = beanObj; } else { paramValues[i] = null; } } try { method.setAccessible(true); method.invoke(processObj, paramValues); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { Throwable t = e.getCause() == null ? e : e.getCause(); throw new XlsMapperException( String.format("fail execute method '%s#%s'.", processObj.getClass().getName(), method.getName()), t); } } /** * 文字列形式のロケールをオブジェクトに変換する。 * <p>アンダーバーで区切った'ja_JP'を分解して、Localeに渡す。 * @param str * @return 引数が空の時はデフォルトロケールを返す。 */ public static Locale getLocale(final String str) { if(isEmpty(str)) { return Locale.getDefault(); } if(!str.contains("_")) { return new Locale(str); } final String[] split = str.split("_"); if(split.length == 2) { return new Locale(split[0], split[1]); } else { return new Locale(split[0], split[1], split[2]); } } /** * エスケープ文字を除去した文字列を取得する。 * @param str * @param escapeChar * @return */ public static String removeEscapeChar(final String str, final char escapeChar) { if(str == null || str.isEmpty()) { return str; } final String escapeStr = String.valueOf(escapeChar); StringBuilder sb = new StringBuilder(); LinkedList<String> stack = new LinkedList<String>(); final int length = str.length(); for(int i=0; i < length; i++) { final char c = str.charAt(i); if(StackUtils.equalsTopElement(stack, escapeStr)) { // スタックの一番上がエスケープ文字の場合 StackUtils.popup(stack); sb.append(c); } else if(c == escapeChar) { // スタックに積む stack.push(String.valueOf(c)); } else { sb.append(c); } } if(!stack.isEmpty()) { sb.append(StackUtils.popupAndConcat(stack)); } return sb.toString(); } /** * Listのインスタンスを他のCollectionのインスタンスに変換する。 * <p>ただし、変換先のクラスタイプがインタフェースの場合は変換しない。 * <p>変換元のクラスと変換先のクラスが同じ場合は、変換しない。 * * @since 1.0 * @param list 変換元のListのインスタンス * @param toClass 変換先のCollectionのクラス * @param beanFactory てインスタンスを生成するファクトリクラス。 * @return 変換したコレクションのインスタンス */ @SuppressWarnings({"rawtypes", "unchecked"}) public static Collection convertListToCollection(final List list, final Class<Collection> toClass, final FactoryCallback<Class<?>, Object> beanFactory) { if(list.getClass().equals(toClass)) { return list; } if(toClass.isInterface()) { if(List.class.isAssignableFrom(toClass)) { // 変換先がListの実態の場合はそのまま。 return list; } else if(Set.class.isAssignableFrom(toClass)) { Collection value = (Collection) beanFactory.create(LinkedHashSet.class); value.addAll(list); return value; } else if(Queue.class.isAssignableFrom(toClass)) { Collection value = (Collection) beanFactory.create(LinkedList.class); value.addAll(list); return value; } else if(Collection.class.isAssignableFrom(toClass)) { Collection value = (Collection) beanFactory.create(ArrayList.class); value.addAll(list); return value; } else { throw new IllegalArgumentException("not support class type:" + toClass.getName()); } } Collection value = (Collection) beanFactory.create(toClass); value.addAll(list); return value; } /** * CollectionのインスタンスをListに変換する。 * * @since 1.0 * @param collection 変換元のCollectionのインスタンス。 * @return 変換したListのインスタンス。 */ public static <T> List<T> convertCollectionToList(final Collection<T> collection) { if(List.class.isAssignableFrom(collection.getClass())) { return (List<T>)collection; } return new ArrayList<>(collection); } /** * セルに数式を設定する。 * @since 1.5 * * @param adaptor フィールド * @param formulaAnno 数式定義用のアノテーション。 * @param config システム設定。 * @param cell 設定対象のセル。 * @param targetBean 処理対象のJavaBean. * @throws XlsMapperException */ public static void setupCellFormula(final FieldAdaptor adaptor, final XlsFormula formulaAnno, final XlsMapperConfig config, final Cell cell, final Object targetBean) throws XlsMapperException { ArgUtils.notNull(adaptor, "adaptor"); ArgUtils.notNull(formulaAnno, "formulaAnno"); ArgUtils.notNull(config, "config"); ArgUtils.notNull(cell, "cell"); final String formula = getFormulaValue(adaptor, formulaAnno, config, cell, targetBean); if(isEmpty(formula)) { cell.setCellType(Cell.CELL_TYPE_BLANK); return; } try { cell.setCellFormula(formula); cell.setCellType(Cell.CELL_TYPE_FORMULA); } catch(FormulaParseException e) { // 数式の解析に失敗した場合 final String message = new StringBuilder() .append(String.format("Fail parse formula '%s'.", formula)) .append(String.format(" Cell '%s' map from '%s#%s'.", formatCellAddress(cell), adaptor.getDeclaringClass().getName(), adaptor.getName())) .toString(); throw new ConversionException(message, e, adaptor.getTargetClass()); } } /** * セルに設定する数式をアノテーションから組み立てる。 * * @since 1.5 * @param adaptor フィールド * @param formulaAnno 数式定義用のアノテーション。 * @param config システム設定。 * @param cell 設定対象のセル。 * @param targetBean 処理対象のJavaBean. * @return 数式。 * @throws XlsMapperException */ public static String getFormulaValue(final FieldAdaptor adaptor, final XlsFormula formulaAnno, final XlsMapperConfig config, final Cell cell, final Object targetBean) throws XlsMapperException { if(isNotEmpty(formulaAnno.value())) { final Map<String, Object> vars = new HashMap<>(); vars.put("rowIndex", cell.getRowIndex()); vars.put("columnIndex", cell.getColumnIndex()); vars.put("rowNumber", cell.getRowIndex()+1); vars.put("columnNumber", cell.getColumnIndex()+1); vars.put("columnAlpha", CellReference.convertNumToColString(cell.getColumnIndex())); vars.put("address", POIUtils.formatCellAddress(cell)); vars.put("targetBean", targetBean); vars.put("cell", cell); try { return config.getFormulaFormatter().interpolate(formulaAnno.value(), vars); } catch(Exception e) { throw new AnnotationInvalidException( String.format("With '%s', @XlsFormula#value '%s' is wrong formula.", adaptor.getNameWithClass(), formulaAnno.value()), formulaAnno); } } else if(isNotEmpty(formulaAnno.methodName())) { // 戻り値が文字列の数式を返すメソッドを探す final Class<?> targetClass = targetBean.getClass(); Method method = null; for(Method m : targetClass.getDeclaredMethods()) { if(m.getName().equals(formulaAnno.methodName()) && m.getReturnType().equals(String.class)) { method = m; break; } } if(method == null) { throw new AnnotationInvalidException( String.format("With '%s', @XlsFormula#methodName '%s' is not found in class '%s'.", adaptor.getNameWithClass(), formulaAnno.methodName(), targetClass.getName()), formulaAnno); } // メソッドの引数の組み立て final Class<?>[] paramTypes = method.getParameterTypes(); final Object[] paramValues = new Object[paramTypes.length]; for(int i=0; i < paramTypes.length; i++) { if(Cell.class.isAssignableFrom(paramTypes[i])) { paramValues[i] = cell; } else if(Point.class.isAssignableFrom(paramTypes[i])) { paramValues[i] = new Point(cell.getColumnIndex(), cell.getRowIndex()); } else if(Sheet.class.isAssignableFrom(paramTypes[i])) { paramValues[i] = cell.getSheet(); } else if(XlsMapperConfig.class.isAssignableFrom(paramTypes[i])) { paramValues[i] = config; } else { paramValues[i] = null; } } // メソッドの実行 try { method.setAccessible(true); return (String) method.invoke(targetBean, paramValues); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { Throwable t = e.getCause() == null ? e : e.getCause(); throw new XlsMapperException( String.format("Fail execute method '%s#%s'.", targetClass.getName(), method.getName()), t); } } else { throw new AnnotationInvalidException(String.format("With '%s', @XlsFormula attribute 'value' or 'methodName' should not be required.", adaptor.getNameWithClass()), formulaAnno); } } }