2

I have an implementation where JasperViewer is used to preview and save reports. The first saving format offered by the viewer is .jrprint and I'm trying to change that to pdf. I'm employing the following method to help me with that

private static void promotePdfSaveFormat(JasperViewer jasperViewer) {
    /* get via reflection (with jOOR) the parameter JasperViewer object's member viewer (of type JRViewer) */
    JRViewer viewer = on(jasperViewer).call("viewer").get();
    /* get via reflection JRViewer object's member tlbToolBar (of type JRViewerToolbar) */
    JRViewerToolbar tlbToolBar = on(viewer).call("tlbToolBar").get();
    /* call tlbToolBar's getSaveContributors() */
    Arrays.stream(tlbToolBar.getSaveContributors()).forEach(
        e -> {
            /* Find the one that is of type JRPdfSaveContributor (using instanceof) */
            if (e instanceof JRPdfSaveContributor){
                /* set JRViewerToolbar object member lastSaveContributor value to the instance of JRPdfSaveContributor */
                on(tlbToolBar).call("lastSaveContributor").get();
            }
        }
    );
}

Everytime a new Object is created inside the class like JasperViewer viewer = new JasperViewer(...), I'm adding promotePdfSaveFormat(viewer) immediately after it. When I run the app, I get an error at JRViewer viewer = on(jasperViewer).call("viewer").get();

The error is as follows

org.joor.ReflectException: java.lang.NoSuchMethodException: No similar method viewer with params [] could be found on type class net.sf.jasperreports.view.JasperViewer.
Alex K
  • 22,315
  • 19
  • 108
  • 236
Roshan Upreti
  • 1,782
  • 3
  • 21
  • 34
  • 1
    You can override `JRViewerToolbar` class and set `JRSaveContributor lastSaveContributor` field with`net.sf.jasperreports.view.save.JRPdfSaveContributor` value at constructor, for example – Alex K Mar 01 '19 at 20:47

3 Answers3

2

They are called JRSaveContributor.

You can set your own JRViewer.setSaveContributors or an easy way to reorder (remove) is to get the ones that are instanced and then loop them checking the description to see what you like to keep/reorder and then set them back to the JasperViewer.

Quick Example, setting pdf first.

final JRViewer jrv = new JRViewer(report);
JRSaveContributor[] sv = jrv.getSaveContributors();
List<JRSaveContributor> reorded = new ArrayList<JRSaveContributor>();
for (JRSaveContributor s : sv) {
    if (s.getDescription().contains(".pdf")){
        reorded.add(0,s);
    }else{
        reorded.add(s);
    }
}
jrv.setSaveContributors((JRSaveContributor[]) reorded.toArray(new JRSaveContributor[reorded.size()]));

You may also just like to skip .jrprint? if you are using java8 this code can be improved using streams, I leave that exercise to reader.

Result

enter image description here

Petter Friberg
  • 21,252
  • 9
  • 60
  • 109
  • 1
    Thank you for the explanation, but in my case, `getSaveContributors()` is inside `JRViewerToolbar` and not `JRViewer`. I also need to set it to `pdf` during runtime. I'm still getting `NoSuchMethodException`. – Roshan Upreti Mar 03 '19 at 08:39
0

After a thorough inspection, I realized that I was making a really stupid mistake. It was bound to throw NoSuchMethodException because the following lines are trying to access a method and not a field.

JRViewer viewer = on(jasperViewer).call("viewer").get();
JRViewerToolbar tlbToolBar = on(viewer).call("tlbToolBar").get();

Then, I modified my method, to access field. The entire method looks like this now:

private static void promotePdfSaveFormat(JasperViewer jasperViewer) {
    /* get via reflection the parameter JasperViewer object's member viewer (of type JRViewer) */
    JRViewer viewer = on(jasperViewer).field("viewer").get();
    /* get via reflection JRViewer object's member tlbToolBar (of type JRViewerToolbar) */
    JRViewerToolbar tlbToolBar = on(viewer).field("tlbToolBar").get();
    /* call tlbToolBar's getSaveContributors() */
    Arrays.stream(tlbToolBar.getSaveContributors())
        /* filter instance of type JRPdfSaveContributor */
        .filter(e -> e instanceof JRPdfSaveContributor)
        /* set JRViewerToolbar object member lastSaveContributor value to the instance of JRPdfSaveContributor */
        .forEach(e -> on(tlbToolBar).set("lastSaveContributor", e));
}

Now, JasperViewer shows PDF as the first saving option. enter image description here

Roshan Upreti
  • 1,782
  • 3
  • 21
  • 34
0

Here is a solution that does not require the reflection to access private/protected fields. It is obviously derived from both @PetterFriberg and @RoshanUpreti's solutions. It involves subclassing JRViewer.

public static final class PdfFirstJRViewer
    extends JRViewer
{
    private static final long serialVersionUID = 1L;

    public PdfFirstJRViewer(JasperPrint print)
    {
        super(print);

        movePdfToTop();
    }

    public void movePdfToTop()
    {
        JRSaveContributor[] sv = tlbToolBar.getSaveContributors();
        List<JRSaveContributor> reorded = new ArrayList<>();
        for (JRSaveContributor s : sv)
        {
            if (s.getDescription().contains(".pdf"))
            {
                reorded.add(0, s);
            }
            else
            {
                reorded.add(s);
            }
        }
        tlbToolBar.setSaveContributors(
            reorded.toArray(new JRSaveContributor[reorded.size()]));
    }
}

You may need to override additional constructors to get the behavior you want.

Pixelstix
  • 702
  • 6
  • 21