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; } }