b3762d797f0b977f9627a6fe28325b6c4f3d141c
[jvcard.git] / TextCharacter.java
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 }