All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mygreen.supercsv.builder.AnnotationExpander Maven / Gradle / Ivy

Go to download

CSVのJavaライブラリであるSuperCSVに、アノテーション機能を追加したライブラリです。

There is a newer version: 2.3
Show newest version
package com.github.mygreen.supercsv.builder;

import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.supercsv.exception.SuperCsvReflectionException;

import com.github.mygreen.supercsv.annotation.CsvComposition;
import com.github.mygreen.supercsv.annotation.CsvOverridesAttribute;
import com.github.mygreen.supercsv.annotation.constraint.CsvConstraint;
import com.github.mygreen.supercsv.annotation.constraint.CsvRequire;
import com.github.mygreen.supercsv.annotation.conversion.CsvConversion;
import com.github.mygreen.supercsv.exception.SuperCsvInvalidAnnotationException;
import com.github.mygreen.supercsv.localization.MessageBuilder;
import com.github.mygreen.supercsv.util.Utils;

/**
 * 繰り返しのアノテーション、合成のアノテーションを考慮して、アノテーションを展開します。
 * 

並び順は、コンストラクタで指定されたものに並び変えられる。

* *

繰り返しのアノテーション{@link Repeatable}が付与されたアノテーションの場合、 * 取得する際には複数のアノテーションがまとめたアノテーションとして別に取得されるため分解する。

* *

合成のアノテーション{@link CsvComposition}が付与されたアノテーションの場合、 * 付与されているアノテーションに分解する。 * その際に、定義されている属性を元に、付与されているアノテーションの属性を上書きする。

* * @since 2.0 * @author T.TSUCHIE * */ public class AnnotationExpander { private final Comparator comparator; /** * アノテーションの並び順を指定するコンストラクタ。 * * @param annotationComparator アノテーションの並び順を指定するためのComparator。 * @throws NullPointerException {@literal annotationComparator == null.} */ public AnnotationExpander(final Comparator annotationComparator) { Objects.requireNonNull(annotationComparator); this.comparator = new Comparator() { @Override public int compare(final ExpandedAnnotation o1, final ExpandedAnnotation o2) { return annotationComparator.compare(o1.getOriginal(), o2.getOriginal()); } }; } /** * 複数のアノテーションを展開する。 * @param targetAnnos 展開対象のアノテーション * @return 展開されたアノテーション * @throws NullPointerException {@literal targetAnnos == null.} */ public List expand(final Annotation[] targetAnnos) { Objects.requireNonNull(targetAnnos); final List expanedList = new ArrayList<>(); for(Annotation targetAnno : targetAnnos) { expanedList.addAll(expand(targetAnno)); } Collections.sort(expanedList, comparator); return expanedList; } /** * アノテーションを展開する。 * @param targetAnno 展開対象のアノテーション * @return 展開されたアノテーション * @throws NullPointerException {@literal targetAnno == null.} */ public List expand(final Annotation targetAnno) { Objects.requireNonNull(targetAnno); final List expandedList = new ArrayList<>(); if(isRepeated(targetAnno)) { // 繰り返しのアノテーションの場合、要素を抽出する。 try { final Method method = targetAnno.getClass().getMethod("value"); final Annotation[] annos = (Annotation[]) method.invoke(targetAnno); int index = 0; for(Annotation anno : annos) { final List repeatedAnnos = expand(anno); for(ExpandedAnnotation repeatedAnno : repeatedAnnos) { repeatedAnno.setIndex(index); } expandedList.addAll(repeatedAnnos); index++; } } catch (Exception e) { throw new RuntimeException("fail get repeated value attribute.", e); } } else if(isComposed(targetAnno)) { final ExpandedAnnotation composedAnno = new ExpandedAnnotation(targetAnno, true); // 合成のアノテーションの場合、メタアノテーションを子供としてさらに抽出する。 final List childAnnos = Arrays.asList(targetAnno.annotationType().getAnnotations()); for(Annotation anno : childAnnos) { final List nestedAnnos = expand(anno).stream() .map(nestedAnno -> overrideAttribute(targetAnno, nestedAnno)) .collect(Collectors.toList()); composedAnno.addChilds(nestedAnnos); } Collections.sort(composedAnno.getChilds(), comparator); expandedList.add(composedAnno); } else { // 通常のアノテーションの場合 expandedList.add(new ExpandedAnnotation(targetAnno, false)); } Collections.sort(expandedList, comparator); return expandedList; } /** * 繰り返されたアノテーションかどうか判定する。 *

属性「value」に、繰り返しのアノテーション{@link Repeatable}が付与されている * アノテーションの配列を保持しているかどうかで判定する。

* @param targetAnno * @return */ private boolean isRepeated(final Annotation targetAnno) { try { final Method method = targetAnno.getClass().getMethod("value"); // 値のクラスタイプがアノテーションの配列かどうかのチェック final Class returnType = method.getReturnType(); if(!(returnType.isArray() && Annotation.class.isAssignableFrom(returnType.getComponentType()))) { return false; } final Annotation[] annos = (Annotation[]) method.invoke(targetAnno); if(annos.length == 0) { return false; } // @Repetableアノテーションが付与されているかどうか if(annos[0].annotationType().getAnnotation(Repeatable.class) != null) { return true; } } catch (Exception e) { } return false; } /** * 合成されたアノテーションかどうか判定する。 *

メタアノテーション{@link CsvComposition}が付与されているかどうかで判定する。

* @param targetAnno * @return */ private boolean isComposed(final Annotation targetAnno) { return targetAnno.annotationType().getAnnotation(CsvComposition.class) != null; } /** * 合成したアノテーションの属性を、構成されるアノテーションに反映する。 * @param compositionAnno * @param nestedAnno * @return */ private ExpandedAnnotation overrideAttribute(final Annotation compositionAnno, final ExpandedAnnotation nestedAnno) { final Annotation originalAnno = nestedAnno.getOriginal(); if(!isOverridableAnnotation(originalAnno)) { return nestedAnno; } // 上書きするアノテーションの属性の組み立て final Map overrideAttrs = buildOverrideAttribute(compositionAnno, nestedAnno); if(overrideAttrs.isEmpty()) { return nestedAnno; } // 既存のアノテーションの属性の作成 final Class annotationClass = originalAnno.annotationType(); final Map defaultValues = new HashMap<>(); for(Method method : annotationClass.getMethods()) { try { method.setAccessible(true); if(method.getParameterCount() == 0) { final Object value = method.invoke(originalAnno); defaultValues.put(method.getName(), value); } else { final Object value = method.getDefaultValue(); if(value != null) { defaultValues.put(method.getName(), value); } } } catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(String.format("fail get annotation attribute %s#%s.", annotationClass.getName(), method.getName()), e); } } // アノテーションのインスタンスの組み立てなおし final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); final Object annoObj = Proxy.newProxyInstance(classLoader, new Class[]{annotationClass}, new InvocationHandler() { @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { final String name = method.getName(); if (name.equals("annotationType")) { return annotationClass; } else if(overrideAttrs.containsKey(name)){ return overrideAttrs.get(name); } else { return defaultValues.get(name); } } }); // 値をコピーする final ExpandedAnnotation propagatedAnno = new ExpandedAnnotation((Annotation)annoObj, nestedAnno.isComposed()); propagatedAnno.setIndex(nestedAnno.getIndex()); propagatedAnno.addChilds(nestedAnno.getChilds()); return propagatedAnno; } /** * アノテーションの属性を上書き可能かか判定する。 *

ただし、実際には合成のアノテーションに属性がなければ上書きはされないので、あくまで上書き可能かの判定しか行わない。

*

条件は以下の通り。

*
    *
  • メタアノテーション{@link CsvConstraint}、{@link CsvConversion}が付与されているアノテーションである。
  • *
  • アノテーション{@link CsvRequire}である。
  • *
  • フォーマット用のアノテーションである。パッケージ名から判定する。
  • *
* @param targetAnno 判定対象のアノテーション * @return trueの場合、上書き対象。 */ private boolean isOverridableAnnotation(final Annotation targetAnno) { final Class annoType = targetAnno.annotationType(); if(annoType.getAnnotation(CsvConstraint.class) != null) { return true; } if(annoType.getAnnotation(CsvConversion.class) != null) { return true; } if(annoType.getTypeName().startsWith("com.github.mygreen.supercsv.annotation.format")) { return true; } return false; } /** * 上書きする属性の組み立て * @param compositionAnno 合成のアノテーション * @param targetAnno 上書き対象のアノテーション * @return */ @SuppressWarnings("rawtypes") private Map buildOverrideAttribute(final Annotation compositionAnno, final ExpandedAnnotation targetAnno) { final Annotation originalAnno = targetAnno.getOriginal(); final Map overrideAttrs = new HashMap<>(); // @CsvOvrerideAttributeが付与されたメソッド名 final Set overrideMethodNames = new HashSet<>(); // @CsvOverridesAttributeが付与された属性の組み立て for(Method compositionMethod : compositionAnno.annotationType().getMethods()) { final List annoList = new ArrayList<>(); final CsvOverridesAttribute overrideAttrAnno = compositionMethod.getAnnotation(CsvOverridesAttribute.class); if(overrideAttrAnno != null) { annoList.add(overrideAttrAnno); } // 繰り返しのアノテーションの場合 final CsvOverridesAttribute.List overrideAttrAnnoList = compositionMethod.getAnnotation(CsvOverridesAttribute.List.class); if(overrideAttrAnnoList != null) { annoList.addAll(Arrays.asList(overrideAttrAnnoList.value())); } if(annoList.isEmpty()) { // @CsvOverridesAttributeが付与されていない場合 continue; } overrideMethodNames.add(compositionMethod.getName()); for(CsvOverridesAttribute anno : annoList) { // アノテーションのクラスの判定 if(!anno.annotation().equals(originalAnno.annotationType())) { continue; } // インデックスの判定 if(anno.index() >= 0 && anno.index() != targetAnno.getIndex()) { continue; } final String attrName = anno.name().isEmpty() ? compositionMethod.getName() : anno.name(); if(!Utils.hasAnnotationAttribute(originalAnno, attrName, compositionMethod.getReturnType())) { // 上書き対象の属性が見つからない場合 throw new SuperCsvInvalidAnnotationException(originalAnno, MessageBuilder.create("anno.CsvOverridesAnnotation.notFoundAttr") .varWithAnno("compositionAnno", compositionAnno.annotationType()) .varWithAnno("overrideAnno", originalAnno.annotationType()) .varWithClass("attrType", compositionMethod.getReturnType()) .var("attrName", attrName) .format()); } // 属性値の取得 try { Object attrValue = compositionMethod.invoke(compositionAnno); overrideAttrs.put(attrName, attrValue); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new SuperCsvReflectionException(MessageBuilder.create("anno.CsvOverridesAnnotation.failGetAttr") .varWithAnno("compositionAnno", compositionAnno.annotationType()) .var("attrName", attrName) .format(), e); } } } // message 属性の取得。 // 既に取得していたり、@CsvOverridesAttributeが付与されている場合はスキップする。 if(!overrideAttrs.containsKey("message") && !overrideMethodNames.contains("message") && Utils.hasAnnotationAttribute(originalAnno, "message", String.class)) { final Optional messageAttr = Utils.getAnnotationAttribute(compositionAnno, "message", String.class); if(messageAttr.isPresent() && Utils.isNotEmpty(messageAttr.get())) { overrideAttrs.put("message", messageAttr.get()); } } // groups 属性の取得。 // 既に取得していたり、@CsvOverridesAttributeが付与されている場合はスキップする。 if(!overrideAttrs.containsKey("groups") && !overrideMethodNames.contains("groups") && Utils.hasAnnotationAttribute(originalAnno, "groups", Class[].class)) { final Optional groupsAttr = Utils.getAnnotationAttribute(compositionAnno, "groups", Class[].class); if(groupsAttr.isPresent() && Utils.isNotEmpty(groupsAttr.get())) { overrideAttrs.put("groups", groupsAttr.get()); } } // cases 属性の取得。 // 既に取得していたり、@CsvOverridesAttributeが付与されている場合はスキップする。 if(!overrideAttrs.containsKey("cases") && !overrideMethodNames.contains("cases") && Utils.hasAnnotationAttribute(originalAnno, "cases", BuildCase[].class)) { final Optional casesAttr = Utils.getAnnotationAttribute(compositionAnno, "cases", BuildCase[].class); if(casesAttr.isPresent() && Utils.isNotEmpty(casesAttr.get())) { overrideAttrs.put("cases", casesAttr.get()); } } return overrideAttrs; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy