/*
* This file is part of lanterna (http://code.google.com/p/lanterna/).
*
* lanterna is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* Copyright (C) 2010-2015 Martin
*/
package com.googlecode.lanterna;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
/**
* Represents a single character with additional metadata such as colors and modifiers. This class is immutable and
* cannot be modified after creation.
* @author Martin
*/
public class TextCharacter {
private static EnumSet toEnumSet(SGR... modifiers) {
if(modifiers.length == 0) {
return EnumSet.noneOf(SGR.class);
}
else {
return EnumSet.copyOf(Arrays.asList(modifiers));
}
}
public static final TextCharacter DEFAULT_CHARACTER = new TextCharacter(' ', TextColor.ANSI.DEFAULT, TextColor.ANSI.DEFAULT);
private final char character;
private final TextColor foregroundColor;
private final TextColor backgroundColor;
private final EnumSet modifiers; //This isn't immutable, but we should treat it as such and not expose it!
/**
* Creates a {@code ScreenCharacter} based on a supplied character, with default colors and no extra modifiers.
* @param character Physical character to use
*/
public TextCharacter(char character) {
this(character, TextColor.ANSI.DEFAULT, TextColor.ANSI.DEFAULT);
}
/**
* Copies another {@code ScreenCharacter}
* @param character screenCharacter to copy from
*/
public TextCharacter(TextCharacter character) {
this(character.getCharacter(),
character.getForegroundColor(),
character.getBackgroundColor(),
character.getModifiers().toArray(new SGR[character.getModifiers().size()]));
}
/**
* Creates a new {@code ScreenCharacter} based on a physical character, color information and optional modifiers.
* @param character Physical character to refer to
* @param foregroundColor Foreground color the character has
* @param backgroundColor Background color the character has
* @param styles Optional list of modifiers to apply when drawing the character
*/
@SuppressWarnings("WeakerAccess")
public TextCharacter(
char character,
TextColor foregroundColor,
TextColor backgroundColor,
SGR... styles) {
this(character,
foregroundColor,
backgroundColor,
toEnumSet(styles));
}
/**
* Creates a new {@code ScreenCharacter} based on a physical character, color information and a set of modifiers.
* @param character Physical character to refer to
* @param foregroundColor Foreground color the character has
* @param backgroundColor Background color the character has
* @param modifiers Set of modifiers to apply when drawing the character
*/
public TextCharacter(
char character,
TextColor foregroundColor,
TextColor backgroundColor,
EnumSet modifiers) {
if(foregroundColor == null) {
foregroundColor = TextColor.ANSI.DEFAULT;
}
if(backgroundColor == null) {
backgroundColor = TextColor.ANSI.DEFAULT;
}
this.character = character;
this.foregroundColor = foregroundColor;
this.backgroundColor = backgroundColor;
this.modifiers = EnumSet.copyOf(modifiers);
}
/**
* The actual character this TextCharacter represents
* @return character of the TextCharacter
*/
public char getCharacter() {
return character;
}
/**
* Foreground color specified for this TextCharacter
* @return Foreground color of this TextCharacter
*/
public TextColor getForegroundColor() {
return foregroundColor;
}
/**
* Background color specified for this TextCharacter
* @return Background color of this TextCharacter
*/
public TextColor getBackgroundColor() {
return backgroundColor;
}
/**
* Returns a set of all active modifiers on this TextCharacter
* @return Set of active SGR codes
*/
public EnumSet getModifiers() {
return EnumSet.copyOf(modifiers);
}
/**
* Returns true if this TextCharacter has the bold modifier active
* @return {@code true} if this TextCharacter has the bold modifier active
*/
public boolean isBold() {
return modifiers.contains(SGR.BOLD);
}
/**
* Returns true if this TextCharacter has the reverse modifier active
* @return {@code true} if this TextCharacter has the reverse modifier active
*/
public boolean isReversed() {
return modifiers.contains(SGR.REVERSE);
}
/**
* Returns true if this TextCharacter has the underline modifier active
* @return {@code true} if this TextCharacter has the underline modifier active
*/
public boolean isUnderlined() {
return modifiers.contains(SGR.UNDERLINE);
}
/**
* Returns true if this TextCharacter has the blink modifier active
* @return {@code true} if this TextCharacter has the blink modifier active
*/
public boolean isBlinking() {
return modifiers.contains(SGR.BLINK);
}
/**
* Returns true if this TextCharacter has the bordered modifier active
* @return {@code true} if this TextCharacter has the bordered modifier active
*/
public boolean isBordered() {
return modifiers.contains(SGR.BORDERED);
}
/**
* Returns true if this TextCharacter has the crossed-out modifier active
* @return {@code true} if this TextCharacter has the crossed-out modifier active
*/
public boolean isCrossedOut() {
return modifiers.contains(SGR.CROSSED_OUT);
}
/**
* Returns a new TextCharacter with the same colors and modifiers but a different underlying character
* @param character Character the copy should have
* @return Copy of this TextCharacter with different underlying character
*/
@SuppressWarnings("SameParameterValue")
public TextCharacter withCharacter(char character) {
if(this.character == character) {
return this;
}
return new TextCharacter(character, foregroundColor, backgroundColor, modifiers);
}
/**
* Returns a copy of this TextCharacter with a specified foreground color
* @param foregroundColor Foreground color the copy should have
* @return Copy of the TextCharacter with a different foreground color
*/
public TextCharacter withForegroundColor(TextColor foregroundColor) {
if(this.foregroundColor == foregroundColor || this.foregroundColor.equals(foregroundColor)) {
return this;
}
return new TextCharacter(character, foregroundColor, backgroundColor, modifiers);
}
/**
* Returns a copy of this TextCharacter with a specified background color
* @param backgroundColor Background color the copy should have
* @return Copy of the TextCharacter with a different background color
*/
public TextCharacter withBackgroundColor(TextColor backgroundColor) {
if(this.backgroundColor == backgroundColor || this.backgroundColor.equals(backgroundColor)) {
return this;
}
return new TextCharacter(character, foregroundColor, backgroundColor, modifiers);
}
/**
* Returns a copy of this TextCharacter with specified list of SGR modifiers. None of the currently active SGR codes
* will be carried over to the copy, only those in the passed in value.
* @param modifiers SGR modifiers the copy should have
* @return Copy of the TextCharacter with a different set of SGR modifiers
*/
public TextCharacter withModifiers(Collection modifiers) {
EnumSet newSet = EnumSet.copyOf(modifiers);
if(modifiers.equals(newSet)) {
return this;
}
return new TextCharacter(character, foregroundColor, backgroundColor, newSet);
}
/**
* Returns a copy of this TextCharacter with an additional SGR modifier. All of the currently active SGR codes
* will be carried over to the copy, in addition to the one specified.
* @param modifier SGR modifiers the copy should have in additional to all currently present
* @return Copy of the TextCharacter with a new SGR modifier
*/
public TextCharacter withModifier(SGR modifier) {
if(modifiers.contains(modifier)) {
return this;
}
EnumSet newSet = EnumSet.copyOf(this.modifiers);
newSet.add(modifier);
return new TextCharacter(character, foregroundColor, backgroundColor, newSet);
}
/**
* Returns a copy of this TextCharacter with an SGR modifier removed. All of the currently active SGR codes
* will be carried over to the copy, except for the one specified. If the current TextCharacter doesn't have the
* SGR specified, it will return itself.
* @param modifier SGR modifiers the copy should not have
* @return Copy of the TextCharacter without the SGR modifier
*/
public TextCharacter withoutModifier(SGR modifier) {
if(!modifiers.contains(modifier)) {
return this;
}
EnumSet newSet = EnumSet.copyOf(this.modifiers);
newSet.remove(modifier);
return new TextCharacter(character, foregroundColor, backgroundColor, newSet);
}
@SuppressWarnings("SimplifiableIfStatement")
@Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
}
if(getClass() != obj.getClass()) {
return false;
}
final TextCharacter other = (TextCharacter) obj;
if(this.character != other.character) {
return false;
}
if(this.foregroundColor != other.foregroundColor && (this.foregroundColor == null || !this.foregroundColor.equals(other.foregroundColor))) {
return false;
}
if(this.backgroundColor != other.backgroundColor && (this.backgroundColor == null || !this.backgroundColor.equals(other.backgroundColor))) {
return false;
}
return !(this.modifiers != other.modifiers && (this.modifiers == null || !this.modifiers.equals(other.modifiers)));
}
@Override
public int hashCode() {
int hash = 7;
hash = 37 * hash + this.character;
hash = 37 * hash + (this.foregroundColor != null ? this.foregroundColor.hashCode() : 0);
hash = 37 * hash + (this.backgroundColor != null ? this.backgroundColor.hashCode() : 0);
hash = 37 * hash + (this.modifiers != null ? this.modifiers.hashCode() : 0);
return hash;
}
@Override
public String toString() {
return "TextCharacter{" + "character=" + character + ", foregroundColor=" + foregroundColor + ", backgroundColor=" + backgroundColor + ", modifiers=" + modifiers + '}';
}
}