# 4.9. コードリスト

## 4.9.1. Overview

コードリストとは、「コード値(value)とその表示名(label)」の集合である。

画面のセレクトボックスなどコード値を画面で表示する際のラベルへのマッピング表として利用される。

共通ライブラリでは、

- xmlファイルやDBに定義されたコードリストをアプリケーション起動時に読み込みキャッシュする機能

- JSPやThymeleafのテンプレートHTML、Javaクラスからコードリストを参照する機能

- コードリストを用いて入力チェックする機能

を提供している。

また、応用的な使い方として、

- コードリストの国際化対応

- キャッシュされたコードリストのリロード

もサポートしている。

Note

標準でリロードが可能なのは、DBに定義されたコードリストを使用する場合のみである。

共通ライブラリでは、以下のコードリスト実装クラスを提供している。

コードリスト種類一覧
種類 | 内容 | Reloadable
--- | --- | ---
`org.terasoluna.gfw.common.codelist.SimpleMapCodeList` | Javaクラス及びXMLに直接記述した内容を使用する。 | NO
`org.terasoluna.gfw.common.codelist.NumberRangeCodeList` | 数値の範囲のリストを作成する際に使用する。 | NO
`org.terasoluna.gfw.common.codelist.JdbcCodeList` | DBから対象のコードをSQLで取得して使用する。 | YES
`org.terasoluna.gfw.common.codelist.EnumCodeList` | `Enum`クラスに定義した定数からコードリストを作成する際に使用する。 | NO
`org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList` | 国際化に対応し、java.util.Localeに応じたコードリストを使用する。 | NO
`org.terasoluna.gfw.common.codelist.i18n.SimpleReloadableI18nCodeList` | 国際化に対応し、java.util.Localeに応じた更新可能なコードリストを使用する。(5.4.2から追加) | YES

上記コードリストのインタフェースについて、共通ライブラリに`org.terasoluna.gfw.common.codelist.CodeList`を提供している。
また国際化に対応しているコードリストのインタフェースについて、`org.terasoluna.gfw.common.codelist.i18n.I18nCodeList`を提供している。

共通ライブラリで提供しているコードリストのクラス図構成を以下に示す。

Picture - Image of codelist class diagram

## 4.9.2. How to use

本項では、各種コードリストを使用する上での設定や実装方法を記述する。

- SimpleMapCodeListの使用方法

- NumberRangeCodeListの使用方法

- JdbcCodeListの使用方法

- EnumCodeListの使用方法

- I18nCodeListの使用方法

- 特定のコード値からコード名を表示する

- コードリストを用いたコード値の入力チェック

### 4.9.2.1. SimpleMapCodeListの使用方法

`org.terasoluna.gfw.common.codelist.SimpleMapCodeList`とは、 Javaクラスまたはxmlファイルに定義したコード値をアプリケーション起動時に読み込み、そのまま使用するコードリストである。

SimpleMapCodeListのイメージ

#### 4.9.2.1.1. コードリスト設定例

bean定義ファイルの定義

bean定義ファイルは、コードリスト用に作成することを推奨する。

- `XxxCodelistConfig.java`

      @Bean("CL_ORDERSTATUS") // (1)
      public SimpleMapCodeList clOrderstatus() {
          Map<String, String> codeMap = new LinkedHashMap<>();
          codeMap.put("1", "Received"); // (2)
          codeMap.put("2", "Sent"); // (2)
          codeMap.put("3", "Cancelled"); // (2)
          SimpleMapCodeList bean = new SimpleMapCodeList();
          bean.setMap(codeMap);
          return bean;
      }

  項番 | 説明
  --- | ---
  (1) | SimpleMapCodeListクラスをbean定義する。 beanIDは、後述する`org.terasoluna.gfw.web.codelist.CodeListInterceptor`のIDパターンに合致する名称にすること。
  (2) | Mapの Key、Valueを定義する。 上記例では`java.util.LinkedHashMap`で登録しているため、「名前と値」が登録順にMapへ保持される。

コードリスト用bean定義ファイルを作成後、既存bean定義ファイルにimportを行う必要がある。

- `XxxDomainConfig.java`

      @Configuration
      @ComponentScan(basePackages = { "com.example.domain" })
      @Import({ XxxCodelistConfig.class }) // (3)
      public class XxxDomainConfig {

  項番 | 説明
  --- | ---
  (3) | コードリスト用bean定義ファイルをimportする。

- `xxx-codelist.xml`

      <bean id="CL_ORDERSTATUS" class="org.terasoluna.gfw.common.codelist.SimpleMapCodeList"> <!-- (1) -->
          <property name="map">
              <util:map>
                  <entry key="1" value="Received" /> <!-- (2) -->
                  <entry key="2" value="Sent" /> <!-- (2) -->
                  <entry key="3" value="Cancelled" /> <!-- (2) -->
              </util:map>
          </property>
      </bean>

  項番 | 説明
  --- | ---
  (1) | SimpleMapCodeListクラスをbean定義する。 beanIDは、後述する`org.terasoluna.gfw.web.codelist.CodeListInterceptor`のIDパターンに合致する名称にすること。
  (2) | Mapの Key、Valueを定義する。 map-class属性を省略した場合`java.util.LinkedHashMap`で登録されるため、上記例では「名前と値」が登録順にMapへ保持される。

コードリスト用bean定義ファイルを作成後、既存bean定義ファイルにimportを行う必要がある。

- `xxx-domain.xml`

      <import resource="classpath:META-INF/spring/projectName-codelist.xml" /> <!-- (3) -->
      <context:component-scan base-package="com.example.domain" />

  項番 | 説明
  --- | ---
  (3) | コードリスト用bean定義ファイルをimportする。 component-scanしている間にimport先の情報が必要な場合があるため、importは`<context:component-scan base-package="com.example.domain" />`より上で設定する必要がある。

#### 4.9.2.1.2. 共通ライブラリから提供しているインタセプターの使用

共通ライブラリから提供しているインタセプターを用いることで、リクエストスコープに自動的に設定し、JSP/テンプレートHTMLからコードリストを容易に参照できる。

bean定義ファイルの定義

- `SpringMvc.java`

      @EnableAspectJAutoProxy
      @EnableWebMvc
      @Configuration
      public class SpringMvcConfig implements WebMvcConfigurer {

          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              registry.addInterceptor(codeListInterceptor())
                      .addPathPatterns("/**"); // (1)
          }

          // (2)
          @Bean
          public CodeListInterceptor codeListInterceptor() {
              CodeListInterceptor codeListInterceptor = new CodeListInterceptor();
              codeListInterceptor.setCodeListIdPattern(Pattern.compile("CL_.+")); // (3)
              return codeListInterceptor;
          }

  項番 | 説明
  --- | ---
  (1) | 適用対象のパスを設定する。
  (2) | CodeListInterceptor クラスをbean定義する。
  (3) | 自動でリクエストスコープに設定する、コードリストのbeanIDのパターンを設定する。 パターンには`java.util.regex.Pattern`で使用する正規表現を設定すること。 上記例では、idが”CL_XXX”形式で定義されているデータのみを対象とする。その場合、idが”CL_”で始まらないbean定義は取り込まれない。 “CL_”で定義したbeanIDは、リクエストスコープに設定されるため、JSPで容易に参照できる。 `codeListIdPattern`プロパティは省略可能である。 `codeListIdPattern`を省略した場合は、すべてのコードリスト(`org.terasoluna.gfw.common.codelist.CodeList`インタフェースを実装しているbean)がリクエストスコープに設定される。

- `spring-mvc.xml`

      <mvc:interceptors>
        <mvc:interceptor>
          <mvc:mapping path="/**" /> <!-- (1) -->
          <bean
            class="org.terasoluna.gfw.web.codelist.CodeListInterceptor"> <!-- (2) -->
            <property name="codeListIdPattern" value="CL_.+" /> <!-- (3) -->
          </bean>
        </mvc:interceptor>

        <!-- omitted -->

      </mvc:interceptors>

  項番 | 説明
  --- | ---
  (1) | 適用対象のパスを設定する。
  (2) | CodeListInterceptor クラスをbean定義する。
  (3) | 自動でリクエストスコープに設定する、コードリストのbeanIDのパターンを設定する。 パターンには`java.util.regex.Pattern`で使用する正規表現を設定すること。 上記例では、idが”CL_XXX”形式で定義されているデータのみを対象とする。その場合、idが”CL_”で始まらないbean定義は取り込まれない。 “CL_”で定義したbeanIDは、リクエストスコープに設定されるため、JSPで容易に参照できる。 `codeListIdPattern`プロパティは省略可能である。 `codeListIdPattern`を省略した場合は、すべてのコードリスト(`org.terasoluna.gfw.common.codelist.CodeList`インタフェースを実装しているbean)がリクエストスコープに設定される。

Warning

例外発生時のコードリスト利用について

terasoluna-gfw-common 5.4.2.RELEASEより、Controllerのハンドラメソッドで例外が発生し `@ExceptionHandler`や `SystemExceptionResolver`で例外ハンドリングを行なった場合は、コードリストがリクエストスコープに登録されなくなった。

これは、 `CodeListInterceptor`が `HandlerInterceptor#postHandle`メソッドでコードリストの登録を行うように変更されたためである。

例外時に遷移する画面でコードリストを利用したい場合は、ハンドラメソッドで例外を捕捉(try-catch)するか、JSP/テンプレートHTMLから直接コードリストBeanを参照するを利用してコードリストを取得することを検討されたい。

例外ハンドリングの方法については、例外のハンドリング方法を参照されたい。

jspの実装例

    <form:select path="orderStatus">
        <form:option value="" label="--Select--" /> <!-- (4) -->
        <form:options items="${CL_ORDERSTATUS}" /> <!-- (5) -->
    </form:select>

項番 | 説明
--- | ---
(4) | セレクトボックスの先頭にダミーの値を設定する場合、valueに空文字を指定すること。
(5) | コードリストを定義したbeanIDを指定する。

テンプレートHTML実装例

    <select th:field="*{orderStatus}">
        <option value="">--Select--</option> <!--/* (4) */-->
        <option th:each="order : ${CL_ORDERSTATUS}" th:value="${order.key}" th:text="${order.value}"></option> <!--/* (5) */-->
    </select>

項番 | 説明
--- | ---
(4) | セレクトボックスの先頭にダミーの値を設定する場合、valueに空文字を指定すること。
(5) | コードリストを定義したbeanIDを指定する。

出力HTML

    <select id="orderStatus" name="orderStatus">
        <option value="">--Select--</option>
        <option value="1">Received</option>
        <option value="2">Sent</option>
        <option value="3">Cancelled</option>
    </select>

出力画面

#### 4.9.2.1.3. Javaクラスでのコードリスト使用

Javaクラスでコードリストを利用する場合、`jakarta.inject.Inject`アノテーションと、`jakarta.inject.Named`アノテーションを設定してコードリストをインジェクションする。
`@Named`にコードリスト名を指定する。

    import org.springframework.stereotype.Service;
    import org.terasoluna.gfw.common.codelist.CodeList;

    import jakarta.inject.Inject;
    import jakarta.inject.Named;

    @Service
    public class OrderServiceImpl implements OrderService {

        @Inject
        @Named("CL_ORDERSTATUS")
        CodeList orderStatusCodeList; // (1)

        public boolean existOrderStatus(String target) {
            return orderStatusCodeList.asMap().containsKey(target); // (2)
        }
    }

項番 | 説明
--- | ---
(1) | beanIDが、”CL_ORDERSTATUS”であるコードリストをインジェクションする。
(2) | CodeList#asMapメソッドでコードリストを`java.util.Map`形式で取得する。

### 4.9.2.2. NumberRangeCodeListの使用方法

`org.terasoluna.gfw.common.codelist.NumberRangeCodeList`とは、アプリケーション起動時に、指定した数値の範囲をリストにするコードリストである。
主に数だけのセレクトボックス、月や日付などのセレクトボックスに使用することを想定している。

NumberRangeCodeListのイメージ

Tip

NumberRangeCodeListはアラビア数字のみ対応しており、漢数字やローマ数字には対応していない。

漢数字やローマ数字を表示したい場合はJdbcCodeList、SimpleMapCodeListに定義することで対応可能である。

NumberRangeCodeListには、以下の特徴がある。

1. Fromの値をToの値より小さくする場合、昇順にinterval分増加した値をFrom～Toの範囲分リストにする。

2. Toの値をFromの値より小さくする場合、降順にinterval分減少した値をTo～Fromの範囲分リストにする。

3. 増加分(減少分)はintervalを設定することで変更できる。

ここでは、昇順の`NumberRangeCodeList`について説明をする。 降順の`NumberRangeCodeList`とインターバルの変更方法については、「NumberRangeCodeListのバリエーション」を参照されたい。

#### 4.9.2.2.1. コードリスト設定例

Fromの値をToの値より小さくする(From < To)場合の実装例を、以下に示す。

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_MONTH")
      public NumberRangeCodeList clMonthAsc() {
          NumberRangeCodeList bean = new NumberRangeCodeList(); // (1)
          bean.setFrom(1); // (2)
          bean.setTo(12); // (3)
          bean.setValueFormat("%d"); // (4)
          bean.setLabelFormat("%02d"); // (5)
          bean.setInterval(1); // (6)
          return bean;
      }

  項番 | 説明
  --- | ---
  (1) | NumberRangeCodeListをbean定義する。
  (2) | 範囲開始の値を指定する。省略した場合、”0”が設定される。
  (3) | 範囲終了の値を設定する。指定必須。
  (4) | コード値のフォーマット形式を設定する。フォーマット形式は`java.lang.String.format`の形式が使用される。 省略した場合、”%s”が設定される。
  (5) | コード名のフォーマット形式を設定する。フォーマット形式は`java.lang.String.format`の形式が使用される。 省略した場合、”%s”が設定される。
  (6) | 増加する値を設定する。省略した場合、”1”が設定される。

- `xxx-codelist.xml`

      <bean id="CL_MONTH"
          class="org.terasoluna.gfw.common.codelist.NumberRangeCodeList"> <!-- (1) -->
          <property name="from" value="1" /> <!-- (2) -->
          <property name="to" value="12" /> <!-- (3) -->
          <property name="valueFormat" value="%d" /> <!-- (4) -->
          <property name="labelFormat" value="%02d" /> <!-- (5) -->
          <property name="interval" value="1" /> <!-- (6) -->
      </bean>

  項番 | 説明
  --- | ---
  (1) | NumberRangeCodeListをbean定義する。
  (2) | 範囲開始の値を指定する。省略した場合、”0”が設定される。
  (3) | 範囲終了の値を設定する。指定必須。
  (4) | コード値のフォーマット形式を設定する。フォーマット形式は`java.lang.String.format`の形式が使用される。 省略した場合、”%s”が設定される。
  (5) | コード名のフォーマット形式を設定する。フォーマット形式は`java.lang.String.format`の形式が使用される。 省略した場合、”%s”が設定される。
  (6) | 増加する値を設定する。省略した場合、”1”が設定される。

#### 4.9.2.2.2. コードリストの使用

設定例の詳細は、前述した共通ライブラリから提供しているインタセプターの使用を参照されたい。

jspの実装例

    <form:select path="depMonth" items="${CL_MONTH}" />

テンプレートHTML実装例

    <select th:field="*{depMonth}">
        <option th:each="month : ${CL_MONTH}" th:value="${month.key}" th:text="${month.value}"></option>
    </select>

出力HTML

    <select id="depMonth" name="depMonth">
        <option value="1">01</option>
        <option value="2">02</option>
        <option value="3">03</option>
        <option value="4">04</option>
        <option value="5">05</option>
        <option value="6">06</option>
        <option value="7">07</option>
        <option value="8">08</option>
        <option value="9">09</option>
        <option value="10">10</option>
        <option value="11">11</option>
        <option value="12">12</option>
    </select>

出力画面

#### 4.9.2.2.3. Javaクラスでのコードリスト使用

設定例の詳細は、前述したJavaクラスでのコードリスト使用を参照されたい。

### 4.9.2.3. JdbcCodeListの使用方法

`org.terasoluna.gfw.common.codelist.JdbcCodeList`とは、アプリケーション起動時にDBから値を取得し、コードリストを作成するクラスである。
`JdbcCodeList`はアプリケーション起動時にキャッシュを作るので、リスト表示時はDBアクセスによる遅延がない。
起動時の読み込み時間を抑えたいならば、取得数の上限を設定するとよい。
`JdbcCodeList`には`org.springframework.jdbc.core.JdbcTemplate`を設定するフィールドがある。
`JdbcTemplate`の`fetchSize`に上限を設定すれば、その分だけのレコードが起動時に読み込まれる。
なお、取得する値はリロードにより動的に変更できる。詳細はコードリストをリロードする場合参照されたい。

JdbcCodeListのイメージ

#### 4.9.2.3.1. コードリスト設定例

テーブル(authority)の定義

authority_id | authority_name
--- | ---
01 | STAFF_MANAGEMENT
02 | MASTER_MANAGEMENT
03 | STOCK_MANAGEMENT
04 | ORDER_MANAGEMENT
05 | SHOW_SHOPPING_CENTER

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Value("${codelist.jdbc.fetchSize:1000}")
      private Integer fetchSize;

      @Bean("jdbcTemplateForCodeList")
      public JdbcTemplate jdbcTemplateForCodeList() {
          JdbcTemplate bean = new JdbcTemplate(); // (1)
          bean.setDataSource(dataSource);
          bean.setFetchSize(fetchSize); // (2)
          return bean;
      }

      // (3)
      private JdbcCodeList abstractJdbcCodeList() {
          JdbcCodeList bean = new JdbcCodeList();
          bean.setJdbcTemplate(jdbcTemplateForCodeList()); // (4)
          return bean;
      }

      @Bean("CL_AUTHORITIES")
      public JdbcCodeList clAuthorities() {
          JdbcCodeList jdbcCodeList = abstractJdbcCodeList(); // (5)
          jdbcCodeList.setQuerySql("SELECT authority_id, authority_name FROM t_authority ORDER BY authority_id"); // (6)
          jdbcCodeList.setValueColumn("authority_id"); // (7)
          jdbcCodeList.setLabelColumn("authority_name"); // (8)
          return jdbcCodeList;
      }

  項番 | 説明
  --- | ---
  (1) | `org.springframework.jdbc.core.JdbcTemplate`クラスをbean定義する。 独自に`fetchSize`を設定するために必要となる。
  (2) | `fetchSize`を設定する。 `fetchSize`のデフォルト設定が、全件取得になっている場合があるため適切な値を設定すること。 `fetchSize`の設定が全件取得のままだと、`JdbcCodeList`の読み込む件数が大きい場合に、DBからリストを取得する際の処理性能が落ちてしまい、アプリケーションの起動時間が長期化する可能性がある。
  (3) | `JdbcCodeList`の共通bean定義。 他の`JdbcCodeList`の共通部分を設定している。そのため、基本`JdbcCodeList`のbean定義は当メソッドを呼び出す。
  (4) | (1)で設定した`jdbcTemplate`を設定。 `fetchSize`を設定した`JdbcTemplate`を、`JdbcCodeList`に格納している。
  (5) | `JdbcCodeList`のbean定義。 parent属性を(3)のbean定義を親クラスとして設定することで、`fetchSize`を設定した`JdbcCodeList`が設定される。 このbean定義では、クエリに関する設定のみを行い、必要なCodeList分作成する。
  (6) | querySqlプロパティに取得するSQLを記述する。その際、必ず「ORDER BY」を指定し、順序を確定させること。 「ORDER BY」を指定しないと、取得する度に順序が変わってしまう。
  (7) | valueColumnプロパティに、MapのKeyに該当する値を設定する。この例ではauthority_idを設定している。
  (8) | labelColumnプロパティに、MapのValueに該当する値を設定する。この例ではauthority_nameを設定している。

- `xxx-codelist.xml`

      <bean id="jdbcTemplateForCodeList" class="org.springframework.jdbc.core.JdbcTemplate" > <!-- (1) -->
          <property name="dataSource" ref="dataSource" />
          <property name="fetchSize" value="${codelist.jdbc.fetchSize:1000}" /> <!-- (2) -->
      </bean>

      <bean id="AbstractJdbcCodeList"
          class="org.terasoluna.gfw.common.codelist.JdbcCodeList" abstract="true"> <!-- (3) -->
          <property name="jdbcTemplate" ref="jdbcTemplateForCodeList" /> <!-- (4) -->
      </bean>

      <bean id="CL_AUTHORITIES" parent="AbstractJdbcCodeList" > <!-- (5) -->
          <property name="querySql"
              value="SELECT authority_id, authority_name FROM authority ORDER BY authority_id" /> <!-- (6) -->
          <property name="valueColumn" value="authority_id" /> <!-- (7) -->
          <property name="labelColumn" value="authority_name" /> <!-- (8) -->
      </bean>

  項番 | 説明
  --- | ---
  (1) | `org.springframework.jdbc.core.JdbcTemplate`クラスをbean定義する。 独自に`fetchSize`を設定するために必要となる。
  (2) | `fetchSize`を設定する。 `fetchSize`のデフォルト設定が、全件取得になっている場合があるため適切な値を設定すること。 `fetchSize`の設定が全件取得のままだと、`JdbcCodeList`の読み込む件数が大きい場合に、DBからリストを取得する際の処理性能が落ちてしまい、アプリケーションの起動時間が長期化する可能性がある。
  (3) | `JdbcCodeList`の共通bean定義。 他の`JdbcCodeList`の共通部分を設定している。そのため、基本`JdbcCodeList`のbean定義はこのbean定義を親クラスに設定する。 abstract属性をtrueにすることで、このbeanはインスタンス化されない。
  (4) | (1)で設定した`jdbcTemplate`を設定。 `fetchSize`を設定した`JdbcTemplate`を、`JdbcCodeList`に格納している。
  (5) | `JdbcCodeList`のbean定義。 parent属性を(3)のbean定義を親クラスとして設定することで、`fetchSize`を設定した`JdbcCodeList`が設定される。 このbean定義では、クエリに関する設定のみを行い、必要なCodeList分作成する。
  (6) | querySqlプロパティに取得するSQLを記述する。その際、必ず「ORDER BY」を指定し、順序を確定させること。 「ORDER BY」を指定しないと、取得する度に順序が変わってしまう。
  (7) | valueColumnプロパティに、MapのKeyに該当する値を設定する。この例ではauthority_idを設定している。
  (8) | labelColumnプロパティに、MapのValueに該当する値を設定する。この例ではauthority_nameを設定している。

#### 4.9.2.3.2. コードリストの使用

下記に示す設定の詳細について、前述した共通ライブラリから提供しているインタセプターの使用を参照されたい。

jspの実装例

    <form:checkboxes items="${CL_AUTHORITIES}" path="authorities" delimiter="<br />" /> <%-- (9) --%>

出力HTML

    <span>
        <input id="authorities1" name="authorities" type="checkbox" value="01"/>
        <label for="authorities1">STAFF_MANAGEMENT</label>
    </span>
    <span>
        <br />
        <input id="authorities2" name="authorities" type="checkbox" value="02"/>
        <label for="authorities2">MASTER_MANAGEMENT</label>
    </span>
    <span>
        <br />
        <input id="authorities3" name="authorities" type="checkbox" value="03"/>
        <label for="authorities3">STOCK_MANAGEMENT</label>
    </span>
    <span>
        <br />
        <input id="authorities4" name="authorities" type="checkbox" value="04"/>
        <label for="authorities4">ORDER_MANAGEMENT</label>
    </span>
    <span>
        <br />
        <input id="authorities5" name="authorities" type="checkbox" value="05"/>
        <label for="authorities5">SHOW_SHOPPING_CENTER</label>
    </span>

項番 | 説明
--- | ---
(9) | `delimiter`属性の値を`<br />`にして改行させることができる。

テンプレートHTML実装例

    <span th:each="authority : ${CL_AUTHORITIES}" class="checkbox-wrapper"> <!--/* (9) */-->
        <br />
        <input type="checkbox" th:field="*{authorities}" th:value="${authority.key}">
        <label th:for="${#ids.prev('authorities')}" th:text="${authority.value}"></label> <!--/* (10) */-->
    </span>

    .checkbox-wrapper { /* (9) */
        display: block;
    }

項番 | 説明
--- | ---
(9) | CSSのclassセレクタで`display: block;`を指定して改行させることができる。
(10) | `#ids.prev`メソッドを使用して、`input`タグの`id`名と対応付けることができる。詳細は、#ids.prevメソッドについてを参照されたい。

出力HTML

    <span>
        <input type="checkbox" value="01" id="authorities1" name="authorities"/>
        <label for="authorities1">STAFF_MANAGEMENT</label>
    </span>
    <span>
        <input type="checkbox" value="02" id="authorities2" name="authorities"/>
        <label for="authorities2">MASTER_MANAGEMENT</label>
    </span>
    <span>
        <input type="checkbox" value="03" id="authorities3" name="authorities"/>
        <label for="authorities3">STOCK_MANAGEMENT</label>
    </span>
    <span>
        <input type="checkbox" value="03" id="authorities3" name="authorities"/>
        <label for="authorities4">ORDER_MANAGEMENT</label>
    </span>
    <span>
        <input type="checkbox" value="31" id="authorities3" name="authorities"/>
        <label for="authorities5">SHOW_SHOPPING_CENTER</label>
    </span>

出力画面

#### 4.9.2.3.3. Javaクラスでのコードリスト使用

下記に示す設定の詳細について、前述したJavaクラスでのコードリスト使用を参照されたい。

### 4.9.2.4. EnumCodeListの使用方法

`org.terasoluna.gfw.common.codelist.EnumCodeList`は、 `Enum`クラスに定義した定数からコードリストを作成するクラスである。

Note

以下の条件に一致するアプリケーションでコードリストを扱う場合は、`EnumCodeList`を使用して、コードリストのラベルを`Enum`クラスで管理することを検討してほしい。

コードリストのラベルを`Enum`クラスで管理することで、コード値に紐づく情報と操作を`Enum`クラスに集約する事ができる。

- コード値を`Enum`クラスで管理する必要がある(つまり、Javaのロジックでコード値を意識した処理を行う必要がある)

- UIの国際化(多言語化)の必要がない

以下に、`EnumCodeList`の使用イメージを示す。

Note

`EnumCodeList`では、`Enum`クラスからコードリストを作成するために必要な情報(コード値とラベル)を取得するためのインタフェースとして、`org.terasoluna.gfw.common.codelist.EnumCodeList.CodeListItem`インタフェースを提供している。

`EnumCodeList`を使用する場合は、作成する`Enum`クラスで`EnumCodeList.CodeListItem`インタフェースを実装する必要がある。

#### 4.9.2.4.1. コードリスト設定例

Enumクラスの作成

`EnumCodeList`を使用する場合は、`EnumCodeList.CodeListItem`インタフェースを実装した`Enum`クラスを作成する。 以下に作成例を示す。

    package com.example.domain.model;

    import org.terasoluna.gfw.common.codelist.EnumCodeList;

    public enum OrderStatus
        // (1)
        implements EnumCodeList.CodeListItem {

        // (2)
        RECEIVED  ("1", "Received"),
        SENT      ("2", "Sent"),
        CANCELLED ("3","Cancelled");

        // (3)
        private final String value;
        private final String label;

        // (4)
        private OrderStatus(String codeValue, String codeLabel) {
            this.value = codeValue;
            this.label = codeLabel;
        }

        // (5)
        @Override
        public String getCodeValue() {
            return value;
        }

        // (6)
        @Override
        public String getCodeLabel() {
            return label;
        }

    }

項番 | 説明
--- | ---
(1) | コードリストとして使用する`Enum`クラスでは、 共通ライブラリから提供している`org.terasoluna.gfw.common.codelist.EnumCodeList.CodeListItem`インタフェースを実装する。 `EnumCodeList.CodeListItem`インタフェースには、コードリストを作成するために必要な情報(コード値とラベル)を取得するためのメソッドとして、 コード値を取得する`getCodeValue`メソッド ラベルを取得する`getCodeLabel`メソッド が定義されている。
(2) | 定数を定義する。 定数を生成する際に、コードリストを作成するために必要な情報(コード値とラベル)を指定する。 上記例では、以下の3つの定数を定義している。 `RECEIVED`(コード値=”`1`“, ラベル=`Received`) `SENT` (コード値=”`2`“, ラベル=`Sent`) `CANCELLED` (コード値=”`3`“, ラベル=`Cancelled`) Note `EnumCodeList`を使用した際のコードリストの並び順は、定数の定義順となる。
(3) | コードリストを作成するために必要な情報(コード値とラベル)を保持するプロパティを用意する。
(4) | コードリストを作成するために必要な情報(コード値とラベル)を受け取るコンストラクタを用意する。
(5) | 定数が保持するコード値を返却する。 このメソッドは、`EnumCodeList.CodeListItem`インタフェースで定義されているメソッドであり、 `EnumCodeList`が定数からコード値を取得する際に呼び出す。
(6) | 定数が保持するラベルを返却する。 このメソッドは、`EnumCodeList.CodeListItem`インタフェースで定義されているメソッドであり、 `EnumCodeList`が定数からラベルを取得する際に呼び出す。

bean定義ファイルの定義

コードリスト用のbean定義ファイルに、`EnumCodeList`を定義する。
以下に定義例を示す。

- `XxxCodelist.java`

      @Bean("CL_ORDERSTATUS")
      public EnumCodeList clEnumOrderstatus() {
          return new EnumCodeList(OrderStatus.class); // (7)(8)
      }

  項番 | 説明
  --- | ---
  (7) | コードリストの実装クラスとして、`EnumCodeList`クラスを指定する。
  (8) | `EnumCodeList`クラスのコンストラクタに、`EnumCodeList.CodeListItem`インタフェースを実装した`Enum`クラスを指定する。

- `xxx-codelist.xml`

      <bean id="CL_ORDERSTATUS"
            class="org.terasoluna.gfw.common.codelist.EnumCodeList"> <!-- (7) -->
          <constructor-arg value="com.example.domain.model.OrderStatus" /> <!-- (8) -->
      </bean>

  項番 | 説明
  --- | ---
  (7) | コードリストの実装クラスとして、`EnumCodeList`クラスを指定する。
  (8) | `EnumCodeList`クラスのコンストラクタに、`EnumCodeList.CodeListItem`インタフェースを実装した`Enum`クラスのFQCNを指定する。

#### 4.9.2.4.2. コードリストの使用

JSP/テンプレートHTMLでコードリストを使用する方法については、前述した共通ライブラリから提供しているインタセプターの使用を参照されたい。

#### 4.9.2.4.3. Javaクラスでのコードリスト使用

Javaクラスでコードリストを使用する方法については、前述したJavaクラスでのコードリスト使用を参照されたい。

### 4.9.2.5. I18nCodeListの使用方法

`org.terasoluna.gfw.common.codelist.i18n.I18nCodeList`は、国際化に対応しているコードリストである。
ロケール毎にコードリストを設定することで、ロケールに対応したコードリストを返却できる。

`I18nCodeList`の実装クラスとして、`org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList`および`org.terasoluna.gfw.common.codelist.i18n.SimpleReloadableI18nCodeList`を提供している。

I18nCodeList（SimpleI18nCodeList）のイメージ

#### 4.9.2.5.1. コードリスト設定例

`I18nCodeList`は行が`Locale`、列がコード値、セルの内容がラベルである2次元のテーブルをイメージすると理解しやすい。

料金を選択するセレクトボックスの場合を例に挙げると以下のようなテーブルができる。

row=Locale,column=Code | 0 | 10000 | 20000 | 30000 | 40000 | 50000
--- | --- | --- | --- | --- | --- | ---
en | unlimited | Less than \10,000 | Less than \20,000 | Less than \30,000 | Less than \40,000 | Less than \50,000
ja | 上限なし | 10,000円以下 | 20,000円以下 | 30,000円以下 | 40,000円以下 | 50,000円以下

この国際化対応コードリストのテーブルを構築するために`SimpleI18nCodeList`は3つの設定方法を用意している。

- 行単位でLocale毎の`CodeList`を設定する

- 行単位でLocale毎の`java.util.Map`(key=コード値, value=ラベル)を設定する

- 列単位でコード値毎の`java.util.Map`(key=Locale, value=ラベル)を設定する

基本的には、「行単位でLocale毎のCodeListを設定する」方法でコードリストを設定することを推奨する。

`SimpleReloadableI18nCodeList`は更新可能なコードリストを行に持つ以下の設定方法を用意している。

- 行単位でLocale毎の`ReloadableCodeList`（`JdbcCodeList`）を設定する

Note

terasoluna-gfw-common 5.4.2.RELEASEからリロードに対応した`SimpleReloadableI18nCodeList`が追加された。

更新可能なコードリストを行に持つ`SimpleI18nCodeList`を利用している場合は、`SimpleReloadableI18nCodeList`に置き換えることを推奨する。

上記例の料金を選択するセレクトボックスの場合を行単位でLocale毎の`CodeList`を設定する方法について説明する。
他の設定方法については SimpleI18nCodeListのコードリスト設定方法参照されたい。

Bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_I18N_PRICE")
      public SimpleI18nCodeList clI18nPrice() {
          Map<Locale, CodeList> rows =  new LinkedHashMap<>(); // (1)
          rows.put(Locale.ENGLISH, clPriceEn());
          rows.put(Locale.JAPANESE, clPriceJa());
          SimpleI18nCodeList bean = new SimpleI18nCodeList();
          bean.setRowsByCodeList(rows);
          return bean;
      }

  項番 | 説明
  --- | ---
  (1) | rowsByCodeListプロパティにkeyが`java.lang.Locale`のMapを設定する。 Mapには、keyにロケール、valueにロケールに対応したコードリストクラスの参照先を指定する。

- `xxx-codelist.xml`

      <bean id="CL_I18N_PRICE"
          class="org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList">
          <property name="rowsByCodeList"> <!-- (1) -->
              <util:map>
                  <entry key="en" value-ref="CL_PRICE_EN" />
                  <entry key="ja" value-ref="CL_PRICE_JA" />
              </util:map>
          </property>
      </bean>

  項番 | 説明
  --- | ---
  (1) | rowsByCodeListプロパティにkeyが`java.lang.Locale`のMapを設定する。 Mapには、keyにロケール、value-refにロケールに対応したコードリストクラスの参照先を指定する。

Locale毎にSimpleMapCodeListを用意する場合のBean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_I18N_PRICE")
      public SimpleI18nCodeList clI18nPrice() {
          Map<Locale, CodeList> rows =  new LinkedHashMap<>();
          rows.put(Locale.ENGLISH, clPriceEn());
          rows.put(Locale.JAPANESE, clPriceJa());
          SimpleI18nCodeList bean = new SimpleI18nCodeList();
          bean.setRowsByCodeList(rows);
          return bean;
      }

      // (2)
      @Bean("CL_PRICE_EN")
      public SimpleMapCodeList clPriceEn() {
          Map<String, String> enMap = new LinkedHashMap<>();
          enMap.put("0", "unlimited");
          enMap.put("10000", "Less than \\10,000");
          enMap.put("20000", "Less than \\20,000");
          enMap.put("30000", "Less than \\30,000");
          enMap.put("40000", "Less than \\40,000");
          enMap.put("50000", "Less than \\50,000");
          SimpleMapCodeList bean = new SimpleMapCodeList();
          bean.setMap(enMap);
          return bean;
      }

      // (3)
      @Bean("CL_PRICE_JA")
      public SimpleMapCodeList clPriceJa() {
          Map<String, String> jaMap = new LinkedHashMap<>();
          jaMap.put("0", "上限なし");
          jaMap.put("10000", "10,000円以下");
          jaMap.put("20000", "20,000円以下");
          jaMap.put("30000", "30,000円以下");
          jaMap.put("40000", "40,000円以下");
          jaMap.put("50000", "50,000円以下");
          SimpleMapCodeList bean = new SimpleMapCodeList();
          bean.setMap(jaMap);
          return bean;
      }

  項番 | 説明
  --- | ---
  (2) | ロケールが”en”であるbean定義`CL_PRICE_EN`について、コードリストクラスを`SimpleMapCodeList`で設定している。
  (3) | ロケールが”ja”であるbean定義`CL_PRICE_JA`について、コードリストクラスを`SimpleMapCodeList`で設定している。

- `xxx-codelist.xml`

      <bean id="CL_I18N_PRICE"
          class="org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList">
          <property name="rowsByCodeList">
              <util:map>
                  <entry key="en" value-ref="CL_PRICE_EN" />
                  <entry key="ja" value-ref="CL_PRICE_JA" />
              </util:map>
          </property>
      </bean>

      <bean id="CL_PRICE_EN" class="org.terasoluna.gfw.common.codelist.SimpleMapCodeList">  <!-- (2) -->
          <property name="map">
              <util:map>
                  <entry key="0" value="unlimited" />
                  <entry key="10000" value="Less than \10,000" />
                  <entry key="20000" value="Less than \20,000" />
                  <entry key="30000" value="Less than \30,000" />
                  <entry key="40000" value="Less than \40,000" />
                  <entry key="50000" value="Less than \50,000" />
              </util:map>
          </property>
      </bean>

      <bean id="CL_PRICE_JA" class="org.terasoluna.gfw.common.codelist.SimpleMapCodeList">  <!-- (3) -->
          <property name="map">
              <util:map>
                  <entry key="0" value="上限なし" />
                  <entry key="10000" value="10,000円以下" />
                  <entry key="20000" value="20,000円以下" />
                  <entry key="30000" value="30,000円以下" />
                  <entry key="40000" value="40,000円以下" />
                  <entry key="50000" value="50,000円以下" />
              </util:map>
          </property>
      </bean>

  項番 | 説明
  --- | ---
  (2) | ロケールが”en”であるbean定義`CL_PRICE_EN`について、コードリストクラスを`SimpleMapCodeList`で設定している。
  (3) | ロケールが”ja”であるbean定義`CL_PRICE_JA`について、コードリストクラスを`SimpleMapCodeList`で設定している。

Locale毎にJdbcCodeListを用意する場合のBean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_I18N_PRICE")
      public SimpleReloadableI18nCodeList clI18nPrice() {
          Map<Locale, ReloadableCodeList> rows =  new LinkedHashMap<>();
          rows.put(Locale.ENGLISH, clPriceEn());
          rows.put(Locale.JAPANESE, clPriceJa());
          SimpleReloadableI18nCodeList bean = new SimpleReloadableI18nCodeList(); // (4)
          bean.setRowsByCodeList(rows);
          return bean;
      }

      @Bean("CL_PRICE_EN")
      public JdbcCodeList clPriceEn() {
          JdbcCodeList jdbcCodeList = abstractJdbcCodeList(); // (5)
          jdbcCodeList.setQuerySql("SELECT code, label FROM price WHERE locale = 'en' ORDER BY code");
          jdbcCodeList.setValueColumn("code");
          jdbcCodeList.setLabelColumn("label");
          return jdbcCodeList;
      }

      @Bean("CL_PRICE_JA")
      public JdbcCodeList clPriceJa() {
          JdbcCodeList jdbcCodeList = abstractJdbcCodeList(); // (6)
          jdbcCodeList.setQuerySql("SELECT code, label FROM price WHERE locale = 'ja' ORDER BY code");
          jdbcCodeList.setValueColumn("code");
          jdbcCodeList.setLabelColumn("label");
          return jdbcCodeList;
      }

  項番 | 説明
  --- | ---
  (4) | 更新可能なコードリストを行に持つ場合は、`SimpleReloadableI18nCodeList`を利用する。
  (5) | ロケールが”en”であるbean定義`CL_PRICE_EN`について、コードリストクラスを`JdbcCodeList`で設定している。
  (6) | ロケールが”ja”であるbean定義`CL_PRICE_JA`について、コードリストクラスを`JdbcCodeList`で設定している。

- `xxx-codelist.xml`

      <bean id="CL_I18N_PRICE"
          class="org.terasoluna.gfw.common.codelist.i18n.SimpleReloadableI18nCodeList">  <!-- (4) -->
          <property name="rowsByCodeList">
              <util:map>
                  <entry key="en" value-ref="CL_PRICE_EN" />
                  <entry key="ja" value-ref="CL_PRICE_JA" />
              </util:map>
          </property>
      </bean>

      <bean id="CL_PRICE_EN" parent="AbstractJdbcCodeList">  <!-- (5) -->
          <property name="querySql"
              value="SELECT code, label FROM price WHERE locale = 'en' ORDER BY code" />
          <property name="valueColumn" value="code" />
          <property name="labelColumn" value="label" />
      </bean>

      <bean id="CL_PRICE_JA" parent="AbstractJdbcCodeList">  <!-- (6) -->
          <property name="querySql"
              value="SELECT code, label FROM price WHERE locale = 'ja' ORDER BY code" />
          <property name="valueColumn" value="code" />
          <property name="labelColumn" value="label" />
      </bean>

  項番 | 説明
  --- | ---
  (4) | 更新可能なコードリストを行に持つ場合は、`SimpleReloadableI18nCodeList`を利用する。
  (5) | ロケールが”en”であるbean定義`CL_PRICE_EN`について、コードリストクラスを`JdbcCodeList`で設定している。
  (6) | ロケールが”ja”であるbean定義`CL_PRICE_JA`について、コードリストクラスを`JdbcCodeList`で設定している。

テーブル定義(priceテーブル)には以下のデータを投入する。

locale | code | label
--- | --- | ---
en | 0 | unlimited
en | 10000 | Less than \10,000
en | 20000 | Less than \20,000
en | 30000 | Less than \30,000
en | 40000 | Less than \40,000
en | 50000 | Less than \50,000
ja | 0 | 上限なし
ja | 10000 | 10,000円以下
ja | 20000 | 20,000円以下
ja | 30000 | 30,000円以下
ja | 40000 | 40,000円以下
ja | 50000 | 50,000円以下

#### 4.9.2.5.2. I18nCodeListにおけるロケール解決

`I18nCodeList`は要求されたロケールがコードリストに定義されていない場合、以下の順序でロケールの解決を行う。

1. 国と言語を組み合わせたロケール（例：ja_JP）がコードリストに定義されていない場合、対応する言語のみのロケール（例：ja）を使用する。

2. 言語のみのロケールがコードリストに定義されていない場合、デフォルトのロケールを使用する。

デフォルトのロケールは以下の順序で決定する。

1. `fallbackTo`プロパティが指定されている場合は、指定されたロケールを使用する。

2. `fallbackTo`プロパティが指定されていない場合は、JVMインスタンスのデフォルトロケール 、もしくは対応する言語のみのロケールを使用する。

Warning

デフォルトのロケールに対応するコードリストが定義されていなかった場合、Bean生成時にエラーとなりアプリケーションの起動に失敗する。

このため、様々な環境でアプリケーションを運用する場合や、デフォルトとしたいロケールとJVMインスタンスのデフォルトロケールが異なる場合は、`fallbackTo`プロパティを指定することを強く推奨する。

`fallbackTo`プロパティの設定例を以下に示す。

fallbackToプロパティの設定

    @Bean("CL_I18N_PRICE")
    public SimpleI18nCodeList clI18nPrice() {
        Map<Locale, CodeList> rows =  new LinkedHashMap<>();
        rows.put(Locale.ENGLISH, clPriceEn());
        rows.put(Locale.JAPANESE, clPriceJa());
        SimpleI18nCodeList bean = new SimpleI18nCodeList();
        bean.setRowsByCodeList(rows);
        bean.setFallbackTo(Locale.ENGLISH); // (1)
        return bean;
    }

項番 | 説明
--- | ---
(1) | `fallbackTo`プロパティにロケール”en”を設定する。 これにより、要求されたロケールの言語ロケールが”en”、”ja”以外の場合、ロケール”en”が使用される。

    <bean id="CL_I18N_PRICE"
        class="org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList">
        <property name="rowsByCodeList">
            <util:map>
                <entry key="en" value-ref="CL_PRICE_EN" />
                <entry key="ja" value-ref="CL_PRICE_JA" />
            </util:map>
        </property>
        <property name="fallbackTo" value="en" />  <!-- (1) -->
    </bean>

項番 | 説明
--- | ---
(1) | `fallbackTo`プロパティにロケール”en”を設定する。 これにより、要求されたロケールの言語ロケールが”en”、”ja”以外の場合、ロケール”en”が使用される。

#### 4.9.2.5.3. コードリストの使用

JPS/テンプレートHTMLでコードリストを使用する方法については、前述した共通ライブラリから提供しているインタセプターの使用を参照されたい。

Note

`CodeListInterceptor`は、`I18nCodeList#asMap()`メソッドを利用して適切なロケールのコードリストを取得している。

`asMap()`メソッドについてはJavaクラスでのコードリスト使用を参照されたい。

jspの実装例

    <form:select path="basePrice" items="${CL_I18N_PRICE}" />

テンプレートHTML実装例

>     <select th:field="*{basePrice}">
>         <option th:each="price : ${CL_I18N_PRICE}" th:value="${price.key}" th:text="${price.value}"></option>
>     </select>

出力HTML lang=en

    <select id="basePrice" name="basePrice">
        <option value="0">unlimited</option>
        <option value="1">Less than \10,000</option>
        <option value="2">Less than \20,000</option>
        <option value="3">Less than \30,000</option>
        <option value="4">Less than \40,000</option>
        <option value="5">Less than \50,000</option>
    </select>

出力HTML lang=ja

    <select id="basePrice" name="basePrice">
        <option value="0">上限なし</option>
        <option value="1">10,000円以下</option>
        <option value="2">20,000円以下</option>
        <option value="3">30,000円以下</option>
        <option value="4">40,000円以下</option>
        <option value="5">50,000円以下</option>
    </select>

出力画面 lang=en

出力画面 lang=ja

#### 4.9.2.5.4. Javaクラスでのコードリスト使用

`I18nCodeList`からコードリストを取得するには、以下のいずれかのメソッドを使用する。

- `asMap()`メソッド
  `org.springframework.context.i18n.LocaleContextHolder`を利用して適切なロケールのコードリストを取得する。`LocaleContextHolder`は`org.springframework.web.servlet.LocaleResolver`を利用してクライアントから指定されたロケールを取得する。
- `asMap(Locale)`メソッド
  指定されたロケールのコードリストを取得する。

Note

`LocaleResolver`の設定方法については、国際化を参照されたい。

`LocaleResolver`の`defaultLocale`プロパティを指定している場合は、コードリストの`fallbackTo`プロパティに同じロケールを指定することで、`LocaleResolver`で意図したロケールのコードリストを使用させることができる。

`asMap()`メソッドを利用する場合、前述したJavaクラスでのコードリスト使用と同様の方法で実装を行うことができる。

    import org.springframework.stereotype.Service;
    import org.terasoluna.gfw.common.codelist.i18n.I18nCodeList;

    import jakarta.inject.Inject;
    import jakarta.inject.Named;

    @Service
    public class OrderServiceImpl implements OrderService {

        @Inject
        @Named("CL_ORDERSTATUS")
        I18nCodeList orderStatusCodeList;

        public boolean existOrderStatus(String target) {
            return orderStatusCodeList.asMap().containsKey(target); // (1)
        }
    }

項番 | 説明
--- | ---
(1) | `I18nCodeList#asMap()`メソッドでコードリストを`java.util.Map`形式で取得する。

業務要件によって、特定のロケールのコードリストを取得する必要がある場合は`asMap(Locale)`メソッドを使用する

    import java.util.Locale;

    import org.springframework.stereotype.Service;
    import org.terasoluna.gfw.common.codelist.i18n.I18nCodeList;

    import jakarta.inject.Inject;
    import jakarta.inject.Named;

    @Service
    public class OrderServiceImpl implements OrderService {

        @Inject
        @Named("CL_ORDERSTATUS")
        I18nCodeList orderStatusCodeList;

        public boolean existOrderStatus(String target) {
            return orderStatusCodeList.asMap(Locale.ENGLISH).containsKey(target); // (1)
        }
    }

項番 | 説明
--- | ---
(1) | `I18nCodeList#asMap(Locale)`メソッドで、指定したロケールのコードリストを`java.util.Map`形式で取得する。 ここでは `Locale.ENGLISH`（”en”）を指定している。

### 4.9.2.6. 特定のコード値からコード名を表示する

JSP/テンプレートHTMLからコードリストを参照する場合は、`CodeListInterceptor`がリクエストスコープにコードリストを `java.util.Map`で格納しているため、`Map`インターフェースと同じ方法で参照することができる。

コードリストを用いて特定のコード値からコード名を表示する方法について、以下に実装例を示す。

jspの実装例

    Order Status : ${f:h(CL_ORDERSTATUS[orderStatusForm.id])} // (1)

項番 | 説明
--- | ---
(1) | コードリストを定義したbeanID(この例では`CL_ORDERSTATUS`) を属性名として、コードリスト(`java.util.Map`インタフェース)を取得する。 取得した`Map`インタフェースのキーとしてコード値(この例では`orderStatusForm.id`に格納された値) を指定することで、対応するコード名を表示することができる。

テンプレートHTML実装例

    <span th:text="${orderStatusForm.id} != null ? |Order Status : ${CL_ORDERSTATUS.get(orderStatusForm.id)}|"></span> <!--/* (1) */-->

項番 | 説明
--- | ---
(1) | コードリストを定義したbeanID(この例では`CL_ORDERSTATUS`) を属性名として、コードリスト(`java.util.Map`インタフェース)を取得する。 取得した`Map`インタフェースのキーとしてコード値(この例では`orderStatusForm.id`に格納された値) を指定することで、対応するコード名を表示することができる。 キーとして利用する変数値は必ず`null`チェックを行うことを推奨する。詳細は、テンプレートエンジン(Thymeleaf)のSpEL評価時におけるnull-safetyの影響についてを参照されたい。

### 4.9.2.7. コードリストを用いたコード値の入力チェック

入力値がコードリスト内に定義されたコード値であるかどうかチェックするような場合、 共通ライブラリでは、BeanValidation用のアノテーション、`org.terasoluna.gfw.common.codelist.ExistInCodeList`を提供している。

BeanValidationや、メッセージ出力方法の詳細については、入力チェックを参照されたい。

#### 4.9.2.7.1. @ExistInCodeList の設定例

コードリストを用いた入力チェック方法について、以下に実装例を示す。

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_GENDER")
      public SimpleMapCodeList clGender() {
          SimpleMapCodeList bean = new SimpleMapCodeList();
          Map<String, String> map = new LinkedHashMap<>();
          map.put("M", "Male");
          map.put("F", "Femal");
          bean.setMap(map);
          return bean;
      }

- `xxx-codelist.xml`

      <bean id="CL_GENDER" class="org.terasoluna.gfw.common.codelist.SimpleMapCodeList">
          <property name="map">
              <map>
                  <entry key="M" value="Male" />
                  <entry key="F" value="Female" />
              </map>
          </property>
      </bean>

Formオブジェクト

    public class Person {
        @ExistInCodeList(codeListId = "CL_GENDER")  // (1)
        private String gender;

        // getter and setter omitted
    }

項番 | 説明
--- | ---
(1) | 入力チェックを行いたいフィールドに対して、`@ExistInCodeList`アノテーションを設定し、 codeListIdにチェック元となる、コードリストを指定する。

上記の結果、`gender`にM、F以外の文字が格納されている場合、エラーになる。

Note

terasoluna-gfw-common 5.4.2.RELEASEから、`@ExistInCodeList`の入力チェックの対象として、 `CharSequence`インタフェースの実装クラス(`String`など) または `Character`に加え、`Number`継承クラス（`Integer`など）をサポートするよう変更された。

`NumberRangeCodeList`の `valueFormat`プロパティを指定している場合、 `Number`型フィールドの値を当該プロパティを利用してフォーマットした値がコードリストに存在することをチェックする。

## 4.9.3. How to extend

### 4.9.3.1. コードリストをリロードする場合

前述した共通ライブラリで提供しているコードリストは、アプリケーション起動時に読み込まれ、それ以降は、基本的に更新されない。
しかし、コードリストのマスタデータを更新した時、コードリストも更新したい場合がある。

例：JdbcCodeListを使用して、DBのマスタを変更した時にコードリストの更新を行う場合。

共通ライブラリでは、コードリストを更新可能とするインタフェースを提供している。

1. `org.terasoluna.gfw.common.codelist.ReloadableCodeList`：コードリストを更新する

2. `org.terasoluna.gfw.common.codelist.i18n.ReloadableI18nCodeList`：行に持つコードリストを含むコードリストを更新する

コードリストの更新方法としては、以下2点の方法がある。

1. Task Schedulerで実現する方法

2. Controller(Service)クラスでrefreshメソッドを呼び出す方法

本ガイドラインでは、Springから提供されているTask Schedulerを使用して、コードリストを定期的にリロードする方式を基本的に推奨する。

ただし、任意のタイミングでコードリストをリフレッシュする必要がある場合はControllerクラスでrefreshメソッドを呼び出す方法で実現すればよい。

Note

`ReloadableCodeList`および`ReloadableI18nCodeList`インターフェースを実装しているコードリストについては、コードリスト種類一覧を参照されたい。

#### 4.9.3.1.1. Task Schedulerで実現する方法

Task Schedulerの設定例について、以下に示す。

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      public class XxxCodelistConfig implements SchedulingConfigurer { // (2)

          @Value("${cron.codelist.refreshTime}")
          private String codelistRefreshTime;

          @Override
          public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
              taskRegistrar.setScheduler(taskScheduler()); // (2)
              taskRegistrar.addTriggerTask(() -> clCronRefreshCodelist().refresh(), // (3)
                      new CronTrigger(codelistRefreshTime)); // (3)
          }

          // (1)
          @Bean("taskScheduler")
          public Executor taskScheduler() {
              return Executors.newScheduledThreadPool(10);
          }

          @Bean("CL_CRON_REFRESH_CODELIST")
          public JdbcCodeList clCronRefreshCodelist() {
              JdbcCodeList jdbcCodeList = abstractJdbcCodeList();
              jdbcCodeList.setQuerySql(
                      "SELECT authority_id, authority_name FROM t_authority ORDER BY authority_id");
              jdbcCodeList.setValueColumn("authority_id");
              jdbcCodeList.setLabelColumn("authority_name");
              return jdbcCodeList;
          }

  項番 | 説明
  --- | ---
  (1) | TaskSchedulerを定義する。引数にはスレッドのプールサイズを指定する。
  (2) | `SchedulingConfigurer`の`configureTasks`メソッドを実装し、schedulerに、(1)で定義したTaskSchedulerを設定する。
  (3) | 実行するTaskを定義する。 ここでは`JdbcCodeList#refresh`を定義している。 cron属性に、`org.springframework.scheduling.support.CronExpression`でサポートされた形式で記述すること。 cron属性は開発環境、商用環境など環境によってリロードするタイミングが変わることが想定されるため、プロパティファイルや、環境変数等から取得することを推奨する。 cron属性の設定例 「秒 分 時 月 年 曜日」で指定する。 毎秒実行 「* * * * * *」 毎時実行 「0 0 * * * *」 平日の9-17時の毎時実行 「0 0 9-17 * * MON-FRI」 cronの指定値の詳細については、CronExpressionのJavaDocを参照されたい。

- `xxx-codelist.xml`

      <task:scheduler id="taskScheduler" pool-size="10"/>  <!-- (1) -->

      <task:scheduled-tasks scheduler="taskScheduler">  <!-- (2) -->
          <task:scheduled ref="CL_AUTHORITIES" method="refresh" cron="${cron.codelist.refreshTime}"/>  <!-- (3) -->
      </task:scheduled-tasks>

      <bean id="CL_AUTHORITIES" parent="AbstractJdbcCodeList">
          <property name="querySql"
              value="SELECT authority_id, authority_name FROM authority ORDER BY authority_id" />
          <property name="valueColumn" value="authority_id" />
          <property name="labelColumn" value="authority_name" />
      </bean>

  項番 | 説明
  --- | ---
  (1) | `<task:scheduler>`の要素を定義する。pool-size属性にスレッドのプールサイズを指定する。 pool-size属性を指定しない場合、”1” が設定される。
  (2) | `<task:scheduled-tasks>`の要素を定義し、scheduler属性に、`<task:scheduler>`のIDを設定する。
  (3) | `<task:scheduled>`要素を定義する。method属性に、refreshメソッドを指定する。 cron属性に、`org.springframework.scheduling.support.CronExpression`でサポートされた形式で記述すること。 cron属性は開発環境、商用環境など環境によってリロードするタイミングが変わることが想定されるため、プロパティファイルや、環境変数等から取得することを推奨する。 cron属性の設定例 「秒 分 時 月 年 曜日」で指定する。 毎秒実行 「* * * * * *」 毎時実行 「0 0 * * * *」 平日の9-17時の毎時実行 「0 0 9-17 * * MON-FRI」 cronの指定値の詳細については、CronExpressionのJavaDocを参照されたい。

#### 4.9.3.1.2. Controller(Service)クラスでrefreshメソッドを呼び出す方法

refreshメソッドを直接呼び出す場合について、JdbcCodeListのrefreshメソッドをServiceクラスで呼び出す場合の実装例を、以下に示す。

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_AUTHORITIES")
      public JdbcCodeList clAuthorities() {
          JdbcCodeList jdbcCodeList = abstractJdbcCodeList();
          jdbcCodeList.setQuerySql("SELECT authority_id, authority_name FROM t_authority ORDER BY authority_id");
          jdbcCodeList.setValueColumn("authority_id");
          jdbcCodeList.setLabelColumn("authority_name");
          return jdbcCodeList;
      }

- `xxx-codelist.xml`

      <bean id="CL_AUTHORITIES" parent="AbstractJdbcCodeList">
          <property name="querySql"
              value="SELECT authority_id, authority_name FROM authority ORDER BY authority_id" />
          <property name="valueColumn" value="authority_id" />
          <property name="labelColumn" value="authority_name" />
      </bean>

Controllerクラス

    @Controller
    @RequestMapping(value = "codelist")
    public class CodeListController {

        @Inject
        CodeListService codeListService; // (1)

        @RequestMapping(method = RequestMethod.GET, params = "refresh")
        public String refreshJdbcCodeList() {
            codeListService.refresh(); // (2)
            return "codelist/jdbcCodeList";
        }
    }

項番 | 説明
--- | ---
(1) | ReloadableCodeListクラスのrefreshメソッドを実行するServiceクラスをインジェクションする。
(2) | ReloadableCodeListクラスのrefreshメソッドを実行するServiceクラスのrefreshメソッドを実行する。

Serviceクラス

以下は実装クラスのみ記述し、インターフェースクラスは省略。

    @Service
    public class CodeListServiceImpl implements CodeListService { // (3)

        @Inject
        @Named(value = "CL_AUTHORITIES") // (4)
        ReloadableCodeList codeListItem; // (5)

        @Override
        public void refresh() { // (6)
            codeListItem.refresh(); // (7)
        }
    }

項番 | 説明
--- | ---
(3) | 実装クラス`CodeListServiceImpl`は、インターフェース`CodeListService`を実装する。
(4) | コードリストをインジェクションするとき、`@Named`で、該当するコードリストを指定する。 value属性に取得したいbeanのIDを指定すること。 Bean定義ファイルに定義されているbeanタグのID属性”CL_AUTHORITIES”のコードリストがインジェクションされる。
(5) | フィールドの型にReloadableCodeListインターフェースを定義すること。 (4)で指定したBeanはReloadableCodeListインターフェースを実装していること。
(6) | Serviceクラスで定義したrefreshメソッド。 Controllerクラスから呼び出されている。
(7) | ReloadableCodeListインターフェースを実装したコードリストのrefreshメソッド。 refreshメソッドを実行することで、コードリストが更新される。

Note

terasoluna-gfw-common 5.4.2.RELEASEで追加された`SimpleReloadableI18nCodeList`では、refreshメソッドで行に持つすべてのReloadableCodeListを更新することが可能である。

アプリケーションの実装によっては、行に持つReloadableCodeListが更新されている前提で`SimpleReloadableI18nCodeList`のみ更新すれば良い場合もあり得る。

この場合は、`ReloadableI18nCodeList#refresh(boolean)`メソッドの引数に`false`をセットして実行すれば良い。

### 4.9.3.2. コードリストを独自カスタマイズする方法

共通ライブラリで提供している4種類のコードリストで実現できないコードリストを作成したい場合、コードリストを独自にカスタマイズすることができる。
独自カスタマイズする場合、作成できるコードリストの種類と実装方法について、以下の表に示す。

項番 | Reloadable | 継承するクラス | 実装箇所
--- | --- | --- | ---
(1) | 不要 | `org.terasoluna.gfw.common.codelist.AbstractCodeList` | `asMap`をオーバライド
(2) | 必要 | `org.terasoluna.gfw.common.codelist.AbstractReloadableCodeList` | `retrieveMap`をオーバライド

`org.terasoluna.gfw.common.codelist.CodeList`、`org.terasoluna.gfw.common.codelist.ReloadableCodeList`インターフェースを直接実装しても実現はできるが、共通ライブラリで提供されている抽象クラスを拡張することで、最低限の実装で済む。

以下に、独自カスタマイズの実例について示す。
例として、今年と来年の年のリストを作るコードリストについて説明する。
(例：今年が2013の場合、コードリストには、”2013、2014”の順で格納される。)

コードリストクラス

    package com.example.sample.domain.codelist;

    // omitted

    public class DepYearCodeList extends AbstractCodeList { // (1)

        private ClockFactory clockFactory;

        public void setClockFactory(ClockFactory clockFactory) { //(2)
            this.clockFactory = clockFactory;
        }

        @Override
        public Map<String, String> asMap() {  // (3)
            Clock clock = clockFactory.fixed();
            LocalDateTime localDateTime = LocalDateTime.now(clock);
            LocalDateTime nextYearDateTime = localDateTime.plusYears(1);

            Map<String, String> depYearMap = new LinkedHashMap<>();

            String thisYear = String.valueOf(localDateTime.getYear());
            String nextYear = String.valueOf(nextYearDateTime.getYear());

            depYearMap.put(thisYear, thisYear);
            depYearMap.put(nextYear, nextYear);

            return Collections.unmodifiableMap(depYearMap);
        }
    }

項番 | 説明
--- | ---
(1) | `AbstractCodeList`を継承する。 今年と来年の年のリストを作る時、動的にシステム日付から算出して作成しているため、リロードは不要。
(2) | システム日付のClockクラスを作成する`org.terasoluna.gfw.common.time.ClockFactory`をインジェクションするためのセッターを用意する。 `ClockFactory`を利用してシステム日時を取得することができる。
(3) | `asMap`メソッドをオーバライドして、今年と来年の年のリストを作成する。 作成したいコードリスト毎に実装が異なる。

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      // (1)
      @Bean("CL_YEAR")
      public DepYearCodeList clYearCodelist(ClockFactory clockFactory) {
          DepYearCodeList bean = new DepYearCodeList(); // (1)
          bean.setClockFactory(clockFactory); // (2)
          return bean;
      }

  項番 | 説明
  --- | ---
  (1) | 作成したコードリストクラスをbean定義する。 id に`CL_YEAR`を指定することで、bean定義で設定した`CodeListInterceptor`によりコードリストをコンポーネント登録する。
  (2) | システム日付のClockクラスを作成する`ClockFactory`を設定する。 事前に、bean定義ファイルにDataFactory実装クラスを設定する必要がある。

- `xxx-codelist.xml`

      <bean id="CL_YEAR" class="com.example.sample.domain.codelist.DepYearCodeList"> <!-- (1) -->
          <property name="clockFactory" ref="clockFactory" /> <!-- (2) -->
      </bean>

  項番 | 説明
  --- | ---
  (1) | 作成したコードリストクラスをbean定義する。 id に`CL_YEAR`を指定することで、bean定義で設定した`CodeListInterceptor`によりコードリストをコンポーネント登録する。
  (2) | システム日付のClockクラスを作成する`ClockFactory`を設定する。 事前に、bean定義ファイルにDataFactory実装クラスを設定する必要がある。

jspの実装例

    <form:select path="mostRecentYear" items="${CL_YEAR}" /> <!-- (1) -->

項番 | 説明
--- | ---
(1) | items属性にコンポーネント登録した`CL_YEAR`を`${}`プレースホルダー で指定することで、該当のコードリストを取得することができる。

テンプレートHTML実装例

    <select th:field="*{mostRecentYear}">
        <option th:each="recentYear : ${CL_YEAR}" th:value="${recentYear.key}" th:text="${recentYear.value}"></option> <!--/* (1) */-->
    </select>

項番 | 説明
--- | ---
(1) | コンポーネント登録した`CL_YEAR`を 変数式`${}`で指定することで、該当のコードリストを取得することができる。

出力HTML

    <select id="mostRecentYear" name="mostRecentYear">
        <option value="2013">2013</option>
        <option value="2014">2014</option>
    </select>

出力画面

Note

リロード可能であるCodeListを独自カスタマイズする場合、スレッドセーフになるように実装すること。

## 4.9.4. Appendix

### 4.9.4.1. SimpleI18nCodeListのコードリスト設定方法

SimpleI18nCodeListのコードリスト設定について、I18nCodeListの使用方法で設定されているコードリスト設定の他に2つ設定方法がある。
料金を選択するセレクトボックスの場合の例を用いて、それぞれの設定方法を説明する。

#### 4.9.4.1.1. 行単位でLocale毎の`java.util.Map`(key=コード値, value=ラベル)を設定する

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_I18N_PRICE")
      public SimpleI18nCodeList clI18nPriceMapLocale() {
          Map<Locale, Map<String, String>> rowsMap = new LinkedHashMap<>();
          Map<String, String> enMap = new LinkedHashMap<>();
          enMap.put("0", "unlimited");
          enMap.put("10000", "Less than \\10,000");
          enMap.put("20000", "Less than \\20,000");
          enMap.put("30000", "Less than \\30,000");
          enMap.put("40000", "Less than \\40,000");
          enMap.put("50000", "Less than \\50,000");
          Map<String, String> jaMap = new LinkedHashMap<>();
          jaMap.put("0", "上限なし");
          jaMap.put("10000", "10,000円以下");
          jaMap.put("20000", "20,000円以下");
          jaMap.put("30000", "30,000円以下");
          jaMap.put("40000", "40,000円以下");
          jaMap.put("50000", "50,000円以下");
          rowsMap.put(Locale.ENGLISH, enMap);
          rowsMap.put(Locale.JAPANESE, jaMap);
          SimpleI18nCodeList bean = new SimpleI18nCodeList();
          bean.setRows(rowsMap); // (1)
          bean.setFallbackTo(Locale.ENGLISH);
          return bean;
      }

  項番 | 説明
  --- | ---
  (1) | rowsプロパティに対して、”MapのMap”を設定する。外側のMapのkeyは`java.lang.Locale`である。 内側のMapのkeyはコード値、valueはロケールに対応したラベルである。

- `xxx-codelist.xml`

      <bean id="CL_I18N_PRICE"
          class="org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList">
          <property name="rows"> <!-- (1) -->
              <util:map>
                  <entry key="en">
                      <util:map>
                          <entry key="0" value="unlimited" />
                          <entry key="10000" value="Less than \10,000" />
                          <entry key="20000" value="Less than \20,000" />
                          <entry key="30000" value="Less than \30,000" />
                          <entry key="40000" value="Less than \40,000" />
                          <entry key="50000" value="Less than \50,000" />
                      </util:map>
                  </entry>
                  <entry key="ja">
                      <util:map>
                          <entry key="0" value="上限なし" />
                          <entry key="10000" value="10,000円以下" />
                          <entry key="20000" value="20,000円以下" />
                          <entry key="30000" value="30,000円以下" />
                          <entry key="40000" value="40,000円以下" />
                          <entry key="50000" value="50,000円以下" />
                      </util:map>
                  </entry>
              </util:map>
          </property>
      </bean>

  項番 | 説明
  --- | ---
  (1) | rowsプロパティに対して、”MapのMap”を設定する。外側のMapのkeyは`java.lang.Locale`である。 内側のMapのkeyはコード値、valueはロケールに対応したラベルである。

#### 4.9.4.1.2. 列単位でコード値毎の`java.util.Map`(key=Locale, value=ラベル)を設定する

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_I18N_PRCL_I18N_PRICEICE_MAP_CODE")
      public SimpleI18nCodeList clI18nPriceMapCode() {
          Map<String, Map<Locale, String>> columnsMap = new LinkedHashMap<>();
          Map<Locale, String> key1Map = new LinkedHashMap<>();
          key1Map.put(Locale.ENGLISH, "unlimited");
          key1Map.put(Locale.JAPANESE, "上限なし");
          Map<Locale, String> key2Map = new LinkedHashMap<>();
          key2Map.put(Locale.ENGLISH, "Less than \\10,000");
          key2Map.put(Locale.JAPANESE, "10,000円以下");
          Map<Locale, String> key3Map = new LinkedHashMap<>();
          key3Map.put(Locale.ENGLISH, "Less than \\20,000");
          key3Map.put(Locale.JAPANESE, "20,000円以下");
          Map<Locale, String> key4Map = new LinkedHashMap<>();
          key4Map.put(Locale.ENGLISH, "Less than \\30,000");
          key4Map.put(Locale.JAPANESE, "30,000円以下");
          Map<Locale, String> key5Map = new LinkedHashMap<>();
          key5Map.put(Locale.ENGLISH, "Less than \\50,000");
          key5Map.put(Locale.JAPANESE, "40,000円以下");
          Map<Locale, String> key6Map = new LinkedHashMap<>();
          key6Map.put(Locale.ENGLISH, "Less than \\50,000");
          key6Map.put(Locale.JAPANESE, "50,000円以下");

          columnsMap.put("0", key1Map);
          columnsMap.put("10000", key2Map);
          columnsMap.put("20000", key3Map);
          columnsMap.put("30000", key4Map);
          columnsMap.put("40000", key5Map);
          columnsMap.put("50000", key6Map);
          SimpleI18nCodeList bean = new SimpleI18nCodeList();
          bean.setColumns(columnsMap); // (1)
          bean.setFallbackTo(Locale.ENGLISH);
          return bean;
      }

  項番 | 説明
  --- | ---
  (1) | columnsプロパティに対して、”MapのMap”を設定する。外側のMapのkeyはコード値である。 内側のMapのkeyは`java.lang.Locale`、valueはロケールに対応したラベルである。

- `xxx-codelist.xml`

      <bean id="CL_I18N_PRICE"
          class="org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList">
          <property name="columns"> <!-- (1) -->
              <util:map>
                  <entry key="0">
                      <util:map>
                          <entry key="en" value="unlimited" />
                          <entry key="ja" value="上限なし" />
                      </util:map>
                  </entry>
                  <entry key="10000">
                      <util:map>
                          <entry key="en" value="Less than \10,000" />
                          <entry key="ja" value="10,000円以下" />
                      </util:map>
                  </entry>
                  <entry key="20000">
                      <util:map>
                          <entry key="en" value="Less than \20,000" />
                          <entry key="ja" value="20,000円以下" />
                      </util:map>
                  </entry>
                  <entry key="30000">
                      <util:map>
                          <entry key="en" value="Less than \30,000" />
                          <entry key="ja" value="30,000円以下" />
                      </util:map>
                  </entry>
                  <entry key="40000">
                      <util:map>
                          <entry key="en" value="Less than \40,000" />
                          <entry key="ja" value="40,000円以下" />
                      </util:map>
                  </entry>
                  <entry key="50000">
                      <util:map>
                          <entry key="en" value="Less than \50,000" />
                          <entry key="ja" value="50,000円以下" />
                      </util:map>
                  </entry>
              </util:map>
          </property>
      </bean>

  項番 | 説明
  --- | ---
  (1) | columnsプロパティに対して、”MapのMap”を設定する。外側のMapのkeyはコード値である。 内側のMapのkeyは`java.lang.Locale`、valueはロケールに対応したラベルである。

### 4.9.4.2. NumberRangeCodeListのバリエーション

#### 4.9.4.2.1. 降順のNumberRangeCodeListの作成

次に、Toの値をFromの値より小さくする(To < From)場合の実装例を、以下に示す。

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_BIRTH_YEAR")
      public NumberRangeCodeList clBirthYear() {
          NumberRangeCodeList bean = new NumberRangeCodeList();
          bean.setFrom(2013); // (1)
          bean.setTo(2000); // (2)
          return bean;
      }

  項番 | 説明
  --- | ---
  (1) | 範囲開始の値を指定する。setToメソッドの引数の値より大きい値を指定する。 この指定によって、interval分減少した値を、From～Toの範囲分のリストとして、降順に表示する。 intervalは設定していないため、デフォルトの値1が適用される。
  (2) | 範囲終了の値を設定する。 本例では、2000を指定することにより、リストには2013～2000までの範囲で1ずつ減少して格納される。

- `xxx-codelist.xml`

      <bean id="CL_BIRTH_YEAR"
          class="org.terasoluna.gfw.common.codelist.NumberRangeCodeList">
          <property name="from" value="2013" /> <!-- (1) -->
          <property name="to" value="2000" /> <!-- (2) -->
      </bean>

  項番 | 説明
  --- | ---
  (1) | 範囲開始の値を指定する。name属性”to”のvalue属性の値より大きい値を指定する。 この指定によって、interval分減少した値を、From～Toの範囲分のリストとして、降順に表示する。 intervalは設定していないため、デフォルトの値1が適用される。
  (2) | 範囲終了の値を設定する。 本例では、2000を指定することにより、リストには2013～2000までの範囲で1ずつ減少して格納される。

jspの実装例

    <form:select path="birthYear" items="${CL_BIRTH_YEAR}" />

テンプレートHTML実装例

    <select th:field="*{birthYear}">
      <option th:each="birthYear : ${CL_BIRTH_YEAR}" th:value="${birthYear.key}" th:text="${birthYear.value}"></option>
    </select>

出力HTML

    <select id="birthYear" name="birthYear">
        <option value="2013">2013</option>
        <option value="2012">2012</option>
        <option value="2011">2011</option>
        <option value="2010">2010</option>
        <option value="2009">2009</option>
        <option value="2008">2008</option>
        <option value="2007">2007</option>
        <option value="2006">2006</option>
        <option value="2005">2005</option>
        <option value="2004">2004</option>
        <option value="2003">2003</option>
        <option value="2002">2002</option>
        <option value="2001">2001</option>
        <option value="2000">2000</option>
    </select>

出力画面

#### 4.9.4.2.2. NumberRangeCodeListのインターバルの変更

次に、interval値を設定する場合の実装例を、以下に示す。

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_BULK_ORDER_QUANTITY_UNIT")
      public NumberRangeCodeList clBulkOrderQuantityUnit() {
          NumberRangeCodeList bean = new NumberRangeCodeList();
          bean.setFrom(10);
          bean.setTo(50);
          bean.setInterval(10); // (1)
          return bean;
      }

  項番 | 説明
  --- | ---
  (1) | 増加(減少)値を指定する。この指定によって、interval値を増加(減少)した値を、From～Toの範囲内でコードリストとして格納する。 上記の例だと、コードリストには`10`,`20`,`30`,`40`,`50`の順で格納される。

- `xxx-codelist.xml`

      <bean id="CL_BULK_ORDER_QUANTITY_UNIT"
          class="org.terasoluna.gfw.common.codelist.NumberRangeCodeList">
          <property name="from" value="10" />
          <property name="to" value="50" />
          <property name="interval" value="10" /> <!-- (1) -->
      </bean>

  項番 | 説明
  --- | ---
  (1) | 増加(減少)値を指定する。この指定によって、interval値を増加(減少)した値を、From～Toの範囲内でコードリストとして格納する。 上記の例だと、コードリストには`10`,`20`,`30`,`40`,`50`の順で格納される。

jspの実装例

    <form:select path="quantity" items="${CL_BULK_ORDER_QUANTITY_UNIT}" />

テンプレートHTML実装例

    <select th:field="*{quantity}">
      <option th:each="quantity : ${CL_BULK_ORDER_QUANTITY_UNIT}" th:value="${quantity.key}" th:text="${quantity.value}"></option>
    </select>

出力HTML

    <select id="quantity" name="quantity">
        <option value="10">10</option>
        <option value="20">20</option>
        <option value="30">30</option>
        <option value="40">40</option>
        <option value="50">50</option>
    </select>

出力画面

Note

interval値分増加(減少)した値が、From～Toの範囲を超えた場合は、コードリストに格納されない。

具体的には、

>     @Bean("CL_BULK_ORDER_QUANTITY_UNIT")
>     public NumberRangeCodeList clBulkOrderQuantityUnit() {
>         NumberRangeCodeList bean = new NumberRangeCodeList();
>         bean.setFrom(10);
>         bean.setTo(55);
>         bean.setInterval(10);
>         return bean;
>     }

>     <bean id="CL_BULK_ORDER_QUANTITY_UNIT"
>         class="org.terasoluna.gfw.common.codelist.NumberRangeCodeList">
>         <property name="from" value="10" />
>         <property name="to" value="55" />
>         <property name="interval" value="10" />
>     </bean>

という定義を行った場合、

コードリストには`10`,`20`,`30`,`40`,`50`の計5つが格納される。

次のintervalである`60`及び範囲の閾値である`55`はコードリストに格納されない。

### 4.9.4.3. JSP/テンプレートHTMLから直接コードリストBeanを参照する

共通ライブラリから提供しているインタセプターの使用では、Spring MVCを経由する全てのリクエストに対して、`CodeListIntercepter`がコードリストのBeanをリクエスト属性として登録するため、コードリストの数が多くなるとリクエスト毎のオーバーヘッドの増加が懸念される。

ここでは、リクエスト毎のオーバーヘッドの増加を防ぐ方法の一つとして、コードリストBeanをJSPから直接参照する方法を紹介する。
JSP/ThymeleafではSpELを利用して直接Beanを参照することができるが、こちらを利用することでオーバーヘッドの増加を防止することができる。
いずれの方法を利用するかは、プロジェクトの要件によって適切に検討されたい。

bean定義ファイルの定義

- `SpringMvcConfig.java`

      @EnableAspectJAutoProxy
      @EnableWebMvc
      @Configuration
      public class SpringMvcConfig implements WebMvcConfigurer {

        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(codeListInterceptor()).addPathPatterns("/**"); // (1)
        }

        // (1)
        @Bean
        public CodeListInterceptor codeListInterceptor() {
            CodeListInterceptor codeListInterceptor = new CodeListInterceptor();
            codeListInterceptor.setCodeListIdPattern(Pattern.compile("CL_.+"));
            return codeListInterceptor;
        }

  項番 | 説明
  --- | ---
  (1) | `CodeListInterceptor`の設定があれば、削除する。

- `spring-mvc.xml`

      <mvc:interceptors>
          <mvc:interceptor>
              <mvc:mapping path="/**" />
              <bean class="org.terasoluna.gfw.web.codelist.CodeListInterceptor"> <!-- (1) -->
                  <property name="codeListIdPattern" value="CL_.+" />
              </bean>
          </mvc:interceptor>

      <!-- omitted -->

      </mvc:interceptors>

  項番 | 説明
  --- | ---
  (1) | `CodeListInterceptor`の設定があれば、削除する。

bean定義ファイルの定義

- `XxxCodelistConfig.java`

      @Bean("CL_ORDERSTATUS")
      public SimpleMapCodeList clOrderstatus() {
          Map<String, String> codeMap = new LinkedHashMap<>();
          codeMap.put("1", "Received");
          codeMap.put("2", "Sent");
          codeMap.put("3", "Cancelled");
          SimpleMapCodeList bean = new SimpleMapCodeList();
          bean.setMap(codeMap);
          return bean;
      }

- `xxx-codelist.xml`

      <bean id="CL_ORDERSTATUS" class="org.terasoluna.gfw.common.codelist.SimpleMapCodeList">
          <property name="map">
              <util:map>
                  <entry key="1" value="Received" />
                  <entry key="2" value="Sent" />
                  <entry key="3" value="Cancelled" />
              </util:map>
          </property>
      </bean>

jspの実装例

    <spring:eval var="statuses" expression="@CL_ORDERSTATUS.asMap()"/> <!-- (1) -->
    <form:select items="${statuses}" path="orderStatus" />

項番 | 説明
--- | ---
(1) | SpELにより取得した`CodeList`型Beanの`asMap`メソッドにより、`Map`形式で取得することができる。

テンプレートHTML実装例

    <select th:field="*{orderStatus}">
        <option th:each="order : ${@CL_ORDERSTATUS.asMap()}" th:value="${order.key}" th:text="${order.value}"></option> <!--/* (1) */-->
    </select>

項番 | 説明
--- | ---
(1) | 変数式により取得したコードリストBeanのasMapメソッドにより、Map形式で参照することができる。 なお、`SimpleI18nCodeList` の場合は、`asMap`メソッドの引数として`Locale`を渡す必要がある。

出力HTML

    <select id="orderStatus" name="orderStatus">
        <option value="1">Received</option>
        <option value="2">Sent</option>
        <option value="3">Cancelled</option>
    </select>

#### 4.9.4.3.1. SimpleI18nCodeListをテンプレートHTMLから直接参照する方法

ここでは、`CodeListInterceptor`の実装と同様に、リクエストのロケールに対するコードリストに定義されていなかった場合、デフォルトで設定したロケールに対するコードリストを表示する例を紹介する。

bean定義ファイルの定義

`SpringMvcConfig.java`/`spring-mvc.xml`はbean定義ファイルの定義と同様なため割愛する。

    @Bean("CL_I18N_PRICE")
    public SimpleI18nCodeList clI18nPrice() {
        Map<Locale, CodeList> rows =  new LinkedHashMap<>();
        rows.put(Locale.ENGLISH, clPriceEn());
        rows.put(Locale.JAPANESE, clPriceJa());
        SimpleI18nCodeList bean = new SimpleI18nCodeList();
        bean.setRowsByCodeList(rows);
        return bean;
    }

    @Bean("CL_PRICE_EN")
    public SimpleMapCodeList clPriceEn() {
        Map<String, String> enMap = new LinkedHashMap<>();
        enMap.put("0", "unlimited");
        enMap.put("10000", "Less than \\10,000");
        enMap.put("20000", "Less than \\20,000");
        enMap.put("30000", "Less than \\30,000");
        enMap.put("40000", "Less than \\40,000");
        enMap.put("50000", "Less than \\50,000");
        SimpleMapCodeList bean = new SimpleMapCodeList();
        bean.setMap(enMap);
        return bean;
    }

    @Bean("CL_PRICE_JA")
    public SimpleMapCodeList clPriceJa() {
        Map<String, String> jaMap = new LinkedHashMap<>();
        jaMap.put("0", "上限なし");
        jaMap.put("10000", "10,000円以下");
        jaMap.put("20000", "20,000円以下");
        jaMap.put("30000", "30,000円以下");
        jaMap.put("40000", "40,000円以下");
        jaMap.put("50000", "50,000円以下");
        SimpleMapCodeList bean = new SimpleMapCodeList();
        bean.setMap(jaMap);
        return bean;
    }

    <bean id="CL_I18N_PRICE"
        class="org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList">
        <property name="rowsByCodeList">
            <util:map>
                <entry key="en" value-ref="CL_PRICE_EN" />
                <entry key="ja" value-ref="CL_PRICE_JA" />
            </util:map>
        </property>
    </bean>

    <bean id="CL_PRICE_EN" class="org.terasoluna.gfw.common.codelist.SimpleMapCodeList">
        <property name="map">
            <util:map>
                <entry key="0" value="unlimited" />
                <entry key="10000" value="Less than \10,000" />
                <entry key="20000" value="Less than \20,000" />
                <entry key="30000" value="Less than \30,000" />
                <entry key="40000" value="Less than \40,000" />
                <entry key="50000" value="Less than \50,000" />
            </util:map>
        </property>
    </bean>

    <bean id="CL_PRICE_JA" class="org.terasoluna.gfw.common.codelist.SimpleMapCodeList">
        <property name="map">
            <util:map>
                <entry key="0" value="上限なし" />
                <entry key="10000" value="10,000円以下" />
                <entry key="20000" value="20,000円以下" />
                <entry key="30000" value="30,000円以下" />
                <entry key="40000" value="40,000円以下" />
                <entry key="50000" value="50,000円以下" />
            </util:map>
        </property>
    </bean>

プロパティファイル

    simpleI18nCodeList.fallback.locale = en

Controllerクラス

    ...

    @Controller
    public class OrderController {

        @Value("${simpleI18nCodeList.fallback.locale}") // (1)
        private Locale fallBackLocale;

        @RequestMapping(value = "price", method = RequestMethod.GET)
        public String price(Model model, HttpServletRequest request) {
            model.addAttribute("requestLocale", RequestContextUtils
                .getLocale(request)); // (2)
            model.addAttribute("fallBackLocale",fallBackLocale); // (3)

            return "order/price";
        }
    }

項番 | 説明
--- | ---
(1) | リクエストで指定したロケールがコードリストに定義されていなかった場合に、どのロケールのコードリストを取得するかをプロパティファイルから取得し、`fallBackLocale`変数に設定する。
(2) | `org.springframework.web.servlet.support.RequestContextUtils`を利用してリクエストで指定されたロケールを取得し、Modelに登録する。 `RequestContextUtils`の`getLocale`メソッドは、引数に`jakarta.servlet.http.HttpServletRequest`を取るため、この場合は`HttpServletRequest`をハンドラメソッドの引数にとっても良い。
(3) | (1) で取得した`fallBackLocale`をModelに登録する。

テンプレートHTML実装例

    <select th:field="*{basePrice}">
        <option th:each="price : ${@CL_I18N_PRICE.asMap(requestLocale).isEmpty()} ?
        ${@CL_I18N_PRICE.asMap(fallBackLocale)} : ${@CL_I18N_PRICE.asMap(requestLocale)}"
        th:value="${price.key}" th:text="${price.value}"></option> <!--/* (1) */-->
    </select>

項番 | 説明
--- | ---
(1) | リクエストで指定したロケールに対応するコードリストを`Map`形式で取得する。 リクエストで指定したロケールがコードリストに定義されていなかった場合、`fallbackLocale`変数に設定したロケールに対応するコードリストを`Map`形式で取得する。

出力HTML lang=en

    <select id="basePrice" name="basePrice">
        <option value="0">unlimited</option>
        <option value="1">Less than \10,000</option>
        <option value="2">Less than \20,000</option>
        <option value="3">Less than \30,000</option>
        <option value="4">Less than \40,000</option>
        <option value="5">Less than \50,000</option>
    </select>

出力HTML lang=ja

    <select id="basePrice" name="basePrice">
        <option value="0">上限なし</option>
        <option value="1">10,000円以下</option>
        <option value="2">20,000円以下</option>
        <option value="3">30,000円以下</option>
        <option value="4">40,000円以下</option>
        <option value="5">50,000円以下</option>
    </select>

出力HTML lang=undefined

    <select id="basePrice" name="basePrice">
        <option value="0">unlimited</option> <!--/* (1) */-->
        <option value="1">Less than \10,000</option>
        <option value="2">Less than \20,000</option>
        <option value="3">Less than \30,000</option>
        <option value="4">Less than \40,000</option>
        <option value="5">Less than \50,000</option>
    </select>

項番 | 説明
--- | ---
(1) | リクエストで指定したロケールがコードリストに定義されていなかった場合に、`fallbackLocale`変数で指定した”en” が設定されるため、ロケールが”en”である`CL_PRICE_EN`コードリストが表示される。