Package com.impetus.kundera.query

Source Code of com.impetus.kundera.query.KunderaQuery

/*******************************************************************************
* * Copyright 2012 Impetus Infotech.
*  *
*  * 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 com.impetus.kundera.query;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.Parameter;
import javax.persistence.PersistenceException;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.impetus.kundera.KunderaException;
import com.impetus.kundera.metadata.KunderaMetadataManager;
import com.impetus.kundera.metadata.MetadataUtils;
import com.impetus.kundera.metadata.model.EntityMetadata;
import com.impetus.kundera.metadata.model.MetamodelImpl;
import com.impetus.kundera.metadata.model.attributes.AbstractAttribute;
import com.impetus.kundera.metadata.model.type.AbstractManagedType;
import com.impetus.kundera.persistence.EntityManagerFactoryImpl.KunderaMetadata;

/**
* The Class KunderaQuery.
*/
public class KunderaQuery
{
    /** The Constant SINGLE_STRING_KEYWORDS. */
    public static final String[] SINGLE_STRING_KEYWORDS = { "SELECT", "UPDATE", "SET", "DELETE", "UNIQUE", "FROM",
            "WHERE", "GROUP BY", "HAVING", "ORDER BY" };

    /** The Constant INTER_CLAUSE_OPERATORS. */
    public static final String[] INTER_CLAUSE_OPERATORS = { "AND", "OR", "BETWEEN", "(", ")" };

    /** The Constant INTRA_CLAUSE_OPERATORS. */
    public static final String[] INTRA_CLAUSE_OPERATORS = { "=", "LIKE", "IN", ">", ">=", "<", "<=", "<>", "NOT IN" };

    /** The INTER pattern. *//*
    private static final Pattern INTER_CLAUSE_PATTERN = Pattern.compile(
            "\\s\\band\\b\\s|\\s\\bor\\b\\s|\\s\\bbetween\\b\\s|\\s\\b^[!?IN]\\s\\b(\\s\\b)|\\s\\b^[!?NOT IN]\\s\\b(\\s\\b|\\s\\b(\\s\\b))", Pattern.CASE_INSENSITIVE);*/

    /** The INTER pattern. */
    private static final Pattern INTER_CLAUSE_PATTERN = Pattern.compile(
            "(\\band\\b|\\bor\\b|\\bbetween\\b|\\s\\b^[!?IN]\\s\\b(\\s\\b)|\\s\\b^[!?NOT IN]\\s\\b(\\s\\b|\\s\\b(\\s\\b)))(?=(?:(?:[^']*[^'\"]'){2})*[^']*$)", Pattern.CASE_INSENSITIVE);
   
    /** The INTRA pattern. */
    private static final Pattern INTRA_CLAUSE_PATTERN = Pattern.compile("=|\\s\\blike\\b|\\bnot in\\b|\\bin\\b|<>|>=|>|<=|<|\\s\\bset",
            Pattern.CASE_INSENSITIVE);

    /** The logger. */
    private static Logger logger = LoggerFactory.getLogger(KunderaQuery.class);

    /** The result. */
    private String[] result;

    /** The from. */
    private String from;

    /** The filter. */
    private String filter;

    /** The ordering. */
    private String ordering;

    /** The entity name. */
    private String entityName;

    /** The entity alias. */
    private String entityAlias;

    /** The entity class. */
    private Class<?> entityClass;

    /** The sort orders. */
    private List<SortOrdering> sortOrders;

    /** Persistence Unit(s). */
    private String persistenceUnit;

    // contains a Queue of alternate FilterClause object and Logical Strings
    // (AND, OR etc.)
    /** The filters queue. */
    private Queue filtersQueue = new LinkedList();

    private boolean isDeleteUpdate;

    private Queue<UpdateClause> updateClauseQueue = new LinkedList<UpdateClause>();

    private TypedParameter typedParameter;

    boolean isNativeQuery;

    private String jpaQuery;

    private final KunderaMetadata kunderaMetadata;

    /**
     * Instantiates a new kundera query.
     *
     * @param persistenceUnits
     *            the persistence units
     */
    public KunderaQuery(final String jpaQuery, final KunderaMetadata kunderaMetadata)
    {
        this.jpaQuery = jpaQuery;
        this.kunderaMetadata = kunderaMetadata;
    }

    /**
     * Sets the grouping.
     *
     * @param groupingClause
     *            the new grouping
     */
    public void setGrouping(String groupingClause)
    {
    }

    /**
     * Sets the result.
     *
     * @param result
     *            the new result
     */
    public final void setResult(String... result)
    {
        this.result = result;
    }

    /**
     * Sets the from.
     *
     * @param from
     *            the new from
     */
    public final void setFrom(String from)
    {
        this.from = from;
    }

    /**
     * Sets the filter.
     *
     * @param filter
     *            the new filter
     */
    public final void setFilter(String filter)
    {
        this.filter = filter;
    }

    /**
     * Sets the ordering.
     *
     * @param ordering
     *            the new ordering
     */
    public final void setOrdering(String ordering)
    {
        this.ordering = ordering;
        parseOrdering(this.ordering);
    }

    /**
     * Gets the filter.
     *
     * @return the filter
     */
    public final String getFilter()
    {
        return filter;
    }

    /**
     * Gets the from.
     *
     * @return the from
     */
    public final String getFrom()
    {
        return from;
    }

    /**
     * Gets the ordering.
     *
     * @return the ordering
     */
    public final List<SortOrdering> getOrdering()
    {
        return sortOrders;
    }

    /**
     * Gets the result.
     *
     * @return the result
     */
    public final String[] getResult()
    {
        return result;
    }

    /**
     * Method to check if required result is to get complete entity or a select
     * scalar value.
     *
     * @return true, if it result is for complete alias.
     *
     */
    public final boolean isAliasOnly()
    {
        // TODO
        return result != null && (result[0].indexOf(".") == -1);
    }

    /**
     * Returns set of parameters.
     *
     * @return jpaParameters
     */
    public Set<Parameter<?>> getParameters()
    {
        return typedParameter != null ? typedParameter.jpaParameters : null;
    }

    /**
     * Parameter is bound if it holds any value, else will return false
     *
     * @param param
     * @return
     */
    public boolean isBound(Parameter param)
    {
        return getClauseValue(param) != null;
    }

    /**
     * Returns clause value for supplied parameter.
     *
     * @param paramString
     * @return
     */
    public List<Object> getClauseValue(String paramString)
    {
        if (typedParameter != null && typedParameter.getParameters() != null)
        {
            List<FilterClause> clauses = typedParameter.getParameters().get(paramString);
            if (clauses != null)
            {
                return clauses.get(0).getValue();
            }
            else
            {
                throw new IllegalArgumentException("parameter is not a parameter of the query");
            }
        }

        logger.error("Parameter {} is not a parameter of the query.", paramString);
        throw new IllegalArgumentException("Parameter is not a parameter of the query.");
    }

    /**
     * Returns specific clause value.
     *
     * @param param
     *            parameter
     *
     * @return clause value.
     */
    public List<Object> getClauseValue(Parameter param)
    {
        Parameter match = null;
        if (typedParameter != null && typedParameter.jpaParameters != null)
        {
            for (Parameter p : typedParameter.jpaParameters)
            {
                if (p.equals(param))
                {
                    match = p;
                    if (typedParameter.getType().equals(Type.NAMED))
                    {
                        List<FilterClause> clauses = typedParameter.getParameters().get(":" + p.getName());
                        if (clauses != null)
                        {
                            return clauses.get(0).getValue();
                        }
                    }
                    else
                    {
                    List<FilterClause> clauses = typedParameter.getParameters().get("?" + p.getPosition());
                        if (clauses != null)
                        {
                            return clauses.get(0).getValue();
                        }
                        else
                        {
                            UpdateClause updateClause = typedParameter.getUpdateParameters().get("?" + p.getPosition());
                            if (updateClause != null)
                            {
                                List<Object> value = new ArrayList<Object>();
                                value.add(updateClause.getValue());
                                return value;
                            }

                        }
                    }
                    break;
                }
            }
            if (match == null)
            {
                throw new IllegalArgumentException("parameter is not a parameter of the query");
            }
        }

        logger.error("parameter{} is not a parameter of the query", param);
        throw new IllegalArgumentException("parameter is not a parameter of the query");
    }

    // must be executed after parse(). it verifies and populated the query
    // predicates.
    /**
     * Post parsing init.
     */
    protected void postParsingInit()
    {
        initEntityClass();
        initFilter();
        initUpdateClause();
    }

    /**
     *
     */
    private void initUpdateClause()
    {
        for (UpdateClause updateClause : updateClauseQueue)
        {
            onTypedParameter(updateClause.getValue(), updateClause, updateClause.getProperty().trim());
        }

    }

    /**
     * Inits the entity class.
     */
    private void initEntityClass()
    {
        if (from == null)
        {
            throw new JPQLParseException("Bad query format FROM clause is mandatory for SELECT queries");
        }
        String fromArray[] = from.split(" ");

        if (!this.isDeleteUpdate)
        {
            if (fromArray.length != 2)
            {
                throw new JPQLParseException("Bad query format: " + from
                        + ". Identification variable is mandatory in FROM clause for SELECT queries");
            }

            // TODO
            StringTokenizer tokenizer = new StringTokenizer(getResult()[0], ",");
            while (tokenizer.hasMoreTokens())
            {
                String token = tokenizer.nextToken();
                if (!StringUtils.containsAny(fromArray[1] + ".", token))
                {
                    throw new QueryHandlerException("bad query format with invalid alias:" + token);
                }
            }
        }

        this.entityName = fromArray[0];
        if (fromArray.length == 2)
            this.entityAlias = fromArray[1];

        persistenceUnit = kunderaMetadata.getApplicationMetadata().getMappedPersistenceUnit(entityName);

        // Get specific metamodel.
        MetamodelImpl model = getMetamodel(persistenceUnit);

        if (model != null)
        {
            entityClass = model.getEntityClass(entityName);
        }

        if (null == entityClass)
        {
            logger.error(
                    "No entity {} found, please verify it is properly annotated with @Entity and not a mapped Super class",
                    entityName);
            throw new QueryHandlerException("No entity found by the name: " + entityName);
        }

        EntityMetadata metadata = model.getEntityMetadata(entityClass);

        if (metadata != null && !metadata.isIndexable())
        {
            throw new QueryHandlerException(entityClass + " is not indexed. Not possible to run a query on it."
                    + " Check whether it was properly annotated for indexing.");
        }
    }

    /**
     * Inits the filter.
     */
    private void initFilter()
    {
        EntityMetadata metadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata, entityClass);
        // String indexName = metadata.getIndexName();

        // String filter = getFilter();

        Metamodel metaModel = kunderaMetadata.getApplicationMetadata().getMetamodel(getPersistenceUnit());
        EntityType entityType = metaModel.entity(entityClass);

        if (null == filter)
        {
            List<String> clauses = new ArrayList<String>();
            addDiscriminatorClause(clauses, entityType);
            return;
        }

        List<String> clauses = tokenize(filter, INTER_CLAUSE_PATTERN,true);

        // parse and structure for "between" clause , if present, else it will
        // return original clause
        /*int i=0;
        String temp=null;
        while(clauses.iterator().hasNext())
        {
       
        if(clauses.get(i).equals("AND") || clauses.get(i).equals("OR") || clauses.get(i).equals("BETWEEN"))
        temp=" "+clauses.get(i)+" ";
        clauses.set(i, temp);
       
        i++;
        }*/
       
        clauses = parseFilterForBetweenClause(clauses);
        // clauses must be alternate Inter and Intra combination, starting with
        // Intra.
        boolean newClause = true;

        for (String clause : clauses)
        {
        if(Arrays.asList(INTER_CLAUSE_OPERATORS).contains(clause.toUpperCase().trim()) || (clause.startsWith("(") && clause.endsWith(")")))
        {
                filtersQueue.add(clause.toUpperCase().trim());
                newClause = true;
            }
        else if (newClause)
            {
                List<String> tokens = tokenize(clause, INTRA_CLAUSE_PATTERN,false);

                if (tokens.size() != 3)
                {
                    throw new PersistenceException("bad jpa query: " + clause);
                }

                // strip alias from property name
                String property = tokens.get(0);
                if (property.indexOf(".") > 0)
                {
                    property = property.substring((entityAlias + ".").length());
                }

                String columnName = null;
                try
                {
                    columnName = ((AbstractAttribute) entityType.getAttribute(property)).getJPAColumnName();
                }
                catch (IllegalArgumentException iaex)
                {
                    logger.warn("No column found by this name : " + property + " checking for embeddedfield");
                }
                // where condition may be for search within embedded object
                if (columnName == null && property.indexOf(".") >= 0)
                {
                    String enclosingEmbeddedField = MetadataUtils.getEnclosingEmbeddedFieldName(metadata, property,
                            true, kunderaMetadata);
                    if (enclosingEmbeddedField != null)
                    {
                        columnName = property;
                    }
                }

                if (columnName == null)
                {
                    logger.error("No column found by this name : " + property);
                    throw new JPQLParseException("No column found by this name : " + property + ". Check your query.");
                }

                String condition = tokens.get(1);
                if (!Arrays.asList(INTRA_CLAUSE_OPERATORS).contains(condition.toUpperCase().trim()))
                {
                    throw new JPQLParseException("Bad JPA query: " + clause);
                }

                FilterClause filterClause = new FilterClause(

                columnName, condition, tokens.get(2));
                filtersQueue.add(filterClause);

                onTypedParameter(tokens, filterClause, property);
                newClause = false;
            }
            else
            {
                throw new JPQLParseException("bad jpa query: " + clause);
            }
        }

        addDiscriminatorClause(clauses, entityType);
    }

    private void addDiscriminatorClause(List<String> clauses, EntityType entityType)
    {
        if (((AbstractManagedType) entityType).isInherited())
        {
            String discrColumn = ((AbstractManagedType) entityType).getDiscriminatorColumn();
            String discrValue = ((AbstractManagedType) entityType).getDiscriminatorValue();

            if (discrColumn != null && discrValue != null)
            {
                if (!clauses.isEmpty())
                {
                    filtersQueue.add("AND");
                }

                FilterClause filterClause = new FilterClause(discrColumn, "=", discrValue);
                filtersQueue.add(filterClause);
            }
        }
    }

    /**
     * Depending upon filter value, if it starts with ":" then it is NAMED
     * parameter, else if starts with "?", it will be INDEXED parameter.
     *
     * @param tokens
     *            tokens
     * @param filterClause
     *            filter clauses.
     */
    private void onTypedParameter(Object value, UpdateClause updateClause, String fieldName)
    {
        String token = value.toString();
        if (token != null && token.startsWith(":"))
        {
            addTypedParameter(Type.NAMED, token, updateClause);
            filterJPAParameterInfo(Type.NAMED, token.substring(1), fieldName);
        }
        else if (token != null && token.startsWith("?"))
        {
            addTypedParameter(Type.INDEXED, token, updateClause);
            filterJPAParameterInfo(Type.INDEXED, token.substring(1), fieldName);
        }
    }

    /**
     * Depending upon filter value, if it starts with ":" then it is NAMED
     * parameter, else if starts with "?", it will be INDEXED parameter.
     *
     * @param tokens
     *            tokens
     * @param filterClause
     *            filter clauses.
     */
    private void onTypedParameter(List<String> tokens, FilterClause filterClause, String fieldName)
    {
        if (tokens.get(2) != null && tokens.get(2).startsWith(":"))
        {
            addTypedParameter(Type.NAMED, tokens.get(2), filterClause);
            filterJPAParameterInfo(Type.NAMED, tokens.get(2).substring(1), fieldName);
        }
        else if (tokens.get(2) != null && tokens.get(2).startsWith("?"))
        {
            addTypedParameter(Type.INDEXED, tokens.get(2), filterClause);
            filterJPAParameterInfo(Type.INDEXED, tokens.get(2).substring(1), fieldName);
        }
    }

    /**
     * Adds typed parameter to {@link TypedParameter}
     *
     * @param type
     *            type of parameter(e.g. NAMED/INDEXED)
     * @param parameter
     *            parameter name.
     * @param clause
     *            filter clause.
     */
    private void addTypedParameter(Type type, String parameter, FilterClause clause)
    {
        if (typedParameter == null)
        {
            typedParameter = new TypedParameter(type);
        }

        if (typedParameter.getType().equals(type))
        {
            typedParameter.addParameters(parameter, clause);
        }
        else
        {
            logger.warn("Invalid type provided, it can either be name or indexes!");
        }
    }

    /**
     * Adds typed parameter to {@link TypedParameter}
     *
     * @param type
     *            type of parameter(e.g. NAMED/INDEXED)
     * @param parameter
     *            parameter name.
     * @param clause
     *            filter clause.
     */
    private void addTypedParameter(Type type, String parameter, UpdateClause clause)
    {
        if (type != null)
        {
            if (typedParameter == null)
            {
                typedParameter = new TypedParameter(type);
            }

            if (typedParameter.getType().equals(type))
            {
                typedParameter.addParameters(parameter, clause);
            }
            else
            {
                logger.warn("Invalid type provided, it can either be name or indexes!");
            }
        }
    }

    private void filterJPAParameterInfo(Type type, String name, String fieldName)
    {
        String attributeName = getAttributeName(fieldName);

        Attribute entityAttribute = ((MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                persistenceUnit)).getEntityAttribute(entityClass, attributeName);
        Class fieldType = entityAttribute.getJavaType();

        if (type.equals(Type.INDEXED))
        {
            typedParameter.addJPAParameter(new JPAParameter(null, Integer.valueOf(name), fieldType));
        }
        else
        {
            typedParameter.addJPAParameter(new JPAParameter(name, null, fieldType));
        }
    }

    private String getAttributeName(String fieldName)
    {
        String attributeName = fieldName;
        if (fieldName.indexOf(".") != -1)
        {
            attributeName = fieldName.substring(0, fieldName.indexOf("."));
        }
        return attributeName;
    }

    /**
     * Sets the parameter.
     *
     * @param name
     *            the name
     * @param value
     *            the value
     */
    public final void setParameter(String name, Object value)
    {
        setParameterValue(":" + name, value);
    }

    public final void setParameter(int position, Object value)
    {
        setParameterValue("?" + position, value);
    }

    /**
     * Sets parameter value into filterClause, depending upon {@link Type}
     *
     * @param name
     *            parameter name.
     * @param value
     *            parameter value.
     */
    private void setParameterValue(String name, Object value)
    {
        if (typedParameter != null)
        {
            List<FilterClause> clauses = typedParameter.getParameters() != null ? typedParameter.getParameters().get(name)
                    : null;
            if (clauses != null)
            {
            for (FilterClause clause : clauses) {
            clause.setValue(value);
}
            }
            else
            {
                if (typedParameter.getUpdateParameters() != null)
                {
                    UpdateClause updateClause = typedParameter.getUpdateParameters().get(name);
                    updateClause.setValue(value);
                }
                else
                {
                    logger.error("Error while setting parameter.");
                    throw new QueryHandlerException("named parameter : " + name + " not found!");
                }
            }
        }
        else
        {
            throw new QueryHandlerException("No named parameter present for query");
        }
    }

    /**
     * Gets the entity class.
     *
     * @return the entityClass
     */
    public final Class getEntityClass()
    {
        return entityClass;
    }

    public final String getEntityAlias()
    {
        return this.entityAlias;
    }
    public boolean isNative()
    {
        return isNativeQuery;
    }

    /**
     * Gets the entity metadata.
     *
     * @return the entity metadata
     */
    public final EntityMetadata getEntityMetadata()
    {
        EntityMetadata metadata = KunderaMetadataManager.getEntityMetadata(kunderaMetadata, entityClass);
        if (metadata == null)
        {
            throw new KunderaException("Unable to load entity metadata for : " + entityClass);
        }
        return metadata;
    }

    /**
     * Gets the filter clause queue.
     *
     * @return the filters
     */
    public final Queue getFilterClauseQueue()
    {
        return filtersQueue;
    }

    /**
     * The FilterClause class to hold a where clause predicate.
     */
    public final class FilterClause
    {

        /** The property. */
        private String property;

        /** The condition. */
        private String condition;

        /** The value. */
        private List<Object> value = new ArrayList<Object>();

        /**
         * The Constructor.
         *
         * @param property
         *            the property
         * @param condition
         *            the condition
         * @param value
         *            the value
         */
        public FilterClause(String property, String condition, Object value)
        {
            super();
            this.property = property;
            this.condition = condition.trim();
            if (value instanceof Collection)
            {
                for (Object valueObject : (Collection) value)
                {
                    this.value.add(KunderaQuery.getValue(valueObject));
                }
            }
            else
            {
                this.value.add(KunderaQuery.getValue(value));
            }
        }

        /**
         * Gets the property.
         *
         * @return the property
         */
        public final String getProperty()
        {
            return property;
        }

        /**
         * Gets the condition.
         *
         * @return the condition
         */
        public final String getCondition()
        {
            return condition;
        }

        /**
         * Gets the value.
         *
         * @return the value
         */
        public final List<Object> getValue()
        {
            return value;
        }

        /**
         * Sets the value.
         *
         * @param value
         *            the value to set
         */
        protected void setValue(Object value)
        {
            List<Object> valObjects = new ArrayList<Object>();
            if (value instanceof Collection)
            {
                for (Object valueObject : (Collection) value)
                {
                    valObjects.add(KunderaQuery.getValue(valueObject));
                }
            }
            else
            {
                valObjects.add(KunderaQuery.getValue(value));
            }

            this.value = valObjects;
        }

        /* @see java.lang.Object#toString() */
        /*
         * (non-Javadoc)
         *
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString()
        {
            StringBuilder builder = new StringBuilder();
            builder.append("FilterClause [property=");
            builder.append(property);
            builder.append(", condition=");
            builder.append(condition);
            builder.append(", value=");
            builder.append(value);
            builder.append("]");
            return builder.toString();
        }
    }

    public final class UpdateClause
    {
        private String property;

        private Object value;

        public UpdateClause(final String property, final Object value)
        {
            this.property = property;
            this.value = KunderaQuery.getValue(value);
        }

        /**
         * @return the property
         */
        public String getProperty()
        {
            return property;
        }

        /**
         * @return the value
         */
        public Object getValue()
        {
            return value;
        }

        /**
         * @param value
         *            the value to set
         */
        public void setValue(Object value)
        {
            this.value = KunderaQuery.getValue(value);
        }

    }

    /* @see java.lang.Object#clone() */
    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#clone()
     */
    @Override
    public final Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }

    /* @see java.lang.Object#toString() */
    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public final String toString()
    {
        StringBuilder builder = new StringBuilder();
        builder.append("KunderaQuery [entityName=");
        builder.append(entityName);
        builder.append(", entityAlias=");
        builder.append(entityAlias);
        builder.append(", filtersQueue=");
        builder.append(filtersQueue);
        builder.append("]");
        return builder.toString();
    }

    // helper method
    /**
     * Tokenize.
     *
     * @param where
     *            the where
     * @param pattern
     *            the pattern
     * @return the list
     */
    private static List<String> tokenize(String where, Pattern pattern, boolean isInterClause)
    {
        List<String> split = new ArrayList<String>();
        Matcher matcher = pattern.matcher(where);
        int lastIndex = 0;
        String s;
        // int count = 0;
        while (matcher.find())
        {
            s = where.substring(lastIndex, matcher.start()).trim();
            addSplit(isInterClause, split, s);
//
//            if(!s.equals(""))
//            split.add(s);
            /*if(s.startsWith("("))
            {
                split.add("(");
                split.add(s.substring(s.indexOf("(")+1));
            }
            else if (s.endsWith(")"))
            {
                split.add(s.substring(0,s.lastIndexOf(")")));
            }*/
            s = matcher.group();
            if(s.equalsIgnoreCase("AND") || s.equalsIgnoreCase("OR") || s.equalsIgnoreCase("BETWEEN"))
            s=" "+s+" ";
            // if next group starts with "(" and last record in split ends with IN on NOT IN, append in previous split only.
            split.add(s.toUpperCase());
            lastIndex = matcher.end();
            if(!isInterClause)
            break;
            // count++;
        }
        s = where.substring(lastIndex).trim();
        if (!s.equals(""))
        {
            // split.add(s);

            if (isInterClause)
            {
                if (s.startsWith("(") && s.endsWith(")"))
                {
                    split.add("(");
                    split.add(s.substring(s.indexOf("(") + 1, s.lastIndexOf(")")));
                    split.add(")");
                } else
                {
                    split.add(s);
                }
            }
            else
            {
                split.add(s);

            }
        }
//        addSplit(isInterClause, split, s);
       
        return split;
    }



    /**
     * Gets the metamodel.
     *
     * @return the metamodel
     */
    private MetamodelImpl getMetamodel(String pu)
    {
        return KunderaMetadataManager.getMetamodel(kunderaMetadata, pu);
    }

    /**
     * Gets the persistence units.
     *
     * @return the persistenceUnits
     */
    public String getPersistenceUnit()
    {
        return persistenceUnit;
    }

    /**
     * Parses the ordering @See Order By Clause.
     *
     * @param ordering
     *            the ordering
     */
    private void parseOrdering(String ordering)
    {
        final String comma = ",";
        final String space = " ";

        StringTokenizer tokenizer = new StringTokenizer(ordering, comma);

        sortOrders = new ArrayList<KunderaQuery.SortOrdering>();
        while (tokenizer.hasMoreTokens())
        {
            String order = (String) tokenizer.nextElement();
            StringTokenizer token = new StringTokenizer(order, space);
            SortOrder orderType = SortOrder.ASC;

            String colName = (String) token.nextElement();
            while (token.hasMoreElements())
            {
                String nextOrder = (String) token.nextElement();

                // more spaces given.
                if (StringUtils.isNotBlank(nextOrder))
                {
                    try
                    {
                        orderType = SortOrder.valueOf(nextOrder);
                    }
                    catch (IllegalArgumentException e)
                    {
                        logger.error("Error while parsing order by clause:");
                        throw new JPQLParseException("Invalid sort order provided:" + nextOrder);
                    }
                }
            }
            sortOrders.add(new SortOrdering(colName, orderType));
        }
    }

    /**
     * Containing SortOrder.
     */
    public class SortOrdering
    {

        /** The column name. */
        String columnName;

        /** The order. */
        SortOrder order;

        /**
         * Instantiates a new sort ordering.
         *
         * @param columnName
         *            the column name
         * @param order
         *            the order
         */
        public SortOrdering(String columnName, SortOrder order)
        {
            this.columnName = columnName;
            this.order = order;
        }

        /**
         * Gets the column name.
         *
         * @return the column name
         */
        public String getColumnName()
        {
            return columnName;
        }

        /**
         * Gets the order.
         *
         * @return the order
         */
        public SortOrder getOrder()
        {
            return order;
        }
    }

    /**
     * The Enum SortOrder.
     */
    public enum SortOrder
    {
        /** The ASC. */
        ASC,
        /** The DESC. */
        DESC;
    }

    /**
     * @return the updateClauseQueue
     */
    public Queue<UpdateClause> getUpdateClauseQueue()
    {
        return updateClauseQueue;
    }

    public boolean isUpdateClause()
    {
        return !updateClauseQueue.isEmpty();
    }

    public void addUpdateClause(final String property, final String value)
    {
        UpdateClause updateClause = new UpdateClause(property.trim(), value.trim());
        updateClauseQueue.add(updateClause);
        addTypedParameter(value.trim().startsWith("?") ? Type.INDEXED : value.trim().startsWith(":") ? Type.NAMED
                : null, property, updateClause);
    }

    /**
     * @param b
     */
    public void setIsDeleteUpdate(boolean b)
    {
        this.isDeleteUpdate = b;
    }

    public boolean isDeleteUpdate()
    {
        return isDeleteUpdate;
    }

    public String getJPAQuery()
    {
        return this.jpaQuery;
    }

    /**
     * Return parsed token string.
     *
     * @param tokens
     *            inter claues token string.
     * @param indexName
     *            table name
     * @return tokens converted to "<=" and ">=" clause
     */
    private List<String> parseFilterForBetweenClause(List<String> tokens)
    {
        // There should be whitespace on bothside of keyword between.
        final String between = " BETWEEN ";

        if (tokens.contains(between))
        {
            // change token set to parse and compile.
            int idxOfBetween = tokens.indexOf(between);
            String property = tokens.get(idxOfBetween - 1);
            Matcher match = INTRA_CLAUSE_PATTERN.matcher(property);
            // in case any intra clause given along with column name.
            if (match.find())
            {
                logger.error("bad jpa query:");
                throw new JPQLParseException("invalid column name" + property);
            }
            String minValue = tokens.get(idxOfBetween + 1);
            String maxValue = tokens.get(idxOfBetween + 3);

            tokens.set(idxOfBetween + 1, property + ">=" + minValue);
            tokens.set(idxOfBetween + 3, property + "<=" + maxValue);
            tokens.remove(idxOfBetween - 1);
            tokens.remove(idxOfBetween - 1);
        }

        return tokens;
    }

    private class TypedParameter
    {
        private Type type;

        private Set<Parameter<?>> jpaParameters = new HashSet<Parameter<?>>();

        private Map<String, List<FilterClause>> parameters;

        private Map<String, UpdateClause> updateParameters;

        /**
         *
         */
        public TypedParameter(Type type)
        {
            this.type = type;
        }

        /**
         * @return the type
         */
        private Type getType()
        {
            return type;
        }

        /**
         * @return the parameters
         */
        Map<String, List<FilterClause>> getParameters()
        {
            return parameters;
        }

        /**
         * @return the parameters
         */
        Map<String, UpdateClause> getUpdateParameters()
        {
            return updateParameters;
        }

        void addParameters(String key, FilterClause clause)
        {
            if (parameters == null)
            {
                parameters = new HashMap<String, List<FilterClause>>();
            }
            if(!parameters.containsKey(key)) {
            parameters.put(key, new ArrayList<KunderaQuery.FilterClause>());
            }
            parameters.get(key).add(clause);
        }

        void addParameters(String key, UpdateClause clause)
        {
            if (updateParameters == null)
            {
                updateParameters = new HashMap<String, UpdateClause>();
            }

            updateParameters.put(key, clause);
        }

        void addJPAParameter(Parameter param)
        {
            jpaParameters.add(param);
        }
    }

    private enum Type
    {
        INDEXED, NAMED
    }

    /*
     * JPA Parameter type
     */
    private class JPAParameter<T> implements Parameter<T>
    {
        private String name;

        private Integer position;

        private Class<T> type;

        /**
         *
         */
        JPAParameter(String name, Integer position, Class<T> type)
        {
            this.name = name;
            this.position = position;
            this.type = type;
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.persistence.Parameter#getName()
         */
        @Override
        public String getName()
        {
            return name;
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.persistence.Parameter#getPosition()
         */
        @Override
        public Integer getPosition()
        {
            return position;
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.persistence.Parameter#getParameterType()
         */
        @Override
        public Class<T> getParameterType()
        {
            return type;
        }

        @Override
        public int hashCode()
        {
            return HashCodeBuilder.reflectionHashCode(this);
        }

        @Override
        public boolean equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (!obj.getClass().equals(this.getClass()))
            {
                return false;
            }

            Parameter<?> typed = (Parameter<?>) obj;

            if (typed.getParameterType().equals(this.getParameterType()))
            {
                if (this.getName() == null && typed.getName() == null)
                {
                    return this.getPosition() != null && this.getPosition().equals(typed.getPosition());
                }
                else
                {
                    return this.getName() != null && this.getName().equals(typed.getName());
                }

            }

            return false;
        }

        @Override
        public String toString()
        {
            StringBuilder strBuilder = new StringBuilder();
            strBuilder.append("[ name = " + this.getName() + "]");
            strBuilder.append("[ position = " + this.getPosition() + "]");
            strBuilder.append("[ type = " + this.getParameterType() + "]");
            return strBuilder.toString();
        }
    }

    /**
     * Method to skip string literal as per JPA specification. if literal starts
     * is enclose within "''" then skip "'" and include "'" in case of "''"
     * replace it with "'".
     *
     * @param value
     *            value.
     *
     * @return replaced string in case of string, else will return original
     *         value.
     */
    private static Object getValue(Object value)
    {
        if (value != null && value.getClass().isAssignableFrom(String.class))
        {
            return ((String) value).replaceAll("^'", "").replaceAll("'$", "").replaceAll("''", "'");
        }

        return value;
    }

    private static void addSplit(boolean isInterClause, List<String> split, String s)
    {
        if (!s.equals(""))
        {

            if (isInterClause)
            {
                if (s.startsWith("(") && s.endsWith(")"))
                {
                    split.add("(");
                    split.add(s.substring(s.indexOf("(") + 1, s.lastIndexOf(")")));
                    split.add(")");
                }
                else if (s.startsWith("("))
                {
                    split.add("(");
                    split.add(s.substring(s.indexOf("(") + 1));
                }
                else if (s.endsWith(")"))
                {
                    split.add(s.substring(0, s.lastIndexOf(")")));
                }else
                {
                    split.add(s);
                }
            }
            else
            {
                split.add(s);
            }
        }
    }
}
TOP

Related Classes of com.impetus.kundera.query.KunderaQuery

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.