001: package org.apache.lucene.search.highlight;
002:
003: /**
004: * Copyright 2002-2004 The Apache Software Foundation
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * Formats text with different color intensity depending on the score of the
021: * term.
022: *
023: * @author maharwood
024: */
025: public class GradientFormatter implements Formatter {
026: private float maxScore;
027:
028: int fgRMin, fgGMin, fgBMin;
029:
030: int fgRMax, fgGMax, fgBMax;
031:
032: protected boolean highlightForeground;
033:
034: int bgRMin, bgGMin, bgBMin;
035:
036: int bgRMax, bgGMax, bgBMax;
037:
038: protected boolean highlightBackground;
039:
040: /**
041: * Sets the color range for the IDF scores
042: *
043: * @param maxScore
044: * The score (and above) displayed as maxColor (See QueryScorer.getMaxWeight
045: * which can be used to callibrate scoring scale)
046: * @param minForegroundColor
047: * The hex color used for representing IDF scores of zero eg
048: * #FFFFFF (white) or null if no foreground color required
049: * @param maxForegroundColor
050: * The largest hex color used for representing IDF scores eg
051: * #000000 (black) or null if no foreground color required
052: * @param minBackgroundColor
053: * The hex color used for representing IDF scores of zero eg
054: * #FFFFFF (white) or null if no background color required
055: * @param maxBackgroundColor
056: * The largest hex color used for representing IDF scores eg
057: * #000000 (black) or null if no background color required
058: */
059: public GradientFormatter(float maxScore, String minForegroundColor,
060: String maxForegroundColor, String minBackgroundColor,
061: String maxBackgroundColor) {
062: highlightForeground = (minForegroundColor != null)
063: && (maxForegroundColor != null);
064: if (highlightForeground) {
065: if (minForegroundColor.length() != 7) {
066: throw new IllegalArgumentException(
067: "minForegroundColor is not 7 bytes long eg a hex "
068: + "RGB value such as #FFFFFF");
069: }
070: if (maxForegroundColor.length() != 7) {
071: throw new IllegalArgumentException(
072: "minForegroundColor is not 7 bytes long eg a hex "
073: + "RGB value such as #FFFFFF");
074: }
075: fgRMin = hexToInt(minForegroundColor.substring(1, 3));
076: fgGMin = hexToInt(minForegroundColor.substring(3, 5));
077: fgBMin = hexToInt(minForegroundColor.substring(5, 7));
078:
079: fgRMax = hexToInt(maxForegroundColor.substring(1, 3));
080: fgGMax = hexToInt(maxForegroundColor.substring(3, 5));
081: fgBMax = hexToInt(maxForegroundColor.substring(5, 7));
082: }
083:
084: highlightBackground = (minBackgroundColor != null)
085: && (maxBackgroundColor != null);
086: if (highlightBackground) {
087: if (minBackgroundColor.length() != 7) {
088: throw new IllegalArgumentException(
089: "minBackgroundColor is not 7 bytes long eg a hex "
090: + "RGB value such as #FFFFFF");
091: }
092: if (maxBackgroundColor.length() != 7) {
093: throw new IllegalArgumentException(
094: "minBackgroundColor is not 7 bytes long eg a hex "
095: + "RGB value such as #FFFFFF");
096: }
097: bgRMin = hexToInt(minBackgroundColor.substring(1, 3));
098: bgGMin = hexToInt(minBackgroundColor.substring(3, 5));
099: bgBMin = hexToInt(minBackgroundColor.substring(5, 7));
100:
101: bgRMax = hexToInt(maxBackgroundColor.substring(1, 3));
102: bgGMax = hexToInt(maxBackgroundColor.substring(3, 5));
103: bgBMax = hexToInt(maxBackgroundColor.substring(5, 7));
104: }
105: // this.corpusReader = corpusReader;
106: this .maxScore = maxScore;
107: // totalNumDocs = corpusReader.numDocs();
108: }
109:
110: public String highlightTerm(String originalText,
111: TokenGroup tokenGroup) {
112: if (tokenGroup.getTotalScore() == 0)
113: return originalText;
114: float score = tokenGroup.getTotalScore();
115: if (score == 0) {
116: return originalText;
117: }
118: StringBuffer sb = new StringBuffer();
119: sb.append("<font ");
120: if (highlightForeground) {
121: sb.append("color=\"");
122: sb.append(getForegroundColorString(score));
123: sb.append("\" ");
124: }
125: if (highlightBackground) {
126: sb.append("bgcolor=\"");
127: sb.append(getBackgroundColorString(score));
128: sb.append("\" ");
129: }
130: sb.append(">");
131: sb.append(originalText);
132: sb.append("</font>");
133: return sb.toString();
134: }
135:
136: protected String getForegroundColorString(float score) {
137: int rVal = getColorVal(fgRMin, fgRMax, score);
138: int gVal = getColorVal(fgGMin, fgGMax, score);
139: int bVal = getColorVal(fgBMin, fgBMax, score);
140: StringBuffer sb = new StringBuffer();
141: sb.append("#");
142: sb.append(intToHex(rVal));
143: sb.append(intToHex(gVal));
144: sb.append(intToHex(bVal));
145: return sb.toString();
146: }
147:
148: protected String getBackgroundColorString(float score) {
149: int rVal = getColorVal(bgRMin, bgRMax, score);
150: int gVal = getColorVal(bgGMin, bgGMax, score);
151: int bVal = getColorVal(bgBMin, bgBMax, score);
152: StringBuffer sb = new StringBuffer();
153: sb.append("#");
154: sb.append(intToHex(rVal));
155: sb.append(intToHex(gVal));
156: sb.append(intToHex(bVal));
157: return sb.toString();
158: }
159:
160: private int getColorVal(int colorMin, int colorMax, float score) {
161: if (colorMin == colorMax) {
162: return colorMin;
163: }
164: float scale = Math.abs(colorMin - colorMax);
165: float relScorePercent = Math.min(maxScore, score) / maxScore;
166: float colScore = scale * relScorePercent;
167: return Math.min(colorMin, colorMax) + (int) colScore;
168: }
169:
170: private static char hexDigits[] = { '0', '1', '2', '3', '4', '5',
171: '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
172:
173: private static String intToHex(int i) {
174: return "" + hexDigits[(i & 0xF0) >> 4] + hexDigits[i & 0x0F];
175: }
176:
177: /**
178: * Converts a hex string into an int. Integer.parseInt(hex, 16) assumes the
179: * input is nonnegative unless there is a preceding minus sign. This method
180: * reads the input as twos complement instead, so if the input is 8 bytes
181: * long, it will correctly restore a negative int produced by
182: * Integer.toHexString() but not neccesarily one produced by
183: * Integer.toString(x,16) since that method will produce a string like '-FF'
184: * for negative integer values.
185: *
186: * @param hex
187: * A string in capital or lower case hex, of no more then 16
188: * characters.
189: * @throws NumberFormatException
190: * if the string is more than 16 characters long, or if any
191: * character is not in the set [0-9a-fA-f]
192: */
193: public static final int hexToInt(String hex) {
194: int len = hex.length();
195: if (len > 16)
196: throw new NumberFormatException();
197:
198: int l = 0;
199: for (int i = 0; i < len; i++) {
200: l <<= 4;
201: int c = Character.digit(hex.charAt(i), 16);
202: if (c < 0)
203: throw new NumberFormatException();
204: l |= c;
205: }
206: return l;
207: }
208:
209: }
|