Skip to content

Commit

Permalink
#switch #on PR post-merge adjustments: Finished Manual updates for #o…
Browse files Browse the repository at this point in the history
…n. Minor test additions, and code cleanup.
  • Loading branch information
ddekany committed Aug 19, 2024
1 parent dbd2508 commit f6b4de2
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ TemplateElement[] accept(Environment env) throws TemplateException, IOException
}
} catch (BreakOrContinueException br) {
// This catches both break and continue, hence continue is incorrectly treated as a break inside a case.
// ("on", does have this bug.)
// ("on", doesn't have this bug.)
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public void testValidPlacements() throws IOException, TemplateException {
assertOutput("<#list 1..2 as x><#switch x><#on 1>one<#continue></#switch>;</#list>", "one;");
assertOutput("<#forEach x in 1..2>${x}<#break></#forEach>", "1");
assertOutput("<#forEach x in 1..2>${x}<#continue></#forEach>", "12");
assertOutput("<#switch 1><#case 1>1<#break></#switch>", "1");
assertOutput("<#switch 1><#default>1<#break></#switch>", "1");
assertOutput("<#switch 1><#case 1>1<#break>unreachable</#switch>.", "1.");
assertOutput("<#switch 1><#default>1<#break>unreachable</#switch>.", "1.");
}

@Test
Expand Down
15 changes: 15 additions & 0 deletions freemarker-core/src/test/java/freemarker/core/SwitchTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,21 @@ public void testOnWhitespace() throws TemplateException, IOException {
+ "]\n"
+ "</#list>",
"[\nC1]\n[\nC2]\n[\nD]\n");
assertOutput(
""
+ "<#list 1..3 as i>\n"
+ "[\n" // <#----> is to avoid unrelated old white-space stripping bug
+ " <#switch i>\n"
+ " <#on 1>\n"
+ " C1\n"
+ " <#on 2>\n"
+ " C2\n"
+ " <#default>\n"
+ " D\n"
+ " </#switch>\n"
+ "]\n"
+ "</#list>",
"[\n C1\n]\n[\n C2\n]\n[\n D\n]\n");
}

}
166 changes: 126 additions & 40 deletions freemarker-manual/src/main/docgen/en_US/book.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21199,6 +21199,10 @@ with_args_last:
<para><link linkend="ref.directive.nt">nt</link></para>
</listitem>

<listitem>
<para><link linkend="ref.directive.on">on</link></para>
</listitem>

<listitem>
<para><link
linkend="ref_directive_outputformat">outputformat</link></para>
Expand Down Expand Up @@ -25181,12 +25185,14 @@ or
</section>

<section xml:id="ref_directive_switch">
<title>switch, case, default, break</title>
<title>switch, on, case, default, break</title>

<anchor xml:id="ref.directive.switch"/>

<anchor xml:id="ref.directive.case"/>

<anchor xml:id="ref.directive.on"/>

<anchor xml:id="ref.directive.default"/>

<anchor xml:id="ref.directive.switch.break"/>
Expand All @@ -25199,6 +25205,10 @@ or
<primary>case directive</primary>
</indexterm>

<indexterm>
<primary>on directive</primary>
</indexterm>

<indexterm>
<primary>default directive</primary>
</indexterm>
Expand All @@ -25216,7 +25226,7 @@ or

<programlisting role="metaTemplate"><literal>&lt;#switch <replaceable>value</replaceable>&gt;
&lt;#on <replaceable>refValue1</replaceable>&gt;
<replaceable>... (Handles both refValue1)</replaceable>
<replaceable>... (Handles refValue1)</replaceable>
&lt;#on <replaceable>refValue2, refValue3</replaceable>&gt;
<replaceable>... (Handles both refValue2 and refValue3)</replaceable>)
<replaceable>...</replaceable>
Expand All @@ -25231,7 +25241,7 @@ or

<programlisting role="metaTemplate"><literal>&lt;#switch <replaceable>value</replaceable>&gt;
&lt;#case <replaceable>refValue1</replaceable>&gt;
<replaceable>... (Handles both refValue1)</replaceable>
<replaceable>... (Handles refValue1)</replaceable>
&lt;#break&gt;
&lt;#case <replaceable>refValue2</replaceable>&gt;
&lt;#case <replaceable>refValue3</replaceable>&gt;
Expand All @@ -25255,12 +25265,59 @@ or
</listitem>
</itemizedlist>

<para><literal>default</literal> is optional.
<literal>break</literal> is used with <literal>case</literal> to
prevent fall-through into the next <literal>case</literal>. When
<literal>on</literal> is used, <literal>break</literal> is not
allowed, except if the <literal>switch</literal> is inside something
that supports <literal>break</literal>.</para>
<para>Additional rules:</para>

<itemizedlist>
<listitem>
<para>A <literal>switch</literal> block either have
<literal>on</literal>-s, or <literal>case</literal>-s, but not
both.</para>
</listitem>

<listitem>
<para><literal>default</literal>: Optional, can occur at most
once. Must be after the <literal>on</literal>-s. There's no
ordering restriction when used with
<literal>case</literal>-s.</para>
</listitem>

<listitem>
<para><literal>break</literal>: </para>

<itemizedlist>
<listitem>
<para>If the <literal>switch</literal> uses
<literal>case</literal> then <literal>break</literal>
immediately exits from the closes enclosing
<literal>switch</literal>. This is used to prevent
fall-through into the next <literal>case</literal>, or into
the following (adjacent) <literal>default</literal>. (To be
precise, <literal>break</literal> behaves as described if
the <literal>switch</literal> doesn't contain
<literal>on</literal>, and thus also if all it contains is a
<literal>default</literal>.)</para>
</listitem>

<listitem>
<para>If the <literal>switch</literal> uses
<literal>on</literal> then <literal>break</literal> is not
supported by <literal>switch</literal> directly, but
naturally can be used when the <literal>switch</literal> is
inside something else that supports <literal>break</literal>
(like inside a <link
linkend="ref.directive.list">list</link>).</para>
</listitem>

<listitem>
<para>With <literal>case</literal>,
<literal>continue</literal> does the same as
<literal>break</literal>. This is an old bug that works for
backward compatibility, but don't utilize it. With
<literal>on</literal>, this is fixed.</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</section>

<section>
Expand All @@ -25276,45 +25333,72 @@ or
instead. </para>
</note>

<para>[TODO: Continue updating this for <literal>on</literal>
instead of <literal>case</literal>]</para>

<para>Switch is used to choose a fragment of template depending on
the value of an expression:</para>
the value of an expression, and is a shorthand instead using <link
linkend="ref.directive.if"><literal>if</literal>-<literal>elseif</literal>-<literal>else</literal></link>:</para>

<programlisting role="template">&lt;#switch animal.size&gt;
&lt;#on "small"&gt;
Processed if animal.size was "small"
&lt;#on "medium"&gt;
Processed if animal.size was "medium"
&lt;#on "large", "extra large"&gt;
Processed if animal.size was "large" or "extra large"
&lt;#default&gt;
Processed if animal.size is neither of the above
&lt;/#switch&gt;</programlisting>

<para>Before FreeMarker 2.3.24, <literal>on</literal> wasn't
supported, only <literal>case</literal> (note the usage of
<literal>break</literal>, and the intentional omission of it after
<literal>&lt;#case "large"&gt;</literal>):</para>

<programlisting role="template">&lt;#switch animal.size&gt;
&lt;#case "small"&gt;
This will be processed if it is small
Processed if animal.size was "small"
&lt;#break&gt;
&lt;#case "medium"&gt;
This will be processed if it is medium
Processed if animal.size was "medium"
&lt;#break&gt;
&lt;#case "large"&gt;
This will be processed if it is large
&lt;#case "extra large"&gt;
Processed if animal.size was "large" or "extra large"
&lt;#break&gt;
&lt;#default&gt;
This will be processed if it is neither
Processed if animal.size is neither of the above
&lt;/#switch&gt;</programlisting>

<para>Inside the <literal>switch</literal> must be one or more
<literal>&lt;#case <replaceable>value</replaceable>&gt;</literal>,
and after all such <literal>case</literal> tags optionally one
<literal>&lt;#default&gt;</literal>. When FM reaches the
<literal>switch</literal> directive, it chooses a
<literal>case</literal> directive where
<para>Above examples are basically equivalent with this:</para>

<programlisting role="template">&lt;#assign value = animal.size&gt;
&lt;#if value == "small"&gt;
Processed if animal.size was "small"
&lt;#elseif value == "medium"&gt;
Processed if animal.size was "medium"
&lt;#elseif value == "large" || value == "extra large"&gt;
Processed if animal.size was "large" or "extra large"
&lt;#else&gt;
Processed if animal.size is neither of the above
&lt;/#if&gt;</programlisting>

<para>That is, when the <literal>switch</literal> directive is
processed, it chooses an <literal>on</literal> or
<literal>case</literal> directive where a
<literal><replaceable>refValue</replaceable></literal> equals with
<literal><replaceable>value</replaceable></literal> and continues
<literal><replaceable>value</replaceable></literal>, and continues
the processing of the template there. If there is no
<literal>case</literal> directive with appropriate value then it
continues processing at the <literal>default</literal> directive if
that exists, otherwise it continues the processing after the end-tag
of <literal>switch</literal>. And now comes the confusing thing:
when it has chosen a <literal>case</literal> directive, it will
continue the processing there, and will go ahead until it reaches a
<literal>break</literal> directive. That is, it will not
automatically leave the <literal>switch</literal> directive when it
reaches another <literal>case</literal> directive or the
<literal>&lt;#default&gt;</literal> tag. Example:</para>
<literal>on</literal> or <literal>case</literal> directive with
appropriate <literal><replaceable>refValue</replaceable></literal>,
then it continues processing at the <literal>default</literal>
directive, if that exists, otherwise it continues the processing
after the end-tag of <literal>switch</literal>.</para>

<para>Be careful with <literal>case</literal>'s fall-through
behavior! It means that after processing have jumped on the matching
<literal>case</literal>, it will not leave the
<literal>switch</literal> directive when it reaches another
<literal>case</literal> or <literal>default</literal>, only when it
reaches a <literal>break</literal>. Example:</para>

<programlisting role="template">&lt;#switch x&gt;
&lt;#case 1&gt;
Expand All @@ -25325,12 +25409,14 @@ or
d
&lt;/#switch&gt;</programlisting>

<para>If <literal>x</literal> is 1, then it will print 1 2 d; if
<literal>x</literal> is 2 then it will print 2 d; if
<literal>x</literal> is 3 then it will print d. This is the
mentioned fall-through behavior. The <literal>break</literal> tag
instructs FM to immediately skip past the <literal>switch</literal>
end-tag.</para>
<para>If <literal>x</literal> is <literal>1</literal>, then it will
print <literal>1 2 d</literal> (actually with more white-space
between); if <literal>x</literal> is <literal>2</literal> then it
will print <literal>2 d</literal>; if <literal>x</literal> is
<literal>3</literal> then it will print <literal>d</literal>. This
is usually unintended, or if it is intended then probably not
obvious for the reader, and that's why <literal>on</literal> is
recommended over <literal>case</literal>.</para>
</section>
</section>

Expand Down

0 comments on commit f6b4de2

Please sign in to comment.