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.io.Serializable;
19
20 import org.apache.commons.lang.ObjectUtils;
21 import org.apache.commons.lang.StringUtils;
22
23 /**
24 * <p>
25 * A simple class representing a color in a platform-independent way.
26 * </p>
27 * <p>
28 * In the most basic form, colors are defined using RGB components. In more
29 * complex scenarios a color can be represented by a string - for instance, if
30 * it is determined by a complex style sheet definition. The
31 * {@code isLogicColor()} method can be used to distinguish between these kinds
32 * of objects. If it returns <strong>true</strong>, the color is defined based
33 * on a string, and thus concrete RGB values are not available.
34 * </p>
35 * <p>
36 * {@code Color} objects are created using static factory methods. They are
37 * immutable and thus can be shared between different threads.
38 * </p>
39 *
40 * @author Oliver Heger
41 * @version $Id: Color.java 205 2012-01-29 18:29:57Z oheger $
42 */
43 public class Color implements Serializable
44 {
45 /**
46 * Constant for an undefined color component. This value is returned by the
47 * access methods for the single color components ({@code getRed()},
48 * {@code getGreen()}, or {@code getBlue()} to indicate that this component
49 * is undefined - which is the case for logic colors.
50 *
51 * @since 1.3
52 */
53 public static final int COMPONENT_UNDEFINED = -1;
54
55 /**
56 * Constant for an undefined color. This constant defines a special instance
57 * which does not have RGB components nor a logic color definition. It can
58 * be used to represent an undefined color.
59 *
60 * @since 1.3
61 */
62 public static final Color UNDEFINED = new Color(COMPONENT_UNDEFINED,
63 COMPONENT_UNDEFINED, COMPONENT_UNDEFINED, null);
64
65 /**
66 * The serial version UID.
67 */
68 private static final long serialVersionUID = 20130606L;
69
70 /** Constant for an error message pattern for an invalid color component. */
71 private static final String ERR_INVALID_COMPONENT =
72 "Invalid value for component %s: %d!";
73
74 /** Constant for the maximum value of a RGB component. */
75 private static final int MAX_RANGE = 255;
76
77 /** Constant for the buffer size for the string generation. */
78 private static final int BUF_SIZE = 32;
79
80 /** The logic color definition. */
81 private final String colorDefinition;
82
83 /** The component for red. */
84 private final int red;
85
86 /** The component for green. */
87 private final int green;
88
89 /** The component for blue. */
90 private final int blue;
91
92 /**
93 * Creates a new instance of {@code Color} and initializes its members.
94 *
95 * @param r the red component
96 * @param g the green component
97 * @param b the blue component
98 * @param def the logic color definition
99 */
100 Color(int r, int g, int b, String def)
101 {
102 red = r;
103 green = g;
104 blue = b;
105 colorDefinition = def;
106 }
107
108 /**
109 * Returns a flag whether this {@code Color} instance is based on a logic
110 * color definition. This means that it was constructed from a text-based
111 * definition. In this case, the values of the RGB components are undefined.
112 *
113 * @return <strong>true</strong> if this {@code Color} instance is based on
114 * a logic definition, <strong>false</strong> otherwise
115 * @since 1.3
116 */
117 public boolean isLogicColor()
118 {
119 return getRed() == COMPONENT_UNDEFINED
120 || getGreen() == COMPONENT_UNDEFINED
121 || getBlue() == COMPONENT_UNDEFINED;
122 }
123
124 /**
125 * Returns the blue component of this color. This value is only defined if
126 * {@code isLogicColor()} returns <strong>false</strong>.
127 *
128 * @return the blue component
129 */
130 public int getBlue()
131 {
132 return blue;
133 }
134
135 /**
136 * Returns the green component of this color. This value is only defined if
137 * {@code isLogicColor()} returns <strong>false</strong>.
138 *
139 * @return the green component
140 */
141 public int getGreen()
142 {
143 return green;
144 }
145
146 /**
147 * Returns the red component of this color. This value is only defined if
148 * {@code isLogicColor()} returns <strong>false</strong>.
149 *
150 * @return the red component
151 */
152 public int getRed()
153 {
154 return red;
155 }
156
157 /**
158 * Returns the logic color definition this {@code Color} instance is based
159 * on. This value is only defined of {@code isLogicColor()} returns
160 * <strong>true</strong>.
161 *
162 * @return the logic color definition
163 * @since 1.3
164 */
165 public String getColorDefinition()
166 {
167 return colorDefinition;
168 }
169
170 /**
171 * Returns a String representation of this object.
172 *
173 * @return a String for this object
174 */
175 @Override
176 public String toString()
177 {
178 StringBuilder buf = new StringBuilder(BUF_SIZE);
179 buf.append(getClass().getSimpleName()).append("[ ");
180
181 if (isLogicColor())
182 {
183 buf.append("def = '").append(getColorDefinition()).append('\'');
184 }
185 else
186 {
187 buf.append("rgb = ");
188 buf.append(ColorHelper.COLDEF_RGB_PREFIX);
189 buf.append(getRed()).append(ColorHelper.SEPARATOR);
190 buf.append(getGreen()).append(ColorHelper.SEPARATOR);
191 buf.append(getBlue()).append(ColorHelper.COLDEF_RGB_SUFFIX);
192 }
193
194 buf.append(" ]");
195 return buf.toString();
196 }
197
198 /**
199 * Tests if a passed in object equals this object. Two {@code Color} objects
200 * are considered equal if and only if all color components are equal.
201 *
202 * @param obj the object to compare to
203 * @return a flag whether the objects are equal
204 */
205 @Override
206 public boolean equals(Object obj)
207 {
208 if (this == obj)
209 {
210 return true;
211 }
212 if (!(obj instanceof Color))
213 {
214 return false;
215 }
216 Color c = (Color) obj;
217 return getRed() == c.getRed()
218 && getGreen() == c.getGreen()
219 && getBlue() == c.getBlue()
220 && ObjectUtils.equals(getColorDefinition(),
221 c.getColorDefinition());
222 }
223
224 /**
225 * Determines a hash code for this object.
226 *
227 * @return a hash code
228 */
229 @Override
230 public int hashCode()
231 {
232 final int seed = 13;
233 final int factor = 43;
234
235 int result = seed;
236 result = result * factor + getRed();
237 result = result * factor + getGreen();
238 result = result * factor + getBlue();
239 if (getColorDefinition() != null)
240 {
241 result = result * factor + getColorDefinition().hashCode();
242 }
243 return result;
244 }
245
246 /**
247 * Creates a new instance of {@code Color} and initializes the single
248 * components. This method will check whether the passed in values are
249 * valid; if one is out of range, an exception will be thrown.
250 *
251 * @param r the r component
252 * @param g the g component
253 * @param b the b component
254 * @return the new {@code Color} instance
255 * @throws IllegalArgumentException if a component is invalid
256 * @deprecated Use {@code newRGBInstance()} instead.
257 */
258 @Deprecated
259 public static Color newInstance(int r, int g, int b)
260 {
261 return newRGBInstance(r, g, b);
262 }
263
264 /**
265 * Creates a new instance of {@code Color} and initializes it with the given
266 * components for the red, green, and blue part. This method checks whether
267 * the passed in values are valid; if one argument is out of range, an
268 * exception is thrown.
269 *
270 * @param r the r component
271 * @param g the g component
272 * @param b the b component
273 * @return the new {@code Color} instance
274 * @throws IllegalArgumentException if a component is invalid
275 * @since 1.3
276 */
277 public static Color newRGBInstance(int r, int g, int b)
278 {
279 checkComponent(r, "red");
280 checkComponent(g, "green");
281 checkComponent(b, "blue");
282 return new Color(r, g, b, null);
283 }
284
285 /**
286 * Creates a new instance of {@code Color} based on a logic, text-based
287 * color definition. The passed in string is just stored and not interpreted
288 * in any form. It must not be <b>null</b>.
289 *
290 * @param coldef the logic color definition
291 * @return the new {@code Color} instance
292 * @throws IllegalArgumentException if the color definition is undefined
293 * @since 1.3
294 */
295 public static Color newLogicInstance(String coldef)
296 {
297 if (StringUtils.isBlank(coldef))
298 {
299 throw new IllegalArgumentException(
300 "Color definition must be defined!");
301 }
302 return new Color(COMPONENT_UNDEFINED, COMPONENT_UNDEFINED,
303 COMPONENT_UNDEFINED, coldef);
304 }
305
306 /**
307 * Checks whether a component of a color is in the correct range. If not, an
308 * exception is thrown.
309 *
310 * @param c the component to be checked
311 * @param name the name of the component (for producing a meaningful
312 * exception message)
313 * @throws IllegalArgumentException if the component has an invalid value
314 */
315 private static void checkComponent(int c, String name)
316 {
317 if (c < 0 || c > MAX_RANGE)
318 {
319 throw new IllegalArgumentException(String.format(
320 ERR_INVALID_COMPONENT, name, c));
321 }
322 }
323 }