# 機能設計書 39-マルチメディアI/O

## 概要

本ドキュメントは、Julia Base ライブラリにおけるマルチメディアI/O機能の設計を記述する。`display` / `MIME` / `@MIME_str` / `AbstractDisplay` によるリッチ表示出力を提供する。

### 本機能の処理概要

本機能は、Julia のマルチメディア表示システムを実装する。MIME タイプに基づくオブジェクトのリッチ表示、ディスプレイバックエンドのスタック管理、テキスト/バイナリ表現の生成を含む。

**業務上の目的・背景**：Julia はインタラクティブな科学計算環境として使用されることが多く、画像・HTML・LaTeX 等の多様な形式での出力が求められる。マルチメディアI/Oシステムは、Julia のディスパッチ機構を活用して MIME タイプごとに異なる表示メソッドを定義可能にし、IJulia（Jupyter Notebook）やVS Code等のリッチなフロントエンドでの表示を実現する。

**機能の利用シーン**：REPL での text/plain 表示、Jupyter Notebook での HTML/SVG/PNG 等のリッチ表示、カスタム型の表示メソッド定義、表示バックエンドの切り替え（pushdisplay/popdisplay）。

**主要な処理内容**：
1. `MIME{mime}` シングルトン型による MIME タイプの型レベル表現（32行目）
2. `@MIME_str` マクロによる `MIME"text/plain"` 記法の提供（42-44行目）
3. `show(io, mime, x)` による MIME タイプ指定の出力（47行目、122行目）
4. `showable(mime, x)` による表示可能性の判定（76行目）
5. `repr(mime, x)` による文字列/バイナリ表現の取得（159行目）
6. `istextmime(m)` によるテキスト MIME タイプの判定（195-206行目）
7. `AbstractDisplay` 抽象型とディスプレイスタックの管理（225行目、272行目）
8. `TextDisplay` によるテキストベースのデフォルト表示（251-253行目）
9. `display(x)` / `display(mime, x)` によるディスプレイスタック経由の表示（336-361行目）
10. `redisplay(x)` による既存表示の更新（395-421行目）
11. `pushdisplay(d)` / `popdisplay()` によるディスプレイスタックの操作（281-301行目）

**関連システム・外部連携**：IJulia（Jupyter Notebook）、VS Code Julia 拡張、Pluto.jl 等のフロントエンドが `AbstractDisplay` を実装してリッチ表示を提供する。

**権限による制御**：該当なし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | REPL | 主画面 | TextDisplay を通じた text/plain 表示 |

## 機能種別

マルチメディア表示システム / MIME タイプディスパッチ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| mime | MIME / AbstractString | Yes（display(mime,x)時） | 出力形式を指定する MIME タイプ | MIME{Symbol} に変換可能であること |
| x | Any | Yes | 表示対象のオブジェクト | show メソッドが定義されていること |
| d | AbstractDisplay | No | 表示先のディスプレイバックエンド | - |
| context | Pair / IO / IOContext | No | repr の IOContext キーワード | - |

### 入力データソース

Julia オブジェクト（任意の型）。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| display結果 | Any | ディスプレイバックエンド依存（通常は副作用として表示） |
| repr結果 | AbstractString / Vector{UInt8} | テキスト MIME の場合は String、バイナリ MIME の場合は Vector{UInt8} |
| showable結果 | Bool | 指定 MIME タイプで表示可能かどうか |
| displayable結果 | Bool | ディスプレイスタックで表示可能かどうか |

### 出力先

ディスプレイバックエンドの IO ストリーム（TextDisplay の場合は stdout）。

## 処理フロー

### 処理シーケンス

```
1. display(x) [336-348行目]
   └─ ディスプレイスタックを末尾から走査
   └─ xdisplayable(displays[i], x) で表示可能性を判定
   └─ display(displays[i], x) を試行
   └─ MethodError なら次のディスプレイへ
   └─ すべて失敗なら MethodError をスロー

2. display(mime, x) [350-361行目]
   └─ ディスプレイスタックを末尾から走査
   └─ xdisplayable(displays[i], mime, x) で判定
   └─ display(displays[i], mime, x) を試行

3. display(d::TextDisplay, x) [255行目]
   └─ display(d, MIME"text/plain"(), x)
   └─ show(d.io, MIME"text/plain"(), x)
   └─ println(d.io)

4. repr(mime, x) [159行目]
   └─ istextmime(mime) を判定
   └─ テキスト: _textrepr → __binrepr → IOBuffer に show → String
   └─ バイナリ: _binrepr → __binrepr → IOBuffer に show → Vector{UInt8}
```

### フローチャート

```mermaid
flowchart TD
    A[開始: display x] --> B[i = length displays]
    B --> C{i >= 1?}
    C -->|No| D[MethodError をスロー]
    C -->|Yes| E{xdisplayable displays_i, x?}
    E -->|No| F[i = i - 1]
    F --> C
    E -->|Yes| G[try: display displays_i, x]
    G --> H{成功?}
    H -->|Yes| I[return 結果]
    H -->|No MethodError| F

    J[開始: display TextDisplay, x] --> K[show d.io, MIME text/plain, x]
    K --> L[println d.io]
    L --> M[return]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-39-01 | MIME シングルトン | MIME{mime} は各 MIME タイプごとのシングルトン型（32行目） | 全体 |
| BR-39-02 | ディスプレイスタック | display(x) はスタック末尾（最新）から順に試行（337行目） | display(x) |
| BR-39-03 | TextDisplay フォールバック | TextDisplay は text/plain のみ表示、他のテキスト MIME も istextmime なら表示可能（258行目） | TextDisplay |
| BR-39-04 | showable 判定 | hasmethod(show, Tuple{IO, MIME{mime}, typeof(x)}) で判定（76行目） | showable |
| BR-39-05 | repr テキスト/バイナリ | istextmime なら String を返し、そうでなければ Vector{UInt8} を返す（159行目） | repr(mime, x) |
| BR-39-06 | repr 透過 | AbstractString（テキスト MIME）や Vector{UInt8}（バイナリ MIME）はそのまま返す。ただし text/plain は除く（164-165, 178行目） | repr |
| BR-39-07 | text/plain フォールバック | show(io, MIME"text/plain", x) のデフォルトは show(io, x) を呼ぶ（47行目） | show |
| BR-39-08 | redisplay | デフォルトは display を呼ぶ。バックエンドがオーバーライド可能（423-425行目） | redisplay |
| BR-39-09 | reinit_displays | ディスプレイスタックをクリアし TextDisplay(stdout) を追加（302-305行目） | 初期化時 |
| BR-39-10 | istextmime 拡張 | "text/" で始まる MIME と特定の application/image/model 系 MIME がテキスト扱い（195-206行目） | istextmime |

### 計算ロジック

該当なし。

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

該当なし。

### テーブル別操作詳細

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | MethodError | display(x) で全ディスプレイが表示不可 | 対応する show メソッドを定義 |
| - | MethodError | display(mime, x) で指定 MIME の表示不可 | show(io, mime, x) メソッドを定義 |
| - | MethodError | displayable でない MIME を TextDisplay に表示 | テキスト MIME のみ使用（260行目） |
| - | KeyError | popdisplay(d) で d がスタックに存在しない | 存在するディスプレイを指定（300行目） |

### リトライ仕様

該当なし。display はスタック内の次のディスプレイにフォールバックする（342-343行目でMethodErrorを捕捉して継続）。

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

該当なし。

## パフォーマンス要件

- `showable` は `hasmethod` を使用するため、メソッドテーブルの検索コストがある
- `repr` は IOBuffer を経由するため、大きなオブジェクトの場合はメモリ使用量に注意
- `display` のスタック走査は通常数個のディスプレイのみなのでオーバーヘッドは小さい

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

- ディスプレイバックエンドは任意のコードを実行できるため、信頼できないバックエンドの追加には注意が必要
- `show` メソッドはユーザー定義可能であり、任意の副作用を持ちうる

## 備考

- `Multimedia` モジュールは `Base` から import しており、`Base.Multimedia` として利用される
- ディスプレイスタックは `const displays = AbstractDisplay[]` としてグローバルに管理される（272行目）
- `xdisplayable` は `applicable(display, D, args...)` を使用して表示可能性を判定する（307行目）
- テキスト MIME として扱われる application 系 MIME タイプ: atom+xml, ecmascript, javascript, julia, json, postscript, rdf+xml, rss+xml, x-latex, xhtml+xml, xml, xml-dtd, image/svg+xml, model/vrml, model/x3d+vrml, model/x3d+xml（198-204行目）

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | multimedia.jl | `base/multimedia.jl` 32行目 | `MIME{mime}` シングルトン型: パラメトリック型でMIMEタイプをJuliaの型システムに埋め込む |
| 1-2 | multimedia.jl | `base/multimedia.jl` 225行目 | `AbstractDisplay` 抽象型: ディスプレイバックエンドの基底型 |
| 1-3 | multimedia.jl | `base/multimedia.jl` 251-253行目 | `TextDisplay` 構造体: io フィールドのみ持つシンプルなテキスト表示バックエンド |
| 1-4 | multimedia.jl | `base/multimedia.jl` 272行目 | `displays` グローバル変数: `AbstractDisplay[]` のスタック |

**読解のコツ**: `MIME{mime}` はパラメトリックシングルトン型であり、`MIME"text/plain"` は `MIME{Symbol("text/plain")}` の糖衣構文である。Julia のディスパッチ機構により、MIMEタイプごとに異なる `show` メソッドを定義できる。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | multimedia.jl | `base/multimedia.jl` 336-348行目 | `display(x)`: ディスプレイスタックを末尾から走査し、最初に成功したバックエンドで表示 |
| 2-2 | multimedia.jl | `base/multimedia.jl` 350-361行目 | `display(mime, x)`: MIME タイプ指定版 |
| 2-3 | multimedia.jl | `base/multimedia.jl` 254-262行目 | `display(d::TextDisplay, ...)`: TextDisplay の表示実装、show + println |
| 2-4 | multimedia.jl | `base/multimedia.jl` 395-421行目 | `redisplay(x)`: 既存表示の更新（IJulia等で使用） |

#### Step 3: ユーティリティ関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | multimedia.jl | `base/multimedia.jl` 76-77行目 | `showable(mime, x)`: hasmethod ベースの表示可能性判定 |
| 3-2 | multimedia.jl | `base/multimedia.jl` 159-178行目 | `repr(mime, x)`: IOBuffer 経由でテキスト/バイナリ表現を取得 |
| 3-3 | multimedia.jl | `base/multimedia.jl` 195-206行目 | `istextmime(m)`: テキスト MIME タイプの判定ロジック |
| 3-4 | multimedia.jl | `base/multimedia.jl` 281-305行目 | `pushdisplay`/`popdisplay`/`reinit_displays`: ディスプレイスタック操作 |
| 3-5 | multimedia.jl | `base/multimedia.jl` 364-372行目 | `displayable(m)`: ディスプレイスタック全体での表示可能性判定 |

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

```
display(x)                              [336行目]
    |
    +-- for i = length(displays):-1:1   [337行目]
    |     |
    |     +-- xdisplayable(displays[i], x)  [307行目, applicable ベース]
    |     |
    |     +-- display(displays[i], x)   [340行目]
    |           |
    |           +-- (TextDisplay の場合)
    |                 |
    |                 +-- display(d, MIME"text/plain"(), x)  [255行目]
    |                       |
    |                       +-- show(d.io, MIME"text/plain"(), x)  [254行目]
    |                       +-- println(d.io)  [254行目]
    |
    +-- throw(MethodError(display, (x,)))  [347行目]

repr(mime, x)                           [159行目]
    |
    +-- istextmime(mime)?               [159行目]
    |     |
    |     +-- Yes: _textrepr(mime, x, context)  [163行目]
    |     |         |
    |     |         +-- String(__binrepr(mime, x, context))
    |     |
    |     +-- No: _binrepr(mime, x, context)  [177行目]
    |               |
    |               +-- __binrepr(mime, x, context)  [168-176行目]
    |                     |
    |                     +-- s = IOBuffer()
    |                     +-- show(IOContext(s, context), mime, x)
    |                     +-- take!(s)

showable(mime, x)                       [76行目]
    |
    +-- hasmethod(show, Tuple{IO, MIME{mime}, typeof(x)})

pushdisplay(d)                          [281行目]
    |
    +-- push!(displays, d)              [283行目]

popdisplay()                            [293行目]
    |
    +-- pop!(displays)

reinit_displays()                       [302行目]
    |
    +-- empty!(displays)                [303行目]
    +-- pushdisplay(TextDisplay(stdout)) [304行目]
```

### データフロー図

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

Juliaオブジェクト x ------> display(x) --------------------> ディスプレイ出力
                               |
                               +-> スタック走査 (displays[end] -> displays[1])
                               |
                               +-> display(displays[i], x)
                               |     |
                               |     +-> (TextDisplay)
                               |           show(io, MIME"text/plain"(), x)
                               |           println(io)
                               |
                               +-> (IJulia等のリッチディスプレイ)
                                     show(io, MIME"text/html"(), x)

Juliaオブジェクト x ------> repr(mime, x) -----------------> String / Vector{UInt8}
                               |
                               +-> IOBuffer 作成
                               +-> show(IOBuffer, mime, x)
                               +-> take!(IOBuffer)
                               +-> istextmime ? String : Vector{UInt8}

MIME + typeof(x) ----------> showable(mime, x) -------------> Bool
                               |
                               +-> hasmethod(show, Tuple{IO, MIME, typeof(x)})
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| multimedia.jl | `base/multimedia.jl` | ソース | MIME型、AbstractDisplay、TextDisplay、display/redisplay/showable/repr/istextmime/displayable、ディスプレイスタック管理の全実装 |
| show.jl | `base/show.jl` | ソース | show(io, MIME"text/plain", x) の各型向け実装 |
| io.jl | `base/io.jl` | ソース | IO 抽象型、IOContext |
| iobuffer.jl | `base/iobuffer.jl` | ソース | IOBuffer（repr 内部で使用） |
