Commit | Line | Data |
---|---|---|
a3b510ab NR |
1 | /* |
2 | * This file is part of lanterna (http://code.google.com/p/lanterna/). | |
3 | * | |
4 | * lanterna is free software: you can redistribute it and/or modify | |
5 | * it under the terms of the GNU Lesser General Public License as published by | |
6 | * the Free Software Foundation, either version 3 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program 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 Lesser General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU Lesser General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | * | |
17 | * Copyright (C) 2010-2015 Martin | |
18 | */ | |
19 | package com.googlecode.lanterna; | |
20 | ||
21 | import java.util.Arrays; | |
22 | import java.util.Collection; | |
23 | import java.util.EnumSet; | |
24 | ||
25 | /** | |
26 | * Represents a single character with additional metadata such as colors and modifiers. This class is immutable and | |
27 | * cannot be modified after creation. | |
28 | * @author Martin | |
29 | */ | |
30 | public class TextCharacter { | |
31 | private static EnumSet<SGR> toEnumSet(SGR... modifiers) { | |
32 | if(modifiers.length == 0) { | |
33 | return EnumSet.noneOf(SGR.class); | |
34 | } | |
35 | else { | |
36 | return EnumSet.copyOf(Arrays.asList(modifiers)); | |
37 | } | |
38 | } | |
39 | ||
40 | public static final TextCharacter DEFAULT_CHARACTER = new TextCharacter(' ', TextColor.ANSI.DEFAULT, TextColor.ANSI.DEFAULT); | |
41 | ||
42 | private final char character; | |
43 | private final TextColor foregroundColor; | |
44 | private final TextColor backgroundColor; | |
45 | private final EnumSet<SGR> modifiers; //This isn't immutable, but we should treat it as such and not expose it! | |
46 | ||
47 | /** | |
48 | * Creates a {@code ScreenCharacter} based on a supplied character, with default colors and no extra modifiers. | |
49 | * @param character Physical character to use | |
50 | */ | |
51 | public TextCharacter(char character) { | |
52 | this(character, TextColor.ANSI.DEFAULT, TextColor.ANSI.DEFAULT); | |
53 | } | |
54 | ||
55 | /** | |
56 | * Copies another {@code ScreenCharacter} | |
57 | * @param character screenCharacter to copy from | |
58 | */ | |
59 | public TextCharacter(TextCharacter character) { | |
60 | this(character.getCharacter(), | |
61 | character.getForegroundColor(), | |
62 | character.getBackgroundColor(), | |
63 | character.getModifiers().toArray(new SGR[character.getModifiers().size()])); | |
64 | } | |
65 | ||
66 | /** | |
67 | * Creates a new {@code ScreenCharacter} based on a physical character, color information and optional modifiers. | |
68 | * @param character Physical character to refer to | |
69 | * @param foregroundColor Foreground color the character has | |
70 | * @param backgroundColor Background color the character has | |
71 | * @param styles Optional list of modifiers to apply when drawing the character | |
72 | */ | |
73 | @SuppressWarnings("WeakerAccess") | |
74 | public TextCharacter( | |
75 | char character, | |
76 | TextColor foregroundColor, | |
77 | TextColor backgroundColor, | |
78 | SGR... styles) { | |
79 | ||
80 | this(character, | |
81 | foregroundColor, | |
82 | backgroundColor, | |
83 | toEnumSet(styles)); | |
84 | } | |
85 | ||
86 | /** | |
87 | * Creates a new {@code ScreenCharacter} based on a physical character, color information and a set of modifiers. | |
88 | * @param character Physical character to refer to | |
89 | * @param foregroundColor Foreground color the character has | |
90 | * @param backgroundColor Background color the character has | |
91 | * @param modifiers Set of modifiers to apply when drawing the character | |
92 | */ | |
93 | public TextCharacter( | |
94 | char character, | |
95 | TextColor foregroundColor, | |
96 | TextColor backgroundColor, | |
97 | EnumSet<SGR> modifiers) { | |
98 | ||
99 | if(foregroundColor == null) { | |
100 | foregroundColor = TextColor.ANSI.DEFAULT; | |
101 | } | |
102 | if(backgroundColor == null) { | |
103 | backgroundColor = TextColor.ANSI.DEFAULT; | |
104 | } | |
105 | ||
106 | this.character = character; | |
107 | this.foregroundColor = foregroundColor; | |
108 | this.backgroundColor = backgroundColor; | |
109 | this.modifiers = EnumSet.copyOf(modifiers); | |
110 | } | |
111 | ||
112 | /** | |
113 | * The actual character this TextCharacter represents | |
114 | * @return character of the TextCharacter | |
115 | */ | |
116 | public char getCharacter() { | |
117 | return character; | |
118 | } | |
119 | ||
120 | /** | |
121 | * Foreground color specified for this TextCharacter | |
122 | * @return Foreground color of this TextCharacter | |
123 | */ | |
124 | public TextColor getForegroundColor() { | |
125 | return foregroundColor; | |
126 | } | |
127 | ||
128 | /** | |
129 | * Background color specified for this TextCharacter | |
130 | * @return Background color of this TextCharacter | |
131 | */ | |
132 | public TextColor getBackgroundColor() { | |
133 | return backgroundColor; | |
134 | } | |
135 | ||
136 | /** | |
137 | * Returns a set of all active modifiers on this TextCharacter | |
138 | * @return Set of active SGR codes | |
139 | */ | |
140 | public EnumSet<SGR> getModifiers() { | |
141 | return EnumSet.copyOf(modifiers); | |
142 | } | |
143 | ||
144 | /** | |
145 | * Returns true if this TextCharacter has the bold modifier active | |
146 | * @return {@code true} if this TextCharacter has the bold modifier active | |
147 | */ | |
148 | public boolean isBold() { | |
149 | return modifiers.contains(SGR.BOLD); | |
150 | } | |
151 | ||
152 | /** | |
153 | * Returns true if this TextCharacter has the reverse modifier active | |
154 | * @return {@code true} if this TextCharacter has the reverse modifier active | |
155 | */ | |
156 | public boolean isReversed() { | |
157 | return modifiers.contains(SGR.REVERSE); | |
158 | } | |
159 | ||
160 | /** | |
161 | * Returns true if this TextCharacter has the underline modifier active | |
162 | * @return {@code true} if this TextCharacter has the underline modifier active | |
163 | */ | |
164 | public boolean isUnderlined() { | |
165 | return modifiers.contains(SGR.UNDERLINE); | |
166 | } | |
167 | ||
168 | /** | |
169 | * Returns true if this TextCharacter has the blink modifier active | |
170 | * @return {@code true} if this TextCharacter has the blink modifier active | |
171 | */ | |
172 | public boolean isBlinking() { | |
173 | return modifiers.contains(SGR.BLINK); | |
174 | } | |
175 | ||
176 | /** | |
177 | * Returns true if this TextCharacter has the bordered modifier active | |
178 | * @return {@code true} if this TextCharacter has the bordered modifier active | |
179 | */ | |
180 | public boolean isBordered() { | |
181 | return modifiers.contains(SGR.BORDERED); | |
182 | } | |
183 | ||
184 | /** | |
185 | * Returns true if this TextCharacter has the crossed-out modifier active | |
186 | * @return {@code true} if this TextCharacter has the crossed-out modifier active | |
187 | */ | |
188 | public boolean isCrossedOut() { | |
189 | return modifiers.contains(SGR.CROSSED_OUT); | |
190 | } | |
191 | ||
192 | /** | |
193 | * Returns a new TextCharacter with the same colors and modifiers but a different underlying character | |
194 | * @param character Character the copy should have | |
195 | * @return Copy of this TextCharacter with different underlying character | |
196 | */ | |
197 | @SuppressWarnings("SameParameterValue") | |
198 | public TextCharacter withCharacter(char character) { | |
199 | if(this.character == character) { | |
200 | return this; | |
201 | } | |
202 | return new TextCharacter(character, foregroundColor, backgroundColor, modifiers); | |
203 | } | |
204 | ||
205 | /** | |
206 | * Returns a copy of this TextCharacter with a specified foreground color | |
207 | * @param foregroundColor Foreground color the copy should have | |
208 | * @return Copy of the TextCharacter with a different foreground color | |
209 | */ | |
210 | public TextCharacter withForegroundColor(TextColor foregroundColor) { | |
211 | if(this.foregroundColor == foregroundColor || this.foregroundColor.equals(foregroundColor)) { | |
212 | return this; | |
213 | } | |
214 | return new TextCharacter(character, foregroundColor, backgroundColor, modifiers); | |
215 | } | |
216 | ||
217 | /** | |
218 | * Returns a copy of this TextCharacter with a specified background color | |
219 | * @param backgroundColor Background color the copy should have | |
220 | * @return Copy of the TextCharacter with a different background color | |
221 | */ | |
222 | public TextCharacter withBackgroundColor(TextColor backgroundColor) { | |
223 | if(this.backgroundColor == backgroundColor || this.backgroundColor.equals(backgroundColor)) { | |
224 | return this; | |
225 | } | |
226 | return new TextCharacter(character, foregroundColor, backgroundColor, modifiers); | |
227 | } | |
228 | ||
229 | /** | |
230 | * Returns a copy of this TextCharacter with specified list of SGR modifiers. None of the currently active SGR codes | |
231 | * will be carried over to the copy, only those in the passed in value. | |
232 | * @param modifiers SGR modifiers the copy should have | |
233 | * @return Copy of the TextCharacter with a different set of SGR modifiers | |
234 | */ | |
235 | public TextCharacter withModifiers(Collection<SGR> modifiers) { | |
236 | EnumSet<SGR> newSet = EnumSet.copyOf(modifiers); | |
237 | if(modifiers.equals(newSet)) { | |
238 | return this; | |
239 | } | |
240 | return new TextCharacter(character, foregroundColor, backgroundColor, newSet); | |
241 | } | |
242 | ||
243 | /** | |
244 | * Returns a copy of this TextCharacter with an additional SGR modifier. All of the currently active SGR codes | |
245 | * will be carried over to the copy, in addition to the one specified. | |
246 | * @param modifier SGR modifiers the copy should have in additional to all currently present | |
247 | * @return Copy of the TextCharacter with a new SGR modifier | |
248 | */ | |
249 | public TextCharacter withModifier(SGR modifier) { | |
250 | if(modifiers.contains(modifier)) { | |
251 | return this; | |
252 | } | |
253 | EnumSet<SGR> newSet = EnumSet.copyOf(this.modifiers); | |
254 | newSet.add(modifier); | |
255 | return new TextCharacter(character, foregroundColor, backgroundColor, newSet); | |
256 | } | |
257 | ||
258 | /** | |
259 | * Returns a copy of this TextCharacter with an SGR modifier removed. All of the currently active SGR codes | |
260 | * will be carried over to the copy, except for the one specified. If the current TextCharacter doesn't have the | |
261 | * SGR specified, it will return itself. | |
262 | * @param modifier SGR modifiers the copy should not have | |
263 | * @return Copy of the TextCharacter without the SGR modifier | |
264 | */ | |
265 | public TextCharacter withoutModifier(SGR modifier) { | |
266 | if(!modifiers.contains(modifier)) { | |
267 | return this; | |
268 | } | |
269 | EnumSet<SGR> newSet = EnumSet.copyOf(this.modifiers); | |
270 | newSet.remove(modifier); | |
271 | return new TextCharacter(character, foregroundColor, backgroundColor, newSet); | |
272 | } | |
273 | ||
274 | @SuppressWarnings("SimplifiableIfStatement") | |
275 | @Override | |
276 | public boolean equals(Object obj) { | |
277 | if(obj == null) { | |
278 | return false; | |
279 | } | |
280 | if(getClass() != obj.getClass()) { | |
281 | return false; | |
282 | } | |
283 | final TextCharacter other = (TextCharacter) obj; | |
284 | if(this.character != other.character) { | |
285 | return false; | |
286 | } | |
287 | if(this.foregroundColor != other.foregroundColor && (this.foregroundColor == null || !this.foregroundColor.equals(other.foregroundColor))) { | |
288 | return false; | |
289 | } | |
290 | if(this.backgroundColor != other.backgroundColor && (this.backgroundColor == null || !this.backgroundColor.equals(other.backgroundColor))) { | |
291 | return false; | |
292 | } | |
293 | return !(this.modifiers != other.modifiers && (this.modifiers == null || !this.modifiers.equals(other.modifiers))); | |
294 | } | |
295 | ||
296 | @Override | |
297 | public int hashCode() { | |
298 | int hash = 7; | |
299 | hash = 37 * hash + this.character; | |
300 | hash = 37 * hash + (this.foregroundColor != null ? this.foregroundColor.hashCode() : 0); | |
301 | hash = 37 * hash + (this.backgroundColor != null ? this.backgroundColor.hashCode() : 0); | |
302 | hash = 37 * hash + (this.modifiers != null ? this.modifiers.hashCode() : 0); | |
303 | return hash; | |
304 | } | |
305 | ||
306 | @Override | |
307 | public String toString() { | |
308 | return "TextCharacter{" + "character=" + character + ", foregroundColor=" + foregroundColor + ", backgroundColor=" + backgroundColor + ", modifiers=" + modifiers + '}'; | |
309 | } | |
310 | } |