All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.velocity.tools.generic.MarkupTool Maven / Gradle / Ivy

Go to download

Generic tools that can be used in any context. PLEASE NOTE: this is a temporary fork to unblock projects migrating to Jakarta, but I won't continue maintaining it in the future as the Velocity team doesn't understand the value of Jakarta. I strongly suggest you plan a switch to a more modern template engine such as Thymeleaf.

The newest version!
package org.apache.velocity.tools.generic;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.velocity.tools.config.DefaultKey;

/**
 * NOTE: This tools is considered "alpha" quality due to lack of testing
 * and a generally unpolished API.  Feel free to use but expect changes.
 * Also, this is not automatically provided via the default tools.xml file.
 * 
 *
 * 

* A tool to make it easy to generate XML or HTML on the fly. It uses a CSS-type * syntax with a vaguely jQuery-ish API to help you generate the markup you need.

*
 * Example uses in a template:
 *   #set( $foospan = $markup.span.id($foo.id).body($foo) )
 *   $markup.tag('table tr.bar td').body("This is $foospan")
 *
 * Output:
 *   <table>
 *     <tr class="bar">
 *       <td>This is <span id="foo1">my first foo.</span></td>
 *     </tr>
 *   </table>
 *
 *
 * Example tools.xml config:
 * <tools>
 *   <toolbox scope="application">
 *     <tool class="org.apache.velocity.tools.generic.alpha.MarkupTool"/>
 *   </toolbox>
 * </tools>
 * 
* * @author Nathan Bubna * @since VelocityTools 2.0 * @version $Id$ */ @DefaultKey("mark") public class MarkupTool extends SafeConfig implements Serializable { private static final long serialVersionUID = -777597069616274442L; public static final String DEFAULT_TAB = " "; public static final String DEFAULT_DELIMITER = " "; private String tab = DEFAULT_TAB; private String delim = DEFAULT_DELIMITER; /** * Configuration * @param tab tab string */ public void setTab(String tab) { if (isConfigLocked()) { getLog().error("setTab() failure: configuration is locked"); } else { this.tab = tab; } } public String getTab() { return this.tab; } public Tag get(String tag) { return tag(tag); } public Tag tag(String definition) { String[] tags = split(definition); Tag last = null; for (int i=0; i < tags.length; i++) { Tag tag = parse(tags[i]); if (last != null) { last.append(tag); } last = tag; } return last; } protected String[] split(String me) { //TODO: fix escaped delimiters return me.split(delim); } private static enum Mode { NAME, ID, CLASS, ATTR } protected Tag parse(String definition) { StringBuilder store = new StringBuilder(); Tag tag = new Tag(this); Mode mode = Mode.NAME; for (int i=0; i < definition.length(); i++) { char c = definition.charAt(i); if (c == '#') { store = clear(mode, tag, store, true); mode = Mode.ID; } else if (c == '.') { store = clear(mode, tag, store, true); mode = Mode.CLASS; } else if (c == '[') { store = clear(mode, tag, store, true); mode = Mode.ATTR; } else if (c == ']') { store = clear(mode, tag, store, true); mode = Mode.NAME; } else { store.append(c); } } clear(mode, tag, store, false); return tag; } private StringBuilder clear(Mode mode, Tag tag, StringBuilder val, boolean emptyStore) { if (val.length() > 0) { String s = val.toString(); switch (mode) { case NAME: tag.name(s); break; case ID: tag.id(s); break; case CLASS: tag.addClass(s); break; case ATTR: if (s.indexOf('=') > 0) { String[] kv = s.split("="); tag.attr(kv[0], kv[1]); } else { tag.attr(s, null); } break; } if (emptyStore) { return new StringBuilder(); } return val; } else { // already is clean return val; } } public static class Tag { private MarkupTool tool; private Tag parent; private Object name; private Object id; private List classes; private Map attributes; private List children; public Tag(MarkupTool tool) { this.tool = tool; } public Tag name(Object name) { this.name = name; return this; } public Tag id(Object id) { this.id = id; return this; } public Tag addClass(Object c) { if (c == null) { return null; } if (classes == null) { classes = new ArrayList(); } classes.add(c); return this; } public Tag attr(Object k, Object v) { if (k == null) { return null; } if (attributes == null) { attributes = new HashMap(); } attributes.put(k, v); return this; } public Tag body(Object o) { if (children == null) { children = new ArrayList(); } else { children.clear(); } children.add(o); return this; } public Tag append(Object o) { if (children == null) { children = new ArrayList(); } children.add(o); if (o instanceof Tag) { ((Tag)o).parent(this); } return this; } public Tag prepend(Object o) { if (children == null) { children = new ArrayList(); children.add(o); } else { children.add(0, o); } if (o instanceof Tag) { ((Tag)o).parent(this); } return this; } public Tag wrap(String tag) { // make new tag Tag prnt = tool.tag(tag); // give root of new tag our parent prnt.root().parent(parent()); // make new tag our parent parent(prnt); return this; } public Tag orphan() { return parent(null); } public Tag parent(Tag parent) { this.parent = parent; return this; } public Tag parent() { return this.parent; } public Tag root() { if (isOrphan()) { return this; } return this.parent.root(); } public List children() { return children; } public boolean isOrphan() { return (parent == null); } public boolean isEmpty() { return (children == null || children().isEmpty()); } public boolean matches(Tag tag) { if (missed(name, tag.name) || missed(id, tag.id) || missed(classes, tag.classes)) { return false; } //TODO: match attributes return true; } protected boolean missed(Object target, Object arrow) { // no arrow, no miss if (arrow == null) { return false; } // otherwise, the arrow must hit the target return !arrow.equals(target); } protected boolean missed(List targets, List arrows) { // no arrows, no miss if (arrows == null) { return false; } // no targets, always miss if (targets == null) { return true; } for (Object o : arrows) { if (!targets.contains(o)) { return true; } } return false; } /************* rendering methods **************/ /** * Render tag * @param indent indentation string * @param s string builder */ protected void render(String indent, StringBuilder s) { if (render_start(indent, s)) { render_body(indent, s); render_end(indent, s); } } /** * Start tag rendering * @param indent indentation string * @param s string builder * @return whether a closing tag is needed */ protected boolean render_start(String indent, StringBuilder s) { if (indent != null) { s.append(indent); } s.append('<'); render_name(s); render_id(s); render_classes(s); render_attributes(s); if (isEmpty()) { s.append("/>"); return false; } else { s.append('>'); return true; } } /** * Render tag name * @param s string builder */ protected void render_name(StringBuilder s) { s.append(name == null ? "div" : name); } /** * render tag id * @param s string builder */ protected void render_id(StringBuilder s) { if (id != null) { s.append(" id=\"").append(id).append('"'); } } /** * render tag classes * @param s string builder */ protected void render_classes(StringBuilder s) { if (classes != null) { s.append(" class=\""); for (int i=0; i < classes.size(); i++) { s.append(classes.get(i)); if (i + 1 != classes.size()) { s.append(' '); } } s.append('"'); } } /** * Render tag attributes * @param s string builder */ protected void render_attributes(StringBuilder s) { if (attributes != null) { for (Map.Entry entry : attributes.entrySet()) { s.append(' ').append(entry.getKey()).append("=\""); if (entry.getValue() != null) { s.append(entry.getValue()); } s.append('"'); } } } /** * Render tag body * @param indent indentation string * @param s string builder */ protected void render_body(String indent, StringBuilder s) { String kidIndent = indent + tool.getTab(); for (Object o : children) { if (o instanceof Tag) { ((Tag)o).render(kidIndent, s); } else { s.append(kidIndent); s.append(o); } } } /** * Render tag end * @param indent indentation string * @param s string builder */ protected void render_end(String indent, StringBuilder s) { if (indent != null) { s.append(indent); } s.append("'); } /** * @return string representation */ public String toString() { StringBuilder s = new StringBuilder(); root().render("\n", s); return s.toString(); } } }