Repository: incubator-freemarker Updated Branches: refs/heads/3 efa000c9c -> 421dc2a93
FREEMARKER-55: skeletal design Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/150ad7f6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/150ad7f6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/150ad7f6 Branch: refs/heads/3 Commit: 150ad7f6039446e2d0e3ee66010abe5be920d8d1 Parents: efa000c Author: Woonsan Ko <woon...@apache.org> Authored: Fri Dec 22 23:21:37 2017 -0500 Committer: Woonsan Ko <woon...@apache.org> Committed: Fri Dec 22 23:21:37 2017 -0500 ---------------------------------------------------------------------- .../AbstractSpringTemplateDirectiveModel.java | 2 +- ...aBoundFormElementTemplateDirectiveModel.java | 16 +++ .../AbstractFormTemplateDirectiveModel.java | 17 +++ ...stractHtmlElementTemplateDirectiveModel.java | 139 +++++++++++++++++++ ...tHtmlInputElementTemplateDirectiveModel.java | 13 ++ .../model/form/InputTemplateDirectiveModel.java | 86 ++++++++++++ 6 files changed, 272 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/150ad7f6/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java index c91b387..4366841 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java @@ -36,7 +36,7 @@ import org.springframework.web.servlet.support.RequestContext; /** * Abstract TemplateDirectiveModel for derived classes to support Spring MVC based templating environment. */ -abstract class AbstractSpringTemplateDirectiveModel extends AbstractSpringTemplateCallableModel +public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSpringTemplateCallableModel implements TemplateDirectiveModel { /** http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/150ad7f6/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java new file mode 100644 index 0000000..f9afb84 --- /dev/null +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java @@ -0,0 +1,16 @@ +package org.apache.freemarker.spring.model.form; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Corresponds to <code>org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag</code>. + */ +public abstract class AbstractDataBoundFormElementTemplateDirectiveModel extends AbstractFormTemplateDirectiveModel { + + protected AbstractDataBoundFormElementTemplateDirectiveModel(HttpServletRequest request, + HttpServletResponse response) { + super(request, response); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/150ad7f6/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractFormTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractFormTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractFormTemplateDirectiveModel.java new file mode 100644 index 0000000..84ca87d --- /dev/null +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractFormTemplateDirectiveModel.java @@ -0,0 +1,17 @@ +package org.apache.freemarker.spring.model.form; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.freemarker.spring.model.AbstractSpringTemplateDirectiveModel; + +/** + * Corresponds to <code>org.springframework.web.servlet.tags.form.AbstractFormTag</code>. + */ +public abstract class AbstractFormTemplateDirectiveModel extends AbstractSpringTemplateDirectiveModel { + + protected AbstractFormTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) { + super(request, response); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/150ad7f6/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java new file mode 100644 index 0000000..a0766f8 --- /dev/null +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java @@ -0,0 +1,139 @@ +package org.apache.freemarker.spring.model.form; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.TemplateBooleanModel; +import org.apache.freemarker.core.model.TemplateHashModelEx; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateNumberModel; +import org.apache.freemarker.core.model.TemplateStringModel; +import org.apache.freemarker.core.util.CallableUtils; +import org.apache.freemarker.core.util._KeyValuePair; + +/** + * Corresponds to <code>org.springframework.web.servlet.tags.form.AbstractHtmlElementTag</code>. + */ +public abstract class AbstractHtmlElementTemplateDirectiveModel + extends AbstractDataBoundFormElementTemplateDirectiveModel { + + protected static Map<String, String> createAttributeKeyNamePairsMap(String ... attrNames) { + Map<String, String> map = new HashMap<>(); + for (String attrName : attrNames) { + map.put(attrName.toUpperCase(), attrName); + } + return map; + } + + private static final Map<String, String> ALLOWED_ATTRIBUTES = Collections.unmodifiableMap( + createAttributeKeyNamePairsMap( + "class", + "style", + "lang", + "title", + "dir", + "tabindex", + "onclick", + "ondblclick", + "onmousedown", + "onmouseup", + "onmouseover", + "onmousemove", + "onmouseout", + "onkeypress", + "onkeyup", + "onkeydown") + ); + + private Map<String, Object> attributes; + private Map<String, Object> unmodifiableAttributes = Collections.emptyMap(); + + protected AbstractHtmlElementTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) { + super(request, response); + } + + public Map<String, Object> getAttributes() { + return unmodifiableAttributes; + } + + public void setAttribute(String localName, Object value) { + if (localName == null) { + throw new IllegalArgumentException("Attribute name must not be null."); + } + + if (!isValidDynamicAttribute(localName, value)) { + throw new IllegalArgumentException("Invalid attribute: " + localName + "=" + value); + } + + if (attributes == null) { + attributes = new LinkedHashMap<String, Object>(); + unmodifiableAttributes = Collections.unmodifiableMap(attributes); + } + + attributes.put(localName, value); + } + + protected boolean isAllowedAttribute(String localName, Object value) { + return ALLOWED_ATTRIBUTES.containsKey(localName.toUpperCase()); + } + + protected boolean isValidDynamicAttribute(String localName, Object value) { + return true; + } + + protected void setAttributes(TemplateModel[] args) throws TemplateException { + final int attrsVarargsIndex = getDirectiveArgumentArrayLayout().getNamedVarargsArgumentIndex(); + final TemplateHashModelEx attrsHashModel = (TemplateHashModelEx) args[attrsVarargsIndex]; + + List<_KeyValuePair<String, String>> attrs = Collections.emptyList(); + + if (!attrsHashModel.isEmptyHash()) { + attrs = new ArrayList<>(); + + for (TemplateHashModelEx.KeyValuePairIterator attrIt = attrsHashModel.keyValuePairIterator(); attrIt.hasNext();) { + TemplateHashModelEx.KeyValuePair pair = attrIt.next(); + TemplateModel attrNameModel = pair.getKey(); + TemplateModel attrValueModel = pair.getValue(); + + if (!(attrNameModel instanceof TemplateStringModel)) { + throw CallableUtils.newArgumentValueException(attrsVarargsIndex, + "Parameter name must be a string.", this); + } + + String attrName = ((TemplateStringModel) attrNameModel).getAsString(); + + if (attrName.isEmpty()) { + throw CallableUtils.newArgumentValueException(attrsVarargsIndex, + "Attribute name must be a non-blank string.", this); + } + + // TODO: Don't assume attribute value is string. Treat it as object and convert properly by using Spring utils. + + String attrValue; + + if (attrValueModel instanceof TemplateStringModel) { + attrValue = ((TemplateStringModel) attrValueModel).getAsString(); + } else if (attrValueModel instanceof TemplateNumberModel) { + attrValue = ((TemplateNumberModel) attrValueModel).getAsNumber().toString(); + } else if (attrValueModel instanceof TemplateBooleanModel) { + attrValue = Boolean.toString(((TemplateBooleanModel) attrValueModel).getAsBoolean()); + } else { + throw CallableUtils.newArgumentValueException(attrsVarargsIndex, + "Format the attribute manually to properly coerce it to a URL parameter value string. " + + "e.g, date?string.iso, date?long, list?join('_'), etc.", + this); + } + + setAttribute(attrName, attrValue); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/150ad7f6/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlInputElementTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlInputElementTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlInputElementTemplateDirectiveModel.java new file mode 100644 index 0000000..dc2ae48 --- /dev/null +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlInputElementTemplateDirectiveModel.java @@ -0,0 +1,13 @@ +package org.apache.freemarker.spring.model.form; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public abstract class AbstractHtmlInputElementTemplateDirectiveModel extends AbstractHtmlElementTemplateDirectiveModel { + + protected AbstractHtmlInputElementTemplateDirectiveModel(HttpServletRequest request, + HttpServletResponse response) { + super(request, response); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/150ad7f6/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/InputTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/InputTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/InputTemplateDirectiveModel.java new file mode 100644 index 0000000..293da38 --- /dev/null +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/InputTemplateDirectiveModel.java @@ -0,0 +1,86 @@ +package org.apache.freemarker.spring.model.form; + +import java.io.IOException; +import java.io.Writer; +import java.util.Collections; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.freemarker.core.CallPlace; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.util.CallableUtils; +import org.springframework.web.servlet.support.RequestContext; + +public class InputTemplateDirectiveModel extends AbstractHtmlElementTemplateDirectiveModel { + + public static final String NAME = "input"; + + private static final Map<String, String> ALLOWED_ATTRIBUTES = Collections.unmodifiableMap( + createAttributeKeyNamePairsMap( + "size", + "maxlength", + "alt", + "onselect", + "readonly", + "autocomplete" + ) + ); + + private static final int PATH_PARAM_IDX = 0; + + private static final ArgumentArrayLayout ARGS_LAYOUT = + ArgumentArrayLayout.create( + 1, + false, + null, + true + ); + + protected InputTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) { + super(request, response); + } + + @Override + public boolean isNestedContentSupported() { + return false; + } + + @Override + public ArgumentArrayLayout getDirectiveArgumentArrayLayout() { + return ARGS_LAYOUT; + } + + @Override + protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env, + ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext) + throws TemplateException, IOException { + + final String path = CallableUtils.getStringArgument(args, PATH_PARAM_IDX, this); + setAttributes(args); + + // TODO: convert value properly and write tag and attributes properly. + out.write("<input"); + + for (Map.Entry<String, Object> entry : getAttributes().entrySet()) { + out.write(' '); + out.write(entry.getKey()); + out.write("=\""); + out.write(entry.getValue().toString()); + out.write('\"'); + } + + out.write("/>"); + } + + @Override + protected boolean isAllowedAttribute(String localName, Object value) { + return super.isAllowedAttribute(localName, value) && ALLOWED_ATTRIBUTES.containsKey(localName); + } + +}