MicroNova YUZU JSP tag library

2008-12-28

MicroNova YUZU (named after Japanese citrus that adds touch of aroma) is a JSP tag library designed to augment JSP Standard Tag Library (JSTL) using Expression Language (EL). For JSTL/EL, please consult JSTL specification. YUZU is designed to be compatible with both JSP 1.2 and JSP 2.0 specifications. YUZU is tested under Apache tomcat (4.x/5.x) with JSTL reference implementation on Linux, and released under BSD-style license.

1 Attribute Evaluation

All YUZU tag attributes are always evaluated as EL on assignment. For example, unlike JSTL <c:set>, you can use dynamic variable names with <m:set> (YUZU version of <c:set>) as follows:

<%-- sets the value of "a" to "b" --%>

<c:set var="variableName" value="a"/>
<m:set var="${variableName}" value="b"/>

However, certain YUZU tag attributes (called "EL attributes") are strings to be evaluated as EL later, and it is necessary to escape evaluation of "${...}" on assignment. One way to do this in standard JSP/JSTL is to use "${'$'}" instead of "$". The following sets the value of "x" to "${xyz}":

<c:set var="x" value="${'$'}{xyz}"/>

To simplify coding, YUZU EL attributes translate a special pattern "@{" to "${" on assignment. The following sets the "test" attribute to "${!empty param.x}" EL expression for later evaluation:

<%-- outputs the value of parameter "x" if not empty, otherwise "EMPTY" --%>

<m:out value="${param.x}" default="EMPTY" test="@{!empty param.x}"/>

Translation from "@{" to "${" is made after evaluating the attribute value as EL, so the following is equivalent to the above:

<c:set var="PARAMNAME" value="param.x"/>
<m:out value="${param.x}" default="EMPTY" test="@{!empty ${PARAMNAME}}"/>

EL attributes common to all YUZU tags are: test, assign, export, prepareCodec, importCodec, processCodec, codec, assignCodec, exportCodec, and cleanupCodec.

2 Overview

Each YUZU tag has an object (called "tagValue" in this document) associated with it, and is always processed in 6 stages as follows:

  1. Prepare stage where the tagValue is prepared (upon tag opening)
  2. Import stage where the tagValue is imported from the tag body, if not prepared (upon tag closing)
  3. Default stage where default value logic is applied to the tagValue (upon tag closing)
  4. Process stage where the tagValue is processed (upon tag closing)
  5. Assign stage where the tagValue is assigned to a variable (upon tag closing)
  6. Export stage where the tagValue is output, if no assignment is made (upon tag closing)

2.1 Prepare Stage

At Prepare stage, the tagValue is set in one of the following ways (in the order of precedence):

Prepare stage is done upon tag opening before processing the tag body, and the tagValue set in Prepare stage is accessible within the tag body using a special page-scoped variable "_" (underscore). See Local Variables below.

In the following example, <m:out> simply outputs the tagValue as-is:

<%-- outputs "Hello World" --%>

<m:out value="Hello World"/>

<%-- outputs current date --%>

<m:out className="java.util.Date"/>

<%-- creates an instance of Gregorian Calendar for 2007/01/01 --%>

<m:out className="java.util.GregorianCalendar:2007:0:1"/>

<%-- creates a double array of size 3 --%>

<m:out className="[]:double:3"/>

You can also specify source attribute as a map of bean properties to initialize the tagValue as in <m:map> tag. The value of source can be an instance of java.util.Map or a string representing such a map in form/xml/json-encoded (with optional '@' prefix to indicate evaluation using "@{..}" syntax), or any other object (in this case the map of all bean property values of given object (as obtained by Bean:getMap codec) is used). For example:

<%-- creates a date in year 1999 --%>

<%-- using json-encoded string --%>

<m:set className="java.util.Date" source="{year:99}"/>

<%-- using xml-encoded string --%>

<m:set className="java.util.Date" source="<root><year>99</year></root>"/>

<%-- using json-encoded evaluated map --%>

<m:set var="year" value="99"/>
<m:set className="java.util.Date" source="@{year:@{year}}"/>

If source is a list or a NestedMap with non-empty "_" sublist, then the tagValue is assumed to be an array or list and its elements are set accordingly. For example,

<%-- creates and initializes a 3x3 int array --%>
<m:set var="x" className="[]:int[]:3">
  <%-- a JSON-encoded NestedMap --%>       
  <m:set property="0" className="[]:int:3" source="{_:[1,2,3]}"/>
  <%-- list obtained by splitting a string --%>
  <m:set property="1" className="[]:int:3">
    <m:set attribute="source" value="4,5,6" codec="String:split"/>
  </m:set>
  <%-- NestedMap with explicitly set sublist elements --%>
  <m:set property="2" className="[]:int:3">
    <m:map attribute="source">
      <m:set property="@_.*">7</m:set>
      <m:set property="@_.*">8</m:set>
      <m:set property="@_.*">9</m:set>
    </m:map>
  </m:set>
</m:set>

<%-- outputs 1 2 3 4 5 6 7 8 9 --%>
<c:forEach var="i" items="0,1,2">
  <c:forEach var="j" items="0,1,2">
    ${x[i][j]}
  </c:forEach>
</c:forEach>           

If necessary, you can specify a pipe of functions ("codec") to be applied to the prepared tagValue using prepareCodec attribute. See Codecs below for more details.

2.2 Import Stage

If the tagValue is not set at Prepare stage, then it is "imported" from the tag body (i.e., set to the string resulting from processing the tag body as JSP) upon tag closing. By default the string is taken as-is, but if necessary, you can specify a "codec" to be applied to the string.

For example:

<%-- outputs "Hello World" --%>

<m:out><m:out value="Hel"/>lo Wor<m:out value="ld"/></m:out>

<%-- outputs the value of "${message}" if not null, otherwise "Hello World" since tagValue is not set in Prepare stage --%>

<m:out value="${message}">Hello World</m:out>

<%-- outputs lower-case "hello world", applying "String:toLowerCase" codec --%>

<m:out importCodec="String:toLowerCase">Hello World</m:out>

2.3 Default Stage

When necessary, a default value for the tagValue can be specified using default attribute. If specified, the default value is taken at Default stage if current tagValue (prepared or imported) is null or empty string (""):

<%-- outputs the value of parameter "X", or "DEFAULT" if not given --%>

<m:out value="${param.X}" default="DEFAULT"/>

You can control when default is taken by specifying test EL attribute (a boolean EL expression that evaluates to true/false). If specified, the default value is taken when test evaluates to false:

<%-- outputs "ONE" if the value of parameter "X" is "1", otherwise "NOTONE" --%>

<m:out test="@{param.X == 1}" value="ONE" default="NOTONE"/>

You can use a special page-scoped variable "_" (underscore) in the test expression to refer to the current tagValue:

<%-- outputs "NONE" if there are no parameters --%>

<m:out test="@{!empty _}" default="NONE"><c:forEach var="p" items="${param}"><m:out value="${p}"/></c:forEach></m:out>

Note: actually the test attribute value is a codec. See Codecs below for more information.

2.4 Process Stage

At Process stage, the tagValue is processed and transformed into another object if necessary. Most YUZU tags perform customized actions here.

If processCodec or codec is specified, it is applied to the processed tagValue before Assign/Export stage.

2.5 Assign Stage

The processed tagValue is assigned to a variable in one of the following ways (as in JSTL <c:set>):

Default variable scope is "page", but this can be controlled by setting localScope attribute in the ancestor tag (see Local Variables below).

When property is specified, target can be omitted if the tag is within the body of another YUZU tag. In this case, the closest surrounding YUZU tag's tagValue is taken as the target.

As in JSTL, when the target object is a map (implementation of java.util.Map), then the property is used as the map key:

<%-- assigns to "map" an instanceof java.util.HashMap initialized as "{a=alpha,b=beta}" --%>

<m:set var="map" className="java.util.HashMap">
  <m:set property="a" value="alpha"/>
  <m:set property="b" value="beta"/>
</m:set>

When the target object is a List (implementation of java.util.List), then the property specifies the element position as follows:

 property  tagValue  operation 
 non-negative integer  any  replace at given index from the beginning (0 = first element) 
 negative integer  any  replace at given index from the end (-1 = last element) 
 "*" or "**"  non-null  append tagValue at the end 
 "*"  null  remove last element 
 "*" or "**" followed by non-negative integer (e.g., "*2")  non-null  insert tagValue at given index from the beginning 
 "*" followed by non-negative integer (e.g., "*2")  null  remove element at given index from the beginning 
 "*" or "**" followed by negative integer (e.g., "*-2")  non-null  insert tagValue at given index from the end 
 "*" followed by negative integer (e.g., "*-2")  null  remove element at given index from the end 
 any string starting with "**"  null  no operation 

For example:

<m:set var="list" className="java.util.ArrayList">
  <%-- append "a", "b", "c", "d" --%>
  <m:set property="*" value="a"/>
  <m:set property="*" value="b"/>
  <m:set property="*" value="c"/>
  <m:set property="*" value="d"/>
  <%-- remove element at 1 ("b") --%>
  <m:set property="*1"/>
  <%-- insert "e" at position 1 --%>
  <m:set property="*1" value="e"/>
  <%-- insert "f" before the last two elements --%>
  <m:set property="*-2" value="f"/>
  <%-- replace element at index 1 with "E" --%>
  <m:set property="1" value="E"/>
  <%-- remove the last element --%>
  <m:set property="*"/>
</m:set>

<%-- this outputs "[a, E, f, c]" --%>

<m:out value="${list}"/>

When the target object is an array, then the property can be either a non-negative integer (index from the beginning) or a negative integer (index from the end), indicating replacement position.

When the target object is a Queue (implementation of java.util.Queue), then property "*" can be used as follows:

 property  tagValue  operation 
 "*"  non-null  inserts tagValue to the queue 
 "*"  null  removes and returns the head of the queue 

As a special case, you can set the value of target to "return" string to return the tagValue to the caller when the JSP is called by another JSP as in <m:return> tag.

By default the tagValue itself is assigned, but you can control what is actually assigned by specifying assign EL expression using a special variable "_" (underscore) representing the tagValue:

<%-- assigns the value of "a" followed by the value of "b" to variable X, instead of the map itself --%>

<m:set var="X" className="java.util.HashMap" assign="@{_.a}@{_.b}">
  <m:set property="a" value="alpha"/>
  <m:set property="b" value="beta"/>
</m:set>

<m:out value="${X}"/>

You can set the value of assign to a special string "_export" to use the same value as export.

If assignCodec is specified, then it is applied to the result of evaluating the assign EL expression (the tagValue by default) before assignment is made.

When necessary, it is possible to set a tag attribute value using attribute attribute within the tag body (in this case target must be omitted). For example,

<m:set var="message">
  <m:set attribute="value">hello</m:set>
</m:set>

is equivalent to the following:

<m:set var="message" value="hello"/>

When necessary, you can use a comma-seperated list of names as property or var. In this case, 1) if the assign value is a list or an array, then each element of the list/array is assigned to each named variable or property, 2) if the assign value is a map, then the map value for each property is assigned to each named variable or property, 3) otherwise the same assign value is assigned to each named variable or property. If a name is an empty string, it is skipped and no assignment is made. For example:

<%-- generates java code from given signature --%>

<m:set var="signature" assignCodec="String:trim">
int foo(int a, String b)
</m:set>

<m:set var="template" assignCodec="String:trim">
@{_.type} @{_.name}(@{_.argDecl}){ 
  return super.@{_.name}(@{_.args});
}
</m:set>

<m:map exportCodec="${template}">
  <m:set property=",type,name,argDecl" value="${signature}" codec="String:matchingGroups:^([^ ]+) ([^(]+)\((.*)\)$"/>
  <m:set property="args" value="${_.argDecl}" codec="String:replaceAll:[^, ]+ ::"/>
</m:map>

or

<%-- set "name" and "home" page-scope variables to the values of system
properties "user.name" and "user.home" --%>

<m:map var="name,home" assign="@{_.user}">
  <m:set property="__param" codec="System:getProperties_"/>
</m:map>

2.6 Export Stage

If no assignment is made in Assign stage, then the tagValue is "exported" (output as string). By default the tagValue itself is exported, but if necessary, it is possible to specify what is exported by using export attribute. The value of export attribute is an EL expression containing a special variable "_" (underscore) representing the tagValue, and the result of evaluating the EL expression is output as string.

Here is an example of using non-default export:

<%-- outputs number of milliseconds from 01/01/1970. --%>

<m:out className="java.util.Date" export="@{_.time}"/>

If exportCodec is specified, then it is applied to the result of evaluating the export EL expression before exporting. Note that, unlike JSTL <c:out>, YUZU tags do not escape tag characters such as '<', '>' etc. unless exportCodec is set to something like "XML:encode".

If the tagValue object is a DOM Node (implementation of org.w3c.dom.Node), then it is output as XML (without XML declaration).

By default, the tagValue is exported only when it is not assigned, but you can force exporting by setting doesExport attribute to "always".

3 Codecs

At various stages above, you can specify extra functions to be applied such as "URL:encode", "Base64:decode", etc.. Such a function is called a "codec" in this document. YUZU tags support the following codec attributes:

The object to which a codec is applied is called operand; e.g., the operand for "importCodec" is the tag body string, and the operand for "processCodec" is the processed "tagValue".

For example:

<%-- if "x=a&x=b&x=c" is given as form parameters, then outputs "a,b,c" --%>

<m:out value="${paramValues.x}" codec="String:join"/>

You can specify additional arguments separated by ":" (colon):

<%-- if "x=a&x=b&x=c" is given as form parameters, then outputs "a-b-c" --%>

<m:out value="${paramValues.x}" codec="String:join:-"/>

You can use two "::" (two colons) to indicate an empty string argument:

<%-- if "x=a&x=b&x=c" is given as form parameters, then outputs "abc" --%>

<m:out value="${paramValues.x}" codec="String:join::"/>

Codec arguments are evaluated as EL upon function call, and you can use "@{...}" to escape evaluation on assignment:

<m:set var="separator" value=":"/>

<%-- if "x=a&x=b&x=c" is given as form parameters, then outputs "a:b:c" --%>

<m:out value="${paramValues.x}" codec="String:join:@{separator}"/>

You can use "|" to indicate a sequence of codecs ("pipe") to be applied. The second codec is applied to the result of the first codec, and so on:

<m:set var="separator" value=":"/>

<%-- if "x=a&x=b&x=c" is given as form parameters, then outputs "A:B:C" --%>

<m:out value="${paramValues.x}" codec="String:join:@{separator}|String:toUpperCase"/>

You can use "\" to escape separators when necessary ("\:" or "\|"):

<%-- if "x=a&x=b&x=c" is given as form parameters, then outputs "|:|:|" --%>

<m:out value="${paramValues.x}" codec="String:join:\:|String:replaceAll:[abc]:\|"/>

There is a special variable named "_operand" that holds the "operand" of the codec (the object to which the codec is applied):

<%-- outputs "MATCH" instead of "abc" if "x=a&x=b&x=c" is given --%>

<m:out value="${paramValues.x}" codec="String:join::|Type:ifEqual:abc:MATCH:@{_operand}"/>

If you append an "_" (underscore) to the name of the codec method, then the codec is applied to the first codec argument, instead of "_operand". The following two are equivalent:

<m:out value="${paramValues.x}" codec="String:join:-"/>

<m:out codec="String:join_:@{paramValues.x}:-"/>

If there is no ":" found in a codec specification, then it is evaluated as EL:

<%-- outputs "true" if parameter "x" contains more than 2 "t"'s --%>

<m:out value="${param.x}" codec="String:replaceAll:[^t]::|String:length|@{_operand > 2}"/>

You can store a codec in a variable:

<m:set var="ISAPHONE" value="String:match:^\([0-9]{3}\)[0-9]{3}-[0-9]{4}$"/>

x is <m:out value="${param.x}" codec="${ISAPHONE}|Type:ifNull:not a:a"/> phone number.

The following built-in codec's are available ("operand" refers to the object to which the codec is applied, and "[:xxx]" indicates optional arguments):

 Backslash:decode  decodes "operand" as backslash-encoded String. Current supports "\n", "\r", "\t", "\b", "\f", "\'", "\"", "\\", "\/", and "\uxxxx" (unicode). Note that NEWLINE (\n) following a "\" is removed. 
 Backslash:encode  encodes special characters using "\" (backslash) in "operand" as String. Currently supports "\n", "\r", "\t", "\b", "\f", "\", "\'", "\"", "\/". 
 Base64:decode[:ENCODING]  decodes "operand" as base64-encoded string using ENCODING (default "utf-8") 
 Base64:encode[:ENCODING]  applies base64 encoding to "operand" using ENCODING (default "utf-8") 
 Bean:decode  deserializes XML "operand" as java bean. 
 Bean:encode  encodes "operand" as java bean using XML serialization. 
 Bean:fill:PROPERTYMAP  sets bean properties of "operand" as bean according to PROPERTYMAP containing (property, value) pairs 
 Bean:get:PROPERTY  gets the bean property named PROPERTY of "operand". If the "operand" is a map/list/queue, then its value/element corresponding to the PROPERTY is returned. For a list, negative integers can be used to index from the end (e.g., "-1" returns the last element). For a queue, "*" can be used to remove and return the head, and "0" can be used to return the head without removing. 
 Bean:getInfo  returns BeanInfo of "operand" 
 Bean:getMap  returns all (property, value) pairs of "operand" bean as a map 
 Bean:getProperty:PROPERTY  gets the bean property named PROPERTY of "operand" without special handling for List/Map. 
 Bean:set:PROPERTY:PROPERTYVALUE  sets the bean property named PROPERTY of "operand" to PROPERTYVALUE and returns "operand" itself. If the "operand" is a map or a list, then its value/element corresponding to the PROPERTY is set. 
 Bean:setProperty:PROPERTY:PROPERTYVALUE  sets the bean property named PROPERTY of "operand" to PROPERTYVALUE and returns "operand" itself without special List/Map handling. 
 Calendar:add:PROPERTY:VALUE  adds VALUE to given PROPERTY of "operand" as calendar and returns "operand" if successful, null otherwise 
 Calendar:get:PROPERTY  returns given PROPERTY of "operand" as calendar (e.g., "DAY_OF_WEEK" returns the day of week) 
 Calendar:monthly  returns monthly calendar nested map structure for given "operand" as today's date or calendar. The following monthly calendar properties are set: "numberOfWeeks" (number of weeks in this calendar), "week._" (list of week maps; e.g., "week._[0]" is the first week), "week._[n]._" (list of dates in week n), "week._[n].dayIndexStart" (inclusive start index of days of the month in week n; 0 except for the first week), "week._[n].dayIndexEnd" (inclusive end index of days of this month in week n; 6 except for the last week), "week._[n].dayIndexToday" (index of "today" in week n; empty if today is not in week n), "today.date" (today's date), "today.weekIndex" (index of week containing today), "today.dayIndex" (index of today in the week containing today), "lastDayOfMonth.date" (the last day of this month), "lastDayOfMonth.dayIndex" (index of the last day of the month in the last week), "firstDayOfMonth.date" (first day of month), "firstDayOfMonth.dayIndex" (index of the first day of the month in the first week), "nextMonth" (next month today), "lastMonth" (last month today), "nextYear" (next year today), "lastYear" (last year today), "thisWeek" (the week containing today). 
 Calendar:roll:PROPERTY:VALUE  "rolls" (changes without changing larger properties) given PROPERTY of "operand" as calendar by VALUE and returns "operand" if successful, null otherwise 
 Calendar:set:PROPERTY:VALUE  sets given PROPERTY of "operand" as calendar to VALUE and returns "operand" if successful, null otherwise 
 File:append:OUTFILE[:ENCODING]  appends "operand" as string to OUTFILE using given ENCODING (default is "iso-8859-1"). Returns "operand" if successful, otherwise null. 
 File:bean  creates an instance of a "file bean" (com.micronova.util.FileBean) from "operand" as an instance of File (java.io.File) or URI (java.net.URI). A "file bean" is a wrapper bean for File that supports the following bean properties: "fileObject" (underlying File object), "canRead" (boolean), "canWrite" (boolean), "exists" (boolean), "absolute" (boolean), "directory" (boolean), "file" (boolean), "hidden" (boolean), "absoluteFile", "absolutePath", "canonicalFile", "canonicalPath", "name", "parent", "path", "lastModified", "length", "uri", and "url". 
 File:close  closes "operand" (as InputStream/OutputStream/Reader/Writer) and returns "operand" 
 File:delete  deletes given file "operand". The "operand" can be either a directory name or a java.io.File object. Returns Boolean.TRUE if successful, null otherwise. 
 File:flush  flushes "operand" (as OutputStream or Writer) and returns "operand" 
 File:list  list files (as list of strings) in the given file directory "operand". The "operand" can be either a directory name or a java.io.File object. 
 File:listFiles  list files (as list of file objects) in the given file directory "operand". The "operand" can be either a directory name or a java.io.File object. 
 File:mkdir[:CREATEPARENTS]  makes directory named "operand". Returns "operand" if successful, otherwise null. If non-null CREATEPARENTS is given, then non-existing parent directories are created. 
 File:mkdirs  equivalent to "File:mkdir:true". 
 File:read[:ENCODING]  reads the content of given file "operand" as string using given ENCODING (default is "iso-8859-1"). 
 File:rename:NEWNAME  renames given file "operand" to NEWNAME. The "operand" can be either a directory name or a java.io.File object. Returns the renamed java.io.File object. 
 File:tempfile  returns a temporary file named according to the "operand" template. The "operand" is supposed to have the form of "prefix*suffix"; e.g., "TEMP*.dat" generates a temp file having prefix "TEMP" and suffix ".dat". If "prefix" contains a "/", then the substring up to the last "/" is taken as the directory specification (e.g., "/tmp/TEMP*.dat"). If "operand" is null, then "temp*" is used as the template. 
 File:type  returns mime type of the given file "operand". "operand" can be either a filename or a java.io.File object. 
 File:write:OUTFILE[:ENCODING]  writes "operand" as string to OUTFILE using given ENCODING (default is "iso-8859-1"). Returns "operand" if successful, otherwise null. 
 Format:date[:PATTERN:LOCALE]  formats "operand" date according to the PATTERN and LOCALE. PATTERN is a date format pattern used by java.util.SimpleDateFormat. LOCALE is either an instance of java.util.Locale or a map specifying a locale using properties "language", "country", and "variant". 
 Format:map:TYPE[:CONTROLMAP]  formats "operand" as NestedMap according to the TYPE ("xml"/"json"/"encoded"/"encodedSorted"; default "xml") with optional type-specific CONTROLMAP as in XMLMap:encode or JSON:encode. 
 Format:number[:PATTERN:LOCALE]  formats "operand" number according to the given PATTERN and LOCALE. PATTERN is a number format pattern used by java.util.NumberFormat. LOCALE is either an instance of java.util.Locale or a map specifying a locale using properties "language", "country", and "variant". 
 Format:paging  computes values for simple paging based on the following properties of the "operand" map: "numberOfItems" (total number of items to page through), "pageSize" (max number of items in each page), "itemIndex" (0-based index of an item to be included in the "current" page), "pageRadius" (number of pages before and after the "current" page to be shown if possible; 0 means the "current" page only, 1 means the "current" plus "previous" and "next", etc.). Returns a map with the following computed properties: "numberOfPages" (total number of pages), "pageIndex" (0-based index of the "current" page), "firstPageIndex" (0-based index of the first page; always 0), "lastPageIndex" (0-based index of the last page), "firstRadiusPageIndex" (index of the first radius page), "lastRadiusPageIndex" (index of the last radius page), "firstItemIndexOnPage" (item index of the first item on the current page), "lastItemIndexOnPage" (item index of the last item on the current page), "centerPageIndex" (index of the center of the radius) 
 GZIP:decode or GZIP:decompress  decompresses "operand" as GZIP-compressed string 
 GZIP:encode or GZIP:compress  compresses "operand" string using GZIP 
 Hex:decode  decodes "operand" as hexadecimal string 
 Hex:encode  encodes "operand" as hexadecimal string 
 JSON:decode[:CONTROLMAP]  converts "operand" string in JSON-like (Javascript) format to an object (Map/List/String). CONTROLMAP specifies a map with the following properties: "allowUnquoted" (default true; if set to false, then unquoted strings are not allowed). See JSON/Javascript for more information. 
 JSON:encode[:CONTROLMAP]  converts "operand" object into JSON-like (Javascript) format. CONTROLMAP specifies a map with the following properties: "quote" (string to be used as quote charater; default is doublequote), "doesEncode" (if true, special characters in a string are escaped using Backslash:encode; default is true). See JSON/Javascript for more information. 
 Javascript:compile  compiles "operand" string as Javascript. Returned object can used as "operand" in Javascript:eval. This requires "js.jar" from "http://www.mozilla.org/rhino/". See JSON/Javascript for more information. 
 Javascript:eval[:ENVMAP]  evaluates "operand" as Javascript and returns the result. key/value pairs in optional ENVMAP are set as global variable/value before evaluation. This requires "js.jar" from "http://www.mozilla.org/rhino/". See JSON/Javascript for more information. 
 Mime:decode  parses mime string of type "x/y;a=1 b=2;..." into a map of type "{type=x, subtype=y, parameter={a=1, b=2, ...}}". 
 Mime:encode  encodes parsed mime map of type "{type=x, subtype=y, parameter={a=1, b=2, ...}}" to string "x/y;a=1;b=2;...". The "subtype" field can be null. 
 NodeMap:decode[:CONTROL]  converts NestedMap structure into DOM Nodes. CONTROL can be either an instance of DOM document (for which DOM nodes are created) or a map with properties for DocumentBuilder used to create such a DOM document. See NodeMap for more information. 
 NodeMap:encode  converts XML Node into NestedMap structure with the following properties: "type" (node type), "value" (node value), "name" (node name), "attributes" (map of attributes), and "_" (list of children). See NodeMap for more information. 
 Number:abs  returns absolute number of "operand" as Double 
 Number:acos  returns arc cosine of "operand" 
 Number:asin  returns arc sine of "operand" 
 Number:atan  returns arc tangent of "operand" 
 Number:ceil  returns "ceiling" of "operand" as Long 
 Number:cos  returns cosine of "operand" 
 Number:e_  returns constant "e" (natural log base) 
 Number:exp  returns exp of "operand" 
 Number:floor  returns "floor" of "operand" as Long 
 Number:fromBinaryString  converts "operand" as binary string to Long. 
 Number:fromHexString  converts "operand" as hexadecimal string to Long. 
 Number:fromOctalString  converts "operand" as octal string to Long. 
 Number:log  returns log of "operand" 
 Number:max_:X:Y  returns max of X and Y as Double 
 Number:min_:X:Y  returns min of X and Y as Double 
 Number:pi_  returns constant "pi" 
 Number:pow_:X:Y  returns X to the power Y 
 Number:round  returns integer value closest to "operand" as Long 
 Number:sin  returns sine of "operand" 
 Number:sqrt  returns square root of "operand" 
 Number:tan  returns tangent of "operand" 
 Number:toBinaryString[:FILLER]  converts "operand" as number to binary string. If FILLER is given, then it is used to fill the digits to the left. 
 Number:toDegrees  converts "operand" in radians to degrees 
 Number:toHexString[:FILLER]  converts "operand" as number to hexadecimal string. If FILLER is given, then it is used to fill the digits to the left (e.g., FILLER="0000" to get a 4-digit hexadecimal number such as "0caf" instead of "caf"). 
 Number:toOctalString[:FILLER]  converts "operand" as number to octal string. If FILLER is given, then it is used to fill the digits to the left. 
 Number:toRadians  returns "operand" in degrees to in radians 
 Random:number  returns a random Double smaller than "operand" as number 
 Random:pick  picks one element randomly out of "operand" list. If the operand is not a list, picks one character out of the operand as string. The distribution can be controlled by having duplicates in "operand" (e.g., if "operand" is "0000000001", then "0" is returned 9 times more likely than "1") 
 Security:decrypt:KEY:ALGORITHM[:IV]  decrypts "operand" as string using given KEY and ALGORITHM (and IV if given). If given, IV is converted to a byte array as an ISO-8859-1 string. 
 Security:digest:ALGORITHM  computes digest of given "operand" string using ALGORITHM (e.g., "MD5", "SHA", etc.) 
 Security:encrypt:KEY:ALGORITHM[:IV]  encrypts "operand" as binary string using given KEY and ALGORITHM (and IV if given). KEY can be any binary string as long as its byte length matches the ALGORITHM. If given, IV is converted to a byte array as an ISO-8859-1 string. 
 Security:generateSecretKey_:ALGORITHM  generates a secret key for given encryption ALGORITHM (e.g., "AES", "DES", "DESede", "Blowfish", etc.) as binary string 
 Security:secureRandom:ALGORITHM  computes secure random bytes of given "operand" size using ALGORITHM (e.g., "SHA1PRNG"); if "operand" is not an integer, then it's byte length as string is taken as the size 
 String:append:STRING[:CONDITION]  appends STRING to "operand" (when CONDITION is true if given); operand can be a string or an instance of java.io.Writer or java.io.OutputStream. 
 String:capitalize  capitalizes "operand" string 
 String:countWords[:PATTERN]  creates frequency map (histogram) of "words" in the "operand" string separated by given PATTERN (default "[ ]+"); e.g., "{a=2, b=3}" for "a b b a b" 
 String:decompose:PATTERN[:GROUP]  returns a list of alternating non-PATTERN-matching and PATTERN-matching substrings in the "operand" string (i.e., "split" and "matchAll" mixed together). GROUP is 0 by default (meaning the whole matching PATTERN is returned if matched). The first and the last elements of the returned list are always non-matching (possibly empty) substrings. 
 String:escapeUnicode[:CONTROLMAP]  escapes characters in "operand" string to "\uxxxx" format. CONTROLMAP is a map or a form/json/xml-encoded string that specifies the following: "minCode" (smallest character code to be escaped; default 128), "maxCode" (largest character code to be escaped; default 65535) 
 String:fromByteArray:ENCODING  converts "operand" byte array to string using given ENCODING 
 String:fromCharacterList  converts "operand" list of Characters to string 
 String:indexOf:SUBSTRING  returns the first index of SUBSTRING in "operand", -1 if not found 
 String:join[:GLUE]  joins elements of "operand" (list or array) using GLUE string. Default GLUE is ",". 
 String:lastIndexOf:SUBSTRING  returns the last index of SUBSTRING in "operand", -1 if not found 
 String:match:PATTERN[:GROUP]  returns the first matching PATTERN GROUP in "operand" string, or null. GROUP is 0 by default (meaning the whole matching PATTERN). 
 String:matchAll:PATTERN[:GROUP]  returns list of all matching PATTERN GROUP's in "operand" string. GROUP is 0 by default (meaning the whole matching PATTERN). If GROUP is -1, then list of all matching groups is returned instead. 
 String:matchingGroups:PATTERN  returns a list of all matching PATTERN groups in "operand" string (0-th element is the whole expression, 1-st element is the first matching group, etc.). Returns empty list if "operand" does not match PATTERN. 
 String:multiply:COUNT  multiplies "operand" string COUNT times 
 String:prepend:OBJECT[:CONDITION]  prepends OBJECT to "operand" string (when CONDITION is true if given); if OBJECT is an instanceof java.io.Writer or java.io.OutputStream, then operand is append to OBJECT. 
 String:replace:SUBSTRING[:WITH]  replaces all SUBSTRING in "operand" string with WITH (default "") 
 String:replaceAll:PATTERN[:WITH]  replaces all given regular expression PATTERN in "operand" string with WITH (default "") 
 String:replaceCharacters:FROM:TO  replaces all characters in FROM with corresponding characters in TO 
 String:replaceFirst:PATTERN[:WITH]  replaces first given regular expression PATTERN in "operand" string with WITH (default "") 
 String:reverse  reverses "operand" string 
 String:splitCSV[:CONTROLMAP]  splits "operand" string into a List of lists (rows of columns) of strings as CSV (comma-separated) format. CONTROLMAP can be a map or form/json/xml-encoded string that specify the following properties: "separator" (default ','), "quote" (default '"' (double quote), "newline" (default '\n'). 
 String:split[:PATTERN]  splits "operand" string into a List at given regular expression PATTERN (default ",") 
 String:substring:STARTINDEX[:ENDINDEX]  gets substring of "operand" starting at STARTINDEX (ending at ENDINDEX if given). If STARTINDEX or ENDINDEX is negative, then it is counted from the end (e.g., "String:substring:-2" means the last two characters). 
 String:toByteArray:ENCODING  converts "operand" as string to byte array using given ENCODING 
 String:toCharacterList  converts "operand" string to list of Characters 
 String:toCharacterArray  converts "operand" string to character array (char[]) 
 String:toLowerCase  converts "operand" string to lower case 
 String:toUpperCase  converts "operand" string to upper case 
 String:trim  removes whitespaces from both sides of "operand" string 
 System:createCache  creates a cache (instance of java.util.LinkedHashMap) according to the specification by "operand" map or form/json/xml-encoded string. Currently supports the following properties: "initialCapacity" (default 16), "loadFactor" (default 0.75), "maxSize" (default 16), "type" (either "LRU" (default) or "FIFO"). If also "lifespan" is specified, then hits older than given "lifespan" milliseconds are automatically invalidated and removed from cache. 
 System:currentTimeMillis_  returns current system time in milliseconds 
 System:currentTime_  returns current time as java.util.Date 
 System:deserialize  deserializes "operand" as binary (ISO-8859-1) string 
 System:getProperties_  returns map of current system properties 
 System:getStatic[:VARIABLE]  returns the value of a static java variable. The operand can be either a fully qualified name (e.g., "java.awt.Color.GREEN") or a class instance or name (e.g., "java.awt.Color") if VARIABLE is given separately (e.g., "GREEN") 
 System:invoke  dynamically invokes a java method. The "operand" must be a nested map with the following properties: "object" (target object to invoke a method on), "class" (name of the class to invoke a static method of), "method" (name of the method to invoke; "*" means a constructor, and a string starting with a "." means a fieldname), "_" (list of arguments where each element is a map of type {type=xxx, value=xxx} or an object if argument type is equal to the type of the given object). Argument types can be fully qualified class name, class names without "java.lang." prefix such as "String", primitive types such as "int", "float" etc.., or arrays of these such as "String[]". See Dynamic Method Invocation below for more information. 
 System:notifyAll:SIGNAL  notifies all threads waiting (using System:wait). If "operand" is a string, then an internal lock uniquely identified by the string is used as lock, and SIGNAL object is sent to all waiting threads. Otherwise the "operand" object is used as lock, and SIGNAL is ignored. 
 System:resetUniqueId_[:NAME]  resets next unique Id to 0 for given NAME (default "") 
 System:serialize  serializes "operand" into binary (ISO-8859-1) string 
 System:setProperties  sets system properties specified by "operand" map as key/value pairs 
 System:sleep  suspends current thread for given "operand" number of milliseconds 
 System:uniqueId_[:NAME]  returns a unique integer (incremented each time called by any thread) for given NAME (default "") 
 System:wait:WAITTIME  waits for up to WAITTIME milliseconds (0 meaning forever) for notification (given by System:notifyAll). If "operand" is a string, then an internal lock uniquely identified by the string is used as lock, and returns the SIGNAL object sent by System:notifyAll if not timed out, or null. Otherwise, the given "object" itself is used as a lock and returned. 
 System:yield_  yields current thread 
 Thread:currentThread_  returns current thread 
 Thread:interrupt  interrupts given "operand" as Thread 
 Thread:join:WAIT  waits for WAIT milliseconds until given "operand" as Thread stops 
 Thread:maxPriority_  returns thread max priority 
 Thread:minPriority_  returns thread min priority 
 Thread:normPriority_  returns thread normal priority 
 Thread:resume  resumes given "operand" as Thread (deprecated) 
 Thread:sleep  equivalent to System:sleep 
 Thread:stop  stops given "operand" as Thread (deprecated) 
 Thread:suspend  suspends given "operand" as Thread (deprecated) 
 Thread:yield_  equivalent to System:yield 
 Type:add:OBJECT  adds OBJECT to "operand" collection if possible 
 Type:addAll:OBJECT  adds all elements of OBJECT (array or collection) to "operand" collection 
 Type:charAsNumber  returns numeric code for "operand" as a Character 
 Type:clear  clears "operand" as a collection 
 Type:clone  invokes "clone()" method on "operand" if defined 
 Type:countElements  returns histogram of elements in "operand" as Collection or Array, or null 
 Type:forName  returns class object for given "operand" class name, if possible. 
 Type:ifContained:LIST[:DEFAULT]  returns "operand" if "operand" is contained in LIST (or comma-separated string); otherwise returns DEFAULT (default null) 
 Type:ifContains:OBJECT[:ELSEOBJECT]  returns "operand" if OBJECT is contained in "operand" collection, ELSEOBJECT (default null) otherwise 
 Type:ifContainsAll:OBJECT[:ELSEOBJECT]  returns "operand" if all elements of OBJECT (array or collection) are contained in "operand" collection, ELSEOBJECT (default null) otherwise 
 Type:ifEmpty:IFOBJECT[:ELSEOBJECT]  returns IFOBJECT if given "operand" is empty, otherwise ELSEOBJECT (default "operand") 
 Type:ifEqual:TARGET:IFOBJECT[:ELSEOBJECT]  returns IFOBJECT if given "operand" is equal to TARGET, otherwise ELSEOBJECT (default "operand") 
 Type:ifNotEmpty:IFOBJECT  returns IFOBJECT if given "operand" is not empty, otherwise "operand" 
 Type:ifNotEqual:TARGET:IFOBJECT  returns IFOBJECT if given "operand" is not equal to TARGET, otherwise "operand" 
 Type:ifNotNull:IFOBJECT  returns IFOBJECT if "operand" is not null, otherwise null 
 Type:ifNull:IFOBJECT[:ELSEOBJECT]  returns IFOBJECT if "operand" is null, otherwise ELSEOBJECT (operand by default) 
 Type:indexOfSubList:SUBLIST  returns the first index of the SUBLIST in the "operand" list, -1 if not found, or null if either "operand" or "SUBLIST" is not a list. 
 Type:isArray  converts "operand" to Array if possible, otherwise null 
 Type:isBoolean  converts "operand" to Boolean if possible, otherwise null 
 Type:isByte  converts "operand" to Byte if possible, otherwise null 
 Type:isCalendar[:PATTERN:LOCALE:TIMEZONE]  converts "operand" to calendar (instance of java.util.Calendar) if possible, as in Type:isDate. 
 Type:isCharacter  converts "operand" to Character if possible, otherwise null 
 Type:isClass:CLASS  converts "operand" to an instance of given CLASS if possible, otherwise null. CLASS can be either a class object or a class name. If CLASS is a primitive wrapper ("java.lang.Integer" etc.), then "operand" is converted to given CLASS. Otherwise, returns "operand" only when it is an instance of given CLASS. 
 Type:isDate[:PATTERN:LOCALE:TIMEZONE]  converts "operand" to date if possible (according to the given PATTERN and LOCALE if "operand" needs parsing, and TIMEZONE), otherwise null. PATTERN is a date format pattern string used by java.util.SimpleDateFormat. LOCALE is either an instance of java.util.Locale or a map specifying a locale using properties "language", "country", and "variant". TIMEZONE is either an instance of java.util.TimeZone or a map or form/json/xml-encoded string specifying a timezone using "id" property. 
 Type:isDouble  converts "operand" to Double if possible, otherwise null 
 Type:isEmpty  returns Boolean.TRUE if given "operand" is empty, otherwise null 
 Type:isEqual:TARGET  returns Boolean.TRUE if given "operand" is equal to TARGET, otherwise null 
 Type:isFile[:PARENTFILE]  converts "operand" as filename or URI to file (instance of java.io.File) if possible (as a child of PARENTFILE if given), otherwise null. 
 Type:isFloat  converts "operand" to Float if possible, otherwise null 
 Type:isInternetAddress  converts "operand" to valid internet email address (instance of javax.mail.internet.InternetAddress) if possible, otherwise null 
 Type:isList  converts "operand" to List if possible, otherwise null 
 Type:isLocale  converts "operand" to locale (instance of java.util.Locale) if possible. The "operand" can be a map specifying a locale using properties "language", "country", and "variant". 
 Type:isLong  converts "operand" to Long if possible, otherwise null 
 Type:isNestedMap  converts "operand" to a NestedMap if possible (as "__source" property), otherwise null 
 Type:isNotEmpty  returns Boolean.TRUE if given "operand" is not empty, otherwise null 
 Type:isNotEqual:TARGET  returns Boolean.TRUE if given "operand" is not equal to TARGET, otherwise null 
 Type:isNotNull  returns Boolean.TRUE if "operand" is not null, otherwise null 
 Type:isNull  returns Boolean.TRUE if "operand" is null, otherwise null 
 Type:isNumber[:PATTERN:LOCALE]  converts "operand" to number if possible (accordin to the given PATTERN and LOCALEP if "operand" needs parsing), otherwise null. PATTERN is a number format pattern used by java.util.NumberFormat. LOCALE is either an instance of java.util.Locale or a map with with properties "language", "country", "variant". 
 Type:isSQLDate[:PATTERN:LOCALE]  converts "operand" to SQL date if possible, as in Type:isDate, otherwise null. 
 Type:isSQLTime[:PATTERN:LOCALE]  converts "operand" to SQL time if possible, as in Type:isDate 
 Type:isSQLTimestamp[:PATTERN:LOCALE]  converts "operand" to SQL timestamp if possible, as in Type:isDate 
 Type:isShort  converts "operand" to Short if possible, otherwise null 
 Type:isString  converts "operand" to String if possible, otherwise null 
 Type:isStringArray  converts "operand" to an array of Strings (String[]) if possible, otherwise null 
 Type:isStringList[:SEPARATOR:ESCAPE]  converts "operand" to List if possible, otherwise null like Type:isList, but when "operand" is a string, then splits it into a list of Strings using SEPARATOR (default ",") and ESCAPE (default "\"). 
 Type:isTimeZone  converts "operand" to TimeZone (instance of java.util.TimeZone) if possible. The "operand" can be a map or form/json/xml-encoded string specifying a timezone by "id" propery (e.g., "id=JST" for Japan standard timezone) 
 Type:isURI  converts "operand" to URI (instance of java.net.URI) if possible, otherwise null 
 Type:isURL[:CONTEXT]  converts "operand" to URL (instance of java.net.URL) if possible (within CONTEXT URL, if given), otherwise null 
 Type:lastIndexOfSubList:SUBLIST  returns the last index of the SUBLIST in the "operand" list, -1 if not found, or null if either "operand" or "SUBLIST" is not a list. 
 Type:length  returns the length of "operand" (collection or array) if possible, otherwise -1 
 Type:makeSynchronized  returns the synchronized version of given "operand" Collection 
 Type:makeUnmodifiable  returns the unmodifiable version of given "operand" Collection 
 Type:numberAsChar  returns Character for given numeric code "operand" 
 Type:numericValueOf  returns numeric value of given "operand" as a Character 
 Type:remove:OBJECT  removes OBJECT from "operand" collection 
 Type:removeAll:OBJECT  removes all elements of OBJECT (array or collection) from "operand" collection 
 Type:retainAll:OBJECT  retains all elements of OBJECT (array or collection) in "operand" collection 
 Type:reverse  reverses "operand" as a list 
 Type:rotate:DISTANCE  rotates "operand" by DISTANCE as a list 
 Type:shuffle  shuffles "operand" as a list 
 Type:subList:START[:END]  returns the sublist of the "operand" list, starting at START (inclusive) and ending at END (exclusive; default end of list). START/END can be negative to indicate relative index from the end (e.g., -1 as START means last element) 
 Type:unicodeBlockOf  returns unicode block of given "operand" as a Character 
 URL:decode[:ENCODING]  decodes "operand" as URL-encoded string using ENCODING (default "utf-8") 
 URL:encode[:ENCODING]  encodes "operand" as String in URL format (e.g., "a b" becomes "a+b") using ENCODING (default "utf-8") 
 URL:nameType  guesses content type of the given "operand" as URL string 
 URL:normalize  normalizes "operand" as URI 
 URL:relativize:BASE  relativizes "operand" as URI against BASE 
 URL:resolve:BASE  resolves "operand" as URI against BASE 
 URL:streamType  guesses content type of the given "operand" as byte stream 
 XML:appendNode:PARENT  appends "operand" Node as the last child of PARENT Node; "operand" can be a NodeMap
 XML:cloneNode  makes shallow copy of "operand" Node. 
 XML:copyNode  makes deep copy of "operand" Node. 
 XML:decode  un-escapes special tag characters from "operand" as String (>, <;, &, ", ', { (&#123;), and } (&#125;)) 
 XML:encode[:PATTERN]  escapes special tag characters from "operand" as string (>,<,&,",',{ (&#123;), and } (&#125;)). You can specify a subset of supported characters to be encoded by giving matching PATTERN regular expression (e.g., "[<>]" to encode "<" and ">" only). If PATTERN is "~" (tilda), then "[&<>]" is assumed. 
 XML:getAttribute:NAME  gets the value of an attribute named NAME of "operand" Element Node. 
 XML:insertNodeBefore:BEFORE  inserts "operand" Node before BEFORE Node; "operand" can be a NodeMap
 XML:normalizeNode  normalized "operand" Node. 
 XML:output[:CONTROLMAP]  outputs "operand" as XML document according to the specifications given by CONTROLMAP (map or encoded string). The CONTROLMAP supports standard XSLT output attributes ("method", "version", "encoding", "omit-xml-declaration", "standalone", "doctype-public", "doctype-system", "cdata-section-elements", "indent", and "media-type"). Default is "omit-xml-declaration=yes". 
 XML:parseHtml[:CONTROLMAP]  parses HTML as in <m:parseHtml>. CONTROLMAP supports attributes of <m:parseHtml>: "include", "exclude", "strategy", and "rootName". 
 XML:parse[:CONTROLMAP]  parses "operand" as XML document according to the specifications given by CONTROLMAP (map or encoded string). The CONTROLMAP supports the following boolean properties: "namespaceAware", "ignoringComments", "ignoringElementContentWhitespace", "expandEntityReferences", "coalescing", and "validating". Default value is "true" except "validating". 
 XML:removeNode  removes "operand" Node from its parent 
 XML:replaceNode:OLD  replaces OLD Node with "operand" Node; "operand" can be a NodeMap
 XML:setAttribute:NAME:VALUE  sets the value of an attribute named NAME of "operand" Element Node to VALUE. 
 XMLMap:decode[:CONTROLMAP]  converts "operand" XML document into NestedMap according to the specifications given by CONTROLMAP (map or encoded string). See XMLMap below for more information. 
 XMLMap:encode[:CONTROLMAP]  outputs "operand" NestedMap as XML document according to the specifications given by CONTROLMAP (map or encoded string). See XMLMap below for more information. 
 Zip:decode or Zip:decompress  decompresses "operand" as Zip-compressed string 
 Zip:encode or Zip:compress  compresses "operand" string using Zip 
 _JSP:applyCodec:CODEC  applies CODEC to the "operand" 
 _JSP:applyFilter:CONTROLMAP  applies filter defined by CONTROLMAP to the "operand" as in <m:filter>. CONTROLMAP supports the following properties: "include", "break", "apply", "applyCodec". 
 _JSP:binarySearch:KEY[:CONTROLMAP]  performs binary search for KEY on a sorted "operand" list, optionally using CONTROLMAP which specifies the following: "codec" (codec to be applied to each element of list before comparison), "equal" (codec that returns "true" when two elements "${_operand.x}" and "${_operand.y}" are considered equal), "more" (codec that returns "true" when "${_operand.x}" is considered greater than "${_operand.y}"). Returns a non-negative integer index of KEY in the "operand" list if found, otherwise a negative integer (which is equal to "-(index + 1)" where "index" is the index for KEY to be inserted), as in java.util.Collections.binarySearch. 
 _JSP:call:CONTROLMAP  calls a JSP as in <m:call> tag with "operand" parameter map. CONTROLMAP is a map or encoded string that specifies the following: "path" (path of JSP; mandatory), "contextPath" (context path of JSP for cross-context call; optional), "doesForward" (forwards if set to true) 
 _JSP:clear  clears "operand" (or pageContext.out if not given) as JspWriter and returns given "operand" 
 _JSP:dynamicIterator  creates a dynamic iterator that calls codecs specified by "hasNextCodec" and "nextCodec" properties of the "operand" map on "hasNext" and "next" operations. The specified codecs are called with the dynamic iterator itself as the "operand". The following special properties are supported: "collection" (collection to iterate over), "next" (next object to be returned, overriding "collection" or "nextCodec"), "hasNext" (boolean value that specifies if the iterator has more objects, overriding "collection" or "hasNextCodec"), "index" (0-based index which is increased each "next" operation). See Dynamic Iterator below for more information. 
 _JSP:dynamicMap  creates a dynamic map that calls codecs specified as "getCodec" and "putCodec" properies of the "operand" map on "get" and "put" operation. The specified codecs are called with the dynamic map itself as the "operand" having "key" and "value" set to the key and value of the get/set operation. The "_" (underscore) variable is also set to the "operand" map upon codec invocation. See Dynamic Map below for more information. 
 _JSP:encodeRedirectURL  encodes "operand" as redirect URL including session ID if necessary 
 _JSP:encodeURL  encodes "operand" as URL including session ID if necessary 
 _JSP:eval[:CONTROLMAP]  evaluates "operand" as EL (or a map of EL values) according to the specifications given by CONTROLMAP (map or encoded string). The CONTROLMAP supports the following properties: "pattern", "recursive", "evalCodec", "keyCodec", "allowGroup", "environment", as in <m:eval> tag below. 
 _JSP:flush  flushes "operand" (or pageContext.out if not given) as Writer and returns given "operand" 
 _JSP:getAttributes[:SCOPENAME]  returns a map containing variable/value pairs for given list of variables in given SCOPENAME (default = page). The "operand" can be a list/arrary or a comma-separated list of variable names. If "operand" is null (or called as _JSP:getAttributes_), then all variables currently in the given scope are copied. 
 _JSP:getLogWriter  creates an instance of LogWriter (java.io.Writer that writes to a specified log) according to "operand" as map (or encoded string); properties supported are "destination", "category", and "log" as in <m:log> tag. 
 _JSP:getMessageMap_[:]  creates a resource message map, optionally from a map that specifies the following properties: "baseName" (resource base name), "defaultValue" (default value to be returned when resource is missing). See MessageMap below for more information. 
 _JSP:getMimeType  returns mime type of "operand" as filename 
 _JSP:getPageScope_[:INDEX]  returns pageScope variables for the entry in the callStack at given INDEX (default 0) as a writable map. This can be used to get/set variables in given pageScope (especially outer pageScope from within a tag file) 
 _JSP:getRealPath  returns real filesystem path of "operand" as URL 
 _JSP:getResource  returns resource URL of "operand" as path 
 _JSP:hasSession_  returns current session as HttpSessionMap (session attributes as a map), or null if none. 
 _JSP:invalidateSession_  invalidates current session and returns null. 
 _JSP:isCalled_  returns true if this JSP is "called" by another JSP. 
 _JSP:log[:CONTROLMAP]  logs "operand" according to the specification by optional CONTROLMAP. CONTROLMAP is a map or form/json/xml-encoded string that specifies the following properties as in <m:log> tag: "destination", "category", and "log"; Default is "destination=log". 
 _JSP:max[:CONTROLMAP]  returns the largest element in the "operand" as Collection using CONTROLMAP which can specify the following: "codec" (codec to be applied to each element of list before comparison), "equal" (codec that returns "true" when two elements "${_operand.x}" and "${_operand.y}" are considered equal), "more" (codec that returns "true" when "${_operand.x}" is considered greater than "${_operand.y}") 
 _JSP:min[:CONTROLMAP]  returns the smallest element in the "operand" as Collection using CONTROLMAP which can specify the following: "codec" (codec to be applied to each element of list before comparison), "equal" (codec that returns "true" when two elements "${_operand.x}" and "${_operand.y}" are considered equal), "more" (codec that returns "true" when "${_operand.x}" is considered greater than "${_operand.y}") 
 _JSP:newSession_  invalidates current session and returns a new session as HttpSessionMap (session attributes as a map). 
 _JSP:putAttributes[:SCOPENAME]  puts all key/value pairs in the "operand" map as variable/value pairs for given SCOPENAME (default = page) 
 _JSP:setResponse:CONTROLMAP  sets HTTP response according to properties defined by CONTROLMAP (map or form/json/xml-encoded string) as in <m:response> tag ("header", "error", "errorMessage", "redirect", "cookie") and returns "operand" as-is. 
 _JSP:setRootPageContext_[:OVERWRITE]  saves current pageContext in requestScope as the pageContext of the root element of the call stack. If OVERWRITE is true, then always overwrites, otherwise overwrites only when current value is null. Returns the pageContext before replacement. 
 _JSP:sort[:CONTROLMAP]  sorts "operand" as list using CONTROLMAP which specifies the following: "codec" (codec to be applied to each element of list before comparison), "equal" (codec that returns "true" when two elements "${_operand.x}" and "${_operand.y}" are considered equal), "more" (codec that returns "true" when "${_operand.x}" is considered greater than "${_operand.y}") 

In Java terms, a codec is any public static method that takes one or more Object arguments and returns an Object; i.e., of the following type:

public static Object codec(Object operand, Object opt1, Object opt2, ...)

Built-in codecs are defined in com.micronova.util.codec package using "Codec" classname prefix. For example, "URL:encode" calls the static method "encode" defined in "com.micronova.util.codec.CodecURL".

You can call your own external codec by using fully qualified class name; e.g., a codec named "abc" in package "com.yourcompany.codec" can be specified by "com.yourcompany.codec:abc".

If necessary, you can call a codec with "implicit" arguments of the following type:

public static Object codec(Object implicit1, Object implicit2, ..., Object operand, Object opt1, Object opt2, ...)

Implicit arguments can be specified using the following syntax:

CODECCLASS:CODECNAME;implicit1;implicit2;...:opt1:opt2:...

If CODECCLASS starts with an "_" (underscore), then current pageContext is passed implicitly; e.g., "_JSP:eval" is equivalent to "JSP:eval;@{pageContext}".

4 Local Variables

All YUZU tags can have tag body. If you need temporary variables to hold values within the tag body, you can use local attribute to specify a comma-separated (or space/tab/newline-separated) list of variables that are "local" to the tag body. The value of a "local" variable is saved before evaluating the tag body, and restored once tag body evaluation is done.

The following outputs "a=AAA, b=BBB" and then "a=A, b=B":

<m:set var="a" value="A"/>

<m:set var="b" value="B"/>

<m:out local="a,b">
  <m:set var="a" value="AAA"/>
  <m:set var="b" value="${a}"/>
  <m:out value="a=${a}, b=${b}"/>
</m:out>

<m:out value="a=${a}, b=${b}"/>

The scope of "local" variables is "page" by default, but can be specified by using localScope attribute.

There is a special request-scoped local variable named "_" (underscore) that holds the prepared tagvalue within the body of a YUZU tag. For example:

<m:set var="map" className="java.util.HashMap">
  <m:set property="a" value="A"/>
  <m:set property="b" value="B"/>
  <m:set property="c" value="${_.a}${_.b}"/>
</m:set>

<m:out value="${map.c}"/>

outputs "AB".

The "_" variable reflects changes made by using attribute attribute. For example:

<%-- this outputs "a-a/a-a" --%>

<m:out value="a">

  <%-- resets tagValue to "a-a" --%>

  <m:set attribute="value" value="${_}-${_}"/>

  <%-- resets tagValue to "a-a/a-a" --%>

  <m:set attribute="value" value="${_}/${_}"/>

</m:out>

When localScope is set in a Yuzu tag, then it is used as the default variable scope by descendent Yuzu tags. For example:

<m:set localScope="request">
  <%-- this uses "request" scope --%>
  <m:set var="x" value="XXX"/>
  <%-- this uses "page" scope --%>
  <m:set var="y" value="YYY" scope="page"/>
</m:set>

Wnen necessary, you can use a map (or a XML/JSON/form-encoded string) as the value of local attribute to initialize local variables (keys are used as variable names, and values are used as initial values). For example:

<m:set var="a" value="alpha"/>
<m:set var="b" value="beta"/>

<m:out local="{a:1, b:2}">
  a is ${a}, b is ${b}       
</m:out>

a is ${a}, b is ${b}

outputs "a is 1, b is 2" followed by "a is alpha, b is beta".

5 Common Attributes

All YUZU tags support the following attributes, unless otherwise specified (those of type "EL" are strings where "@{" are translated to "${"):

 name  type  description  default value 
 value  Object  tagValue to be set  null 
 className  EL  name of java class of which the tagValue is instantiated  null 
 source  Object  object or map to be used to initialize the tagValue  null 
 var  String  name of variable to which the tagValue is assigned  null 
 scope  String  scope of var variable (either "page", "request", "session", or "application")  page 
 target  Object  target Java bean whose property is to be set  null 
 property  String  name of the target bean property to be set  null 
 attribute  String  name of the tag attribute to be set  null 
 default  Object  default value to be used as tagValue when the tagValue is null (or when test EL expression evaluates to false)  null 
 test  EL  boolean EL expression that evaluates to false to make default used  null 
 prepareCodec  EL  codecs to be applied to the tagValue before processing the tag body  null 
 importCodec  EL  codecs to be applied to the tag body on importing  null 
 processCodec  EL  codecs to be applied to processed tagValue  null 
 codec  EL  alias for processCodec  null 
 assignCodec  EL  codecs to be applied to the value to be assigned (value of assign EL expression; default is the tagValue itself)  null 
 exportCodec  EL  codecs to be applied to the value to be exported (value of export EL expression; default is the tagValue itself)  null 
 cleanupCodec  EL  codecs to be applied to the prepared tagValue on cleanup; exceptions are ignored  null 
 assign  EL  EL expression that defines object to be assigned (with underscore (_) representing the tagValue itself)  ${_} (tagValue itself) 
 export  EL  EL expression that defines object to be exported (with underscore (_) representing the tagValue itself)  ${_} (tagValue itself) 
 doesExport  String  forces export if set to "always"  default 
 local  String  comma-separated list of page-scoped variables that are "local" for this tag (saved on tag entry, restored on exit)  null 
 localScope  String  scope of local variables, and also default scope for its descendents  null (meaning "page" scope) 

6 Tag Stacking

All YUZU tags can be nested beyond tag file boundaries (called "stackable" in this document) using a request-scope tag stack. For example, if you have a tag "<u:url>" defined by a tag file like this (tag definitions are omitted):

<%@ tag %>
<%@ attribute name="prefix" %>
<%@ attribute name="target" type="java.lang.Object"%>
<%@ attribute name="property" %>
<%@ attribute name="attribute" %>

<m:map target="${target}" property="${property}" attribute="${attribute}" 
    codec="Bean:get:@param.__encodedSorted|String:prepend:?:@{!empty _operand}|String:prepend:@{_.prefix}">
  <m:set property="prefix" value="${prefix}"/>
  <m:map property="param">       
    <jsp:doBody/>
  </m:map>
</m:map>

then you can output a "url" like this:

<%-- this outputs "http://www.micronova.com?a=alpha&b=beta" --%>

<m:map var="ENV"/>

<u:url target="${ENV}" property="url" prefix="http://www.micronova.com">
  <m:set property="a" value="alpha"/>
  <m:set property="b" value="beta"/>
</u:url>

${ENV.url}

Note that the <m:set> statements above use the map defined by the <m:map> tag surrounding <jsp:doBody/> in the tag file as their target.

When necessary, it is possible to get a shallow copy of the current tag stack by using "_JSP:getCallStack_" codec. "_JSP:getCallStack_" returns a list of tag stack entries with the closest ancestor first, and each tag stack entry is a map with the following properties: 'tag' (instance of YUZU tag object), 'pageContext' (value of pageContext for the tag), and optionally 'hidden' (if set to true, then the tag is ignored when searching for ancestor YUZU tags). Other properties can be set for custom usage - for example:

<%-- 
  't:swallow' tag marks itself hidden and also 'swallow' 
  so 't:output' tag can find in the tag stack
--%>

<%@ tag %><m:out export="" value="${pageContext.out}">
  <m:set var="stackEntry" prepareCodec="_JSP:getCallStack_|Bean:get:0">
    <m:set property="hidden" value="true"/>
    <m:set property="swallow" value="true"/>
  </m:set>        
  <jsp:doBody/>
</m:out>

<%-- 
  't:output' tag finds the closest ancestor with 'swallow' mark 
  and appends the output to it's value (pageContext.out) 
--%>

<%@ tag %>
<%@ attribute name="value" %>
<%@ attribute name="codec" %>
<m:set var="output" value="${value}" assignCodec="${codec}"><jsp:doBody/></m:set>
<m:filter var="swallowTag" prepareCodec="_JSP:getCallStack_" include="@{_element['swallow'] == true}" assignCodec="@{_[0].tag}"/>
<m:set target="${swallowTag}" property="value" value="${swallowTag.value}" codec="String:append:@{output}"/>

<%-- 
  then the following code outputs 'Hello' and 'World' followed by newlines 
--%>

<t:swallow>
  This is not output
  <t:output codec="Backslash:decode">Hello\n</t:output>
  This is not output
  <t:output codec="Backslash:decode">World\n</t:output>
  This is not output
</t:swallow>

The last element of the list returned by _JSP:getCallStack_ is a map with only one property 'pageContext'. The value of 'pageContext' of this last entry is the value of a request scope variable set by "_JSP:setRootPageContext_" codec (to be the value of the PageContext when this is called). This can be used to access outer pageScope variables from within a tag file. For example, using "_JSP:getPageScope_" codec which returns pageScope variables in the pageContext at 0-th index in the call stack:

<%-- with a tag file name "t:test" (tag file declarations are omitted): --%>
<m:set var="env" codec="_JSP:getPageScope_"/>
<m:set export="" value="${env}">
  <m:set property="P" value="${_.P}${_.P}"/>
  <m:set property="Q" value="q"/>
</m:set>
<jsp:doBody/>

<%-- this JSP outputs:
  outside before: a, b
  inside:aa, q
  outside after:aa, q
--%>
<m:set export="" codec="_JSP:setRootPageContext_"/>
<m:set var="P" value="a"/>
<m:set var="Q" value="b"/>
outside before: ${P}, ${Q}
<t:test>
  inside:${P}, ${Q}
</t:test>
outside after:${P}, ${Q}

"_JSP:setRootPageContext_" by default does not overwrite existing PageContext value, and can be put in a JSP prelude code.

7 Tags

YUZU consists of the following tags:

 name  summary 
 set  sets a variable 
 out  outputs a value 
 map  nested map structure 
 eval  evaluates expression 
 param  structured form parameters 
 call  subroutine call 
 return  returns a value to the caller 
 value  modifies the tagValue of the parent 
 include  http client 
 log  server log output 
 postData  posted HTTP data 
 throw  throws an Exception 
 synchronized  synchronizes tag processing 
 query  SQL query with embedded expressions 
 update  SQL update with embedded expressions 
 transaction  stackable SQL transactions 
 response  HTTP response control 
 system  system call 
 filter  filters elements 
 parseHtml  parses HTML document 
 mail  sends email 
 mailFolder  retrieves email 

7.1 set

<m:set> is an enhanced version of JSTL <c:set>. It supports all common attributes, and serves as the default YUZU tag (with no processing done in Process stage)

7.2 out

<m:out> is an alias for <m:set> without support for the following assignment-related attributes: var, scope, target, property, attribute, assign, assignCodec.

7.3 map

<m:map> is a tag with a map (key-value pairs) prepared as its tagValue. For a given key, you can get/set its value using the key as the map's "property". For Example,

<%-- initializes a map --%>

<m:map var="x"/>

<m:set target="${x}" property="a" value="alpha"/>

<m:out value="a is ${x.a}"/>

outputs the following:

a is alpha

You can "nest" maps as follows:

<%-- setting up a nested map --%>

<m:map var="account">
  <m:set property="balance" value="100"/>
  <m:map property="customer">
    <m:set property="firstName" value="John"/>
    <m:set property="lastName" value="Doe"/>
  </m:map>
</m:map>

then, the following:

Account Balance of <m:out value="${account.customer.firstName} ${account.customer.lastName}"/>
is <m:out value="${account.balance}"/>.

outputs:

Account Balance of John Doe is 100.

You can set the "firstName" in the above example in the following two ways:

<%-- using "account.customer" as the target --%>

<m:set target="${account.customer}" property="firstName" value="Johnny"/>

<%-- or, using a "nested property name" starting with "@" --%>

<m:set target="${account}" property="@customer.firstName" value="Johnny"/>

If a property name starts with "@", then the rest of the propery name is taken as nested property specification using "dot" notation. Note that if "@" is omitted, then the whole string "customer.firstName" (including the "dot") is taken as the property name (if necessary, you can start the property name with "#" to force the rest to be taken as a non-nested property name, even if it starts with "@").

When a nested property name is used, then non-existent intermediate maps are automatically created. For example, the nested map above can also be created as follows:

<%-- setting up a nested map using nested properties--%>

<m:map var="account">
  <m:set property="@balance" value="100"/>
  <m:set property="@customer.firstName" value="John"/>
  <m:set property="@customer.lastName" value="Doe"/>
</m:map>
In this case, the intermediate map "${account.customer}" is created by the <m:map> tag automatically and initialized as "{firstName=John, lastName=Doe}".

There is a special propery named "__encoded" (with two underscores) which can be used to convert a nested map to/from form-style encoded string ("x=a&y=b&..."). The following code:

<m:out value="${account.__encoded}"/>

outputs:

balance=100&customer.lastName=Doe&customer.firstName=John

You can use "__encodedSorted" instead of "__encoded" to sort the output by key when necessary.

There also are special properties for other formats: "__json" for JSON-style output, "__xml" for XML output, "__dom" for DOM output, "__css" for single-level CSS-style output ("key:value;key:value;..."), and "__attrList" for XML attribute list output (with submaps output using __css output). For example:

<m:out value="${account.__json}"/>

<%-- this outputs: 

{"balance":"100","customer":{"firstName":"John","lastName":"Doe"}}

 --%>

<m:out value="${account.__xml}"/>

<%-- this outputs: 

<root><balance>100</balance><customer><firstName>John</firstName><lastName>Doe</lastName></customer></root> 

--%>

<m:out value="${account.__css}"/>

<%-- this outputs:

balance:100;customer:{firstName=John, lastName=Doe}

--%>

<m:out value="${account.__attrList}"/>

<%-- this outputs (with a leading space):

 balance="100" customer="firstName:John;lastName:Doe;"

--%>

You can also set up a map from a form-style string as follows:

<m:map var="account">
  <m:set property="__encoded">
    balance=100&customer.lastName=Doe&customer.firstName=John
  </m:set>
</m:map>

and other output properties such as "__json", "__xml", or "__dom" can be used to set up a map from corresponding output.

Each map also has a special element named "_" (single underscore) which is a sparse list ("sparse" means that you can set elements at any index in any order). You can set elements of the "_" list using "@_.property" where "property" is an integer, "*", or "*" followed by an integer the same way as in Assign stage. For example:

<m:map var="map">
  <m:set property="@_.5" value="Fifth"/>
  <m:set property="@_.2" value="Second"/>
  <m:set property="@_.*" value="Last"/>
</m:map>

<c:forEach var="element" items="${map._}" varStatus="status">
  <m:out value="${status.index}:${element}"/>
</c:forEach>

outputs the following:

0: 1: 2:Second 3: 4: 5:Fifth 6:Last

Elements of the "_" list are encoded/decoded as "_.index=xxx" or "_.index.key=value" (when the list element is a map). For example:

<m:map export="@{_.__encodedSorted}">
  <m:set property="@_.*" value="NAMES"/>
  <m:map property="@_.*">
    <m:set property="firstName" value="John"/>
    <m:set property="lastName" value="Doe"/>
  </m:map>
  <m:map property="@_.*">
    <m:set property="firstName" value="Jane"/>
    <m:set property="lastName" value="Doe"/>
  </m:map>
</m:map>

outputs:

_.0=NAMES&_.1.firstName=John&_.1.lastName=Doe&_.2.firstName=Jane&_.2.lastName=Doe

Properties starting with a single underscore other than "_" are hidden from the key list, and also not copied when used with source attribute or __source property.

You can use source attribute to initialize a <m:map> tag from a map, a form/json/xml-encoded string, or a list of maps or encoded strings (when source is a list, elements are copied to the "_" (underscore) list). For example:

<%-- create a map from encoded string --%>

<m:map var="x" source="a=alpha&b=beta"/>

<%-- make a copy of "x" --%>

<m:map source="${x}"/>

<%-- copy a list.  This outputs "{_=[1, 2, 3, 4, 5]}" --%>

<m:set var="list" value="1,2,3,4,5" codec="String:split:,"/>

<m:map source="${list}"/>

If source is a string starting with "@", then the rest of the string is first used as source and then the resulting map is evaluated as in <m:eval>. For example:

<%-- sets x to {a=alpha,b=beta} using json-encoded evaluated source --%>
<m:set var="a" value="alpha"/>
<m:set var="b" value="beta"/>
<m:map var="x" source="@{a=@{a},b=@{b}}"/>
<%-- or using evaluated xml source --%>
<m:map var="x" source="@<root><a>@{a}</a><b>@{b}</b></root>"/>

You can also use attribute attribute (or "__source" property) within the tag body to set source. In this case, existing key/value pairs are overwritten:

<%-- outputs "a=A&b=beta&c=C" --%>

<m:map source="a=alpha&b=beta" export="@{_.__encodedSorted}">
  <m:set attribute="source" value="a=A&c=C"/>
  <%-- or alternatively <m:set property="__source" value="a=A&c=C"/> --%>
</m:map>

Note that source makes "shallow" copy. When necessary, use __merge property instead to do nested copy.

Special property names supported are as follows (all starting with two underscores; "get/set" indicates if the property can be used to "get" values or "set" values):

 name  description  type  get/set 
 __keyList  list of keys  List  get 
 __valueList  list of values  List  get 
 __entryList  list of entries (key/value pairs)  List  get 
 __encoded  nested key/value pairs as form-encoded String  String  get/set 
 __param  nested key/value pairs as map  Map  get/set 
 __keyListSorted  list of keys, sorted  List  get 
 __entryListSorted  list of entries, sorted by key  List  get 
 __encodedSorted  same as _encoded, but sorted by key  String  get 
 __listSize  size of the "_" list  Integer  get 
 __listActualSize  actual number of elements in the "_" sparse list  Inger  get 
 __source  copies values from Map, List, or form-encoded string (shallow copy)  Map/List/String  set 
 __merge  copies values from Map, List, or form-encoded string (nested copy)  Map/List/String  set 
 __xml  XML output  String  get/set 
 __dom  XML DOM output  Node  get/set 
 __json  JSON output  String  get/set 
 __css  CSS style output  String  get 
 __attrList  XML attribute list output  String  get 
 __leaf  map of leaf elements (with non-composite (map/list) values)  Map  get 
 __submap  map of elements with map values (submaps)  Map  get 

Attributes specific to <m:map> are as follows:

 name  type  description  default value 
 source  Object  source of map  null 
 bodyProperty  String  if not null, then tag body string is assigned to this property  null 

See XMLMap on encoding/decoding to/from XML, and JSON/Javscript on encoding/deconding to/from JSON.

When necessary, it is possible to specify what keys are acceptable for a map. Acceptable keys can be specified using a map or form-encoded string that maps key names to either "r" (read-only), "w" (write-only), or "b" (both) as follows:


<%-- sets a map that accepts "x" as read-only key, "y" as write-only key, and "z" as read-write key --%

<m:map var="p" prepareCodec="Bean:setProperty:acceptable:x=r&y=w&z=b"/>

When non-null acceptable key map is specified, an exception is thrown when a non-acceptable key is used.

7.4 eval

<m:eval> evaluates the tag body (tagValue) as an EL expression using a custom pattern (default is "@{X}" for expression "X"). For example, the following code:

<m:set var="a" value="3"/>
<m:set var="b" value="5"/>

<m:eval>
  a is @{a}, b is @{b}, and a + b is @{a + b}
</m:eval>

outputs:

a is 3, b is 5, and a + b is 8

You can specify your own regular expression pattern to be used instead of "@{...}" by using pattern attribute (if you are not familiar with java regular expressions, please consult Sun tutorial). The value of pattern attribute is a regular expression whose first "capturing group" (string matching the expression inside the first parentheses) is used as the EL expression. The default pattern is "[@]\{([^}]*)\}". The following two are equivalent:

<%-- using default pattern --%>

<m:eval>
  a is @{a} and b is @{b}
</m:eval>

<%-- using custom pattern "[[XXX]]" --%>

<m:eval pattern="\[\[([^\]]*)\]\]">
  a is [[a]] and b is [[b]]
</m:eval>

If pattern doesn't contain an "(" (open parenthesis), then it is assumed to indicate the prefix for "{...}" and "\{([^}]*)\}" is appended to make a full regular expression. For example, the following sets the pattern to "!{...}":

<%-- using "!{....}" pattern --%>

<m:eval pattern="[!]">
  a is !{a} and b is !{b}
</m:eval>

You can use evalCodec attribute to specify codecs to be applied to the result of each EL evaluation:

<%-- applies "XML:encode" --%>

<m:set var="text" value="<B>BOLDTEXT</B>"/>

<m:eval evalCodec="XML:encode">
This is @{text}.
</m:eval>

literally outputs the following:

This is &lt;B&gt;BOLDTEXT&lt;/B&gt;.

Also you can use keyCodec attribute to specify codecs to be applied before EL evaluation:

<m:map var="x" source="a=alpha&b=beta"/>

<m:eval keyCodec="String:prepend:x.">
  A is @{a} and B is @{b}        
</m:eval>

outputs "A is alpha and B is beta". When necessary, you can set valueCodec to "noeval" to disable EL evaluation:

<%-- this outputs "A is #a and B is #b" --%>

<m:eval keyCodec="String:prepend:#" evalCodec="noeval">
  A is @{a} and B is @{b}        
</m:eval>

You can use environment attribute to set the "_" (underscore) variable for use in evaluation:

<%-- expression to be evaluted --%>

<m:set var="expr">
  a is @{_.a} and b is @{_.b}
</m:set>

<%-- intialized an environment --%>

<m:map var="env1">
  <m:set property="a" value="AAA1"/>
  <m:set property="b" value="BBB1"/>
</m:map>

<%-- intialized another environment --%>

<m:map var="env2">
  <m:set property="a" value="AAA2"/>
  <m:set property="b" value="BBB2"/>
</m:map>

<%-- evaluate "expr" according to "env1" --%>

<m:eval environment="${env1}" value="${expr}"/>

<%-- evaluate "expr" according to "env2" --%>

<m:eval environment="${env2}" value="${expr}"/>

When necessary, you can set recursive attribute to "true" to perform recursive EL evaluation. The following code:

<m:set var="welcomeMessage" value="welcome, @{userName}"/>
<m:set var="userName" value="john"/>
<m:eval recursive="true">@{welcomeMessage}</m:eval>

outputs:

welcome, john

When the tagValue is a NestedMap, then each key value is evaluated as above:

<%-- outputs "<root><a>alpha</a><b>beta</b></root>" --%>

<m:map var="x" source="a=@{a}&b=@{b}"/>
<m:set var="a" value="alpha"/>
<m:set var="b" value="beta"/>
<m:eval value="${x}" codec="XMLMap:encode"/>

You can also use JSP:eval codec instead:

<m:set var="a" value="3"/>
<m:set var="b" value="5"/>

<%-- these two are equivalent --%>

<m:eval>a is @{a}, b is @{b}, and a + b is @{a + b}</m:eval>

<m:out codec="_JSP:eval">a is @{a}, b is @{b}, and a + b is @{a + b}</m:out>

Attributes specific to <m:eval> are as follows:

 name  type  description  default value 
 pattern  String  pattern to be used as EL expression  [@]\\{([^}]*)\\} 
 recursive  Boolean  enables recursive evaluation  false 
 evalCodec  EL  codecs applied to each evaluated EL expression  null 
 keyCodec  EL  codecs applied to each expression before EL evaluation  null 
 allowGroup  Boolean  enables regular expression group specification ("$1") in the value of EL expression  false 
 environment  Object  sets the "_" variable to be used in evaluation  null 

7.5 param

<m:param> is a special kind of <m:map> that builds a nested map from the form parameter string in Process stage (as if it were given as the "__encoded" property). For example, the following JSP page:

<m:param var="in"/>

<m:out value="${in.user}"/>

outputs the map "${in.user}" as follows if "user.first=John&user.last=Doe" is given as form parameters:

{first=John, last=Doe}

Conversion from form parameter string is done in Process stage (after processing the tag body), so you can set default values for optional form parameters as follows:

<%-- outputs the value of form parameter "x" or "DEFAULT" if not given --%>

<m:param var="in">
  <m:set property="x" value="DEFAULT"/>
</m:param>

<m:out value="${in.x}"/>

There is a special property "request" which holds the current servlet request object (an instance of javax.servlet.http.HttpServletRequest). You can for example obtain the current request URI as "${in._request.requestURI}" if "${in}" holds the <m:param> structure.

When necessary, you can use parameterMap attribute to specify a form-encoded string or a map to be used instead of the actual parameter map of the request. For example, you can use encoded name with HTML "SUBMIT" button as follows:

<%-- this "turns on" the clicked button --%>

<m:param var="in">
  <m:set property="maxRows" value="3"/>
  <m:set property="maxColumns" value="3"/>
</m:param>

<m:param target="${in}" property="__source">
  <m:set attribute="parameterMap" value="${in.SUBMIT.__keyList}" codec="String:join"/>
</m:param>

<HTML>
  <BODY>
    <FORM METHOD="POST">
      <TABLE CELLPADDING="4">
        <c:forEach var="row" begin="1" end="${in.maxRows}">
          <TR>
            <c:forEach var="column" begin="1" end="${in.maxColumns}">
              <m:map var="submit" assign="@{_.__encoded}">
                <m:set property="row" value="${row}"/>
                <m:set property="column" value="${column}"/>
              </m:map>
              <m:set var="cellAttribute" test="${row == in.row && column == in.column}">BGCOLOR="#000000"</m:set>
              <m:eval>
                <TD @{cellAttribute}>
                  <INPUT TYPE="SUBMIT" NAME="SUBMIT.@{submit}" VALUE="Click Here">
                </TD>
              </m:eval>
            </c:forEach>
          </TR>
        </c:forEach>
      </TABLE>
    </FORM>
  </BODY>
</HTML>

You can use encoding attribute to set the character set to be used in URL decoding, if necessary (default is null meaning system default). If encoding is set to "*", then either the request encoding (if available) or default encoding ("utf-8", or the value of a configuration variable "com.micronova.jsp.tag.ParamTag.defaultEncoding" evaluated as EL) is used.

You can specify the max content-length acceptable by maxContentLength attribute. If maxContentLength is positive, then HTTP requests with content-length larger than given maxContentLength are quietly ignored. If maxContentLength is negative, then HTTP requests with content-length larger than the absolute value of given maxContentLength throws an Exception with message "max content length exceeded". You can use a configuration variable named "com.micronova.jsp.tag.ParamTag.maxContentLength" to set the default value for maxContentLength attribute as EL.

You can specify codecs to be applied to each form parameter name/value before it is put into the map structure using nameCodec and valueCodec attribute. nameCodec is applied to each parameter name (default is to remove __ (two underscores)), and valueCodec is applied to each parameter value (default is to encode XML tags to help deal with XSS (cross-site-scripting)).

<m:param> handles certain parameter names specially. If the parameter matches the regular expression pattern given as selectPattern attribute, then it is considered as a dropdown list parameter and specially treated; e.g., if selectPattern is "select_.*" and "select_x=a" is given, then it is treated as if "select_x_SELECTED.a=SELECTED" were also given. Similarly, if the parameter matches the regular expression pattern given as radioPattern attribute, then it is considered as a radio button parameter; e.g., if radioPattern is "radio_.*" and "radio_x=a" is given, then it is treated as if "radio_x_CHECKED.a=CHECKED" were also given. By default these are treated as single-valued (i.e., only the last parameter of the given name takes effect), but if the parameter name also matches the regular expression pattern given as multiPattern attribute, then it is treated as multi-valued. For example:

<m:set var="in">
  <m:param attribute="value" control="multiPattern=m_.*&selectPattern=.*s_.*&radioPattern=.*r_.*"/>
  <c:if test="${empty _.submit}">
    <m:set property="@m_s_x_SELECTED.USA" value="SELECTED"/>
    <m:set property="@m_r_x_CHECKED.INCLUDE" value="CHECKED"/>
  </c:if>
</m:set>

<m:eval>
  <FORM>
    <TABLE>
      <TR>
        <TD>single select</TD>
        <TD>
          <SELECT NAME="s_x">
            <OPTION VALUE="USA" @{in.s_x_SELECTED.USA}>United States</OPTION>
            <OPTION VALUE="EU" @{in.s_x_SELECTED.EU}>European Union</OPTION>
            <OPTION VALUE="JPN" @{in.s_x_SELECTED.JPN}>Japan</OPTION>
          </SELECT>
        </TD>
      </TR>

      <TR>
        <TD>multi select</TD>
        <TD>
          <SELECT NAME="m_s_x" MULTIPLE>
            <OPTION VALUE="USA" @{in.m_s_x_SELECTED.USA}>United States</OPTION>
            <OPTION VALUE="EU" @{in.m_s_x_SELECTED.EU}>European Union</OPTION>
            <OPTION VALUE="JPN" @{in.m_s_x_SELECTED.JPN}>Japan</OPTION>
          </SELECT>
        </TD>
      </TR>

      <TR>
        <TD>radio</TD>
        <TD>
          <INPUT TYPE="RADIO" NAME="r_x" VALUE="INCLUDE" @{in.r_x_CHECKED.INCLUDE}>Include</INPUT>
          <INPUT TYPE="RADIO" NAME="r_x" VALUE="EXCLUDE" @{in.r_x_CHECKED.EXCLUDE}>Exclude</INPUT>
        </TD>
      </TR>

      <TR>
        <TD>checkbox</TD>
        <TD>
          <INPUT TYPE="CHECKBOX" NAME="m_r_x" VALUE="INCLUDE" @{in.m_r_x_CHECKED.INCLUDE}>Include</INPUT>
          <INPUT TYPE="CHECKBOX" NAME="m_r_x" VALUE="EXCLUDE" @{in.m_r_x_CHECKED.EXCLUDE}>Exclude</INPUT>
        </TD>
      </TR>
      
      <TR>
        <TD>&nbsp;</TD>
        <TD>
          <INPUT TYPE="SUBMIT" NAME="submit" VALUE="submit">
        </TD>
      </TR>
    </TABLE>
  </FORM>
</m:eval>

If a regular parameter name matches multiPattern, then it is treated as if "._.*" were appended to it; e.g., if "multi_x=a&multi_x=b" is given, then it is treated as "multi_x._.*=a&multi_x._.*=b", and "multi_x._" becomes a list "[a,b]".

<m:param> also supports "multipart/form-data" requests (file upload). Each uploaded file parameter has file input stream (instance of java.io.InputStream) as its value instead of string, and you can save it using "File:write" codec, or convert it into binary string using "File:read" codec for further processing. A special property named multipart holds the details of the request (e.g., headers, filetype, size, etc.) as in <m:mailFolder> tag below. For example, you can get the size of an uploaded file named "xxx" as multipart.partName.xxx.fileSize. Note that regular (non-file) form parameters behave the same regardless of the ENCTYPE. For example,

<m:set var="MAXFILESIZE" value="1000000"/>

<m:param var="in"/>

<c:if test="${!empty in.submit}">

  <m:set var="fileDetail" value="${in.multipart.partName.file_upload}"/>

  <m:set var="result" value="file is larger than ${MAXFILESIZE} bytes"/>

  <c:if test="${fileDetail.size < MAXFILESIZE}">
    <m:set var="tempFile" codec="File:tempfile"/>

    <m:set var="result" value="${in.file_upload}" codec="File:write:@{tempFile}|Type:ifNull:Failed:Successfully written to @{tempFile}"/>
  </c:if>

</c:if>

<m:eval>
  <FORM METHOD="POST" ENCTYPE="multipart/form-data">
    <TABLE>
      <c:if test="${!empty result}">
        <TR><TD>result</TD><TD>@{result}</TD></TR>
      </c:if>
      <TR><TD>file (max @{MAXFILESIZE} bytes)</TD><TD><INPUT TYPE="FILE" NAME="file_upload"></TD></TR>
      <TR><TD>memo</TD><TD><INPUT TYPE="TEXT" NAME="memo" VALUE="@{in.memo}"></TD></TR>
      <TR><TD>&nbsp;</TD><TD><INPUT TYPE="SUBMIT" NAME="submit" VALUE="upload"></TD></TR>
    </TABLE>
  </FORM>
</m:eval>

valueCodec is applied to text parameter values only, but some browsers may upload text files as text instead of binary. You can use filePattern attribute to specify parameter name pattern to be considered as file parameters to avoid application of valueCodec. The default value is ".*[Ff]ile.*" (i.e., names containing "file" or "File" as substring are considered as file parameters).

You can use control attribute to set these attributes and any other control properties accepted by <m:mailFolder> by using as its value a map or a form-encoded string for the attributes and their values to be set.

As an extension, <m:param> supports all attributes of <m:map>. Attributes specific to <m:param> are as follows:

 name  type  description  default value 
 parameterMap  Object  map to be used instead of actual parameterMap of the request. This can be an instance of a map, or a form-encoded string.  null 
 nameCodec  String  codec applied to each form parameter name  String:replaceAll:__:: 
 valueCodec  String  codec applied to each form parameter value  XML:encode 
 encoding  String  character encoding used for form parameters  null 
 maxContentLength  String  maximal request content length accepted  0 (unlimited) 
 multiPattern  String  regular expression pattern for multi-valued forma parameter names  null 
 selectPattern  String  regular expression pattern for "select" form parameters  null 
 radioPattern  String  regular expression pattern for "radio" form parameters  null 
 filePattern  String  regular expression pattern for form parameters to be treated as file names  .*[Ff]ile.* 
 control  Object  map or form-encoded string that specifies the following atttibutes: "nameCodec", "valueCodec", "encoding", "maxContentLength", "filePattern", "multiPattern", "selectPattern", "radioPattern", plus any other controlproperties accepted by <m:mailFolder>  null 

7.6 call

<m:call> extends <m:map> and makes a "subroutine call" to another JSP in the same context at Process stage. Suppose we have the following JSP named "/Box.jsp" that puts the value of "parameter" inside a gray box:

<m:param var="in"/>

<TABLE BGCOLOR="DDDDDD" CELLPADDING="6">
  <TR><TD><PRE><m:out value="${in.parameter}"/></PRE></TD></TR>
</TABLE>

Then the following behaves as if "/Box.jsp?parameter=This+is+a+test" were called:

<m:call path="/Box.jsp">
  <m:set property="parameter" value="This is a test"/>
</m:call>

and outputs the following:

<TABLE BGCOLOR="DDDDDD" CELLPADDING="6">
  <TR><TD><PRE>This is a test</PRE></TD></TR>
</TABLE>

The value of a property named "x" of <m:call> appears in the callee JSP as "${in.x}" if "in" is the structured parameter variable name set by <m:param> tag. Property values are passed to the callee JSP as-is without marshalling, so if a property value is a submap, then its content is shared by both the caller and the callee.

By default, the tagValue of a <m:call> is set to the output of the callee JSP, but the callee JSP can explicitly set the value to be returned to the caller JSP using <m:return> tag or by setting target attribute to "return". The following is equivalent to "/Box.jsp" above:

<m:param var="in"/>

<m:return>
<TABLE BGCOLOR="DDDDDD" CELLPADDING="6">
  <TR><TD><PRE><m:out value="${in.parameter}"/></PRE></TD></TR>
</TABLE>
</m:return>
OUTPUT HERE IS IGNORED

You can return arbitrary object back to the caller. For example, you can write a simple "MVC"-style controller code like this:

<m:param var="in">
  <m:set property="action" value="login"/>
</m:param>

<m:call var="view" path="control/${in.action}.jsp" source="${in}"/>

<m:call path="view/${view.path}" source="${view.param}" doesForward="true"/>

where controllers such as "control/login.jsp" return the path and parameters to be shown like this:

<%-- return path/param using map --%>

<m:map target="return">
  <m:set property="path" value="PATH-TO-VIEW"/>
  <m:map property="param">
    <m:set property="VIEWPARAM-1" value="VALUE-OF-VIEWPARAM-1"/>
    <m:set property="VIEWPARAM-2" value="VALUE-OF-VIEWPARAM-2"/>
  ...
  </m:map>
</m:map>

When necessary, you can make recursive calls. For example, the following displays a file directory given as "file=xxx" form parameter as a simple tree using HTML "UL" tag:

<m:param var="in"/>
 
<m:set var="file" value="${in.file}" codec="Type:isFile"/>
 
<LI><c:out value="${file.name}"/></LI>
 
<c:if test="${file.directory == true}">
  <m:set var="subfiles" value="${file}" codec="File:listFiles"/>
  <UL>
    <c:forEach var="subfile" items="${subfiles}">
      <%-- call this JSP recursively --%>
      <m:call path="${in._request.servletPath}">
        <m:set property="file" value="${subfile}"/>
      </m:call>
    </c:forEach>
  </UL>
</c:if>

and the following computes the fibonacci number for given number "x" using a shared "cache" of previously computed numbers:

<m:param var="in">
  <m:set property="x" value="1"/>
</m:param>

<c:if test="${empty in.cache}">
  <m:map target="${in}" property="cache" source="1=1&2=1"/>
</c:if>

<m:set var="cached" value="${in.cache[in.x]}"/>

<c:if test="${empty cached}">
  <m:call var="a" path="${in._request.servletPath}">
    <m:set property="x" value="${in.x - 2}"/>
    <m:set property="cache" value="${in.cache}"/>
  </m:call>

  <m:call var="b" path="${in._request.servletPath}">
    <m:set property="x" value="${in.x - 1}"/>
    <m:set property="cache" value="${in.cache}"/>
  </m:call>

  <m:set var="cached" target="${in.cache}" property="${in.x}" value="${a + b}"/>
</c:if>

<m:return value="${cached}"/>

If necessary, you can set doesForward to "true" to pass control over to the callee JSP.

The tag body string of <m:call> is assigned to the property named "parameter" by default (you can use bodyParameter attribute to specify a non-default property name). So the following is equivalent to the caller example above:

<m:call path="/Box.jsp">This is a test</m:call>

When a JSP page is "called" by another one, <m:param> tag adds a special property named "_caller" which holds the http request object (an instance of "javax.servlet.http.HttpServletRequest") of the calling JSP/servlet, and you can find the caller's path by using "_caller.servletPath", for example. The "_caller" property is set only when the JSP is "called".

When necessary, you can call a JSP in other servlet context by specifying contextPath. In order to cross context boundary, objects being passed need to be loaded by the same class loader (e.g., by placing JSTL and YUZU jar files in "common/lib" instead of individual webapp's "WEB-INF/lib" directory).

<m:call> supports all attributes of <m:map>. Attributes specific to <m:call> are as follows:

 name  type  description  default value 
 contextPath  String  name of the servlet context for cross-context call  null 
 bodyProperty  String  name of the property for the tag body  parameter 
 body  Object  the value of the property named bodyProperty  null 
 doesForward  boolean  if set to true, control is passed to the callee JSP.  false 

Alternatively you can use "_JSP:call" codec.

7.7 return

<m:return> returns a value to the caller JSP when called. See <m:call> above for more information about JSP calling. When used in a JSP that is not called by another JSP, this always exports the tagValue. This makes it easier to debug the callee JSP because you can directly call the callee JSP to view the returned value. If the tagValue is null, then "" (empty string) is returned to the caller.

You can alternatively set the value of target attribute of a YUZU tag to string "return" to return its tagValue. For example, the following:

<m:return>
  <m:map attribute="value">
    <m:set property="name" value="test"/>
  </m:map>
</m:return>

is equivalent to:

<m:map target="return">
  <m:set property="name" value="test"/>
</m:map>

Due to the nature of the tag, <m:return> does not support the following attributes: var, target, property, attribute, scope, assignCodec, assign.

7.8 value

<m:value> sets the tagValue of the parent YUZU tag as if attribute attribute were set to "value". The following two are equivalent:

<m:out>
  <m:set attribute="value" codec="String:toUpperCase">this is a test</m:value>
</m:out>

<m:out>
  <m:value codec="String:toUpperCase">this is a test</m:value>
</m:out>

The default attribute value of a <m:value> is set to the tagValue of the closest ancestor YUZU tag (as if it were set to "${_}", but across tagfile boundaries) unless explicitly set to a non-null value. For example:

<%-- map is initialized to "a=alpha&b=beta" --%>

<m:map>
  <m:set attribute="value" default="${_}" codec="Bean:set:a:alpha"/>
  <m:set attribute="value" default="${_}" codec="Bean:set:b:beta"/>
</m:map>

<%-- this is equivalent to the above --%>

<m:map>
  <m:value codec="Bean:set:a:alpha"/>
  <m:value codec="Bean:set:b:beta"/>
</m:map>

If value attribute is set to "*" (asterisk), then <m:value>'s tagValue is replaced with the tagValue of the closest ancestor YUZU tag before the tag body is evaluated.

Due to the nature of the tag, <m:value> does not support attribute attribute.

7.9 include

<m:include> extends <m:map> and makes HTTP/HTTPS request at Process stage. The following properties are supported:

 name  summary 
 url  target URL to call 
 method  HTTP method; either "get" or "post" is supported. Default is "post". 
 throwsException  if "true", then HTTP errors (status other than 200) causes Exception. Default value is "true". If false, then response.status and response.message are set. See below. 
 followsRedirects   if "true", then HTTP redirection is automatically followed. Default value is "true" 
 usesTrustManager  if "true", then HTTPS connection uses trust manager. Default is "true" 
 allowsFile  if "true", then "file://*" URLs are allowed. Default is "false" 
 usesHostnameVerifier  if "true", then HTTPS connection uses hostname verifier. Default value is "true" 
 connectTimeout  connection timeout in milliseconds (JDK 1.5 and above only). 
 readTimeout  read timeout in milliseconds (JDK 1.5 and above only). 
 request  request data structure (a nested map). 
 request.content  request content string 
 request.parameter  request parameter map. If request.content is not given, then this map is encoded as form-data ("a=b&c=d&...") and used as request.content 
 request.encoding  character encoding used to encode the request.parameter map 
 request.header  request header name/value map. Either a string or an array/list of strings or a NestedMap with "_" list of strings can be used as "value" 
 request.authorization  if set to "basic", then request.user and request.password are used to generate HTTP basic "Authorization" header 
 request.user  user for HTTP authentication. Used if request.authorization is set to "basic". 
 request.password  password for HTTP authentication. Used if request.authorization is set to "basic". 
 response  response data structure (nested map) 
 response.status  response status. Set if request.throwsException is false. 
 response.message  response status message. Set if request.throwsException is false. 
 response.header  response header name/value map; each value is a NestedMap with "_" list containing header values 
 response.type  parsed response content mime type map 
 response.type.type  mime type; e.g., "text" if content-type is "text/html; charset=utf-8" 
 response.type.subtype  mime subtype; e.g., "html" if content-type is "text/html; charset=utf-8" 
 response.type.parameter  mime type parameter map; e.g., {charset=utf-8} if content-type is "text/html; charset=utf-8" 
 response.content  response content string which is exported by default. 

The default export attribute of <m:include> is set to "${_.response.content}" to make it simpler to include a URL. The default assign attribute is "${_}" so that the response values can be examined by assigning to a variable.

Here is a simple "get" example:

<m:include>
  <m:set property="url" value="http://www.micronova.com/"/>
</m:include>

This sends a request parameter using "get" method:

<m:include>
  <m:set property="url" value="http://www.micronova.com/"/>
  <m:set property="method" value="get"/>
  <m:map property="request">
    <m:map property="parameter">
      <m:set property="page" value="yuzu"/>
    </m:map>
  </m:map>
</m:include>

If asynchronous attribute is set to "true", then asynchronous request is made. In this case, the "tagValue" is set to the thread making the request.

As an extension, <m:include> supports all attributes of <m:map>. Attributes specific to <m:include> are as follows:

 name  type  description  default value 
 url  String  sets the url property  null 
 allowsFile  boolean  allows "file://*" URL  false 
 asynchronous  boolean  if set, makes asynchronous request  false 

7.10 log

<m:log> writes the tagValue to the server log at Process stage:

<m:param var="in"/>

<m:log value="servlet request is : ${in._request}"/>

By default the output goes to the servlet log. You can change where the log output goes by using destination attribute. If destination is set to "out" (or "err"), then the output goes to stdout (or stderr).

For apache logging, you can also specify the following logging levels as destination: "trace", "debug", "info", "warn", "error" or "fatal", and optional category using category attribute. You can also specify an instance of org.apache.commons.logging.Log to be used using log if necessary.

Alternatively you can use JSP:log codec instead of <m:log> tag. Internally <m:log> uses an instance of LogWriter (which can be obtained by JSP:getLogWriter codec).

Due to the nature of the tag, <m:log> does not support the following attributes: var, target, property, attribute, scope, exportCodec, assignCodec, assign, export, doesExport. Attributes specific to <m:log> are as follows:

 name  type  description  default value 
 destination  String  log output destination, either "log", "out", or "err"; or for apache logging, "trace", "debug", "info", "warn", "error" or "fatal".  value of configuration variable "com.micronova.jsp.tag.LogTag.destination" evaluated as EL (default "log") 
 category  String  category for apache logging  value of configuration variable "com.micronova.jsp.tag.LogTag.category" evaluated as EL (default "") 
 log  org.apache.commons.logging.Log  specific instance of org.apache.commons.logging.Log to be used  value of configuration variable "com.micronova.jsp.tag.LogTag.log" evaluated as EL (default null) 

7.11 postData

<m:postData> gets the HTTP post data. For example, you can parse the POST data as XML as follows:

<x:parse var="x"><m:postData/></x:parse>

If you specify contentTypePrefix, then <m:postData> returns POST data only if the request content type starts with given contentTypePrefix. For example, the following outputs POST data only if the request content type is "text/xml*":

<m:postData contentTypePrefix="text/xml"/>

By default the POST data is returned as string, but actually <m:postData> first sets the tagValue to the current ServletRequest object at Prepare stage if the request content type matches contentTypePrefix, and then sets tagValue to the POST data as string right before Default stage (after processing the tag body) if tagValue is still equal to the current ServletRequest. So when necessary you can directly process the POST data within the tag body as follows:

<m:postData var="parsed" contentTypePrefix="text/xml">
  <m:set attribute="value" value="${_.reader}" codec="XML:parse"/>
</m:postData>

<m:postData> does not do anything if used in a JSP called by using <m:call>.

You can use encoding attribute to set the character set to be used to convert the POST data to string. The value of encoding takes precedence over the request encoding unless it is prefixed by "*" (asterisk) - in this case, the request encoding takes precedence (if given). The default value of encoding is "*utf-8" or the value of the configuration variable "com.micronova.jsp.tag.PostDataTag.defaultEncoding" evaluated as EL.

You can specify the max content-length acceptable by maxContentLength attribute. If maxContentLength is positive, then HTTP requests with content-length larger than given maxContentLength are quietly ignored. If maxContentLength is negative, then HTTP requests with content-length larger than the absolute value of given maxContentLength throws an Exception with message "max content length exceeded". You can use a configuration variable named "com.micronova.jsp.tag.PostDataTag.maxContentLength" to set the default value for maxContentLength attribute.

Attributes specific to <m:postData> are as follows:

 name  type  description  default value 
 contentTypePrefix  String  if given, returns POST data only if the request content type starts with this  null (accepts any content type) 
 encoding  String  character encoding used to obtain postData as String  null 
 maxContentLength  Number  maximal request content length accepted  value of configuration variable "com.micronova.jsp.tag.PostDataTag.maxContentLength" evaluated as EL or 0 (unlimited) 

7.12 throw

<m:throw> throws an Exception having the tagValue as its message when tagValue is not null at Process stage. You can catch the thrown exception using JSTL's <c:catch>. The following outputs "Exception: bad input given" if input parameter "x" equals "bad":

<m:param var="in"/>

<c:catch var="exception">
  <m:throw test="${in.x == 'bad'}" value="bad input given"/>
</c:catch>

<m:out value="Exception:${exception.message}" test="${!empty exception}"/>

If necessary, you can use <m:throw> to break out of JSTL loop tags such as <c:forEach>:

<m:param var="in"/>

<c:catch var="exception">
  <c:forEach var="p" items="${in}">
    <m:throw test="${p.key == 'needle'}" value="needle found"/>
  </c:forEach>
</c:catch>

<c:if test="${exception.message == 'needle found'}">
  Form Parameter named 'needle' found
</c:if>

Or you can define tag files such as 't:block' and 't:break' as follows to break out of a specified block:

<%-- 
  't:block' tag catches special 'break' exception with message 'YUZUBREAK-XXX';
  if 'XXX' part matches its 'label' or '*', then consumes the exception; 
  otherwise rethrows.
--%>

<%@ tag %>
<%@ attribute name="label" %>
<m:set var="label" value="${label}" default="*"/>
<c:catch var="exception">
  <jsp:doBody/>
</c:catch>
<c:if test="${!empty exception}">
  <m:set var="breakLabel" value="${exception.message}" codec="String:match:^YUZUBREAK-(.*)$:1"/>
  <c:if test="${breakLabel == label || breakLabel == '*'}">
    <m:set var="exception"/>
  </c:if>
</c:if>
<m:throw value="${exception}"/>

<%-- 
  't:break' tag throws a special 'break' exception with message 
  'YUZUBREAK-${label}' when the value of optional 'test' is true (default).
--%>

<%@ tag %>
<%@ attribute name="label" %>
<%@ attribute name="test" %>
<m:set var="label" value="${label}" default="*"/>
<m:set var="test" value="${test}" default="true"/>
<c:if test="${test}">
  <m:throw>YUZUBREAK-${label}</m:throw>
</c:if>

<%--
  using  't:block' and 't:break', the following outputs 'A1 B1 C1 A2'
--%>

<t:block label="A">
  A1
  <t:block label="B">
    B1
    <t:block label="C">
      C1
      <t:break label="B"/>
      C2
    </t:block>
    B2
  </t:block>
  A2
</t:block>

Due to the nature of the tag, <m:throw> does not support the following attributes: var, target, property, attribute, scope, exportCodec, assignCodec, assign, export, doesExport, processCodec, codec.

7.13 synchronized

<m:synchronized> synchronizes tag body processing so that only one thread can execute it. For example:

<m:synchronized>
  <m:set target="${applicationScope.data}" property="${name}" value="${value}"/>
</m:synchronized>

modifies an application-scope object "applicationScope.data" synchronously.

If necessary, you can set thread lock timeout in milliseconds using waitTime attribute (default is 0 meaning "no timeout"). If thread lock can not be obtained within given waitTime, then an exception with "thread lock timeout" message is thrown.

You can also specify the name of the lock to be used for synchronization by using lockId attribute (default is "" (empty string)). The current thread executes the body of a <m:synchronized> with lockId "X" only when no other thread is executing the body of any <m:synchronized> tag with the same lockId. For example:

<%-- only one thread writes ${content} to ${fileName} --%>

<m:synchronized lockId="${fileName}">
  <m:set var="result" value="${content}" codec="File:write:@{fileName}"/>
</m:synchronized>

If the value of lockId is not a String, then the value object itself is used as a lock.

Attribute specific to <m:synchronized> is as follows:

 name  type  description  default value 
 waitTime  Long  thread lock timeout in milliseconds  0 (no timeout) 
 lockId  Object  lock identifier  "" (empty string) 

7.14 query

<m:query> is a simpler version of JSTL <sql:query> that takes embedded patterns ("%{x}" by default) inside queries to be translated as SQL parameters. For example, the following JSTL code:

<sql:query>
  select * from Test where Age = ? and Name = ?
  <sql:param value="${age}"/>
  <sql:param value="${name}"/>
</sql:query>

can be written using <m:query> as follows:

<m:query>
  select * from Test where Age = %{age} and Name = %{name}
</m:query>

The tagValue of <m:query> is an instance of javax.servlet.jsp.jstl.sql.Result as in JSTL. You can use assign or export to obtain particular row or field as follows:

<%-- Assuming that "Test" has a primary key "id" and a field "name", this assigns the name for the given id, if exists --%>

<m:query var="name" assign="@{_.rows[0].name}">
  select name from Test where id = %{id}
</m:query>

You can embed a List value in the query as follows:

<%-- set "names" to a list ["a","b","c","d","e"] --%>

<m:set var="names" value="a,b,c,d,e" codec="String:split"/>

<%-- this query is equivalent to "select id from Test where name in ('a','b','c','d','e')" --%>

<m:query>
  select id from Test where name in (%{names})
</m:query>

Note that if the value of an embedded EL expression is a byte (or char) array, then it is treated as a binary (or character) stream.

<m:query> extends <m:eval> and supports all its attributes. Attributes specific to <m:query> are as follows:

 name  type  description  default value 
 dataSource  String or javax.sql.DataSource  jdbc URL, resource name, or an instance of javax.sql.DataSource  value of configuration variable "javax.servlet.jsp.jstl.sql.dataSource" evaluated as EL 
 startRow  Integer  first query row to be returned  0 
 maxRows  Integer  max number of rows to be returned  -1 (unlimited) 

When non-empty dataSource is given, then a new database connection for the given dataSource is always opened and used. When dataSource is not given and there is no ancestor <sql:transaction> or <m:transaction> tag, then a new database connection for the dataSource specified by the default configuration variable is used. Otherwise the connection that belongs to the outermost <sql:transaction> or <m:transaction> is used.

7.15 update

<m:update> is a simpler version of JSTL <sql:update> that takes embedded patterns ("%{x}" by default) within the query to be translated as SQL parameters. For example, the following JSTL code:

<sql:update>
  update table Test set age = ? where name = ?
  <sql:param value="${age}"/>
  <sql:param value="${name}"/>
</sql:update>

can be written using <m:update> as follows:

<m:update>
  update table Test set age = %{age} where name = %{name}
</m:update>

In this case the tagValue of <m:update> is the number of rows affected as in JSTL.

Note that if the value of an embedded EL expression is a byte (or char) array, then it is treated as a binary (or character) stream.

<m:update> can also execute SQL stored procedures and return output values. In stored procedure syntax ("{...}"), you can use embedded patterns starting with '#' (e.g., "%{#output}") to indicate output variables using the following syntax ("[...]" indicates optional parts):

#[NAME][#TYPE][#TYPENAME]

where "NAME" is the name of the output value (default "OUTPUT"), "TYPE" is the SQL type of the output value (e.g., "BIGINT", "VARCHAR", etc. as defined in java.sql.Types; default "BIGINT"), and TYPANAME is the type name required for types such as "JAVA_OBJECT" (please see "java.sql.CallableStatemet" class for more details). The tagValue of <m:update> in this case is a map of output name/value pairs. For example:

<m:update>{%{#result#BIGINT} = myfunction(%{a}, %{b})}</m:update>

returns a map like {result=32}. When the called stored procedure returns a ResultSet, then the tagValue is an instance of javax.servlet.jsp.jstl.sql.Result. For an "IN OUT" argument, you can append the output specification as above to the "IN" expression (e.g., "%{12#outputValue}" to send "12" as "IN" value, and receive the "OUT" value as "outputValue").

<m:update> extends <m:eval> and supports all its attributes. Attributes specific to <m:update> are as follows:

 name  type  description  default value 
 dataSource  String or javax.sql.DataSource  jdbc URL, resource name, or an instance of javax.sql.DataSource  value of configuration variable "javax.servlet.jsp.jstl.sql.dataSource" evaluated as EL 
 startRow  Integer  first query row to be returned (used only when stored procedure returns a ResultSet)  0 
 maxRows  Integer  max number of rows to be returned (userd only when stored procedure returns a ResultSet)  -1 (unlimited) 

When non-empty dataSource is given, then a new database connection for the given dataSource is always opened and used. When dataSource is not given and there is no ancestor <sql:transaction> or <m:transaction> tag, then a new database connection for the dataSource specified by the default configuration variable is used. Otherwise the connection that belongs to the outermost <sql:transaction> or <m:transaction> is used.

7.16 transaction

<m:transaction> is a nestable (stackable) version of JSTL <sql:transaction>. The body of a <m:transaction> forms a database transaction boundary for <m:update> or <m:query> tags inside. A successful closing of <m:transaction> tag commits its transaction, and any uncaught exception inside its body rolls back the transaction and throws an exception to the outside of the <m:transaction> tag. For example,

<%-- typical MySQL row insertion, assuming a table named "Test" with 
auto-increment primary key "id", "firstName", and "lastName" fields --%>

<m:map var="fields">
  <m:set property="firstName" value="john"/>
  <m:set property="lastName" value="doe"/>
</m:map>

<m:transaction>
  <m:update environment="${fields}">
    insert into Test
    (<m:out value="${_.__keyList}" codec="String:join"/>)
    values
    (<m:filter value="${_.__keyList}" apply="%{_.${_element}}" codec="String:join"/>)
  </m:update>
  <m:query var="id" assign="@{_.rows[0].id}">
    select last_insert_id() as id
  </m:query>
</m:transaction>

inserted id is ${id}

When <m:transaction> tags are nested, then the outermost <m:transaction> defines the the transaction boundary; i.e., the following code either inserts two rows or no rows at all:

<m:map var="ENV"/>

<m:transaction>

  <m:map var="fields">
    <m:set property="firstName" value="john1"/>
    <m:set property="lastName" value="doe1"/>
  </m:map>

  <m:transaction>

    <m:update>
      insert into Test
      (<m:out value="${fields.__keyList}" codec="String:join"/>)
      values
      (<m:filter apply="%{fields.@{_element}}" value="${fields.__keyList}" codec="String:join"/>)
    </m:update>

    <m:query var="id" assign="@{_.rows[0].id}">
      select last_insert_id() as id
    </m:query>

  </m:transaction>

  <m:set target="${ENV}" property="id1" value="${id}"/>

  <m:map var="fields">
    <m:set property="firstName" value="john2"/>
    <m:set property="lastName" value="doe2"/>
  </m:map>

  <m:transaction>

    <m:update>
      insert into Test
      (<m:out value="${fields.__keyList}" codec="String:join"/>)
      values
      (<m:filter apply="%{fields.@{_element}}" value="${fields.__keyList}" codec="String:join"/>)
    </m:update>

    <m:query var="id" assign="@{_.rows[0].id}">
      select last_insert_id() as id
    </m:query>

  </m:transaction>

  <m:throw>Error</m:throw>

  <m:set target="${ENV}" property="id2" value="${id}"/>
  
</m:transaction>

${ENV}

Since <m:transaction> is stackable, if you have a tag <d:create> defined by a tag file like this (tag definitions are omitted):

<%@ tag %>

<%@ attribute name="target" type="java.lang.Object" %>
<%@ attribute name="property" %>
<%@ attribute name="table" %>

<m:map var="fields">
  <jsp:doBody/>
</m:map>

<m:transaction>

  <m:update>
    insert into ${table}
    (<m:out value="${fields.__keyList}" codec="String:join"/>)
    values
    (<m:filter apply="%{fields.@{_element}}" value="${fields.__keyList}" codec="String:join"/>)
  </m:update>

  <m:query var="id" assign="@{_.rows[0].id}">
    select last_insert_id() as id
  </m:query>

</m:transaction>

<m:set target="${target}" property="${property}" value="${id}"/>

then the code above can be rewritten as follows:

<m:map var="ENV"/>

<m:transaction>
  <d:create target="${ENV}" property="id1" table="Test">
    <m:set property="firstName" value="john1"/>
    <m:set property="lastName" value="doe1"/>
  </d:create>

  <d:create target="${ENV}" property="id2" table="Test">
    <m:set property="firstName" value="john2"/>
    <m:set property="lastName" value="doe2"/>
  </m:map>
</m:transaction>

${ENV}

When isolation is given, then it always affects the transaction defined by the tag (even when nested). Also, if non-empty dataSource is given, then a new transaction (connection) is made always, regardless of nesting.

The tagValue of <m:transaction> is undefined (null).

Attributes specific to <m:transaction> are as follows:

 name  type  description  default value 
 dataSource  String or javax.sql.DataSource  jdbc URL, resource name, or an instance of javax.sql.DataSource  value of configuration variable "javax.servlet.jsp.jstl.sql.dataSource" evaluated as EL 
 isolation  String  transaction isolation level; either "read_committed", "read_uncommitted", "repeatable_read", "serializable" or null (no change)  null 

7.17 response

<m:response> extends <m:map> and modifies the response for the HTTP request currently being processed. The following properties are supported:

 name  summary 
 header  response header name/value map. The value can be either string or a list of strings. 
 error  error code 
 errorMessage  error message 
 redirect  redirect URL 
 cookie  cookie name/value map. The value can be either an instance of javax.servlet.http.Cookie or a map of Cookie bean property/value pairs (comment, domain, maxAge, path, secure, value, version). 

The following redirects the browser:

<m:response>
  <m:set property="redirect" value="http://www.micronova.com/"/>
</m:response>

The following sets the content type:

<m:response>
  <m:set property="@header.content-type" value="text/xml; charset=utf-8"/>
</m:response>

The following sets the response code to "500":

<m:response>
  <m:set property="error" value="500"/>
  <m:set property="errorMessage" value="Something happened"/> 
</m:response>

The following sets a secure cookie:

<m:response>
  <m:map property="@cookie.test">
    <m:set property="value" value="test cookie"/>
    <m:set property="secure" value="true"/>
  </m:map>
</m:response>

7.18 system

<m:system> extends <m:map> and makes a system call. The following properties/attributes are supported:

 name  summary 
 command  system command to execute 
 stdin  standard input for the executed command 
 stdout  standard output of the executed command 
 stderr  standard error of the executed command 
 encoding  encoding for stdin/stdout/stderr (default: iso-8859-1) 
 rc  status code of the executed command (property only) 

The default export is set to "${_.stdout}" to make it easier to import the standard output. The following creates a tar file for download:

<m:system command="sh">
  <m:set property="stdin">
    cd mydirectory
    tar cz *
  </m:set>
  <m:response>
    <m:map property="header">
      <m:set property="content-type" value="application/binary"/>
      <m:set property="content-disposition" value="filename=myfile.tar.gz"/>
    </m:map>
  </m:response>
  <%-- make sure no character follows "</m:system>" --%>
</m:system>

You can specify a Writer as stdout or stderr to pipe the output; for example, the following outputs the shell's output unbuffered:

<%@ page contentType="text/plain; charset=utf-8" buffer="none" %>
<m:system command="sh" stdout="${pageContext.out}" stderr="${pageContext.out}">
  <m:set property="stdin">
    curl -o "/tmp/file" "http://www.micronova.com/YUZU/yuzu-20070909.zip"
  </m:set>
</m:system>
DONE

Attributes specific to <m:system> are as follows:

 name  type  description  default value 
 command  String  system command to execute  null 
 stdin  Object  standard input for the command to be executed, either a reader (instance of java.io.Reader) or a string  null 
 stdout  Object  standard output for the command to be execute; if this is a Writer (istance of java.io.Writer) then the command's standard output is piped to it (unbuffered)  null 
 stderr  Object  standard error for the command to be executed; if this is set to a Writer (instance of java.io.Writer), then the commands' standard error is piped to it (unbuffered)  null 
 encoding  String  character encoding for stdin/stdout/stderr  iso-8859-1 
 export  String  object to be exported  ${_.stdout} 

7.19 filter

<m:filter> "filters" elements of "tagValue" list (i.e., creates a new list of "filtered" elements). It iterates over elements of "tagValue" setting the following page-scoped local variables for each element:

and each element is processed according to given EL attributes include, apply, applyCodec, and break as follows:

By default, all elements are included (include is "true" and break is null) and copied as-is to the new list (apply is "${_element}", and applyCodec is null).

The following "squares" each odd number in the list up to (and not including) the first negative number:

<%-- outputs [1, 9, 25] --%>

<m:set var="list" value="1,2,3,4,5,-1,6,7,8,9" codec="String:split"/>

<m:filter value="${list}" include="@{(_element % 2 > 0) && (_element > 0)}"
  apply="@{_element * _element}" break="@{_element < 0}"/>

The "tagValue" can also be a string or map. When "tagValue" is a string, then each character of the "tagValue" is used as _element , and the filtered values are appended together as a string:

<%-- converts each character into 4-digit hex value --%>

<m:filter value="Hello World" applyCodec="Type:charAsNumber|Number:toHexString:0000"/>

When "tagValue" is a map, then each key/value pair is used as _element , and the filtered value is used as the value for the given key in the resulting map:

<m:map var="map">
  <m:set property="a" value="alpha"/>
  <m:set property="b" value="beta"/>
  <m:set property="c" value="gamma"/>
</m:map>

<%-- converts value to "key:value" --%>

<m:filter value="${map}" apply="@{_element.key}:@{_element.value}"/>

If necessary, you can use "_JSP:applyFilter" codec instead. For example:

<%-- the following two are equivalent --%>

<m:filter value="Hello World" applyCodec="Type:charAsNumber|Number:toHexString:0000"/>

<m:out value="Hello World" codec="_JSP:applyFilter:applyCodec=Type\:charAsNumber\|Number\:toHexString\:0000"/>

7.20 parseHtml

<m:parseHtml> parses the tagValue as HTML document into a DOM structure (so that JSTL XML tags can be applied). For example, the following prints out all the links in MicroNova's home page:

<m:parseHtml var="html"><m:include url="http://www.micronova.com/"/></m:parseHtml>

<x:forEach var="link" select="$html//a/@href">
  link: <x:out select="$link"/>
</x:forEach>

<m:parseHtml> supports the following attributes:

 name  description  default value 
 include  comma-separated list of HTML tag names to be included while parsing, null by default (meaning "include all"). The following special element names can also be used: "text", "comment", "script" (<script> tag), "style" (<style> tag), "sgml" (&lt!...> other than comments), and "pi" (<?...>).  null (include all) 
 exclude  comma-separated list of HTML tag names to be excluded while parsing, null by default (meaning "exclude none"). The special element names as above can be used.  null (exclude none) 
 strategy  parsing strategy for handling missing closing tags; either "single", "data", or "none". See below for more information.  single 
 rootName  root XML document name to be used.  root 

strategy sets the behavior of the parser when closing tags are missing. The "single" strategy assumes the opening tag is a single tag ("<x/>"). The "data" strategy assumes the opening tag encloses the following text. The "none" strategy assumes the opening tag encloses all following texts and tags. For example, the following:

<a>
<b>
text1
<c>
text1
<d>
text2
</a>

is parsed into the following XML if strategy is "single":

<a>
  <b/>
  text1
  <c/>
  text1
  <d/>
  text2
</a>

and if strategy is "data":

<a>
  <b>text1</b>
  <c>text1</c>
  <d>text2</d>
</a>

and finally if strategy is "none":

<a>
  <b>
    text1
    <c>
      text1
      <d>
        text2
      </d>
    </c>
  </b>
</a>

7.21 mail

<m:mail> extends <m:map> and sends multi-part emails in Process stage. In its simplest form, the following sends a text email:

<m:mail url="smtp://your-smtp-server">
  <m:set property="to">somebody@someaddress</m:set>
  <m:set property="from">somebody@someaddress</m:set>
  <m:set property="subject">Test email</m:set>
  <m:set property="type">text/plain; charset=ISO-8859-1</m:set>
  <m:set property="content">Hello, this is a test</m:set>
</m:mail>

When the "_" (underscore) list property is not empty, then a multi-part email is composed of the list elements ("_.0", "_.1", ..., each having its own "content", "header", etc.) recursively using "type" as the multi-part subtype. The following sends a multi-part email consisting of alternative text/html parts with images:

<m:mail url="smtp://your-smtp-server">
  <m:set property="to">addressee@somewhere.com</m:set>
  <m:set property="from">somebody@somewhere.com</m:set>
  <m:set property="subject">Test</m:set>
  <m:set property="type" value="alternative"/>

  <%-- alternative text part --%>

  <m:map property="@_.*">
    <m:set property="type" value="text/plain; charset=iso-8859-1"/>
    <m:set property="content">This is Text</m:set>
  </m:map>

  <%-- alternative HTML part with images --%>

  <m:map property="@_.*">
    <m:set property="type" value="related"/>

    <%-- related HTML part --%>
 
    <m:map property="@_.*">
      <m:set property="type" value="text/html; charset=iso-8859-1"/>
      <m:set property="content">
        <HTML>
          <BODY>Here is an image:<IMG SRC="cid:image1"></BODY>
        </HTML>
      </m:set>
    </m:map>

    <%-- HTML image, CID: image1 --%>
    
    <m:map property="@_.*">
      <m:set property="dataSource" value="file:///somedirectory/image.png"/>
      <m:set property="@header.Content-ID" value="<image1>"/>
      <m:set property="@header.Content-Transfer-Encoding" value="base64"/>
    </m:map>

  </m:map>
</m:mail>

The following properties are supported:

 name  description 
 to  "to" addresses. Either a comma-separated list of email addresses, or an instance of java.util.List of email addresses or maps of type {address=xxx, personal=yyy, charset=zzz} (for "yyy <xxx>" email address encoded using the charset "zzz"; "personal" and "charset" are optional) 
 cc  "cc" addresses. Same type of value as "to" 
 bcc  "bcc" addresses. Same type of value as "to" 
 from  "from" addresses. Same typeof value as "to" 
 replyTo  "replyTo" addresses. Same type of value as "to" 
 subject  email subject 
 type  content type. Default is "text/plain; charset=ISO-8859-1". For multi-part email, this specifies subtype ("related", "alternative", "mixed") 
 content  message content 
 header  header map. Can be name/value pairs, or with "_" list consisting of {name=xxx, value=yyy} type maps or header line strings such as "Content-type: text/plain" when ordering is significant. 
 fileName  content fileName 
 dataSource  content dataSource URL 

By default the connection to the mail server is closed each time <m:mail> tag is closed, but if the "url" attribute is not given, then <m:mail> shares the mail server connection of the the closest surrounding <m:mail> tag with non-null "url". For example:

<m:set var="addresses" importCodec="String:split">a1,a2,a3</m:set>

<m:mail url="smtp://your-smtp-server">
  <c:forEach var="address" items="${addresses}">
    <m:mail>
      <m:set property="to" value="${address}"/>
      <m:set property="from">somebody@someaddress</m:set>
      <m:set property="subject">Test email</m:set>
      <m:set property="type">text/plain; charset=ISO-8859-1</m:set>
      <m:set property="content">Hello, this is a test</m:set>
    </m:mail>
  </c:forEach>
</m:mail>

sends out emails to "a1", "a2", "a3" using the mail server connection of the outer <m:mail>. The mail server connection is closed when the outer <m:mail> is closed.

You can specify username/password for the mail server either in the url attribute (e.g., "smtp://USERNAME:PASSWORD@smtpserver") or using mail properties (e.g., "mail.smtp.user" and "mail.smtp.password"), along with other properties. The following sends an email using Google's smtp server with debug output:

<m:map var="mailProperties">
  <m:set property="mail.smtp.user">${USERNAME}</m:set>
  <m:set property="mail.smtp.password">${PASSWORD}</m:set>
  <m:set property="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</m:set>
  <m:set property="mail.debug">true</m:set>
</m:map>

<m:mail url="smtp://smtp.gmail.com:465" properties="${mailProperties}">
  <m:set property="to">yuzu@micronova.com</m:set>
  <m:set property="subject">Test</m:set>
  <m:set property="type">text/plain; charset="utf-8"</m:set>
  <m:set property="content">This is a test</m:set>
</m:mail>

As an extension, <m:mail> supports all attributes of <m:map>. Attributes specific to <m:mail> are as follows:

 name  type  description  default value 
 url  String  mail server url  null 
 properties  Object  map (or form-encoded string) of javax.mail.Session properties  null 

7.22 mailFolder

<m:mailFolder> retrieves/deletes emails from a mailbox (folder) in Process stage. For Example:

<%-- read all messages --%>

<m:mailFolder url="pop3://your-username:your-password@your-popserver/INBOX" operation="read" messages="*"/>

<%-- reads messages 123 and 126 --%>

<m:mailFolder url="pop3://your-username:your-password@your-popserver/INBOX" operation="read" messagesNumbers="123,126"/>

<%-- deletes messages 123,124,125,126 --%>

<m:mailFolder url="pop3://your-username:your-password@your-popserver/INBOX" operation="delete" messages="123-126"/>

messages specifies the messages to read or delete. It can be either a comma-separated string of message numbers or range of message numbers ("m-n" indicating "m,m+1,...,n"; "m-*" meaning "equal or larger than m", "*-n" meaning "equal or smaller than n", or "*" meaning all), or a list of message numbers or "javax.mail.Message" instances.

The tagValue of <m:mailFolder> is set to a nested map with the following properties:

 name  dscription 
 name  name of the folder 
 fullName  full name of the folder 
 urlName  URL name of the folder 
 messageCount  number of messages in the folder 
  _folder   instance of javax.mail.Folder 
  _   list of messages for "read" operation 

Each message/part is retrieved as nested maps with the following properties:

 name  description 
  to._   "to" addresses. List of maps of type {address=xxx, personal=yyy} 
  cc._   "cc" addresses. List of maps of type {address=xxx, personal=yyy} 
  bcc._   "bcc" addresses. List of maps of type {address=xxx, personal=yyy} 
  from._   "from" addresses. List of maps of type {address=xxx, personal=yyy} 
  replyTo._   "replyTo" addresses. List of maps of type type {address=xxx, personal=yyy} 
 subject  message subject 
 sentDate  sent date 
 receivedDate  received date 
 type  parsed content mime type; e.g., a map {type=text, subtype=html, parameter={charset=utf-8}} for content type "text/html; charset=utf-8". If "type" is "multipart", then the "_" (underscore) list contains message parts as in <m:mail>
 content  message/part content 
 header  header map. The "_" (underscore) sublist contains the headers in the given order as {name=xxx, value=yyy} maps. 
 description  description, if given 
 lineCount  line count, if given 
 fileName  file name, if given 
 size  size, if given 
 disposition  parsed disposition map like type, if given 
 partName  subpart map by disposition name; e.g., if a subpart's disposition has "name=xxx", then it is accessible as "partName.xxx") 
  _   list of subparts 
  _part   instance of javax.mail.Part 

For example:

<%-- shows subject/date/from of all messages --%>

<m:mailFolder var="folder" url="pop3://your-username:your-password@your-popserver/INBOX" operation="read" messages="*" control="content=false&header=false"/>

<TABLE>
  <TR>
    <TH>subject</TH>
    <TH>sent date</TH>
    <TH>from</TH>
  </TR>

  <c:forEach var="message" items="${folder._}">
    <m:set target="${message}" property="fromList" value="${message.from._}" codec="String:join"/>
    <m:eval evalCodec="XML:encode">
      <TR>
        <TD>@{message.subject}</TD>
        <TD>@{message.sentDate}</TD>
        <TD>@{message.fromList}</TD>
      </TR>
    </m:eval>
  </c:forEach>
</TABLE>

You can specify a map of various control properties using control attribute. The value for control can be either a form-encoded string or a map. The following control properties are supported:

 name  description  default value 
 content  if set to "false", then message content is not read  true 
 header  if set to "false", then message header is not read  true 
 headerMap  if set to "true", then the "header" maps each header name to its value  false 
 maxContentSize  specifies max part content size. Part contents larger than this are ignored.  0 (unlimited) 
 maxPartSize  specifies max part size (including multiparts). Parts larger than this are ignored.  0 (unlimited) 

By default the connection to the mailbox server is closed each time <m:mailFolder> tag is closed, but if the "url" attribute is not given, then <m:mailFolder> shares the mailbox server connection of the the closest surrounding <m:mailFolder> tag with non-null "url". For example:

<m:mailFolder url="pop3://your-username:your-password@your-popserver/INBOX">

  <%-- reads messages 123-130 --%>

  <m:mailFolder operation="read" messagesNumbers="123-130"/>

  <%-- then reads messages 150-180 --%>

  <m:mailFolder operation="read" messagesNumbers="150-180"/>

</m:mailFolder>

As an extension, <m:mailFolder> supports all attributes of <m:map>. Attributes specific to <m:mailFolder> are as follows:

 name  type  description  default value 
 url  String  mailbox server url  null 
 operation  String  operation to perform; either "read" or "delete"  read 
 messages  Object  comma-separated string of message numbers or ranges of message numnbers, or list of message numbers or instances of javax.mail.Message, to be read or deleted; "*" means "all"  null 
 control  Object  Map or form-encoded string of control properties.  null 

8 Miscellaneous

8.1 Dynamic Method Invocation

When necessary, you can use "System:invoke" codec to call a java method dynamically. "System:invoke" takes a nested map operand having the following properties:

 name  description 
 object  Object on which an instance method is invoked. 
 class  Class object of which a static method is invoked. This can be either an instance of java.lang.Class, or type string ("String", "int[]", "java.lang.security.MessageDigest", etc.). If "." (dot) is missing from the class name, then "java.lang" package is assumed. 
 method  name of the method to be invoked. If this is "*" (asterisk), then a constructor for given class is invoked. If this starts with a "." (dot), then it is taken as a field name instead of a method name. 
  _   list of method arguments. Each list element is a map with type and value properties, or an object if its type matches the argument type. The type can be either an instance of a java.lang.Class or type string like "java.lang.Integer". 

For example:

<%-- performs "This is a Test".substring(3) --%>

<m:map codec="System:invoke">
  <m:set property="object" value="This is a Test"/>
  <m:set property="method" value="substring"/>
  <m:map property="@_.*">
    <m:set property="type" value="int"/>
    <m:set property="value" value="3"/>
  </m:map>
</m:map>

<%-- creates a char[] of size 4 initialized to {'a', 'b', 'c', 'd'} --%>

<m:set var="c">
  <m:map attribute="value" codec="System:invoke">
    <m:set property="class" value="java.lang.reflect.Array"/>
    <m:set property="method" value="newInstance"/>
    <m:map property="@_.*">
      <m:set property="type" value="java.lang.Class"/>
      <m:set property="value" codec="Type:forName" value="char"/>
    </m:map>
    <m:map property="@_.*">
      <m:set property="type" value="int"/>
      <m:set property="value" value="4"/>
    </m:map>
  </m:map>

  <%-- initialize char[] to {'a','b','c','d'} --%>

  <m:set property="0" codec="Type:isCharacter" value="a"/>
  <m:set property="1" codec="Type:isCharacter" value="b"/>
  <m:set property="2" codec="Type:isCharacter" value="c"/>
  <m:set property="3" codec="Type:isCharacter" value="d"/>
</m:set>

<%-- creates a String "abcd" out of char[] "c" --%>

<m:map codec="System:invoke">
  <m:set property="class" value="String"/>
  <m:set property="method" value="*"/>
  <m:map property="@_.*">
    <m:set property="type" value="char[]"/>
    <m:set property="value" value="${c}"/>
  </m:map>
</m:map>

<%-- gets the value of "javax.xml.transform.OutputKeys.MEDIA_TYPE" --%>

<m:map codec="System:invoke">
  <m:set property="class" value="javax.xml.transform.OutputKeys"/>
  <m:set property="method" value=".MEDIA_TYPE"/>
</m:map>

8.2 Dynamic Map

A "dynamic map" is a special kind of map that calls specified codecs on "get" and "put" operations. Such a map can be created by "_JSP:dynamicMap" codec on an "operand" map with "getCodec" and "putCodec" properties set. On each "get" operation with key "XXX" on the resulting dynamic map, the codec defined by "getCodec" property is invoked on the original "operand" map with its "key" property set to "XXX". On each "put" operation with key "XXX" and value "YYY" on the resulting dynamic map, the codec defined by "putCodec" proprty is invoked on the original "operand" map with its "key" property set to "XXX" and its "value" property set to "YYY". In either case, the original "codec" map is also accessible by using the "_" (underscore) variable within the invoked codecs. For example:

<%-- a dynamic map that maps key to its length --%>

<m:map var="lengthMap" codec="_JSP:dynamicMap">
  <m:set property="getCodec" value="Bean:get:key|Type:length"/>
  <%-- or equivalently
  <m:set property="getCodec" value="Type:length_:@{_.key}"/>
  --%>
</m:map>

<%-- this outputs 10 --%>

<m:out value="${lengthMap['somestring']}"/>

Properties of the "operand" map other than "getCodec", "putCodec", "key", "value" can be freely used. For example:

<%-- a case-insensitive dynamic map --%>

<m:map var="ignoreCaseMap" codec="_JSP:dynamicMap">
  <m:map property="storage"/>
  <m:map property="getCodec" value="String:toUpperCase_:@{_.key}|Bean:get_:@{_.storage}:@{_operand}"/>
  <m:map property="putCodec" value="String:toUpperCase_:@{_.key}|Bean:set_:@{_.storage}:@{_operand}:@{_.value}"/>
</m:map>

<m:set target="${ignoreCaseMap}" property="this" value="something"/>
<m:out value="${ignoreCaseMap['This']}"/>
<m:out value="${ignoreCaseMap['THIS']}"/>

You can combine "System:invoke" to create a dynamic map that calls java methods:

<m:map var="SYSTEMPROPERTY" codec="_JSP:dynamicMap">
  <m:map property="getMethod">
    <m:set property="class" value="java.lang.System"/>
    <m:set property="method" value="getProperty"/>
    <m:map property="@_.*">
      <m:set property="type" value="String"/>
    </m:map>
  </m:map>
  <m:map property="putMethod">
    <m:set property="class" value="java.lang.System"/>
    <m:set property="method" value="setProperty"/>
    <m:map property="@_.*">
      <m:set property="type" value="String"/>
    </m:map>
    <m:map property="@_.*">
      <m:set property="type" value="String"/>
    </m:map>
  </m:map>
  <m:set property="getCodec" value="Bean:set_:@{_.getMethod}:@_.0.value:@{_.key}|System:invoke_:@{_.getMethod}"/>
  <m:set property="putCodec" value="Bean:set_:@{_.putMethod}:@_.0.value:@{_.key}|Bean:set_:@{_.putMethod}:@_.1.value:@{_.value}|System:invoke_:@{_.putMethod}"/>
</m:map>

<m:out value="${SYSTEMPROPERTY['os.name']}"/>

<m:set target="${SYSTEMPROPERTY}" property="unknown" value="UNKNOWNPROPERTY"/>

<m:out value="${SYSTEMPROPERTY['unknown']}"/>

8.3 Dynamic Iterator

A "dynamic iterator" is a dynamically controllable iterator. You can create a dynamic iterator for a collection using "_JSP:dynamicIterator" as follows:

<%-- "words" is a ["this", "is", "a", "test"] --%>

<m:set var="words" value="this is a test" codec="String:split: :"/>

<%-- regular iteration over "words" --%>

<c:forEach var="word" items="${words}">
  <m:out value="${word}"/>
</c:forEach>

<%-- create a dynamic iterator for "words" --%>

<m:map var="wordIterator" codec="_JSP:dynamicIterator">
  <m:set property="collection" value="${words}"/>
</m:map>

<%-- iteration using dynamic iterator, same as above --%>

<c:forEach var="word" items="${wordIterator}">
  <m:out value="${word}"/>
</c:forEach>

A dynamic iterator can be changed dynamically; e.g., the following breaks out of the loop after finding a word "a":

<%-- iteration using dynamic iterator, break on "a" --%>

<c:forEach var="word" items="${wordIterator}">
  <m:out value="${word}"/>
  <c:if test="${word == 'a'}">
    <%-- makes the "wordIterator" return "false" on the next iteration --%>
    <m:set target="${wordIterator}" property="hasNext" value="false"/>
  </c:if>
</c:forEach>

You can create a "while" loop:

<%-- keep removing "10" while found --%>

<m:map var="whileLoop" codec="_JSP:dynamicIterator">
  <m:set property="hasNext" value="true"/>
</m:map>

<m:set var="x" value="10001010111001101"/>

<c:forEach items="${whileLoop}">
  <m:set var="before" value="${x}"/>
  <m:set var="x" value="${x}" codec="String:replaceAll:10::"/>
  <m:out value="[[${before}:${x}]]"/>
  <m:set target="${whileLoop}" property="hasNext" value="${x != before}"/>
</c:forEach>

<m:out value="${x}"/>

or a "for" loop:

<%-- loop for i = 0, 1, 2, 3, 4 --%>

<m:map var="forLoop" codec="_JSP:dynamicIterator">
  <m:set property="hasNextCodec" value="@{_.index < 5}"/>
  <m:set property="nextCodec" value="Bean:get:index"/>
</m:map>

<c:forEach var="i" items="${forLoop}">
  <m:out value="${i}"/>
</c:forEach>

The following properties are supported:

 name  description 
 collection  collection to iterate over 
 hasNext  boolean value that indicates if this iterator has more elements to visit or not. If "hasNextCodec" is not set, then this takes precedence over "collection" if set. 
 hasNextCodec  codec to be invoked for each "hasNext" operation. This is supposed to return true if the iterator has more elements to iterate over, or false otherwise. This has precendence over "hasNext" or "collection" if set. 
 next  next object to be visited. If "nextCodec" is not set, then this takes precendence over "collection" if set. 
 nextCodec  codec to be invoked for each "next" operation. This is supposed to return the next object to visit. This takes precedence over "next" or "collection" if set. 
 index  0-based iteration index increased each time iteration is made. 

8.4 XMLMap

You can use XMLMap:encode and XMLMap:decode to perform simple conversion of a NestedMap to/from XML. By default, it performs conversion according to the following rules:

  1. unless specified, the root document name is "root";
  2. each key/value pair is converted to <key>[value]</key>, where the '[value]' part is either a string or recursively generated XML presentation of the key value;
  3. sublist "_" of a NestedMap is mapped to <_><item index="0">[value0]</item><item index="1">[value1]</item>....</_> where '[value0]' is the XML presentation of "${_[0]}", '[value1]' is the XML presentation of "${_[1]}", etc..

For example, the following:

<m:map exportCodec="XMLMap:encode">
  <m:set property="a">p</m:set>
  <m:map property="b">
    <m:set property="c">q</m:set>
    <m:set property="d">r</m:set>
  </m:map>
</m:map>

outputs:

<root>
  <a>p</a>
  <b>
    <c>q</c>
    <d>r</d>
  </b>
</root>

and the following:

<m:map exportCodec="XMLMap:encode">
  <m:map property="@_.*">
    <m:set property="a">p0</m:set>
    <m:set property="b">q0</m:set>
  </m:map>
  <m:map property="@_.*">
    <m:set property="a">p1</m:set>
    <m:set property="b">q1</m:set>
  </m:map>
  <m:map property="@_.*">
    <m:set property="a">p2</m:set>
    <m:set property="b">q2</m:set>
  </m:map>
</m:map>

outputs:

<root>
  <_>
    <item index="0">
      <a>p0</a>
      <b>q0</b>
    </item>
    <item index="1">
      <a>p1</a>
      <b>q1</b>
    </item>
    <item index="2">
      <a>p2</a>
      <b>q2</b>
    </item>
  </_>
</root>

You can convert generated XML back to NestedMap using XMLMap:decode:

<%-- this outputs "{_=[{a=p0, b=q0}, {a=p1, b=q1}, {a=p2, b=q2}]}" --%>

<m:out codec="XML:parse|XMLMap:decode|Bean:get:root">
  <m:map exportCodec="XMLMap:encode">
    <m:map property="@_.*">
      <m:set property="a">p0</m:set>
      <m:set property="b">q0</m:set>
    </m:map>
    <m:map property="@_.*">
      <m:set property="a">p1</m:set>
      <m:set property="b">q1</m:set>
    </m:map>
    <m:map property="@_.*">
      <m:set property="a">p2</m:set>
      <m:set property="b">q2</m:set>
    </m:map>
  </m:map>
</m:out>

You can change the default behaviour by using optional "controlMap" (which is either a map or a form-encoded string). For example, you can change the default root name "root" by using controlMap set as "rootName=xxx":

<m:map exportCodec="XMLMap:encode:rootName=response">
  <m:set property="a">p</m:set>
  <m:map property="b">
    <m:set property="c">q</m:set>
    <m:set property="d">r</m:set>
  </m:map>
</m:map>

outputs:

<response>
  <a>p</a>
  <b>
    <c>q</c>
    <d>r</d>
  </b>
</response>

On decoding, you can specify a "nameMap" controlMap element which can be used to perform simple element name conversion; for example, if given XML has multiple elements:

<list>
  <item>a</item>
  <item>b</item>
  <item>c</item>
</list>

You can still apply XMLMap:decode using the following controlMap:


<%-- this outputs {list={_=[a,b,c]}} --%>

<m:out codec="XML:parse|XMLMap:decode:nameMap.item=@_.*">
  <list>
    <item>a</item>
    <item>b</item>
    <item>c</item>
  </list>
</m:out>

When XMLMap encounters an element named "xxx", the value of "nameMap.xxx" of the controlMap, if given, is used as the map property to assign the element value to, instead of default "xxx". In this case, each value of "item" element is assigned to "@_.*" property of the resulting map.

When a map key is not a valid XML element name (e.g., a number), then XMLMap performs the following conversion on encoding/decoding:

  1. if the first character of the key name is not valid, then "_0" is prepended;
  2. other invalid or non-alphabetic characters are represented as "_xhhhh_", where "hhhh" is the hexadecimal presentation of the character;
  3. "__" is used to represent "_" when necessary.

8.5 MessageMap

_JSP:getMessageMap creates a map that can be used to access resource messages instead of using JSTL <fmt:message>. You can create a message map as follows:

<m:set var="message" codec="_JSP:getMessageMap_"/>

Then the following two are equivalent:

${message['x']}

<fmt:message key="x"/>

You can specifiy message parameters using ':' like codec syntax. The following two are equivalent:

${message['x:@{a}:b']}

<fmt:message key="x">
  <fmt:param value="${a}"/>
  <fmt:param value="b"/>
</fmt:message>

Unlike <fmt:message>, the default value returned for missing messages is empty string:

This is empty:${message['undefined']}
But this is not:<fmt:message key="undefined"/>

You can set your own default value by setting the "defaultValue" property of the message map:

<m:set target="${message}" property="defaultValue" value="MISSING"/>
This is "MISSING":${message['undefined']}

To make the message map return the same default value as <fmt:message>, set the "defaultValue" property to "*". If necessary, you can also set the base name of the message resource by setting "baseName" property. You can apply "_JSP:getMessageMap" to a map that defines these properties:

<%-- creates a message map that behaves like <fmt:message> --%>
<m:map var="message" codec="_JSP:getMessageMap">
  <m:set property="defaultValue" value="*"/>
</m:map>

8.6 JSON/Javascript

You can use "JSON:encode" and "JSON:decode" codecs to encode/decode object (string/list/map) to/from string in JSON format. For example:

<m:map var="x">
  <m:set property="a">'quoted'</m:set>
  <m:set property="b" codec="JSON:decode">{p:q, r:s}</m:set>
  <m:set property="@_.*" value="${3.2}"/>
  <m:set property="@_.*" value="${true}"/>
</m:map>

<%-- This outputs:
'\'quoted\''
[3.2,true]
{"a":"\'quoted\'","_":[3.2,true],"b":{"r":"s","p":"q"}}
--%>

<m:out value="${x.a}" codec="JSON:encode:quote='"/>
<m:out value="${x._}" codec="JSON:encode"/>
<m:out value="${x}" codec="JSON:encode"/>

Note that only booleans and numbers are encoded without quotes. If "${3.2}" above (which is evaluated as double) is replaced with string "3.2", then the output will be "3.2" with quotes.

For a NestedMap, you can use "__source" or "__json" property instead:

<m:map export="@{_.__json}">
  <m:set property="__source">{"a":"\'quoted\'","_":[3.2,true],"b":{"r":"s","p":"q"}}</m:set>
</m:map>

When necessary, it is possible to run Javascript code on the server using "Javascript:eval" codec (this requires "js.jar" from Rhino project in your CLASSPATH (e.g., "/WEB-INF/lib)). For example:

<%-- environment for Javascript --%>

<m:map var="env">
  <m:set property="x" value="123"/>
</m:map>

<%-- this prints "ok" --%>

<m:out codec="Javascript:eval:@{env}">

  // returns "ok" if d is an integer
       
  function checkInteger(d)
  {
    if (!d)
    {
      return "missing";
    }
    else if (!d.match("[+-]?[0-9]+"))
    { 
      return "format";
    }
    else
    {
      return "ok";
    }
  }

  // x is set by env

  checkInteger(x);
</m:set>

It is also possible to compile Javascript code using "Javascript:compile" codec. The returned value can be used as the operand for "Javascript:eval". For example, the code above can be pre-compiled as follows:

<m:set var="compiled" codec="Javascript:compile">
  function checkInteger(d)
  {
    if (!d)
    {
      return "missing";
    }
    else if (!d.match("[+-]?[0-9]+"))
    { 
      return "format";
    }
    else
    {
      return "ok";
    }
  }

  checkInteger(x);
</m:set>

<%-- evaluates compiled code --%>

<m:out value="${compiled}" codec="Javascript:eval:@{env}"/>

8.7 NodeMap

NodeMap is a special kind of NestedMap that mirrors XML DOM Nodes. Please refer to Sun's documentation for more information about DOM Nodes. DOM Nodes are mapped to NestedMaps with "type", "name", "value", "attributes", and "_" list properties as follows:

A DOM Node can be converted to NodeMap by using "NodeMap:encode" codec, and a NodeMap can be converted to DOM Node by using "NodeMap:decode" codec. For example:

<m:map var="inputs" source="{a:alpha, b:beta, c:gamma}"/>

<%-- convert a template to NodeMap and take the "input" part (note that 
the root element is "Document" --%>

<m:set var="inputSegment" codec="XML:parse|NodeMap:encode|Bean:get:@_.0">
  <input type="text" style="text-align:right" name="x" value="y" />
</m:set>

<%-- replace name/value attributes for each entry of "inputs" and output as HTML --%>

<c:forEach var="input" items="${inputs.__entryListSorted}">
  <m:map source="${inputSegment}" codec="NodeMap:decode|XML:output:method=html">
    <m:set property="@attributes.name" value="${input.key}"/>
    <m:set property="@attributes.value" value="${input.value}"/>
  </m:map>
</c:forEach>

<%-- similarly generated option list --%>

<m:set var="selected" value="b"/>

<m:set var="optionSegment" codec="XML:parse|NodeMap:encode|Bean:get:@_.0">
  <option style="text-align:right" value="y">name</option>
</m:set>

<select style="text-align:right">
<c:forEach var="input" items="${inputs.__entryListSorted}">
  <m:map source="${optionSegment}" codec="NodeMap:decode|XML:output:method=html">
    <m:set property="@attributes.value" value="${input.key}"/>
    <m:set property="@_.0" value="${input.value}"/>
    <m:set property="@attributes.selected" value="selected" test="${input.key == selected}"/>
  </m:map>
</c:forEach>
</select>

outputs the following:

<input name="a" style="text-align:right" type="text" value="alpha">
<input name="b" style="text-align:right" type="text" value="beta">
<input name="c" style="text-align:right" type="text" value="gamma">

<select style="text-align:right">
  <option style="text-align:right" value="a">alpha</option>
  <option selected style="text-align:right" value="b">beta</option>
  <option style="text-align:right" value="c">gamma</option>
</select>

9 Customization

This section assumes that you are familiar with writing custom JSP tags. You need to have "yuzu.jar" in your CLASSPATH along with JSTL jar files and other servlet/jsp-related class files.

9.1 Tags without custom attributes

To build a custom YUZU tag that doesn't need custom attributes,

For example, the following code defines a tag that converts given tagvalue to lower case as String:

package com.mycompany.jsp.tag;

import com.micronova.jsp.tag.*;

public class LowerCaseTag extends YuzuTag
{
    protected Object processValue(Object tagValue) throws Exception
    {
        if (tagValue != null)
        {
            tagValue = tagValue.toString().toLowerCase();
        }

        return tagValue;
    }
}

Then publish it by adding to the TLD file something like the following:

<tag>
 <name>lowerCase</name>
 <tag-class>com.mycompany.jsp.tag.LowerCaseTag</tag-class>
 <body-content>JSP</body-content>

 <attribute><name>var</name><required>false</required></attribute>
 <attribute><name>target</name><required>false</required></attribute>
 <attribute><name>property</name><required>false</required></attribute>
 <attribute><name>attribute</name><required>false</required></attribute>
 <attribute><name>scope</name><required>false</required></attribute>
 <attribute><name>value</name><required>false</required></attribute>
 <attribute><name>default</name><required>false</required></attribute>
 <attribute><name>className</name><required>false</required></attribute>
 <attribute><name>source</name><required>false</required></attribute>
 <attribute><name>exportCodec</name><required>false</required></attribute>
 <attribute><name>importCodec</name><required>false</required></attribute>
 <attribute><name>processCodec</name><required>false</required></attribute>
 <attribute><name>codec</name><required>false</required></attribute>
 <attribute><name>assignCodec</name><required>false</required></attribute>
 <attribute><name>test</name><required>false</required></attribute>
 <attribute><name>local</name><required>false</required></attribute>
 <attribute><name>assign</name><required>false</required></attribute>
 <attribute><name>export</name><required>false</required></attribute>
 <attribute><name>doesExport</name><required>false</required></attribute>
</tag>

For JSP 2.0, add "<rtexprvalue>true</rtexprvalue>" to each "attribute" statement.

All YUZU attributes work with the new "lowerCase" tag. For example, the following code:

<m:map>
  <m:lowerCase property="lower" value="UpperCase"/>
</m:map>

outputs:

lower=uppercase

9.2 Tags with custom attributes

To build a custom YUZU tag with custom attributes, the following extra steps are necessary:

For example, the following code defines a tag that "multiplies" (appends to itself) given tagvalue as String. It takes a custom integer attribute named "count".

package com.mycompany.jsp.tag;

import com.micronova.jsp.tag.*;

public class MultiplyTag extends YuzuTag
{
    /** "count" attribute */

    Integer _count;

    /** initialization.  Make sure super.init() is called first. */

    protected void init()
    {
        super.init();

        /** default count is 2 */

        _count = new Integer(2);
    }

    /** processes tagValue. */

    protected Object processValue(Object tagValue) throws Exception
    {
        if (tagValue != null)
        {
            String tagValueString = tagValue.toString();
            StringBuffer buffer = new StringBuffer();

            for (int i = _count.intValue(); --i >= 0;)
            {
                buffer.append(tagValueString);
            }

            tagValue = buffer.toString();
        }

        return tagValue;
    }

    /** attribute setter for "count".*/

    public void setCount(Object expression) throws Exception
    {
        _count = (Integer)evaluateAttribute("count", expression, Integer.class, _count);
    }
}

The method "evaluateAttribute()" takes the name of the attribute (for error output), an EL expression, the type of the value resulting from evaluating the EL expression, and the attribute's current value. Note that the "expression" is an Object, not a String, to make attribute attribute work ("evaluateAttribute()" returns the "expression" object as-is if called within tag body).

The TLD entry will be something like this:

<tag>
 <name>multiply</name>
 <tag-class>com.mycompany.jsp.tag.MultiplyTag</tag-class>
 <body-content>JSP</body-content>

 <attribute><name>var</name><required>false</required></attribute>
 <attribute><name>target</name><required>false</required></attribute>
 <attribute><name>property</name><required>false</required></attribute>
 <attribute><name>attribute</name><required>false</required></attribute>
 <attribute><name>scope</name><required>false</required></attribute>
 <attribute><name>value</name><required>false</required></attribute>
 <attribute><name>default</name><required>false</required></attribute>
 <attribute><name>className</name><required>false</required></attribute>
 <attribute><name>source</name><required>false</required></attribute>
 <attribute><name>exportCodec</name><required>false</required></attribute>
 <attribute><name>importCodec</name><required>false</required></attribute>
 <attribute><name>processCodec</name><required>false</required></attribute>
 <attribute><name>codec</name><required>false</required></attribute>
 <attribute><name>assignCodec</name><required>false</required></attribute>
 <attribute><name>test</name><required>false</required></attribute>
 <attribute><name>local</name><required>false</required></attribute>
 <attribute><name>assign</name><required>false</required></attribute>
 <attribute><name>export</name><required>false</required></attribute>
 <attribute><name>doesExport</name><required>false</required></attribute>

 <attribute><name>count</name><required>false</required></attribute>
</tag>

For JSP 2.0, add "<rtexprvalue>true</rtexprvalue>" to each "attribute" statement.

10 JSP 2.0 Functions

This chapter only applies to the JSP 2.0 version of YUZU.

JSP 2.0 introduces EL functions, but unfortunately it does not support multiple function signatures, so the tag descriptor "m.tld" declares YUZU codecs as EL functions using the following naming convention by default:

m:CodecName_CodecMethod[_NumberOfArguments](...)

If "NumberOfArguments" suffix is omitted, it refers to the method with the smallest number of arguments (without optional arguments). For example, "m:URL_encode" is equivalent to "m:URL_encode_1", and is different from "m:URL_encode_2" which takes optional ENCODING argument. Note that the object to which the codec is applied is always the first argument. The following two are equivalent:

<m:out value="Hello World" exportCodec="URL:encode:utf-8|Hex:encode"/>

<m:out value="${m:Hex_encode(m:URL_encode_2('Hello World', 'utf-8'))}"/>

Another problem is that EL functions are not well supported in dynamic EL evaluation yet (e.g., the prefix used to declare EL functions is only known at compile time). Because of this, YUZU uses hardcoded "fn" prefix for JSTL standard functions, and "m" prefix for YUZU codec functions when EL expressions are dynamically evaluated (i.e., in "@{...}" or "%{...}").

For dynamic EL evaluation, you can also use codec-style function names such as "URL:encode", or "com.yourcompany.codec.Codec:method". To handle multiple function signatures, you can append the number of arguments using underscore (e.g., "String:join_2"), or append "_max" for the method with the largest number of arguments, or "_min" for the method with the smallest number of arguments (this is the default). For example, "URL:encode_max" is equivalent to "URL:encode_2", and "URL:encode" is equivalent to "URL:encode_min" or "URL:encode_1".

There is no mechanism to pass implicit variables in JSP 2.0 EL functions yet, so you need to supply all variables explicitly. For example, the following are equivalent:

<m:out value="index.jsp" codec="_JSP:encodeURL"/>

<m:out value="${m:JSP_encodeURL(pageContext, 'index.jsp')}"/>