blob: ff6bb9a23ceaabd86029eb511ce7129944d056cf [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.compiler.expression.nodes;
import java.util.Collection;
import org.apache.sling.scripting.sightly.compiler.SightlyCompilerException;
import org.apache.sling.scripting.sightly.compiler.util.ObjectModel;
/**
* Binary operators used in expressions.
*/
public enum BinaryOperator {
/**
* Logical conjunction.
*/
AND {
@Override
public Object eval(Object left, Object right) {
return (ObjectModel.toBoolean(left)) ? right : left;
}
},
/**
* Logical disjunction.
*/
OR {
@Override
public Object eval(Object left, Object right) {
return (ObjectModel.toBoolean(left)) ? left : right;
}
},
/**
* String concatenation.
*/
CONCATENATE {
@Override
public Object eval(Object left, Object right) {
return ObjectModel.toString(left).concat(ObjectModel.toString(right));
}
},
/**
* Less than.
*/
LT {
@Override
public Object eval(Object left, Object right) {
return lt(left, right);
}
},
/**
* Less or equal.
*/
LEQ {
@Override
public Object eval(Object left, Object right) {
return leq(left, right);
}
},
/**
* Greater than.
*/
GT {
@Override
public Object eval(Object left, Object right) {
return !leq(left, right);
}
},
/**
* Greater or equal.
*/
GEQ {
@Override
public Object eval(Object left, Object right) {
return !lt(left, right);
}
},
/**
* Equal.
*/
EQ {
@Override
public Object eval(Object left, Object right) {
return eq(left, right);
}
},
/**
* Not equal.
*/
NEQ {
@Override
public Object eval(Object left, Object right) {
return !eq(left, right);
}
},
/**
* Strict version of equality, restricted to just some types.
*/
STRICT_EQ {
@Override
public Object eval(Object left, Object right) {
return strictEq(left, right);
}
},
/**
* Strict version of the not-equal operator.
*/
STRICT_NEQ {
@Override
public Object eval(Object left, Object right) {
return !strictEq(left, right);
}
},
/**
* Addition.
*/
ADD {
@Override
public Object eval(Object left, Object right) {
return adjust(ObjectModel.toNumber(left).doubleValue() + ObjectModel.toNumber(right).doubleValue());
}
},
/**
* Difference.
*/
SUB {
@Override
public Object eval(Object left, Object right) {
return adjust(ObjectModel.toNumber(left).doubleValue() - ObjectModel.toNumber(right).doubleValue());
}
},
/**
* Multiplication.
*/
MUL {
@Override
public Object eval(Object left, Object right) {
return adjust(ObjectModel.toNumber(left).doubleValue() * ObjectModel.toNumber(right).doubleValue());
}
},
/**
* Floating point division.
*/
DIV {
@Override
public Object eval(Object left, Object right) {
return adjust(ObjectModel.toNumber(left).doubleValue() / ObjectModel.toNumber(right).doubleValue());
}
},
/**
* Integer division.
*/
I_DIV {
@Override
public Object eval(Object left, Object right) {
return ObjectModel.toNumber(left).intValue() / ObjectModel.toNumber(right).intValue();
}
},
/**
* Reminder.
*/
REM {
@Override
public Object eval(Object left, Object right) {
return adjust(ObjectModel.toNumber(left).intValue()
% ObjectModel.toNumber(right).intValue());
}
},
IN {
@Override
public Object eval(Object left, Object right) {
return inOp(left, right);
}
};
public static boolean eq(Object left, Object right) {
if (left == null) {
return right == null;
}
return left.equals(right);
}
public static boolean lt(final Object left, final Object right) {
if (left instanceof Number && right instanceof Number) {
return ((Number) left).doubleValue() < ((Number) right).doubleValue();
}
throw new SightlyCompilerException("Operands are not of the same type: comparison is supported for Number types only.");
}
public static boolean leq(final Object left, final Object right) {
if (left instanceof Number && right instanceof Number) {
return ((Number) left).doubleValue() <= ((Number) right).doubleValue();
}
throw new SightlyCompilerException("Operands are not of the same type: comparison is supported for Number types only.");
}
public static boolean strictEq(Object left, Object right) {
if (left instanceof Number && right instanceof Number) {
return ((Number) left).doubleValue() == ((Number) right).doubleValue();
}
if (left instanceof String && right instanceof String) {
return left.equals(right);
}
if (left instanceof Boolean && right instanceof Boolean) {
return left.equals(right);
}
if (left == null && right == null) {
return true;
}
if ((left instanceof Enum && right instanceof String) || (left instanceof String && right instanceof Enum)) {
String constantName = left instanceof String ? (String) left : (String) right;
Enum enumObject = left instanceof Enum ? (Enum) left : (Enum) right;
try {
Enum enumComparisonObject = Enum.valueOf(enumObject.getClass(), constantName);
return enumComparisonObject == enumObject;
} catch (Exception e) {
return false;
}
}
if (left == null || right == null) {
Object notNull = (left != null) ? left : right;
if (notNull instanceof String || notNull instanceof Boolean || notNull instanceof Number) {
return false;
}
}
throw new SightlyCompilerException("Operands are not of the same type: the equality operator can only be applied to String, Number" +
" and Boolean types.");
}
public static boolean inOp(Object left, Object right) {
if (left instanceof String && right instanceof String) {
String leftString = (String) left;
String rightString = (String) right;
return rightString.contains(leftString);
}
Collection rightElements = ObjectModel.toCollection(right);
for (Object element : rightElements) {
if (element.equals(left)) {
return true;
}
}
return false;
}
private static Number adjust(double x) {
if (Math.floor(x) == x) {
return (int) x;
}
return x;
}
public abstract Object eval(Object left, Object right);
}