/*
* File : $Source: /alkacon/cvs/alkacon/com.alkacon.opencms.survey/src/com/alkacon/opencms/survey/CmsAverageUtil.java,v $
* Date : $Date: 2010/03/19 15:31:14 $
* Version: $Revision: 1.2 $
*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (C) 2010 Alkacon Software (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.alkacon.opencms.survey;
import java.util.ArrayList;
import java.util.List;
/**
* A class providing utility functions for the "average bar" functionality of the survey
* report.<p>
*
* @author Georg Westenberger
*
*/
public class CmsAverageUtil {
/**
* Tries to parse a string as a "double" number and return it.<p>
*
* @param s the string that should be parsed
* @return a Double object or null if the parsing failed
*/
public static Double parseDouble(String s) {
try {
return new Double(Double.parseDouble(s));
} catch (Exception e) {
return null;
}
}
/**
* Tries to parse a string as a "Long" number and return it.<p>
*
* @param s the string that should be parsed
* @return a Long object or null if the parsing failed
*/
public static Long parseLong(String s) {
try {
return new Long(Long.parseLong(s));
} catch (Exception e) {
return null;
}
}
/**
* Given a list of Double objects, this method creates a new list of Double objects
* by negating all the values from the input list.<p>
*
* For example, if the input list is [1, 2, -3], the result will be [-1, -2, 3].
* The input list is not modified.
*
* @param numbers the input list
* @return the negated list of numbers
*/
public static List<Double> negateAll(List<Double> numbers) {
List<Double> result = new ArrayList<Double>();
for (double number : numbers) {
result.add(-number);
}
return result;
}
/**
* From a "FieldDefault" specification used in form generator forms for e.g.
* radio buttons, this method extracts all the numeric keys.<p>
*
* For example, if the input is "1:foo|2:bar|3|bar:qqq|xyzzy", then the list
* [1, 2, 3] will be returned.
*
* @param fieldDefault The FieldDefault entry
* @return the list of extracted numbers.
*/
public static List<Double> getNumbersFromDefault(String fieldDefault) {
List<Double> result = new ArrayList<Double>();
String[] entries = fieldDefault.split("\\|");
for (String entry : entries) {
int colonIndex = entry.indexOf(":");
if (colonIndex != -1) {
entry = entry.substring(0, colonIndex);
}
Double d = parseDouble(entry);
if (d != null) {
result.add(d);
}
}
return result;
}
/**
* Finds the maximum from a list of Doubles.<p>
*
* If the list is empty, 0 is returned.
*
* @param numbers the list of Doubles
* @return the maximum
*/
public static double getMax(List<Double> numbers) {
if (numbers.size() == 0) {
return 0.0;
}
double max = numbers.get(0);
for (Double number : numbers) {
max = Math.max(max, number);
}
return max;
}
/**
* Finds the minimum from a list of Doubles.<p>
*
* If the list is empty, 0 is returned.
* @param numbers the list of Doubles
* @return the minimum
*/
public static double getMin(List<Double> numbers) {
if (numbers.size() == 0) {
return 0.0;
}
double min = numbers.get(0);
for (Double number : numbers) {
min = Math.min(min, number);
}
return min;
}
/**
* Returns the difference between the maximum and the minimum of a list of Doubles.<p>
*
* @param numbers the input list
* @return the range of the input list
*/
public static double getRange(List<Double> numbers) {
return getMax(numbers) - getMin(numbers);
}
/**
* Formats a ratio as a percentage.<p>
*
* For example, 0.362 will be formatted as "36.2%".
* @param ratio the value that should be formatted as a percentage.
* @return the input value formatted as a percentage.
*/
public static String asPercentage(double ratio) {
return "" + (ratio * 100) + "%";
}
/**
* Returns a corrected bar split ratio by reserving a certain portion of the bar
* as initial part that will always be displayed even if the split ratio is 0 (i.e.
* the average equals the minimum value.<p>
*
* For example, if the split ratio is 0.5 and initialPart is 1/7, then the corrected
* split ratio is 4/7.
*
*
* @param initialPart the portion of the bar that should be reserved as the initial part
* @param ratio the original split ratio
* @return the corrected split ratio
*/
public static double getBarSplitRatio(double initialPart, double ratio) {
return (1 - initialPart) * ratio + initialPart;
}
/**
* This method does the same as getBarHtml, but reverses the orientation,
* so that an average value equal to the minimum value will be displayed as
* a full bar.<p>
*
* @see CmsAverageUtil#getBarHtml(String, String, String, String)
*
*/
public static String getBarHtmlReverse(String fieldDefault, String averageStr, String displayStr, String color) {
Double average = parseDouble(averageStr);
if (average == null) {
return "<!-- average is null -->";
}
Double negAverage = new Double(-average.doubleValue());
List<Double> numbers = getNumbersFromDefault(fieldDefault);
List<Double> negNumbers = negateAll(numbers);
double range = getRange(negNumbers);
if (range == 0) {
return "<!-- range is 0 -->";
}
double ratio = (negAverage - getMin(negNumbers)) / range;
double correctedRatio = getBarSplitRatio(0.03, ratio);
return makeBar(correctedRatio, displayStr, color);
}
/**
* Returns the HTML for a bar showing the average value.<p>
*
* @param fieldDefault the FieldDefault entry
* @param averageStr a string containing the average value
* @param displayStr the string that should be displayed inside the bar.
* @param color the color of the bar.
* @return the HTML for the average bar.
*/
public static String getBarHtml(String fieldDefault, String averageStr, String displayStr, String color) {
Double average = parseDouble(averageStr);
if (average == null) {
return "<!-- average is null -->";
}
List<Double> numbers = getNumbersFromDefault(fieldDefault);
double range = getRange(numbers);
if (range == 0) {
return "<!-- range is 0 -->";
}
double ratio = (average - getMin(numbers)) / range;
double correctedRatio = getBarSplitRatio(0.03, ratio);
return makeBar(correctedRatio, displayStr, color);
}
/**
* Helper method that actually creates the HTML for a bar.<p>
*
* @param ratio the width of the bar as a value between 0 and 1
* @param text the text to be displayed inside a bar
* @param color the bar color
* @return the HTML code for the bar.
*/
public static String makeBar(double ratio, String text, String color) {
StringBuffer buffer = new StringBuffer();
buffer.append("<div class=\"reportitem\">");
buffer.append("<span class=\"processbar\">");
buffer.append("<span class=\"bar\" style=\"width:"
+ asPercentage(ratio)
+ "; background-color:"
+ color
+ "; color:#FFF;\" >"
+ text
+ "</span>");
buffer.append("</span>");
buffer.append("</div>");
return buffer.toString();
}
}