/* (c) 2014 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.wps.executor;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.geoserver.wps.ProcessDismissedException;
import org.geoserver.wps.WPSException;
import org.geotools.util.NullProgressListener;
import org.geotools.util.SimpleInternationalString;
import org.geotools.util.SubProgressListener;
import org.opengis.util.ProgressListener;
/**
* A map using input providers internally, allows for deferred execution of the input parsing (it
* happens in a single shot when the first input is fetched)
*
* @author Andrea Aime - GeoSolutions
*/
class LazyInputMap extends AbstractMap<String, Object> {
private static ProgressListener DEFAULT_LISTENER = new NullProgressListener();
Map<String, InputProvider> providers = new LinkedHashMap<String, InputProvider>();
Map<String, Object> values = new HashMap<String, Object>();
boolean parsed = false;
ProgressListener listener = DEFAULT_LISTENER;
public LazyInputMap(Map<String, InputProvider> providers) {
this.providers = providers;
}
public Object get(Object key) {
// make sure we just kill the process is a dismiss happened
if (listener.isCanceled()) {
throw new ProcessDismissedException(listener);
}
// lazy parse inputs
parseInputs();
// return the value
return values.get(key);
}
private void parseInputs() {
// we want to (try to) actually parse stuff just once
if (parsed) {
return;
}
parsed = true;
// count long parses
int totalSteps = 0;
for (InputProvider provider : providers.values()) {
totalSteps += provider.longStepCount();
}
listener.started();
float stepsSoFar = 0;
for (InputProvider provider : providers.values()) {
listener.setTask(new SimpleInternationalString("Retrieving/parsing process input: "
+ provider.getInputId()));
try {
// force parsing
float providerLongSteps = provider.longStepCount();
ProgressListener subListener;
if (providerLongSteps > 0) {
subListener = new SubProgressListener(listener,
(stepsSoFar / totalSteps) * 100, (providerLongSteps / totalSteps) * 100);
} else {
subListener = new NullProgressListener();
}
stepsSoFar += providerLongSteps;
subListener.started();
subListener.progress(0);
Object value = provider.getValue(subListener);
values.put(provider.getInputId(), value);
} catch (Exception e) {
listener.exceptionOccurred(e);
if (e instanceof WPSException) {
throw (WPSException) e;
}
throw new WPSException("Failed to retrieve value for input "
+ provider.getInputId(), e);
}
}
}
@Override
public Set<Entry<String, Object>> entrySet() {
Set<Entry<String, Object>> result = new HashSet<Map.Entry<String, Object>>();
for (String key : providers.keySet()) {
result.add(new DeferredEntry(key));
}
return result;
}
public int longStepCount() {
int count = 0;
for (InputProvider provider: providers.values()) {
count += provider.longStepCount();
}
return count;
}
public class DeferredEntry implements Entry<String, Object> {
private String key;
public DeferredEntry(String key) {
this.key = key;
}
@Override
public String getKey() {
return key;
}
@Override
public Object getValue() {
parseInputs();
return values.get(key);
}
@Override
public Object setValue(Object value) {
throw new UnsupportedOperationException();
}
}
/**
* The listener will be informed of the parse progress, when it happens
* @param listener
*/
public void setListener(ProgressListener listener) {
this.listener = listener;
}
}