/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.io.gml.writer.internal.geometry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import com.vividsolutions.jts.geom.Geometry;
import eu.esdihumboldt.hale.io.gml.writer.internal.geometry.converters.MultiLineStringToLineString;
import eu.esdihumboldt.hale.io.gml.writer.internal.geometry.converters.MultiPointToPoint;
import eu.esdihumboldt.hale.io.gml.writer.internal.geometry.converters.MultiPolygonToMultiLineString;
import eu.esdihumboldt.hale.io.gml.writer.internal.geometry.converters.MultiPolygonToPolygon;
import eu.esdihumboldt.hale.io.gml.writer.internal.geometry.converters.PolygonToLineString;
/**
* Registry for {@link GeometryConverter}s
*
* @author Simon Templer
* @partner 01 / Fraunhofer Institute for Computer Graphics Research
* @version $Id$
*/
public class GeometryConverterRegistry {
/**
* Conversion ladder - offers conversion for a geometry in the style of an
* iterator. Best conversions will be served first.
*/
public class ConversionLadder implements Iterator<Geometry> {
private final Map<Class<? extends Geometry>, Geometry> results = new HashMap<Class<? extends Geometry>, Geometry>();
private final Queue<Geometry> pendingGeometries = new LinkedList<Geometry>();
private final Queue<GeometryConverter<?, ?>> pendingConverters = new LinkedList<GeometryConverter<?, ?>>();
private final boolean noLossOnly;
/**
* Create a conversion ladder for the given geometry
*
* @param geometry the geometry
* @param noLossOnly if only no-loss converters may be used
*/
protected ConversionLadder(Geometry geometry, boolean noLossOnly) {
this.noLossOnly = noLossOnly;
Class<? extends Geometry> geomClass = geometry.getClass();
results.put(geomClass, geometry);
pendingGeometries.add(geometry);
}
/**
* @see Iterator#hasNext()
*/
@Override
public synchronized boolean hasNext() {
if (pendingConverters.isEmpty()) {
prepareNext();
}
return !pendingConverters.isEmpty();
}
/**
* Prepare next step
*/
@SuppressWarnings("unchecked")
private void prepareNext() {
if (!pendingConverters.isEmpty()) {
return;
}
Geometry geom = pendingGeometries.poll();
if (geom != null) {
// prepare best conversions reachable from geom
Class<? extends Geometry> geomClass = geom.getClass();
// find level one converters
Set<GeometryConverter<?, ?>> l1 = converters.get(geomClass);
if (l1 != null) {
// sort by loss/no loss
List<GeometryConverter<?, ?>> noloss = new ArrayList<GeometryConverter<?, ?>>();
List<GeometryConverter<?, ?>> loss = new ArrayList<GeometryConverter<?, ?>>();
for (GeometryConverter<?, ?> converter : l1) {
@SuppressWarnings("rawtypes")
GeometryConverter conv = converter; // correct source
// type is assured
if (!ignore(converter)) {
if (conv.lossOnConversion(geom)) {
loss.add(converter);
}
else {
noloss.add(converter);
}
}
}
// TODO collect converters through more levels to allow
// multi-level-noloss is preferred to loss?
pendingConverters.addAll(noloss);
if (!noLossOnly) {
pendingConverters.addAll(loss);
}
}
}
}
/**
* Tells if to ignore a converter
*
* @param converter the converter
*
* @return if to ignore the converter
*/
private boolean ignore(GeometryConverter<?, ?> converter) {
return results.containsKey(converter.getTargetType());
}
/**
* @see Iterator#next()
*/
@SuppressWarnings("unchecked")
@Override
public synchronized Geometry next() {
prepareNext();
@SuppressWarnings("rawtypes")
GeometryConverter converter = pendingConverters.poll();
if (results.containsKey(converter.getTargetType())) {
// for any reason this was already calculated (e.g.
// preconversion when descending into converter levels)
return results.get(converter.getTargetType());
}
// convert
Geometry source = results.get(converter.getSourceType());
Geometry target = converter.convert(source);
results.put(converter.getTargetType(), target);
pendingGeometries.add(target);
return target;
}
/**
* @see Iterator#remove()
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private static final GeometryConverterRegistry INSTANCE = new GeometryConverterRegistry();
/**
* Get the singleton instance of the registry
*
* @return the geometry converter registry
*/
public static GeometryConverterRegistry getInstance() {
return INSTANCE;
}
/**
* Converters organized by source geometry type
*/
private final Map<Class<? extends Geometry>, Set<GeometryConverter<?, ?>>> converters = new HashMap<Class<? extends Geometry>, Set<GeometryConverter<?, ?>>>();
/**
* Default constructor
*/
private GeometryConverterRegistry() {
super();
init();
}
/**
* Initialize the registry
*/
private void init() {
// built-in converters
registerConverter(new MultiPolygonToMultiLineString());
registerConverter(new PolygonToLineString());
registerConverter(new MultiPolygonToPolygon());
registerConverter(new MultiLineStringToLineString());
registerConverter(new MultiPointToPoint());
// TODO other converters?
}
/**
* Register a geometry converter
*
* @param converter the converter
*/
public void registerConverter(GeometryConverter<?, ?> converter) {
Set<GeometryConverter<?, ?>> cs = converters.get(converter.getSourceType());
if (cs == null) {
cs = new HashSet<GeometryConverter<?, ?>>();
converters.put(converter.getSourceType(), cs);
}
cs.add(converter);
}
/**
* Create a conversion ladder for the given geometry
*
* @param geometry the geometry
*
* @return the conversion ladder
*/
public ConversionLadder createLadder(Geometry geometry) {
return new ConversionLadder(geometry, false);
}
/**
* Create a conversion ladder for the given geometry that does only no-loss
* conversions.
*
* @param geometry the geometry
*
* @return the conversion ladder
*/
public ConversionLadder createNoLossLadder(Geometry geometry) {
return new ConversionLadder(geometry, true);
}
}