Skip to content

XbaseInterpreter.java:_doEvaluate(XCastedExpression - throw new EvaluationException(new ClassCastException(typeName)) - not enough information to show to the user what cannot be casted to what #3595

@dilyanpalauzov

Description

@dilyanpalauzov
class A {
    public static void main(String[] arg) {
        try {
            Object x = Integer.valueOf(0);
            System.out.println("A" + (String)x);
        } catch (ClassCastException e) {
            System.out.println(e.getMessage());
        }
    }
}

produces with Java 21

$ java A.java
class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')

That is: ClassCastException.getMessage() is expected to spell both source and target classes of casting.

When XbaseInterpreter.java:protected Object _doEvaluate(XCastedExpression castedExpression, IEvaluationContext, CancelIndicator) creates a ClassCastException, it puts as detailed message the typeName - the target type.

To print a casting error to the user, containing both source and target classes of casting, one has in a derived class to:

override Object _doEvaluate(XCastedExpression castedExpression, IEvaluationContext context, CancelIndicator indicator) {
    try {
        return super._doEvaluate(castedExpression, context, indicator)
    } catch (RuntimeException e) {
        if (e.cause instanceof ClassCastException) {

and then constructs a message, containing both source and target types of the casting. Obtaining the target type is easy, it is a matter of calling castedExpression.getType().getType().getQualifiedName(). In fact this is the value of the parameter passed to the constructor of ClassCastException in XbaseInterpreter.java:_doEvaluate(XCastedExpression, IEvaluationContext, CancelIndicator).

However there is no way to know, when the EvaluationException(new ClassCastException()) is catched, what was the source type of the expression: In a as B the class of a.

In openHAB to get the source type is re-executed val Object result = internalEvaluate(castedExpression.getTarget(), context, indicator);. The problem is that calling once val Object result = internalEvaluate(castedExpression.getTarget(), context, indicator); in the super class, and calling again val Object result = internalEvaluate(castedExpression.getTarget(), context, indicator); in the derived class might not necessary have the same result: states might have changed in the meantime, so that re-evaluating the same expression, for the sake of logging an error, might produce no more errors. This in fact does happen, as described at openhab/openhab-core#5323 .

That said in

protected Object _doEvaluate(XCastedExpression castedExpression, IEvaluationContext context, CancelIndic
ator indicator) {
…
                try {
                        expectedType.cast(result);
                } catch (ClassCastException e) {
                        throw new EvaluationException(new ClassCastException(typeName));
                }
  • spell in the ClassCastException constructor parameter as sentence the target and source types of the casting, as java does in the example of the beginning, or
  • instead of passing the target class name (which can be implied when overriding the method by calling castedExpression.getType().getType().getQualifiedName() , pass the name of the source class.

In both cases the aim is to present to the user a message telling what cannot be casted to what, and currently this is not possible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions