blob: 8dc3ce5c3c2fda860226dace0bb88a6c198c3cc9 [file] [log] [blame]
/************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
************************************************************************/
package org.odftoolkit.odfdom.type;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* This class represents the in OpenDocument format used data type {@odf.datatype color}
* See <a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-color">W3C CSS specification</a> for further details.
*/
public class Color implements OdfDataType {
private static final Pattern sixHexRGBPattern = Pattern.compile("^#[0-9a-fA-F]{6}$");
private static final Pattern threeHexRGBPattern = Pattern.compile("^#[0-9a-fA-F]{3}$");
private static final Map<String, String> labeledColors = new HashMap<String, String>();
static {
labeledColors.put("aqua", "#00ffff");
labeledColors.put("black", "#000000");
labeledColors.put("blue", "#0000ff");
labeledColors.put("fuchsia", "#ff00ff");
labeledColors.put("gray", "#808080");
labeledColors.put("green", "#008000");
labeledColors.put("lime", "#00ff00");
labeledColors.put("maroon", "#800000");
labeledColors.put("navy", "#000080");
labeledColors.put("olive", "#808000");
labeledColors.put("orange", "#ffA500");
labeledColors.put("purple", "#800080");
labeledColors.put("red", "#ff0000");
labeledColors.put("silver", "#c0c0c0");
labeledColors.put("teal", "#008080");
labeledColors.put("white", "#ffffff");
labeledColors.put("yellow", "#ffff00");
}
private static final String COLOR_PREFIX = "#";
private final String mColorAsSixHexRGB;
/**
* The color aqua in sRGB space.
*/
public static final Color AQUA = new Color("#00ffff");
/**
* The color black in sRGB space.
*/
public static final Color BLACK = new Color("#000000");
/**
* The color blue in sRGB space.
*/
public static final Color BLUE = new Color("#0000ff");
/**
* The color fuchsia in sRGB space.
*/
public static final Color FUCHSIA = new Color("#ff00ff");
/**
* The color gray in sRGB space.
*/
public static final Color GRAY = new Color("#808080");
/**
* The color green in sRGB space.
*/
public static final Color GREEN = new Color("#008000");
/**
* The color lime in sRGB space.
*/
public static final Color LIME = new Color("#00ff00");
/**
* The color maroon in sRGB space.
*/
public static final Color MAROON = new Color("#800000");
/**
* The color navy in sRGB space.
*/
public static final Color NAVY = new Color("#000080");
/**
* The color olive in sRGB space.
*/
public static final Color OLIVE = new Color("#808000");
/**
* The color orange in sRGB space.
*/
public static final Color ORANGE = new Color("#ffA500");
/**
* The color purple in sRGB space.
*/
public static final Color PURPLE = new Color("#800080");
/**
* The color red in sRGB space.
*/
public static final Color RED = new Color("#ff0000");
/**
* The color silver in sRGB space.
*/
public static final Color SILVER = new Color("#c0c0c0");
/**
* The color teal in sRGB space.
*/
public static final Color TEAL = new Color("#008080");
/**
* The color white in sRGB space.
*/
public static final Color WHITE = new Color("#ffffff");
/**
* The color yellow in sRGB space.
*/
public static final Color YELLOW = new Color("#ffff00");
/**
* Construct Color by the parsing the given string. The string should be observed sRGB color standard
* which starts with "#" and following with six numbers or three numbers in Hex format.
* For example, "#FFFFFF" is a valid argument and white color will be constructed.
* <p>
* For further information on sRGB,
* see <A href="http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html">
* http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html
* </A>.
*
* @param color represented using the 3 or 6 HEX sRGB notation.
* @throws IllegalArgumentException if the given argument is not a valid Color in sRGB HEX notation.
*/
public Color(String color) {
if(!isValid(color)){
throw new IllegalArgumentException("parameter is invalid for datatype Color");
}
if(color.length()==4){
mColorAsSixHexRGB = mapColorFromThreeToSixHex(color);
}else{
mColorAsSixHexRGB = color;
}
}
/**
* Construct Color using the specified red, green and blue values in the range (0 - 255).
*
* @param red the red component.
* @param green the green component.
* @param blue the blue component.
* @throws IllegalArgumentException if <code>red</code>, <code>green</code> or <code>blue</code>
* are outside of the range 0 to 255, inclusive.
*/
public Color(int red, int green, int blue) {
this(mapColorIntegerToString(red, green, blue));
}
/**
* Construct Color using the specified red, green, and blue values in the range (0.0 - 1.0).
*
* @param red the red component
* @param green the green component
* @param blue the blue component
* @throws IllegalArgumentException if <code>red</code>, <code>green</code> or <code>blue</code> are
* outside of the range 0.0 to 1.0, inclusive.
*/
public Color(float red, float green, float blue) {
this((int) (red * 255 + 0.5), (int) (green * 255 + 0.5), (int) (blue * 255 + 0.5));
}
/**
* Construct Color using {@link java.awt.Color <code>java.awt.Color</code>}.
*
* @param color the specified {@link java.awt.Color <code>java.awt.Color</code>}.
* @throws IllegalArgumentException if the given argument is not a valid Color.
* @see java.awt.Color
*/
public Color(java.awt.Color color) {
this(color.getRed(),color.getGreen(),color.getBlue());
}
/**
* Returns the Color in six HEX sRGB notation.
* format.
*
* @return a six number hexadecimal string representation of the Color
*/
@Override
public String toString() {
return mColorAsSixHexRGB;
}
/**
* Returns a Color instance representing the specified String value.
*
* @param colorValue a six (or three) number hexadecimal string representation of the Color
* @return return a Color instance representing <code>stringValue</code>.
* @throws IllegalArgumentException if the given argument is not a valid Color.
*/
public static Color valueOf(String colorValue) {
return new Color(colorValue);
}
/**
* Check if the specified String is a valid {@odf.datatype color} data type.
*
* @param colorValue a six (or three) number hexadecimal string representation of the Color
* @return true if the value of argument is valid for{@odf.datatype color} data type false otherwise.
*/
public static boolean isValid(String colorValue) {
if ((colorValue == null) || !(threeHexRGBPattern.matcher(colorValue).matches() || sixHexRGBPattern.matcher(colorValue).matches())) {
return false;
} else {
return true;
}
}
/**
* Convert RGB color formats to six-digit hex RGB format.
*
* The RGB mapping works as follows:
* rgb(110%, 0%, 0%)----clipped to rgb(100%,0%,0%), return #ff0000
* maroon----one of the seventeen fixed labeled numbers, return #800000
* #ff0000----six-digit notation #rrggbb, returns the input
* #f00----three-digit notation #rgb, return #ff0000
* rgb(255,0,0)----integer range 0 - 255, return #ff0000
* rgb(300,0,0)----clipped to rgb(255,0,0), return #ff0000
* rgb(255,-10,0)----clipped to rgb(255,0,0), return #ff0000
*
* @param colorValue The sRGB color value to be converted.
* @return the converted color.
*/
public static String toSixDigitHexRGB(String colorValue){
if (colorValue == null) {
throw new IllegalArgumentException("parameter should not be null.");
} else {
colorValue = colorValue.toLowerCase().trim();
if (sixHexRGBPattern.matcher(colorValue).matches()) {
// 6-digit notation #rrggbb - return itself.
return colorValue;
} else if (threeHexRGBPattern.matcher(colorValue).matches()) {
// convert 3-digit notation #rgb.
return mapColorFromThreeToSixHex(colorValue);
} else if (colorValue.startsWith("rgb")) {
colorValue = colorValue.substring(3);
colorValue = colorValue.substring(colorValue.indexOf("(") + 1,
colorValue.indexOf(")"));
String[] rgbValues = colorValue.split(",");
if (rgbValues.length == 3) {
int r = mapColorValueToInteger(rgbValues[0].trim());
int g = mapColorValueToInteger(rgbValues[1].trim());
int b = mapColorValueToInteger(rgbValues[2].trim());
String rs = Integer.toHexString(r);
String gs = Integer.toHexString(g);
String bs = Integer.toHexString(b);
String hexColor = COLOR_PREFIX;
if (r < 16) {
hexColor += "0";
}
hexColor += rs;
if (g < 16) {
hexColor += "0";
}
hexColor += gs;
if (b < 16) {
hexColor += "0";
}
hexColor += bs;
return hexColor;
}else{
throw new IllegalArgumentException("parameter: "+ colorValue + " can't be converted six-digit hex RGB.");
}
} else {
// convert the seventeen fixed labeled numbers.
String hexColor = labeledColors.get(colorValue);
if (hexColor != null) {
return hexColor;
} else {
throw new IllegalArgumentException("parameter: "+ colorValue + " can't be converted six-digit hex RGB.");
}
}
}
}
/**
* Return the corresponding {@link java.awt.Color <code>java.awt.Color</code>} instance of the Color data type.
*
* @return the converted {@link java.awt.Color <code>java.awt.Color</code>} instance..
*/
public java.awt.Color getAWTColor(){
return mapColorToAWTColor(this);
}
/**
* Map a Color data type to {@link java.awt.Color <code>java.awt.Color</code>}.
*
* @param color The color data type to be mapped..
* @return the converted {@link java.awt.Color <code>java.awt.Color</code>} instance.
*/
public static java.awt.Color mapColorToAWTColor(Color color) {
int rgb = Integer.decode("0x" + color.mColorAsSixHexRGB.substring(1));
return new java.awt.Color(rgb);
}
/**
* Converts Color expressed by red, green and blue values in the range (0 - 255) to
* a string format which is used in {@odf.datatype color}.
*
* @param red the red component.
* @param green the green component.
* @param blue the blue component.
* @return the string format color.
*/
private static String mapColorIntegerToString(int red, int green, int blue) {
String rs = Integer.toHexString(red);
String gs = Integer.toHexString(green);
String bs = Integer.toHexString(blue);
String hexColor = COLOR_PREFIX;
if (red < 16) {
hexColor += "0";
}
hexColor += rs;
if (green < 16) {
hexColor += "0";
}
hexColor += gs;
if (blue < 16) {
hexColor += "0";
}
hexColor += bs;
return hexColor;
}
/**
* Converts Color from three-digit to six-digit form. The three-digit (#rgb) is converted into six-digit form (#rrggbb) by replicating digits,
* not by adding zeros. For example, #fb0 expands to #ffbb00.
* *
* @param threeDigitcColor the three-digit color form.
* @return the six-digit color form.
*/
private static String mapColorFromThreeToSixHex(String threeDigitcColor) {
char[] colorData = threeDigitcColor.toCharArray();
char[] sixDigitColor = new char[7];
for (int i = 0; i < 7; i++) {
sixDigitColor[i] = colorData[(i + 1) / 2];
}
return new String(sixDigitColor);
}
/** Convert percent string and integer string to integer.
value range will be checked and adapted to border (error correction) */
private static int mapColorValueToInteger(String colorValue) {
if (colorValue.endsWith("%")) {
colorValue = colorValue.substring(0, colorValue.indexOf("%"));
int value = Integer.parseInt(colorValue);
if (value < 0) {
value = 0;
}
if (value > 100) {
value = 100;
}
return 255 * value / 100;
} else {
int value = Integer.parseInt(colorValue);
if (value < 0) {
value = 0;
}
if (value > 255) {
value = 255;
}
return value;
}
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Color other = (Color) obj;
if ((this.mColorAsSixHexRGB == null) ? (other.mColorAsSixHexRGB != null) : !this.mColorAsSixHexRGB.equals(other.mColorAsSixHexRGB)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 97 * hash + (this.mColorAsSixHexRGB != null ? this.mColorAsSixHexRGB.hashCode() : 0);
return hash;
}
}