/* * Copyright 2010 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.LinkedList; import java.util.List; import java.util.Queue; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.PersistenceException; import com.impetus.kundera.ejb.EntityManagerImpl; import com.impetus.kundera.metadata.EntityMetadata; import com.impetus.kundera.metadata.MetadataManager; /** * The Class KunderaQuery. */ public abstract class KunderaQuery { /** The Constant SINGLE_STRING_KEYWORDS. */ public static final String[] SINGLE_STRING_KEYWORDS = { "SELECT", "UPDATE", "DELETE", "UNIQUE", "FROM", "WHERE", "GROUP BY", "HAVING", "ORDER BY" }; /** The Constant INTER_CLAUSE_OPERATORS. */ public static final String[] INTER_CLAUSE_OPERATORS = { "AND", "OR" }; /** The Constant INTRA_CLAUSE_OPERATORS. */ public static final String[] INTRA_CLAUSE_OPERATORS = { "=", "LIKE" }; /** The INTER pattern. */ private static final Pattern INTER_CLAUSE_PATTERN = Pattern.compile("\\band\\b|\\bor\\b", Pattern.CASE_INSENSITIVE); /** The INTRA pattern. */ private static final Pattern INTRA_CLAUSE_PATTERN = Pattern.compile("=|\\blike\\b", Pattern.CASE_INSENSITIVE); /** The EntityManager. */ private EntityManagerImpl em; /** The MetadataManager. */ private MetadataManager metadataManager; /** 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; // contains a Queue of alternate FilterClause object and Logical Strings // (AND, OR etc.) /** The filters queue. */ private Queue filtersQueue = new LinkedList(); /** * Instantiates a new kundera query. * * @param em EntityManager * @param metadataManager MetadataManager */ public KunderaQuery(EntityManagerImpl em, MetadataManager metadataManager) { this.em = em; this.metadataManager = metadataManager; } /** * Gets the entity manager. * * @return the em */ public EntityManagerImpl getEntityManager () { return em; } /** * Gets the metadata manager. * * @return the metadataManager */ public MetadataManager getMetadataManager() { return metadataManager; } /** * 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; } /** * 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 String getOrdering() { return ordering; } /** * Gets the result. * * @return the result */ public final String getResult() { return result; } // must be executed after parse(). it verifies and populated the query // predicates. /** * Post parsing init. */ protected void postParsingInit() { initEntityClass(); initFilter(); } /** * Inits the entity class. */ private void initEntityClass() { // String result = getResult(); // String from = getFrom(); String fromArray[] = from.split(" "); if (fromArray.length != 2) { throw new PersistenceException("Bad query format: " + from); } if (!fromArray[1].equals(result)) { throw new PersistenceException("Bad query format: " + from); } this.entityName = fromArray[0]; this.entityAlias = fromArray[1]; entityClass = metadataManager.getEntityClassByName(entityName); if (null == entityClass) { throw new PersistenceException("No entity found by the name: " + entityName); } EntityMetadata metadata = metadataManager.getEntityMetadata(entityClass); if (!metadata.isIndexable()) { throw new PersistenceException(entityClass + " is not indexed. What are you searching for dude?"); } } /** * Inits the filter. */ private void initFilter() { EntityMetadata metadata = metadataManager.getEntityMetadata(entityClass); String indexName = metadata.getIndexName(); // String filter = getFilter(); if (null == filter) { return; } List<String> clauses = tokenize(filter, INTER_CLAUSE_PATTERN); // clauses must be alternate Inter and Intra conbination, starting with // Intra. boolean newClause = true; for (String clause : clauses) { if (newClause) { List<String> tokens = tokenize(clause, INTRA_CLAUSE_PATTERN); if (tokens.size() != 3) { throw new PersistenceException("bad jpa query: " + clause); } // strip alias from property name String property = tokens.get(0); property = property.substring((entityAlias + ".").length()); property = indexName + "." + property; // verify condition String condition = tokens.get(1); if (!Arrays.asList(INTRA_CLAUSE_OPERATORS).contains(condition.toUpperCase())) { throw new PersistenceException("bad jpa query: " + clause); } filtersQueue.add(new FilterClause(property, condition, tokens.get(2))); newClause = false; } else { if (Arrays.asList(INTER_CLAUSE_OPERATORS).contains(clause.toUpperCase())) { filtersQueue.add(clause.toUpperCase()); newClause = true; } else { throw new PersistenceException("bad jpa query: " + clause); } } } } /** * Sets the parameter. * * @param name * the name * @param value * the value */ public final void setParameter(String name, String value) { boolean found = false; for (Object object : getFilterClauseQueue()) { if (object instanceof FilterClause) { FilterClause filter = (FilterClause) object; // key if (filter.getValue().equals(":" + name)) { filter.setValue(value); found = true; return; } } } if (!found) { throw new PersistenceException("invalid parameter: " + name); } } /** * Gets the entity class. * * @return the entityClass */ public final Class<?> getEntityClass() { return entityClass; } /** * Gets the filter clause queue. * * @return the filters */ public final Queue getFilterClauseQueue() { return filtersQueue; } // class to keep hold of a where clause predicate /** * The Class FilterClause. */ public final class FilterClause { /** The property. */ private String property; /** The condition. */ private String condition; /** The value. */ String value; /** * The Constructor. * * @param property * the property * @param condition * the condition * @param value * the value */ public FilterClause(String property, String condition, String value) { super(); this.property = property; this.condition = condition; this.value = 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 String getValue() { return value; } /** * Sets the value. * * @param value * the value to set */ protected void setValue(String value) { this.value = value; } /* @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(); } } /* @see java.lang.Object#clone() */ @Override public final Object clone() throws CloneNotSupportedException { return super.clone(); } /* @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) { 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(); split.add(s); s = matcher.group(); split.add(s); lastIndex = matcher.end(); // count++; } s = where.substring(lastIndex).trim(); split.add(s); return split; } }