PropertyPathTokenizer.java

  1. package com.gh.mygreen.xlsmapper.util;

  2. import java.util.LinkedList;

  3. import com.gh.mygreen.xlsmapper.util.PropertyPath.Token;

  4. /**
  5.  * プロパティアクセス用の式をパースする。
  6.  * <p>{@link PropertyValueNavigator}で使用する。
  7.  * @since 1.0
  8.  * @author T.TSUCHIE
  9.  *
  10.  */
  11. public class PropertyPathTokenizer {
  12.    
  13.     /**
  14.      * エスケープ文字(バックスペース\\)
  15.      */
  16.     private static final String STR_ESCAPE_CHAR = "\\";
  17.    
  18.     /**
  19.      * プロパティのアクセス用の書式をトークンに分割する。
  20.      * @param path
  21.      * @return
  22.      * @throws IllegalArgumentException path is null or empty.
  23.      */
  24.     public PropertyPath parse(final String path) {
  25.        
  26.         ArgUtils.notEmpty(path, "path");
  27.        
  28.         final PropertyPath tokenStore = new PropertyPath(path);
  29.         splitToken(tokenStore, path);
  30.         return tokenStore;
  31.     }
  32.    
  33.     /**
  34.      * パスを最小限の項目に分割する。
  35.      *
  36.      * @param tokenStore 分割した結果
  37.      * @param path パス
  38.      *
  39.      */
  40.     private void splitToken(final PropertyPath tokenStore, final String path) {
  41.        
  42.         // 解析時の途中の文字を一時的に補完しておく。
  43.         final LinkedList<String> stack = new LinkedList<String>();
  44.        
  45.         final int length = path.length();
  46.         for(int i=0; i < length; i++) {
  47.             final char c = path.charAt(i);
  48.            
  49.             if(StackUtils.equalsTopElement(stack, STR_ESCAPE_CHAR)) {
  50.                 // スタックの一番上がエスケープの文字の場合、通常の文字として扱う。
  51.                 stack.push(String.valueOf(c));
  52.                
  53.             } else if (c == '.') {
  54.                
  55.                 if(StackUtils.equalsBottomElement(stack, "[")) {
  56.                     // キーの囲み文字の途中の場合、文字列としてスタックに積む。
  57.                     stack.push(String.valueOf(c));
  58.                    
  59.                 } else if(!stack.isEmpty()) {
  60.                     // 既に文字が入っている場合は、既存のものを取り出し分割する。
  61.                     tokenStore.add(new Token.Name(StackUtils.popupAndConcat(stack).trim()));
  62.                     tokenStore.add(new Token.Separator());
  63.                    
  64.                 } else {
  65.                     tokenStore.add(new Token.Separator());
  66.                 }
  67.                
  68.             } else if(c == '[') {
  69.                
  70.                 if(!stack.isEmpty()) {
  71.                     // 既に文字が入っている場合は、既存のものを取り出し分割する。
  72.                     tokenStore.add(new Token.Name(StackUtils.popupAndConcat(stack).trim()));
  73.                 }
  74.                
  75.                 stack.push(String.valueOf(c));
  76.                
  77.             } else if(c == ']') {
  78.                 if(StackUtils.equalsBottomElement(stack, "[")) {
  79.                     // 条件の終わりの場合
  80.                     tokenStore.add(new Token.Key(StackUtils.popupAndConcat(stack) + c));
  81.                    
  82.                 } else {
  83.                     stack.push(String.valueOf(c));
  84.                 }
  85.                
  86.             } else if(c == ' ') {
  87.                 // キーの囲み文字の中であれば、空白は無視しない。
  88.                 if(StackUtils.equalsBottomElement(stack, "[")) {
  89.                     stack.push(String.valueOf(c));
  90.                 }
  91.                
  92.             } else {
  93.                 stack.push(String.valueOf(c));
  94.             }
  95.            
  96.         }
  97.        
  98.         if(!stack.isEmpty()) {
  99.             tokenStore.add(new Token.Name(StackUtils.popupAndConcat(stack).trim()));
  100.         }
  101.        
  102.     }
  103.    
  104. }