/**
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.atlas.util;

import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasElement;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.repository.store.graph.v2.AtlasGraphUtilsV2;
import org.apache.atlas.type.AtlasEntityType;
import org.apache.atlas.type.AtlasRelationshipType;
import org.apache.atlas.type.AtlasTypeRegistry;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SearchPredicateUtil {
    private static final Logger LOG = LoggerFactory.getLogger(SearchPredicateUtil.class);

    private static final Predicate ALWAYS_FALSE = object -> false;

    private SearchPredicateUtil() {
        // to block instantiation
    }

    public static ElementAttributePredicateGenerator getLTPredicateGenerator() {
        LOG.debug("==> getLTPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (Short.class.isAssignableFrom(attrClass)) {
                return ShortPredicate.getLTPredicate(attrName, attrClass, (Short) attrVal);
            } else if (Integer.class.isAssignableFrom(attrClass)) {
                return IntegerPredicate.getLTPredicate(attrName, attrClass, (Integer) attrVal);
            } else if (Long.class.isAssignableFrom(attrClass)) {
                return LongPredicate.getLTPredicate(attrName, attrClass, (Long) attrVal);
            } else if (Float.class.isAssignableFrom(attrClass)) {
                return FloatPredicate.getLTPredicate(attrName, attrClass, (Float) attrVal);
            } else if (Double.class.isAssignableFrom(attrClass)) {
                return DoublePredicate.getLTPredicate(attrName, attrClass, (Double) attrVal);
            } else if (Byte.class.isAssignableFrom(attrClass)) {
                return BytePredicate.getLTPredicate(attrName, attrClass, (Byte) attrVal);
            } else if (BigInteger.class.isAssignableFrom(attrClass)) {
                return BigIntegerPredicate.getLTPredicate(attrName, attrClass, (BigInteger) attrVal);
            } else if (BigDecimal.class.isAssignableFrom(attrClass)) {
                return BigDecimalPredicate.getLTPredicate(attrName, attrClass, (BigDecimal) attrVal);
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getLTPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getLTPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getGTPredicateGenerator() {
        LOG.debug("==> getGTPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (Short.class.isAssignableFrom(attrClass)) {
                return ShortPredicate.getGTPredicate(attrName, attrClass, (Short) attrVal);
            } else if (Integer.class.isAssignableFrom(attrClass)) {
                return IntegerPredicate.getGTPredicate(attrName, attrClass, (Integer) attrVal);
            } else if (Long.class.isAssignableFrom(attrClass)) {
                return LongPredicate.getGTPredicate(attrName, attrClass, (Long) attrVal);
            } else if (Float.class.isAssignableFrom(attrClass)) {
                return FloatPredicate.getGTPredicate(attrName, attrClass, (Float) attrVal);
            } else if (Double.class.isAssignableFrom(attrClass)) {
                return DoublePredicate.getGTPredicate(attrName, attrClass, (Double) attrVal);
            } else if (Byte.class.isAssignableFrom(attrClass)) {
                return BytePredicate.getGTPredicate(attrName, attrClass, (Byte) attrVal);
            } else if (BigInteger.class.isAssignableFrom(attrClass)) {
                return BigIntegerPredicate.getGTPredicate(attrName, attrClass, (BigInteger) attrVal);
            } else if (BigDecimal.class.isAssignableFrom(attrClass)) {
                return BigDecimalPredicate.getGTPredicate(attrName, attrClass, (BigDecimal) attrVal);
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getGTPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getGTPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getLTEPredicateGenerator() {
        LOG.debug("==> getLTEPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (Short.class.isAssignableFrom(attrClass)) {
                return ShortPredicate.getLTEPredicate(attrName, attrClass, (Short) attrVal);
            } else if (Integer.class.isAssignableFrom(attrClass)) {
                return IntegerPredicate.getLTEPredicate(attrName, attrClass, (Integer) attrVal);
            } else if (Long.class.isAssignableFrom(attrClass)) {
                return LongPredicate.getLTEPredicate(attrName, attrClass, (Long) attrVal);
            } else if (Float.class.isAssignableFrom(attrClass)) {
                return FloatPredicate.getLTEPredicate(attrName, attrClass, (Float) attrVal);
            } else if (Double.class.isAssignableFrom(attrClass)) {
                return DoublePredicate.getLTEPredicate(attrName, attrClass, (Double) attrVal);
            } else if (Byte.class.isAssignableFrom(attrClass)) {
                return BytePredicate.getLTEPredicate(attrName, attrClass, (Byte) attrVal);
            } else if (BigInteger.class.isAssignableFrom(attrClass)) {
                return BigIntegerPredicate.getLTEPredicate(attrName, attrClass, (BigInteger) attrVal);
            } else if (BigDecimal.class.isAssignableFrom(attrClass)) {
                return BigDecimalPredicate.getLTEPredicate(attrName, attrClass, (BigDecimal) attrVal);
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getLTEPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getLTEPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getInRangePredicateGenerator() {
        LOG.debug("==> getInRangePredicateGenerator");

        ElementAttributePredicateGenerator ret = new ElementAttributePredicateGenerator() {
            @Override
            public Predicate generatePredicate(String attrName, Object attrVal, Class<?> attrClass) {
                return generatePredicate(attrName, attrVal, attrVal, attrClass);
            }

            @Override
            public Predicate generatePredicate(String attrName, Object attrVal, Object attrVal2, Class<?> attrClass) {
                if (attrName == null || attrClass == null || attrVal == null || attrVal2 == null) {
                    return ALWAYS_FALSE;
                } else if (Long.class.isAssignableFrom(attrClass)) {
                    return LongPredicate.getInRangePredicate(attrName, attrClass, (Long) attrVal, (Long) attrVal2);
                } else {
                    return ALWAYS_FALSE;
                }
            }
        };

        LOG.debug("<== getInRangePredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getGTEPredicateGenerator() {
        LOG.debug("==> getGTEPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (Short.class.isAssignableFrom(attrClass)) {
                return ShortPredicate.getGTEPredicate(attrName, attrClass, (Short) attrVal);
            } else if (Integer.class.isAssignableFrom(attrClass)) {
                return IntegerPredicate.getGTEPredicate(attrName, attrClass, (Integer) attrVal);
            } else if (Long.class.isAssignableFrom(attrClass)) {
                return LongPredicate.getGTEPredicate(attrName, attrClass, (Long) attrVal);
            } else if (Float.class.isAssignableFrom(attrClass)) {
                return FloatPredicate.getGTEPredicate(attrName, attrClass, (Float) attrVal);
            } else if (Double.class.isAssignableFrom(attrClass)) {
                return DoublePredicate.getGTEPredicate(attrName, attrClass, (Double) attrVal);
            } else if (Byte.class.isAssignableFrom(attrClass)) {
                return BytePredicate.getGTEPredicate(attrName, attrClass, (Byte) attrVal);
            } else if (BigInteger.class.isAssignableFrom(attrClass)) {
                return BigIntegerPredicate.getGTEPredicate(attrName, attrClass, (BigInteger) attrVal);
            } else if (BigDecimal.class.isAssignableFrom(attrClass)) {
                return BigDecimalPredicate.getGTEPredicate(attrName, attrClass, (BigDecimal) attrVal);
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getGTEPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<- getGTEPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getEQPredicateGenerator() {
        LOG.debug("==> getEQPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (Boolean.class.isAssignableFrom(attrClass)) {
                return BooleanPredicate.getEQPredicate(attrName, attrClass, (Boolean) attrVal);
            } else if (Byte.class.isAssignableFrom(attrClass)) {
                return BytePredicate.getEQPredicate(attrName, attrClass, (Byte) attrVal);
            } else if (Short.class.isAssignableFrom(attrClass)) {
                return ShortPredicate.getEQPredicate(attrName, attrClass, (Short) attrVal);
            } else if (Integer.class.isAssignableFrom(attrClass)) {
                return IntegerPredicate.getEQPredicate(attrName, attrClass, (Integer) attrVal);
            } else if (Long.class.isAssignableFrom(attrClass)) {
                return LongPredicate.getEQPredicate(attrName, attrClass, (Long) attrVal);
            } else if (Float.class.isAssignableFrom(attrClass)) {
                return FloatPredicate.getEQPredicate(attrName, attrClass, (Float) attrVal);
            } else if (Double.class.isAssignableFrom(attrClass)) {
                return DoublePredicate.getEQPredicate(attrName, attrClass, (Double) attrVal);
            } else if (BigInteger.class.isAssignableFrom(attrClass)) {
                return BigIntegerPredicate.getEQPredicate(attrName, attrClass, (BigInteger) attrVal);
            } else if (BigDecimal.class.isAssignableFrom(attrClass)) {
                return BigDecimalPredicate.getEQPredicate(attrName, attrClass, (BigDecimal) attrVal);
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getEQPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getEQPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getNEQPredicateGenerator() {
        LOG.debug("==> getNEQPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null) {
                return ALWAYS_FALSE;
            } else if (Boolean.class.isAssignableFrom(attrClass)) {
                return BooleanPredicate.getNEQPredicate(attrName, attrClass, (Boolean) attrVal);
            } else if (Byte.class.isAssignableFrom(attrClass)) {
                return BytePredicate.getNEQPredicate(attrName, attrClass, (Byte) attrVal);
            } else if (Short.class.isAssignableFrom(attrClass)) {
                return ShortPredicate.getNEQPredicate(attrName, attrClass, (Short) attrVal);
            } else if (Integer.class.isAssignableFrom(attrClass)) {
                return IntegerPredicate.getNEQPredicate(attrName, attrClass, (Integer) attrVal);
            } else if (Long.class.isAssignableFrom(attrClass)) {
                return LongPredicate.getNEQPredicate(attrName, attrClass, (Long) attrVal);
            } else if (Float.class.isAssignableFrom(attrClass)) {
                return FloatPredicate.getNEQPredicate(attrName, attrClass, (Float) attrVal);
            } else if (Double.class.isAssignableFrom(attrClass)) {
                return DoublePredicate.getNEQPredicate(attrName, attrClass, (Double) attrVal);
            } else if (BigInteger.class.isAssignableFrom(attrClass)) {
                return BigIntegerPredicate.getNEQPredicate(attrName, attrClass, (BigInteger) attrVal);
            } else if (BigDecimal.class.isAssignableFrom(attrClass)) {
                return BigDecimalPredicate.getNEQPredicate(attrName, attrClass, (BigDecimal) attrVal);
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getNEQPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getNEQPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getContainsAnyPredicateGenerator() {
        LOG.debug("==> getContainsAnyPredicateGenerator");

        ElementAttributePredicateGenerator ret = new ElementAttributePredicateGenerator() {
            @Override
            public Predicate generatePredicate(final String attrName, final Object attrVal, final Class<?> attrClass) {
                if (attrName == null || attrClass == null || !isValid(attrVal, attrClass)) {
                    return ALWAYS_FALSE;
                } else {
                    return new ElementAttributePredicate(attrName, attrClass) {
                        @Override
                        public boolean compareValue(final Object vertexAttrVal) {
                            return CollectionUtils.containsAny((Collection<?>) attrVal, (Collection<?>) vertexAttrVal);
                        }
                    };
                }
            }

            private boolean isValid(final Object attrVal, final Class<?> attrClass) {
                return attrVal instanceof Collection && Collection.class.isAssignableFrom(attrClass);
            }
        };

        LOG.debug("<== getContainsAnyPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getContainsAllPredicateGenerator() {
        LOG.debug("==> getContainsAllPredicateGenerator");

        ElementAttributePredicateGenerator ret = new ElementAttributePredicateGenerator() {
            @Override
            public Predicate generatePredicate(final String attrName, final Object attrVal, final Class<?> attrClass) {
                if (attrName == null || attrClass == null || !isValid(attrVal, attrClass)) {
                    return ALWAYS_FALSE;
                } else {
                    return new ElementAttributePredicate(attrName, attrClass) {
                        @Override
                        public boolean compareValue(final Object vertexAttrVal) {
                            return ((Collection<?>) attrVal).containsAll((Collection<?>) vertexAttrVal);
                        }
                    };
                }
            }

            private boolean isValid(final Object attrVal, final Class<?> attrClass) {
                return attrVal instanceof Collection && Collection.class.isAssignableFrom(attrClass);
            }
        };

        LOG.debug("<== getContainsAllPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getINPredicateGenerator() {
        LOG.debug("==> getINPredicateGenerator");

        ElementAttributePredicateGenerator ret = new ElementAttributePredicateGenerator() {
            @Override
            public Predicate generatePredicate(final String attrName, final Object attrVal, final Class<?> attrClass) {
                if (attrName == null || attrClass == null || !isValid(attrVal, attrClass)) {
                    return ALWAYS_FALSE;
                } else {
                    return new ElementAttributePredicate(attrName, attrClass) {
                        @Override
                        public boolean compareValue(final Object vertexAttrVal) {
                            return ((Collection<?>) attrVal).contains(vertexAttrVal);
                        }
                    };
                }
            }

            private boolean isValid(final Object attrVal, final Class<?> attrClass) {
                return attrVal instanceof Collection;
            }
        };

        LOG.debug("<== getINPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getRegexPredicateGenerator() {
        LOG.debug("==> getRegexPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getRegexPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getRegexPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getLIKEPredicateGenerator() {
        LOG.debug("==> getLIKEPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getContainsPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getLIKEPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getStartsWithPredicateGenerator() {
        LOG.debug("==> getStartsWithPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getStartsWithPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getStartsWithPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getEndsWithPredicateGenerator() {
        LOG.debug("==> getEndsWithPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getEndsWithPredicate(attrName, attrClass, (String) attrVal);
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getEndsWithPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getContainsPredicateGenerator() {
        LOG.debug("==> getContainsPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null || attrVal == null) {
                return ALWAYS_FALSE;
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getContainsPredicate(attrName, attrClass, (String) attrVal);
            } else if (Collection.class.isAssignableFrom(attrClass)) {
                // Check if the provided value is present in the list of stored values
                return new ElementAttributePredicate(attrName, attrClass) {
                    @Override
                    protected boolean compareValue(final Object vertexAttrVal) {
                        return ((Collection<?>) vertexAttrVal).contains(attrVal);
                    }
                };
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getContainsPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getNotContainsPredicateGenerator() {
        LOG.debug("==> getNotContainsPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null) {
                return ALWAYS_FALSE;
            } else if (String.class.isAssignableFrom(attrClass)) {
                return StringPredicate.getNotContainsPredicate(attrName, attrClass, (String) attrVal);
            } else if (Collection.class.isAssignableFrom(attrClass)) {
                // Check if the provided value is present in the list of stored values
                return new ElementAttributePredicate(attrName, attrClass, true) {
                    @Override
                    protected boolean compareValue(final Object vertexAttrVal) {
                        return vertexAttrVal == null || !((Collection<?>) vertexAttrVal).contains(attrVal);
                    }
                };
            } else {
                return ALWAYS_FALSE;
            }
        };

        LOG.debug("<== getNotContainsPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getIsNullPredicateGenerator() {
        LOG.debug("==> getIsNullPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null) {
                return ALWAYS_FALSE;
            } else {
                return new ElementAttributePredicate(attrName, attrClass, true) {
                    @Override
                    protected boolean compareValue(final Object vertexAttrVal) {
                        return vertexAttrVal == null;
                    }
                };
            }
        };

        LOG.debug("<== getIsNullPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getNotNullPredicateGenerator() {
        LOG.debug("==> getNotNullPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null) {
                return ALWAYS_FALSE;
            } else {
                return new ElementAttributePredicate(attrName, attrClass, true) {
                    @Override
                    protected boolean compareValue(final Object vertexAttrVal) {
                        return vertexAttrVal != null;
                    }
                };
            }
        };

        LOG.debug("<== getNotNullPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getNotEmptyPredicateGenerator() {
        LOG.debug("==> getNotEmptyPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null) {
                return ALWAYS_FALSE;
            } else {
                return new ElementAttributePredicate(attrName, attrClass, true) {
                    @Override
                    protected boolean compareValue(final Object vertexAttrVal) {
                        if (vertexAttrVal != null) {
                            if (vertexAttrVal instanceof Collection) {
                                return CollectionUtils.isNotEmpty((Collection<?>) vertexAttrVal);
                            } else if (vertexAttrVal instanceof String) {
                                return StringUtils.isNotEmpty((String) vertexAttrVal);
                            } else {
                                return true; // for other datatypes, a non-null is treated as non-empty
                            }
                        } else {
                            return false;
                        }
                    }
                };
            }
        };

        LOG.debug("<== getNotEmptyPredicateGenerator");

        return ret;
    }

    public static ElementAttributePredicateGenerator getIsNullOrEmptyPredicateGenerator() {
        LOG.debug("==> getIsNullOrEmptyPredicateGenerator");

        ElementAttributePredicateGenerator ret = (attrName, attrVal, attrClass) -> {
            if (attrName == null || attrClass == null) {
                return ALWAYS_FALSE;
            } else {
                return new ElementAttributePredicate(attrName, attrClass, true) {
                    @Override
                    protected boolean compareValue(final Object vertexAttrVal) {
                        if (vertexAttrVal == null) {
                            return true;
                        } else if (vertexAttrVal instanceof Collection) {
                            return CollectionUtils.isEmpty((Collection<?>) vertexAttrVal);
                        } else if (vertexAttrVal instanceof String) {
                            return StringUtils.isEmpty((String) vertexAttrVal);
                        } else {
                            return false;
                        }
                    }
                };
            }
        };

        LOG.debug("<== getIsNullOrEmptyPredicateGenerator");

        return ret;
    }

    public static Predicate generateIsEntityVertexPredicate(AtlasTypeRegistry typeRegistry) {
        return new IsEntityVertexPredicate(typeRegistry);
    }

    public static Predicate generateIsRelationshipEdgePredicate(AtlasTypeRegistry typeRegistry) {
        return new IsRelationshipEdgePredicate(typeRegistry);
    }

    public interface ElementAttributePredicateGenerator {
        Predicate generatePredicate(String attrName, Object attrVal, Class<?> attrClass);

        default Predicate generatePredicate(String attrName, Object attrVal, Object attrVal2, Class<?> attrClass) {
            return generatePredicate(attrName, attrVal, attrClass);
        }
    }

    abstract static class ElementAttributePredicate implements Predicate {
        final String   attrName;
        final Class<?> attrClass;
        final boolean  isNullValid;

        ElementAttributePredicate(String attrName, Class<?> attrClass) {
            this(attrName, attrClass, false);
        }

        ElementAttributePredicate(String attrName, Class<?> attrClass, boolean isNullValid) {
            this.attrName    = attrName;
            this.attrClass   = attrClass;
            this.isNullValid = isNullValid;
        }

        @Override
        public boolean evaluate(final Object object) {
            AtlasElement element = (object instanceof AtlasVertex || object instanceof AtlasEdge) ? (AtlasElement) object : null;

            if (element != null) {
                Object attrValue;
                if (Collection.class.isAssignableFrom(attrClass)) {
                    attrValue = element.getPropertyValues(attrName, attrClass);
                } else {
                    attrValue = AtlasGraphUtilsV2.getProperty(element, attrName, attrClass);
                }

                return (isNullValid || attrValue != null) && compareValue(attrValue);
            } else {
                return false;
            }
        }

        protected abstract boolean compareValue(Object vertexAttrVal);
    }

    abstract static class BooleanPredicate extends ElementAttributePredicate {
        final Boolean value;

        BooleanPredicate(String attrName, Class<?> attrClass, Boolean value) {
            super(attrName, attrClass);

            this.value = value;
        }

        BooleanPredicate(String attrName, Class<?> attrClass, Boolean value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);

            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, Boolean value) {
            return new SearchPredicateUtil.BooleanPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Boolean) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, Boolean value) {
            return new SearchPredicateUtil.BooleanPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((Boolean) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }
    }

    abstract static class ShortPredicate extends ElementAttributePredicate {
        final Short value;

        ShortPredicate(String attrName, Class<?> attrClass, Short value) {
            super(attrName, attrClass);

            this.value = value;
        }

        ShortPredicate(String attrName, Class<?> attrClass, Short value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);
            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, Short value) {
            return new ShortPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Short) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, Short value) {
            return new ShortPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((Short) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, Short value) {
            return new ShortPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Short) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, Short value) {
            return new ShortPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Short) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, Short value) {
            return new ShortPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Short) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, Short value) {
            return new ShortPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Short) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }
    }

    abstract static class IntegerPredicate extends ElementAttributePredicate {
        final Integer value;

        IntegerPredicate(String attrName, Class<?> attrClass, Integer value) {
            super(attrName, attrClass);

            this.value = value;
        }

        IntegerPredicate(String attrName, Class<?> attrClass, Integer value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);
            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, Integer value) {
            return new IntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Integer) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, Integer value) {
            return new IntegerPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((Integer) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, Integer value) {
            return new IntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Integer) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, Integer value) {
            return new IntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Integer) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, Integer value) {
            return new IntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Integer) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, Integer value) {
            return new IntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Integer) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }
    }

    abstract static class LongPredicate extends ElementAttributePredicate {
        final Long value;
        final Long value2;

        LongPredicate(String attrName, Class<?> attrClass, Long value) {
            super(attrName, attrClass);

            this.value  = value;
            this.value2 = null;
        }

        LongPredicate(String attrName, Class<?> attrClass, Long value, Long value2, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);

            this.value  = value;
            this.value2 = value2;
        }

        LongPredicate(String attrName, Class<?> attrClass, Long value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);

            this.value  = value;
            this.value2 = null;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, Long value) {
            return new LongPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Long) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, Long value) {
            return new LongPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((Long) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, Long value) {
            return new LongPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Long) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, Long value) {
            return new LongPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Long) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, Long value) {
            return new LongPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Long) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, Long value) {
            return new LongPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Long) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }

        static ElementAttributePredicate getInRangePredicate(String attrName, Class<?> attrClass, Long rangeStart, Long rangeEnd) {
            return new LongPredicate(attrName, attrClass, rangeStart, rangeEnd, false) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Long) vertexAttrVal).compareTo(value) >= 0 && value2 != null && ((Long) vertexAttrVal).compareTo(value2) <= 0;
                }
            };
        }
    }

    abstract static class FloatPredicate extends ElementAttributePredicate {
        final Float value;

        FloatPredicate(String attrName, Class<?> attrClass, Float value) {
            super(attrName, attrClass);

            this.value = value;
        }

        FloatPredicate(String attrName, Class<?> attrClass, Float value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);

            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, Float value) {
            return new FloatPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Float) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, Float value) {
            return new FloatPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((Float) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, Float value) {
            return new FloatPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Float) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, Float value) {
            return new FloatPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Float) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, Float value) {
            return new FloatPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Float) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, Float value) {
            return new FloatPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Float) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }
    }

    abstract static class DoublePredicate extends ElementAttributePredicate {
        final Double value;

        DoublePredicate(String attrName, Class<?> attrClass, Double value) {
            super(attrName, attrClass);

            this.value = value;
        }

        DoublePredicate(String attrName, Class<?> attrClass, Double value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);
            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, Double value) {
            return new DoublePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Double) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, Double value) {
            return new DoublePredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((Double) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, Double value) {
            return new DoublePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Double) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, Double value) {
            return new DoublePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Double) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, Double value) {
            return new DoublePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Double) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, Double value) {
            return new DoublePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Double) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }
    }

    abstract static class BytePredicate extends ElementAttributePredicate {
        final Byte value;

        BytePredicate(String attrName, Class<?> attrClass, Byte value) {
            super(attrName, attrClass);

            this.value = value;
        }

        BytePredicate(String attrName, Class<?> attrClass, Byte value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);

            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, Byte value) {
            return new BytePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Byte) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, Byte value) {
            return new BytePredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((Byte) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, Byte value) {
            return new BytePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Byte) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, Byte value) {
            return new BytePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Byte) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, Byte value) {
            return new BytePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Byte) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, Byte value) {
            return new BytePredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((Byte) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }
    }

    abstract static class BigIntegerPredicate extends ElementAttributePredicate {
        final BigInteger value;

        BigIntegerPredicate(String attrName, Class<?> attrClass, BigInteger value) {
            super(attrName, attrClass);

            this.value = value;
        }

        BigIntegerPredicate(String attrName, Class<?> attrClass, BigInteger value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);
            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, BigInteger value) {
            return new BigIntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigInteger) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, BigInteger value) {
            return new BigIntegerPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((BigInteger) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, BigInteger value) {
            return new BigIntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigInteger) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, BigInteger value) {
            return new BigIntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigInteger) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, BigInteger value) {
            return new BigIntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigInteger) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, BigInteger value) {
            return new BigIntegerPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigInteger) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }
    }

    abstract static class BigDecimalPredicate extends ElementAttributePredicate {
        final BigDecimal value;

        BigDecimalPredicate(String attrName, Class<?> attrClass, BigDecimal value) {
            super(attrName, attrClass);

            this.value = value;
        }

        BigDecimalPredicate(String attrName, Class<?> attrClass, BigDecimal value, boolean isNullValid) {
            super(attrName, attrClass, true);

            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, BigDecimal value) {
            return new BigDecimalPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigDecimal) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, BigDecimal value) {
            return new BigDecimalPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((BigDecimal) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, BigDecimal value) {
            return new BigDecimalPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigDecimal) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, BigDecimal value) {
            return new BigDecimalPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigDecimal) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, BigDecimal value) {
            return new BigDecimalPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigDecimal) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, BigDecimal value) {
            return new BigDecimalPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((BigDecimal) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }
    }

    abstract static class StringPredicate extends ElementAttributePredicate {
        final String value;

        StringPredicate(String attrName, Class<?> attrClass, String value) {
            super(attrName, attrClass);

            this.value = value;
        }

        StringPredicate(String attrName, Class<?> attrClass, String value, boolean isNullValid) {
            super(attrName, attrClass, isNullValid);
            this.value = value;
        }

        static ElementAttributePredicate getEQPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((String) vertexAttrVal).compareTo(value) == 0;
                }
            };
        }

        static ElementAttributePredicate getNEQPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || ((String) vertexAttrVal).compareTo(value) != 0;
                }
            };
        }

        static ElementAttributePredicate getLTPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((String) vertexAttrVal).compareTo(value) < 0;
                }
            };
        }

        static ElementAttributePredicate getLTEPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((String) vertexAttrVal).compareTo(value) <= 0;
                }
            };
        }

        static ElementAttributePredicate getGTPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((String) vertexAttrVal).compareTo(value) > 0;
                }
            };
        }

        static ElementAttributePredicate getGTEPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((String) vertexAttrVal).compareTo(value) >= 0;
                }
            };
        }

        static ElementAttributePredicate getContainsPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((String) vertexAttrVal).contains(value);
                }
            };
        }

        static ElementAttributePredicate getNotContainsPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value, true) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return vertexAttrVal == null || !((String) vertexAttrVal).contains(value);
                }
            };
        }

        static ElementAttributePredicate getStartsWithPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((String) vertexAttrVal).startsWith(value);
                }
            };
        }

        static ElementAttributePredicate getEndsWithPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    return ((String) vertexAttrVal).endsWith(value);
                }
            };
        }

        static ElementAttributePredicate getRegexPredicate(String attrName, Class<?> attrClass, String value) {
            return new StringPredicate(attrName, attrClass, value) {
                protected boolean compareValue(Object vertexAttrVal) {
                    Pattern pattern = Pattern.compile(value, Pattern.CASE_INSENSITIVE);
                    Matcher matcher = pattern.matcher((String) vertexAttrVal);

                    return matcher.matches();
                }
            };
        }
    }

    static class IsEntityVertexPredicate implements Predicate {
        final AtlasTypeRegistry typeRegistry;

        public IsEntityVertexPredicate(AtlasTypeRegistry typeRegistry) {
            this.typeRegistry = typeRegistry;
        }

        @Override
        public boolean evaluate(final Object object) {
            AtlasVertex vertex = (object instanceof AtlasVertex) ? (AtlasVertex) object : null;

            if (vertex != null) {
                String          typeName   = AtlasGraphUtilsV2.getTypeName(vertex);
                AtlasEntityType entityType = typeRegistry.getEntityTypeByName(typeName);

                return entityType != null && !entityType.isInternalType();
            } else {
                return false;
            }
        }
    }

    static class IsRelationshipEdgePredicate implements Predicate {
        final AtlasTypeRegistry typeRegistry;

        public IsRelationshipEdgePredicate(AtlasTypeRegistry typeRegistry) {
            this.typeRegistry = typeRegistry;
        }

        @Override
        public boolean evaluate(final Object object) {
            AtlasEdge edge = (object instanceof AtlasEdge) ? (AtlasEdge) object : null;

            if (edge != null) {
                String                typeName = AtlasGraphUtilsV2.getTypeName(edge);
                AtlasRelationshipType type     = typeRegistry.getRelationshipTypeByName(typeName);

                return type != null;
            } else {
                return false;
            }
        }
    }
}
