Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CI

on:
push:
branches:
- main
- master
pull_request:
branches:
- '**'

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up JDK 8
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'temurin'

- name: Build and test with Maven
run: mvn clean install --no-transfer-progress -s .maven-settings.xml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ nb-configuration.xml
*.ftf
/jhotdraw-samples/jhotdraw-samples-misc/nbproject/
*.iml
docs
12 changes: 12 additions & 0 deletions .maven-settings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>github</id>
<username>${env.GITHUB_ACTOR}</username>
<password>${env.GITHUB_TOKEN}</password>
</server>
</servers>
</settings>
22 changes: 22 additions & 0 deletions Labs/L2-ConceptLocation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Concept Location: The Selection Tool

To find the classes that implement the Selection feature I located the concept dynamically rather than by reading code. I launched the runnable SVG sample (`org.jhotdraw.samples.svg.Main` in `jhotdraw-samples-misc`), set a breakpoint on the first line of `SelectionTool.mousePressed(MouseEvent)` (`SelectionTool.java:217`), and then exercised the feature three ways on the canvas: clicking a figure, clicking one of a figure's handles, and clicking empty space. Each click suspended the debugger at the same entry point, which confirmed that `SelectionTool` is the controller Tool the editor delegates mouse events to and the natural starting point of the concept.

From that breakpoint I stepped into the body. `view.findHandle(anchor)` took me into `DrawingView`; when it returned null I followed `findTargetFigure(...)` into `Figure.isSelectable()`/`contains()` and `Drawing.findFigureBehind(...)`; then `resolveTracker(handle, figure, evt)` (`SelectionTool.java:318`) picked a tracker and `setTracker(...)` activated it. Stepping into `tracker.mousePressed(evt)` dropped me into one of the three Default trackers depending on which gesture I had performed. Holding Alt while clicking overlapping figures took the `isSelectBehindModifierHeld` branch and stepped into `findFigureBehindCurrentSelection`. Walking those call chains gave me the set of collaborating classes below.

| Domain Class | Responsibility |
| --- | --- |
| `SelectionTool` | Controller/entry Tool the debugger lands in first; holds the current tracker and routes mouse/key events to it (Strategy context). |
| `AbstractTool` | Superclass providing common Tool plumbing: the `anchor` point, view/editor accessors, and the `fire*` event helpers. |
| `Tool` | Interface defining the Tool contract (`activate`, `mousePressed`, `draw`, listener methods) that `SelectionTool` and the trackers implement. |
| `HandleTracker` / `DefaultHandleTracker` | Tracker state for manipulating a handle; default impl drags the handle and its compatible handles. |
| `DragTracker` / `DefaultDragTracker` | Tracker state for dragging a selected figure; default impl moves the figure with the mouse. |
| `SelectAreaTracker` / `DefaultSelectAreaTracker` | Tracker state for rubber-band area selection; default impl draws the selection rectangle and selects enclosed figures. |
| `DrawingView` | The view clicked on; resolves handles and figures (`findHandle`, `findFigure`), converts coordinates (`viewToDrawing`), and manages selection state. |
| `Drawing` | The figure container; `findFigureBehind(...)` walks the z-order for the select-behind gesture. |
| `DrawingEditor` | Editor context passed to `activate`/`deactivate` when trackers are swapped. |
| `Figure` | The drawable object being selected; queried via `isSelectable()` and `contains(point)`. |
| `Handle` | A draggable control point on a selected figure; the target a `HandleTracker` operates on. |
| `ToolEvent` / `ToolAdapter` | Event plumbing; the inner `TrackerHandler` extends `ToolAdapter` to catch tracker `toolDone` events and reset to the select-area tracker. |

The initial concept set is therefore `SelectionTool` together with `AbstractTool`/`Tool`, the three tracker pairs (`HandleTracker`/`DefaultHandleTracker`, `DragTracker`/`DefaultDragTracker`, `SelectAreaTracker`/`DefaultSelectAreaTracker`), and the collaborators `DrawingView`, `Drawing`, `DrawingEditor`, `Figure`, `Handle`, and the `ToolEvent`/`ToolAdapter` event types.
29 changes: 29 additions & 0 deletions Labs/L2-UserStory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# L2 — Change Request: User Story (Selection Tool)

For my individual portfolio I picked the **Selection Tool** (`org.jhotdraw.draw.tool.SelectionTool`, in `jhotdraw-core`). It is the tool the user is in when they interact directly with the canvas to pick figures and move them around, so it is the most user-facing piece of behaviour I could attach a clear story to.

## Story

> **As a** person drawing a diagram in the SVG editor,
> **I want** to select and manipulate the figures I have already placed on the canvas,
> **so that** I can move, resize and rearrange my drawing without redrawing it from scratch.

This is tracked on the team's GitHub backlog (we keep the backlog as GitHub issues) as **issue #12**, labelled **`user story`**, currently sitting in the **TODO** column: https://github.com/timadam03/JHotDraw/issues/12

## Acceptance criteria

These line up one-to-one with the behaviours I later drove out in the BDD scenarios (US1-US5).

| # | Given / When | Then |
|---|--------------|------|
| US1 | A selectable figure exists and I click on it | The figure becomes the drag target and I can drag to move it (`DragTracker`) |
| US2 | I click on an empty part of the canvas | The current selection is cleared |
| US3 | I click directly on a handle of a selected figure | I can drag that handle to resize/transform the figure (`HandleTracker`) |
| US4 | I press and drag starting on empty canvas | A rubber-band selection rectangle is drawn over the area (`SelectAreaTracker`) |
| US5 | Two figures overlap and I hold Alt/Ctrl while clicking | The figure *behind* the front one is selected (select-behind) |

## Notes / scope

- The story is about the existing user-visible behaviour; I am not adding a new feature here. The selection of this story is what frames the later refactoring (branch `altan/develop`) and the unit + BDD tests on the same feature.
- "Selectable" follows `Figure.isSelectable()`; deselect and select-behind only apply when the view is active, so the criteria assume an enabled `DrawingView`.
- US5 is gated by the bound `selectBehindEnabled` property (default on), which is why the modifier-key behaviour is listed as a criterion rather than always-on.
51 changes: 51 additions & 0 deletions Labs/L3-ImpactAnalysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Lab 3 — Impact Analysis: SelectionTool

## Starting point

Concept location (Lab 2) ended on `org.jhotdraw.draw.tool.SelectionTool` as the class that implements the Selection Tool feature. Following the static + dynamic impact analysis process in Figure 7.9 (Rajlich), I mark `SelectionTool` as **CHANGED**, mark all of its direct neighbours **NEXT**, and then walk the graph: each time I take a class off the NEXT set I decide whether a change to `SelectionTool` would actually force a change in it (**CHANGED**), whether it would only relay the change further (**PROPAGATES**), or whether it would be left **UNCHANGED**. New neighbours of a CHANGED/PROPAGATES class get added to NEXT. I stop when NEXT is empty.

I built the initial neighbour set from the imports and field/parameter types in `SelectionTool.java` plus the call sites I traced while reading `mousePressed`, `resolveTracker`, `findTargetFigure`, `findFigureBehindCurrentSelection`, `findFigureAtPoint` and `setTracker`. I sanity-checked the reverse direction with a `grep -rl "new SelectionTool"` / `extends SelectionTool` to see who depends *on* the tool — only `DelegationSelectionTool` and the editor wiring do, and neither is touched by the refactor.

## Walking the graph

Starting from `SelectionTool` (**CHANGED**), the first ring of NEXT nodes is: `AbstractTool`, `Tool`, the three tracker interfaces (`DragTracker`, `HandleTracker`, `SelectAreaTracker`) and their three default impls (`DefaultDragTracker`, `DefaultHandleTracker`, `DefaultSelectAreaTracker`), then `DrawingView`, `Drawing`, `DrawingEditor`, `Figure`, `Handle`, and the event plumbing `ToolEvent` / `ToolAdapter` / `ToolListener`.

**`AbstractTool` (superclass) — UNCHANGED.** `SelectionTool` calls `super.mousePressed`, `getView`, `getEditor`, `firePropertyChange`, `fireToolDone`, `fireAreaInvalidated`, `fireBoundsInvalidated` and reads `anchor`. All of these are existing protected members; the refactor (commits `8243c188` and `35f97d94`) only re-organised code *inside* `SelectionTool` and added private helpers. It never changed how the superclass is called, so nothing propagates upward.

**`Tool` interface — UNCHANGED.** The local field `tracker` is typed as `Tool` and `resolveTracker`/`setTracker` pass `Tool` around, but I never added a method to the interface — `setTracker` still uses `activate`, `deactivate`, `mousePressed`, `addToolListener`, `removeToolListener`, all pre-existing. Visited, left alone.

**`DragTracker`, `HandleTracker`, `SelectAreaTracker` interfaces — UNCHANGED.** These are the State roles in the Strategy pattern. `getDragTracker`, `getHandleTracker`, `getSelectAreaTracker` still call `setDraggedFigure`, `setHandles`, etc. The whole point of the refactor was to keep their contracts intact, so a change to `SelectionTool` does not force a change here. They are the boundary where the walk stops on the tracker side.

**`DefaultDragTracker`, `DefaultHandleTracker`, `DefaultSelectAreaTracker` — UNCHANGED.** Lazily instantiated by the `getXxxTracker` methods exactly as before. `DefaultHandleTracker` is mentioned in a Javadoc note about keeping the figure-search order consistent; I checked that order in `findFigureAtPoint` and it matches, so the note is honoured and the class does not need editing.

**`DrawingView` — UNCHANGED (interface), PROPAGATES at most.** This is the busiest collaborator: `findHandle`, `findFigure`, `viewToDrawing`, `getDrawing`, `getSelectedFigures`, `getCompatibleHandles`, `clearSelection`, `setHandleDetailLevel`, `isEnabled`. Every one of these is an existing method on the `DrawingView` interface. The refactor moved the calls into helper methods but kept the same calls with the same arguments, so the interface is visited and left UNCHANGED.

**`Drawing` — UNCHANGED.** Reached through `view.getDrawing()` inside `findFigureBehindCurrentSelection` and `findFigureAtPoint`, then `drawing.findFigureBehind(...)`. Pre-existing method, unchanged signature. Visited, left alone.

**`DrawingEditor` — UNCHANGED.** Only used as the activate/deactivate context (`activate(DrawingEditor)`, `deactivate(DrawingEditor)`, `getEditor()`). No change.

**`Figure` — UNCHANGED.** The tool reads `isSelectable()` and `contains(Point2D.Double)`. Both already existed; the select-behind loop and the "prefer current selection" loop use them as before. Visited, left alone.

**`Handle` — UNCHANGED.** Returned by `view.findHandle` and passed into `getHandleTracker`. The tool only holds and forwards it; no member of `Handle` is touched.

**`ToolEvent`, `ToolAdapter`, `ToolListener` — UNCHANGED.** The inner class `TrackerHandler extends ToolAdapter` overrides `toolDone`, `areaInvalidated`, `boundsInvalidated` and reads `ToolEvent.getInvalidatedArea()`. These overrides and that accessor are unchanged by the refactor, and `ToolListener` is only used through `addToolListener`/`removeToolListener`. Event plumbing is visited and left alone.

After processing all of the above, NEXT is empty and the walk terminates.

## Conclusion of the walk

The change is contained inside the **single CHANGED class, `SelectionTool`**. Every neighbour I visited was either a collaborator interface (`Tool`, the three tracker interfaces, `DrawingView`, `Drawing`, `DrawingEditor`, `Figure`, `Handle`) or a class whose contract the refactor deliberately preserved (`AbstractTool`, the three `Default*` trackers, the three event classes). None of them had to change, because both refactor commits only restructured `SelectionTool`'s own body — extracting `findTargetFigure`/`resolveTracker`/`isViewActive`/`setTracker` (`8243c188`) and then splitting `findTargetFigure` into `isSelectBehindModifierHeld`/`findFigureBehindCurrentSelection`/`findFigureAtPoint` (`35f97d94`) — while keeping every external call unchanged.

## Table 1 — Packages visited during impact analysis

| Package name | # of classes | Comments |
|---|---|---|
| `org.jhotdraw.draw.tool` | 20 | Home package of the feature; holds `SelectionTool` (CHANGED) plus `Tool`/`AbstractTool` and the three tracker interfaces + three `Default*` impls (all visited, UNCHANGED). |
| `org.jhotdraw.draw` | 23 | Provides the core collaborators `DrawingView`, `Drawing`, `DrawingEditor` that the tool queries and mutates; visited, UNCHANGED. |
| `org.jhotdraw.draw.figure` | 27 | Supplies `Figure` (`isSelectable`, `contains`), the objects the tool selects and drags; visited, UNCHANGED. |
| `org.jhotdraw.draw.handle` | 26 | Supplies `Handle`, the draggable control point returned by `findHandle` and fed to the handle tracker; visited, UNCHANGED. |
| `org.jhotdraw.draw.event` | 26 | Event plumbing — `ToolEvent`, `ToolAdapter`, `ToolListener` used by the inner `TrackerHandler` to relay tool events; visited, UNCHANGED. |

## Estimated impact set

**{ `SelectionTool` }** — the only CHANGED class. All other classes in the five packages above were visited during the walk and marked UNCHANGED, because the refactor preserved the contracts of the interfaces (`Tool`, `DragTracker`, `HandleTracker`, `SelectAreaTracker`, `DrawingView`, `Drawing`, `DrawingEditor`, `Figure`, `Handle`) and of the collaborating classes (`AbstractTool`, the `Default*` trackers, the event classes). The test code (`SelectionToolTest`, the JGiven BDD stages) is also affected as a consumer, but it sits in `src/test` and follows the change rather than being part of the production impact set.
Loading
Loading