4

There are many other topics about how to override an existing renderer in JSF. All of them lead to the same entry in faces-config. You "only" need a component-family, renderer-type, render-kit-id and your implementenation in renderer-class

I understand and can reproduce all of these examples, but in my specific case I would like to override the behavior of <ui:include> and <ui:insert>. Sadly I cannot find out, which values I have to put in the attributes mentioned above, because the file ui:taglib.xml is not as verbose as the other taglibs.

Do you have any idea, how to override them?

Edit: BalusC mentioned another topic, it uses custom tags like <my:include> or <my:insert>, but these I don't want to use. If I did so, I had to refactor all of my existing code, and my future code would not be working without using my custom taglib.

I would like to hook before the rendering of <ui:include> and <ui:insert> so I can reuse the plugin I'm about to develop without changing any of the existing code.

Community
  • 1
  • 1
Rokko_11
  • 837
  • 2
  • 10
  • 24
  • *"... without changing any of the existing code"*: You can't. At least, not without being tight coupled to a specific implementation (Mojarra or MyFaces). If this is not a problem, then tell which implementation you'd like to have the answer for. Otherwise the answerer has to take all implementation specific solutions into account which goes a bit overboard. The alternative would be to bite the bullet and replace all `` by `` (but you already knew that). – BalusC Oct 26 '15 at 14:29
  • Hi BalusC. That's good to know, that a general solution is impossible, so I can stop searching for it. I found [this issue](http://stackoverflow.com/questions/30368750/customize-uiinclude-rendering-to-add-prefix-postfix), and I try to do something like the same he tries to. But his answer also did not got answered satisfactory. When I use a JSF event (PreRenderView or preRenderComponent), the xhtml-files already got included - so I loose this information. – Rokko_11 Oct 26 '15 at 16:33
  • Reasonable requirement. This can be done, but the solution is unfortunately going to be implementation specific and you can only tie it to the webapp (and thus not make it part of a library). So if you tell which implementation you're using, then an answer can be baked for that. – BalusC Oct 26 '15 at 17:56
  • Thank you! I'm using Mojarra 2.1.19. Today I also tried to customize ui:debug - did not work neigher. I'm very shocked that the JSF specification does not provide enough hooks to hook into :( – Rokko_11 Oct 27 '15 at 14:41

1 Answers1

4

... without changing any of the existing code

You can't. At least, not without being tight coupled to a specific implementation (Mojarra or MyFaces). The alternative would be to bite the bullet and replace all <ui:include> and <ui:define> by <my:include> and <my:define>. Facelets is not fully abstracted/standardized into JSF specification yet. There's only a Facelet cache factory, but no Facelet context factory, otherwise it'd have been easy. Generally, forget about customizing <ui:xxx> tags in an abstract way. You'd need to hack the implementation.

Given that you're using Mojarra 2.1.19, you'd need to copypaste its com.sun.faces.facelets.impl.DefaultFaceletContext class into your web application project, maintaining its com.sun.faces.facelets.impl package. Classes in WAR have higher classloading precedence than those in WAR's /WEB-INF/lib and server's /lib. So this one in your WAR would be used instead.

Given that you'd like to achieve the same as asked in Customize ui:include rendering to add prefix/postfix on <ui:include> and <ui:define>:

Example, supposing a blank file.xhtml:

Input

<ui:include src="file.xhtml" />

Output

<!-- START file.xhtml -->
<!-- END file.xhtml -->

Here's how you can achieve it by editing the copypasted DefaultFaceletContext class:

  1. Add a helper method to the class which creates a comment component (which is just a plain output text component).

    private static UIComponent createComment(String comment) {
        UIOutput text = new UIOutput();
        text.setValue("\n<!-- " + comment + " -->\n");
        return text;
    }
    
  2. Extend the oneliner method includeFacelet(UIComponent, String) at line 199 as below:

    parent.getChildren().add(createComment("START INCLUDE " + relativePath));
    facelet.include(this, parent, relativePath);
    parent.getChildren().add(createComment("END INCLUDE " + relativePath));
    
  3. Extend the includeDefinition(UIComponent, String) around line 366 with client.apply(this, parent, name) as below:

    int start = parent.getChildCount();
    found = client.apply(this, parent, name);
    
    if (found) {
        parent.getChildren().add(start, createComment("START DEFINE " + name));
        parent.getChildren().add(createComment("END DEFINE " + name));
    }
    

During testing I however found a caveat. I have my HTML <title> templated like below:

<h:head>
    <title><ui:insert name="title">#{view.viewId}</ui:insert></title>
</h:head>

Comments thus also end up inside <title> element. Unfortunately comments in a HTML title is invalid syntax (only PCDATA allowed) and they are interpreted literally and thus show up in the document title. You might want to create a blacklist based on name of the definition or perhaps the parent.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 2
    Hi fritjof, redirecting to some include-Urls is not the point. The reason of doing all of this is to wrap all `includes` and `inserts` into a `
    ` (of course only in debug-mode). So I can border these divs to see the templating-structure of the web page AND (this is the biggest point) I can use IntelliJ's "Remote call"-Plugin to open the xhtml-fragments directly out of the web-browser. Doesn't this sound insane?
    – Rokko_11 Oct 29 '15 at 13:44