/*
* Copyright 2004-2012 the Seasar Foundation and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.seasar.mayaa.impl.engine.processor;
import java.util.Iterator;
import java.util.Stack;
import org.seasar.mayaa.cycle.scope.AttributeScope;
import org.seasar.mayaa.engine.Page;
import org.seasar.mayaa.engine.processor.IterationProcessor;
import org.seasar.mayaa.engine.processor.ProcessStatus;
import org.seasar.mayaa.engine.processor.ProcessorProperty;
import org.seasar.mayaa.impl.cycle.CycleUtil;
import org.seasar.mayaa.impl.cycle.DefaultCycleLocalInstantiator;
import org.seasar.mayaa.impl.provider.ProviderUtil;
import org.seasar.mayaa.impl.util.IteratorUtil;
/**
* @author Koji Suga (Gluegent, Inc.)
*/
public class ForEachProcessor extends TemplateProcessorSupport
implements IterationProcessor {
private static final long serialVersionUID = 5143412585484588336L;
private static final String PROCESS_TIME_INFO_KEY =
ForEachProcessor.class.getName() + "#processTimeInfo";
static {
CycleUtil.registVariableFactory(PROCESS_TIME_INFO_KEY,
new DefaultCycleLocalInstantiator() {
public Object create(Object owner, Object[] params) {
ForEachProcessor processor = (ForEachProcessor) owner;
return processor.new IndexIteratorStack();
}
});
}
private String _var;
protected ProcessorProperty _items;
protected String _indexName;
// MLD property, required
public void setVar(String var) {
_var = var;
}
// MLD property, required=true, expectedClass=void
public void setItems(ProcessorProperty items) {
if (items == null) {
throw new IllegalArgumentException();
}
_items = items;
}
// MLD property
public void setIndexName(String indexName) {
_indexName = indexName;
}
public boolean isIteration() {
return true;
}
/**
* 次の要素があるかどうか判定します。
* また、次の要素がある場合はそれを現在のスコープのvarにセットします。
* このとき必要ならばindexもセットします。
*
* @return 次の要素があるならtrue
*/
protected boolean prepareEvalBody() {
IndexIteratorStack stack = (IndexIteratorStack) CycleUtil.getLocalVariable(
PROCESS_TIME_INFO_KEY, this, null);
IndexedIterator iterator = stack.peek();
if (iterator.hasNext() == false) {
return false;
}
AttributeScope currentScope = CycleUtil.getCurrentPageScope();
currentScope.setAttribute(_var, iterator.next());
if (_indexName != null) {
currentScope.setAttribute(_indexName, iterator.getNextIndex());
}
return true;
}
public ProcessStatus doStartProcess(Page topLevelPage) {
if (_items == null || _var == null) {
throw new IllegalStateException();
}
IndexIteratorStack stack = (IndexIteratorStack) CycleUtil.getLocalVariable(
PROCESS_TIME_INFO_KEY, this, null);
stack.pushOne();
if (prepareEvalBody() == false) {
stack.pop();
return ProcessStatus.SKIP_BODY;
}
return ProcessStatus.EVAL_BODY_INCLUDE;
}
public ProcessStatus doAfterChildProcess() {
if (prepareEvalBody() == false) {
IndexIteratorStack stack = (IndexIteratorStack) CycleUtil.getLocalVariable(
PROCESS_TIME_INFO_KEY, this, null);
stack.pop();
return ProcessStatus.SKIP_BODY;
}
return ProcessStatus.EVAL_BODY_AGAIN;
}
// for serialize
private void readObject(java.io.ObjectInputStream in)
throws java.io.IOException, ClassNotFoundException {
in.defaultReadObject();
}
// support class
/**
* forEachの入れ子に対応するためのIndexIterator専用Stackクラス。
*
* @author Koji Suga (Gluegent Inc.)
*/
private class IndexIteratorStack {
private Stack _stack;
public IndexIteratorStack() {
_stack = new Stack();
}
public void pushOne() {
_stack.push(new IndexedIterator());
}
public IndexedIterator pop() {
return (IndexedIterator) _stack.pop();
}
public IndexedIterator peek() {
return (IndexedIterator) _stack.peek();
}
}
/**
* nextの回数を取得できるIterator。
* getNextIndex() は 0~を返します。
* ただし余分な処理をしないために、next()の次にgetNextINdex()を呼ぶ
* 形で使うことを前提とします。
*
* @author Koji Suga (Gluegent Inc.)
*/
private class IndexedIterator {
private int _index;
private Iterator _iterator;
public IndexedIterator() {
if (_indexName != null) {
_index = -1;
}
Object obj =
ProviderUtil.getScriptEnvironment().convertFromScriptObject(
_items.getValue().execute(null));
_iterator = IteratorUtil.toIterator(obj);
}
public Integer getNextIndex() {
return new Integer(++_index);
}
public boolean hasNext() {
return _iterator.hasNext();
}
public Object next() {
return _iterator.next();
}
}
}