/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2011, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.process.mapfile;
import java.util.List;
import java.util.ArrayList;
import org.geotoolkit.process.mapfile.MapfileExpressionTokenizer.Token;
import org.geotoolkit.filter.DefaultFilterFactory2;
import org.geotoolkit.processing.AbstractProcess;
import org.geotoolkit.process.ProcessException;
import org.geotoolkit.style.DefaultStyleFactory;
import org.geotoolkit.style.MutableStyleFactory;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.FilterFactory;
import org.opengis.parameter.ParameterValueGroup;
import static org.geotoolkit.process.mapfile.MapfileFilterToOGCFilterDescriptor.*;
import static org.geotoolkit.parameter.Parameters.*;
/**
* @author Johann Sorel (Geomatys)
* @module
*/
public class MapfileFilterToOGCFilterProcess extends AbstractProcess{
private static final MutableStyleFactory SF = new DefaultStyleFactory();
private static final FilterFactory FF = new DefaultFilterFactory2();
public MapfileFilterToOGCFilterProcess(final ParameterValueGroup input){
super(INSTANCE, input);
}
@Override
protected void execute() throws ProcessException {
final String text = value(IN_TEXT, inputParameters);
final Expression ref = value(IN_REFERENCE, inputParameters);
final List<Token> tokens = MapfileExpressionTokenizer.toTokens(text);
final Object result = parse(ref, tokens);
getOrCreate(OUT_OGC, outputParameters).setValue(result);
}
private Object parse(final Expression ref,final List<Token> tokens) throws ProcessException{
int index = 0;
Object result = null;
while(index < tokens.size()){
Token token = tokens.get(index);
if("(".equals(token.value)){
//a block, find the block end ad parse it's content
index++;
final int blockEnd = getBlockEnd(tokens, index);
final List<Token> content = tokens.subList(index, blockEnd);
index = blockEnd+1;
result = parse(null, content);
}else if("[".equals(token.value)){
//a property name
index++;
final String propName = tokens.get(index).value;
index++;
//check it's a ']'
final String v = tokens.get(index).value;
if(!"]".equals(v)){
throw new ProcessException("Was expecting a ']' but was : "+v, this, null);
}
index++;
result = FF.property(propName);
}else if("/".equals(token.value)){
//a list of values
final List<Filter> filters = new ArrayList<Filter>();
while(true){
token = tokens.get(++index);
final Filter f = FF.equals(ref, FF.literal(token.value));
filters.add(f);
token = tokens.get(++index);
if("/".equals(token.value)){
break;
}
}
index++;
result = FF.or(filters);
}else if("=".equals(token.value)){
//and equality test
//left side
final Expression left = (Expression) result;
//right side
index++;
final Expression right;
final Token next = tokens.get(index);
if("(".equals(next.value)){
//a sub expression
index++;
final int blockEnd = getBlockEnd(tokens, index);
final List<Token> content = tokens.subList(index, blockEnd);
index = blockEnd+1;
right = (Expression) parse(null, content);
}else{
//a literal value
final List<Token> content = tokens.subList(index, index+1);
right = (Expression) parse(null,content);
index++;
}
result = FF.equals(left, right);
}else if("and".equalsIgnoreCase(token.value)){
//and operand
//left side
final Filter left = (Filter) result;
//right side
index++;
final List<Token> content = tokens.subList(index, tokens.size());
final Filter right = (Filter) parse(null,content);
index = tokens.size();
result = FF.and(left, right);
}else if("or".equalsIgnoreCase(token.value)){
//or operand
//left side
final Filter left = (Filter) result;
//right side
index++;
final List<Token> content = tokens.subList(index, tokens.size());
final Filter right = (Filter) parse(null,content);
index = tokens.size();
result = FF.or(left,right);
}else{
index++;
String text = token.value;
if(text.startsWith("\"") || text.startsWith("'")){
text = text.substring(1, text.length()-1);
}
final Expression literal = toExpression(text);
//a single value
if(ref != null){
//it's a property equal to
result = FF.equals(ref,literal);
}else{
//it's a literal
result = literal;
}
}
}
return result;
}
/**
* Parse string and replace encapsulated property names
*/
private static Expression toExpression(String text){
if(text.startsWith("\"") || text.startsWith("'")){
text = text.substring(1, text.length()-1);
}
final List<Expression> parts = new ArrayList<Expression>();
while(true){
final int from = text.indexOf('[');
final int end = text.indexOf(']');
if(from >= 0){
if(from > 0){
final String before = text.substring(0, from);
parts.add(FF.literal(before));
}
final String propName = text.substring(from+1, end);
parts.add(FF.property(propName));
text = text.substring(end+1);
}else if(text.length()>0 || parts.isEmpty()){
parts.add(FF.literal(text));
break;
}else{
break;
}
}
//concatenate expressions
while(parts.size()>1){
final Expression exp1 = parts.remove(0);
final Expression exp2 = parts.remove(0);
final Expression concat = FF.function("strConcat", exp1, exp2);
parts.add(0,concat);
}
return parts.get(0);
}
/**
* @return int, token index of the closing current block
*/
private static int getBlockEnd(final List<Token> tokens, int from){
int block = 0;
for(; from<tokens.size(); from++){
final Token token = tokens.get(from);
if("(".equals(token.value)){
block++;
}else if(")".equals(token.value)){
block--;
if(block<0){
//we found the end
return from;
}
}
}
return -1;
}
}