/**
 * Copyright 2014-2016 the original author or authors.
 *
 * Licensed 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 net.kaczmarzyk.spring.data.jpa.domain;

import net.kaczmarzyk.spring.data.jpa.utils.Converter;

import javax.persistence.criteria.*;
import java.math.BigDecimal;
import java.util.Arrays;

/**
 * <p>Filters with geo-coordinates. It determines whether a point is in the square with center (latitude, longitude) and side = 2*distance.</p>
 * 
 * <p>Supports field types: BigDecimal.</p>
 * 
 * Created by d.romantsov on 23.06.2016.
 */
public class GeoPosition<T> extends PathSpecification<T> {

    private Converter converter;

    private BigDecimal latitude;
    private BigDecimal longitude;
    private BigDecimal distance;

    public GeoPosition(String[] path, String... args) {
        super(path);
        if (args == null || args.length != 3) {
            throw new IllegalArgumentException("Expected exactly three argument (the fragment to match against), but got: " + Arrays.toString(args));
        } else {
            latitude = new BigDecimal(args[0]);
            longitude = new BigDecimal(args[1]);
            distance = new BigDecimal(args[2]);
        }
    }

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
        if (path.length != 2) {
            throw new IllegalArgumentException("Expected exactly two paths united with \";\", but got: " + path);
        }

        Expression latitudeEx = root.get(path[0]);
        Expression longitudeEx = root.get(path[1]);

        return builder.and(
                builder.greaterThan(latitudeEx, latitude.subtract(distance)),
                builder.lessThan(latitudeEx, latitude.add(distance)),
                builder.greaterThan(longitudeEx, longitude.subtract(distance)),
                builder.lessThan(longitudeEx, longitude.add(distance)));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof GeoPosition)) return false;
        if (!super.equals(o)) return false;

        GeoPosition<?> that = (GeoPosition<?>) o;

        if (latitude != null ? !latitude.equals(that.latitude) : that.latitude != null) return false;
        if (longitude != null ? !longitude.equals(that.longitude) : that.longitude != null) return false;
        return distance != null ? distance.equals(that.distance) : that.distance == null;

    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + (latitude != null ? latitude.hashCode() : 0);
        result = 31 * result + (longitude != null ? longitude.hashCode() : 0);
        result = 31 * result + (distance != null ? distance.hashCode() : 0);
        return result;
    }
}
@@ -1249,61 +1249,214 @@
 ce;%0A
-%0A    public GeoPosition(String%5B%5D path, String... args
+    private boolean allowNull = false;%0A%0A    public GeoPosition(String path%5B%5D, String%5B%5D args) %7B%0A        this(path, args, null);%0A    %7D%0A    %0A    public GeoPosition(String%5B%5D path, String%5B%5D args, String%5B%5D config
 ) %7B%0A
@@ -1832,24 +1832,81 @@
 l(args%5B2%5D);%0A
+            allowNull = Boolean.parseBoolean(config%5B0%5D);%0A
         %7D%0A  
@@ -2295,43 +2295,30 @@
     
+P
 re
-turn builder.and(%0A               
+dicate latitudeGt =
  bui
@@ -2366,33 +2366,33 @@
 tract(distance))
-,
+;
 %0A               
@@ -2376,39 +2376,54 @@
 ance));%0A        
-       
+Predicate latitudeLs =
  builder.lessTha
@@ -2451,33 +2451,33 @@
 e.add(distance))
-,
+;
 %0A               
@@ -2461,39 +2461,55 @@
 ance));%0A        
-       
+Predicate longitudeGt =
  builder.greater
@@ -2555,17 +2555,17 @@
 stance))
-,
+;
 %0A       
@@ -2565,23 +2565,39 @@
         
-       
+Predicate longitudeLs =
  builder
@@ -2643,16 +2643,487 @@
 stance))
+;%0A%0A        return allowNull ?%0A                builder.and(%0A                        builder.or(%0A                                builder.isNull(latitudeEx),%0A                                builder.and(latitudeGt, latitudeLs)),%0A                        builder.or(%0A                                builder.isNull(latitudeEx),%0A                                builder.and(longitudeGt, longitudeLs))) :%0A                builder.and(latitudeGt, latitudeLs, longitudeGt, longitudeLs
 );%0A    %7D

Allow to config enable\disable null values after geo-positional filtration.