1 /*
2 * Copyright 2006-2016 The JGUIraffe Team.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License")
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.jguiraffe.gui.builder.components;
17
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.NoSuchElementException;
24 import java.util.StringTokenizer;
25
26 /**
27 * <p>
28 * A helper class for dealing with colors.
29 * </p>
30 * <p>
31 * This class defines helper methods for converting color values specified in
32 * form builder Jelly scripts. In this model colors can be defined in the
33 * following different ways:
34 * <ul>
35 * <li>Using a symbolic name. For this purpose this class defines constants
36 * that roughly correspond to the colors defined by the
37 * {@code java.awt.Color} class.</li>
38 * <li>As a hexadecimal numeric value. In this case the color definition must
39 * start with a "#" sign, e.g. {@code #80FF80}. This is
40 * analogous to color definitions in HTML.</li>
41 * <li>As triple of decimal rgb values. Definitions of this type look like
42 * {@code (r, g, b)}, with r, g, b in the range from 0 to 255.</li>
43 * <li>Logic color definitions. These are arbitrary strings which are not
44 * further interpreted, e.g. style sheet names. To be recognized, such a string
45 * must start with a "~" character, e.g. {@code ~MyStyle}.</li>
46 * </ul>
47 * </p>
48 *
49 * @author Oliver Heger
50 * @version $Id: ColorHelper.java 205 2012-01-29 18:29:57Z oheger $
51 */
52 public final class ColorHelper
53 {
54 /** Constant for the RGB color definition prefix. */
55 static final String COLDEF_RGB_PREFIX = "(";
56
57 /** Constant for the RGB color definition suffix. */
58 static final String COLDEF_RGB_SUFFIX = ")";
59
60 /** Constant for a separator used by the toString() method. */
61 static final String SEPARATOR = ", ";
62
63 /** Constant for the RGB color definition delimiters. */
64 private static final String RGB_DELIMITERS = COLDEF_RGB_PREFIX
65 + COLDEF_RGB_SUFFIX + ",; ";
66
67 /** Constant for the hexadecimal color definition character. */
68 private static final String COLDEF_HEXA = "#";
69
70 /** Constant for the prefix for logic color definitions. */
71 private static final String COLDEF_LOGIC = "~";
72
73 /** Constant for the bit mask for removing the high byte. */
74 private static final int BYTE_MASK = 0xFF;
75
76 /** Constant for shifting a byte. */
77 private static final int BYTE = 8;
78
79 /** Constant for shifting a word. */
80 private static final int WORD = 16;
81
82 /** Constant for the base 16. */
83 private static final int BASE_16 = 16;
84
85 /** A list with the names of the existing predefined color names. */
86 private static final List<String> PREDEFINED_COLOR_NAMES;
87
88 /**
89 * Private constructor so no instance can be created.
90 */
91 private ColorHelper()
92 {
93 }
94
95 /**
96 * Returns the predefined color with the given name. The passed in name must
97 * be one of the names returned by the {@code getPredefinedNames()}
98 * method (case does not matter).
99 *
100 * @param name the name of the desired color
101 * @return the color
102 * @throws FormBuilderException if this color does not exist
103 */
104 public static Color getPredefinedColor(String name)
105 throws FormBuilderException
106 {
107 try
108 {
109 return NamedColor.valueOf(name.toUpperCase(Locale.ENGLISH)).getColor();
110 }
111 catch (IllegalArgumentException iex)
112 {
113 throw new FormBuilderException(
114 "No predefined color found: " + name, iex);
115 }
116 catch (NullPointerException npex)
117 {
118 throw new FormBuilderException(
119 "Name of predefined color must not be null!");
120 }
121 }
122
123 /**
124 * Returns an iterator with the names (Strings) of all predefined colors.
125 * These names can be passed to the {@code getPredefinedColor()}
126 * method.
127 *
128 * @return the names of the predefined colors
129 */
130 public static Iterator<String> getPredefinedNames()
131 {
132 return PREDEFINED_COLOR_NAMES.iterator();
133 }
134
135 /**
136 * The main method for resolving a color definition. This method can be
137 * given a color definition in one of the supported flavors. It will try to
138 * resolve this definition and return the corresponding {@code Color}
139 * object. If this fails, an exception will be thrown.
140 *
141 * @param c the color definition
142 * @return the corresponding color object or <b>null</b> if the passed in
143 * color definition was <b>null</b>
144 * @throws FormBuilderException if the color definition cannot be resolved
145 */
146 public static Color resolveColor(String c) throws FormBuilderException
147 {
148 if (c == null)
149 {
150 return null;
151 }
152 else if (c.startsWith(COLDEF_LOGIC))
153 {
154 return resolveLogicColor(c);
155 }
156 else if (c.startsWith(COLDEF_HEXA))
157 {
158 return resolveHexColor(c);
159 }
160 else if (c.startsWith(COLDEF_RGB_PREFIX)
161 && c.endsWith(COLDEF_RGB_SUFFIX))
162 {
163 return resolveRGBColor(c);
164 }
165 else
166 {
167 return getPredefinedColor(c);
168 }
169 }
170
171 /**
172 * Resolves a logic color definition.
173 *
174 * @param c the color definition
175 * @return the resolved {@code Color} instance
176 * @throws FormBuilderException if the definition is invalid
177 */
178 private static Color resolveLogicColor(String c)
179 throws FormBuilderException
180 {
181 try
182 {
183 return Color.newLogicInstance(c.substring(COLDEF_LOGIC.length()));
184 }
185 catch (IllegalArgumentException iex)
186 {
187 throw new FormBuilderException("Invalid logic color definition: "
188 + c, iex);
189 }
190 }
191
192 /**
193 * Resolves the given color definition in the hexadecimal flavor.
194 *
195 * @param s the color definition
196 * @return the resolved color
197 * @throws FormBuilderException if the color definition is invalid
198 */
199 private static Color resolveHexColor(String s) throws FormBuilderException
200 {
201 try
202 {
203 int value =
204 Integer.parseInt(s.substring(COLDEF_HEXA.length()), BASE_16);
205 return Color.newRGBInstance(value >> WORD,
206 (value >> BYTE) & BYTE_MASK, value & BYTE_MASK);
207 }
208 catch (NumberFormatException nex)
209 {
210 throw new FormBuilderException("Invalid color definition: " + s,
211 nex);
212 }
213 catch (IllegalArgumentException iex)
214 {
215 throw new FormBuilderException(
216 "Color component out of range in color definition: " + s,
217 iex);
218 }
219 }
220
221 /**
222 * Resolves the given color definition in decimal RGB flavor.
223 *
224 * @param c the color definition
225 * @return the resolved color
226 * @throws FormBuilderException if the color definition is invalid
227 */
228 private static Color resolveRGBColor(String c) throws FormBuilderException
229 {
230 StringTokenizer tok = new StringTokenizer(c, RGB_DELIMITERS);
231 try
232 {
233 Color col = Color.newRGBInstance(Integer.parseInt(tok.nextToken()),
234 Integer.parseInt(tok.nextToken()), Integer.parseInt(tok
235 .nextToken()));
236 if (tok.hasMoreTokens())
237 {
238 throw new FormBuilderException(
239 "Too many components in color definition: " + c);
240 }
241 return col;
242 }
243
244 catch (NumberFormatException nex)
245 {
246 throw new FormBuilderException(
247 "Invalid RGB value in color definition: " + c, nex);
248 }
249 catch (NoSuchElementException nse)
250 {
251 throw new FormBuilderException(
252 "Too few components in color definition: " + c, nse);
253 }
254 catch (IllegalArgumentException iex)
255 {
256 throw new FormBuilderException(
257 "Color component out of range in color definition: " + c,
258 iex);
259 }
260 }
261
262 // static initializer; initializes the list with the predefined color names
263 static
264 {
265 NamedColor[] values = NamedColor.values();
266 List<String> names = new ArrayList<String>(values.length);
267 for (NamedColor nc : values)
268 {
269 names.add(nc.name());
270 }
271 PREDEFINED_COLOR_NAMES = Collections.unmodifiableList(names);
272 }
273
274 /**
275 * An enumeration with predefined color constants. The names defined here
276 * can be passed to {@code getPredefinedColor()}.
277 */
278 public static enum NamedColor
279 {
280 /** Default color black. */
281 BLACK(Color.newRGBInstance(0, 0, 0)),
282
283 /** Default color blue. */
284 BLUE(Color.newRGBInstance(0, 0, 255)),
285
286 /** Default color cyan. */
287 CYAN(Color.newRGBInstance(0, 255, 255)),
288
289 /** Default color dark gray. */
290 DARK_GRAY(Color.newRGBInstance(64, 64, 64)),
291
292 /** Default color gray. */
293 GRAY(Color.newRGBInstance(128, 128, 128)),
294
295 /** Default color green. */
296 GREEN(Color.newRGBInstance(0, 255, 0)),
297
298 /** Default color light gray. */
299 LIGHT_GRAY(Color.newRGBInstance(192, 192, 192)),
300
301 /** Default color magenta. */
302 MAGENTA(Color.newRGBInstance(255, 0, 255)),
303
304 /** Default color orange. */
305 ORANGE(Color.newRGBInstance(255, 200, 0)),
306
307 /** Default color pink. */
308 PINK(Color.newRGBInstance(255, 175, 175)),
309
310 /** Default color red. */
311 RED(Color.newRGBInstance(255, 0, 0)),
312
313 /** Default color white. */
314 WHITE(Color.newRGBInstance(255, 255, 255)),
315
316 /** Default color yellow. */
317 YELLOW(Color.newRGBInstance(255, 255, 0));
318
319 /** Stores the referenced {@code Color} object. */
320 private final Color color;
321
322 /**
323 * Creates a new instance of {@code NamedColor} and sets the
324 * associated color.
325 *
326 * @param c the associated color
327 */
328 private NamedColor(Color c)
329 {
330 color = c;
331 }
332
333 /**
334 * Returns the associated {@code Color} object.
335 *
336 * @return the {@code Color} represented by this object
337 */
338 public Color getColor()
339 {
340 return color;
341 }
342 }
343 }