CustomModelParser error when parsing priority statement

I’ve been trying to implement a custom routing profile that will choose primary roads over secondary. So far I have the following code:

   GraphHopper graphHopper() {
        final var graphHopperConfiguration = new GraphHopperConfig();

        graphHopperConfiguration.putObject("datareader.file", "my-path-to-file");
        graphHopperConfiguration.putObject("graph.location", "my-path-to-graph-cache");
        graphHopperConfiguration.putObject("graph.flag_encoders", "car|turn_costs=true");
        graphHopperConfiguration.setProfiles(List.of(getProfile()));

        return new GraphHopper().init(graphHopperConfiguration).importOrLoad();
    }

    private Profile getProfile() {
        final var customModel = new CustomModel();
        customModel.addToPriority(If("road_class == SECONDARY", MULTIPLY, 0.3));
        customModel.setDistanceInfluence(150);

        return new CustomProfile("car_profile")
                .setCustomModel(customModel)
                .setVehicle("car")
                .setTurnCosts(true);
    }

However, when I run my code the following exception is thrown:

Error: Cannot compile expression: Enclosing scope is already set for statement "protected EnumEncodedValue road_class_enc" at File 'source', Line 8, Column 11

After hours of debugging and investigations, I can confirm that the issue lies in CustomModelParser.injectStatements.copyFieldDeclaration().

It seems like the DeepCopier tries to reassign road_class_enc variable that was already declared in the classTemplate string (line 129).

Did anyone encounter this issue? Any help would be very much appreciated

Thank you

Which version are you using?

You can you have a look into RoutingExample and RouteResourceCustomModelTest (for your version e.g. 5.x) to see how you can make this working. Would be still interesting why this fails for you and how we can improve the error message in this case.

Thanks for the quick reply. I’m using version 5.3

I would like to mention that when I use non-custom profiles or if I don’t addToPriority then everything works fine and no exception is thrown. I’ll have a look at RouteResourceCustomModelTest and come back with additional feedback. Cheers

I tried running RoutingExample in my project and unfortunately the exact same error occurs. Here’re some debug values:

classTemplate generated by CustomModelParser.createClassTemplate():

package com.graphhopper.routing.weighting.custom;import com.graphhopper.routing.weighting.custom.CustomWeightingHelper;
import com.graphhopper.routing.ev.EncodedValueLookup;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.routing.ev.*;
import java.util.Map;

public class JaninoCustomWeightingHelperSubclass2 extends CustomWeightingHelper {
    protected EnumEncodedValue road_class_enc;
    @Override
    public void init(EncodedValueLookup lookup, com.graphhopper.routing.ev.DecimalEncodedValue avgSpeedEnc, com.graphhopper.routing.ev.DecimalEncodedValue priorityEnc, Map<String, com.graphhopper.util.JsonFeature> areas) {
        this.avg_speed_enc = avgSpeedEnc;
        this.priority_enc = priorityEnc;
        if (lookup.hasEncodedValue("road_class")) this.road_class_enc = (EnumEncodedValue) lookup.getEncodedValue("road_class", EncodedValue.class);
    }

    @Override
    public double getPriority(EdgeIteratorState edge, boolean reverse) {
        return 1; //will be overwritten by code injected in DeepCopier
    }
    @Override
    public double getSpeed(EdgeIteratorState edge, boolean reverse) {
        return getRawSpeed(edge, reverse); //will be overwritten by code injected in DeepCopier
    }
}

In CustomModelParser.injectStatements (place that throws the exception)

 public FieldDeclaration copyFieldDeclaration(FieldDeclaration subject) throws CompileException {
   FieldDeclaration fd = super.copyFieldDeclaration(subject);
   fd.setEnclosingScope(subject.getEnclosingScope());
   return fd;
 }

The value of subject in this method is protected EnumEncodedValue road_class_env. I assume .setEnclosingScope is the line that throws the exception.

I did investigate further this issue and found out that Java.Statement.setEnclosingScope from janino module is indeed called twice with the same value: JaninoCustomWeightingHelperSubclass2

The first time it is called immediately after executing fd.setEnclosingScope (line 350 in CustomModelParser) and the second time it’s probably executed internally by janino library (although I’m not sure) which causes this exception to be thrown.

There is no such issue in the release (otherwise many tests would fail). You can of course provide us with a reproducer unit test that fails with this exception and I’ll have a look.

(btw: please make sure that you disable JANINO_DEBUG and remove all generated Java classes from the project before you compile it)

I’ve finally found the issue.

In my project I use spring-boot which by default sets janino version to 3.1.7 (which is incompatible with graphhopper).

Solution: in my pom.xml file I’ve explicitly set janino version to 3.1.2 and it worked like a charm.

Hope this helps anyone in the future

1 Like

It is a bit strange that 3.1.7 is used there as it was not yet released according to their list: Releases · janino-compiler/janino · GitHub (and yes, in order to update to this version we’ll have to remove the workaround)

2 Likes
Powered by Discourse