Coverage Report - jp.co.y_net.amm.dao.TextDao
 
Classes in this File Line Coverage Branch Coverage Complexity
TextDao
0%
0/187
0%
0/56
0
TextDao$1
0%
0/4
0%
0/4
0
TextDao$TextDBAccSampleDto
0%
0/8
N/A
0
 
 1  0
 package jp.co.y_net.amm.dao;
 2  
 
 3  
 
 4  
 import java.io.BufferedReader;
 5  
 import java.io.BufferedWriter;
 6  
 import java.io.File;
 7  
 import java.io.FileFilter;
 8  
 import java.io.FileInputStream;
 9  
 import java.io.FileOutputStream;
 10  
 import java.io.IOException;
 11  
 import java.io.InputStreamReader;
 12  
 import java.io.OutputStream;
 13  
 import java.io.OutputStreamWriter;
 14  
 import java.io.Writer;
 15  
 import java.lang.reflect.Field;
 16  
 import java.nio.charset.Charset;
 17  
 import java.util.ArrayList;
 18  
 import java.util.Arrays;
 19  
 import java.util.Collections;
 20  
 import java.util.List;
 21  
 
 22  
 import javax.servlet.ServletContext;
 23  
 
 24  
 import org.apache.wicket.protocol.http.WebApplication;
 25  
 
 26  
 import com.fasterxml.jackson.core.JsonProcessingException;
 27  
 import com.fasterxml.jackson.databind.ObjectMapper;
 28  
 
 29  
 /**
 30  
 * テキストファイルをストレージとするData Access Object
 31  
 *
 32  
 * オブジェクトはJSON形式に変換する。
 33  
 *
 34  
 * 必須ライブラリは、親プロジェクトの pom.xml に記述
 35  
 * 
 36  
         <!-- Jackson -->
 37  
         <dependency>
 38  
             <groupId>com.fasterxml.jackson.core</groupId>
 39  
             <artifactId>jackson-core</artifactId>
 40  
             <version>2.5.0</version>
 41  
         </dependency>
 42  
         <dependency>
 43  
             <groupId>com.fasterxml.jackson.core</groupId>
 44  
             <artifactId>jackson-databind</artifactId>
 45  
             <version>2.5.0</version>
 46  
         </dependency>
 47  
         <dependency>
 48  
             <groupId>com.fasterxml.jackson.core</groupId>
 49  
             <artifactId>jackson-annotations</artifactId>
 50  
             <version>2.5.0</version>
 51  
         </dependency>
 52  
 *
 53  
 * @author k.inaba
 54  
 */
 55  
 public class TextDao {
 56  
 
 57  
      /*
 58  
      * --------------------------------------------------------------------------
 59  
      * サンプル実装
 60  
      */
 61  
 
 62  
      /**
 63  
      * 実装のサンプル このDAOの使用方法を示す。
 64  
      * @param args パラメータ不要
 65  
      */
 66  
      public static void main(String[] args) {
 67  
 
 68  
           /*
 69  
           * DAOをインスタンス化する。
 70  
           * このとき保存先を指定する。
 71  
           */
 72  0
           TextDao dao = new TextDao("C:\\tmp\\textdb");
 73  
 
 74  
           /* 登録のサンプル */
 75  
 
 76  0
           TextDBAccSampleDto insertdto1 = new TextDBAccSampleDto(10, "aiueo");
 77  0
           int insertId1 = dao.insert(insertdto1);
 78  0
           System.out.println(insertdto1.toString() + "を[" + insertId1 + "]として保存しました。");
 79  
 
 80  0
           TextDBAccSampleDto insertdto2 = new TextDBAccSampleDto(10, "あいうえお");
 81  0
           int insertId2 = dao.insert(insertdto2);
 82  0
           System.out.println(insertdto2.toString() + "を[" + insertId2 + "]として保存しました。");
 83  
 
 84  
           /* 検索のサンプル*/
 85  
 
 86  0
           TextDBAccSampleDto condA = new TextDBAccSampleDto();
 87  0
           condA.col1 = 10;
 88  0
           List<TextDBAccSampleDto> resultsA = dao.select(condA, TextDBAccSampleDto.class);
 89  
 
 90  0
           System.out.println(condA.toString() + " を条件として検索を実行。");
 91  0
           for (TextDBAccSampleDto dto: resultsA) {
 92  0
                System.out.println(">" + dto.toString());
 93  
           }
 94  
 
 95  0
           TextDBAccSampleDto condB = new TextDBAccSampleDto();
 96  0
           condB.col1 = 10;
 97  0
           condB.col2 = "あいうえお";
 98  0
           List<TextDBAccSampleDto> resultsB = dao.select(condB, TextDBAccSampleDto.class);
 99  
 
 100  0
           System.out.println(condB.toString() + " を条件として検索を実行。");
 101  0
           for (TextDBAccSampleDto dto: resultsB) {
 102  0
                System.out.println(">" + dto.toString());
 103  
           }
 104  
 
 105  
           /* 削除のサンプル */
 106  
 
 107  0
           List<TextDBAccSampleDto> resultsAll = dao.select(TextDBAccSampleDto.class);
 108  0
           Collections.reverse(resultsAll);
 109  0
           int i = 0;
 110  0
           for (TextDBAccSampleDto dto : resultsAll) {
 111  0
                System.out.println(dto.toString());
 112  0
                if (i > 5) {
 113  0
                     System.out.println(dto.id + " を削除します。");
 114  
                     // dao.delete(r, false); 論理削除ではなく、実際にファイルを削除する。
 115  0
                     dao.delete(dto);
 116  
                }
 117  0
                i++;
 118  
           }
 119  0
      }
 120  
 
 121  
      /**
 122  
      * 実装のサンプル このDAOで扱うDTOを実装する際の参考とする。
 123  
      */
 124  
      private static class TextDBAccSampleDto {
 125  
           /**
 126  
           * 制御用のフィールド[id]
 127  
           *
 128  
           */
 129  
           public int id;
 130  
           /* 保存するフィールド */
 131  
           public int col1;
 132  
           public String col2;
 133  
 
 134  
           /**
 135  
           * JSONからオブジェクトへ変換するために、 引数付きのコンストラクタが実装されている場合は、 デフォルトコンストラクタの実装が必要である。
 136  
           */
 137  0
           public TextDBAccSampleDto() {
 138  
                // 処理なし
 139  0
           }
 140  
 
 141  0
           public TextDBAccSampleDto(int col1, String col2) {
 142  0
                this.col1 = col1;
 143  0
                this.col2 = col2;
 144  0
           }
 145  
 
 146  
           @Override
 147  
           public String toString() {
 148  0
                return "TextDBAccSampleBean [id=" + id + ", col1=" + col1
 149  0
                          + ", col2=" + col2 + "]";
 150  
           }
 151  
      }
 152  
 
 153  
      /* -------------------------------------------------------------------------- */
 154  
 
 155  
      private File dbroot;
 156  
      /**
 157  
      * @param root データを保存するディレクトリ
 158  
      */
 159  0
      public TextDao(String dbname) {
 160  0
           dbroot = new File(getDbPath(dbname));
 161  0
           if (dbroot.exists() == false) {
 162  0
                dbroot.mkdirs();
 163  
           }
 164  0
      }
 165  
      /**
 166  
       * Tomcatインスタンスのコンテキストルートより2階層上位のディレクトリを
 167  
       * テキストデータを保存するディレクトリのパスとして取得する。
 168  
       * 
 169  
       * Linuxでの実行環境では
 170  
       *  /var/local/opt/tomcat/apache-tomcat/7.0/{???}-web/{dbname}/
 171  
       * 
 172  
       * 開発環境(ローカルのEclipse)では
 173  
       *  {ワークスペース}\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\{dbname}
 174  
       * 
 175  
       * @return テキストデータを保存するディレクトリのパス
 176  
       */
 177  
      private static String getDbPath(String dbname) {
 178  0
          ServletContext servletContext = WebApplication.get().getServletContext();
 179  0
          File tomCatInstance = new File(servletContext.getRealPath("")).getParentFile().getParentFile(); // コンテキストルートより2階層上位
 180  0
          File file = new File(new File(tomCatInstance, dbname), dbname + ".txt");
 181  0
          if (file.exists() == false) {
 182  0
              file.getParentFile().mkdirs();
 183  
              try {
 184  0
                  boolean result = file.createNewFile();
 185  0
                  if (result == false) {
 186  0
                      throw new RuntimeException("ファイルの作成に失敗:" + file.getAbsolutePath());
 187  
                  }
 188  0
              } catch (IOException e) {
 189  0
                  throw new RuntimeException(e);
 190  
              }
 191  
          }
 192  0
          return file.getParent();
 193  
      }
 194  
 
 195  
      /**
 196  
      * 保存する
 197  
      * @param bean 保存するデータ
 198  
      * @return id 保存したときに付与されたid
 199  
      */
 200  
      public int insert(Object bean) {
 201  0
           String tablename = bean.getClass().getName();
 202  0
           int maxId = -1;
 203  0
           File[] existfiles = getDbFiles(tablename);
 204  0
           for (File existfile : existfiles) {
 205  0
                int id = toId(existfile.getName(), tablename);
 206  0
                maxId = Math.max(maxId, id);
 207  
           }
 208  0
           int insertId = maxId + 1;
 209  0
           ObjectMapper objectMapper = new ObjectMapper();
 210  
           try {
 211  0
                String json = objectMapper.writeValueAsString(bean);
 212  0
                File f = toFile(bean.getClass(), (insertId));
 213  0
                writeText(f.getAbsolutePath(), json);
 214  0
           } catch (JsonProcessingException e) {
 215  0
                throw new RuntimeException(e);
 216  
           }
 217  0
           return insertId;
 218  
      }
 219  
 
 220  
      /**
 221  
      * 指定されたクラスのデータをすべて取得する。
 222  
      * @param clz 指定クラス
 223  
      * @return 取得したデータ
 224  
      */
 225  
      public <T> List<T> select(Class<T> clz) {
 226  0
           return select(null, clz) ;
 227  
      }
 228  
 
 229  
      /**
 230  
      * 指定されたクラスのデータのうち、引数 cond の条件に合うものを取得する。
 231  
      *
 232  
      * ・cond のフィールドのうち、 nullでないフィールドが検索条件となる。
 233  
      * ・AND条件となる。
 234  
      * ・フィールド id は無視する。
 235  
      *
 236  
      * @param clz 指定クラス
 237  
      * @return 取得したデータ
 238  
      */
 239  
      public <T> List<T> select(T cond, Class<T> clz) {
 240  0
           List<Field> cndFields = new ArrayList<Field>();
 241  0
           for (Field f : clz.getFields()) {
 242  0
                if (f.getName().equals("id") == false) {
 243  0
                     cndFields.add(f);
 244  
                }
 245  
           }
 246  0
           String tablename = clz.getName();
 247  0
           File[] existfiles = getDbFiles(tablename);
 248  0
           List<T> result = new ArrayList<T>();
 249  0
           for (File file : existfiles) {
 250  
                // String text = "{\"a\":123,\"b\":true,\"c\":\"あいう\"}";
 251  0
                String text = readTextAsString(file.getAbsolutePath());
 252  0
                ObjectMapper mapper = new ObjectMapper();
 253  
                T bean;
 254  
                try {
 255  
                     // JsonNode root = mapper.readTree(text);
 256  
                     // String c = root.get("c").asText();
 257  0
                     bean = mapper.readValue(text, clz);
 258  0
                } catch (JsonProcessingException e) {
 259  0
                     throw new RuntimeException(e);
 260  0
                } catch (IOException e) {
 261  0
                     throw new RuntimeException(e);
 262  
                }
 263  
 
 264  
                /* 絞り込みのための判定 */
 265  
                boolean isMatch;
 266  0
                if (cond == null) {
 267  
                     /* 条件なしのため必ず選択(true) */
 268  0
                     isMatch = true;
 269  0
                } else {
 270  0
                     isMatch = true;
 271  0
                     for (Field cndField : cndFields) {
 272  
                          Object cndValue;
 273  
                          Object targetValue;
 274  
                          try {
 275  0
                               cndValue = cndField.get(cond);
 276  0
                               targetValue = cndField.get(bean);
 277  0
                          } catch (Exception e) {
 278  0
                               throw new RuntimeException(e);
 279  
                          }
 280  0
                          if (cndValue != null
 281  0
                                    && cndValue.equals(targetValue) == false) {
 282  
                               /* 不一致の条件を検出 */
 283  0
                               isMatch = false;
 284  0
                               break;
 285  
                          }
 286  
                     }
 287  
                }
 288  0
                if (isMatch) {
 289  
                     /* beanにidを付与する */
 290  
                     /* ファイル名からidを取得する */
 291  
                     try {
 292  0
                          Field f = clz.getField("id");
 293  0
                          f.set(bean, toId(file.getName(), tablename));
 294  0
                     } catch (NoSuchFieldException e) {
 295  
                          /* フィールド[id]がなければ無視する。エラーとしない。 */
 296  0
                     } catch (Exception e) {
 297  0
                          throw new RuntimeException(e);
 298  
                     }
 299  0
                     result.add(bean);
 300  
                }
 301  
           }
 302  0
           return result;
 303  
      }
 304  
 
 305  
      /**
 306  
      * 指定されたBeanのidと一致するデータを論理削除する。
 307  
      * 論理削除の場合は、該当のデータの拡張子に[.del]が付与され、無効化される。
 308  
      * @param bean
 309  
      */
 310  
      public void delete(Object bean) {
 311  0
           delete(bean, true); // デフォルトは論理削除
 312  0
      }
 313  
      /**
 314  
      * 指定されたBeanのidと一致するデータを削除する。
 315  
      * 論理削除の場合は、該当のデータの拡張子に[.del]が付与され、無効化される。
 316  
      * @param bean
 317  
      * @param logical 論理削除とする場合はTrue, 物理削除はFalseを指定する。
 318  
      */
 319  
      private void delete(Object bean, boolean logical) {
 320  
           int id;
 321  
           try {
 322  0
                Field f = bean.getClass().getField("id");
 323  0
                id = f.getInt(bean);
 324  0
           } catch (NoSuchFieldException e) {
 325  0
                throw new RuntimeException("フィールド[id]がないbeanは削除できません。", e);
 326  0
           } catch (Exception e) {
 327  0
                throw new RuntimeException(e);
 328  
           }
 329  0
           File file = toFile(bean.getClass(), id);
 330  0
           if (logical) {
 331  
                /* 論理削除 (ファイル名の末尾に[.del]を付与する) */
 332  0
                File dest = getDelFile(file);
 333  0
                file.renameTo(dest);
 334  0
           } else {
 335  0
                file.delete();
 336  
           }
 337  0
      }
 338  
 
 339  
      /**
 340  
      * 引数srcのファイルオブジェクトのファイル名の末尾に [.del]を付与したオブジェクトを取得する。
 341  
      * すでに同名のファイルが存在するときは、[.del.del]となるように 再起処理を行う。
 342  
      *
 343  
      * @param src
 344  
      * @return
 345  
      */
 346  
      private File getDelFile(File src) {
 347  0
           File delFile = new File(src.getAbsolutePath() + ".del");
 348  0
           if (delFile.exists()) {
 349  0
                return getDelFile(delFile);
 350  
           }
 351  0
           return delFile;
 352  
      }
 353  
 
 354  
      private File toFile(Class<? extends Object> clz, int id) {
 355  0
           String tablename = clz.getName();
 356  0
           String filename = dbroot.getAbsolutePath() + File.separatorChar
 357  0
                     + tablename + "_" + id + ".txt";
 358  0
           return new File(filename);
 359  
      }
 360  
 
 361  
      private int toId(String filename, String tablename) {
 362  
           // Matcher m = Pattern.compile("_([0-9]+)\\.txt").matcher(tmp);
 363  
           // tmp = m.group(1);
 364  0
           String tmp = filename;
 365  0
           tmp = tmp.substring(tablename.length()); // 接頭のテーブル名を除去
 366  0
           tmp = tmp.substring(1); // _を除去
 367  0
           tmp = tmp.substring(0, tmp.lastIndexOf(".txt"));
 368  
           int id;
 369  
           try {
 370  0
                id = Integer.parseInt(tmp);
 371  0
           } catch (NumberFormatException e) {
 372  0
                throw new RuntimeException(e);
 373  
           }
 374  0
           return id;
 375  
      }
 376  
 
 377  
      private File[] getDbFiles(final String tablename) {
 378  0
           File[] existfiles = dbroot.listFiles(new FileFilter() {
 379  
                @Override
 380  
                public boolean accept(File f) {
 381  0
                     return (f.getName().startsWith(tablename + "_") && f.getName()
 382  0
                               .endsWith(".txt"));
 383  
                }
 384  
           });
 385  0
           return existfiles;
 386  
      }
 387  
      /*------------------------------------------------------------------------------*/
 388  
      private static final String DEFAULT_ENC = "UTF-8";
 389  0
      public static final String FILE_SEPARATOR = File.separator;
 390  0
      public static final String LINE_SEPARATOR = System.getProperty("line.separator");
 391  
      /**
 392  
       * テキストファイルを書き出す
 393  
       * @param filepath
 394  
       * @param charsetName 例:"UTF-8"
 395  
       * @param text
 396  
       */
 397  
      public static void writeText(String filepath, String charsetName, List<String> text) {
 398  0
          File f = new File(filepath);
 399  0
          OutputStream os = null;
 400  0
          Writer w = null;
 401  0
          BufferedWriter bw = null;
 402  
          try {
 403  0
              os = new FileOutputStream(f);
 404  0
              w  = new OutputStreamWriter(os, charsetName);
 405  0
              bw = new BufferedWriter(w);
 406  0
              for (String line: text) {
 407  0
                  bw.write(line);
 408  0
                  bw.newLine();
 409  
                  //bw.flush();
 410  
              }
 411  0
          } catch (Exception e) {
 412  0
              throw new RuntimeException(e);
 413  0
          } finally {
 414  
              try {
 415  0
                  if (bw != null) bw.close();
 416  0
                  if (w != null) w.close();
 417  0
                  if (os != null) os.close();
 418  0
              } catch (IOException e) {
 419  0
                  throw new RuntimeException(e);
 420  
              }
 421  0
          }
 422  0
      }
 423  
      public static void writeText(String filepath, String charsetName, String text) {
 424  0
          String[] array = text.split(LINE_SEPARATOR);
 425  0
          List<String> list = Arrays.asList(array);
 426  0
          writeText(filepath, charsetName, list);
 427  0
      }
 428  
      public static void writeText(String filepath, List<String> string) {
 429  0
          writeText(filepath, DEFAULT_ENC, string);
 430  0
      }
 431  
      public static void writeText(String filepath, String string) {
 432  0
          writeText(filepath, DEFAULT_ENC, string);
 433  0
      }
 434  
      public static String readTextAsString(String filepath, String charsetName) {
 435  0
          StringBuilder sb = new StringBuilder();
 436  0
          List<String> lines = readText(filepath, charsetName);
 437  0
          for (String line: lines) {
 438  0
              sb.append(line).append(LINE_SEPARATOR);
 439  
          }
 440  0
          return sb.toString();
 441  
      }
 442  
      /**
 443  
       * テキストファイルを読み込む
 444  
       * @param filepath
 445  
       * @return
 446  
       */
 447  
      public static String readTextAsString(String filepath) {
 448  0
          return readTextAsString(filepath, DEFAULT_ENC);
 449  
      }
 450  
      /**
 451  
       * テキストファイルを読み込む
 452  
       * @param filepath
 453  
       * @param charsetName 例:"UTF-8"
 454  
       * @return List<String>
 455  
       */
 456  
      public static List<String> readText(String filepath, String charsetName) {
 457  0
          if( new File(filepath).exists() == false ) {
 458  0
              throw new RuntimeException("ファイルが見つかりません。 [" + new File(filepath).getAbsolutePath() + "]");
 459  
          }
 460  0
          List<String> result = new ArrayList<String>();
 461  0
          FileInputStream is = null;
 462  0
          InputStreamReader r = null;
 463  0
          BufferedReader br = null;
 464  
          try {
 465  0
              is = new FileInputStream(filepath);
 466  0
              r = new InputStreamReader(is, Charset.forName(charsetName));
 467  0
              br = new BufferedReader(r);
 468  
 //           int ch;
 469  
 //           while ((ch = in.read()) != -1) {
 470  
              String line;
 471  0
              while((line = br.readLine()) != null) {
 472  0
                  result.add(line);
 473  
 
 474  
                  //System.out.print(str + NEW_LINE_CODE);
 475  
                  /* Unicode表現でデバッグ表示 */
 476  
                  //System.out.print(Integer.toHexString(ch) + " ");
 477  
              }
 478  0
          } catch (IOException e) {
 479  0
              throw new RuntimeException(e);
 480  0
          } finally {
 481  
              try {
 482  0
                  if (br != null) br.close();
 483  0
                  if (r != null) r.close();
 484  0
                  if (is != null) is.close();
 485  0
              } catch (IOException e) {
 486  0
                  throw new RuntimeException(e);
 487  
              }
 488  0
          }
 489  0
          return result;
 490  
      }
 491  
 }