Utils.java

package com.github.mygreen.supercsv.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;

import com.github.mygreen.supercsv.annotation.PatternFlag;
import com.github.mygreen.supercsv.builder.BuildCase;


/**
 * ユーティリティクラス。
 * 
 * @version 2.2
 * @author T.TSUCHIE
 *
 */
public class Utils {
    
    /**
     * <a href="http://www.joda.org/joda-time/" target="_blank">Joda-Time</a>のライブラリが利用可能かどうか。
     */
    public static final boolean ENABLED_LIB_JODA_TIME;
    static {
        boolean enabled;
        try {
            Class.forName("org.joda.time.LocalDateTime");
            enabled = true;
        } catch(ClassNotFoundException e) {
            enabled = false;
        }
        ENABLED_LIB_JODA_TIME = enabled;
    }
    
    /**
     * 文字列が空文字か判定する。
     * @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);
    }
    
    /**
     * 文字列形式のロケールをオブジェクトに変換する。
     * <p>アンダーバーで区切った'ja_JP'を分解して、Localeに渡す。
     * @since 1.2
     * @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]);
        }
        
    }
    
    /**
     * アノテーションの指定した属性値を取得する。
     * <p>アノテーションの修飾子はpublicである必要があります。</p>
     * @param anno アノテーションのインスタンス
     * @param attrName 属性名
     * @param attrType 属性のタイプ。
     * @return 属性を持たない場合、空を返す。
     */
    @SuppressWarnings("unchecked")
    public static <T> Optional<T> getAnnotationAttribute(final Annotation anno, final String attrName, final Class<T> attrType) {
        
        try {
            final Method method = anno.annotationType().getMethod(attrName);
            method.setAccessible(true);
            if(!attrType.equals(method.getReturnType())) {
                return Optional.empty();
            }
            
            final Object value = method.invoke(anno);
            return Optional.of((T)value);
            
        } catch (Exception e) {
            return Optional.empty();
        }
        
    }
    
    /**
     * アノテーションの指定した属性値を持つかどうか判定する。
     * <p>アノテーションの修飾子はpublicである必要があります。</p>
     * @param anno アノテーションのインスタンス
     * @param attrName 属性名
     * @param attrType 属性のタイプ。
     * @return 属性を持つ場合trueを返す。
     */
    public static <T>  boolean hasAnnotationAttribute(final Annotation anno, final String attrName, final Class<T> attrType) {
        
        return getAnnotationAttribute(anno, attrName, attrType).isPresent();
        
    }
    
    /**
     * アノテーションの属性「cases」を持つ場合、指定した種類を持つか判定する。
     * <p>属性「buildCase」を持たない場合、または、空の配列の場合は、必ずtrueを返します。</p>
     * 
     * @param anno 判定対象のアノテーション。
     * @param buildCase 組み立てる種類。
     * @return trueの場合、指定した種類を含みます。
     * @throws NullPointerException anno or buildCase is null.
     */
    public static boolean containsBuildCase(final Annotation anno, final BuildCase buildCase) {
        
        Objects.requireNonNull(anno);
        Objects.requireNonNull(buildCase);
        
        final Optional<BuildCase[]> attrCases = getAnnotationAttribute(anno, "cases", BuildCase[].class);
        if(attrCases.isPresent()) {
            final BuildCase[] casesValue = attrCases.get();
            if(casesValue.length == 0) {
                // 値が空の配列の場合
                return true;
            }
            
            for(BuildCase value : casesValue) {
                if(value == buildCase) {
                    return true;
                }
            }
            
            return false;
        }
        
        // 属性を持たない場合
        return true;
    }
    
    /**
     * <a href="http://www.joda.org/joda-time/" target="_blank">Joda-Time</a>のライブラリが利用可能かどうか。
     * @return {@literal true}利用可能。
     */
    public static boolean isEnabledJodaTime() {
        return ENABLED_LIB_JODA_TIME;
    }
    
    /**
     * プリミティブ型の初期値を取得する。
     * @param type プリミティブ型のクラス型。
     * @return 非プリミティブ型や該当するクラスがない場合はnullを返す。
     * @throws NullPointerException type is null.
     */
    public static Object getPrimitiveDefaultValue(final Class<?> type) {
        
        Objects.requireNonNull(type, "type should not be null.");
        
        if(!type.isPrimitive()) {
            return null;
            
        } else if(boolean.class.isAssignableFrom(type)) {
            return false;
            
        } else if(char.class.isAssignableFrom(type)) {
            return '\u0000';
            
        } else if(byte.class.isAssignableFrom(type)) {
            return (byte)0;
            
        } else if(short.class.isAssignableFrom(type)) {
            return (short)0;
            
        } else if(int.class.isAssignableFrom(type)) {
            return 0;
            
        } else if(long.class.isAssignableFrom(type)) {
            return 0l;
            
        } else if(float.class.isAssignableFrom(type)) {
            return 0.0f;
            
        } else if(double.class.isAssignableFrom(type)) {
            return 0.0d;
        }
        
        return null;
        
    }
    
    /**
     * 文字列配列の結合
     * @param array1
     * @param array2
     * @return 結合した配列。引数のどちらからnullの場合は、cloneした配列を返します。
     */
    public static String[] concat(final String[] array1, final String[] array2) {
        
        if(array1 == null || array1.length == 0) {
            return clone(array2);
            
        } else if(array2 == null || array2.length == 0) {
            return clone(array1);
        }
        
        final String[] joinedArray = new String[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
        
    }
    
    /**
     * 文字列の配列をクローンします。
     * @since 2.2
     * @param array クローン対象の配列
     * @return クローンした配列。引数がnullの場合は、nullを返します。
     */
    public static String[] clone(final String[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }
    
    /**
     * コレクションを配列に変換する。
     * @param collection 変換対象のコレクション。
     * @return 変換した配列。
     * @throws NullPointerException collection is null.
     */
    public static int[] toArray(final Collection<Integer> collection) {
        Objects.requireNonNull(collection);
        
        final int size = collection.size();
        final int[] array = new int[size];
        
        int i=0;
        for(Integer value : collection) {
            array[i] = value;
            i++;
        }
        
        return array;
    }
    
    /**
     * 正規表現のフラグを組み立てる。
     * @param flags 正規表現の列挙型のフラグ
     * @return
     */
    public static int buildRegexFlags(final PatternFlag[] flags) {
        
        int intFlag = 0;
        for(PatternFlag flag : flags) {
            intFlag = intFlag | flag.getValue();
        }
        
        return intFlag;
        
    }
    
    /**
     * 先頭の文字を小文字にする。
     * @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();
    }
    
}