/*
* Licensed to Prodevelop SL under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The Prodevelop SL licenses this file
* to you 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.
*
* For more information, contact:
*
* Prodevelop, S.L.
* Pza. Don Juan de Villarrasa, 14 - 5
* 46001 Valencia
* Spain
*
* +34 963 510 612
* +34 963 510 968
* prode@prodevelop.es
* http://www.prodevelop.es
*
* @author Alberto Romeu Carrasco http://www.albertoromeu.com
*/
package es.alrocar.jpe.parser.handler;
import java.util.ArrayList;
import org.xml.sax.Attributes;
import es.alrocar.jpe.parser.JPEParser;
import es.alrocar.poiproxy.configuration.DescribeService;
import es.alrocar.poiproxy.configuration.FeatureType;
import es.alrocar.poiproxy.proxy.LocalFilter;
import es.alrocar.utils.Utils;
/**
* Base class that can be used by a json or xml content handler to throw the
* events needed by {@link JPEContentHandler}. The idea is to use an event
* driven json or xml parser, and through a {@link DescribeService} instance
* send the proper events to the {@link JPEContentHandler}
*
* @author albertoromeu
*
*/
public class BaseContentHandler {
private JPEContentHandler contentHandler;
private JPEContentHandler writerContentHandler;
private DescribeService service;
private Object featureCollection;
private Object currentFeature;
private Object currentPoint;
private FeatureType currentFeatureType;
private String currentKey;
private Object geoJSON;
private Object currentFeatureGeoJSON;
private Object currentGeometryGeoJSON;
private boolean processedLon = false;
private boolean processedLat = false;
private LocalFilter localFilter;
private boolean hasPassedLocalFilter = false;
/**
* A {@link LocalFilter} instance to apply filters on the features to be
* parsed
*
* @param localFilter
*/
public void setLocalFilter(LocalFilter localFilter) {
this.localFilter = localFilter;
}
/**
* Sets an implementation of {@link JPEContentHandler} to load into memory
* the document that is going to be parsed
*
* @param contentHandler
* The {@link JPEContentHandler} implementation
*/
public void setJPEParseContentHandler(JPEContentHandler contentHandler) {
this.contentHandler = contentHandler;
}
/**
* Sets an implementation of {@link JPEContentHandler} to write into GeoJSON
* format the document to be parsed
*
* @param contentHandler
* The {@link JPEContentHandler} that will write the GeoJSON
*/
public void setJPEWriteContentHandler(JPEContentHandler contentHandler) {
this.writerContentHandler = contentHandler;
}
/**
* The DescribeService instance used to parse the document. The parser will
* parse only the attributes in the {@link FeatureType} object of the
* {@link DescribeService}
*
* @param describeService
* The {@link DescribeService}
*/
public void setDescribeService(DescribeService describeService) {
this.service = describeService;
}
/**
* Returns the feature collection created by the {@link JPEContentHandler}
* set at {@link #setJPEParseContentHandler(JPEContentHandler)}
*
* @return An ArrayList of features
*/
public ArrayList getResult() {
return (ArrayList) this.featureCollection;
}
/**
* Calls the event {@link JPEContentHandler#endFeatureCollection(Object)} of
* both {@link JPEContentHandler} set through
* {@link #setJPEParseContentHandler(JPEContentHandler)} and
* {@link #setJPEWriteContentHandler(JPEContentHandler)}
*/
public void end() {
if (((ArrayList) this.featureCollection).size() == 0) {
this.endNewElement();
}
contentHandler.endFeatureCollection(this.featureCollection);
if (writerContentHandler != null)
geoJSON = writerContentHandler.endFeatureCollection(geoJSON);
}
/**
* This method is called after {@link #startNewElement(localName)} if the
* localName of {@link #startNewElement(localName)} is equals to any of the
* {@link FeatureType#getElements()}, {@link FeatureType#getLat())},
* {@link FeatureType#getLon())} or {@link FeatureType#getCombinedLonLat())}
* then the correspondant event of {@link JPEContentHandler} is thrownn
*
* @param arg0
*/
public void processValue(String arg0) {
System.out.println(this.currentKey);
System.out.println(arg0);
if (arg0 == null || this.currentFeatureGeoJSON == null
|| arg0.trim().isEmpty())
return;
FeatureType fType = this.currentFeatureType;
final int size = fType.getElements().size();
// HACK for the special case when lon and lat came in an array
if (processedLat && processedLon) {
processedLat = false;
processedLon = false;
}
for (String destProp : fType.getElements().keySet()) {
if (fType.getElements().get(destProp).getInput()
.compareTo(this.currentKey.toString()) == 0) {
checkValidAttribute(localFilter, arg0.toString());
if (writerContentHandler != null)
writerContentHandler.addElementToFeature(arg0.toString(),
destProp, this.currentFeatureGeoJSON);
contentHandler.addElementToFeature(arg0.toString(), destProp,
this.currentFeature);
return;
}
}
if (fType.getLon() != null
&& currentKey.toString().compareTo(fType.getLon()) == 0
&& !processedLon) {
double lon = Utils
.formatNumber(arg0.toString(),
service.getDecimalSeparator(),
service.getNumberSeparator());
if (this.currentGeometryGeoJSON == null)
this.currentGeometryGeoJSON = writerContentHandler.startPoint();
if (writerContentHandler != null)
writerContentHandler.addXToPoint(new Double(lon),
this.currentGeometryGeoJSON);
contentHandler.addXToPoint(new Double(lon), this.currentPoint);
processedLon = true;
return;
}
if (fType.getLat() != null
&& currentKey.toString().compareTo(fType.getLat()) == 0
&& !processedLat) {
double lat = Utils
.formatNumber(arg0.toString(),
service.getDecimalSeparator(),
service.getNumberSeparator());
if (this.currentGeometryGeoJSON == null)
this.currentGeometryGeoJSON = writerContentHandler.startPoint();
if (writerContentHandler != null)
writerContentHandler.addYToPoint(new Double(lat),
this.currentGeometryGeoJSON);
contentHandler.addYToPoint(new Double(lat), this.currentPoint);
processedLat = true;
return;
}
try {
if (fType.getCombinedLonLat() != null
&& currentKey.toString().compareTo(
fType.getCombinedLonLat()) == 0) {
String[] latLon = arg0.toString().split(
fType.getLonLatSeparator());
int lonPos = 0;
int latPos = 1;
if (fType.getReverseLonLat()) {
lonPos = 1;
latPos = 0;
}
double lon = Utils.formatNumber(latLon[lonPos].toString(),
service.getDecimalSeparator(),
service.getNumberSeparator());
double lat = Utils.formatNumber(latLon[latPos].toString(),
service.getDecimalSeparator(),
service.getNumberSeparator());
if (writerContentHandler != null) {
writerContentHandler.addYToPoint(lat,
this.currentGeometryGeoJSON);
writerContentHandler.addXToPoint(lon,
this.currentGeometryGeoJSON);
}
contentHandler.addYToPoint(lat, this.currentPoint);
contentHandler.addXToPoint(lon, this.currentPoint);
return;
}
} catch (Exception e) {
e.printStackTrace();
}
return;
}
private boolean checkValidAttribute(LocalFilter localFilter,
String attribute) {
if (localFilter == null) {
hasPassedLocalFilter = true;
} else {
hasPassedLocalFilter = localFilter.apply(attribute)
|| hasPassedLocalFilter;
}
return hasPassedLocalFilter;
}
/**
* Calls {@link JPEContentHandler#startFeatureCollection()} of both
* {@link JPEContentHandler} registered
*/
public void start() {
featureCollection = null;
currentFeature = null;
currentPoint = null;
geoJSON = null;
currentFeatureGeoJSON = null;
currentGeometryGeoJSON = null;
if (writerContentHandler != null)
this.geoJSON = writerContentHandler.startFeatureCollection();
featureCollection = contentHandler.startFeatureCollection();
this.currentFeatureType = this.service.getFeatureTypes().get(
this.service.getType());
}
/**
* Compares the localname with the {@link FeatureType#getFeature()}
* attribute of the current {@link FeatureType} of the
* {@link DescribeService}
*
* If the current tag being parsed is equals to
* {@link FeatureType#getFeature()} then the
* {@link JPEContentHandler#startFeature()} and
* {@link JPEContentHandler#startPoint()} are thrown
*
* @param localName
* The current tag name being parsed
* @param atts
* Additional attributes
* @param atts
*/
public void startNewElement(String localName, Attributes atts) {
String arg0 = localName;
this.currentKey = arg0;
if (arg0.compareTo(this.currentFeatureType.getFeature()) == 0) {
endNewElement();
if (writerContentHandler != null) {
this.currentFeatureGeoJSON = writerContentHandler
.startFeature();
this.currentGeometryGeoJSON = writerContentHandler.startPoint();
}
this.currentFeature = contentHandler.startFeature();
this.currentPoint = contentHandler.startPoint();
}
// FIXME improve the support for attributes
if (atts != null) {
int length = atts.getLength();
for (int i = 0; i < length; i++) {
String key = atts.getQName(i);
String value = atts.getValue(i);
this.currentKey = key;
this.processValue(value);
}
}
}
/**
* Ends the current feature parsed, its point associated and adds the
* feature to the current feature collection by throwing the proper events
* of {@link JPEContentHandler}
*/
public void endNewElement() {
if (this.currentFeature != null) {
if (!hasPassedLocalFilter) {
hasPassedLocalFilter = false;
return;
}
if (writerContentHandler != null) {
this.currentFeatureGeoJSON = this.fillCategories(
writerContentHandler, this.currentFeatureGeoJSON,
service);
this.currentFeatureGeoJSON = this.fillService(
writerContentHandler, this.currentFeatureGeoJSON,
service);
this.currentFeatureGeoJSON = writerContentHandler
.addPointToFeature(this.currentFeatureGeoJSON,
writerContentHandler.endPoint(
this.currentGeometryGeoJSON,
this.service.getSRS(),
DescribeService.DEFAULT_SRS));
this.currentFeatureGeoJSON = writerContentHandler
.endFeature(this.currentFeatureGeoJSON);
writerContentHandler.addFeatureToCollection(this.geoJSON,
this.currentFeatureGeoJSON);
}
this.currentFeature = this.fillCategories(contentHandler,
this.currentFeature, service);
this.currentFeature = this.fillService(contentHandler,
this.currentFeature, service);
this.currentFeature = contentHandler
.addPointToFeature(this.currentFeature, contentHandler
.endPoint(this.currentPoint, this.service.getSRS(),
DescribeService.DEFAULT_SRS));
this.currentFeature = contentHandler
.endFeature(this.currentFeature);
contentHandler.addFeatureToCollection(this.featureCollection,
this.currentFeature);
hasPassedLocalFilter = false;
}
}
public Object fillService(JPEContentHandler handler, Object feature,
DescribeService service) {
return handler.addElementToFeature(service.getId(), JPEParser.SERVICE,
feature);
}
public Object fillCategories(JPEContentHandler handler, Object feature,
DescribeService service) {
String categories = service.getCategoriesAsString();
return handler.addElementToFeature(categories, JPEParser.CATEGORIES,
feature);
}
}