/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.ows.kvp;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.platform.GeoServerExtensions;
import org.geotools.util.DateRange;
import org.geotools.util.NumberRange;
import org.geotools.util.logging.Logging;
/**
* Parses the {@code elevation} parameter of the request.
*
* @author Ariel Nunez, GeoSolutions S.A.S.
* @author Simone Giannecchini, GeoSolutions S.A.S.
* @version $Id$
*/
public class ElevationParser {
static final Logger LOGGER = Logging.getLogger(ElevationParser.class);
/**
* Built-in limits
*/
private final static int MAX_ELEMENTS_ELEVATIONS_KVP;
private final static int DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP = 100;
static {
// initialization of the renderer choice flag
String value = GeoServerExtensions.getProperty("MAX_ELEMENTS_ELEVATIONS_KVP");
// default to true, but allow switching on
if (value == null)
MAX_ELEMENTS_ELEVATIONS_KVP = DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP;
else {
int iVal = -1;
try {
iVal = Integer.parseInt(value.trim());
} catch (Exception e) {
iVal = DEFAULT_MAX_ELEMENTS_ELEVATIONS_KVP;
}
MAX_ELEMENTS_ELEVATIONS_KVP = iVal;
}
}
/**
* Parses the elevation given in parameter. The string may contains either a single double, or a
* start value, end value and a period. In the first case, this method returns a singleton
* containing only the parsed value. In the second case, this method returns a list including
* all elevations from start value up to the end value with the interval specified in the
* {@code value} string.
*
* @param value
* The elevation item or items to parse.
* @return A list of doubles, or an empty list of the {@code value} string is null or empty.
* @throws ParseException
* if the string can not be parsed.
*/
@SuppressWarnings( { "unchecked", "rawtypes" })
public Collection parse(String value) throws ParseException {
if (value == null) {
return Collections.emptyList();
}
value = value.trim();
if (value.length() == 0) {
return Collections.emptyList();
}
final Set values = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
final boolean o1Double = o1 instanceof Double;
final boolean o2Double = o2 instanceof Double;
// o1 date
if (o1Double) {
final Double left = (Double) o1;
if (o2Double) {
// o2 date
return left.compareTo((Double) o2);
}
// o2 number range
return left.compareTo(((NumberRange<Double>) o2).getMinValue());
}
// o1 number range
final NumberRange left = (NumberRange) o1;
if (o2Double) {
// o2 date
return left.getMinValue().compareTo(((Double) o2));
}
// o2 daterange
return left.getMinValue().compareTo(((NumberRange) o2).getMinValue());
}
});
final String[] listValues = value.split(",");
for (String d : listValues) {
if (d.indexOf("/") <= 0) {
addValue(values, Double.valueOf(d.trim()));
} else {
// period
String[] period = d.split("/");
// Only one value given.
if (period.length == 2) {
// Period continuous
final Double begin = Double.valueOf(period[0]);
final Double end = Double.valueOf(period[1]);
addPeriod(values, NumberRange.create(begin, end));
} else if (period.length == 3) {
// Period discrete
final Double begin = Double.valueOf(period[0]);
final Double end = Double.valueOf(period[1]);
final Double increment = Double.valueOf(period[2]);
Double step;
int j = 0;
while ((step = j * increment + begin) <= end) {
addValue(values, step);
j++;
// limiting discrete period elements
if (j >= MAX_ELEMENTS_ELEVATIONS_KVP) {
if (LOGGER.isLoggable(Level.INFO))
LOGGER.info("Lmiting number of elements in this periodo to "
+ MAX_ELEMENTS_ELEVATIONS_KVP);
break;
}
}
} else {
throw new ParseException("Invalid elevation parameter: " + period, 0);
}
}
}
return new ArrayList(values);
}
private void addValue(Collection result, Double step) {
for (Iterator it = result.iterator(); it.hasNext();) {
final Object element = it.next();
if (element instanceof Double) {
// convert
final Double local = (Double) element;
if (local.equals(step))
return;
} else {
// convert
final DateRange local = (DateRange) element;
if (local.contains(step))
return;
}
}
result.add(step);
}
private void addPeriod(Collection result, NumberRange<Double> newRange) {
for (Iterator it = result.iterator(); it.hasNext();) {
final Object element = it.next();
if (element instanceof Double) {
// convert
if (newRange.contains((Number) element)) {
it.remove();
}
} else {
// convert
final NumberRange<Double> local = (NumberRange<Double>) element;
if (local.contains(newRange))
return;
if (newRange.contains(local))
it.remove();
}
}
result.add(newRange);
}
}