Crossfire JXClient, Trunk  R20561
Parser.java
Go to the documentation of this file.
1 /*
2  * This file is part of JXClient, the Fullscreen Java Crossfire Client.
3  *
4  * JXClient is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * JXClient is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with JXClient; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * Copyright (C) 2005-2008 Yann Chachkoff.
19  * Copyright (C) 2006-2011 Andreas Kirschbaum.
20  */
21 
22 package com.realtime.crossfire.jxclient.gui.log;
23 
24 import java.awt.Color;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.regex.Pattern;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31 
37 public class Parser {
38 
42  @NotNull
43  private static final Map<String, FontID> FONTS = new HashMap<>();
44 
45  static {
46  FONTS.put("print", FontID.PRINT);
47  FONTS.put("fixed", FontID.FIXED);
48  FONTS.put("arcane", FontID.ARCANE);
49  FONTS.put("hand", FontID.HAND);
50  FONTS.put("strange", FontID.STRANGE);
51  }
52 
56  @NotNull
57  private static final Map<String, Color> COLORS = new HashMap<>();
58 
59  static {
60  COLORS.put("black", Color.BLACK);
61  COLORS.put("blue", Color.BLUE);
62  COLORS.put("green", Color.GREEN);
63  COLORS.put("red", Color.RED);
64  COLORS.put("white", Color.WHITE);
65  }
66 
70  @NotNull
71  private static final Pattern WORD_SEPARATOR_PATTERN = Pattern.compile(" ");
72 
76  @NotNull
77  private static final Pattern END_OF_LINE_PATTERN = Pattern.compile(" *\n");
78 
82  private boolean bold;
83 
87  private boolean italic;
88 
92  private boolean underline;
93 
97  private FontID font = FontID.PRINT;
98 
102  @Nullable
103  private Color color;
104 
111  public void parse(@NotNull final CharSequence text, @Nullable final Color defaultColor, @NotNull final Buffer buffer) {
112  resetAttributes(defaultColor);
113  for (final String line : END_OF_LINE_PATTERN.split(text, -1)) {
114  parseLine(line, defaultColor, buffer);
115  }
116  buffer.prune();
117  }
118 
125  public void parseWithoutMediaTags(@NotNull final CharSequence text, @NotNull final Color color, @NotNull final Buffer buffer) {
126  if (text.length() == 0) {
127  return;
128  }
129 
130  resetAttributes(color);
131  for (final String line : END_OF_LINE_PATTERN.split(text, -1)) {
132  parseLineWithoutMediaTags(line, buffer);
133  }
134  buffer.prune();
135  }
136 
143  private void parseLine(@NotNull final String text, @Nullable final Color defaultColor, @NotNull final Buffer buffer) {
144  if (buffer.mergeLines(text, defaultColor)) {
145  buffer.replaceLine(parseLine(text+" [["+buffer.getLastCount()+" times]", defaultColor));
146  } else {
147  buffer.addLine(parseLine(text, defaultColor));
148  }
149  }
150 
157  @NotNull
158  private Line parseLine(@NotNull final String text, @Nullable final Color defaultColor) {
159  final Line line = new Line();
160 
161  int begin = 0;
162  boolean active = false;
163  final int iMax = text.length();
164  for (int i = 0; i < iMax; i++) {
165  final char ch = text.charAt(i);
166  if (active) {
167  if (ch == ']') {
168  processTag(text.substring(begin, i), defaultColor);
169  begin = i+1;
170  active = false;
171  } else if (ch == '[' && i == begin) {
172  processText("[", line);
173  begin = i+1;
174  active = false;
175  }
176  } else {
177  if (ch == '[') {
178  processText(text.substring(begin, i), line);
179  begin = i+1;
180  active = true;
181  }
182  }
183  }
184  if (!active) {
185  processText(text.substring(begin, iMax), line);
186  }
187 
188  return line;
189  }
190 
196  private void parseLineWithoutMediaTags(@NotNull final String text, @NotNull final Buffer buffer) {
197  final Line line = new Line();
198  if (buffer.mergeLines(text, null)) {
199  processText(text+" ["+buffer.getLastCount()+" times]", line);
200  buffer.replaceLine(line);
201  } else {
202  processText(text, line);
203  buffer.addLine(line);
204  }
205  }
206 
211  private void resetAttributes(@Nullable final Color defaultColor) {
212  bold = false;
213  italic = false;
214  underline = false;
215  font = FontID.PRINT;
216  color = defaultColor;
217  }
218 
225  private void processTag(@NotNull final String tag, @Nullable final Color defaultColor) {
226  if (tag.isEmpty()) {
227  return;
228  }
229 
230  if (tag.equals("b")) {
231  bold = true;
232  } else if (tag.equals("/b")) {
233  bold = false;
234  } else if (tag.equals("i")) {
235  italic = true;
236  } else if (tag.equals("/i")) {
237  italic = false;
238  } else if (tag.equals("ul")) {
239  underline = true;
240  } else if (tag.equals("/ul")) {
241  underline = false;
242  } else if (FONTS.containsKey(tag)) {
243  font = FONTS.get(tag);
244  assert font != null;
245  } else if (tag.startsWith("color=")) {
246  final String colorName = tag.substring(6).toLowerCase();
247  if (COLORS.containsKey(colorName)) {
248  color = COLORS.get(colorName);
249  assert color != null;
250  } else if (colorName.startsWith("#") && colorName.length() == 7) {
251  try {
252  color = Color.decode(colorName);
253  } catch (final NumberFormatException ignored) {
254  // never mind invalid colors
255  }
256  }
257  // ignore unknown color
258  } else if (tag.equals("/color")) {
259  color = defaultColor;
260  //} else {
261  // ignore unknown tag
262  }
263  }
264 
270  private void processText(@NotNull final String text, @NotNull final Line line) {
271  if (text.isEmpty()) {
272  return;
273  }
274 
275  final CharSequence newText;
276  final Segment prevSegment = line.getLastSegment();
277  if (prevSegment instanceof TextSegment) {
278  final TextSegment prevTextSegment = (TextSegment)prevSegment;
279  if (prevTextSegment.matches(bold, italic, underline, font, color)) {
280  newText = prevTextSegment.getText()+text;
281  line.removeLastSegment();
282  } else {
283  newText = text;
284  }
285  } else {
286  newText = text;
287  }
288 
289  final String[] words = WORD_SEPARATOR_PATTERN.split(newText, -1);
290  for (int i = 0; i < words.length-1; i++) {
291  line.addSegment(words[i]+" ", bold, italic, underline, font, color);
292  }
293  if (!words[words.length-1].isEmpty()) {
294  line.addSegment(words[words.length-1], bold, italic, underline, font, color);
295  }
296  }
297 
303  @NotNull
304  public static String toString(@NotNull final Color color) {
305  // function need not be efficient since it is used for regression tests
306  // only
307  for (final Entry<String, Color> e : COLORS.entrySet()) {
308  if (e.getValue() == color) {
309  return e.getKey();
310  }
311  }
312 
313  return color.toString();
314  }
315 
316 }
String getText()
Returns the text to display.
Manages the contents of one text line.
Definition: Line.java:38
Parser for parsing drawextinfo messages received from a Crossfire server to update a Buffer instance...
Definition: Parser.java:37
static final Pattern END_OF_LINE_PATTERN
Pattern to match line breaks.
Definition: Parser.java:77
boolean bold
Whether bold face is enabled.
Definition: Parser.java:82
STRANGE
The font used by the [strange] tag.
Definition: FontID.java:53
boolean italic
Whether italic face is enabled.
Definition: Parser.java:87
void processTag(@NotNull final String tag, @Nullable final Color defaultColor)
Processes a tag.
Definition: Parser.java:225
void parseLineWithoutMediaTags(@NotNull final String text, @NotNull final Buffer buffer)
Parses one text line of a plain text message without media tags.
Definition: Parser.java:196
One segment of a Line which should be displayed without changing text attributes. ...
Definition: Segment.java:34
One segment of a Line which should be displayed without changing attributes.
void parseLine(@NotNull final String text, @Nullable final Color defaultColor, @NotNull final Buffer buffer)
Parses one text line.
Definition: Parser.java:143
void parseWithoutMediaTags(@NotNull final CharSequence text, @NotNull final Color color, @NotNull final Buffer buffer)
Parses a plain text message without media tags.
Definition: Parser.java:125
FIXED
The font used by the [fixed] tag.
Definition: FontID.java:38
static String toString(@NotNull final Color color)
Returns the string representation for a color.
Definition: Parser.java:304
Manages the contents of the contents of a log window.
Definition: Buffer.java:41
void processText(@NotNull final String text, @NotNull final Line line)
Processes one text segment.
Definition: Parser.java:270
static final Pattern WORD_SEPARATOR_PATTERN
The pattern to split a string into words.
Definition: Parser.java:71
void resetAttributes(@Nullable final Color defaultColor)
Resets all attributes to default values.
Definition: Parser.java:211
static final Map< String, Color > COLORS
Maps color tag name to color instance.
Definition: Parser.java:57
void parse(@NotNull final CharSequence text, @Nullable final Color defaultColor, @NotNull final Buffer buffer)
Parses a text message.
Definition: Parser.java:111
HAND
The font used by the [hand] tag.
Definition: FontID.java:48
ARCANE
The font used by the [arcane] tag.
Definition: FontID.java:43
boolean matches(final boolean bold, final boolean italic, final boolean underline, @NotNull final FontID font, @Nullable final Color color)
Returns whether this segment matches the given attributes.
boolean underline
Whether underlining is enabled.
Definition: Parser.java:92
static final Map< String, FontID > FONTS
Maps font tag name to font instance.
Definition: Parser.java:43
Line parseLine(@NotNull final String text, @Nullable final Color defaultColor)
Parses one text line.
Definition: Parser.java:158