Skip to content

Commit 73384d2

Browse files
committed
Merge pull request #164 from cemartins/xml-editor-demo
Add XML editor demo.
2 parents ece0cde + a10feff commit 73384d2

2 files changed

Lines changed: 154 additions & 0 deletions

File tree

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package org.fxmisc.richtext.demo;
2+
3+
import java.util.Collection;
4+
import java.util.Collections;
5+
import java.util.regex.Matcher;
6+
import java.util.regex.Pattern;
7+
8+
import org.fxmisc.richtext.CodeArea;
9+
import org.fxmisc.richtext.LineNumberFactory;
10+
import org.fxmisc.richtext.StyleSpans;
11+
import org.fxmisc.richtext.StyleSpansBuilder;
12+
13+
import javafx.application.Application;
14+
import javafx.scene.Scene;
15+
import javafx.scene.layout.StackPane;
16+
import javafx.stage.Stage;
17+
18+
public class XMLEditor extends Application {
19+
20+
private static final Pattern XML_TAG = Pattern.compile("(?<ELEMENT>(</?\\h*)(\\w+)([^<>]*)(\\h*/?>))"
21+
+"|(?<COMMENT><!--[^<>]+-->)");
22+
23+
private static final Pattern ATTRIBUTES = Pattern.compile("(\\w+\\h*)(=)(\\h*\"[^\"]+\")");
24+
25+
private static final int GROUP_OPEN_BRACKET = 2;
26+
private static final int GROUP_ELEMENT_NAME = 3;
27+
private static final int GROUP_ATTRIBUTES_SECTION = 4;
28+
private static final int GROUP_CLOSE_BRACKET = 5;
29+
private static final int GROUP_ATTRIBUTE_NAME = 1;
30+
private static final int GROUP_EQUAL_SYMBOL = 2;
31+
private static final int GROUP_ATTRIBUTE_VALUE = 3;
32+
33+
private static final String sampleCode = String.join("\n", new String[] {
34+
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>",
35+
"<!-- Sample XML -->",
36+
"< orders >",
37+
" <Order number=\"1\" table=\"center\">",
38+
" <items>",
39+
" <Item>",
40+
" <type>ESPRESSO</type>",
41+
" <shots>2</shots>",
42+
" <iced>false</iced>",
43+
" <orderNumber>1</orderNumber>",
44+
" </Item>",
45+
" <Item>",
46+
" <type>CAPPUCCINO</type>",
47+
" <shots>1</shots>",
48+
" <iced>false</iced>",
49+
" <orderNumber>1</orderNumber>",
50+
" </Item>",
51+
" <Item>",
52+
" <type>LATTE</type>",
53+
" <shots>2</shots>",
54+
" <iced>false</iced>",
55+
" <orderNumber>1</orderNumber>",
56+
" </Item>",
57+
" <Item>",
58+
" <type>MOCHA</type>",
59+
" <shots>3</shots>",
60+
" <iced>true</iced>",
61+
" <orderNumber>1</orderNumber>",
62+
" </Item>",
63+
" </items>",
64+
" </Order>",
65+
"</orders>"
66+
});
67+
68+
69+
public static void main(String[] args) {
70+
launch(args);
71+
}
72+
73+
@Override
74+
public void start(Stage primaryStage) {
75+
CodeArea codeArea = new CodeArea();
76+
codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
77+
78+
codeArea.textProperty().addListener((obs, oldText, newText) -> {
79+
codeArea.setStyleSpans(0, computeHighlighting(newText));
80+
});
81+
codeArea.replaceText(0, 0, sampleCode);
82+
83+
Scene scene = new Scene(new StackPane(codeArea), 600, 400);
84+
scene.getStylesheets().add(JavaKeywordsAsync.class.getResource("xml-highlighting.css").toExternalForm());
85+
primaryStage.setScene(scene);
86+
primaryStage.setTitle("XML Editor Demo");
87+
primaryStage.show();
88+
}
89+
90+
private static StyleSpans<Collection<String>> computeHighlighting(String text) {
91+
92+
Matcher matcher = XML_TAG.matcher(text);
93+
int lastKwEnd = 0;
94+
StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
95+
while(matcher.find()) {
96+
97+
spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
98+
if(matcher.group("COMMENT") != null) {
99+
spansBuilder.add(Collections.singleton("comment"), matcher.end() - matcher.start());
100+
}
101+
else {
102+
if(matcher.group("ELEMENT") != null) {
103+
String attributesText = matcher.group(GROUP_ATTRIBUTES_SECTION);
104+
105+
spansBuilder.add(Collections.singleton("tagmark"), matcher.end(GROUP_OPEN_BRACKET) - matcher.start(GROUP_OPEN_BRACKET));
106+
spansBuilder.add(Collections.singleton("anytag"), matcher.end(GROUP_ELEMENT_NAME) - matcher.end(GROUP_OPEN_BRACKET));
107+
108+
if(!attributesText.isEmpty()) {
109+
110+
lastKwEnd = 0;
111+
112+
Matcher amatcher = ATTRIBUTES.matcher(attributesText);
113+
while(amatcher.find()) {
114+
spansBuilder.add(Collections.emptyList(), amatcher.start() - lastKwEnd);
115+
spansBuilder.add(Collections.singleton("attribute"), amatcher.end(GROUP_ATTRIBUTE_NAME) - amatcher.start(GROUP_ATTRIBUTE_NAME));
116+
spansBuilder.add(Collections.singleton("tagmark"), amatcher.end(GROUP_EQUAL_SYMBOL) - amatcher.end(GROUP_ATTRIBUTE_NAME));
117+
spansBuilder.add(Collections.singleton("avalue"), amatcher.end(GROUP_ATTRIBUTE_VALUE) - amatcher.end(GROUP_EQUAL_SYMBOL));
118+
lastKwEnd = amatcher.end();
119+
}
120+
if(attributesText.length() > lastKwEnd)
121+
spansBuilder.add(Collections.emptyList(), attributesText.length() - lastKwEnd);
122+
}
123+
124+
lastKwEnd = matcher.end(GROUP_ATTRIBUTES_SECTION);
125+
126+
spansBuilder.add(Collections.singleton("tagmark"), matcher.end(GROUP_CLOSE_BRACKET) - lastKwEnd);
127+
}
128+
}
129+
lastKwEnd = matcher.end();
130+
}
131+
spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
132+
return spansBuilder.create();
133+
}
134+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.tagmark {
2+
-fx-fill: gray;
3+
}
4+
.anytag {
5+
-fx-fill: crimson;
6+
}
7+
.paren {
8+
-fx-fill: firebrick;
9+
-fx-font-weight: bold;
10+
}
11+
.attribute {
12+
-fx-fill: darkviolet;
13+
}
14+
.avalue {
15+
-fx-fill: black;
16+
}
17+
18+
.comment {
19+
-fx-fill: teal;
20+
}

0 commit comments

Comments
 (0)