blob: fe85f03aabf9506f8190b279c66a8fe8ea4046fa [file] [log] [blame]
/*
* 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.apache.sling.resource.filter.api.impl;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.resource.filter.api.Context;
import org.apache.sling.resource.filter.api.Visitor;
import org.apache.sling.resource.filter.impl.FilterParserConstants;
import org.apache.sling.resource.filter.impl.node.Node;
import org.apache.sling.resource.filter.impl.predicates.Null;
public class ComparisonVisitor implements Visitor<Function<Resource, Object>> {
private Context context;
public ComparisonVisitor(Context context) {
this.context = context;
context.setComparionVisitor(this);
}
@Override
public Function<Resource, Object> visit(Node node) {
switch (node.kind) {
case FilterParserConstants.FUNCTION_NAME:
return functionHandler(node);
case FilterParserConstants.NULL:
return resource -> new Null();
case FilterParserConstants.NUMBER:
return resource -> numericHandler(node.text);
case FilterParserConstants.OFFSETDATETIME:
return resource -> OffsetDateTime.parse(node.text).toInstant();
case FilterParserConstants.DATETIME:
return resource -> LocalDateTime.parse(node.text).atOffset(ZoneOffset.UTC).toInstant();
case FilterParserConstants.DATE:
return resource -> LocalDate.parse(node.text).atStartOfDay(ZoneOffset.UTC).toInstant();
case FilterParserConstants.PROPERTY:
return resource -> {
Object value = valueMapOf(resource).get(node.text);
if (value instanceof Boolean) {
return value.toString();
}
if (value instanceof Calendar) {
return ((Calendar) value).toInstant();
}
return value;
};
case FilterParserConstants.DYNAMIC_ARG:
return resource -> {
String argument = node.text;
return context.getArgument(argument).orElse(new Null());
};
default:
return resource -> node.text;
}
}
private ValueMap valueMapOf(Resource resource) {
if (resource == null || ResourceUtil.isNonExistingResource(resource)) {
return ValueMap.EMPTY;
}
return resource.adaptTo(ValueMap.class);
}
private static Object dateHandler(Object[] arguments) {
if (arguments.length == 0) {
return Instant.now();
}
String dateString = arguments[0].toString();
String formatString = null;
if (arguments.length > 1) {
formatString = arguments[1].toString();
SimpleDateFormat dateFormat = new SimpleDateFormat(formatString);
try {
return Instant.ofEpochMilli(dateFormat.parse(dateString).getTime());
} catch (ParseException e) {
return null;
}
} else {
return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dateString, OffsetDateTime::from).toInstant();
}
}
private Function<Resource, Object> functionHandler(Node node){
// will only get here in the case of the 'FUNCTION' switch case
switch (node.text) {
case "name":
return Resource::getName;
case "path":
return Resource::getPath;
case "date":
return resource -> {
Object[] arguments = node.visitChildren(this).stream().map(funct -> funct.apply(resource))
.toArray();
return dateHandler(arguments);
};
default:
Optional<BiFunction<Object[], Resource, Object>> temp = context.getFunction(node.text);
if (temp.isPresent()) {
final List<Function<Resource, Object>> children2 = node.visitChildren(this);
return resource -> {
Object[] arguments = children2.stream().map(funct -> funct.apply(resource)).toArray();
return temp.get().apply(arguments, resource);
};
}
}
return null;
}
private static Number numericHandler(String numberText) {
Number numericValue = null;
try {
numericValue = Integer.valueOf(numberText);
} catch (NumberFormatException nfe1) {
try {
numericValue = new BigDecimal(numberText);
} catch (NumberFormatException nfe2) {
// swallow
}
}
return numericValue;
}
}