# (DD03) NumericFilter — Class Detailed Design [41 LOC]

| Field | Value |
|-------|-------|
| Fully Qualified Name | `com.github.blaxk3.ui.NumericFilter` |
| Layer | Utility |
| Module | `com.github.blaxk3.ui` |

## Class Overview

`NumericFilter` is a Swing `DocumentFilter` used to constrain text input so that only numeric values and at most one decimal point can be entered into a document. It acts as an input-validation guard at the UI layer, preventing invalid characters from being committed to the underlying text model. The implementation is intentionally small and self-contained, using a validate-then-delegate pattern in both mutation methods. The private validator method centralizes the acceptance rules for all document edits.

---

## Method: insertString(FilterBypass fb, int offset, String string, AttributeSet attr)

### 1. Role

Intercepts a text insertion request and allows it only when the resulting document content is still a valid numeric string. This method protects the document from invalid characters before they are committed.

### 2. Processing Pattern

```mermaid
flowchart TD
A["insertString called"] --> B["Build current document text"]
B --> C["Insert incoming string at offset"]
C --> D["isValid checks combined text"]
D --> E{"Valid text"}
E -->|"yes"| F["Delegate to super.insertString"]
E -->|"no"| G["Reject change"]
```

### 3. Parameter Analysis

| Parameter | Type | Required | Meaning | Usage |
|---|---|---:|---|---|
| `fb` | `FilterBypass` | Yes | Access path to the target document and superclass mutation hooks | Reads the current document and delegates to `super.insertString` when valid |
| `offset` | `int` | Yes | Insertion index within the document | Used to splice the proposed text into the current content |
| `string` | `String` | Yes | Incoming text to insert | Combined with existing content to validate the post-insert result |
| `attr` | `AttributeSet` | No | Text attributes for the inserted run | Passed through unchanged to the superclass method |

### 4. CRUD Operations / Called Services

| Category | Target | Operation | Purpose |
|---|---|---|---|
| Read | `Document` | `getText(0, doc.getLength())` | Capture current text before simulating the insertion |
| Transform | `StringBuilder` | `insert(offset, string)` | Build the candidate document text after insertion |
| Validate | `isValid(String)` | Local validation | Check whether the candidate text is numerically acceptable |
| Create / Update | `DocumentFilter` superclass | `super.insertString(...)` | Commit the insertion only when validation succeeds |

### 5. Dependency Trace

| Dependency | Type | Evidence | Notes |
|---|---|---|---|
| `DocumentFilter` | Inheritance | Class declaration | `NumericFilter` extends `DocumentFilter` |
| `FilterBypass` | Framework API | Method parameter | Provides document access and mutation bypass |
| `Document` | Swing text API | Local variable `doc` | Source of the current document text |
| `StringBuilder` | JDK utility | Local builder | Used to simulate the final content |
| `isValid(String)` | Internal method | Called directly | Central validation gate for insertions |
| `super.insertString(...)` | Framework call | Conditional delegation | Performs the actual insertion only if valid |

### 6. Per-Branch Detail Blocks

- **Branch A: Candidate text is valid**
  - The method reads the full current document text.
  - It inserts the requested `string` at `offset` into a temporary buffer.
  - The resulting content is checked by `isValid(...)`.
  - If validation succeeds, the method calls `super.insertString(...)` and the insertion is committed.

- **Branch B: Candidate text is invalid**
  - The same candidate string is constructed.
  - `isValid(...)` returns `false` because the inserted text would violate numeric formatting rules.
  - The method exits without calling the superclass.
  - The document remains unchanged.

- **Branch C: Exception path inherited from Swing text API**
  - The signature declares `BadLocationException` through the superclass contract.
  - This branch is part of the framework-level behavior rather than custom logic.
  - No custom recovery is implemented in the method body.

---

## Method: replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)

### 1. Role

Intercepts a text replacement request and permits it only if the resulting document content remains a valid numeric value. This supports editing behavior such as overwriting or pasting while preserving the numeric constraint.

### 2. Processing Pattern

```mermaid
flowchart TD
A["replace called"] --> B["Build current document text"]
B --> C["Replace range with incoming text"]
C --> D["isValid checks combined text"]
D --> E{"Valid text"}
E -->|"yes"| F["Delegate to super.replace"]
E -->|"no"| G["Reject change"]
```

### 3. Parameter Analysis

| Parameter | Type | Required | Meaning | Usage |
|---|---|---:|---|---|
| `fb` | `FilterBypass` | Yes | Access path to the target document and superclass mutation hooks | Reads the current document and delegates to `super.replace` when valid |
| `offset` | `int` | Yes | Starting index of the replacement | Used as the beginning of the replaced segment |
| `length` | `int` | Yes | Number of characters to remove | Used with `offset` to define the replaced range |
| `text` | `String` | Yes | Replacement text | Inserted into the candidate document to validate the final content |
| `attrs` | `AttributeSet` | No | Text attributes for the replaced run | Passed through unchanged to the superclass method |

### 4. CRUD Operations / Called Services

| Category | Target | Operation | Purpose |
|---|---|---|---|
| Read | `Document` | `getText(0, doc.getLength())` | Capture current text before simulating the replacement |
| Transform | `StringBuilder` | `replace(offset, offset + length, text)` | Build the candidate document text after replacement |
| Validate | `isValid(String)` | Local validation | Check whether the candidate text is numerically acceptable |
| Create / Update | `DocumentFilter` superclass | `super.replace(...)` | Commit the replacement only when validation succeeds |

### 5. Dependency Trace

| Dependency | Type | Evidence | Notes |
|---|---|---|---|
| `DocumentFilter` | Inheritance | Class declaration | `NumericFilter` extends `DocumentFilter` |
| `FilterBypass` | Framework API | Method parameter | Provides document access and mutation bypass |
| `Document` | Swing text API | Local variable `doc` | Source of the current document text |
| `StringBuilder` | JDK utility | Local builder | Used to simulate the final content |
| `isValid(String)` | Internal method | Called directly | Central validation gate for replacements |
| `super.replace(...)` | Framework call | Conditional delegation | Performs the actual replacement only if valid |

### 6. Per-Branch Detail Blocks

- **Branch A: Candidate text is valid**
  - The method reads the full current document text.
  - It applies the requested replacement into a temporary buffer.
  - The resulting content is checked by `isValid(...)`.
  - If validation succeeds, the method calls `super.replace(...)` and the replacement is committed.

- **Branch B: Candidate text is invalid**
  - The same candidate string is constructed.
  - `isValid(...)` returns `false` because the edited text would violate numeric formatting rules.
  - The method exits without calling the superclass.
  - The document remains unchanged.

- **Branch C: Exception path inherited from Swing text API**
  - The signature declares `BadLocationException` through the superclass contract.
  - This branch is framework-driven and not handled explicitly in the method body.
  - No custom recovery logic is provided.

---

## Method: isValid(String text)

### 1. Role

Validates whether a candidate string is acceptable as a numeric input. The method allows an empty string, digits, and a single decimal point, and rejects any other character.

### 2. Processing Pattern

```mermaid
flowchart TD
A["isValid called"] --> B{"Text empty"}
B -->|"yes"| C["Return true"]
B -->|"no"| D["Initialize decimal counter"]
D --> E["Scan characters left to right"]
E --> F{"Character is decimal point and count not 1"}
F -->|"yes"| G["Increment decimal counter"]
F -->|"no"| H{"Character is digit"}
H -->|"yes"| E
H -->|"no"| I["Return false"]
G --> E
E --> J["Return true"]
```

### 3. Parameter Analysis

| Parameter | Type | Required | Meaning | Usage |
|---|---|---:|---|---|
| `text` | `String` | Yes | Candidate document content to validate | Evaluated character by character against numeric rules |

### 4. CRUD Operations / Called Services

| Category | Target | Operation | Purpose |
|---|---|---|---|
| Read | `String` | `isEmpty()` | Allow the document to become blank |
| Read / Iterate | `String` | `length()`, `charAt(i)` | Traverse each character in the candidate text |
| Validate | `Character` API | `Character.isDigit(ch)` | Confirm that non-decimal characters are digits |
| Internal state | `decimalCount` | Increment and compare | Enforce at most one decimal point according to the current implementation |
| Return | Method result | `true` / `false` | Communicate acceptance or rejection to callers |

### 5. Dependency Trace

| Dependency | Type | Evidence | Notes |
|---|---|---|---|
| `Character.isDigit(char)` | JDK API | Called inside the loop | Accepts digit characters only |
| `insertString(...)` | Internal caller | Validation gate | Uses this method before committing inserts |
| `replace(...)` | Internal caller | Validation gate | Uses this method before committing replacements |
| `Decimal-point rule` | Local policy | Implemented in loop logic | Only one `.` should be accepted by the intended design |

### 6. Per-Branch Detail Blocks

- **Branch A: Empty input**
  - If `text.isEmpty()` is `true`, the method returns `true` immediately.
  - This allows deletion of all characters and supports clearing the field.

- **Branch B: Decimal point encountered**
  - Each character is scanned in order.
  - When the character is `'.'` and the decimal counter condition passes, the method increments `decimalCount` and continues.
  - This branch is the only path that permits a decimal separator.

- **Branch C: Digit encountered**
  - If the current character is not a decimal point, the method checks `Character.isDigit(ch)`.
  - Digit characters keep the scan moving forward.
  - No state is changed other than loop progression.

- **Branch D: Invalid character encountered**
  - If a character is neither an allowed decimal point nor a digit, the method returns `false` immediately.
  - This rejects alphabetic characters, symbols, whitespace, and other unsupported input.

- **Branch E: End of scan**
  - If the loop completes without hitting the invalid-character branch, the method returns `true`.
  - This means the candidate text matches the method’s numeric acceptance rules.
