16
16
*/
17
17
package org .apache .logging .log4j .docgen .processor ;
18
18
19
+ import java .util .ArrayDeque ;
20
+ import java .util .ArrayList ;
21
+ import java .util .Deque ;
19
22
import java .util .EmptyStackException ;
23
+ import java .util .List ;
20
24
import java .util .function .Function ;
25
+ import java .util .regex .Pattern ;
21
26
import org .apache .logging .log4j .docgen .processor .internal .BlockImpl ;
22
27
import org .apache .logging .log4j .docgen .processor .internal .DocumentImpl ;
23
28
import org .apache .logging .log4j .docgen .processor .internal .SectionImpl ;
26
31
import org .asciidoctor .ast .StructuralNode ;
27
32
28
33
final class AsciidocData {
34
+ private static final Pattern WHITESPACE_SEQUENCE = Pattern .compile ("\\ s+" );
35
+ private static final String SPACE = " " ;
36
+ private static final char SPACE_CHAR = ' ' ;
37
+ private static final char CODE_CHAR = '`' ;
38
+ private static final String NEW_LINE = "\n " ;
39
+
29
40
private final Document document ;
30
41
private int currentSectionLevel ;
31
42
private StructuralNode currentNode ;
32
- // not attached to the current node
33
- private Block currentParagraph ;
34
- private final StringBuilder currentLine ;
43
+ // A stack of nested text blocks. Each can have a different style.
44
+ private final Deque < Block > paragraphs = new ArrayDeque <>() ;
45
+ private final Deque < StringBuilder > lines = new ArrayDeque <>() ;
35
46
36
47
public AsciidocData () {
37
48
document = new DocumentImpl ();
38
49
currentSectionLevel = 1 ;
39
50
currentNode = document ;
40
- currentParagraph = new BlockImpl (currentNode );
41
- currentLine = new StringBuilder ();
51
+ paragraphs . push ( new BlockImpl (currentNode ) );
52
+ lines . push ( new StringBuilder () );
42
53
}
43
54
44
55
public void newLine () {
45
56
// Remove trailing space
46
- final String line = currentLine .toString ().stripTrailing ();
57
+ final String line = getCurrentLine () .toString ().stripTrailing ();
47
58
// Ignore leading empty lines
48
- if (!currentParagraph .getLines ().isEmpty () || !line .isEmpty ()) {
49
- currentParagraph .getLines ().add (line );
59
+ if (!getCurrentParagraph () .getLines ().isEmpty () || !line .isEmpty ()) {
60
+ getCurrentParagraph () .getLines ().add (line );
50
61
}
51
- currentLine .setLength (0 );
62
+ getCurrentLine () .setLength (0 );
52
63
}
53
64
54
65
public AsciidocData append (final String text ) {
55
66
final String [] lines = text .split ("\r ?\n " , -1 );
56
67
for (int i = 0 ; i < lines .length ; i ++) {
57
- currentLine .append (lines [i ]);
68
+ getCurrentLine () .append (lines [i ]);
58
69
if (i != lines .length - 1 ) {
59
70
newLine ();
60
71
}
61
72
}
62
73
return this ;
63
74
}
64
75
65
- public void appendWords (final String words ) {
66
- if (words .isBlank ()) {
67
- return ;
76
+ public AsciidocData appendAdjustingSpace (final CharSequence text ) {
77
+ final String normalized = WHITESPACE_SEQUENCE .matcher (text ).replaceAll (SPACE );
78
+ if (!normalized .isEmpty ()) {
79
+ final StringBuilder currentLine = getCurrentLine ();
80
+ // Last char of current line or space
81
+ final char lineLastChar = currentLine .isEmpty () ? SPACE_CHAR : currentLine .charAt (currentLine .length () - 1 );
82
+ // First char of test
83
+ final char textFirstChar = normalized .charAt (0 );
84
+ if (lineLastChar == SPACE_CHAR && textFirstChar == SPACE_CHAR ) {
85
+ // Merge spaces
86
+ currentLine .append (normalized , 1 , normalized .length ());
87
+ } else if (lineLastChar == CODE_CHAR && Character .isAlphabetic (textFirstChar )) {
88
+ currentLine .append (SPACE_CHAR ).append (normalized );
89
+ } else {
90
+ currentLine .append (normalized );
91
+ }
68
92
}
69
- // Separate text from previous words
70
- if (!currentLine .isEmpty () && Character .isAlphabetic (words .codePointAt (0 ))) {
71
- currentLine .append (" " );
93
+ return this ;
94
+ }
95
+
96
+ public void newTextSpan (final String style ) {
97
+ paragraphs .push (new BlockImpl (paragraphs .peek ()));
98
+ lines .push (new StringBuilder ());
99
+ }
100
+
101
+ public String popTextSpan () {
102
+ // Flush the paragraph
103
+ final StringBuilder line = lines .peek ();
104
+ if (line != null && !line .isEmpty ()) {
105
+ newLine ();
72
106
}
73
- currentLine .append (words );
107
+ lines .pop ();
108
+ return String .join (SPACE , paragraphs .pop ().getLines ());
74
109
}
75
110
76
111
public void newParagraph () {
112
+ newParagraph (currentNode );
113
+ }
114
+
115
+ private void newParagraph (final StructuralNode parent ) {
77
116
newLine ();
117
+ final Block currentParagraph = paragraphs .pop ();
78
118
final java .util .List <String > lines = currentParagraph .getLines ();
79
119
// Remove trailing empty lines
80
120
for (int i = lines .size () - 1 ; i >= 0 ; i --) {
@@ -84,20 +124,20 @@ public void newParagraph() {
84
124
}
85
125
if (!currentParagraph .getLines ().isEmpty ()) {
86
126
currentNode .append (currentParagraph );
87
- currentParagraph = new BlockImpl (currentNode );
88
127
}
128
+ paragraphs .push (new BlockImpl (parent ));
89
129
}
90
130
91
131
public StructuralNode getCurrentNode () {
92
132
return currentNode ;
93
133
}
94
134
95
135
public Block getCurrentParagraph () {
96
- return currentParagraph ;
136
+ return paragraphs . peek () ;
97
137
}
98
138
99
139
public StringBuilder getCurrentLine () {
100
- return currentLine ;
140
+ return lines . peek () ;
101
141
}
102
142
103
143
public Document getDocument () {
@@ -121,28 +161,24 @@ public void setCurrentSectionLevel(final int sectionLevel) {
121
161
* @param supplier a function to create a new node that takes its parent node a parameter.
122
162
*/
123
163
public StructuralNode pushChildNode (final Function <? super StructuralNode , ? extends StructuralNode > supplier ) {
124
- // Flushes the current paragraph
125
- newParagraph ();
126
-
127
164
final StructuralNode child = supplier .apply (currentNode );
128
- // Creates a new current paragraph
129
- currentParagraph = new BlockImpl (child );
165
+
166
+ // Flushes and reparents the current paragraph
167
+ newParagraph (child );
130
168
131
169
currentNode .append (child );
132
170
return currentNode = child ;
133
171
}
134
172
135
173
public void popNode () {
136
174
final StructuralNode currentNode = this .currentNode ;
137
- // Flushes the current paragraph
138
- newParagraph ();
139
175
140
176
final StructuralNode parent = (StructuralNode ) currentNode .getParent ();
141
177
if (parent == null ) {
142
178
throw new EmptyStackException ();
143
179
}
144
- // Creates a new current paragraph
145
- currentParagraph = new BlockImpl (parent );
180
+ // Flushes and creates a new current paragraph
181
+ newParagraph (parent );
146
182
147
183
this .currentNode = parent ;
148
184
}
0 commit comments