package com.jediterm.terminal; import org.jetbrains.annotations.NotNull; import java.lang.ref.WeakReference; import java.util.EnumSet; import java.util.WeakHashMap; public class TextStyle implements Cloneable { public static final EnumSet<Option> NO_OPTIONS = EnumSet.noneOf(Option.class); public static final TextStyle EMPTY = new TextStyle(); private static final WeakHashMap<TextStyle, WeakReference<TextStyle>> styles = new WeakHashMap<TextStyle, WeakReference<TextStyle>>(); private TerminalColor myForeground; private TerminalColor myBackground; private EnumSet<Option> myOptions; public TextStyle() { this(null, null, NO_OPTIONS); } public TextStyle(final TerminalColor foreground, final TerminalColor background) { this(foreground, background, NO_OPTIONS); } public TextStyle(final TerminalColor foreground, final TerminalColor background, final EnumSet<Option> options) { myForeground = foreground; myBackground = background; myOptions = options.clone(); } public void setBackground(TerminalColor background) { myBackground = background; } public void setForeground(TerminalColor foreground) { myForeground = foreground; } public void setOptions(EnumSet<Option> options) { myOptions = options; } public void setOption(final Option opt, final boolean val) { setOptions(opt.set(EnumSet.copyOf(myOptions), val)); } public TextStyle readonlyCopy() { return new TextStyle(myForeground, myBackground, myOptions) { private TextStyle readonly() { throw new IllegalStateException("Text Style is readonly"); } @Override public void setBackground(TerminalColor background) { readonly(); } @Override public void setForeground(TerminalColor foreground) { readonly(); } @Override public void setOptions(EnumSet<Option> options) { readonly(); } }; } @NotNull public static TextStyle getCanonicalStyle(TextStyle currentStyle) { final WeakReference<TextStyle> canonRef = styles.get(currentStyle); if (canonRef != null) { final TextStyle canonStyle = canonRef.get(); if (canonStyle != null) { return canonStyle; } } styles.put(currentStyle, new WeakReference<TextStyle>(currentStyle)); return currentStyle; } public TerminalColor getForeground() { return myForeground; } public TerminalColor getBackground() { return myBackground; } @Override public TextStyle clone() { return new TextStyle(myForeground, myBackground, myOptions); } public TextStyle createEmptyWithColors() { return new TextStyle(myForeground, myBackground); } public int getId() { return hashCode(); } public boolean hasOption(final Option option) { return myOptions.contains(option); } @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + (myBackground == null ? 0 : myBackground.hashCode()); result = PRIME * result + (myForeground == null ? 0 : myForeground.hashCode()); result = PRIME * result + (myOptions == null ? 0 : myOptions.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final TextStyle other = (TextStyle)obj; if (myBackground == null) { if (other.myBackground != null) { return false; } } else if (!myBackground.equals(other.myBackground)) { return false; } if (myForeground == null) { if (other.myForeground != null) { return false; } } else if (!myForeground.equals(other.myForeground)) { return false; } if (myOptions == null) { if (other.myOptions != null) { return false; } } else if (!myOptions.equals(other.myOptions)) { return false; } return true; } public TerminalColor getBackgroundForRun() { return myOptions.contains(Option.INVERSE) ? myForeground : myBackground; } public TerminalColor getForegroundForRun() { return myOptions.contains(Option.INVERSE) ? myBackground : myForeground; } public void clearOptions() { myOptions.clear(); } public enum Option { BOLD, ITALIC, BLINK, DIM, INVERSE, UNDERLINED, HIDDEN; public EnumSet<Option> set(EnumSet<Option> options, boolean val) { if (val) { options.add(this); } else { options.remove(this); } return options; } } }