SheetFinder.java

  1. package com.gh.mygreen.xlsmapper;

  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.Method;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import java.util.Optional;
  7. import java.util.regex.Pattern;

  8. import org.apache.poi.ss.usermodel.Sheet;
  9. import org.apache.poi.ss.usermodel.Workbook;

  10. import com.gh.mygreen.xlsmapper.annotation.XlsSheetName;
  11. import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessor;
  12. import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessorFactory;
  13. import com.gh.mygreen.xlsmapper.localization.MessageBuilder;
  14. import com.gh.mygreen.xlsmapper.annotation.XlsSheet;
  15. import com.gh.mygreen.xlsmapper.util.ClassUtils;
  16. import com.gh.mygreen.xlsmapper.util.Utils;
  17. import com.gh.mygreen.xlsmapper.xml.AnnotationReadException;
  18. import com.gh.mygreen.xlsmapper.xml.AnnotationReader;

  19. /**
  20.  * 読み込み時/書き込み処理時のシートを取得するクラス。
  21.  * <p>アノテーション{@link XlsSheet}の設定値に従いシートを取得する。
  22.  *
  23.  * @since 1.1
  24.  * @author T.TSUCHIE
  25.  *
  26.  */
  27. public class SheetFinder {
  28.    
  29.     /**
  30.      * 読み込み時のシートを取得する。
  31.      *
  32.      * @param workbook Excelのワークブック。
  33.      * @param sheetAnno JavaBeanのクラスに付与されているアノテーション{@link XlsSheet}。
  34.      * @param annoReader
  35.      * @param beanClass JavaBeanのクラス。
  36.      * @return Excelのシート情報。複数ヒットする場合は、該当するものを全て返す。
  37.      * @throws SheetNotFoundException 該当のシートが見つからない場合にスローする。
  38.      * @throws AnnotationInvalidException アノテーションの使用方法が不正な場合
  39.      * @throws AnnotationReadException アノテーションをXMLで指定する方法が不正な場合。
  40.      */
  41.     public Sheet[] findForLoading(final Workbook workbook, final XlsSheet sheetAnno,
  42.             final AnnotationReader annoReader, final Class<?> beanClass)
  43.                     throws SheetNotFoundException, AnnotationInvalidException, AnnotationReadException {
  44.        
  45.         if(sheetAnno.name().length() > 0) {
  46.             // シート名から取得する。
  47.             final Sheet xlsSheet = workbook.getSheet(sheetAnno.name());
  48.             if(xlsSheet == null) {
  49.                 throw new SheetNotFoundException(sheetAnno.name());
  50.             }
  51.             return new Sheet[]{ xlsSheet };
  52.            
  53.         } else if(sheetAnno.number() >= 0) {
  54.             // シート番号から取得する
  55.             if(sheetAnno.number() >= workbook.getNumberOfSheets()) {
  56.                 throw new SheetNotFoundException(sheetAnno.number(), workbook.getNumberOfSheets());
  57.             }
  58.            
  59.             return new Sheet[]{ workbook.getSheetAt(sheetAnno.number()) };
  60.            
  61.         } else if(sheetAnno.regex().length() > 0) {
  62.             // シート名(正規表現)をもとにして、取得する。
  63.             final Pattern pattern = Pattern.compile(sheetAnno.regex());
  64.             final List<Sheet> matches = new ArrayList<>();
  65.             for(int i=0; i < workbook.getNumberOfSheets(); i++) {
  66.                 final Sheet xlsSheet = workbook.getSheetAt(i);
  67.                 if(pattern.matcher(xlsSheet.getSheetName()).matches()) {
  68.                     matches.add(xlsSheet);
  69.                 }
  70.             }
  71.            
  72.             if(matches.isEmpty()) {
  73.                 throw new SheetNotFoundException(sheetAnno.regex());
  74.             }
  75.            
  76.             return matches.toArray(new Sheet[matches.size()]);
  77.         }
  78.        
  79.         throw new AnnotationInvalidException(sheetAnno, MessageBuilder.create("anno.attr.required.any")
  80.                 .varWithClass("property", beanClass)
  81.                 .varWithAnno("anno", XlsSheet.class)
  82.                 .varWithArrays("attrNames", "name", "number", "regex")
  83.                 .format());
  84.     }
  85.    
  86.     /**
  87.      * 書き込み時のシートを取得する。
  88.      * @param workbook Excelのワークブック。
  89.      * @param sheetAnno JavaBeanのクラスに付与されているアノテーション{@link XlsSheet}。
  90.      * @param annoReader
  91.      * @param beanObj JavaBeanのオブジェクト。
  92.      * @return Excelのシート情報。複数ヒットする場合は、該当するものを全て返す。
  93.      * @throws SheetNotFoundException 該当のシートが見つからない場合にスローする。
  94.      * @throws AnnotationInvalidException アノテーションの使用方法が不正な場合
  95.      * @throws AnnotationReadException アノテーションをXMLで指定する方法が不正な場合。
  96.      */
  97.     public Sheet[] findForSaving(final Workbook workbook, final XlsSheet sheetAnno,
  98.             final AnnotationReader annoReader, final Object beanObj)
  99.                     throws SheetNotFoundException, AnnotationInvalidException, AnnotationReadException {
  100.        
  101.         if(sheetAnno.name().length() > 0) {
  102.             // シート名から取得する。
  103.             final Sheet xlsSheet = workbook.getSheet(sheetAnno.name());
  104.             if(xlsSheet == null) {
  105.                 throw new SheetNotFoundException(sheetAnno.name());
  106.             }
  107.             return new Sheet[]{ xlsSheet };
  108.            
  109.         } else if(sheetAnno.number() >= 0) {
  110.             // シート番号から取得する
  111.             if(sheetAnno.number() >= workbook.getNumberOfSheets()) {
  112.                 throw new SheetNotFoundException(sheetAnno.number(), workbook.getNumberOfSheets());
  113.             }
  114.            
  115.             return new Sheet[]{ workbook.getSheetAt(sheetAnno.number()) };
  116.            
  117.         } else if(sheetAnno.regex().length() > 0) {
  118.             // シート名(正規表現)をもとにして、取得する。
  119.             String sheetNameValue = null;
  120.             Optional<FieldAccessor> sheetNameField = getSheetNameField(beanObj, annoReader);
  121.             if(sheetNameField.isPresent()) {
  122.                 sheetNameValue = (String)sheetNameField.get().getValue(beanObj);
  123.             }
  124.            
  125.             final Pattern pattern = Pattern.compile(sheetAnno.regex());
  126.             final List<Sheet> matches = new ArrayList<>();
  127.             for(int i=0; i < workbook.getNumberOfSheets(); i++) {
  128.                 final Sheet xlsSheet = workbook.getSheetAt(i);
  129.                 if(pattern.matcher(xlsSheet.getSheetName()).matches()) {
  130.                    
  131.                     // オブジェクト中の@XslSheetNameで値が設定されている場合、Excelファイル中の一致するシートを元にする比較する
  132.                     if(Utils.isNotEmpty(sheetNameValue) && xlsSheet.getSheetName().equals(sheetNameValue)) {
  133.                         return new Sheet[]{ xlsSheet };
  134.                        
  135.                     }
  136.                    
  137.                     matches.add(xlsSheet);
  138.                 }
  139.             }
  140.            
  141.             if(sheetNameValue != null && !matches.isEmpty()) {
  142.                 // シート名が直接指定の場合
  143.                 throw new SheetNotFoundException(sheetNameValue);
  144.                
  145.             } else if(matches.isEmpty()) {
  146.                 throw new SheetNotFoundException(sheetAnno.regex());
  147.                
  148.             } else if(matches.size() == 1) {
  149.                 // 1つのシートに絞り込めた場合
  150.                 return new Sheet[]{ matches.get(0) };
  151.                
  152.             } else {
  153.                 // 複数のシートがヒットした場合
  154.                 List<String> names = new ArrayList<>();
  155.                 for(Sheet sheet : matches) {
  156.                     names.add(sheet.getSheetName());
  157.                 }
  158.                 throw new SheetNotFoundException(sheetAnno.regex(),
  159.                         MessageBuilder.create("sheet.regexMultipleHit")
  160.                             .var("regex", sheetAnno.regex())
  161.                             .var("names", names)
  162.                             .format());
  163.             }
  164.         }
  165.        
  166.         throw new AnnotationInvalidException(sheetAnno, MessageBuilder.create("anno.attr.required.any")
  167.                 .varWithClass("property", beanObj.getClass())
  168.                 .varWithAnno("anno", XlsSheet.class)
  169.                 .varWithArrays("attrNames", "name", "number", "regex")
  170.                 .format());
  171.        
  172.     }
  173.    
  174.     /**
  175.      * アノテーション「@XlsSheetName」が付与されているフィールド/メソッドを取得する。
  176.      * @param beanObj
  177.      * @param config
  178.      * @param annoReader
  179.      * @return 見つからない場合、空を返す。
  180.      * @throws AnnotationReadException
  181.      */
  182.     private Optional<FieldAccessor> getSheetNameField(final Object beanObj, final AnnotationReader annoReader) throws AnnotationReadException {
  183.        
  184.         FieldAccessorFactory adpterFactory = new FieldAccessorFactory(annoReader);
  185.        
  186.         Class<?> clazz = beanObj.getClass();
  187.         for(Method method : clazz.getMethods()) {
  188.             method.setAccessible(true);
  189.             if(!ClassUtils.isGetterMethod(method)) {
  190.                 continue;
  191.             }
  192.            
  193.             if(!annoReader.hasAnnotation(method, XlsSheetName.class)) {
  194.                 continue;
  195.             }
  196.            
  197.             return Optional.of(adpterFactory.create(method));
  198.         }
  199.        
  200.         for(Field field : clazz.getDeclaredFields()) {
  201.             field.setAccessible(true);
  202.            
  203.             if(!annoReader.hasAnnotation(field, XlsSheetName.class)) {
  204.                 continue;
  205.             }
  206.            
  207.             return Optional.of(adpterFactory.create(field));
  208.            
  209.         }
  210.        
  211.         // not found
  212.         return Optional.empty();
  213.     }
  214.    
  215. }