blob: 180546ca2407785724e1f4cd3439c73204b1e158 [file] [log] [blame]
using System;
using System.Text;
namespace Lucene.Net.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.
*/
/// <summary>
/// Formats text with different color intensity depending on the score of the term.
/// </summary>
public class GradientFormatter : IFormatter
{
private float maxScore;
protected int m_fgRMin, m_fgGMin, m_fgBMin;
protected int m_fgRMax, m_fgGMax, m_fgBMax;
protected bool m_highlightForeground;
protected int m_bgRMin, m_bgGMin, m_bgBMin;
protected int m_bgRMax, m_bgGMax, m_bgBMax;
protected bool m_highlightBackground;
/// <summary> Sets the color range for the IDF scores</summary>
/// <param name="maxScore">
/// The score (and above) displayed as maxColor (See <see cref="QueryScorer.MaxTermWeight"/>
/// which can be used to callibrate scoring scale)
/// </param>
/// <param name="minForegroundColor">
/// The hex color used for representing IDF scores of zero eg
/// #FFFFFF (white) or null if no foreground color required
/// </param>
/// <param name="maxForegroundColor">
/// The largest hex color used for representing IDF scores eg
/// #000000 (black) or null if no foreground color required
/// </param>
/// <param name="minBackgroundColor">
/// The hex color used for representing IDF scores of zero eg
/// #FFFFFF (white) or null if no background color required
/// </param>
/// <param name="maxBackgroundColor">
/// The largest hex color used for representing IDF scores eg
/// #000000 (black) or null if no background color required
/// </param>
public GradientFormatter(float maxScore, string minForegroundColor,
string maxForegroundColor, string minBackgroundColor,
string maxBackgroundColor)
{
m_highlightForeground = (minForegroundColor != null) && (maxForegroundColor != null);
if (m_highlightForeground)
{
if (minForegroundColor.Length != 7)
{
throw new ArgumentException("minForegroundColor is not 7 bytes long eg a hex "
+ "RGB value such as #FFFFFF");
}
if (maxForegroundColor.Length != 7)
{
throw new ArgumentException("minForegroundColor is not 7 bytes long eg a hex "
+ "RGB value such as #FFFFFF");
}
m_fgRMin = HexToInt32(minForegroundColor.Substring(1, 3 - 1));
m_fgGMin = HexToInt32(minForegroundColor.Substring(3, 5 - 3));
m_fgBMin = HexToInt32(minForegroundColor.Substring(5, 7 - 5));
m_fgRMax = HexToInt32(maxForegroundColor.Substring(1, 3 - 1));
m_fgGMax = HexToInt32(maxForegroundColor.Substring(3, 5 - 3));
m_fgBMax = HexToInt32(maxForegroundColor.Substring(5, 7 - 5));
}
m_highlightBackground = (minBackgroundColor != null)
&& (maxBackgroundColor != null);
if (m_highlightBackground)
{
if (minBackgroundColor.Length != 7)
{
throw new ArgumentException("minBackgroundColor is not 7 bytes long eg a hex "
+ "RGB value such as #FFFFFF");
}
if (maxBackgroundColor.Length != 7)
{
throw new ArgumentException("minBackgroundColor is not 7 bytes long eg a hex "
+ "RGB value such as #FFFFFF");
}
m_bgRMin = HexToInt32(minBackgroundColor.Substring(1, 3 - 1));
m_bgGMin = HexToInt32(minBackgroundColor.Substring(3, 5 - 3));
m_bgBMin = HexToInt32(minBackgroundColor.Substring(5, 7 - 5));
m_bgRMax = HexToInt32(maxBackgroundColor.Substring(1, 3 - 1));
m_bgGMax = HexToInt32(maxBackgroundColor.Substring(3, 5 - 3));
m_bgBMax = HexToInt32(maxBackgroundColor.Substring(5, 7 - 5));
}
// this.corpusReader = corpusReader;
this.maxScore = maxScore;
// totalNumDocs = corpusReader.numDocs();
}
public virtual string HighlightTerm(string originalText, TokenGroup tokenGroup)
{
if (tokenGroup.TotalScore == 0)
return originalText;
float score = tokenGroup.TotalScore;
if (score == 0)
{
return originalText;
}
var sb = new StringBuilder();
sb.Append("<font ");
if (m_highlightForeground)
{
sb.Append("color=\"");
sb.Append(GetForegroundColorString(score));
sb.Append("\" ");
}
if (m_highlightBackground)
{
sb.Append("bgcolor=\"");
sb.Append(GetBackgroundColorString(score));
sb.Append("\" ");
}
sb.Append(">");
sb.Append(originalText);
sb.Append("</font>");
return sb.ToString();
}
protected internal virtual string GetForegroundColorString(float score)
{
int rVal = GetColorVal(m_fgRMin, m_fgRMax, score);
int gVal = GetColorVal(m_fgGMin, m_fgGMax, score);
int bVal = GetColorVal(m_fgBMin, m_fgBMax, score);
var sb = new StringBuilder();
sb.Append("#");
sb.Append(Int32ToHex(rVal));
sb.Append(Int32ToHex(gVal));
sb.Append(Int32ToHex(bVal));
return sb.ToString();
}
protected internal virtual string GetBackgroundColorString(float score)
{
int rVal = GetColorVal(m_bgRMin, m_bgRMax, score);
int gVal = GetColorVal(m_bgGMin, m_bgGMax, score);
int bVal = GetColorVal(m_bgBMin, m_bgBMax, score);
var sb = new StringBuilder();
sb.Append("#");
sb.Append(Int32ToHex(rVal));
sb.Append(Int32ToHex(gVal));
sb.Append(Int32ToHex(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 = new char[] { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/// <summary>
/// NOTE: This was intToHex() in Lucene
/// </summary>
private static string Int32ToHex(int i)
{
return "" + hexDigits[(i & 0xF0) >> 4] + hexDigits[i & 0x0F];
}
/// <summary>
/// Converts a hex string into an <see cref="int"/>.
/// <para/>
/// NOTE: This was hexToInt() in Lucene
/// </summary>
/// <param name="hex">
/// A string in capital or lower case hex, of no more then 16
/// characters.
/// </param>
/// <exception cref="FormatException">if the string is more than 16 characters long, or if any
/// character is not in the set [0-9a-fA-f]</exception>
public static int HexToInt32(string hex)
{
int len = hex.Length;
if (len > 16)
throw new FormatException();
try
{
// LUCENENET NOTE: Convert.ToInt32(string, int) throws a
// FormatException if the character does not fall in
// the correct range, which is the behavior we are expecting.
// But throws an ArgumentException if passed a negative number.
// So we need to convert to FormatException to provide the correct behavior.
return Convert.ToInt32(hex, 16);
}
catch (ArgumentException e)
{
throw new FormatException(e.Message, e);
}
}
}
}