AnnotationMappingInfo.java

package com.gh.mygreen.xlsmapper.xml.bind;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.xml.bind.JAXB;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import com.gh.mygreen.xlsmapper.util.ArgUtils;


/**
 * XMLで定義したアノテーションの設定情報。
 * 
 * XMLの使用:
 * <pre class="highlight"><code class="xml">
 * {@literal <!-- ルート要素--> }
 * {@literal <annotations>}
 *     ・・・
 * {@literal </annotations>}
 * </code></pre>
 * 
 * @version 2.0
 * @since 0.5
 * @author T.TSUCHIE
 * 
 */
@XmlRootElement(name="annotations")
public class AnnotationMappingInfo implements Serializable {
    
    /** serialVersionUID */
    private static final long serialVersionUID = 1L;
    
    private List<ClassInfo> classInfos = new ArrayList<>();
    
    /**
     * ビルダクラスのインスタンスを取得する。
     * @since 1.1
     * @return
     */
    public static Builder builder() {
        return new Builder();
    }
    
    public AnnotationMappingInfo() {
        
    }
    
    private AnnotationMappingInfo(final Builder builder) {
        setClassInfos(builder.classInfos);
    }
    
    /**
     * クラス情報を追加する。
     * <p>ただし、既に同じクラス名が存在する場合は、それと入れ替えされます。</p>
     * @param classInfo FQCN(完全限定クラス名)を指定します。
     * @throws IllegalArgumentException classInfo is null.
     */
    public void addClassInfo(final ClassInfo classInfo) {
        ArgUtils.notNull(classInfo, "classInfo");
        
        removeClassInfo(classInfo.getClassName());
        this.classInfos.add(classInfo);
    }
    
    /**
     * 複数のクラス上方を追加する。
     * @since 2.0
     * @param classInfos 複数のクラス情報
     */
    public void addClassInfos(Collection<ClassInfo> classInfos) {
        classInfos.forEach(info -> addClassInfo(info));
    }
    
    /**
     * クラス名を指定してクラス情報を取得する。
     * @param className FQCN(完全限定クラス名)を指定します。
     * @return 存在しないクラス名の場合、nullを返します。
     */
    public ClassInfo getClassInfo(final String className) {
        for(ClassInfo item : classInfos) {
            if(item.getClassName().equals(className)) {
                return item;
            }
        }
        return null;
    }
    
    /**
     * 指定したクラスが含まれるかどうか。
     * @since 1.1
     * @param className FQCN(完全限定クラス名)を指定します。
     * @return true:指定したクラス名を含む場合。
     */
    public boolean containsClassInfo(final String className) {
        return getClassInfo(className) != null;
    }
    
    /**
     * 指定したクラス情報を削除します。
     * @since 1.4.1
     * @param className FQCN(完全限定クラス名)を指定します。
     * @return true:指定したクラス名を含み、それが削除できた場合。
     */
    public boolean removeClassInfo(final String className) {
        
        final ClassInfo existInfo = getClassInfo(className);
        if(existInfo != null) {
            this.classInfos.remove(existInfo);
            return true;
        }
        
        return false;
        
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("XmlInfo:");
        
        for(ClassInfo clazz : classInfos) {
            sb.append("\n  ").append(clazz.toString());
        }
        
        return sb.toString();
    }
    
    /**
     * JAXB用のクラス情報を設定するメソッド。
     * <p>XMLの読み込み時に呼ばれます。
     *  <br>ただし、Java8からはこのメソッドは呼ばれず、{@link #getClassInfos()} で取得したインスタンスに対して要素が追加されます。
     * </p>
     * <p>既存の情報はクリアされます。</p>
     * @since 1.1
     * @param classInfos クラス情報
     */
    public void setClassInfos(final List<ClassInfo> classInfos) {
        if(classInfos == this.classInfos) {
            // Java7の場合、getterで取得したインスタンスをそのまま設定するため、スキップする。
            return;
        }
        
        this.classInfos.clear();
        for(ClassInfo item : classInfos) {
            addClassInfo(item);
        }
        
    }
    
    /**
     * JAXB用のクラス情報を取得するメソッド。
     * <p>XMLの書き込み時に呼ばれます。
     *  <br>Java8から読み込み時に呼ばれるようになり、取得したインスタンスに対して、読み込んだ要素が呼ばれます。
     * </p>
     * @since 1.1
     * @return
     */
    @XmlElement(name="class")
    public List<ClassInfo> getClassInfos() {
        return classInfos;
    }
    
    /**
     * XML(テキスト)として返す。
     * <p>JAXB標準の設定でXMLを作成します。</p>
     * @since 1.1
     * @return XML情報。
     */
    public String toXml() {
        
        StringWriter writer = new StringWriter();
        JAXB.marshal(this, writer);
        writer.flush();
        
        return writer.toString();
        
    }
    
    /**
     * {@link InputStream}として返す。
     * <p>XlsLoaderなどに直接渡せる形式。</p>
     * <p>{@link #toXml()}にてXMLに変換後に、InputStreamにサイド変換する。</p>
     * @since 1.1
     * @return XML情報。
     */
    public InputStream toInputStream() {
        
        String text = toXml();
        return new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
        
    }
    
    /**
     * {@link AnnotationMappingInfo}を組み立てるためのクラス。
     *
     */
    public static final class Builder {
        
        private List<ClassInfo> classInfos;
        
        private Builder() {
            this.classInfos = new ArrayList<>();
        }
        
        /**
         * 組み立てた{@link AnnotationMappingInfo}のインスタンスを返す。
         * @return {@link AnnotationMappingInfo}オブジェクト。
         */
        public AnnotationMappingInfo buildXml() {
            return new AnnotationMappingInfo(this);
        }
        
        /**
         * クラス情報を追加する。
         * @param classInfo クラス情報
         * @return
         * @throws IllegalArgumentException classInfo is null
         */
        public Builder classInfo(final ClassInfo classInfo) {
            ArgUtils.notNull(classInfo, "classInfo");
            this.classInfos.add(classInfo);
            return this;
        }
        
    }
}