blob: 89ea26e7f372c6c071e5ceb62cc1887f960e720a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
*/
package org.apache.olingo.commons.core.edm.primitivetype;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.geo.ComposedGeospatial;
import org.apache.olingo.commons.api.edm.geo.Geospatial;
import org.apache.olingo.commons.api.edm.geo.Geospatial.Dimension;
import org.apache.olingo.commons.api.edm.geo.Geospatial.Type;
import org.apache.olingo.commons.api.edm.geo.GeospatialCollection;
import org.apache.olingo.commons.api.edm.geo.LineString;
import org.apache.olingo.commons.api.edm.geo.MultiLineString;
import org.apache.olingo.commons.api.edm.geo.MultiPoint;
import org.apache.olingo.commons.api.edm.geo.MultiPolygon;
import org.apache.olingo.commons.api.edm.geo.Point;
import org.apache.olingo.commons.api.edm.geo.Polygon;
import org.apache.olingo.commons.api.edm.geo.SRID;
public abstract class AbstractGeospatialType<T extends Geospatial> extends SingletonPrimitiveType {
private static final Pattern PATTERN =
Pattern.compile("([a-z]+)'SRID=([0-9]+);([a-zA-Z]+)\\((.*)\\)'");
private static final Pattern COLLECTION_PATTERN =
Pattern.compile("([a-z]+)'SRID=([0-9]+);Collection\\(([a-zA-Z]+)\\((.*)\\)\\)'");
private final Class<T> reference;
protected final Dimension dimension;
protected final Type type;
protected AbstractGeospatialType(final Class<T> reference, final Dimension dimension, final Type type) {
this.reference = reference;
this.dimension = dimension;
this.type = type;
}
@Override
public Class<?> getDefaultType() {
return reference;
}
private Matcher getMatcher(final Pattern pattern, final String value) throws EdmPrimitiveTypeException {
final Matcher matcher = pattern.matcher(value);
if (!matcher.matches()) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.");
}
Geospatial.Dimension _dimension = null;
Geospatial.Type _type = null;
try {
_dimension = Geospatial.Dimension.valueOf(matcher.group(1).toUpperCase());
_type = Geospatial.Type.valueOf(matcher.group(3).toUpperCase());
} catch (IllegalArgumentException e) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.", e);
}
if (_dimension != this.dimension || (!pattern.equals(COLLECTION_PATTERN) && _type != this.type)) {
throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.");
}
return matcher;
}
private Point newPoint(final SRID srid, final String point, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode)
throws EdmPrimitiveTypeException {
final List<String> pointCoo = split(point, ' ');
if (pointCoo == null || pointCoo.size() != 2) {
throw new EdmPrimitiveTypeException("The literal '" + point + "' has illegal content.");
}
final Point result = new Point(this.dimension, srid);
result.setX(EdmDouble.getInstance().valueOfString(pointCoo.get(0),
isNullable, maxLength, precision, scale, isUnicode, Double.class));
result.setY(EdmDouble.getInstance().valueOfString(pointCoo.get(1),
isNullable, maxLength, precision, scale, isUnicode, Double.class));
return result;
}
protected Point stringToPoint(final String value, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
final Matcher matcher = getMatcher(PATTERN, value);
return newPoint(SRID.valueOf(matcher.group(2)), matcher.group(4),
isNullable, maxLength, precision, scale, isUnicode);
}
protected MultiPoint stringToMultiPoint(final String value, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
final Matcher matcher = getMatcher(PATTERN, value);
final List<Point> points = new ArrayList<>();
for (final String pointCoo : split(matcher.group(4), ',')) {
points.add(newPoint(null, pointCoo.substring(1, pointCoo.length() - 1),
isNullable, maxLength, precision, scale, isUnicode));
}
return new MultiPoint(dimension, SRID.valueOf(matcher.group(2)), points);
}
private LineString newLineString(final SRID srid, final String lineString, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode)
throws EdmPrimitiveTypeException {
final List<Point> points = new ArrayList<>();
for (final String pointCoo : split(lineString, ',')) {
points.add(newPoint(null, pointCoo, isNullable, maxLength, precision, scale, isUnicode));
}
return new LineString(this.dimension, srid, points);
}
protected LineString stringToLineString(final String value, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
final Matcher matcher = getMatcher(PATTERN, value);
return newLineString(SRID.valueOf(matcher.group(2)), matcher.group(4),
isNullable, maxLength, precision, scale, isUnicode);
}
protected MultiLineString stringToMultiLineString(final String value, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode)
throws EdmPrimitiveTypeException {
final Matcher matcher = getMatcher(PATTERN, value);
final List<LineString> lineStrings = new ArrayList<>();
for (String coo : matcher.group(4).contains("),(")
? matcher.group(4).split("\\),\\(") : new String[] { matcher.group(4) }) {
String lineString = coo;
if (lineString.charAt(0) == '(') {
lineString = lineString.substring(1);
}
if (lineString.endsWith(")")) {
lineString = lineString.substring(0, lineString.length() - 1);
}
lineStrings.add(newLineString(null, lineString, isNullable, maxLength, precision, scale, isUnicode));
}
return new MultiLineString(this.dimension, SRID.valueOf(matcher.group(2)), lineStrings);
}
private Polygon newPolygon(final SRID srid, final String polygon, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode)
throws EdmPrimitiveTypeException {
final String[] first = polygon.split("\\),\\(");
final List<LineString> interiorRings = new ArrayList<>();
for (int i = 0; i < first.length -1; i++) {
List<Point> interior = new ArrayList<Point>();
for (final String pointCoo : split(first[i].substring(i==0?1:0, first[i].length()), ',')) {
interior.add(newPoint(null, pointCoo, isNullable, maxLength, precision, scale, isUnicode));
}
interiorRings.add(new LineString(dimension, srid, interior));
}
final List<Point> exterior = new ArrayList<Point>();
for (final String pointCoo : split(first[first.length -1].substring(0, first[first.length -1].length() - 1), ',')) {
exterior.add(newPoint(null, pointCoo, isNullable, maxLength, precision, scale, isUnicode));
}
return new Polygon(dimension, srid, interiorRings, new LineString(dimension, srid, exterior));
}
protected Polygon stringToPolygon(final String value, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
final Matcher matcher = getMatcher(PATTERN, value);
return newPolygon(SRID.valueOf(matcher.group(2)), matcher.group(4),
isNullable, maxLength, precision, scale, isUnicode);
}
protected MultiPolygon stringToMultiPolygon(final String value, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
final Matcher matcher = getMatcher(PATTERN, value);
final List<Polygon> polygons = new ArrayList<>();
for (String coo : matcher.group(4).contains(")),((") ?
matcher.group(4).split("\\)\\),\\(\\(") :
new String[] { matcher.group(4) }) {
String polygon = coo;
if (polygon.startsWith("((")) {
polygon = polygon.substring(1);
}
if (polygon.endsWith("))")) {
polygon = polygon.substring(0, polygon.length() - 1);
}
if (polygon.charAt(0) != '(') {
polygon = "(" + polygon;
}
if (!polygon.endsWith(")")) {
polygon += ")";
}
polygons.add(newPolygon(null, polygon, isNullable, maxLength, precision, scale, isUnicode));
}
return new MultiPolygon(dimension, SRID.valueOf(matcher.group(2)), polygons);
}
protected GeospatialCollection stringToCollection(final String value, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode)
throws EdmPrimitiveTypeException {
final Matcher matcher = getMatcher(COLLECTION_PATTERN, value);
Geospatial item = null;
switch (Geospatial.Type.valueOf(matcher.group(3).toUpperCase())) {
case POINT:
item = newPoint(SRID.valueOf(matcher.group(2)), matcher.group(4),
isNullable, maxLength, precision, scale, isUnicode);
break;
case MULTIPOINT:
final List<Point> points = new ArrayList<>();
for (final String pointCoo : split(matcher.group(4), ',')) {
points.add(newPoint(null, pointCoo.substring(1, pointCoo.length() - 1),
isNullable, maxLength, precision, scale, isUnicode));
}
item = new MultiPoint(dimension, SRID.valueOf(matcher.group(2)), points);
break;
case LINESTRING:
item = newLineString(SRID.valueOf(matcher.group(2)), matcher.group(4),
isNullable, maxLength, precision, scale, isUnicode);
break;
case MULTILINESTRING:
final List<LineString> lineStrings = new ArrayList<>();
for (final String coo : split(matcher.group(4), ',')) {
lineStrings.add(newLineString(null, coo.substring(1, coo.length() - 1),
isNullable, maxLength, precision, scale, isUnicode));
}
item = new MultiLineString(this.dimension, SRID.valueOf(matcher.group(2)), lineStrings);
break;
case POLYGON:
item = newPolygon(SRID.valueOf(matcher.group(2)), matcher.group(4),
isNullable, maxLength, precision, scale, isUnicode);
break;
case MULTIPOLYGON:
final List<Polygon> polygons = new ArrayList<>();
for (final String coo : split(matcher.group(4), ',')) {
polygons.add(newPolygon(null, coo.substring(1, coo.length() - 1),
isNullable, maxLength, precision, scale, isUnicode));
}
item = new MultiPolygon(dimension, SRID.valueOf(matcher.group(2)), polygons);
break;
default:
}
return new GeospatialCollection(dimension, SRID.valueOf(matcher.group(2)),
Collections.<Geospatial> singletonList(item));
}
private StringBuilder toStringBuilder(final SRID srid) {
return new StringBuilder(dimension.name().toLowerCase()).append('\'').
append("SRID=").append(srid).append(';');
}
private String point(final Point point, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode)
throws EdmPrimitiveTypeException {
return new StringBuilder().
append(EdmDouble.getInstance().valueToString(point.getX(),
isNullable, maxLength, precision, scale, isUnicode)).
append(' ').
append(EdmDouble.getInstance().valueToString(point.getY(),
isNullable, maxLength, precision, scale, isUnicode)).
toString();
}
protected String toString(final Point point, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
if (dimension != point.getDimension()) {
throw new EdmPrimitiveTypeException("The value '" + point + "' is not valid.");
}
return toStringBuilder(point.getSrid()).
append(reference.getSimpleName()).
append('(').
append(point(point, isNullable, maxLength, precision, scale, isUnicode)).
append(")'").
toString();
}
protected String toString(final MultiPoint multiPoint, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
if (dimension != multiPoint.getDimension()) {
throw new EdmPrimitiveTypeException("The value '" + multiPoint + "' is not valid.");
}
final StringBuilder result = toStringBuilder(multiPoint.getSrid()).
append(reference.getSimpleName()).
append('(');
for (final Iterator<Point> itor = multiPoint.iterator(); itor.hasNext();) {
result.append('(').
append(point(itor.next(), isNullable, maxLength, precision, scale, isUnicode)).
append(')');
if (itor.hasNext()) {
result.append(',');
}
}
return result.append(")'").toString();
}
private StringBuilder appendPoints(final ComposedGeospatial<Point> points, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode,
final StringBuilder result) throws EdmPrimitiveTypeException {
for (final Iterator<Point> itor = points.iterator(); itor.hasNext();) {
result.append(point(itor.next(), isNullable, maxLength, precision, scale, isUnicode));
if (itor.hasNext()) {
result.append(',');
}
}
return result;
}
protected String toString(final LineString lineString, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
if (dimension != lineString.getDimension()) {
throw new EdmPrimitiveTypeException("The value '" + lineString + "' is not valid.");
}
StringBuilder builder = toStringBuilder(lineString.getSrid()).
append(reference.getSimpleName()).
append('(');
return appendPoints(lineString, isNullable, maxLength, precision, scale, isUnicode, builder).
append(")'").toString();
}
protected String toString(final MultiLineString multiLineString, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
if (dimension != multiLineString.getDimension()) {
throw new EdmPrimitiveTypeException("The value '" + multiLineString + "' is not valid.");
}
final StringBuilder result = toStringBuilder(multiLineString.getSrid()).
append(reference.getSimpleName()).
append('(');
for (final Iterator<LineString> itor = multiLineString.iterator(); itor.hasNext();) {
result.append('(');
appendPoints(itor.next(), isNullable, maxLength, precision, scale, isUnicode, result).
append(')');
if (itor.hasNext()) {
result.append(',');
}
}
return result.append(")'").toString();
}
private String polygon(final Polygon polygon, final Boolean isNullable,
final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode)
throws EdmPrimitiveTypeException {
final StringBuilder result = new StringBuilder();
for (int i = 0; i < polygon.getNumberOfInteriorRings(); i++) {
result.append('(');
appendPoints(polygon.getInterior(i), isNullable, maxLength, precision, scale, isUnicode, result);
result.append("),");
}
result.append('(');
appendPoints(polygon.getExterior(), isNullable, maxLength, precision, scale, isUnicode, result);
return result.append(')').toString();
}
protected String toString(final Polygon polygon, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
if (dimension != polygon.getDimension()) {
throw new EdmPrimitiveTypeException("The value '" + polygon + "' is not valid.");
}
return toStringBuilder(polygon.getSrid()).
append(reference.getSimpleName()).
append('(').
append(polygon(polygon, isNullable, maxLength, precision, scale, isUnicode)).
append(")'").toString();
}
protected String toString(final MultiPolygon multiPolygon, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
if (dimension != multiPolygon.getDimension()) {
throw new EdmPrimitiveTypeException("The value '" + multiPolygon + "' is not valid.");
}
final StringBuilder result = toStringBuilder(multiPolygon.getSrid()).
append(reference.getSimpleName()).
append('(');
for (final Iterator<Polygon> itor = multiPolygon.iterator(); itor.hasNext();) {
result.append('(').
append(polygon(itor.next(), isNullable, maxLength, precision, scale, isUnicode)).
append(')');
if (itor.hasNext()) {
result.append(',');
}
}
return result.append(")'").toString();
}
protected String toString(final GeospatialCollection collection, final Boolean isNullable, final Integer maxLength,
final Integer precision, final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
if (dimension != collection.getDimension()) {
throw new EdmPrimitiveTypeException("The value '" + collection + "' is not valid.");
}
StringBuilder result = toStringBuilder(collection.getSrid()).append("Collection(");
if (collection.iterator().hasNext()) {
final Geospatial item = collection.iterator().next();
result.append(item.getClass().getSimpleName()).append('(');
switch (item.getEdmPrimitiveTypeKind()) {
case GeographyPoint:
case GeometryPoint:
result.append(point((Point) item, isNullable, maxLength, precision, scale, isUnicode));
break;
case GeographyMultiPoint:
case GeometryMultiPoint:
for (final Iterator<Point> itor = ((MultiPoint) item).iterator(); itor.hasNext();) {
result.append('(').
append(point(itor.next(), isNullable, maxLength, precision, scale, isUnicode)).
append(')');
if (itor.hasNext()) {
result.append(',');
}
}
break;
case GeographyLineString:
case GeometryLineString:
appendPoints((LineString) item, isNullable, maxLength, precision, scale, isUnicode, result);
break;
case GeographyMultiLineString:
case GeometryMultiLineString:
for (final Iterator<LineString> itor = ((MultiLineString) item).iterator(); itor.hasNext();) {
result.append('(');
appendPoints(itor.next(), isNullable, maxLength, precision, scale, isUnicode, result).
append(')');
if (itor.hasNext()) {
result.append(',');
}
}
break;
case GeographyPolygon:
case GeometryPolygon:
result.append(polygon((Polygon) item, isNullable, maxLength, precision, scale, isUnicode));
break;
case GeographyMultiPolygon:
case GeometryMultiPolygon:
for (final Iterator<Polygon> itor = ((MultiPolygon) item).iterator(); itor.hasNext();) {
result.append('(').
append(polygon(itor.next(), isNullable, maxLength, precision, scale, isUnicode)).
append(')');
if (itor.hasNext()) {
result.append(',');
}
}
break;
default:
}
result.append(')');
}
return result.append(")'").toString();
}
private List<String> split(final String input, final char separator) {
if (input == null) {
return null;
}
List<String> list = new ArrayList<String>();
int start = 0;
int end;
while ((end = input.indexOf(separator, start)) >= 0) {
list.add(input.substring(start, end));
start = end + 1;
}
if (start < input.length()) {
list.add(input.substring(start));
}
return list;
}
}