/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma.type; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import org.json.JSONArray; import org.json.JSONException; import org.obiba.magma.Coordinate; import org.obiba.magma.MagmaEngine; import org.obiba.magma.MagmaRuntimeException; import org.obiba.magma.Value; import org.obiba.magma.ValueSequence; import com.google.common.collect.Iterables; public class PolygonType extends JSONAwareValueType { private static final long serialVersionUID = 1175515418625286891L; @SuppressWarnings("StaticNonFinalField") private static WeakReference<PolygonType> instance; private PolygonType() { } @SuppressWarnings("ConstantConditions") @edu.umd.cs.findbugs.annotations.SuppressWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") @NotNull public static PolygonType get() { if(instance == null || instance.get() == null) { instance = MagmaEngine.get().registerInstance(new PolygonType()); } return instance.get(); } @NotNull @Override public String getName() { return "polygon"; } @Override public Class<?> getJavaClass() { return ArrayList.class; } @Override public boolean acceptsJavaClass(@NotNull Class<?> clazz) { return getJavaClass().isAssignableFrom(clazz); } @Override public boolean isDateTime() { return false; } @Override public boolean isNumeric() { return false; } @Override public boolean isGeo() { return true; } @NotNull @Override public Value valueOf(@Nullable String string) { if(string == null) { return nullValue(); } try { JSONArray array = new JSONArray(string.trim()); return valueOf(array); } catch(JSONException e) { throw new MagmaRuntimeException("Invalid Polygon format", e); } } public Value valueOf(@Nullable JSONArray array) { if(array == null) { return nullValue(); } Collection<List<Coordinate>> polygon = new ArrayList<>(array.length()); for(int i = 0; i < array.length(); i++) { try { List<Coordinate> points = new ArrayList<>(array.getJSONArray(i).length()); for(int j = 0; j < array.getJSONArray(i).length(); j++) { points.add(Coordinate.getCoordinateFrom(array.getJSONArray(i).get(j))); } if(points.isEmpty()) { throw new MagmaRuntimeException("The Polygon can't be empty"); } if(!(points.get(0).compareTo(points.get(points.size() - 1)) == 0)) { throw new MagmaRuntimeException("The last Coordinate must be the same as the first coordinate"); } polygon.add(points); } catch(JSONException e) { throw new MagmaRuntimeException("Invalid Polygon format"); } } return Factory.newValue(this, (Serializable) polygon); } @NotNull @Override public Value valueOf(@Nullable Object object) { if(object == null) { return nullValue(); } Class<?> type = object.getClass(); if(type.equals(String.class)) { return valueOf((String) object); } if(type.equals(JSONArray.class)) { return valueOf((JSONArray) object); } if(type.equals(getJavaClass())) { return valueOfPolygon((Iterable<?>) object); } if(object instanceof Value) { return convert((Value)object); } return valueOf(object.toString()); } private Value valueOfPolygon(Iterable<?> object) { Collection<List<Coordinate>> polygon = new ArrayList<>(); List<Coordinate> pts = new ArrayList<>(); for(Object obj : object) { try { for(Object o : (Iterable<?>) obj) { pts.add(Coordinate.getCoordinateFrom(o)); } } catch(ClassCastException e) { throw new MagmaRuntimeException("List of Coordinates expected", e); } } if(pts.isEmpty()) { throw new MagmaRuntimeException("A polygon can't be empty"); } polygon.add(pts); return Factory.newValue(this, (Serializable) polygon); } @Override @SuppressWarnings({ "unchecked", "PMD.NcssMethodCount" }) public int compare(Value o1, Value o2) { if(o1.isNull() && o2.isNull()) return 0; if(o1.isNull()) return -1; if(o2.isNull()) return 1; Iterable<List<Coordinate>> list1 = (Iterable<List<Coordinate>>) o1.getValue(); Iterable<List<Coordinate>> list2 = (Iterable<List<Coordinate>>) o2.getValue(); if(list1 == list2) return 0; int size1 = Iterables.size(list1); int size2 = Iterables.size(list2); if(size1 == size2) { for(List<Coordinate> l : list1) { if(!Iterables.contains(list2, l)) return -1; } return 0; } return size1 < size2 ? -1 : 1; } @Nullable @Override protected String toString(@Nullable ValueSequence sequence) { return "[" + super.toString(sequence) + "]"; } }