/*
* Copyright (c) 2011 NTT DATA Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jp.terasoluna.fw.collector.util;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import jp.terasoluna.fw.collector.Collector;
import jp.terasoluna.fw.collector.LogId;
import jp.terasoluna.fw.collector.util.strategy.ComparatorCompareStrategy;
import jp.terasoluna.fw.collector.util.strategy.CompareStrategy;
import jp.terasoluna.fw.logger.TLogger;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
/**
* コントロールブレイクチェッカー.<br>
*/
public class ControlBreakChecker {
/**
* Log.
*/
private static final TLogger LOGGER = TLogger
.getLogger(ControlBreakChecker.class);
/**
* コンストラクタ.
*/
protected ControlBreakChecker() {
}
/**
* 前処理コントロールブレイク判定メソッド.<br>
* @param collector Collector<?>
* @param keys String...
* @return true:コントロールブレイクを行う/false:コントロールブレイクしない
*/
public static boolean isPreBreak(Collector<?> collector, String... keys) {
return isPreBreak(collector, null, keys);
}
/**
* 前処理コントロールブレイク判定メソッド.<br>
* keysの数とcompareStrategiesの数と、比較時に利用されるCompareStrategyの関係は以下のようになる.<br>
* <ul>
* <li>keys : compareStrategies = N : N (またはN以上)の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[i]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : 1の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[0]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : M (N > M)の場合
* <ul>
* <li>keys[i] (i < M)のCompareStrategyはcompareStrategies[i]</li>
* <li>keys[i] (i >= M)のCompareStrategyはnull</li>
* </ul>
* </li>
* </ul>
* 比較仕様は、{@link #equalsObjects(Object, Object, CompareStrategy)} を参照のこと.
* @param collector Collector<?>
* @param compareStrategies CompareStrategy<?>[]
* @param keys String[]
* @return true:コントロールブレイクを行う/false:コントロールブレイクしない
* @see #equalsObjects(Object, Object, CompareStrategy)
*/
public static boolean isPreBreak(Collector<?> collector,
CompareStrategy<?>[] compareStrategies, String[] keys) {
if (collector != null) {
Object current = collector.getCurrent();
Object other = collector.getPrevious();
return isBreakInternal(current, other, compareStrategies, keys);
}
return false;
}
/**
* 後処理コントロールブレイク判定メソッド.<br>
* @param collector Collector<?>
* @param keys String...
* @return true:コントロールブレイクを行う/false:コントロールブレイクしない
*/
public static boolean isBreak(Collector<?> collector, String... keys) {
return isBreak(collector, null, keys);
}
/**
* 後処理コントロールブレイク判定メソッド.<br>
* keysの数とcompareStrategiesの数と、比較時に利用されるCompareStrategyの関係は以下のようになる.<br>
* <ul>
* <li>keys : compareStrategies = N : N (またはN以上)の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[i]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : 1の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[0]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : M (N > M)の場合
* <ul>
* <li>keys[i] (i < M)のCompareStrategyはcompareStrategies[i]</li>
* <li>keys[i] (i >= M)のCompareStrategyはnull</li>
* </ul>
* </li>
* </ul>
* 比較仕様は、{@link #equalsObjects(Object, Object, CompareStrategy)} を参照のこと.
* @param collector Collector<?>
* @param compareStrategies CompareStrategy<?>[]
* @param keys String[]
* @return true:コントロールブレイクを行う/false:コントロールブレイクしない
* @see #equalsObjects(Object, Object, CompareStrategy)
*/
public static boolean isBreak(Collector<?> collector,
CompareStrategy<?>[] compareStrategies, String[] keys) {
if (collector != null) {
Object current = collector.getCurrent();
Object other = collector.getNext();
return isBreakInternal(current, other, compareStrategies, keys);
}
return false;
}
/**
* コントロールブレイク判定メソッド.<br>
* このメソッドは、ver.1.1.x以前との互換性を保つために残している.<br>
* 新規に作成するコードの場合は、<br>
* {@link #isBreak(Collector, CompareStrategy[], String[])}、<br>
* {@link #isPreBreak(Collector, CompareStrategy[], String[])}、<br>
* {@link #isBreakInternal(Object, Object, CompareStrategy[], String...)}<br>
* を使用すること.<br>
* @param current Object 比較元オブジェクト
* @param other Object 比較先オブジェクト
* @param comparators Comparator<?>[]
* @param keys String...
* @return true:コントロールブレイクを行う/false:コントロールブレイクしない
* @see #isBreak(Collector, CompareStrategy[], String[])
* @see #isPreBreak(Collector, CompareStrategy[], String[])
* @see #isBreakInternal(Object, Object, CompareStrategy[], String...)
*/
protected static boolean isBreakInternal(Object current, Object other,
Comparator<?>[] comparators, String... keys) {
// comparator -> compareStrategyへの詰め替えを行う
if (comparators != null) {
CompareStrategy<?>[] compareStrategies = new CompareStrategy[comparators.length];
for (int i = 0; i < comparators.length; i++) {
compareStrategies[i] = new ComparatorCompareStrategy(
comparators[i]);
}
return isBreakInternal(current, other, compareStrategies, keys);
} else {
return isBreakInternal(current, other, (CompareStrategy[]) null,
keys);
}
}
/**
* コントロールブレイク判定メソッド.<br>
* keysの数とcompareStrategiesの数と、比較時に利用されるCompareStrategyの関係は以下のようになる.<br>
* <ul>
* <li>keys : compareStrategies = N : N (またはN以上)の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[i]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : 1の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[0]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : M (N > M)の場合
* <ul>
* <li>keys[i] (i < M)のCompareStrategyはcompareStrategies[i]</li>
* <li>keys[i] (i >= M)のCompareStrategyはnull</li>
* </ul>
* </li>
* </ul>
* 比較には {@link #equalsObjects(Object, Object, CompareStrategy)} メソッドを使用する.
* @param current Object 比較元オブジェクト
* @param other Object 比較先オブジェクト
* @param compareStrategies CompareStrategy<?>[]
* @param keys String...
* @return true:コントロールブレイクを行う/false:コントロールブレイクしない
* @see #equalsObjects(Object, Object, CompareStrategy)
*/
protected static boolean isBreakInternal(Object current, Object other,
CompareStrategy<?>[] compareStrategies, String... keys) {
// keyリストが空もしくはnullの場合はfalse
if (keys == null || keys.length == 0) {
// コントロールブレイクなし
return false;
}
// 片方がnullで、もう片方がnot nullの場合はtrue
if ((current != null && other == null)
|| (current == null && other != null)) {
// コントロールブレイク発生
return true;
}
if (other != null && current != null) {
for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
String key = keys[keyIndex];
CompareStrategy<?> compareStrategy = null;
if (compareStrategies != null) {
if (compareStrategies.length == 1) {
compareStrategy = compareStrategies[0];
} else if (keyIndex < compareStrategies.length) {
compareStrategy = compareStrategies[keyIndex];
}
}
if (key != null && key.length() != 0) {
Object currentValue = null;
Object otherValue = null;
// 値を取得する
try {
currentValue = PropertyUtils.getProperty(current, key);
} catch (Exception e) {
logOutputPropNotFound(e, current, key);
// ログを出力して次の項目をチェック
continue;
}
// 値を取得する
try {
otherValue = PropertyUtils.getProperty(other, key);
} catch (Exception e) {
logOutputPropNotFound(e, other, key);
// ログを出力して次の項目をチェック
continue;
}
// 比較
if (!equalsObjects(currentValue, otherValue, compareStrategy)) {
return true;
}
}
}
}
// コントロールブレイクなし
return false;
}
/**
* 前処理コントロールブレイクキー取得.<br>
* @param collector Collector<?>
* @param keys String...
* @return コントロールブレイクキーリスト
*/
public static Map<String, Object> getPreBreakKey(Collector<?> collector,
String... keys) {
return getPreBreakKey(collector, null, keys);
}
/**
* 前処理コントロールブレイクキー取得.<br>
* keysの数とcompareStrategiesの数と、比較時に利用されるCompareStrategyの関係は以下のようになる.<br>
* <ul>
* <li>keys : compareStrategies = N : N (またはN以上)の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[i]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : 1の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[0]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : M (N > M)の場合
* <ul>
* <li>keys[i] (i < M)のCompareStrategyはcompareStrategies[i]</li>
* <li>keys[i] (i >= M)のCompareStrategyはnull</li>
* </ul>
* </li>
* </ul>
* 比較仕様は、{@link #equalsObjects(Object, Object, CompareStrategy)} を参照のこと.
* @param collector Collector<?>
* @param compareStrategies CompareStrategy<?>[]
* @param keys String[]
* @return コントロールブレイクキーリスト
* @see #equalsObjects(Object, Object, CompareStrategy)
*/
public static Map<String, Object> getPreBreakKey(Collector<?> collector,
CompareStrategy<?>[] compareStrategies, String[] keys) {
if (collector != null) {
Object current = collector.getCurrent();
Object other = collector.getPrevious();
return getBreakKeyInternal(current, other,
(CompareStrategy<?>[]) compareStrategies, keys);
}
return new LinkedHashMap<String, Object>();
}
/**
* 後処理コントロールブレイクキー取得.<br>
* @param collector Collector<?>
* @param keys String...
* @return コントロールブレイクキーリスト
*/
public static Map<String, Object> getBreakKey(Collector<?> collector,
String... keys) {
return getBreakKey(collector, null, keys);
}
/**
* 後処理コントロールブレイクキー取得.<br>
* keysの数とcompareStrategiesの数と、比較時に利用されるCompareStrategyの関係は以下のようになる.<br>
* <ul>
* <li>keys : compareStrategies = N : N (またはN以上)の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[i]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : 1の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[0]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : M (N > M)の場合
* <ul>
* <li>keys[i] (i < M)のCompareStrategyはcompareStrategies[i]</li>
* <li>keys[i] (i >= M)のCompareStrategyはnull</li>
* </ul>
* </li>
* </ul>
* 比較仕様は、{@link #equalsObjects(Object, Object, CompareStrategy)} を参照のこと.
* @param collector Collector<?>
* @param compareStrategies CompareStrategy<?>[]
* @param keys String[]
* @return コントロールブレイクキーリスト
* @see #equalsObjects(Object, Object, CompareStrategy)
*/
public static Map<String, Object> getBreakKey(Collector<?> collector,
CompareStrategy<?>[] compareStrategies, String[] keys) {
if (collector != null) {
Object current = collector.getCurrent();
Object other = collector.getNext();
return getBreakKeyInternal(current, other,
(CompareStrategy<?>[]) compareStrategies, keys);
}
return new LinkedHashMap<String, Object>();
}
/**
* コントロールブレイクキー取得.<br>
* このメソッドは、ver.1.1.x以前との互換性を保つために残している.<br>
* 新規に作成するコードの場合は、<br>
* {@link #getBreakKey(Collector, CompareStrategy[], String[])}、<br>
* {@link #getPreBreakKey(Collector, CompareStrategy[], String[])}、<br>
* {@link #getBreakKeyInternal(Object, Object, CompareStrategy[], String...)}<br>
* を使用すること.<br>
* @param current Object 比較元オブジェクト
* @param other Object 比較先オブジェクト
* @param comparators Comparator<?>[]
* @param keys String...
* @return コントロールブレイクキーリスト
* @see #getBreakKey(Collector, CompareStrategy[], String[])
* @see #getPreBreakKey(Collector, CompareStrategy[], String[])
* @see #getBreakKeyInternal(Object, Object, CompareStrategy[], String...)
*/
protected static Map<String, Object> getBreakKeyInternal(Object current,
Object other, Comparator<?>[] comparators, String... keys) {
// comparator -> compareStrategyへの詰め替えを行う
if (comparators != null) {
CompareStrategy<?>[] compareStrategies = new CompareStrategy[comparators.length];
for (int i = 0; i < comparators.length; i++) {
compareStrategies[i] = new ComparatorCompareStrategy(
comparators[i]);
}
return getBreakKeyInternal(current, other, compareStrategies, keys);
} else {
return getBreakKeyInternal(current, other,
(CompareStrategy[]) null, keys);
}
}
/**
* コントロールブレイクキー取得.<br>
* keysの数とcompareStrategiesの数と、比較時に利用されるCompareStrategyの関係は以下のようになる.<br>
* <ul>
* <li>keys : compareStrategies = N : N (またはN以上)の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[i]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : 1の場合
* <ul>
* <li>keys[i]のCompareStrategyはcompareStrategies[0]</li>
* </ul>
* </li>
* <li>keys : compareStrategies = N : M (N > M)の場合
* <ul>
* <li>keys[i] (i < M)のCompareStrategyはcompareStrategies[i]</li>
* <li>keys[i] (i >= M)のCompareStrategyはnull</li>
* </ul>
* </li>
* </ul>
* 比較には {@link #equalsObjects(Object, Object, CompareStrategy)} メソッドを使用する.
* @param current Object 比較元オブジェクト
* @param other Object 比較先オブジェクト
* @param compareStrategies CompareStrategy<?>[]
* @param keys String...
* @return コントロールブレイクキーリスト
* @see #equalsObjects(Object, Object, CompareStrategy)
*/
protected static Map<String, Object> getBreakKeyInternal(Object current,
Object other, CompareStrategy<?>[] compareStrategies,
String... keys) {
boolean inBreak = false;
Map<String, Object> result = new LinkedHashMap<String, Object>();
// keyリストが空もしくはnullの場合はfalse
if (keys == null || keys.length == 0) {
// コントロールブレイクなし
return result;
}
for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
String key = keys[keyIndex];
CompareStrategy<?> compareStrategy = null;
Object currentValue = null;
Object otherValue = null;
if (compareStrategies != null) {
if (compareStrategies.length == 1) {
compareStrategy = compareStrategies[0];
} else if (keyIndex < compareStrategies.length) {
compareStrategy = compareStrategies[keyIndex];
}
}
if (key != null && key.length() != 0) {
// 値を取得する
if (current != null) {
try {
currentValue = PropertyUtils.getProperty(current, key);
} catch (Exception e) {
logOutputPropNotFound(e, current, key);
// ログを出力して次の項目をチェック
continue;
}
}
// 値を取得する
if (other != null) {
try {
otherValue = PropertyUtils.getProperty(other, key);
} catch (Exception e) {
logOutputPropNotFound(e, other, key);
// ログを出力して次の項目をチェック
continue;
}
}
if (!inBreak) {
// 片方がnullで、もう片方がnot nullの場合はtrue
if ((current != null && other == null)
|| (current == null && other != null)) {
// コントロールブレイク発生
inBreak = true;
}
// 比較
if (!equalsObjects(currentValue, otherValue, compareStrategy)) {
// コントロールブレイク発生
inBreak = true;
}
}
}
if (inBreak) {
result.put(key, currentValue);
}
}
return result;
}
/**
* あるオブジェクトと別のオブジェクトが等しいかどうか比較する.<br>
* <code>equalsObjects(value1, value2, null);</code> を実行するのと等価である.<br>
* このメソッドは、ver.1.1.x以前とのコンパイル互換性を保つために残している.<br>
*
* 以下のクラスを<b>除く</b>、Comparable実装クラスのインスタンス同士の比較は、
* ver.1.1.x以前とは比較結果が異なる可能性がある.<br>
* <ul>
* <li>ver.1.1.x以前ではequalsメソッドで比較され、かつ、
* Comparableの実装とequalsの実装に一貫性があるクラス.
* <ul>
* <li>java.math.BigInteger</li>
* <li>java.lang.Byte</li>
* <li>java.lang.Double</li>
* <li>java.lang.Float</li>
* <li>java.lang.Integer</li>
* <li>java.lang.Long</li>
* <li>java.lang.Short</li>
* <li>java.lang.Boolean</li>
* <li>java.lang.Character</li>
* <li>java.lang.String</li>
* <li>java.util.Date(java.sql.Date等のサブクラスを含まない)</li>
* </ul>
* </li>
* </ul>
*
* このメソッドの代わりに、<br>
* {@link #equalsObjects(Object, Object, CompareStrategy)}<br>
* を使用すること.<br>
* @param value1 Object 比較元オブジェクト
* @param value2 Object 比較先オブジェクト
* @return 等しい場合:true / そうでない場合:false
* @see #equalsObjects(Object, Object, CompareStrategy)
* @deprecated このメソッドの代わりに、{@link #equalsObjects(Object, Object, CompareStrategy)}を使用すること.
*/
@Deprecated
protected static boolean equalsObjects(Object value1, Object value2) {
return equalsObjects(value1, value2, null);
}
/**
* ログ出力(プロパティが見つからなかった場合).<br>
* @param e Exception
* @param obj Object
* @param key String
*/
protected static void logOutputPropNotFound(Exception e, Object obj,
String key) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn(LogId.WAL041002, key, obj == null ? null : obj
.getClass().getSimpleName(),
e == null ? null : e.getMessage());
}
}
/**
* あるオブジェクトと別のオブジェクトが等しいかどうか比較する.<br>
* <ul>
* <li>どちらもnullでない場合、以下のように比較する.
* <ul>
* <li>compareStrategyがnullでない場合、compareStrategyで比較</li>
* <li>compareStrategyがnullである場合、比較元オブジェクトの型に応じて、以下のように比較
* <ul>
* <li>比較元オブジェクトがComparable実装クラスのインスタンスの場合、Comparable#compareToで比較
* <li>比較元オブジェクトがClassまたはそのスーパークラスのインスタンスの場合、Object#equalsで比較
* <li>上記2つ以外の場合、org.apache.commons.lang.builder.EqualsBuilder#reflectionEqualsで比較
* </ul>
* </li>
* </ul>
* </li>
* <li>どちらもnullの場合、等しいとみなす.</li>
* <li>どちか一方のみがnullの場合、等くないとみなす.</li>
* </ul>
* @param value1 Object 比較元オブジェクト
* @param value2 Object 比較先オブジェクト
* @param compareStrategy CompareStrategy
* @return 等しい場合:true / そうでない場合:false
*/
@SuppressWarnings({"rawtypes", "unchecked"})
protected static boolean equalsObjects(Object value1,
Object value2, CompareStrategy compareStrategy) {
if (value1 != null && value2 != null) {
// value1,value2のどちらにも値が入っている場合
if (compareStrategy != null) {
return compareStrategy.equalsObjects(value1, value2);
} else {
Class<? extends Object> clazz = value1.getClass();
if (value1 instanceof Comparable) {
// value1がComparableを実装している場合はcompareToで比較する
return (((Comparable) value1).compareTo(value2) == 0);
} else if (clazz.isAssignableFrom(Class.class)) {
// value1がClassまたはそのスーパークラスのインスタンスの場合Object#equalsで比較する
return value1.equals(value2);
} else {
// それ以外の場合はreflectionEqualsで比較する
return EqualsBuilder.reflectionEquals(value1, value2);
}
}
} else if (value1 == null && value2 == null) {
// value1,value2のどちらにも値が入っていない場合
return true;
}
// value1,value2のどちらか一方に値が入っている場合
return false;
}
}