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
045: * QueryScorer.getMaxWeight which can be used to callibrate scoring
046: * scale)
047: * @param minForegroundColor
048: * The hex color used for representing IDF scores of zero eg #FFFFFF
049: * (white) or null if no foreground color required
050: * @param maxForegroundColor
051: * The largest hex color used for representing IDF scores eg #000000
052: * (black) or null if no foreground color required
053: * @param minBackgroundColor
054: * The hex color used for representing IDF scores of zero eg #FFFFFF
055: * (white) or null if no background color required
056: * @param maxBackgroundColor
057: * The largest hex color used for representing IDF scores eg #000000
058: * (black) or null if no background color required
059: */
060: public GradientFormatter(float maxScore, String minForegroundColor,
061: String maxForegroundColor, String minBackgroundColor,
062: String maxBackgroundColor) {
063: highlightForeground = (minForegroundColor != null)
064: && (maxForegroundColor != null);
065: if (highlightForeground) {
066: if (minForegroundColor.length() != 7) {
067: throw new IllegalArgumentException(
068: "minForegroundColor is not 7 bytes long eg a hex "
069: + "RGB value such as #FFFFFF");
070: }
071: if (maxForegroundColor.length() != 7) {
072: throw new IllegalArgumentException(
073: "minForegroundColor is not 7 bytes long eg a hex "
074: + "RGB value such as #FFFFFF");
075: }
076: fgRMin = hexToInt(minForegroundColor.substring(1, 3));
077: fgGMin = hexToInt(minForegroundColor.substring(3, 5));
078: fgBMin = hexToInt(minForegroundColor.substring(5, 7));
079:
080: fgRMax = hexToInt(maxForegroundColor.substring(1, 3));
081: fgGMax = hexToInt(maxForegroundColor.substring(3, 5));
082: fgBMax = hexToInt(maxForegroundColor.substring(5, 7));
083: }
084:
085: highlightBackground = (minBackgroundColor != null)
086: && (maxBackgroundColor != null);
087: if (highlightBackground) {
088: if (minBackgroundColor.length() != 7) {
089: throw new IllegalArgumentException(
090: "minBackgroundColor is not 7 bytes long eg a hex "
091: + "RGB value such as #FFFFFF");
092: }
093: if (maxBackgroundColor.length() != 7) {
094: throw new IllegalArgumentException(
095: "minBackgroundColor is not 7 bytes long eg a hex "
096: + "RGB value such as #FFFFFF");
097: }
098: bgRMin = hexToInt(minBackgroundColor.substring(1, 3));
099: bgGMin = hexToInt(minBackgroundColor.substring(3, 5));
100: bgBMin = hexToInt(minBackgroundColor.substring(5, 7));
101:
102: bgRMax = hexToInt(maxBackgroundColor.substring(1, 3));
103: bgGMax = hexToInt(maxBackgroundColor.substring(3, 5));
104: bgBMax = hexToInt(maxBackgroundColor.substring(5, 7));
105: }
106: // this.corpusReader = corpusReader;
107: this .maxScore = maxScore;
108: // totalNumDocs = corpusReader.numDocs();
109: }
110:
111: public String highlightTerm(String originalText,
112: TokenGroup tokenGroup) {
113: if (tokenGroup.getTotalScore() == 0)
114: return originalText;
115: float score = tokenGroup.getTotalScore();
116: if (score == 0) {
117: return originalText;
118: }
119: StringBuffer sb = new StringBuffer();
120: sb.append("<font ");
121: if (highlightForeground) {
122: sb.append("color=\"");
123: sb.append(getForegroundColorString(score));
124: sb.append("\" ");
125: }
126: if (highlightBackground) {
127: sb.append("bgcolor=\"");
128: sb.append(getBackgroundColorString(score));
129: sb.append("\" ");
130: }
131: sb.append(">");
132: sb.append(originalText);
133: sb.append("</font>");
134: return sb.toString();
135: }
136:
137: protected String getForegroundColorString(float score) {
138: int rVal = getColorVal(fgRMin, fgRMax, score);
139: int gVal = getColorVal(fgGMin, fgGMax, score);
140: int bVal = getColorVal(fgBMin, fgBMax, score);
141: StringBuffer sb = new StringBuffer();
142: sb.append("#");
143: sb.append(intToHex(rVal));
144: sb.append(intToHex(gVal));
145: sb.append(intToHex(bVal));
146: return sb.toString();
147: }
148:
149: protected String getBackgroundColorString(float score) {
150: int rVal = getColorVal(bgRMin, bgRMax, score);
151: int gVal = getColorVal(bgGMin, bgGMax, score);
152: int bVal = getColorVal(bgBMin, bgBMax, score);
153: StringBuffer sb = new StringBuffer();
154: sb.append("#");
155: sb.append(intToHex(rVal));
156: sb.append(intToHex(gVal));
157: sb.append(intToHex(bVal));
158: return sb.toString();
159: }
160:
161: private int getColorVal(int colorMin, int colorMax, float score) {
162: if (colorMin == colorMax) {
163: return colorMin;
164: }
165: float scale = Math.abs(colorMin - colorMax);
166: float relScorePercent = Math.min(maxScore, score) / maxScore;
167: float colScore = scale * relScorePercent;
168: return Math.min(colorMin, colorMax) + (int) colScore;
169: }
170:
171: private static char hexDigits[] = { '0', '1', '2', '3', '4', '5',
172: '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
173:
174: private static String intToHex(int i) {
175: return "" + hexDigits[(i & 0xF0) >> 4] + hexDigits[i & 0x0F];
176: }
177:
178: /**
179: * Converts a hex string into an int. Integer.parseInt(hex, 16) assumes the
180: * input is nonnegative unless there is a preceding minus sign. This method
181: * reads the input as twos complement instead, so if the input is 8 bytes
182: * long, it will correctly restore a negative int produced by
183: * Integer.toHexString() but not neccesarily one produced by
184: * Integer.toString(x,16) since that method will produce a string like '-FF'
185: * for negative integer values.
186: *
187: * @param hex
188: * A string in capital or lower case hex, of no more then 16
189: * characters.
190: * @throws NumberFormatException
191: * if the string is more than 16 characters long, or if any
192: * character is not in the set [0-9a-fA-f]
193: */
194: public static final int hexToInt(String hex) {
195: int len = hex.length();
196: if (len > 16)
197: throw new NumberFormatException();
198:
199: int l = 0;
200: for (int i = 0; i < len; i++) {
201: l <<= 4;
202: int c = Character.digit(hex.charAt(i), 16);
203: if (c < 0)
204: throw new NumberFormatException();
205: l |= c;
206: }
207: return l;
208: }
209:
210: }
|