http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java new file mode 100644 index 0000000..661046f --- /dev/null +++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java @@ -0,0 +1,256 @@ +/* + * 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. + */ + +package org.apache.freemarker.servlet.jsp; + +import java.beans.IntrospectionException; +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.BodyTag; +import javax.servlet.jsp.tagext.IterationTag; +import javax.servlet.jsp.tagext.SimpleTag; +import javax.servlet.jsp.tagext.Tag; +import javax.servlet.jsp.tagext.TryCatchFinally; + +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.CallPlace; +import org.apache.freemarker.core.model.TemplateDirectiveModel; +import org.apache.freemarker.core.model.TemplateHashModelEx2; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Adapts a {@link Tag}-based custom JSP tag to be a value that's callable in templates as an user-defined directive. + * For {@link SimpleTag}-based custom JSP tags {@link SimpleTagDirectiveModel} is used instead. + */ +class TagDirectiveModel extends JspTagModelBase implements TemplateDirectiveModel { + private static final Logger LOG = LoggerFactory.getLogger(TagDirectiveModel.class); + + private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create( + 0, false, + null, true); + + private final boolean isBodyTag; + private final boolean isIterationTag; + private final boolean isTryCatchFinally; + + public TagDirectiveModel(String tagName, Class tagClass) throws IntrospectionException { + super(tagName, tagClass); + isIterationTag = IterationTag.class.isAssignableFrom(tagClass); + isBodyTag = isIterationTag && BodyTag.class.isAssignableFrom(tagClass); + isTryCatchFinally = TryCatchFinally.class.isAssignableFrom(tagClass); + } + + @Override + public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env) + throws TemplateException, IOException { + try { + Tag tag = (Tag) getTagInstance(); + FreeMarkerPageContext pageContext = PageContextFactory.getCurrentPageContext(); + Tag parentTag = (Tag) pageContext.peekTopTag(Tag.class); + tag.setParent(parentTag); + tag.setPageContext(pageContext); + setupTag(tag, (TemplateHashModelEx2) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()], + pageContext.getObjectWrapper()); + // If the parent of this writer is not a JspWriter itself, use + // a little Writer-to-JspWriter adapter... + boolean usesAdapter; + if (out instanceof JspWriter) { + // This is just a sanity check. If it were JDK 1.4-only, + // we'd use an assert. + if (out != pageContext.getOut()) { + throw new TemplateModelException( + "out != pageContext.getOut(). Out is " + + out + " pageContext.getOut() is " + + pageContext.getOut()); + } + usesAdapter = false; + } else { + out = new JspWriterAdapter(out); + pageContext.pushWriter((JspWriter) out); + usesAdapter = true; + } + + // TODO [FM3] In FM2 this was done with a TemplateTransformModel, which has returned a Writer that + // encapsulated the logic. See if there's a better solution now that we use the redesigned + // TemplateDirectiveModel. + TagBodyContent bodyContent = new TagBodyContent(out, tag, pageContext, usesAdapter); + pageContext.pushTopTag(tag); + pageContext.pushWriter(bodyContent); + try { + if (bodyContent.doStartTag()) { + do { + callPlace.executeNestedContent(null, bodyContent, env); + } while (bodyContent.doAfterBody()); + } + } catch (Throwable e) { + bodyContent.doCatch(e); + } finally { + bodyContent.close(); // Pops `topTag` and `writer` + } + } catch (Throwable e) { + throw toTemplateModelExceptionOrRethrow(e); + } + } + + @Override + public ArgumentArrayLayout getArgumentArrayLayout() { + return ARGS_LAYOUT; + } + + /** + * Implements extra methods to help mimicking JSP container behavior around the + * {@link TemplateDirectiveModel#execute(TemplateModel[], CallPlace, Writer, Environment)} call. + */ + class TagBodyContent extends BodyContentImpl { + private final Tag tag; + private final FreeMarkerPageContext pageContext; + private boolean needPop = true; + private final boolean needDoublePop; + + TagBodyContent(Writer out, Tag tag, FreeMarkerPageContext pageContext, boolean needDoublePop) { + super((JspWriter) out, false); + this.needDoublePop = needDoublePop; + this.tag = tag; + this.pageContext = pageContext; + } + + @Override + public String toString() { + return "TagBodyContent for " + tag.getClass().getName() + " wrapping a " + getEnclosingWriter().toString(); + } + + Tag getTag() { + return tag; + } + + FreeMarkerPageContext getPageContext() { + return pageContext; + } + + /** + * @return Whether to execute the nested content (the body, with JSP terminology) + */ + private boolean doStartTag() throws TemplateModelException { + try { + int dst = tag.doStartTag(); + switch(dst) { + case Tag.SKIP_BODY: + // EVAL_PAGE is illegal actually, but some taglibs out there + // use it, and it seems most JSP compilers allow them to and + // treat it identically to SKIP_BODY, so we're going with + // the flow and we allow it too, altough strictly speaking + // it's in violation of the spec. + case Tag.EVAL_PAGE: { + endEvaluation(); + return false; + } + case BodyTag.EVAL_BODY_BUFFERED: { + if (isBodyTag) { + initBuffer(); + BodyTag btag = (BodyTag) tag; + btag.setBodyContent(this); + btag.doInitBody(); + } else { + throw new TemplateModelException("Can't buffer body since " + tag.getClass().getName() + " does not implement BodyTag."); + } + // Intentional fall-through + } + case Tag.EVAL_BODY_INCLUDE: { + return true; + } + default: { + throw new RuntimeException("Illegal return value " + dst + " from " + tag.getClass().getName() + ".doStartTag()"); + } + } + } catch (Exception e) { + throw toTemplateModelExceptionOrRethrow(e); + } + } + + /** + * @return Whether to execute the nested content again (the body, with JSP terminology) + */ + private boolean doAfterBody() throws TemplateModelException { + try { + if (isIterationTag) { + int dab = ((IterationTag) tag).doAfterBody(); + switch(dab) { + case Tag.SKIP_BODY: + endEvaluation(); + return false; + case IterationTag.EVAL_BODY_AGAIN: + return true; + default: + throw new TemplateModelException("Unexpected return value " + dab + "from " + tag.getClass().getName() + ".doAfterBody()"); + } + } + endEvaluation(); + return false; + } catch (Exception e) { + throw toTemplateModelExceptionOrRethrow(e); + } + } + + private void endEvaluation() throws JspException { + if (needPop) { + pageContext.popWriter(); + needPop = false; + } + if (tag.doEndTag() == Tag.SKIP_PAGE) { + LOG.warn("Tag.SKIP_PAGE was ignored from a {} tag.", tag.getClass().getName()); + } + } + + private void doCatch(Throwable t) throws Throwable { + if (isTryCatchFinally) { + ((TryCatchFinally) tag).doCatch(t); + } else { + throw t; + } + } + + @Override + public void close() { + if (needPop) { + pageContext.popWriter(); + } + pageContext.popTopTag(); + try { + if (isTryCatchFinally) { + ((TryCatchFinally) tag).doFinally(); + } + // No pooling yet + tag.release(); + } finally { + if (needDoublePop) { + pageContext.popWriter(); + } + } + } + + } +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagTransformModel.java ---------------------------------------------------------------------- diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagTransformModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagTransformModel.java deleted file mode 100644 index ce7b040..0000000 --- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagTransformModel.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * 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. - */ - -package org.apache.freemarker.servlet.jsp; - -import java.beans.IntrospectionException; -import java.io.CharArrayReader; -import java.io.CharArrayWriter; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.util.Map; - -import javax.servlet.jsp.JspException; -import javax.servlet.jsp.JspWriter; -import javax.servlet.jsp.tagext.BodyContent; -import javax.servlet.jsp.tagext.BodyTag; -import javax.servlet.jsp.tagext.IterationTag; -import javax.servlet.jsp.tagext.SimpleTag; -import javax.servlet.jsp.tagext.Tag; -import javax.servlet.jsp.tagext.TryCatchFinally; - -import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.TemplateTransformModel; -import org.apache.freemarker.core.model.TransformControl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Adapts a {@link Tag}-based custom JSP tag to be a value that's callable in templates as an user-defined directive. - * For {@link SimpleTag}-based custom JSP tags {@link SimpleTagDirectiveModel} is used instead. - */ -class TagTransformModel extends JspTagModelBase implements TemplateTransformModel { - private static final Logger LOG = LoggerFactory.getLogger(TagTransformModel.class); - - private final boolean isBodyTag; - private final boolean isIterationTag; - private final boolean isTryCatchFinally; - - public TagTransformModel(String tagName, Class tagClass) throws IntrospectionException { - super(tagName, tagClass); - isIterationTag = IterationTag.class.isAssignableFrom(tagClass); - isBodyTag = isIterationTag && BodyTag.class.isAssignableFrom(tagClass); - isTryCatchFinally = TryCatchFinally.class.isAssignableFrom(tagClass); - } - - @Override - public Writer getWriter(Writer out, Map args) throws TemplateModelException { - try { - Tag tag = (Tag) getTagInstance(); - FreeMarkerPageContext pageContext = PageContextFactory.getCurrentPageContext(); - Tag parentTag = (Tag) pageContext.peekTopTag(Tag.class); - tag.setParent(parentTag); - tag.setPageContext(pageContext); - setupTag(tag, args, pageContext.getObjectWrapper()); - // If the parent of this writer is not a JspWriter itself, use - // a little Writer-to-JspWriter adapter... - boolean usesAdapter; - if (out instanceof JspWriter) { - // This is just a sanity check. If it were JDK 1.4-only, - // we'd use an assert. - if (out != pageContext.getOut()) { - throw new TemplateModelException( - "out != pageContext.getOut(). Out is " + - out + " pageContext.getOut() is " + - pageContext.getOut()); - } - usesAdapter = false; - } else { - out = new JspWriterAdapter(out); - pageContext.pushWriter((JspWriter) out); - usesAdapter = true; - } - JspWriter w = new TagWriter(out, tag, pageContext, usesAdapter); - pageContext.pushTopTag(tag); - pageContext.pushWriter(w); - return w; - } catch (Exception e) { - throw toTemplateModelExceptionOrRethrow(e); - } - } - - /** - * An implementation of BodyContent that buffers it's input to a char[]. - */ - static class BodyContentImpl extends BodyContent { - private CharArrayWriter buf; - - BodyContentImpl(JspWriter out, boolean buffer) { - super(out); - if (buffer) initBuffer(); - } - - void initBuffer() { - buf = new CharArrayWriter(); - } - - @Override - public void flush() throws IOException { - if (buf == null) { - getEnclosingWriter().flush(); - } - } - - @Override - public void clear() throws IOException { - if (buf != null) { - buf = new CharArrayWriter(); - } else { - throw new IOException("Can't clear"); - } - } - - @Override - public void clearBuffer() throws IOException { - if (buf != null) { - buf = new CharArrayWriter(); - } else { - throw new IOException("Can't clear"); - } - } - - @Override - public int getRemaining() { - return Integer.MAX_VALUE; - } - - @Override - public void newLine() throws IOException { - write(JspWriterAdapter.NEWLINE); - } - - @Override - public void close() throws IOException { - } - - @Override - public void print(boolean arg0) throws IOException { - write(arg0 ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); - } - - @Override - public void print(char arg0) throws IOException { - write(arg0); - } - - @Override - public void print(char[] arg0) throws IOException { - write(arg0); - } - - @Override - public void print(double arg0) throws IOException { - write(Double.toString(arg0)); - } - - @Override - public void print(float arg0) throws IOException { - write(Float.toString(arg0)); - } - - @Override - public void print(int arg0) throws IOException { - write(Integer.toString(arg0)); - } - - @Override - public void print(long arg0) throws IOException { - write(Long.toString(arg0)); - } - - @Override - public void print(Object arg0) throws IOException { - write(arg0 == null ? "null" : arg0.toString()); - } - - @Override - public void print(String arg0) throws IOException { - write(arg0); - } - - @Override - public void println() throws IOException { - newLine(); - } - - @Override - public void println(boolean arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void println(char arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void println(char[] arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void println(double arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void println(float arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void println(int arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void println(long arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void println(Object arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void println(String arg0) throws IOException { - print(arg0); - newLine(); - } - - @Override - public void write(int c) throws IOException { - if (buf != null) { - buf.write(c); - } else { - getEnclosingWriter().write(c); - } - } - - @Override - public void write(char[] cbuf, int off, int len) throws IOException { - if (buf != null) { - buf.write(cbuf, off, len); - } else { - getEnclosingWriter().write(cbuf, off, len); - } - } - - @Override - public String getString() { - return buf.toString(); - } - - @Override - public Reader getReader() { - return new CharArrayReader(buf.toCharArray()); - } - - @Override - public void writeOut(Writer out) throws IOException { - buf.writeTo(out); - } - - } - - class TagWriter extends BodyContentImpl implements TransformControl { - private final Tag tag; - private final FreeMarkerPageContext pageContext; - private boolean needPop = true; - private final boolean needDoublePop; - - TagWriter(Writer out, Tag tag, FreeMarkerPageContext pageContext, boolean needDoublePop) { - super((JspWriter) out, false); - this.needDoublePop = needDoublePop; - this.tag = tag; - this.pageContext = pageContext; - } - - @Override - public String toString() { - return "TagWriter for " + tag.getClass().getName() + " wrapping a " + getEnclosingWriter().toString(); - } - - Tag getTag() { - return tag; - } - - FreeMarkerPageContext getPageContext() { - return pageContext; - } - - @Override - public int onStart() - throws TemplateModelException { - try { - int dst = tag.doStartTag(); - switch(dst) { - case Tag.SKIP_BODY: - // EVAL_PAGE is illegal actually, but some taglibs out there - // use it, and it seems most JSP compilers allow them to and - // treat it identically to SKIP_BODY, so we're going with - // the flow and we allow it too, altough strictly speaking - // it's in violation of the spec. - case Tag.EVAL_PAGE: { - endEvaluation(); - return TransformControl.SKIP_BODY; - } - case BodyTag.EVAL_BODY_BUFFERED: { - if (isBodyTag) { - initBuffer(); - BodyTag btag = (BodyTag) tag; - btag.setBodyContent(this); - btag.doInitBody(); - } else { - throw new TemplateModelException("Can't buffer body since " + tag.getClass().getName() + " does not implement BodyTag."); - } - // Intentional fall-through - } - case Tag.EVAL_BODY_INCLUDE: { - return TransformControl.EVALUATE_BODY; - } - default: { - throw new RuntimeException("Illegal return value " + dst + " from " + tag.getClass().getName() + ".doStartTag()"); - } - } - } catch (Exception e) { - throw toTemplateModelExceptionOrRethrow(e); - } - } - - @Override - public int afterBody() - throws TemplateModelException { - try { - if (isIterationTag) { - int dab = ((IterationTag) tag).doAfterBody(); - switch(dab) { - case Tag.SKIP_BODY: - endEvaluation(); - return END_EVALUATION; - case IterationTag.EVAL_BODY_AGAIN: - return REPEAT_EVALUATION; - default: - throw new TemplateModelException("Unexpected return value " + dab + "from " + tag.getClass().getName() + ".doAfterBody()"); - } - } - endEvaluation(); - return END_EVALUATION; - } catch (Exception e) { - throw toTemplateModelExceptionOrRethrow(e); - } - } - - private void endEvaluation() throws JspException { - if (needPop) { - pageContext.popWriter(); - needPop = false; - } - if (tag.doEndTag() == Tag.SKIP_PAGE) { - LOG.warn("Tag.SKIP_PAGE was ignored from a {} tag.", tag.getClass().getName()); - } - } - - @Override - public void onError(Throwable t) throws Throwable { - if (isTryCatchFinally) { - ((TryCatchFinally) tag).doCatch(t); - } else { - throw t; - } - } - - @Override - public void close() { - if (needPop) { - pageContext.popWriter(); - } - pageContext.popTopTag(); - try { - if (isTryCatchFinally) { - ((TryCatchFinally) tag).doFinally(); - } - // No pooling yet - tag.release(); - } finally { - if (needDoublePop) { - pageContext.popWriter(); - } - } - } - - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java ---------------------------------------------------------------------- diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java index cacb22b..606cef8 100644 --- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java +++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java @@ -64,11 +64,11 @@ import javax.xml.parsers.SAXParserFactory; import org.apache.freemarker.core.ConfigurationException; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.model.ObjectWrapper; +import org.apache.freemarker.core.model.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateHashModel; import org.apache.freemarker.core.model.TemplateMethodModelEx; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.TemplateTransformModel; import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util.CommonBuilder; @@ -232,7 +232,7 @@ public class TaglibFactory implements TemplateHashModel { * to integrate JSP taglib support should do the same. * * @return a {@link TemplateHashModel} representing the JSP taglib. Each element of this hash represents a single - * custom tag or EL function from the library, implemented as a {@link TemplateTransformModel} or + * custom tag or EL function from the library, implemented as a {@link TemplateDirectiveModel} or * {@link TemplateMethodModelEx}, respectively. */ @Override @@ -1762,7 +1762,7 @@ public class TaglibFactory implements TemplateHashModel { final TemplateModel customTagModel; try { if (Tag.class.isAssignableFrom(tagClass)) { - customTagModel = new TagTransformModel(tagNameCData, tagClass); + customTagModel = new TagDirectiveModel(tagNameCData, tagClass); } else { customTagModel = new SimpleTagDirectiveModel(tagNameCData, tagClass); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java ---------------------------------------------------------------------- diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java index 22f4f27..0a9e9cd 100644 --- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java +++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java @@ -20,43 +20,40 @@ package org.apache.freemarker.test.templateutil; import java.io.IOException; -import java.util.Map; +import java.io.Writer; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.NestedContentNotSupportedException; import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.CallPlace; import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateDirectiveBody; import org.apache.freemarker.core.model.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.util.FTLUtil; +import org.apache.freemarker.core.util.StringToIndexMap; public class AssertDirective implements TemplateDirectiveModel { - public static AssertDirective INSTANCE = new AssertDirective(); - - private static final String TEST_PARAM = "test"; - + + private static final String TEST_ARG_NAME = "test"; + private static final int TEST_ARG_IDX = 0; + private static final StringToIndexMap ARG_NAMES_TO_IDX = StringToIndexMap.of(TEST_ARG_NAME, TEST_ARG_IDX); + private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create( + 0, false, + ARG_NAMES_TO_IDX, false); + private AssertDirective() { } @Override - public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env) throws TemplateException, IOException { - TemplateModel test = null; - for (Object paramEnt : params.entrySet()) { - Map.Entry<String, TemplateModel> param = (Map.Entry) paramEnt; - String paramName = param.getKey(); - if (paramName.equals(TEST_PARAM)) { - test = param.getValue(); - } else { - throw new UnsupportedParameterException(paramName, env); - } - } + NestedContentNotSupportedException.check(callPlace); + + TemplateModel test = args[TEST_ARG_IDX]; if (test == null) { - throw new MissingRequiredParameterException(TEST_PARAM, env); + throw new MissingRequiredParameterException(TEST_ARG_NAME, env); } - NestedContentNotSupportedException.check(body); - if (!(test instanceof TemplateBooleanModel)) { throw new AssertationFailedInTemplateException("Assertion failed:\n" + "The value had to be boolean, but it was of type" + FTLUtil.getTypeDescription(test), @@ -67,7 +64,10 @@ public class AssertDirective implements TemplateDirectiveModel { + "the value was false.", env); } - } + @Override + public ArgumentArrayLayout getArgumentArrayLayout() { + return ARGS_LAYOUT; + } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java ---------------------------------------------------------------------- diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java index 9df8992..a02308f 100644 --- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java +++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java @@ -20,72 +20,79 @@ package org.apache.freemarker.test.templateutil; import java.io.IOException; -import java.util.Map; +import java.io.Writer; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.NestedContentNotSupportedException; import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.CallPlace; import org.apache.freemarker.core.model.TemplateBooleanModel; import org.apache.freemarker.core.model.TemplateDateModel; -import org.apache.freemarker.core.model.TemplateDirectiveBody; import org.apache.freemarker.core.model.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateNumberModel; import org.apache.freemarker.core.model.TemplateScalarModel; +import org.apache.freemarker.core.util.StringToIndexMap; import org.apache.freemarker.core.util._StringUtil; public class AssertEqualsDirective implements TemplateDirectiveModel { public static AssertEqualsDirective INSTANCE = new AssertEqualsDirective(); - private static final String ACTUAL_PARAM = "actual"; - private static final String EXPECTED_PARAM = "expected"; + private static final int ACTUAL_ARG_IDX = 0; + private static final int EXPECTED_ARG_IDX = 1; + + private static final String ACTUAL_ARG_NAME = "actual"; + private static final String EXPECTED_ARG_NAME = "expected"; + + private static final StringToIndexMap ARG_NAME_TO_IDX = StringToIndexMap.of( + ACTUAL_ARG_NAME, ACTUAL_ARG_IDX, + EXPECTED_ARG_NAME, EXPECTED_ARG_IDX); + + private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create( + 0, false, + ARG_NAME_TO_IDX, false); private AssertEqualsDirective() { } @Override - public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env) throws TemplateException, IOException { - TemplateModel actual = null; - TemplateModel expected = null; - for (Object paramEnt : params.entrySet()) { - Map.Entry<String, TemplateModel> param = (Map.Entry) paramEnt; - String paramName = param.getKey(); - if (paramName.equals(ACTUAL_PARAM)) { - actual = param.getValue(); - } else if (paramName.equals(EXPECTED_PARAM)) { - expected = param.getValue(); - } else { - throw new UnsupportedParameterException(paramName, env); - } - } + NestedContentNotSupportedException.check(callPlace); + + TemplateModel actual = args[ACTUAL_ARG_IDX]; if (actual == null) { - throw new MissingRequiredParameterException(ACTUAL_PARAM, env); + throw new MissingRequiredParameterException(ACTUAL_ARG_NAME, env); } + + TemplateModel expected = args[EXPECTED_ARG_IDX]; if (expected == null) { - throw new MissingRequiredParameterException(EXPECTED_PARAM, env); + throw new MissingRequiredParameterException(EXPECTED_ARG_NAME, env); } - NestedContentNotSupportedException.check(body); - + if (!env.applyEqualsOperatorLenient(actual, expected)) { throw new AssertationFailedInTemplateException("Assertion failed:\n" - + "Expected: " + tryUnwrapp(expected) + "\n" - + "Actual: " + tryUnwrapp(actual), + + "Expected: " + tryUnwrap(expected) + "\n" + + "Actual: " + tryUnwrap(actual), env); } - } - private String tryUnwrapp(TemplateModel value) throws TemplateModelException { + private String tryUnwrap(TemplateModel value) throws TemplateModelException { if (value == null) return "null"; - // This is the same order as comparison goes: + // This is the same order as comparison goes: else if (value instanceof TemplateNumberModel) return ((TemplateNumberModel) value).getAsNumber().toString(); else if (value instanceof TemplateDateModel) return ((TemplateDateModel) value).getAsDate().toString(); else if (value instanceof TemplateScalarModel) return _StringUtil.jQuote(((TemplateScalarModel) value).getAsString()); else if (value instanceof TemplateBooleanModel) return String.valueOf(((TemplateBooleanModel) value).getAsBoolean()); - // This shouldn't be reached, as the comparison should have failed earlier: + // This shouldn't be reached, as the comparison should have failed earlier: else return value.toString(); } + @Override + public ArgumentArrayLayout getArgumentArrayLayout() { + return ARGS_LAYOUT; + } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java ---------------------------------------------------------------------- diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java index 4bb0721..effc0e7 100644 --- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java +++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java @@ -20,107 +20,110 @@ package org.apache.freemarker.test.templateutil; import java.io.IOException; -import java.util.Map; +import java.io.Writer; import java.util.regex.Pattern; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.TemplateDirectiveBody; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.CallPlace; import org.apache.freemarker.core.model.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateNumberModel; import org.apache.freemarker.core.model.TemplateScalarModel; +import org.apache.freemarker.core.util.StringToIndexMap; import org.apache.freemarker.core.util._NullWriter; import org.apache.freemarker.core.util._StringUtil; public class AssertFailsDirective implements TemplateDirectiveModel { - + public static AssertFailsDirective INSTANCE = new AssertFailsDirective(); - private static final String MESSAGE_PARAM = "message"; - private static final String MESSAGE_REGEXP_PARAM = "messageRegexp"; - private static final String EXCEPTION_PARAM = "exception"; - private static final String CAUSE_NESTING_LEVEL_PARAM = "causeNestingLevel"; - - private AssertFailsDirective() { } + private static final int MESSAGE_ARG_IDX = 0; + private static final int MESSAGE_REGEXP_ARG_IDX = 1; + private static final int EXCEPTION_ARG_IDX = 2; + private static final int CAUSE_NESTING_LEVEL_ARG_IDX = 3; - @Override - public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + private static final String MESSAGE_ARG_NAME = "message"; + private static final String MESSAGE_REGEXP_ARG_NAME = "messageRegexp"; + private static final String EXCEPTION_ARG_NAME = "exception"; + private static final String CAUSE_NESTING_LEVEL_ARG_NAME = "causeNestingLevel"; + + private static final StringToIndexMap ARG_NAME_TO_IDX = StringToIndexMap.of( + MESSAGE_ARG_NAME, MESSAGE_ARG_IDX, + MESSAGE_REGEXP_ARG_NAME, MESSAGE_REGEXP_ARG_IDX, + EXCEPTION_ARG_NAME, EXCEPTION_ARG_IDX, + CAUSE_NESTING_LEVEL_ARG_NAME, CAUSE_NESTING_LEVEL_ARG_IDX); + + private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create( + 0, false, + ARG_NAME_TO_IDX, false); + + private AssertFailsDirective() { + } + + public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env) throws TemplateException, IOException { - String message = null; - Pattern messageRegexp = null; - String exception = null; - int causeNestingLevel = 0; - for (Object paramEnt : params.entrySet()) { - Map.Entry<String, TemplateModel> param = (Map.Entry) paramEnt; - String paramName = param.getKey(); - if (paramName.equals(MESSAGE_PARAM)) { - message = getAsString(param.getValue(), MESSAGE_PARAM, env); - } else if (paramName.equals(MESSAGE_REGEXP_PARAM)) { - messageRegexp = Pattern.compile( - getAsString(param.getValue(), MESSAGE_REGEXP_PARAM, env), - Pattern.CASE_INSENSITIVE); - } else if (paramName.equals(EXCEPTION_PARAM)) { - exception = getAsString(param.getValue(), EXCEPTION_PARAM, env); - } else if (paramName.equals(CAUSE_NESTING_LEVEL_PARAM)) { - causeNestingLevel = getAsInt(param.getValue(), CAUSE_NESTING_LEVEL_PARAM, env); - } else { - throw new UnsupportedParameterException(paramName, env); - } - } - - if (body != null) { + String message = getAsString(args[MESSAGE_ARG_IDX], MESSAGE_ARG_NAME, env); + Pattern messageRegexp = getAsPattern(args[MESSAGE_REGEXP_ARG_IDX], MESSAGE_REGEXP_ARG_NAME, env); + String exception = getAsString(args[EXCEPTION_ARG_IDX], EXCEPTION_ARG_NAME, env); + int causeNestingLevel = getAsInt(args[CAUSE_NESTING_LEVEL_ARG_IDX], 0, CAUSE_NESTING_LEVEL_ARG_NAME, env); + if (callPlace.hasNestedContent()) { boolean blockFailed; try { - body.render(_NullWriter.INSTANCE); + callPlace.executeNestedContent(null, _NullWriter.INSTANCE, env); blockFailed = false; } catch (Throwable e) { blockFailed = true; - - int causeNestingLevelCountDown = causeNestingLevel; + + int causeNestingLevelCountDown = causeNestingLevel; while (causeNestingLevelCountDown != 0) { e = e.getCause(); if (e == null) { throw new AssertationFailedInTemplateException( "Failure is not like expected: The cause exception nesting dept was lower than " - + causeNestingLevel + ".", + + causeNestingLevel + ".", env); } causeNestingLevelCountDown--; } - + if (message != null || messageRegexp != null) { if (e.getMessage() == null) { throw new AssertationFailedInTemplateException( "Failure is not like expected. The exception message was null, " - + "and thus it doesn't contain:\n" + _StringUtil.jQuote(message) + ".", + + "and thus it doesn't contain:\n" + _StringUtil.jQuote(message) + ".", env); } if (message != null) { String actualMessage = e instanceof TemplateException ? ((TemplateException) e).getMessageWithoutStackTop() : e.getMessage(); - if (actualMessage.toLowerCase().indexOf(message.toLowerCase()) == -1) { + if (!actualMessage.toLowerCase().contains(message.toLowerCase())) { throw new AssertationFailedInTemplateException( - "Failure is not like expected. The exception message:\n" + _StringUtil.jQuote(actualMessage) - + "\ndoesn't contain:\n" + _StringUtil.jQuote(message) + ".", + "Failure is not like expected. The exception message:\n" + _StringUtil + .jQuote(actualMessage) + + "\ndoesn't contain:\n" + _StringUtil.jQuote(message) + ".", env); } } if (messageRegexp != null) { if (!messageRegexp.matcher(e.getMessage()).find()) { throw new AssertationFailedInTemplateException( - "Failure is not like expected. The exception message:\n" + _StringUtil.jQuote(e.getMessage()) - + "\ndoesn't match this regexp:\n" + _StringUtil.jQuote(messageRegexp.toString()) - + ".", + "Failure is not like expected. The exception message:\n" + _StringUtil + .jQuote(e.getMessage()) + + "\ndoesn't match this regexp:\n" + _StringUtil + .jQuote(messageRegexp.toString()) + + ".", env); } } } - if (exception != null && e.getClass().getName().indexOf(exception) == -1) { + if (exception != null && !e.getClass().getName().contains(exception)) { throw new AssertationFailedInTemplateException( - "Failure is not like expected. The exception class name " + _StringUtil.jQuote(e.getClass().getName()) - + " doesn't contain " + _StringUtil.jQuote(message) + ".", + "Failure is not like expected. The exception class name " + _StringUtil + .jQuote(e.getClass().getName()) + + " doesn't contain " + _StringUtil.jQuote(message) + ".", env); } } @@ -134,19 +137,37 @@ public class AssertFailsDirective implements TemplateDirectiveModel { private String getAsString(TemplateModel value, String paramName, Environment env) throws BadParameterTypeException, TemplateModelException { + if (value == null) { + return null; + } if (value instanceof TemplateScalarModel) { - return ((TemplateScalarModel) value).getAsString(); + return ((TemplateScalarModel) value).getAsString(); } else { throw new BadParameterTypeException(paramName, "string", value, env); } } - private int getAsInt(TemplateModel value, String paramName, Environment env) throws BadParameterTypeException, TemplateModelException { + private Pattern getAsPattern(TemplateModel value, String paramName, Environment env) + throws TemplateModelException, BadParameterTypeException { + String s = getAsString(value, paramName, env); + return s != null ? Pattern.compile(s, Pattern.CASE_INSENSITIVE) : null; + } + + private int getAsInt(TemplateModel value, int defaultValue, String paramName, Environment env) throws + BadParameterTypeException, + TemplateModelException { + if (value == null) { + return defaultValue; + } if (value instanceof TemplateNumberModel) { return ((TemplateNumberModel) value).getAsNumber().intValue(); } else { throw new BadParameterTypeException(paramName, "number", value, env); } } - + + @Override + public ArgumentArrayLayout getArgumentArrayLayout() { + return ARGS_LAYOUT; + } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java ---------------------------------------------------------------------- diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java index 6dbf8f5..b027974 100644 --- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java +++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java @@ -20,14 +20,14 @@ package org.apache.freemarker.test.templateutil; import java.io.IOException; -import java.util.Map; +import java.io.Writer; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.TemplateDirectiveBody; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.CallPlace; import org.apache.freemarker.core.model.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.util._NullWriter; public class NoOutputDirective implements TemplateDirectiveModel { @@ -39,12 +39,13 @@ public class NoOutputDirective implements TemplateDirectiveModel { } @Override - public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env) throws TemplateException, IOException { - if (!params.isEmpty()) { - throw new TemplateModelException("This directivey doesn't support any parameters."); - } - body.render(_NullWriter.INSTANCE); + callPlace.executeNestedContent(null, _NullWriter.INSTANCE, env); } + @Override + public ArgumentArrayLayout getArgumentArrayLayout() { + return ArgumentArrayLayout.PARAMETERLESS; + } }
