You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@tiles.apache.org by "Marc Ewert (JIRA)" <ji...@apache.org> on 2013/05/27 08:39:20 UTC

[jira] [Created] (TILES-569) Proposal for conditionals in tiles definitions

Marc Ewert created TILES-569:
--------------------------------

             Summary: Proposal for conditionals in tiles definitions
                 Key: TILES-569
                 URL: https://issues.apache.org/jira/browse/TILES-569
             Project: Tiles
          Issue Type: Improvement
          Components: tiles-core
    Affects Versions: 3.0.1
            Reporter: Marc Ewert


Hi,

I've recently tried the OptionsRenderer suggested in this tutorial [http://tech.finn.no/2012/07/25/the-ultimate-view-tiles-3/4/], but wasn't able to succeed. The defined attributes wasn't available for some reason.

So I took the idea and implemented my own solution. Perhaps it's something for the trunk? My syntax is as follows:

{code}
<tiles-definitions>

	<definition name="main/*/*" template="/WEB-INF/tiles/_layout/main.jsp">
	    <put-attribute name="title" value="/WEB-INF/tiles/[{1}/{2}|{1}|_common]/title.jsp"/>
	    <put-attribute name="navigation" value="/WEB-INF/tiles/[{1}/{2}|{1}|_common]/navigation.jsp"/>
	    <put-attribute name="content" value="/WEB-INF/tiles/[{1}/{2}|{1}|_common]/content.jsp"/>
	    <put-attribute name="footer" value="/WEB-INF/tiles/[{1}/{2}|{1}|_common]/footer.jsp"/>
	    <put-attribute name="javaScript" value="/WEB-INF/tiles/[{1}/{2}|{1}|_common]/javaScript.jsp"/>
	</definition>

</tiles-definitions>
{code}

The renderer searches for patterns like [opt_1|opt_2|...|opt_n] which may occur several times in the value partameters. With a backtrack algorithm the first matching valid path is searched and rendered.

I like the compactness of the notation, no need for attribute lists, which didn't function in my case.

Here is my renderer implementation, perhaps you are interested in integrating it. The path evaluation eventually has to be refactored. I don't know if there is a more elegant way and how costly the current solution is. Perhaps there has to be done some caching...

{code}
/**
 * Renderer implementation supporting the notation "[CHOICE_1|...|CHOICE_n]" in the paths.
 * The first matching path replacement will be choosen. 
 */
public final class ChoiceRenderer implements Renderer {

    private static final Pattern CHOICE_PATTERN = Pattern.compile("\\[([^\\]]+)\\]");

    private final Renderer renderer;

    /**
     * Creates a new instance wrapping the given renderer.
     */
    public ChoiceRenderer(Renderer renderer) {
        this.renderer = renderer;
    }

    /**
     * @see org.apache.tiles.request.render.Renderer#isRenderable(java.lang.String, org.apache.tiles.request.Request)
     */
    @Override
    public boolean isRenderable(String path, Request request) {
        // only the overall format is checked, so no extra handling here
        return this.renderer.isRenderable(path, request);
    }

    /**
     * @see org.apache.tiles.request.render.Renderer#render(java.lang.String, org.apache.tiles.request.Request)
     */
    @Override
    public void render(String path, Request request) throws IOException {
        Matcher matcher = CHOICE_PATTERN.matcher(path);
        List<String[]> groups = new ArrayList<String[]>();
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            // adds a pattern to the resulting path, which will be replaced by the
            // available choices
            matcher.appendReplacement(sb, "{[" + groups.size() + "]}");
            groups.add(matcher.group(1).split("\\|"));
        }
        matcher.appendTail(sb);
        if (groups.isEmpty()) {
            this.renderer.render(path, request);
        } else {
            backtrackPaths(sb.toString(), request, groups, 0);
        }
    }

    private String backtrackPaths(String pathPattern, Request request, List<String[]> groups, int depth)
            throws IOException {
        String matchPath = null;
        String[] parts = groups.get(depth);
        for (int i = 0; i < parts.length; ++i) {
            String path = pathPattern.replace("{[" + depth + "]}", parts[i]);
            if (depth == groups.size() - 1) {
                if (isPathValid(path, request)) {
                    // found the first matching choice
                    this.renderer.render(path, request);
                    matchPath = path;
                    break;
                }
            } else {
                matchPath = backtrackPaths(path, request, groups, depth + 1);
            }
        }
        return matchPath;
    }

    // TODO should we use caching here?
    private boolean isPathValid(String path, Request request) {
        boolean rtn = false;
        // apparently the corresponding Renderer method seems to check the
        // path's format only
        if (this.renderer.isRenderable(path, request)) {
            try {
                rtn = request.getApplicationContext().getResource(path) != null;
            } catch (IllegalArgumentException e) {
                // TODO the javadoc states that null will be returned, but
                // instead of it an exception is thrown.
                // Seems to be a bug?!
                boolean throwException = true;
                if (e.getCause() instanceof FileNotFoundException) {
                    FileNotFoundException fex = (FileNotFoundException) e.getCause();
                    throwException = fex.getMessage().indexOf(path) == -1;
                }
                if (throwException) {
                    // seems to be a different reason as our searched path
                    throw e;
                }
            }
        }
        return rtn;
    }
}
{code}

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira