blob: 9178f44703fb3ed26b7d7b0a964432c54f2e3b6e [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.apache.lucene.search.highlight;
/** Formats text with different color intensity depending on the score of the term. */
public class GradientFormatter implements Formatter {
private float maxScore;
int fgRMin, fgGMin, fgBMin;
int fgRMax, fgGMax, fgBMax;
protected boolean highlightForeground;
int bgRMin, bgGMin, bgBMin;
int bgRMax, bgGMax, bgBMax;
protected boolean highlightBackground;
/**
* Sets the color range for the IDF scores
*
* @param maxScore The score (and above) displayed as maxColor (See {@link
* QueryScorer#getMaxTermWeight()} which can be used to calibrate scoring scale)
* @param minForegroundColor The hex color used for representing IDF scores of zero eg #FFFFFF
* (white) or null if no foreground color required
* @param maxForegroundColor The largest hex color used for representing IDF scores eg #000000
* (black) or null if no foreground color required
* @param minBackgroundColor The hex color used for representing IDF scores of zero eg #FFFFFF
* (white) or null if no background color required
* @param maxBackgroundColor The largest hex color used for representing IDF scores eg #000000
* (black) or null if no background color required
*/
public GradientFormatter(
float maxScore,
String minForegroundColor,
String maxForegroundColor,
String minBackgroundColor,
String maxBackgroundColor) {
highlightForeground = (minForegroundColor != null) && (maxForegroundColor != null);
if (highlightForeground) {
if (minForegroundColor.length() != 7) {
throw new IllegalArgumentException(
"minForegroundColor is not 7 bytes long eg a hex " + "RGB value such as #FFFFFF");
}
if (maxForegroundColor.length() != 7) {
throw new IllegalArgumentException(
"minForegroundColor is not 7 bytes long eg a hex " + "RGB value such as #FFFFFF");
}
fgRMin = hexToInt(minForegroundColor.substring(1, 3));
fgGMin = hexToInt(minForegroundColor.substring(3, 5));
fgBMin = hexToInt(minForegroundColor.substring(5, 7));
fgRMax = hexToInt(maxForegroundColor.substring(1, 3));
fgGMax = hexToInt(maxForegroundColor.substring(3, 5));
fgBMax = hexToInt(maxForegroundColor.substring(5, 7));
}
highlightBackground = (minBackgroundColor != null) && (maxBackgroundColor != null);
if (highlightBackground) {
if (minBackgroundColor.length() != 7) {
throw new IllegalArgumentException(
"minBackgroundColor is not 7 bytes long eg a hex " + "RGB value such as #FFFFFF");
}
if (maxBackgroundColor.length() != 7) {
throw new IllegalArgumentException(
"minBackgroundColor is not 7 bytes long eg a hex " + "RGB value such as #FFFFFF");
}
bgRMin = hexToInt(minBackgroundColor.substring(1, 3));
bgGMin = hexToInt(minBackgroundColor.substring(3, 5));
bgBMin = hexToInt(minBackgroundColor.substring(5, 7));
bgRMax = hexToInt(maxBackgroundColor.substring(1, 3));
bgGMax = hexToInt(maxBackgroundColor.substring(3, 5));
bgBMax = hexToInt(maxBackgroundColor.substring(5, 7));
}
// this.corpusReader = corpusReader;
this.maxScore = maxScore;
// totalNumDocs = corpusReader.numDocs();
}
@Override
public String highlightTerm(String originalText, TokenGroup tokenGroup) {
if (tokenGroup.getTotalScore() == 0) return originalText;
float score = tokenGroup.getTotalScore();
if (score == 0) {
return originalText;
}
StringBuilder sb = new StringBuilder();
sb.append("<font ");
if (highlightForeground) {
sb.append("color=\"");
sb.append(getForegroundColorString(score));
sb.append("\" ");
}
if (highlightBackground) {
sb.append("bgcolor=\"");
sb.append(getBackgroundColorString(score));
sb.append("\" ");
}
sb.append(">");
sb.append(originalText);
sb.append("</font>");
return sb.toString();
}
protected String getForegroundColorString(float score) {
int rVal = getColorVal(fgRMin, fgRMax, score);
int gVal = getColorVal(fgGMin, fgGMax, score);
int bVal = getColorVal(fgBMin, fgBMax, score);
StringBuilder sb = new StringBuilder();
sb.append("#");
sb.append(intToHex(rVal));
sb.append(intToHex(gVal));
sb.append(intToHex(bVal));
return sb.toString();
}
protected String getBackgroundColorString(float score) {
int rVal = getColorVal(bgRMin, bgRMax, score);
int gVal = getColorVal(bgGMin, bgGMax, score);
int bVal = getColorVal(bgBMin, bgBMax, score);
StringBuilder sb = new StringBuilder();
sb.append("#");
sb.append(intToHex(rVal));
sb.append(intToHex(gVal));
sb.append(intToHex(bVal));
return sb.toString();
}
private int getColorVal(int colorMin, int colorMax, float score) {
if (colorMin == colorMax) {
return colorMin;
}
float scale = Math.abs(colorMin - colorMax);
float relScorePercent = Math.min(maxScore, score) / maxScore;
float colScore = scale * relScorePercent;
return Math.min(colorMin, colorMax) + (int) colScore;
}
private static char hexDigits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
private static String intToHex(int i) {
return "" + hexDigits[(i & 0xF0) >> 4] + hexDigits[i & 0x0F];
}
/**
* Converts a hex string into an int. Integer.parseInt(hex, 16) assumes the input is nonnegative
* unless there is a preceding minus sign. This method reads the input as twos complement instead,
* so if the input is 8 bytes long, it will correctly restore a negative int produced by
* Integer.toHexString() but not necessarily one produced by Integer.toString(x,16) since that
* method will produce a string like '-FF' for negative integer values.
*
* @param hex A string in capital or lower case hex, of no more then 16 characters.
* @throws NumberFormatException if the string is more than 16 characters long, or if any
* character is not in the set [0-9a-fA-f]
*/
public static final int hexToInt(String hex) {
int len = hex.length();
if (len > 16) throw new NumberFormatException();
int l = 0;
for (int i = 0; i < len; i++) {
l <<= 4;
int c = Character.digit(hex.charAt(i), 16);
if (c < 0) throw new NumberFormatException();
l |= c;
}
return l;
}
}