![JAR search and dependency download from the Maven repository](/logo.png)
js.template.xhtml.NumberingOperator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of js-xhtml-template Show documentation
Show all versions of js-xhtml-template Show documentation
Reference implementation for j(s)-lib template API, declarative, natural and based on X(HT)ML language.
The newest version!
package js.template.xhtml;
import java.io.IOException;
import java.util.Stack;
import js.dom.Element;
import js.template.TemplateException;
/**
* Set element text content accordingly numbering format and item index. It is usable inside elements with ordered lists and
* maps operators which are responsible for index creation and increment; this operators deals only with formatting. Trying to
* use it outside index scope will rise exception.
*
*
* <ul data-olist=".">
* <li data-numbering="D.2.%s)"></li>
* </ul>
*
*
* After template execution li elements text content will be: D.2.a) D.2.b) ... D.2.i). Operand is an format describing
* numbering format with next syntax:
*
*
* numberingFormat := character* (indexFormat character*)+
* character := any less "%"
* indexFormat := "%" formatCode
* formatCode := "s" | "S" | "i" | "I" | "n"
*
*
* As observe, syntax allows for multiple index formating usable for nested list. Below sample will expand in a series like:
* A.1, A.2 ... , B.1, B.2 ... .
*
*
* <section data-olist="outer">
* <ul data-olist="inner">
* <li data-numbering="%S.%n"></li>
* </ul>
* </section>
*
*
* Because only ordered list have index, mixing order and unordered list is supported, although not really a use case.
*
*
* <ul data-olist=".">
* <li>
* <h1 data-numbering="%I"></h1>
* <ul data-list="."> // unordered list between two with numbering
* <li>
* <h2></h2>
* <ul data-olist=".">
* <li>
* <h3 data-numbering="%I.%S"></h3>
* </li>
* </ul>
* </li>
* </ul>
* </li>
* </ul>
*
*
* Because of mixed unordered list, h3 elements will have a series like: I.A, I.B, I.A, I.B, II.A, II.B, II.A, II.B ... .
*
* @author Iulian Rotaru
*/
final class NumberingOperator extends Operator
{
/**
* Operator serializer.
*/
private Serializer serializer;
/**
* Construct numbering operator instance.
*
* @param serializer parent serializer.
*/
NumberingOperator(Serializer serializer)
{
this.serializer = serializer;
}
/**
* Insert formatted numbering as element text content. If serializer indexes stack is empty throws templates exception;
* anyway, validation tool running on build catches numbering operator without surrounding ordered list.
*
* @param element element declaring numbering operator,
* @param scope scope object,
* @param format numbering format, see class description for syntax,
* @param arguments optional arguments, not used.
* @return always returns null to signal full processing.
* @throws TemplateException if serializer indexes stack is empty.
* @throws IOException if underlying writer fails to write.
*/
@Override
protected Object doExec(Element element, Object scope, String format, Object... arguments) throws IOException
{
Stack indexes = this.serializer.getIndexes();
if(indexes.size() == 0) {
throw new TemplateException("Required ordered collection index is missing. Numbering operator cancel execution.");
}
this.serializer.writeTextContent(getNumbering(this.serializer.getIndexes(), format));
return null;
}
/**
* Parse numbering format and inject index values. First argument,the stack of indexes, is global per serializer and format is
* the operand literal value. For nested numbering, format may contain more than one single format code; this is the reason
* first argument is the entire indexes stack, not only current index. Given a stack with four indexes those values are 1, 2,
* 3 and 4 and "%S - %I.%n-%s)" the format, resulting formatted string is "A - II.3-d)".
*
* @param format numbering format.
* @return formatted numbering.
*/
private static String getNumbering(Stack indexes, String format)
{
StringBuilder sb = new StringBuilder();
int i = format.length();
int j = i;
int indexPosition = indexes.size() - 1;
for(;;) {
i = format.lastIndexOf('%', i);
if(i == -1 && j > 0) {
sb.insert(0, format.substring(0, j));
break;
}
// if(i > 0 && format.charAt(i - 1) == '%') continue;
if(i + 2 < format.length()) sb.insert(0, format.substring(i + 2, j));
if(i + 1 == format.length()) continue;
NumberingFormat numberingFormat = getNumberingFormat(format.charAt(i + 1));
sb.insert(0, numberingFormat.format(indexes.get(indexPosition--).value));
if(i == 0) break;
j = i;
i--;
}
return sb.toString();
}
/**
* Factory method for numbering format implementations. If format code is not recognized throws templates exception; anyway
* validation tool catches this condition. See {@link NumberingFormat} for the list of valid format codes.
*
* @param formatCode single char format code.
* @return requested numbering format instance.
* @throws TemplateException if format code is not recognized.
*/
public static NumberingFormat getNumberingFormat(char formatCode)
{
switch(formatCode) {
case 'n':
return new ArabicNumeralNumbering();
case 's':
return new LowerCaseStringNumbering();
case 'S':
return new UpperCaseStringNumbering();
case 'i':
return new LowerCaseRomanNumbering();
case 'I':
return new UpperCaseRomanNumbering();
}
throw new TemplateException("Invalid numbering format code |%s|.", formatCode);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy