How do you put integers within a range in Java? - java

Though I am sure that this answer is simple, I am not sure that is asked of me for this assignment. Here is the full code that I have written (so just a return!) as well as the instructions that were given:
package code;
/**
* This class contains a variety of methods that will be used throughout the Ratings and Reviews
* project.
*/
public class Utilities{
/**
* Computes the average of two ratings
*
* #param rating0 An integer rating in the range of 1-5 inclusive
* #param rating1 An integer rating in the range of 1-5 inclusive
* #return the average of rating0 and rating1 as a double
*/
public double averageRating(int rating0, int rating1){
return ((rating0 + rating1) / 2); // Don't forget to replace this return statement with your own
}
Sorry for bad structure upon pasting it. I think my return is suitable for what is being done, provided that the rating could be just anything. I know that it can only be between 1-5, though, so how would one go about specifying that?

How about throwing an InvalidArgumentException if the range is violated?
e.g.
public double averageRating(int rating0, int rating1){
if (rating0 < 1 || rating0 > 5 || rating1 < 1 || rating1 > 5) {
throw new InvalidArgumentException("Rating out of range");
}
return ((rating0 + rating1) / 2.0); // Don't forget to replace this return statement with your own
}

Related

JavaBDD sat count with subset of variables

I am using JavaBDD to do some computation with BDDs.
I have a very large BDD with many variables and I want to calculate how many ways it can be satisfied with a small subset of those variables.
My current attempt looks like this:
// var 1,2,3 are BDDVarSets with 1 variable.
BDDVarSet union = var1;
union = union.union(var2);
union = union.union(var3);
BDD varSet restOfVars = allVars.minus(union);
BDD result = largeBdd.exist(restOfVars);
double sats = result.satCount(); // Returns a very large number (way too large).
double partSats = result.satCount(union) // Returns an inccorrect number. It is documented that this should not work.
Is the usage of exist() incorrect?
After a bit of playing around I understood what my problem was.
double partSats = result.satCount(union);
Does return the correct answer. What it does is calculate how many possible solutions there are, and divides the solution by 2^(#vars in set).
The reason I thought satCount(union) does not work is due to an incorrect usage of exist() somewhere else in the code.
Here is the implementation of satCound(varSet) for reference:
/**
* <p>Calculates the number of satisfying variable assignments to the variables
* in the given varset. ASSUMES THAT THE BDD DOES NOT HAVE ANY ASSIGNMENTS TO
* VARIABLES THAT ARE NOT IN VARSET. You will need to quantify out the other
* variables first.</p>
*
* <p>Compare to bdd_satcountset.</p>
*
* #return the number of satisfying variable assignments
*/
public double satCount(BDDVarSet varset) {
BDDFactory factory = getFactory();
if (varset.isEmpty() || isZero()) /* empty set */
return 0.;
double unused = factory.varNum();
unused -= varset.size();
unused = satCount() / Math.pow(2.0, unused);
return unused >= 1.0 ? unused : 1.0;
}

How to verify that all own thrown runtime exceptions are covered in Javadoc?

I throw a bunch of custom runtime exceptions in my code and I want to make sure that in all public methods, I document which runtime exception might be thrown (by myself) and why. This would be very hulpful since I'm maintaining a library which is used by many projects and I want it to be upfront and predictable regarding thrown (runtime) exceptions.
Is there a compiler option, maven plugin, Intellij plugin or custom tool that can help me find missed throws clauses? With checked exceptions it's easy, the compiler will just complain if I missed one, but for runtime exceptions both throws and #throws are not enforced.
One thing I thought of was to temporarily make all my own runtime exceptions checked exceptions (they already share a super class), but that would be a one-off exercise. I would like to verify my code/documentation each time I make changes so I can never forget to document my runtime exceptions.
Another way could be to actually have checked exceptions throughout the code and convert them to runtime only in the public api:
class Foo {
// oops, throws not documented with #throws
public void publicMethod() {
try {
privateMethod1();
} catch (CheckedFooException e) {
throw new RuntimeFooException(e);
}
}
private void privateMethod1() throws CheckedFooException {
privateMethod2();
}
private void privateMethod2() throws CheckedFooException {
throw new CheckedFooException();
}
}
This approach would force me to think about CheckedFooException in all public methods. Then to check if I missed documenting one (ie. #throws RuntimeFooException), I would simply do a regex search on catch.*CheckedFooException and check for missing #throws entries. Rather unwieldy process though (and there's a lot of public api that would get peppered with try...catch statements).
Answer: There is some discussion about whether you should document (your own thrown) runtime exceptions at all (the summary so far: it depends), but as far as a direct answer to my question, the accepted answer answers it adequately; I can take that approach, implement my use case and even make a maven plugin with it, given some time and effort. I uploaded a cleaned up start project for this.
After understanding your question and researching this subject, I finally found what I thought to be one of the best tools to do this job. With this not only you can find each throws instance that you haven't documented, but you can also find where you don't throw anything but accidentally document a throw value.
The idea behind this is to parse the code into an abstract syntax tree. Then look for methods and look for throws statement in the methods. If a method have any throw statement, extract the exception name from those statements. Then get the Javadoc for that method. Check the Javadoc for all the #throw tags and get the name of the exception that been documented. After that, compare the exception throws versus the one that been documented. The last, you kind of have to figure that out on your own depend on your usage circumstance.
The tool I used for this is JavaParser. You can find them on Github at https://github.com/javaparser/javaparser. I downloaded their latest version. Their website is at https://javaparser.org/. They wrote a book on this subject and they mentioned that you can pay $0 dollar for the book. However, I didn't read that as they also have a Javadoc version for their program which can be found at https://www.javadoc.io/doc/com.github.javaparser/javaparser-core/3.15.1.
I wrote a demonstrate code below. In no mean that this code is final. It is just an example. You have to fix it into making it work for your case. I didn't take into consideration of nested classes, nested method, or methods within classes that are within a method. Also, the example code was written for class only and not interface. However, it is easy to adapt the code to change to able to handle interfaces.
For this, you would need to download javaParser, build it, and have their javaparser-core-3.15.1.jar or whichever version in your classpath.
The demonstrated code is below and the test.java is a file from a project that I wrote but you could use any. I also included comments in the example code.
import com.github.javaparser.*;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.comments.*;
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.javadoc.*;
import java.io.IOException;
import java.nio.file.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;
class Main{
public static void main(String[] args) throws IOException {
// Set file path
Path path = Paths.get("test.java");
// Set configuration
ParserConfiguration parseConfig = new ParserConfiguration();
parseConfig.setCharacterEncoding(Charset.forName("UTF-8"));
parseConfig.setTabSize(4);
parseConfig.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8);
// Get the parser
JavaParser jvParser = new JavaParser(parseConfig);
// Parse the result
ParseResult<CompilationUnit> parseResult = jvParser.parse(path);
// Check for problem
if ( !parseResult.isSuccessful() ) {
System.out.print("Parsing java code fail with the following problems:");
List<Problem> problems = parseResult.getProblems();
for ( Problem problem : problems ){
System.out.println(problem.getMessage());
}
return;
}
// Get the compilationUnit
// No optional checking for Optional<CompilationUnit> due to already check above.
CompilationUnit compilationUnit = parseResult.getResult().get();
// Get Classes
List<ClassOrInterfaceDeclaration> classes = compilationUnit.findAll(ClassOrInterfaceDeclaration.class).stream()
.filter(c -> !c.isInterface())
.collect(Collectors.toList());
// Traverse through each class to get method
for ( ClassOrInterfaceDeclaration c : classes ) {
// Get methods
List<MethodDeclaration> methods = c.getMethods();
for ( MethodDeclaration method : methods ) {
// Get the body statement
Optional <BlockStmt> body = method.getBody();
// if no body continue
if ( !body.isPresent() ) continue;
// After getting the body of the method code
// Search for the throw statements.
List<ThrowStmt> throwStatements = body.get().findAll(ThrowStmt.class);
// No throw statements, skip
if ( throwStatements.size() == 0 ) continue;
// Storing name of exceptions thrown into this list.
List<String> exceptionsThrown = new ArrayList<String>();
for ( ThrowStmt stmt : throwStatements ){
// Convert the throw expression to object creation expression and get the type.
String exceptionName = stmt.getExpression().asObjectCreationExpr().getType().toString();
if ( !exceptionsThrown.contains(exceptionName) ) exceptionsThrown.add(exceptionName);
}
/*
* Debug block for up to this point
System.out.println(method.getName());
System.out.println(exceptionsThrown);
System.out.println();
*
**/
// Get The Javadoc
Optional<Javadoc> javadoc = method.getJavadoc();
// To store the throws Tags
List<JavadocBlockTag> throwTags;
// A list of thrown exception that been documented.
List<String> exceptionsDocumented = new ArrayList<String>();
if ( javadoc.isPresent() ) {
throwTags = javadoc.get()
.getBlockTags()
.stream()
.filter(t -> t.getType() == JavadocBlockTag.Type.THROWS)
.collect(Collectors.toList());
for ( JavadocBlockTag tag : throwTags ) {
/*
* This may be buggy as
* the code assumed #throw exception
* to be on its own line. Therefore
* it will just take the first line as the exception name.
*/
String exceptionName = tag.getContent().toText()
.split("\n")[0]; // Use system line separator or change
// line accordingly.
if ( !exceptionsDocumented.contains(exceptionName) )
exceptionsDocumented.add(exceptionName);
}
}
// getBegin can extract the line out. But evaluating the optional would take some more code
// and is just for example so this was done like this without any checking.
System.out.println("Method: " + method.getName() + " at line " + method.getBegin());
System.out.println("Throws Exceptions: ");
System.out.println(exceptionsThrown);
System.out.println("Documented Exceptions:");
System.out.println(exceptionsDocumented);
System.out.println(System.lineSeparator() + System.lineSeparator());
}
}
}
}
test.java content:
package host.fai.lib.faiNumber;
/*
* Copyright 2019 Khang Hoang Nguyen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
**/
/**
* <p>The <code>Base2Util</code> class is a final class that provides
* static methods for converting base 2 numbering system values in
* string representation to a Java's Primitive Data Type.
*
* <p>Currently this class supports converting base 2 numbers values
* in string representation to integer int values and integer
* long values.
*
* <p>This class can parse unsigned base 2 numbers to a supported
* integer signed type as if the integer type is unsigned. However,
* some of the values must be interprete properly to get the correct
* result.
*
* <p>Example for interpreting signed value as unsigned value.
*
* <p>It is possible to store the value of 18446744073709551615L
* into a long(signed) value. However, if that value is stored into a
* signed long integer type and if we were to interprete the value
* normally, we would get a -1L value. However, if the -1L value is
* pass to LongUtil.toStringAsUnsigned, we would get
* 18446744073709551615 in string format.
*
* <p>The following example is to get to -1L. First, we assign a value
* of 9223372036854775807L to an interger long variable, multiply that
* variable to 2L, and add 1L to it.
* <pre>
* long a = 9223372036854775807L * 2L + 1L;
* System.out.println(a);
* System.out.println(LongUtil.toStringAsUnsigned(a));
* </pre>
*
* <p>Example methods for interprete signed type as unsigned type
* in a decimal strings value are
* {#link IntUtil#toStringAsUnsigned(int) IntUtil.toStringAsUnsigned}
* and {#link LongUtil#toStringAsUnsigned(long) LongUtil.toStringAsUnsigned}.
* </p>
*
* #author Khang Hoang Nguyen
*
* #since 1.0.0.f
**/
public final class Base2Util{
private Base2Util(){};
/**
* Parse the input string as signed base 2 digits representation
* into an integer int value.
*
* #param input
* A string to be parsed as signed base 2 number to an
* integer int value.
*
* #return An integer int value of the signed base 2 number
* {#code input} string.
*
* #throws NumberFormatException
* If the {#code input} string contains invalid signed
* base 2 digits, if the {#code input} string contains a
* value that is smaller than the value of Integer.MIN_VALUE(
* {#value java.lang.Integer#MIN_VALUE}),
* or if the {#code input} string contains a value that
* is larger than the value of Integer.MAX_VALUE(
* {#value java.lang.Integer#MAX_VALUE}).
*
* #throws EmptyStringException
* If the {#code input} string is empty.
*
* #since 1.0.0.f
**/
public static final int toInt(final String input){
final int length = input.length();
if ( length == 0 ) throw new EmptyStringException();
final char ch1 = input.charAt(0); int start;
if ( ch1 == '-' || ch1 == '+' ){
if ( length == 1 ) throw new NumberFormatException(input);
start = 1;
} else {
start = 0;
}
int out = 0, c;
while ( start < length && input.charAt(start) == '0' ) start++;
final int runlen = length - start;
if ( runlen > 31 ){
if ( runlen > 32 ) throw new NumberFormatException(input);
if ( ch1 != '-' ) throw new NumberFormatException(input);
if ( input.charAt(start++) != '1') throw new NumberFormatException(input);
for ( ; start < length; start++){
if ( input.charAt(start) != '0' ) throw new NumberFormatException(input);
}
return -2147483648;
}
for ( ; start < length; start++){
c = (input.charAt(start) ^ '0');
if ( c > 1 ) throw new NumberFormatException(input);
out = (out << 1) | c;
}
if ( ch1 == '-' ) return ~out + 1;
return out;
}
/**
* Parse the input string as unsigned base 2 number representation
* into an integer int value as if the integer int is an unsigned
* type. For values that need to be interpreted correctly, see the
* {#link IntUtil#toStringAsUnsigned(int) toStringAsUnsigned} method
* of the {#link IntUtil IntUtil} class.
*
* #param input
* A string to be parsed as unsigned base 2 number to an
* integer int value as if the integer int is an unsigned
* type.
*
* #return An int value that represents an unsigned integer int
* value of the unsigned base 2 number {#code input} string.
*
* #throws NumberFormatException
* If the {#code input} string contains invalid unsigned
* base 2 digits, if the {#code input} string contains a
* value that is beyond the capacity of the integer int
* data type.
*
* #throws EmptyStringException
* If the {#code input} string is empty.
*
* #since 1.0.0.f
**/
public static final int toIntAsUnsigned(final String input){
final int length = input.length();
if ( length == 0 ) throw new EmptyStringException();
int start = 0;
int out = 0, c;
while ( start < length && input.charAt(start) == '0' ) start++;
if ( length - start > 32 ) throw new NumberFormatException(input);
for ( ; start < length; start++){
c = (input.charAt(start) ^ '0');
if ( c > 1 ) throw new NumberFormatException(input);
out = (out << 1) | c;
}
return out;
}
/**
* Parse the input string as signed base 2 number representation
* into an integer long value.
*
* #param input
* A string to be parsed as signed base 2 number to an
* integer long value.
*
* #return An integer long value of the signed base 2 number
* {#code input} string.
*
* #throws NumberFormatException
* If the {#code input} string contains invalid signed
* base 2 digits, if the {#code input} string contains a
* value that is smaller than the value of Long.MIN_VALUE(
* {#value java.lang.Long#MIN_VALUE}), or if
* the {#code input} string contains a value that is larger
* than the value of Long.MAX_VALUE(
* {#value java.lang.Long#MAX_VALUE}).
*
* #throws EmptyStringException
* If the {#code input} string is empty.
*
* #since 1.0.0.f
**/
public static final long toLong(final String input){
final int length = input.length();
if ( length == 0 ) throw new EmptyStringException();
final char ch1 = input.charAt(0); int start = 0;
if ( ch1 == '-' || ch1 == '+' ){
if ( length == 1 ) throw new NumberFormatException(input);
start = 1;
}
long out = 0, c;
while ( start < length && input.charAt(start) == '0' ) start++;
final int runlen = length - start;
if ( runlen > 63 ){
if ( runlen > 64 ) throw new NumberFormatException(input);
if ( ch1 != '-' ) throw new NumberFormatException(input);
if ( input.charAt(start++) != '1') throw new NumberFormatException(input);
for ( ; start < length; start++){
if ( input.charAt(start) != '0' ) throw new NumberFormatException(input);
}
return -9223372036854775808L;
}
for ( ; start < length; start++){
c = (input.charAt(start) ^ '0');
if ( c > 1L ) throw new NumberFormatException(input);
out = (out << 1) | c;
}
if ( ch1 == '-' ) return ~out + 1L;
return out;
}
/**
* Parse the input string as unsigned base 2 number representation
* into an integer long value as if the integer long is an unsigned
* type. For values that need to be interpreted correctly, see the
* {#link LongUtil#toStringAsUnsigned(long) toStringAsUnsigned} method
* of the {#link LongUtil LongUtil} class.
*
* #param input
* A string to be parsed as unsigned base 2 number to an
* integer long value as if the integer long is an unsigned
* type.
*
* #return An integer long value represent the unsigned integer
* long value of the unsigned base 2 number {#code input}
* string.
*
* #throws NumberFormatException
* If the {#code input} string contains invalid unsigned
* base 2 digits, or if the {code input} string
* contains a value that is beyond the capacity of the
* long data type.
*
* #throws EmptyStringException
* If the {#code input} string is empty.
*
* #since 1.0.0.f
**/
public static final long toLongAsUnsigned(final String input){
final int length = input.length();
if ( length == 0 ) throw new EmptyStringException();
int start = 0;
long out = 0, c;
while ( start < length && input.charAt(start) == '0' ) start++;
if ( length - start > 64 ) throw new NumberFormatException(input);
for ( ; start < length; start++){
c = (input.charAt(start) ^ '0');
if ( c > 1L ) throw new NumberFormatException(input);
out = (out << 1) | c;
}
return out;
}
}
If I understand your question correctly, you are violating the purpose of RuntimeException.
As explained in the thread here,
RuntimeException(s) are the one's that are not supposed to be handled by the client. Rather it is a situation, where the client cannot recover. In such case, all he can do is either abandon the application or throw back the error.
If you are adding documentation to cover these exceptions, that means you very well know why this exception is occurring. In such cases it should be checked exception, and not unchecked.
So Technically speaking, no library will provide the functionality you are looking for, as runtime exceptions are not expected to be documented. It's a design smell. so better you correct the design, than adding documentation.
If it is not possible, and you insist to use RuntimeException only, then I would recommend to look at this answer and build your own Findbugs/checkstyle rule that will do the trick.
Let all your exceptions inherit from one exception superclass:
public class MySuperException extends RuntimeException {
}
public class MyException extends MySuperException {
}
To validate, that all exceptions are documented, simply exchange your super class (for example by providing another version of the file at a later position of your classpath):
// temporary class, only for compile time checks
// do not export this into jar
public class MySuperException extends Exception {
}
Please check Semmle code analysis, which has a query "Missing Javadoc for thrown exception"
Semmle has plugins LGTM and QL that could be used from IDE like Eclipse.
or
as an alternative approach please use something similar to Eclipse plugin JAutodoc to complete existing Javadoc.

Getting the unicode value of the first character in a string

I am basically being asked to take the Unicode value of a string, multiply it by 10% and add whatever level the object currently has. It's frustrating because as it turns out I have the logic down including the code yet I still get an error that says: expected:<0> but was:<8>. Any suggestions, maybe it's just a slight nuance I have to make in the logic, although I'm fairly certain it's right. Take note of the getLevel method because that's where the error is
public class PouchCreature implements Battleable {
private String name;
private int strength;
private int levelUps;
private int victoriesSinceLevelUp;
/**
* Standard constructor. levelUps and victoriesSinceLevelUp start at 0.
*
* #param nameIn desired name for this PouchCreature
* #param strengthIn starting strength for this PouchCreature
*/
public PouchCreature(String nameIn, int strengthIn) {
this.name = nameIn;
this.strength = strengthIn;
this.levelUps = 0;
this.victoriesSinceLevelUp = 0;
}
/**
* Copy constructor.
*
* #param other reference to the existing object which is the basis of the new one
*/
public PouchCreature(PouchCreature other) {
this.name=other.name;
this.strength=other.strength;
this.levelUps=other.levelUps;
this.victoriesSinceLevelUp=other.victoriesSinceLevelUp;
}
/**
* Getter for skill level of the PouchCreature, which is based on the
* first character of its name and the number of levelUps it has.
* Specifically, the UNICODE value of the first character in its name
* taken %10 plus the levelUps.
*
* #return skill level of the PouchCreature
*/
public int getLevel() {
int value = (int)((int)(getName().charAt(0)) * 0.1);
return value + this.levelUps;
}
You've said you're supposed to increase the value by 10%. What you're actually doing, though, is reducing it 90% by taking just 10% of it (and then truncating that to an int). 67.0 * 0.1 = 6.7, which when truncated to an int is 6.
Change the 0.1 to 1.1 to increase it by 10%:
int value = (int)((int)(getName().charAt(0)) * 1.1);
// --------------------------------------------^
There, if getName() returns "Centaur" (for instance), the C has the Unicode value 67, and value ends up being 73.
We need to see the code you're calling the class with and that is generating your error message. Why is it expecting 0? 8 seems like a valid return value from the information you've given.

whats is wrong in this basic method Java?

i'm newbe in this programming language (in almost too), i just wanna use a "if and else" with getter and setter methods, i'm doing a basic RPG for learning purpose.
I've a character x, and all i wanna do is; if the life of this character is lower than 0 revive this character,else just increase his life. This is my
code.
public Skill bless(Character x) {
if (0>= x.getCurrentHp()) {
x.getCurrentHp() == x.getHp() * 3/4;
}
else if (x.getCurrentHp() > 0) {
x.getCurrentHp() =+ x.getHp() * 1/2;
}
return x;
}
Well, the issue is in these lines:
x.getCurrentHp() == x.getHp() * 3/4;
x.getCurrentHp() =+ x.getHp() * 1/2;
You have to use methods like:
x.setCurrentHp(x.getHp() * 3/4);
x.setCurrentHp(x.getHp() * 1/2);
Under the hood methods setCurrentHp / getCurrentHp should be like the following:
public class Character {
private int currentHp;
// ... other methods and fields
public void setCurrentHp(int hp){ currentHp = hp; }
public int getCurrentHp(){ return hp; }
}
Because when you call x.getCurrentHp() it simply returns the value. And then you are just assigning x.getHp() * 1/2 to that value.
As a tutorial, you can refer to official examples of getter/setter methods.
Instead of
x.getCurrentHp() == x.getHp() * 3/4
Use
x.setCurrentHp(x.getHp() * 3/4)
Notice I change it to a Set method. Assuming this method has already been created.

Trend analysis using iterative value increments

We have configured iReport to generate the following graph:
The real data points are in blue, the trend line is green. The problems include:
Too many data points for the trend line
Trend line does not follow a Bezier curve (spline)
The source of the problem is with the incrementer class. The incrementer is provided with the data points iteratively. There does not appear to be a way to get the set of data. The code that calculates the trend line looks as follows:
import java.math.BigDecimal;
import net.sf.jasperreports.engine.fill.*;
/**
* Used by an iReport variable to increment its average.
*/
public class MovingAverageIncrementer
implements JRIncrementer {
private BigDecimal average;
private int incr = 0;
/**
* Instantiated by the MovingAverageIncrementerFactory class.
*/
public MovingAverageIncrementer() {
}
/**
* Returns the newly incremented value, which is calculated by averaging
* the previous value from the previous call to this method.
*
* #param jrFillVariable Unused.
* #param object New data point to average.
* #param abstractValueProvider Unused.
* #return The newly incremented value.
*/
public Object increment( JRFillVariable jrFillVariable, Object object,
AbstractValueProvider abstractValueProvider ) {
BigDecimal value = new BigDecimal( ( ( Number )object ).doubleValue() );
// Average every 10 data points
//
if( incr % 10 == 0 ) {
setAverage( ( value.add( getAverage() ).doubleValue() / 2.0 ) );
}
incr++;
return getAverage();
}
/**
* Changes the value that is the moving average.
* #param average The new moving average value.
*/
private void setAverage( BigDecimal average ) {
this.average = average;
}
/**
* Returns the current moving average average.
* #return Value used for plotting on a report.
*/
protected BigDecimal getAverage() {
if( this.average == null ) {
this.average = new BigDecimal( 0 );
}
return this.average;
}
/** Helper method. */
private void setAverage( double d ) {
setAverage( new BigDecimal( d ) );
}
}
How would you create a smoother and more accurate representation of the trend line?
This depends on the behavior of the item you are measuring. Is this something that moves (or changes) in a manner that can be modeled?
If the item is not expected to change, then your trend should be the underlying mean value of the entire sample set, not just the past two measurements. You can get this using Bayes theorem. The running average can be calculated incrementally using the simple formula
Mtn1 = (Mtn * N + x) / (N+1)
where x is the measurement at time t+1, Mtn1 is the mean a time t+1, Mtn is the mean at time t, and N is the number of measurements taken by time t.
If the item you are measuring fluctuates in a manner that can be predicted by some underlying equation, then you can use a Kalman filter to provide a best estimate of the next point based on the previous (recent) measurements and the equation that models the predicted behavior.
As a starting point, the Wikipedia entry on Bayesian estimators and Kalman Filters will be helpful.
Resulting Image
The result is still incomplete, however it clearly shows a better trend line than that in the question.
Calculation
There were two key components missing:
Sliding window. A List of Double values that cannot grow beyond a given size.
Calculation. A variation on the accept answer (one less call to getIterations()):
((value - previousAverage) / (getIterations() + 1)) + previousAverage
Source Code
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import net.sf.jasperreports.engine.fill.AbstractValueProvider;
import net.sf.jasperreports.engine.fill.JRFillVariable;
import net.sf.jasperreports.engine.fill.JRIncrementer;
/**
* Used by an iReport variable to increment its average.
*/
public class RunningAverageIncrementer
implements JRIncrementer {
/** Default number of tallies. */
private static final int DEFAULT_TALLIES = 128;
/** Number of tallies within the sliding window. */
private static final int DEFAULT_SLIDING_WINDOW_SIZE = 30;
/** Stores a sliding window of values. */
private List<Double> values = new ArrayList<Double>( DEFAULT_TALLIES );
/**
* Instantiated by the RunningAverageIncrementerFactory class.
*/
public RunningAverageIncrementer() {
}
/**
* Calculates the average of previously known values.
* #return The average of the list of values returned by getValues().
*/
private double calculateAverage() {
double result = 0.0;
List<Double> values = getValues();
for( Double d: getValues() ) {
result += d.doubleValue();
}
return result / values.size();
}
/**
* Called each time a new value to be averaged is received.
* #param value The new value to include for the average.
*/
private void recordValue( Double value ) {
List<Double> values = getValues();
// Throw out old values that should no longer influence the trend.
//
if( values.size() > getSlidingWindowSize() ) {
values.remove( 0 );
}
this.values.add( value );
}
private List<Double> getValues() {
return values;
}
private int getIterations() {
return getValues().size();
}
/**
* Returns the newly incremented value, which is calculated by averaging
* the previous value from the previous call to this method.
*
* #param jrFillVariable Unused.
* #param tally New data point to average.
* #param abstractValueProvider Unused.
* #return The newly incremented value.
*/
public Object increment( JRFillVariable jrFillVariable, Object tally,
AbstractValueProvider abstractValueProvider ) {
double value = ((Number)tally).doubleValue();
recordValue( value );
double previousAverage = calculateAverage();
double newAverage =
((value - previousAverage) / (getIterations() + 1)) + previousAverage;
return new BigDecimal( newAverage );
}
protected int getSlidingWindowSize() {
return DEFAULT_SLIDING_WINDOW_SIZE;
}
}

Categories