Skip to content

On reflection and type inference in Java

It happens. Sometimes you just have to write ugly code.

As an example, you may find out that the private field you really need in your web framework (yes Richfaces, I’m talking to you) is really unaccessible.

There are tons of examples on the internet on how to access private fields, just set the setAccessible property of the Field class to true and then call get.

Since I have to extrapolate data from two or three classes, I decided to write a simple helper function to do it.

import java.lang.reflect.Field;

public class ReflectionUtils {
    @SuppressWarnings("unchecked")
    public static <T> T getPrivateField(final Class clazzFrom, final String fieldName, final Object objFrom) throws NoSuchFieldException, IllegalAccessException {
        final Field tag = clazzFrom.getDeclaredField(fieldName);
        tag.setAccessible(true);
        return (T) tag.get(objFrom);
    }
}

What is the cool (and different from the others) thing about this method? It is in the type signature

static <T> T getPrivateField.

Java simply infers the return value type from the type of the variable, So that you can use it like this:

import static ReflectionUtils.getPrivateField;

final ValueExpressionImpl orig = getPrivateField(TagValueExpression.class, "orig", expression);

Sometimes even ugly code can become pretty!

Using BigDecimals in JSF

One funny thing I had to struggle with is using BigDecimal with Seam and JSF.
As you probably know if you are reading this, you shouldn’t really use double if you care about operations on decimal numbers.
Even the simplest operation like

  • 2.35 – 1.75 = 0.6000000000000001

Can give you this kind of results, which sometimes (and that was my case) it’s not the right thing.
Plus, you probably want to define the scale of the numbers you are dealing with. In my case, all the operations were to be made using at last three decimals, and all the rounding was to be made by flooring:

  • 4.0001 should become 4

I want the BigDecimal numbers to be shown always with three decimals after the dot, even if there wasn’t any of it, and since I live in an european country, I have to use the comma (,) instead of (.) as a separator.

  • 1 => 1,000
  • 1.4 => 1,500

So many things… unfortunately JSF doesn’t give you any help on it:

  1. The f:convertNumber tag doesn’t work on BigDecimal
  2. The toString() method of the bigDecimal ignores the locale.

After struggling with built-in method, I decided to go all by myself.

First of all, we have to understand that we have two different problems: a data type problem and a formatting problem.

The former means that we have to be sure that each time the database gives us the number we have the right scale set.
There are different ways to set the scale on the BigDecimal, one of that is to use the constructor

new BigDecimal(double val, MathContext ctx) 

and

new MathContext(int setPrecision, RoundingMode setRoundingMode)

But since I’m using JPA, there is now way I can tell it to use a special constructor. I relied on a simple hack

	@Column
	public void getQuantity() {
        return this.quantity;
    }

    public void setQuantity(BigDecimal q) {
        this.quantity = q.setScale(NumberUtils.RE_SCALE);
    }

This assures that JPA will use the getter and setter to populate the bean, and when the setter will be called, the quantity will have the right scale.

As for the operation on BigDecimals, this paragraph of the JDK documentation is interesting:

Preferred Scales for Results of Arithmetic Operations
Operation Preferred Scale of Result
Add max(addend.scale(), augend.scale())
Subtract max(minuend.scale(), subtrahend.scale())
Multiply multiplier.scale() + multiplicand.scale()
Divide dividend.scale() – divisor.scale()

So a simple sum method:


    public BigDecimal getTotal() {
        BigDecimal i = BigDecimal.ZERO;
        for(BigDecimal c : getQuantities()) {
            i = i.add(c);
        }
        return i;
    }
	

Will have the right scale, since all the quantities are set with a scale of 3.

As for the latter, the formatting problem, I ended up writing a BigDecimal converter:


@Name("bigDecimalConverter")
@BypassInterceptors
@Converter(forClass = BigDecimal.class)
public class BigDecimalConverter implements javax.faces.convert.Converter {
    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String s) {
        try {
            return NumberUtils.parseBigDecimal(s);
        } catch (ParseException e) {
            final String msg = Messages.instance().get("javax.faces.converter.BigDecimalConverter.STRING");
            throw new ConverterException(new FacesMessage(msg));
        }
    }
    
    @Override
    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object o) {
        return new DecimalFormat(NumberUtils.BIG_DECIMAL_FORMAT).format(o);
    }
}

Note that, since I’m using Seam, I can create a converter simply adding the

@BypassInterceptor
@Converter(forClass = BigDecimal.class)

annotations and the JSF converter will be called each time you use a h:outputText with a BigDecimal value.

We need a parse method too:

public class NumberUtils {
    public static final String BIG_DECIMAL_FORMAT = "0.000";
    public static final DecimalFormat format = new DecimalFormat(BIG_DECIMAL_FORMAT);
    public static final int RE_SCALE = 3;

    static {
        format.setParseBigDecimal(true);
    }

    public static BigDecimal parseBigDecimal(String s) throws ParseException {
        final int i = s.indexOf(".");
        if (i != -1) {
            throw new ParseException(s, i);
        }
        final BigDecimal bigDecimal = (BigDecimal) NumberUtils.format.parse(s);
        if (bigDecimal.scale() &gt; RE_SCALE) {
            throw new ParseException(s, i);
        }
        return bigDecimal.setScale(RE_SCALE, RoundingMode.DOWN);
    }
}

So that I can use the string format I want, in this case 0.000 that means: always show three digits after the dot. The formatter will use the right separator according to the locale, in my case the comma (,).
Just remember to put the right locale into the faces-config.xml:

<faces-config>
    <application>
        <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
        <locale-config>
            <default-locale>it</default-locale>
            <supported-locale>it</supported-locale>
        </locale-config>
    </application>

My parse method is a little rude. It won’t let you write any number with more than three decimal digits or using a dot.
Note that you have to wrap your ParseException into a ConverterException and write a FacesMessage if you want the error message to be shown next to the field. It will magically work!

That’s all, bye!

PDFs aren’t that fun

<p:table columns="7" headerRows="1" widths="2 4 1 3 3 2 2" >

My first experience with the Seam PDF library was not that great.

First of all, when I wrote a bad tag (a <p:section> without a <p:chapter>) I had a NPE, withouth any information on where it happened.

Then, I failed to write the number of the footer rows, and I got an ArrayOutOfBoundException.

In the end, I had to copy and paste my <p:font> tags because I haven’t found any trace of style abstractions. I miss the Seam Excel library…

%d bloggers like this: