-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathXMessageStylesParser.java
More file actions
144 lines (120 loc) · 4.95 KB
/
XMessageStylesParser.java
File metadata and controls
144 lines (120 loc) · 4.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package messagerosa.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class XMessageStylesParser {
private final static List<Character> KEYWORDS = Arrays.asList('*', '_', '~', '`');
private final static List<Character> NO_SUB_PARSING_KEYWORDS = Arrays.asList('`');
private final static List<Character> BLOCK_KEYWORDS = Arrays.asList('`');
private final static boolean ALLOW_EMPTY = false;
private final static boolean PARSE_HIGHER_ORDER_END = true;
public static List<Style> parse(CharSequence text) {
return parse(text, 0, text.length() - 1);
}
public static List<Style> parse(CharSequence text, int start, int end) {
List<Style> styles = new ArrayList<>();
for (int i = start; i <= end; ++i) {
char c = text.charAt(i);
if (KEYWORDS.contains(c) && precededByWhiteSpace(text, i, start) && !followedByWhitespace(text, i, end)) {
if (BLOCK_KEYWORDS.contains(c) && isCharRepeatedTwoTimes(text, c, i + 1, end)) {
int to = seekEndBlock(text, c, i + 3, end);
if (to != -1 && (to != i + 5 || ALLOW_EMPTY)) {
String keyword = String.valueOf(c) + String.valueOf(c) + String.valueOf(c);
styles.add(new Style(keyword, i, to));
i = to;
continue;
}
}
int to = seekEnd(text, c, i + 1, end);
if (to != -1 && (to != i + 1 || ALLOW_EMPTY)) {
styles.add(new Style(c, i, to));
if (!NO_SUB_PARSING_KEYWORDS.contains(c)) {
styles.addAll(parse(text, i + 1, to - 1));
}
i = to;
}
}
}
return styles;
}
private static boolean isCharRepeatedTwoTimes(CharSequence text, char c, int index, int end) {
return index + 1 <= end && text.charAt(index) == c && text.charAt(index + 1) == c;
}
private static boolean precededByWhiteSpace(CharSequence text, int index, int start) {
return index == start || Character.isWhitespace(text.charAt(index - 1));
}
private static boolean followedByWhitespace(CharSequence text, int index, int end) {
return index >= end || Character.isWhitespace(text.charAt(index + 1));
}
private static int seekEnd(CharSequence text, char needle, int start, int end) {
for (int i = start; i <= end; ++i) {
char c = text.charAt(i);
if (c == needle && !Character.isWhitespace(text.charAt(i - 1))) {
if (!PARSE_HIGHER_ORDER_END || followedByWhitespace(text, i, end)) {
return i;
} else {
int higherOrder = seekHigherOrderEndWithoutNewBeginning(text, needle, i + 1, end);
if (higherOrder != -1) {
return higherOrder;
}
return i;
}
} else if (c == '\n') {
return -1;
}
}
return -1;
}
private static int seekHigherOrderEndWithoutNewBeginning(CharSequence text, char needle, int start, int end) {
int result = -1;
for (int i = start; i <= end; ++i) {
char c = text.charAt(i);
if (c == '\n') {
result = -1;
break;
}
boolean isNeedleChar = (c == needle);
boolean precededByWhitespace = precededByWhitespace(text, i, start);
boolean followedByWhitespace = followedByWhitespace(text, i, end);
if (isNeedleChar && precededByWhitespace && !followedByWhitespace) {
result = -1; // New beginning
break;
} else if (isNeedleChar && !Character.isWhitespace(text.charAt(i - 1)) && followedByWhitespace) {
result = i;
break;
}
}
return result;
}
private static int seekEndBlock(CharSequence text, char needle, int start, int end) {
for (int i = start; i <= end; ++i) {
char c = text.charAt(i);
if (c == needle && isCharRepeatedTwoTimes(text, needle, i + 1, end)) {
return i + 2;
}
}
return -1;
}
public static class Style {
private final String keyword;
private final int start;
private final int end;
public Style(char character, int start, int end) {
this(String.valueOf(character), start, end);
}
public Style(String keyword, int start, int end) {
this.keyword = keyword;
this.start = start;
this.end = end;
}
public String getKeyword() {
return keyword;
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
}
}