blob: d48aa85a393624d76d96fc151b5aa49cc483fccf [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sling.scripting.sightly.impl.plugin;
import java.util.Map;
import org.apache.sling.scripting.sightly.compiler.commands.Conditional;
import org.apache.sling.scripting.sightly.compiler.commands.Loop;
import org.apache.sling.scripting.sightly.compiler.commands.VariableBinding;
import org.apache.sling.scripting.sightly.compiler.expression.Expression;
import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperation;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperator;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.Identifier;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.NumericConstant;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperation;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperator;
import org.apache.sling.scripting.sightly.impl.compiler.PushStream;
import org.apache.sling.scripting.sightly.impl.compiler.Syntax;
import org.apache.sling.scripting.sightly.impl.compiler.frontend.CompilerContext;
public class ListPlugin extends AbstractRepeatPlugin {
public ListPlugin() {
name = "list";
priority = 130;
}
@Override
public PluginInvoke invoke(final Expression expression, final PluginCallInfo callInfo, final CompilerContext compilerContext) {
return new DefaultPluginInvoke() {
private String listVariable = compilerContext.generateVariable("collectionVar");
private String collectionSizeVar = compilerContext.generateVariable("size");
private String collectionNotEmpty = compilerContext.generateVariable("notEmpty");
private String beginVariable = compilerContext.generateVariable(BEGIN);
private String stepVariable = compilerContext.generateVariable(STEP);
private String endVariable = compilerContext.generateVariable(END);
private String validStartStepEnd = compilerContext.generateVariable("validStartStepEnd");
private boolean beginAtIndexZero = false;
private boolean stepOne = false;
@Override
public void beforeElement(PushStream stream, String tagName) {
stream.write(new VariableBinding.Start(listVariable, expression.getRoot()));
stream.write(new VariableBinding.Start(collectionSizeVar,
new UnaryOperation(UnaryOperator.LENGTH, new Identifier(listVariable))));
stream.write(new VariableBinding.Start(collectionNotEmpty, new BinaryOperation(BinaryOperator.GT, new Identifier
(collectionSizeVar), NumericConstant.ZERO)));
stream.write(new Conditional.Start(collectionNotEmpty, true));
Map<String, ExpressionNode> options = expression.getOptions();
if (options.containsKey(BEGIN)) {
stream.write(new VariableBinding.Start(beginVariable, expression.getOptions().get(BEGIN)));
} else {
beginAtIndexZero = true;
stream.write(new VariableBinding.Start(beginVariable, NumericConstant.ZERO));
}
if (options.containsKey(STEP)) {
stream.write(new VariableBinding.Start(stepVariable, expression.getOptions().get(STEP)));
} else {
stepOne = true;
stream.write(new VariableBinding.Start(stepVariable, NumericConstant.ONE));
}
if (options.containsKey(END)) {
stream.write(new VariableBinding.Start(endVariable, expression.getOptions().get(END)));
} else {
stream.write(new VariableBinding.Start(endVariable, new Identifier(collectionSizeVar)));
}
stream.write(new VariableBinding.Start(validStartStepEnd,
new BinaryOperation(BinaryOperator.AND,
new BinaryOperation(BinaryOperator.AND,
new BinaryOperation(BinaryOperator.LT, new Identifier(beginVariable), new Identifier(collectionSizeVar)),
new BinaryOperation(
BinaryOperator.AND,
new BinaryOperation(BinaryOperator.GEQ, new Identifier(beginVariable), NumericConstant.ZERO),
new BinaryOperation(BinaryOperator.GT, new Identifier(stepVariable), NumericConstant.ZERO)
)
),
new BinaryOperation(BinaryOperator.GT, new Identifier(endVariable), NumericConstant.ZERO)
)
)
);
stream.write(new Conditional.Start(validStartStepEnd, true));
}
@Override
public void beforeChildren(PushStream stream) {
String itemVariable = decodeItemVariable();
String loopStatusVar = Syntax.itemLoopStatusVariable(itemVariable);
String indexVariable = compilerContext.generateVariable("index");
stream.write(new Loop.Start(listVariable, itemVariable, indexVariable));
stream.write(new VariableBinding.Start(loopStatusVar, buildStatusObj(indexVariable, collectionSizeVar)));
String stepConditionVariable = compilerContext.generateVariable("stepCondition");
stream.write(new VariableBinding.Start(stepConditionVariable,
beginAtIndexZero && stepOne ? new NumericConstant(0) :
new BinaryOperation(
BinaryOperator.REM,
new BinaryOperation(
BinaryOperator.SUB,
new Identifier(indexVariable),
new Identifier(beginVariable)
),
new Identifier(stepVariable))
)
);
String loopTraversalVariable = compilerContext.generateVariable("traversal");
stream.write(new VariableBinding.Start(loopTraversalVariable,
new BinaryOperation(
BinaryOperator.AND,
new BinaryOperation(
BinaryOperator.AND,
new BinaryOperation(BinaryOperator.GEQ, new Identifier(indexVariable), new Identifier(beginVariable)),
new BinaryOperation(BinaryOperator.LEQ, new Identifier(indexVariable), new Identifier(endVariable))
),
new BinaryOperation(BinaryOperator.EQ, new Identifier(stepConditionVariable), NumericConstant.ZERO)
)
)
);
stream.write(new Conditional.Start(loopTraversalVariable, true));
}
@Override
public void afterChildren(PushStream stream) {
stream.write(Conditional.END);
stream.write(VariableBinding.END);
stream.write(VariableBinding.END);
stream.write(VariableBinding.END);
stream.write(Loop.END);
}
@Override
public void afterElement(PushStream stream) {
stream.write(Conditional.END);
stream.write(VariableBinding.END);
stream.write(VariableBinding.END);
stream.write(VariableBinding.END);
stream.write(VariableBinding.END);
stream.write(Conditional.END);
stream.write(VariableBinding.END);
stream.write(VariableBinding.END);
stream.write(VariableBinding.END);
}
private String decodeItemVariable() {
String[] args = callInfo.getArguments();
if (args.length > 0) {
return args[0];
}
return Syntax.DEFAULT_LIST_ITEM_VAR_NAME;
}
};
}
}