# 機能設計書 36-リストビュー

## 概要

本ドキュメントは、Jenkinsにおけるリストビュー（ListView）機能の設計を記載したものである。リストビューは、ジョブを一覧形式で表示するビューであり、最も汎用的なビュータイプである。

### 本機能の処理概要

本機能は、選択したジョブまたは正規表現にマッチするジョブを一覧形式で表示し、カラムやフィルターを柔軟にカスタマイズできるビューを提供する。

**業務上の目的・背景**：Jenkinsのデフォルトビュー（AllView）は全てのジョブを表示するが、特定のジョブのみを表示したい場合が多い。リストビューを使用することで、ユーザーは関心のあるジョブのみを選択して表示でき、ビルド状況の把握や管理が効率化される。正規表現による動的フィルタリングにより、ジョブ数が増減しても自動的に表示内容が更新される。

**機能の利用シーン**：
- 特定のプロジェクトに関連するジョブのみを表示
- 正規表現で「dev-*」などの命名規則に従うジョブを動的にフィルタリング
- 失敗中のジョブのみを表示するフィルターを適用
- 表示カラムをカスタマイズ（最終ビルド時刻、ビルド時間など）
- フォルダ内のジョブを再帰的に表示

**主要な処理内容**：
1. ジョブの明示的な追加・削除
2. 正規表現による動的フィルタリング
3. ViewJobFilterによる追加フィルタリング
4. ListViewColumnによるカラム表示カスタマイズ
5. ジョブの再帰検索（recurseオプション）

**関連システム・外部連携**：Viewの基底クラスを継承。ViewJobFilterとListViewColumnはExtensionPointであり、プラグインで拡張可能。

**権限による制御**：View.CONFIGUREでビューの設定変更、Item.CONFIGUREでジョブの追加・削除を制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | 新しいビューの作成 | 作成画面 | リストビューの新規作成 |

## 機能種別

UI管理 / ジョブフィルタリング / カスタマイズ可能ビュー

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | Yes | ビュー名 | Jenkins.checkGoodName() |
| recurse | boolean | No | フォルダ内を再帰検索 | デフォルト: false |
| includeRegex | String | No | ジョブ名フィルタ正規表現 | 有効な正規表現 |
| jobNames | Set<String> | No | 明示的に含めるジョブ名 | - |
| columns | List<ListViewColumn> | No | 表示カラム設定 | - |
| jobFilters | List<ViewJobFilter> | No | ジョブフィルター設定 | - |

### 入力データソース

- HTTPリクエスト: StaplerRequest2からフォームデータを取得
- 設定ファイル: config.xmlからのデシリアライズ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| items | List<TopLevelItem> | フィルタリングされたジョブ一覧 |
| columns | DescribableList<ListViewColumn> | カラム設定 |
| jobFilters | DescribableList<ViewJobFilter> | フィルター設定 |
| includeRegex | String | 現在の正規表現パターン |

### 出力先

- HTMLレスポンス: ジョブ一覧テーブル
- JSON/XML: REST APIレスポンス

## 処理フロー

### 処理シーケンス

```
1. getItems()呼び出し
   └─ ビューに含まれるジョブの取得要求
2. jobNames取得
   └─ 明示的に登録されたジョブ名セットを取得
3. ジョブ解決
   └─ 名前からTopLevelItemオブジェクトを取得
4. 正規表現フィルタ適用
   └─ includePatternにマッチするジョブを追加
5. JobFilter適用
   └─ 各ViewJobFilterで追加のフィルタリング
6. 重複除去
   └─ LinkedHashSetで重複を除去しつつ順序を保持
7. 結果返却
   └─ フィルタリングされたジョブリストを返却
```

### フローチャート

```mermaid
flowchart TD
    A[getItems呼出] --> B[jobNamesから初期リスト作成]
    B --> C{recurse?}
    C -->|Yes| D[getAllItems で再帰取得]
    C -->|No| E[getItems で直接取得]
    D --> F{includeRegex?}
    E --> F
    F -->|Yes| G[正規表現マッチでフィルタ]
    F -->|No| H[jobFiltersに進む]
    G --> H
    H --> I{jobFilters存在?}
    I -->|Yes| J[各ViewJobFilterを適用]
    I -->|No| K[結果返却]
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-36-01 | 重複除去 | 同一ジョブは1度のみ表示 | 常時（LinkedHashSet使用） |
| BR-36-02 | 大文字小文字非区別 | ジョブ名はcase-insensitive | jobNames TreeSet |
| BR-36-03 | 正規表現エラー処理 | 無効な正規表現はOldDataMonitorで報告 | readResolve時 |
| BR-36-04 | ジョブ移動追従 | ジョブリネーム時にjobNamesを自動更新 | ItemListener.onLocationChanged |
| BR-36-05 | ジョブ削除追従 | ジョブ削除時にjobNamesから自動削除 | ItemListener.onDeleted |

### 計算ロジック

正規表現マッチ: `includePattern.matcher(itemName).matches()`

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| ジョブ追加 | config.xml | UPDATE | jobNamesにジョブ名を追加 |
| ジョブ削除 | config.xml | UPDATE | jobNamesからジョブ名を削除 |
| 設定変更 | config.xml | UPDATE | includeRegex、columns、jobFiltersを更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | PatternSyntaxException | 無効な正規表現 | doCheckIncludeRegex()で事前検証 |
| - | Failure | ジョブ追加時にジョブが存在しない | 正しいジョブ名を指定 |
| - | AccessDeniedException | ジョブへの参照権限なし | 権限のあるジョブのみ追加 |

### リトライ仕様

getItems()はステートレスな読み取り操作であり、リトライは安全。

## トランザクション仕様

View.save()により、ビュー設定全体がアトミックに保存される。

## パフォーマンス要件

- getItems()は頻繁に呼び出されるため、効率的な実装が必要
- recurse=trueの場合、getAllItems()を使用するため大規模環境では注意
- jobFiltersの処理はO(n)のため、フィルター数とジョブ数に注意

## セキュリティ考慮事項

- AccessDeniedExceptionはcatchして無視（権限のないジョブは表示しない）
- 正規表現はユーザー入力であり、DoS攻撃に注意（ReDoS対策）

## 備考

- @Symbol("list")により、JenkinsfileからlistViewとして参照可能
- statusFilterはViewJobFilterに移行（@Deprecated）
- ListViewColumnはプラグインで追加可能（StatusColumn、WeatherColumn等）

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: ListViewの構造を理解する

リストビューのフィールド構造とコンストラクタを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ListView.java | `core/src/main/java/hudson/model/ListView.java` | クラス構造と初期化 |

**主要処理フロー**:
- **82行目**: ListViewクラス定義（View継承、DirectlyModifiableView実装）
- **87-88行目**: jobNamesフィールド（TreeSet、case-insensitive）
- **90-92行目**: jobFilters、columnsフィールド
- **97-102行目**: includeRegex、recurseフィールド
- **117-127行目**: コンストラクタとinitColumns()、initJobFilters()

**読解のコツ**: jobNamesはSortedSet<String>で、TreeSetにString.CASE_INSENSITIVE_ORDERを指定している。これによりジョブ名の大文字小文字を区別しない。

#### Step 2: getItems()の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ListView.java | `core/src/main/java/hudson/model/ListView.java` | ジョブ取得ロジック |

**主要処理フロー**:
- **203-206行目**: getItems() - recurseオプションを渡して内部メソッド呼び出し
- **218-272行目**: getItems(boolean recurse) - 実際のジョブ取得ロジック
- **228-240行目**: recurse=trueの場合のgetAllItems使用
- **241-257行目**: recurse=falseの場合の直接取得
- **260-267行目**: jobFiltersの適用
- **269行目**: LinkedHashSetで重複除去

#### Step 3: ジョブの追加・削除を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ListView.java | `core/src/main/java/hudson/model/ListView.java` | ジョブ操作API |

**主要処理フロー**:
- **299-304行目**: add() - ジョブの追加
- **312-319行目**: remove() - ジョブの削除
- **395-410行目**: doAddJobToView() - REST APIでのジョブ追加
- **414-427行目**: doRemoveJobFromView() - REST APIでのジョブ削除
- **563-652行目**: Listener内部クラス - ジョブ移動・削除の追従

### プログラム呼び出し階層図

```
ListView.getItems()
    │
    └─ getItems(recurse)
           │
           ├─ jobNames取得（synchronized）
           │
           ├─ [recurse=true]
           │      └─ ItemGroup.getAllItems(TopLevelItem.class, filter)
           │
           ├─ [recurse=false]
           │      ├─ ItemGroup.getItem(name) for each jobName
           │      └─ ItemGroup.getItems(filter) for includePattern
           │
           ├─ [includePattern != null]
           │      └─ pattern.matcher(itemName).matches()
           │
           └─ [jobFilters.isNotEmpty()]
                  └─ for each ViewJobFilter
                         └─ filter.filter(items, candidates, this)
```

### データフロー図

```
[入力]                      [処理]                           [出力]

jobNames ─────────────▶ getItems() ────────────────────▶ List<TopLevelItem>
    │                        │
includeRegex ────────▶       │
    │                        │
recurse ─────────────▶       │
                             │
            ┌────────────────┴────────────────┐
            ▼                                 ▼
     jobNamesから取得               includePatternでフィルタ
            │                                 │
            └────────────┬────────────────────┘
                         ▼
                  ViewJobFilters適用
                         │
                         ▼
                  LinkedHashSetで重複除去
                         │
                         ▼
                    結果リスト
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ListView.java | `core/src/main/java/hudson/model/ListView.java` | ソース | リストビュー実装 |
| ViewJobFilter.java | `core/src/main/java/hudson/views/ViewJobFilter.java` | ソース | ジョブフィルターの基底クラス |
| ListViewColumn.java | `core/src/main/java/hudson/views/ListViewColumn.java` | ソース | カラム表示の基底クラス |
| StatusFilter.java | `core/src/main/java/hudson/views/StatusFilter.java` | ソース | 有効/無効フィルター |
| DirectlyModifiableView.java | `core/src/main/java/hudson/model/DirectlyModifiableView.java` | ソース | ジョブ追加・削除インターフェース |
| view/configure.jelly | `core/src/main/resources/hudson/model/ListView/configure.jelly` | ビュー | 設定画面 |
| view/main.jelly | `core/src/main/resources/hudson/model/ListView/main.jelly` | ビュー | メイン表示画面 |
