Skip to content

Commit 2909585

Browse files
authored
#449: Needs parsed past end of section (#451)
* #449: Parsing Feature Needs from MD goes past next field. Added way to switch off parsing for segments.
1 parent d886448 commit 2909585

File tree

12 files changed

+320
-71
lines changed

12 files changed

+320
-71
lines changed

doc/changes/changes_4.2.0.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# OpenFastTrace 4.2.0, released ???
1+
# OpenFastTrace 4.2.0, released 2025-05-19
22

33
Code name: Markdown code blocks
44

@@ -8,6 +8,8 @@ In this release we changed the behavior of the Markdown importer, so that if we
88

99
We also added a whole section about understanding and fixing broken links between specification items to the user guide.
1010

11+
The new token `oft:on|off` allows switching off OFT parsing for certain text passages in Markdown and RST documents.
12+
1113
## Features
1214

1315
* #437: Upgrade build and test dependencies on top of 4.1.0
@@ -17,5 +19,6 @@ We also added a whole section about understanding and fixing broken links betwee
1719

1820
* #427: Removed old `CHANGELOG.md` file and merged missing parts into release history.
1921
* #431: Documented "unwanted coverage" in user guide.
22+
* #449: Fix parsing past end of "needs" paragraph.
2023
* #440: Added Tag importer support for TOML files.
2124
* #442: Added support for javascript file extensions `.cjs`, `.mjs` and `.ejs`

doc/spec/design.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,24 @@ Covers:
220220

221221
Needs: impl, utest, itest
222222

223+
### Line Parser for Lightweight Markup Import
224+
225+
RST and Markdown share a common underlying parser that operates on a line-by-line basis.
226+
227+
##### Disabling OFT Parsing for Parts of a Markup File
228+
`dsn~disabling-oft-parsing-for-parts-of-a-markup-file~1`
229+
230+
When it encounters the token `oft:off`, the line parser stops extracting specification items until it
231+
232+
* either encounters the token `oft:on`
233+
* or reaches the end of the current document.
234+
235+
Covers:
236+
237+
* `req~disabling-oft-parsing-for-parts-of-a-markup-file~1`
238+
239+
Needs: impl, utest
240+
223241
## Tracing
224242

225243
### Tracing Needed Coverage
@@ -756,8 +774,10 @@ The Markdown Importer supports forwarding required coverage from one artifact ty
756774

757775
The following example shows an architectural specification item that forwards the needed coverage directly to the detailed design and an integration test:
758776

777+
<!-- oft:off -->
759778
arch --> dsn, itest : req~skip-this-requirement~1
760-
779+
<!-- oft:on -->
780+
761781
Covers:
762782

763783
* `req~artifact-type-forwarding-in-markdown~1`

doc/spec/system_requirements.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ The same benefits as for [Markdown](#markdown-import) apply:
101101
* is portable across platforms
102102
* easy to process with text manipulation tools
103103

104+
Needs: req
105+
104106
### ReqM2 Import
105107
`feat~reqm2-import~1`
106108

@@ -251,9 +253,43 @@ Needs: dsn
251253

252254
### Supported Formats
253255

256+
#### Common Requirements for Lightweight Markup Import
257+
258+
Typical OFT specification are written in a lightweight markup language like [Markdown](#markdown-import) or [ReStructured Text](#restructured-text-rst-import). Before we go into the specifics, this section discusses the common requirements.
259+
260+
##### Disabling OFT Parsing for Parts of a Markup File
261+
`req~disabling-oft-parsing-for-parts-of-a-markup-file~1`
262+
263+
OFT-enhanced markup allows excluding text blocks from OFT parsing with the syntax `oft:on|off`.
264+
265+
Example for Markdown:
266+
267+
<!-- oft:off -->
268+
This part of the document will not be parsed for OFT specification items.
269+
270+
Until the end marker or the end of the current document is reached
271+
<!-- oft:on -->
272+
273+
Example for RST:
274+
275+
.. oft:off
276+
Not imported.
277+
.. oft:on
278+
279+
Rationale:
280+
281+
This allows creating OFT examples that do not contribute to the code and avoid accidental recognition of specification items in text that is not supposed to contain them.
282+
283+
Covers:
284+
285+
* [feat~markdown-import~1](#markdown-import)
286+
* [feat~rst-import~1](#restructured-text-rst-import)
287+
288+
Needs: dsn
289+
254290
#### Markdown
255291

256-
Markdown is a simple ASCII-based markup format that is designed to be human readable in the source. While it can be rendered into HTML, it is perfectly eye-friendly even before rendering.
292+
Markdown is a simple ASCII-based markup format that is designed to be human-readable in the source. While it can be rendered into HTML, it is perfectly eye-friendly even before rendering.
257293

258294
Markdown focuses on content over formatting by giving the document structure like headlines, paragraphs and lists. The combination of being lightweight, human-readable and structure-oriented makes it a good fit for writing specifications as code.
259295

doc/user_guide.md

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,6 @@ Requirements should be accompanied by a rationale in all cases where the reason
261261
the details are up to the detailed design.
262262

263263
Needs: dsn
264-
265-
266264

267265
`Needs`, `Rationale` and `Comment` are OpenFastTrace keywords that tell OpenFastTrace how to process the following content. There are other keywords in the context of specification items written in Markdown described in the following sections.
268266

@@ -298,6 +296,22 @@ Given the Feature `feat~rubber-ducky~1` exists and needs a `req`. A requirement
298296
Covers:
299297
- feat~rubber-ducky~1
300298

299+
##### `Needs`
300+
301+
The `Needs` keyword states which artifact types are needed to cover the current specification item. It is followed by a list of artifact types that are needed, each one written on a new line starting with a bullet character (`+`, `*`, or `-`) followed by the artifact type abbreviation. `Needs` comes in two flavors: as one-liner or as list.
302+
303+
**Variant a) one-line `needs`**
304+
305+
Needs: impl, utest, itest
306+
307+
**Variant b) as List**
308+
309+
Needs:
310+
- dsn
311+
- uman
312+
313+
Please note that you cannot mix the two styles in one specification item.
314+
301315
##### `Depends`
302316

303317
The `Depends` keyword defines dependencies between specification items. It is followed by a list of items the current specification item depends on, each one written on a new line starting witch a bullet character (`+`, `*`, or `-`) followed by the referenced specification item id. At the moment this has no effect on the HTML or plaintext output, but only if the `-o aspec` option is used. This has no effect on the coverage of specification items.
@@ -332,6 +346,26 @@ is functionally equivalent to
332346

333347
Tags are described in detail later in this document, see section [Distributing the Detailing Work](#distributing-the-detailing-work).
334348

349+
### Excluding Parts of a Specification Document for OFT Parsing
350+
351+
Sometimes you want specific sections or a whole document to be excluded from OFT parsing. One reason could be that it is a document that contains an OFT example, that should not contribute to the trace. Or, you could have data in a document and don't want to risk that something accidentally looks like an OFT artifact.
352+
353+
To switch of scanning use the token `oft:on|off` in your document at the appropriate location.
354+
355+
Markdown example:
356+
357+
<!-- oft:off -->
358+
This part is ignored by OFT.
359+
<!-- oft:on -->
360+
Here OFT scans again.
361+
362+
ReStructured text example:
363+
364+
.. oft:off
365+
This part is ignored by OFT.
366+
.. oft:on
367+
Here OFT scans again.
368+
335369
### Delegating Requirement Coverage
336370

337371
Consider a situation where you are responsible for the high-level software architecture of your project. You define the component breakdown, the interfaces and the interworking of the components. You get your requirements from a system requirement specification, but it turns out many of those incoming requirements are at a detail level that does not require design decisions on inter-component-level but rather affects the internals of a single component.

importer/lightweightmarkup/src/main/java/org/itsallcode/openfasttrace/importer/lightweightmarkup/statemachine/LineParserState.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
public enum LineParserState
88
{
99
/**
10-
* Parser started (at beginning of the file) or outside of a specification
10+
* Parser started (at beginning of the file) or outside a specification
1111
* item
1212
*/
1313
START,
@@ -23,8 +23,10 @@ public enum LineParserState
2323
RATIONALE,
2424
/** Inside a comment section */
2525
COMMENT,
26-
/** Inside a section defining the required coverage */
27-
NEEDS,
26+
/** Inside a section defining the required coverage (inline form) */
27+
NEEDS_LINE,
28+
/** Required coverage (list form) */
29+
NEEDS_LIST,
2830
/** Found a title */
2931
TITLE,
3032
/** Found tags */

importer/lightweightmarkup/src/main/java/org/itsallcode/openfasttrace/importer/lightweightmarkup/statemachine/LineParserStateMachine.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import java.util.*;
44
import java.util.logging.Logger;
5+
import java.util.regex.Pattern;
6+
7+
import static java.util.regex.Pattern.UNICODE_CHARACTER_CLASS;
58

69
/**
710
* This machine implements the core of a state based parser.
@@ -19,10 +22,15 @@
1922
public class LineParserStateMachine
2023
{
2124
private static final Logger LOG = Logger.getLogger(LineParserStateMachine.class.getName());
25+
private static final Pattern PARSER_OFF_PATTERN = Pattern.compile("(?:^|\\W)oft:off(?:\\W|$)",
26+
UNICODE_CHARACTER_CLASS);
27+
private static final Pattern PARSER_ON_PATTERN = Pattern.compile("(?:^|\\W)oft:on(?:\\W|$)",
28+
UNICODE_CHARACTER_CLASS);
2229

2330
private LineParserState state = LineParserState.START;
2431
private String lastToken = "";
2532
private final Transition[] transitions;
33+
private boolean enabled = true;
2634

2735
/**
2836
* Create a new instance of the {@link LineParserStateMachine}
@@ -48,7 +56,29 @@ public LineParserStateMachine(final Transition[] transitions)
4856
* patterns that span multiple lines like underlined titles in
4957
* Markdown or RST.
5058
*/
59+
// [impl -> dsn~disabling-oft-parsing-for-parts-of-a-markup-file~1]
5160
public void step(final String line, final String nextLine)
61+
{
62+
if (enabled)
63+
{
64+
if (PARSER_OFF_PATTERN.matcher(line).find())
65+
{
66+
enabled = false;
67+
}
68+
else
69+
{
70+
stepEnabled(line, nextLine);
71+
}
72+
}
73+
else
74+
{
75+
if (PARSER_ON_PATTERN.matcher(line).find()) {
76+
enabled = true;
77+
}
78+
}
79+
}
80+
81+
private void stepEnabled(final String line, final String nextLine)
5282
{
5383
boolean matched = false;
5484
for (final Transition entry : this.transitions)

0 commit comments

Comments
 (0)