--- /dev/null
+package org.apache.lucene.search.highlight;
+/**
+ * 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.
+ */
+
+/**
+ * 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 QueryScorer.getMaxWeight
+ * 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();
+ }
+
+ 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;
+ }
+
+}
+