2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.util
.ArrayList
;
32 import java
.util
.HashSet
;
33 import java
.util
.List
;
35 import java
.util
.ResourceBundle
;
36 import java
.util
.regex
.Matcher
;
37 import java
.util
.regex
.Pattern
;
40 * A Topic is a page of help text with a title and possibly links to other
43 public class Topic
implements Comparable
<Topic
> {
48 private static final ResourceBundle i18n
= ResourceBundle
.getBundle(Topic
.class.getName());
50 // ------------------------------------------------------------------------
51 // Constants --------------------------------------------------------------
52 // ------------------------------------------------------------------------
55 * The "not found" topic to display when a key or index term does not
56 * have an associated topic. Note package private access.
58 static Topic NOT_FOUND
= null;
61 * The regex for identifying index tags.
63 private static final String INDEX_REGEX_STR
= "\\#\\{([^\\}]*)\\}";
66 * The regex for identifying link tags.
68 private static final String LINK_REGEX_STR
= "\\[([^\\]]*)\\]\\(([^\\)]*)\\)";
71 * The regex for identifying words.
73 private static final String WORD_REGEX_STR
= "[ \\t]+";
76 * The index match regex.
78 private static Pattern INDEX_REGEX
;
81 * The link match regex.
83 private static Pattern LINK_REGEX
;
86 * The word match regex.
88 private static Pattern WORD_REGEX
;
90 // ------------------------------------------------------------------------
91 // Variables --------------------------------------------------------------
92 // ------------------------------------------------------------------------
95 * The title for this topic.
100 * The text for this topic.
105 * The index keys in this topic.
107 private Set
<String
> indexKeys
= new HashSet
<String
>();
110 * The links in this topic.
112 private List
<Link
> links
= new ArrayList
<Link
>();
114 // ------------------------------------------------------------------------
115 // Constructors -----------------------------------------------------------
116 // ------------------------------------------------------------------------
119 * Static constructor.
123 INDEX_REGEX
= Pattern
.compile(INDEX_REGEX_STR
);
124 LINK_REGEX
= Pattern
.compile(LINK_REGEX_STR
);
125 WORD_REGEX
= Pattern
.compile(WORD_REGEX_STR
);
127 NOT_FOUND
= new Topic(i18n
.getString("topicNotFoundTitle"),
128 i18n
.getString("topicNotFoundText"));
129 } catch (Exception e
) {
135 * Public constructor.
137 * @param title the topic title
138 * @param text the topic text
140 public Topic(final String title
, final String text
) {
146 * Package private constructor.
148 * @param title the topic title
149 * @param text the topic text
150 * @param links links to add after processing text
152 Topic(final String title
, final String text
, final List
<Link
> links
) {
155 this.links
.addAll(links
);
158 // ------------------------------------------------------------------------
159 // Topic ------------------------------------------------------------------
160 // ------------------------------------------------------------------------
163 * Get the topic title.
167 public String
getTitle() {
172 * Get the topic text.
176 public String
getText() {
181 * Get the index keys.
185 public Set
<String
> getIndexKeys() {
194 public List
<Link
> getLinks() {
199 * Comparison operator.
201 * @param that another Topic instance
202 * @return comparison by topic title
204 public int compareTo(final Topic that
) {
205 return title
.compareTo(that
.title
);
209 * Generate a human-readable string for this widget.
211 * @return a human-readable string
214 public String
toString() {
215 return String
.format("%s(%8x) topic %s text %s links %s indexKeys %s",
216 getClass().getName(), hashCode(), title
, text
, links
, indexKeys
);
220 * Process a string through the regexes, building up the indexes and
223 * @param text the text to process
225 private void processText(final String text
) {
226 StringBuilder sb
= new StringBuilder();
227 String
[] lines
= text
.split("\n");
229 for (String line
: lines
) {
232 String cleanLine
= "";
234 // System.err.println("LINE " + wordIndex + " : '" + line + "'");
236 Matcher index
= INDEX_REGEX
.matcher(line
);
238 while (index
.find()) {
239 cleanLine
+= line
.substring(start
, index
.start());
240 String key
= index
.group(1);
243 // System.err.println("ADD KEY: " + key);
246 cleanLine
+= line
.substring(start
);
252 System.err.println("line after removing #{index} tags: " +
253 wordIndex + " '" + line + "'");
256 Matcher link
= LINK_REGEX
.matcher(line
);
259 boolean hasLink
= link
.find();
261 // System.err.println("hasLink " + hasLink);
265 if (hasLink
== false) {
266 cleanLine
+= line
.substring(start
);
268 String remaining
= line
.substring(start
).trim();
269 Matcher word
= WORD_REGEX
.matcher(remaining
);
270 while (word
.find()) {
271 // System.err.println("word.find() true");
274 if (remaining
.length() > 0) {
275 // The last word on the line.
281 assert (hasLink
== true);
283 int linkWordIndex
= link
.start();
284 int cleanLineStart
= cleanLine
.length();
285 cleanLine
+= line
.substring(start
, linkWordIndex
);
286 String linkText
= link
.group(1);
287 String topic
= link
.group(2);
288 cleanLine
+= linkText
;
291 // Increment wordIndex until we reach the first word of
293 Matcher word
= WORD_REGEX
.matcher(cleanLine
.
294 substring(cleanLineStart
));
295 while (word
.find()) {
296 if (word
.end() <= linkWordIndex
) {
299 // We have found the word that matches the first
300 // word of link text, bail out.
305 System.err.println("ADD LINK --> " + topic + ": '" +
306 linkText + "' word index " + wordIndex);
308 links
.add(new Link(topic
, linkText
, wordIndex
));
310 // The rest of the words in the link text.
311 while (word
.find()) {
314 // The final word after the last whitespace.
317 hasLink
= link
.find();
325 System.err.println("line after removing [link](...) tags: '" +
329 // Append the entire line.
330 sb
.append(cleanLine
);
333 this.text
= sb
.toString();
335 } // for (String line: lines)