org.jopendocument.dom.template.statements.ForEach Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jOpenDocument Show documentation
Show all versions of jOpenDocument Show documentation
jOpenDocument is a free library for developers looking to use
Open Document files without OpenOffice.org.
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2013 jOpenDocument, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU
* General Public License Version 3 only ("GPL").
* You may not use this file except in compliance with the License.
* You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html
* See the License for the specific language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*
*/
package org.jopendocument.dom.template.statements;
import org.jopendocument.dom.template.TemplateException;
import org.jopendocument.dom.template.engine.DataModel;
import org.jopendocument.dom.template.engine.Processor;
import org.jopendocument.util.ExceptionUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.logging.Logger;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.xpath.XPath;
/**
* ForEach tag: repeats a document element for each item in a collection.
* Attributes:
*
* - element (required): the document element to which this tag applies, e.g. "table-row"
* - items (required): expression evaluating to a Collection to be iterated
* - var (required): variable name where the current iteration item is stored
* - alternate (optional): number of document elements to be alternated during iteration,
* typically used for alternating row background colors with a value of "2"
* - multiple (optional): number of document elements to be copied during iteration, not
* compatible with "alternate".
*
*/
public class ForEach extends BaseStatement {
private static Logger logger = Logger.getLogger(ForEach.class.getName());
public ForEach() {
super("forEach");
}
@SuppressWarnings("unchecked")
public void prepare(Element script, Element command) throws TemplateException {
String elementName = command.getAttributeValue("element");
if (elementName == null)
throw new TemplateException("missing required attribute for forEach tag: element");
String itemsExpression = command.getAttributeValue("items");
if (itemsExpression == null)
throw new TemplateException("missing required attribute for forEach tag: items");
String varName = command.getAttributeValue("var");
if (varName == null)
throw new TemplateException("missing required attribute for forEach tag: var");
// alternate
final int alternate;
String alternateString = command.getAttributeValue("alternate");
if (alternateString == null) {
alternate = 1;
} else {
try {
alternate = Integer.parseInt(alternateString);
} catch (Throwable t) {
throw new TemplateException("invalid alternate attribute for forEach tag: " + alternateString);
}
}
// multiple
final int multiple;
final String multipleString = command.getAttributeValue("multiple");
if (multipleString == null) {
multiple = 0;
} else {
try {
multiple = Integer.parseInt(multipleString);
} catch (Throwable t) {
throw new TemplateException("invalid alternate attribute for forEach tag: " + multipleString);
}
}
if (alternate > 1 && multiple > 0) {
throw new TemplateException("both alternate and multiple have been specified");
}
Element element = getAncestorByName(script, elementName);
if (element == null) {
throw new TemplateException("no such element enclosing forEach: " + elementName + " for expression: " + itemsExpression);
}
Element parent = element.getParentElement();
// Element forEachTag = parent.addElement(getQName("forEach")).addAttribute("items",
// itemsExpression).addAttribute("var", varName);
final Element forEachTag = getElement("forEach").setAttribute("items", itemsExpression).setAttribute("var", varName);
final int siblingsToAdd;
if (multiple > 0) {
forEachTag.setAttribute("multiple", "yes");
siblingsToAdd = multiple - 1;
} else {
siblingsToAdd = alternate - 1;
}
int index = parent.indexOf(element);
List targets = new ArrayList();
targets.add(element);
if (siblingsToAdd > 0) {
List siblings;
try {
final XPath xpath = XPath.newInstance("following-sibling::*");
siblings = xpath.selectNodes(element);
} catch (JDOMException e) {
throw ExceptionUtils.createExn(TemplateException.class, "xpath error", e);
}
if (siblingsToAdd > siblings.size())
throw new TemplateException("alternate or multiple is greater (" + (siblingsToAdd + 1) + ") than actual table size (" + siblings.size() + ").");
for (int i = 0; i < siblingsToAdd; i++) {
Element sibling = (Element) siblings.get(i);
targets.add(sibling);
sibling.detach();
}
}
element.detach();
parent.getContent().add(index, forEachTag);
for (int i = 0; i < targets.size(); i++) {
forEachTag.addContent(targets.get(i));
}
script.detach();
}
@SuppressWarnings("unchecked")
public void execute(Processor processor, Element tag, DataModel model) throws TemplateException {
String varName = tag.getAttributeValue("var");
String itemsExpression = tag.getAttributeValue("items");
final boolean multiple = "yes".equals(tag.getAttributeValue("multiple"));
Object value = model.eval(itemsExpression);
if (value == null) {
logger.info("forEach items: null expression: " + itemsExpression);
return;
}
final Collection items;
if (value instanceof Collection)
items = (Collection) value;
else if (value instanceof Object[])
items = Arrays.asList((Object[]) value);
else
items = null;
if (items == null && !(value instanceof Iterator))
throw new TemplateException("forEach items neither a Collection nor an Iterator: " + itemsExpression + " => " + value.getClass() + ":" + value);
List targets = tag.getChildren();
List parentContent = tag.getParentElement().getContent();
int iterationCount = 0;
int index = parentContent.indexOf(tag);
// ne peut utiliser de Map si plusieurs fois le même éléments dans 'items'
final List