IsEmptyBuilder.java

  1. package com.gh.mygreen.xlsmapper.util;

  2. import java.lang.reflect.AccessibleObject;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Modifier;
  5. import java.util.Arrays;
  6. import java.util.Collection;
  7. import java.util.Map;
  8. import java.util.concurrent.atomic.AtomicBoolean;


  9. /**
  10.  * 値が全て空かどかチェックするためのクラス。
  11.  * <p>アノテーション{@link com.gh.mygreen.xlsmapper.annotation.XlsIgnorable}を付与したメソッドの実装に利用します。</p>
  12.  *
  13.  * <p>リフレクションを利用して判定する場合は、位置情報のフィールドpositions、ラベル情報のフィールドlabelsを除外します。</p>
  14.  * <pre class="highlight"><code class="java">
  15.  * // リフレクションを使用する場合
  16.  * {@literal @XlsIgnoable}
  17.  * public boolean isEmpty() {
  18.  *     return IsEmptyBuilder.reflectionIsEmpty(this, "positions", "labels");
  19.  * }
  20.  * </code></pre>
  21.  *
  22.  * <p>フィールドを1つずつ判定する場合は、{@code append(...)}メソッドを利用します。</p>
  23.  * メソッド{@link #compare(IsEmptyComparator)}を利用することで、独自の実装も可能で、その際にLambda式を利用することもできます。
  24.  *
  25.  * <pre class="highlight"><code class="java">
  26.  * // 独自に組み立てる場合
  27.  * {@literal @XlsIgnoable}
  28.  * public boolean isEmpty() {
  29.  *     return new IsEmptyBuilder()
  30.  *         .append(name)
  31.  *         .append(age)
  32.  *         .compare(() {@literal ->} StringUtils.isBlank(address))
  33.  *         .isEmpty();
  34.  * }
  35.  * </code></pre>
  36.  *
  37.  * <p>オプションを指定して処理する場合、{@link IsEmptyConfig}を利用します。
  38.  * <pre class="highlight"><code class="java">
  39.  * // オプションを指定する場合
  40.  * {@literal @XlsIgnoable}
  41.  * public boolean isEmpty() {
  42.  *     return IsEmptyBuilder.reflectionIsEmpty(this
  43.  *         , IsEmptyConfig.create().withTestArrayElement(false).withTestCollectionElement(false)
  44.  *         , "positions", "labels");
  45.  * }
  46.  * </code></pre>
  47.  *
  48.  *
  49.  * @since 0.5
  50.  * @author T.TSUCHIE
  51.  *
  52.  */
  53. public class IsEmptyBuilder {

  54.     /**
  55.      * 現在までの判定結果を保持する。
  56.      * true: 値が空かどうか。
  57.      */
  58.     private final AtomicBoolean result;

  59.     /**
  60.      * 数値の場合、0を空として扱うか。
  61.      */
  62.     private boolean zeroAsEmpty;

  63.     /**
  64.      * 配列の場合、値も検証対象とするかどうか。
  65.      */
  66.     private boolean testArrayElement;

  67.     /**
  68.      * Collectionの場合、値も検証対象とするかどうか。
  69.      */
  70.     private boolean testCollectionElement;

  71.     /**
  72.      * Mapの場合、値も対象とするかどうか。
  73.      */
  74.     private boolean testMapValue;

  75.     /**
  76.      * transientが付与されたフィールドも対象とするかどうか。
  77.      */
  78.     private boolean testTransient;

  79.     /**
  80.      * コンストラクタ。
  81.      */
  82.     public IsEmptyBuilder() {
  83.         this(IsEmptyConfig.create());

  84.     }

  85.     /**
  86.      * {@link IsEmptyConfig}を指定するコンストラクタ。
  87.      * @param config 設定用クラス。
  88.      * @throws IllegalArgumentException config is null.
  89.      */
  90.     public IsEmptyBuilder(final IsEmptyConfig config) {
  91.         ArgUtils.notNull(config, "config");

  92.         this.result = new AtomicBoolean(true);

  93.         this.zeroAsEmpty = config.isZeroAsEmpty();
  94.         this.testArrayElement = config.isTestArrayElement();
  95.         this.testCollectionElement = config.isTestCollectionElement();
  96.         this.testMapValue = config.isTestMapValue();
  97.         this.testTransient = config.isTestTransient();
  98.     }

  99.     /**
  100.      * リフレクションを使用しフィールドの値を取得し判定する。
  101.      * <p>static修飾子を付与しているフィールドは、除外されます。
  102.      * <p>transient修飾子を付与しているフィールドは、除外されます。
  103.      * @param obj 判定対象のオブジェクト。
  104.      * @param excludedFields 除外対処のフィールド名。
  105.      * @return 引数で指定したobjがnullの場合、trueを返す。
  106.      */
  107.     public static boolean reflectionIsEmpty(final Object obj, final String... excludedFields) {
  108.         return reflectionIsEmpty(obj, IsEmptyConfig.create(), Arrays.asList(excludedFields));
  109.     }

  110.     /**
  111.      * リフレクションを使用しフィールドの値を取得し判定する。
  112.      * @since 1.0
  113.      * @param obj 判定対象のオブジェクト。
  114.      * @param config 判定用の設定クラス。
  115.      * @param excludedFields 除外対処のフィールド名。
  116.      * @return 引数で指定したobjがnullの場合、trueを返す。
  117.      */
  118.     public static boolean reflectionIsEmpty(final Object obj, final IsEmptyConfig config, final String... excludedFields) {
  119.         return reflectionIsEmpty(obj, config, Arrays.asList(excludedFields));
  120.     }

  121.     /**
  122.      * リフレクションを使用しフィールドの値を取得し判定する。
  123.      * @since 1.0
  124.      * @param obj 判定対象のオブジェクト。
  125.      * @param config 判定用の設定クラス。
  126.      * @param excludedFields 除外対処のフィールド名。
  127.      * @return 引数で指定したobjがnullの場合、trueを返す。
  128.      */
  129.     public static boolean reflectionIsEmpty(final Object obj, final IsEmptyConfig config, final Collection<String> excludedFields) {

  130.         if(obj == null) {
  131.             return true;
  132.         }

  133.         final IsEmptyBuilder builder = new IsEmptyBuilder(config);
  134.         final Field[] fields = obj.getClass().getDeclaredFields();
  135.         AccessibleObject.setAccessible(fields, true);

  136.         for(Field field : fields) {

  137.             // static フィールドかどうか
  138.             if(Modifier.isStatic(field.getModifiers())) {
  139.                 continue;
  140.             }

  141.             // transientのフィールドかどうか。
  142.             if(!builder.testTransient && Modifier.isTransient(field.getModifiers())) {
  143.                 continue;
  144.             }

  145.             // 除外対象のフィールド名かどうか。
  146.             if(excludedFields != null && excludedFields.contains(field.getName())) {
  147.                 continue;
  148.             }

  149.             try {
  150.                 final Object value = field.get(obj);
  151.                 builder.append(value);

  152.             } catch (IllegalArgumentException | IllegalAccessException e) {
  153.                 throw new InternalError("Unexpected IllegalAccessException");
  154.             }

  155.         }

  156.         return builder.isEmpty();
  157.     }

  158.     /**
  159.      * 値が空でないことを設定する。
  160.      */
  161.     private void setNotEmpty() {
  162.         this.result.set(false);
  163.     }

  164.     /**
  165.      * String型の値を追加する。
  166.      * @param value nullまたは空文字の場合、空と判断する。
  167.      * @return this.
  168.      */
  169.     public IsEmptyBuilder append(final String value) {
  170.         return append(value, false);
  171.     }

  172.     /**
  173.      * String型の値を追加する。
  174.      * @param value nullまたは空文字の場合、空と判断する。
  175.      * @param trim 引数valueをトリムした後空文字と判定するかどうか。
  176.      * @return this.
  177.      */
  178.     public IsEmptyBuilder append(final String value, final boolean trim) {
  179.         if(isNotEmpty()) {
  180.             return this;
  181.         }

  182.         if(trim) {
  183.             if(value != null && !value.trim().isEmpty()) {
  184.                 setNotEmpty();
  185.             }
  186.         } else {
  187.             if(value != null && !value.isEmpty()) {
  188.                 setNotEmpty();
  189.             }
  190.         }

  191.         return this;
  192.     }

  193.     /**
  194.      * char型の値を追加する。
  195.      * @param value 空文字の場合、空と判断する。
  196.      * @return this
  197.      */
  198.     public IsEmptyBuilder append(final char value) {
  199.         if(isNotEmpty()) {
  200.             return this;
  201.         }

  202.         final String str = (value == '\u0000' ? "" : String.valueOf(value));
  203.         return append(str);
  204.     }

  205.     /**
  206.      * char型の値を追加する。
  207.      * @param value 空文字の場合、空と判断する。
  208.      * @param trim 引数valueをトリムした後空文字と判定するかどうか。
  209.      * @return this
  210.      */
  211.     public IsEmptyBuilder append(final char value, final boolean trim) {
  212.         if(isNotEmpty()) {
  213.             return this;
  214.         }

  215.         final String str = (value == '\u0000' ? "" : String.valueOf(value));
  216.         return append(str, trim);
  217.     }

  218.     /**
  219.      * boolean型の値を追加する。
  220.      * @param value false場合、空と判断する。
  221.      * @return this.
  222.      */
  223.     public IsEmptyBuilder append(final boolean value) {
  224.         if(isNotEmpty()) {
  225.             return this;
  226.         }

  227.         if(value) {
  228.             setNotEmpty();
  229.         }

  230.         return this;
  231.     }

  232.     /**
  233.      * byte型の値を追加する。
  234.      * @param value {@link #isZeroAsEmpty()}がtrueの場合、0の値を空として扱う。
  235.      * @return this
  236.      */
  237.     public IsEmptyBuilder append(final byte value) {
  238.         if(isNotEmpty()) {
  239.             return this;
  240.         }

  241.         if(!(isZeroAsEmpty() && value == 0)) {
  242.             setNotEmpty();
  243.         }

  244.         return this;
  245.     }

  246.     /**
  247.      * short型の値を追加する。
  248.      * @param value {@link #isZeroAsEmpty()}がtrueの場合、0の値を空として扱う。
  249.      * @return this
  250.      */
  251.     public IsEmptyBuilder append(final short value) {
  252.         if(isNotEmpty()) {
  253.             return this;
  254.         }

  255.         if(!(isZeroAsEmpty() && value == 0)) {
  256.             setNotEmpty();
  257.         }

  258.         return this;
  259.     }

  260.     /**
  261.      * int型の値を追加する。
  262.      * @param value {@link #isZeroAsEmpty()}がtrueの場合、0の値を空として扱う。
  263.      * @return this
  264.      */
  265.     public IsEmptyBuilder append(final int value) {
  266.         if(isNotEmpty()) {
  267.             return this;
  268.         }

  269.         if(!(isZeroAsEmpty() && value == 0)) {
  270.             setNotEmpty();
  271.         }

  272.         return this;
  273.     }

  274.     /**
  275.      * long型の値を追加する。
  276.      * @param value {@link #isZeroAsEmpty()}がtrueの場合、0の値を空として扱う。
  277.      * @return this
  278.      */
  279.     public IsEmptyBuilder append(final long value) {
  280.         if(isNotEmpty()) {
  281.             return this;
  282.         }

  283.         if(!(isZeroAsEmpty() && value == 0L)) {
  284.             setNotEmpty();
  285.         }

  286.         return this;
  287.     }

  288.     /**
  289.      * float型の値を追加する。
  290.      * @param value {@link #isZeroAsEmpty()}がtrueの場合、0.0の値を空として扱う。
  291.      * @return this
  292.      */
  293.     public IsEmptyBuilder append(final float value) {
  294.         if(isNotEmpty()) {
  295.             return this;
  296.         }

  297.         if(!(isZeroAsEmpty() && value == 0.0)) {
  298.             setNotEmpty();
  299.         }

  300.         return this;
  301.     }

  302.     /**
  303.      * double型の値を追加する。
  304.      * @param value {@link #isZeroAsEmpty()}がtrueの場合、0の値を空として扱う。
  305.      * @return this
  306.      */
  307.     public IsEmptyBuilder append(final double value) {
  308.         if(isNotEmpty()) {
  309.             return this;
  310.         }

  311.         if(!(isZeroAsEmpty() && value == 0.0)) {
  312.             setNotEmpty();
  313.         }

  314.         return this;
  315.     }

  316.     /**
  317.      * Object型の値を追加する。
  318.      * @param value nullの場合、空と判断する。
  319.      * @return this
  320.      */
  321.     public IsEmptyBuilder append(final Object value) {
  322.         if(isNotEmpty()) {
  323.             return this;
  324.         }

  325.         if(value == null) {
  326.             return this;
  327.         }

  328.         final Class<?> clazz = value.getClass();
  329.         if(clazz.isPrimitive()) {
  330.             if(clazz.equals(Boolean.TYPE)) {
  331.                 return append((boolean) value);
  332.             } else if(clazz.equals(Byte.TYPE)) {
  333.                 return append((byte) value);
  334.             } else if(clazz.equals(Character.TYPE)) {
  335.                 return append((char) value);
  336.             } else if(clazz.equals(Short.TYPE)) {
  337.                 return append((short) value);
  338.             } else if(clazz.equals(Integer.TYPE)) {
  339.                 return append((int) value);
  340.             } else if(clazz.equals(Long.TYPE)) {
  341.                 return append((long) value);
  342.             } else if(clazz.equals(Float.TYPE)) {
  343.                 return append((float) value);
  344.             } else if(clazz.equals(Double.TYPE)) {
  345.                 return append((double) value);
  346.             }

  347.         } else if(clazz.isArray()) {
  348.             if(value instanceof boolean[]) {
  349.                 return append((boolean[]) value);
  350.             } else if(value instanceof char[]) {
  351.                 return append((char[]) value);
  352.             } else if(value instanceof byte[]) {
  353.                 return append((byte[]) value);
  354.             } else if(value instanceof short[]) {
  355.                 return append((short[]) value);
  356.             } else if(value instanceof int[]) {
  357.                 return append((int[]) value);
  358.             } else if(value instanceof long[]) {
  359.                 return append((long[]) value);
  360.             } else if(value instanceof float[]) {
  361.                 return append((float[]) value);
  362.             } else if(value instanceof double[]) {
  363.                 return append((double[]) value);
  364.             } else {
  365.                 return append((Object[]) value);
  366.             }

  367.         } else if(value instanceof String) {
  368.             return append((String) value);

  369.         } else if(value instanceof Boolean) {
  370.             return append((boolean) value);

  371.         } else if(value instanceof Byte) {
  372.             return append((byte) value);

  373.         } else if(value instanceof Character) {
  374.             return append((char) value);

  375.         } else if(value instanceof Short) {
  376.             return append((short) value);

  377.         } else if(value instanceof Integer) {
  378.             return append((int) value);

  379.         } else if(value instanceof Long) {
  380.             return append((long) value);

  381.         } else if(value instanceof Float) {
  382.             return append((float) value);

  383.         } else if(value instanceof Double) {
  384.             return append((double) value);

  385.         } else if(value instanceof Collection) {
  386.             return append((Collection<?>) value);

  387.         } else if(value instanceof Map) {
  388.             return append((Map<?, ?>) value);

  389.         } else {
  390.             setNotEmpty();
  391.         }

  392.         return this;
  393.     }

  394.     /**
  395.      * 配列型の値を追加する。
  396.      * @param value nullの場合、サイズが0の場合、空と判断する。
  397.      * @return this
  398.      */
  399.     public IsEmptyBuilder append(final Object[] value) {
  400.         if(isNotEmpty()) {
  401.             return this;
  402.         }

  403.         if(value != null && isTestArrayElement()) {
  404.             for(Object o : value) {
  405.                 if(isNotEmpty()) {
  406.                     return this;
  407.                 }

  408.                 append(o);
  409.             }

  410.         } else if(value != null && value.length != 0) {
  411.             setNotEmpty();
  412.         }

  413.         return this;

  414.     }

  415.     /**
  416.      * booleanの配列型の値を追加する。
  417.      * @param value nullの場合、サイズが0の場合、空と判断する。
  418.      * @return this
  419.      */
  420.     public IsEmptyBuilder append(final boolean[] value) {
  421.         if(isNotEmpty()) {
  422.             return this;
  423.         }

  424.         if(value != null && isTestArrayElement()) {
  425.             for(boolean o : value) {
  426.                 if(isNotEmpty()) {
  427.                     return this;
  428.                 }

  429.                 append(o);
  430.             }

  431.         } else if(value != null && value.length != 0) {
  432.             setNotEmpty();
  433.         }

  434.         return this;
  435.     }

  436.     /**
  437.      * charの配列型の値を追加する。
  438.      * @param value nullの場合、サイズが0の場合、空と判断する。
  439.      * @return this
  440.      */
  441.     public IsEmptyBuilder append(final char[] value) {
  442.         if(isNotEmpty()) {
  443.             return this;
  444.         }

  445.         if(value != null && isTestArrayElement()) {
  446.             for(char o : value) {
  447.                 if(isNotEmpty()) {
  448.                     return this;
  449.                 }

  450.                 append(o);
  451.             }

  452.         } else if(value != null && value.length != 0) {
  453.             setNotEmpty();
  454.         }

  455.         return this;
  456.     }

  457.     /**
  458.      * byteの配列型の値を追加する。
  459.      * @param value nullの場合、サイズが0の場合、空と判断する。
  460.      * @return this
  461.      */
  462.     public IsEmptyBuilder append(final byte[] value) {
  463.         if(isNotEmpty()) {
  464.             return this;
  465.         }

  466.         if(value != null && isTestArrayElement()) {
  467.             for(byte o : value) {
  468.                 if(isNotEmpty()) {
  469.                     return this;
  470.                 }

  471.                 append(o);
  472.             }

  473.         } else if(value != null && value.length != 0) {
  474.             setNotEmpty();
  475.         }

  476.         return this;
  477.     }

  478.     /**
  479.      * shortの配列型の値を追加する。
  480.      * @param value nullの場合、サイズが0の場合、空と判断する。
  481.      * @return this
  482.      */
  483.     public IsEmptyBuilder append(final short[] value) {
  484.         if(isNotEmpty()) {
  485.             return this;
  486.         }

  487.         if(value != null && isTestArrayElement()) {
  488.             for(short o : value) {
  489.                 if(isNotEmpty()) {
  490.                     return this;
  491.                 }

  492.                 append(o);
  493.             }

  494.         } else if(value != null && value.length != 0) {
  495.             setNotEmpty();
  496.         }

  497.         return this;
  498.     }

  499.     /**
  500.      * intの配列型の値を追加する。
  501.      * @param value nullの場合、サイズが0の場合、空と判断する。
  502.      * @return this
  503.      */
  504.     public IsEmptyBuilder append(final int[] value) {
  505.         if(isNotEmpty()) {
  506.             return this;
  507.         }

  508.         if(value != null && isTestArrayElement()) {
  509.             for(int o : value) {
  510.                 if(isNotEmpty()) {
  511.                     return this;
  512.                 }

  513.                 append(o);
  514.             }

  515.         } else if(value != null && value.length != 0) {
  516.             setNotEmpty();
  517.         }

  518.         return this;
  519.     }

  520.     /**
  521.      * longの配列型の値を追加する。
  522.      * @param value nullの場合、サイズが0の場合、空と判断する。
  523.      * @return this
  524.      */
  525.     public IsEmptyBuilder append(final long[] value) {
  526.         if(isNotEmpty()) {
  527.             return this;
  528.         }

  529.         if(value != null && isTestArrayElement()) {
  530.             for(long o : value) {
  531.                 if(isNotEmpty()) {
  532.                     return this;
  533.                 }

  534.                 append(o);
  535.             }

  536.         } else if(value != null && value.length != 0) {
  537.             setNotEmpty();
  538.         }

  539.         return this;
  540.     }

  541.     /**
  542.      * floatの配列型の値を追加する。
  543.      * @param value nullの場合、サイズが0の場合、空と判断する。
  544.      * @return this
  545.      */
  546.     public IsEmptyBuilder append(final float[] value) {
  547.         if(isNotEmpty()) {
  548.             return this;
  549.         }

  550.         if(value != null && isTestArrayElement()) {
  551.             for(float o : value) {
  552.                 if(isNotEmpty()) {
  553.                     return this;
  554.                 }

  555.                 append(o);
  556.             }

  557.         } else if(value != null && value.length != 0) {
  558.             setNotEmpty();
  559.         }

  560.         return this;
  561.     }

  562.     /**
  563.      * doubleの配列型の値を追加する。
  564.      * @param value nullの場合、サイズが0の場合、空と判断する。
  565.      * @return this
  566.      */
  567.     public IsEmptyBuilder append(final double[] value) {
  568.         if(isNotEmpty()) {
  569.             return this;
  570.         }

  571.         if(value != null && isTestArrayElement()) {
  572.             for(double o : value) {
  573.                 if(isNotEmpty()) {
  574.                     return this;
  575.                 }

  576.                 append(o);
  577.             }

  578.         } else if(value != null && value.length != 0) {
  579.             setNotEmpty();
  580.         }

  581.         return this;
  582.     }

  583.     /**
  584.      * Collection型の値を追加する。
  585.      * @param value nullの場合、サイズが0の場合、空と判断する。
  586.      * @return this
  587.      */
  588.     public IsEmptyBuilder append(final Collection<?> value) {
  589.         if(isNotEmpty()) {
  590.             return this;
  591.         }

  592.         if(value != null && isTestCollectionElement()) {
  593.             // コレクションの値も検証する。
  594.             for(Object o : value) {
  595.                 if(isNotEmpty()) {
  596.                     return this;
  597.                 }

  598.                 append(o);
  599.             }

  600.         } else if(value != null && !value.isEmpty()) {
  601.             setNotEmpty();
  602.         }

  603.         return this;

  604.     }

  605.     /**
  606.      * Map型の値を追加する。
  607.      * @param value nullの場合、サイズが0の場合、空と判断する。
  608.      * @return this
  609.      */
  610.     public IsEmptyBuilder append(final Map<?, ?> value) {
  611.         if(isNotEmpty()) {
  612.             return this;
  613.         }

  614.         if(value != null && isTestMapValue()) {
  615.             // コレクションの値も検証する。
  616.             for(Object o : value.values()) {
  617.                 if(isNotEmpty()) {
  618.                     return this;
  619.                 }

  620.                 append(o);
  621.             }

  622.         } else if(value != null && !value.isEmpty()) {
  623.             setNotEmpty();

  624.         }

  625.         return this;

  626.     }

  627.     /**
  628.      * 独自の実装で値を空かどうか判定する。
  629.      * <p>Java8のLambda式を利用すると簡潔に書ける。
  630.      * <pre class="highlight"><code class="java">
  631.      * public boolean isEmpty() {
  632.      *      return new IsEmptyBuilder()
  633.      *          .append(age)
  634.      *          .compare(() {@literal ->} StringUtils.isBlank(address))
  635.      *          .isEmpty();
  636.      *  }
  637.      * </code></pre>
  638.      *
  639.      * @param compare {@link IsEmptyComparator}のインスタンス。
  640.      * @return this.
  641.      */
  642.     public <T> IsEmptyBuilder compare(final IsEmptyComparator compare) {
  643.         if(isNotEmpty()) {
  644.             return this;
  645.         }

  646.         if(!compare.isEmpty()) {
  647.             setNotEmpty();
  648.         }

  649.         return this;
  650.     }

  651.     /**
  652.      * 判定結果が空かどうか。
  653.      * <p>{@code append(XXXX)}メソッドで何も追加されない場合、trueを返します。
  654.      * @return true:値が空。
  655.      */
  656.     public boolean isEmpty() {
  657.         return result.get();
  658.     }

  659.     /**
  660.      * 判定結果がが空でないかどうか。
  661.      * @return true:値が空でない。
  662.      */
  663.     public boolean isNotEmpty() {
  664.         return !isEmpty();
  665.     }

  666.     /**
  667.      * 数値の0を空として扱うかどうか。
  668.      * @return true:0を空として扱う。
  669.      */
  670.     private boolean isZeroAsEmpty() {
  671.         return zeroAsEmpty;
  672.     }

  673.     /**
  674.      * Collectionの値も検証するかどうか。
  675.      * @return true:Collectionの値も検証する。
  676.      */
  677.     private boolean isTestArrayElement() {
  678.         return testArrayElement;
  679.     }

  680.     /**
  681.      * Collectionの値も検証するかどうか。
  682.      * @return true:Collectionの値も検証する。
  683.      */
  684.     private boolean isTestCollectionElement() {
  685.         return testCollectionElement;
  686.     }

  687.     /**
  688.      * Mapの値も検証するかどうか。
  689.      * @return true:Mapの値も検証する。
  690.      */
  691.     private boolean isTestMapValue() {
  692.         return testMapValue;
  693.     }

  694. }