This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit debcdc94dab0812372b6fbb04d7be9b1db17d079 Author: Andrus Adamchik <[email protected]> AuthorDate: Sun Aug 30 12:03:35 2026 -0400 removing dead code - EOQuery --- .../wocompat/EOFetchSpecificationParser.java | 302 +++++++++++++ .../apache/cayenne/wocompat/EOModelProcessor.java | 2 +- .../org/apache/cayenne/wocompat/EOObjEntity.java | 4 +- .../java/org/apache/cayenne/wocompat/EOQuery.java | 495 --------------------- ...st.java => EOFetchSpecificationParserTest.java} | 45 +- 5 files changed, 331 insertions(+), 517 deletions(-) diff --git a/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOFetchSpecificationParser.java b/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOFetchSpecificationParser.java new file mode 100644 index 000000000..a52d0866f --- /dev/null +++ b/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOFetchSpecificationParser.java @@ -0,0 +1,302 @@ +/***************************************************************** + * 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 + * + * https://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 org.apache.cayenne.wocompat; + +import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.ExpressionException; +import org.apache.cayenne.exp.ExpressionFactory; +import org.apache.cayenne.exp.ExpressionParameter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * EOFetchSpecificationParser parses EOFetchSpecifications from a + * WebObjects-style EOModel. It recursively builds Cayenne Expression + * objects and assembles them into the final aggregate Expression. + */ +@SuppressWarnings("unchecked") +class EOFetchSpecificationParser { + + // Xcode/EOModeler expressions have a colon at the end of the selector + // name + // (just like standard Objective-C syntax). WOLips does not. Add both + // sets to the hash map to handle both types of models. + + // Selector strings (Java-base). + static final String IS_EQUAL_TO = "isEqualTo"; + static final String IS_NOT_EQUAL_TO = "isNotEqualTo"; + static final String IS_LIKE = "isLike"; + static final String CASE_INSENSITIVE_LIKE = "isCaseInsensitiveLike"; + static final String IS_LESS_THAN = "isLessThan"; + static final String IS_LESS_THAN_OR_EQUAL_TO = "isLessThanOrEqualTo"; + static final String IS_GREATER_THAN = "isGreaterThan"; + static final String IS_GREATER_THAN_OR_EQUAL_TO = "isGreaterThanOrEqualTo"; + + private static final String OBJ_C = ":"; // Objective-C syntax addition. + + private static Map<String, Integer> selectorToExpressionBridge; + private static final Logger LOGGER = LoggerFactory.getLogger(EOFetchSpecificationParser.class); + + /** + * selectorToExpressionBridge is just a mapping of EOModeler's selector + * types to Cayenne Expression types. + * + * @return HashMap of Expression types, keyed by the corresponding + * selector name + */ + static synchronized Map<String, Integer> selectorToExpressionBridge() { + // Initialize selectorToExpressionBridge if needed. + if (null == selectorToExpressionBridge) { + selectorToExpressionBridge = new HashMap<>(); + + selectorToExpressionBridge.put(IS_EQUAL_TO, Expression.EQUAL_TO); + selectorToExpressionBridge.put(IS_EQUAL_TO + OBJ_C, Expression.EQUAL_TO); + + selectorToExpressionBridge.put(IS_NOT_EQUAL_TO, Expression.NOT_EQUAL_TO); + selectorToExpressionBridge.put(IS_NOT_EQUAL_TO + OBJ_C, Expression.NOT_EQUAL_TO); + + selectorToExpressionBridge.put(IS_LIKE, Expression.LIKE); + selectorToExpressionBridge.put(IS_LIKE + OBJ_C, Expression.LIKE); + + selectorToExpressionBridge.put(CASE_INSENSITIVE_LIKE, Expression.LIKE_IGNORE_CASE); + selectorToExpressionBridge.put(CASE_INSENSITIVE_LIKE + OBJ_C, Expression.LIKE_IGNORE_CASE); + + selectorToExpressionBridge.put(IS_LESS_THAN, Expression.LESS_THAN); + selectorToExpressionBridge.put(IS_LESS_THAN + OBJ_C, Expression.LESS_THAN); + + selectorToExpressionBridge.put(IS_LESS_THAN_OR_EQUAL_TO, Expression.LESS_THAN_EQUAL_TO); + selectorToExpressionBridge.put(IS_LESS_THAN_OR_EQUAL_TO + OBJ_C, Expression.LESS_THAN_EQUAL_TO); + + selectorToExpressionBridge.put(IS_GREATER_THAN, Expression.GREATER_THAN); + selectorToExpressionBridge.put(IS_GREATER_THAN + OBJ_C, Expression.GREATER_THAN); + + selectorToExpressionBridge.put(IS_GREATER_THAN_OR_EQUAL_TO, Expression.GREATER_THAN_EQUAL_TO); + selectorToExpressionBridge.put(IS_GREATER_THAN_OR_EQUAL_TO + OBJ_C, Expression.GREATER_THAN_EQUAL_TO); + } + + return selectorToExpressionBridge; + } + + /** + * isAggregate determines whether a qualifier is "aggregate" -- has + * children -- or "simple". + * + * @param qualifier + * - a Map containing the qualifier settings + * @return boolean indicating whether the qualifier is "aggregate" + * qualifier + */ + static boolean isAggregate(Map<String, ?> qualifier) { + boolean result = true; + + String theClass = (String) qualifier.get("class"); + if (theClass == null) { + return false; // should maybe throw an exception? + } + if (theClass.equalsIgnoreCase("EOKeyValueQualifier") + || theClass.equalsIgnoreCase("EOKeyComparisonQualifier")) { + result = false; + } + + return result; + } + + /** + * expressionTypeForQualifier looks at a qualifier containing the + * EOModeler FetchSpecification and returns the equivalent Cayenne + * Expression type for its selector. + * + * @param qualifierMap + * - a Map containing the qualifier settings to examine. + * @return int Expression type + */ + static int expressionTypeForQualifier(Map<String, ?> qualifierMap) { + // get selector + String selector = (String) qualifierMap.get("selectorName"); + return expressionTypeForSelector(selector); + } + + /** + * expressionTypeForSelector looks at a selector from an EOModeler + * FetchSpecification and returns the equivalent Cayenne Expression + * type. + * + * @param selector + * - a String containing the selector name. + * @return int Expression type + */ + static int expressionTypeForSelector(String selector) { + Integer expType = selectorToExpressionBridge().get(selector); + return (expType != null ? expType : -1); + } + + /** + * aggregateExpressionClassForQualifier looks at a qualifer and returns + * the aggregate type: one of Expression.AND, Expression.OR, or + * Expression.NOT + * + * @param qualifierMap + * - containing the qualifier to examine + * @return int aggregate Expression type + */ + static int aggregateExpressionClassForQualifier(Map<String, ?> qualifierMap) { + String qualifierClass = (String) qualifierMap.get("class"); + if (qualifierClass != null) { + if (qualifierClass.equalsIgnoreCase("EOAndQualifier")) { + return Expression.AND; + } else if (qualifierClass.equalsIgnoreCase("EOOrQualifier")) { + return Expression.OR; + } else if (qualifierClass.equalsIgnoreCase("EONotQualifier")) { + return Expression.NOT; + } + } + + return -1; // error + } + + /** + * makeQualifier recursively builds an Expression for each condition in + * the qualifierMap and assembles from them the complex Expression to + * represent the entire EOFetchSpecification. + * + * @param qualifierMap + * - Map representation of EOFetchSpecification + * @return Expression translation of the EOFetchSpecification + */ + static Expression makeQualifier(EOObjEntity entity, Map<String, ?> qualifierMap) { + if (isAggregate(qualifierMap)) { + // the fetch specification has more than one qualifier + int aggregateClass = aggregateExpressionClassForQualifier(qualifierMap); // AND, + // OR, + // NOT + + if (aggregateClass == Expression.NOT) { + // NOT qualifiers only have one child, keyed with + // "qualifier" + Map<String, ?> child = (Map<String, ?>) qualifierMap.get("qualifier"); + // build the child expression + Expression childExp = makeQualifier(entity, child); + + return childExp.notExp(); // add the "not" clause and return + // the + // result + } else { + // AND, OR qualifiers can have multiple children, keyed with + // "qualifiers" + // get the list of children + List<Map<String, ?>> children = (List<Map<String, ?>>) qualifierMap.get("qualifiers"); + if (children != null) { + ArrayList<Expression> childExpressions = new ArrayList<>(); + // build an Expression for each child + for (Map<String, ?> child : children) { + Expression childExp = makeQualifier(entity, child); + childExpressions.add(childExp); + } + // join the child expressions and return the result + return ExpressionFactory.joinExp(aggregateClass, childExpressions); + } + } + + } // end if isAggregate(qualifierMap)... + + // the query has a single qualifier + // get expression selector type + String qualifierClass = (String) qualifierMap.get("class"); + + // the key or key path we're comparing + String key = null; + // the key, keyPath, value, or parameterized value against which + // we're + // comparing the key + Object comparisonValue = null; + + if ("EOKeyComparisonQualifier".equals(qualifierClass)) { + // Comparing two keys or key paths + key = (String) qualifierMap.get("leftValue"); + comparisonValue = qualifierMap.get("rightValue"); + // FIXME: I think EOKeyComparisonQualifier style Expressions are not supported... + return null; + } else if ("EOKeyValueQualifier".equals(qualifierClass)) { + // Comparing key with a value or parameterized value + key = (String) qualifierMap.get("key"); + Object value = qualifierMap.get("value"); + + if (value instanceof Map) { + Map<String, String> valueMap = (Map<String, String>) value; + String objClass = valueMap.get("class"); // can be a + // qualifier class or java type + if ("EOQualifierVariable".equals(objClass) && valueMap.containsKey("_key")) { + // make a parameterized expression + String paramName = valueMap.get("_key"); + comparisonValue = new ExpressionParameter(paramName); + } else { + Object queryVal = valueMap.get("value"); + if ("NSNumber".equals(objClass)) { + // comparison to NSNumber -- cast + comparisonValue = queryVal; + } else if ("EONull".equals(objClass)) { + // comparison to null + comparisonValue = null; + } else { // Could there be other types? boolean, date, + // etc.??? + // no cast + comparisonValue = queryVal; + } + } + + } else if (value instanceof String) { + // value expression + comparisonValue = value; + } // end if (value instanceof Map) else... + } + + // check whether the key is an object path; if at least one + // component is not, + // switch to db path.. + + Expression keyExp = ExpressionFactory.exp(key); + try { + entity.lastPathComponent(keyExp, Collections.emptyMap()); + } catch (ExpressionException e) { + try { + keyExp = entity.translateToDbPath(keyExp); + } catch (Exception dbpathEx) { + LOGGER.warn("Couldn't find {} in {} in EOModel", keyExp, entity.getName()); + } + } + + try { + Expression exp = ExpressionFactory.expressionOfType(expressionTypeForQualifier(qualifierMap)); + + exp.setOperand(0, keyExp); + exp.setOperand(1, comparisonValue); + return exp; + } catch (ExpressionException e) { + LOGGER.warn(e.getUnlabeledMessage()); + return null; + } + } +} diff --git a/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java b/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java index eb1eb79e1..079ccf07b 100644 --- a/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java +++ b/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java @@ -263,7 +263,7 @@ public class EOModelProcessor { // qualifiers Map<String, ?> qualifierMap = (Map<String, ?>) plistMap.get("qualifier"); if (qualifierMap != null && !qualifierMap.isEmpty()) { - descriptor.setQualifier(EOQuery.EOFetchSpecificationParser.makeQualifier((EOObjEntity) root, qualifierMap)); + descriptor.setQualifier(EOFetchSpecificationParser.makeQualifier((EOObjEntity) root, qualifierMap)); } // prefetches diff --git a/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOObjEntity.java b/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOObjEntity.java index 53cba8228..515e47bdf 100644 --- a/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOObjEntity.java +++ b/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOObjEntity.java @@ -83,7 +83,7 @@ public class EOObjEntity extends ObjEntity { * @since 1.2 */ // TODO: andrus, 5/27/2006 - make public after 1.2. Also maybe move entity - // initialization code from EOModelProcessor to this class, kind of like EOQuery does. + // initialization code from EOModelProcessor to this class. Map getEoMap() { return eoMap; } @@ -92,7 +92,7 @@ public class EOObjEntity extends ObjEntity { * @since 1.2 */ // TODO: andrus, 5/27/2006 - make public after 1.2. Also maybe move entity - // initialization code from EOModelProcessor to this class, kind of like EOQuery does. + // initialization code from EOModelProcessor to this class. void setEoMap(Map eoMap) { this.eoMap = eoMap; } diff --git a/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOQuery.java b/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOQuery.java deleted file mode 100644 index 5fecd4bc6..000000000 --- a/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOQuery.java +++ /dev/null @@ -1,495 +0,0 @@ -/***************************************************************** - * 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 - * - * https://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 org.apache.cayenne.wocompat; - -import org.apache.cayenne.exp.Expression; -import org.apache.cayenne.exp.ExpressionException; -import org.apache.cayenne.exp.ExpressionFactory; -import org.apache.cayenne.exp.ExpressionParameter; -import org.apache.cayenne.exp.parser.ASTObjPath; -import org.apache.cayenne.map.Entity; -import org.apache.cayenne.map.ObjAttribute; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.map.ObjRelationship; -import org.apache.cayenne.query.ObjectSelect; -import org.apache.cayenne.query.PrefetchTreeNode; -import org.apache.cayenne.query.SortOrder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A descriptor of SelectQuery loaded from EOModel. It is an informal - * "decorator" of Cayenne SelectQuery to provide access to the extra information - * of WebObjects EOFetchSpecification. - * - * @since 1.1 - * @since 5.0 this query extends {@link ObjectSelect} - */ -@SuppressWarnings("unchecked") -public class EOQuery<T> extends ObjectSelect<T> { - - protected Map<String, ?> plistMap; - protected Map<String, String> bindings; - - public EOQuery(ObjEntity root, Map<String, ?> plistMap) { - super(); - entityName(root.getName()); - setRoot(root); - this.plistMap = plistMap; - initFromPlist(plistMap); - } - - protected void initFromPlist(Map<String, ?> plistMap) { - - if("YES".equalsIgnoreCase((String) plistMap.get("usesDistinct"))) { - distinct(); - } - - Object fetchLimit = plistMap.get("fetchLimit"); - if (fetchLimit != null) { - try { - if (fetchLimit instanceof Number) { - limit(((Number) fetchLimit).intValue()); - } else { - limit(Integer.parseInt(fetchLimit.toString())); - } - } catch (NumberFormatException nfex) { - // ignoring... - } - } - - // sort orderings - List<Map<String, String>> orderings = (List<Map<String, String>>) plistMap.get("sortOrderings"); - if (orderings != null && !orderings.isEmpty()) { - for (Map<String, String> ordering : orderings) { - boolean asc = !"compareDescending:".equals(ordering.get("selectorName")); - String key = ordering.get("key"); - if (key != null) { - orderBy(key, asc ? SortOrder.ASCENDING : SortOrder.DESCENDING); - } - } - } - - // qualifiers - Map<String, ?> qualifierMap = (Map<String, ?>) plistMap.get("qualifier"); - if (qualifierMap != null && !qualifierMap.isEmpty()) { - where(makeQualifier(qualifierMap)); - } - - // prefetches - List<?> prefetches = (List<?>) plistMap.get("prefetchingRelationshipKeyPaths"); - if (prefetches != null && !prefetches.isEmpty()) { - for (Object prefetch : prefetches) { - prefetch((String) prefetch, PrefetchTreeNode.UNDEFINED_SEMANTICS); - } - } - - // data rows - note that we do not support fetching individual columns - // in the - // modeler... - if (plistMap.containsKey("rawRowKeyPaths")) { - fetchDataRows(); - } - } - - public Collection<String> getBindingNames() { - if (bindings == null) { - initBindings(); - } - - return bindings.keySet(); - } - - public String bindingClass(String name) { - if (bindings == null) { - initBindings(); - } - - return bindings.get(name); - } - - private synchronized void initBindings() { - if (bindings != null) { - return; - } - - bindings = new HashMap<>(); - - if (!(root instanceof Entity)) { - return; - } - - Map<String, ?> qualifier = (Map<String, ?>) plistMap.get("qualifier"); - initBindings(bindings, (Entity<?,?,?>) root, qualifier); - } - - private void initBindings(Map<String,String> bindings, Entity<?,?,?> entity, Map<String, ?> qualifier) { - if (qualifier == null) { - return; - } - - if ("EOKeyValueQualifier".equals(qualifier.get("class"))) { - String key = (String) qualifier.get("key"); - if (key == null) { - return; - } - - Object value = qualifier.get("value"); - if (!(value instanceof Map)) { - return; - } - - Map<String, ?> valueMap = (Map<String, ?>) value; - if (!"EOQualifierVariable".equals(valueMap.get("class")) || !valueMap.containsKey("_key")) { - return; - } - - String name = (String) valueMap.get("_key"); - String className = null; - - // we don't know whether its obj path or db path, so the expression - // can blow - // ... in fact we can't support DB Path as the key is different from - // external - // name, - // so we will use Object type for all DB path... - try { - Object lastObject = new ASTObjPath(key).evaluate(entity); - - if (lastObject instanceof ObjAttribute) { - className = ((ObjAttribute) lastObject).getType(); - } else if (lastObject instanceof ObjRelationship) { - ObjEntity target = ((ObjRelationship) lastObject).getTargetEntity(); - if (target != null) { - className = target.getClassName(); - } - } - } catch (ExpressionException ex) { - className = "java.lang.Object"; - } - - if (className == null) { - className = "java.lang.Object"; - } - - bindings.put(name, className); - - return; - } - - List<Map<String, ?>> children = (List<Map<String, ?>>) qualifier.get("qualifiers"); - if (children != null) { - for (Map<String, ?> child : children) { - initBindings(bindings, entity, child); - } - } - } - - /** - * Creates the Expression equivalent of the EOFetchSpecification represented - * by the Map. - * - * @param qualifierMap - * - FetchSpecification to translate - * @return Expression equivalent to FetchSpecification - */ - public synchronized Expression makeQualifier(Map<String, ?> qualifierMap) { - if (qualifierMap == null) { - return null; - } - - return EOFetchSpecificationParser.makeQualifier((EOObjEntity) getRoot(), qualifierMap); - } - - /** - * EOFetchSpecificationParser parses EOFetchSpecifications from a - * WebObjects-style EOModel. It recursively builds Cayenne Expression - * objects and assembles them into the final aggregate Expression. - */ - static class EOFetchSpecificationParser { - - // Xcode/EOModeler expressions have a colon at the end of the selector - // name - // (just like standard Objective-C syntax). WOLips does not. Add both - // sets to the hash map to handle both types of models. - - // Selector strings (Java-base). - static final String IS_EQUAL_TO = "isEqualTo"; - static final String IS_NOT_EQUAL_TO = "isNotEqualTo"; - static final String IS_LIKE = "isLike"; - static final String CASE_INSENSITIVE_LIKE = "isCaseInsensitiveLike"; - static final String IS_LESS_THAN = "isLessThan"; - static final String IS_LESS_THAN_OR_EQUAL_TO = "isLessThanOrEqualTo"; - static final String IS_GREATER_THAN = "isGreaterThan"; - static final String IS_GREATER_THAN_OR_EQUAL_TO = "isGreaterThanOrEqualTo"; - - private static final String OBJ_C = ":"; // Objective-C syntax addition. - - private static Map<String, Integer> selectorToExpressionBridge; - private static final Logger LOGGER = LoggerFactory.getLogger(EOFetchSpecificationParser.class); - - /** - * selectorToExpressionBridge is just a mapping of EOModeler's selector - * types to Cayenne Expression types. - * - * @return HashMap of Expression types, keyed by the corresponding - * selector name - */ - static synchronized Map<String, Integer> selectorToExpressionBridge() { - // Initialize selectorToExpressionBridge if needed. - if (null == selectorToExpressionBridge) { - selectorToExpressionBridge = new HashMap<>(); - - selectorToExpressionBridge.put(IS_EQUAL_TO, Expression.EQUAL_TO); - selectorToExpressionBridge.put(IS_EQUAL_TO + OBJ_C, Expression.EQUAL_TO); - - selectorToExpressionBridge.put(IS_NOT_EQUAL_TO, Expression.NOT_EQUAL_TO); - selectorToExpressionBridge.put(IS_NOT_EQUAL_TO + OBJ_C, Expression.NOT_EQUAL_TO); - - selectorToExpressionBridge.put(IS_LIKE, Expression.LIKE); - selectorToExpressionBridge.put(IS_LIKE + OBJ_C, Expression.LIKE); - - selectorToExpressionBridge.put(CASE_INSENSITIVE_LIKE, Expression.LIKE_IGNORE_CASE); - selectorToExpressionBridge.put(CASE_INSENSITIVE_LIKE + OBJ_C, Expression.LIKE_IGNORE_CASE); - - selectorToExpressionBridge.put(IS_LESS_THAN, Expression.LESS_THAN); - selectorToExpressionBridge.put(IS_LESS_THAN + OBJ_C, Expression.LESS_THAN); - - selectorToExpressionBridge.put(IS_LESS_THAN_OR_EQUAL_TO, Expression.LESS_THAN_EQUAL_TO); - selectorToExpressionBridge.put(IS_LESS_THAN_OR_EQUAL_TO + OBJ_C, Expression.LESS_THAN_EQUAL_TO); - - selectorToExpressionBridge.put(IS_GREATER_THAN, Expression.GREATER_THAN); - selectorToExpressionBridge.put(IS_GREATER_THAN + OBJ_C, Expression.GREATER_THAN); - - selectorToExpressionBridge.put(IS_GREATER_THAN_OR_EQUAL_TO, Expression.GREATER_THAN_EQUAL_TO); - selectorToExpressionBridge.put(IS_GREATER_THAN_OR_EQUAL_TO + OBJ_C, Expression.GREATER_THAN_EQUAL_TO); - } - - return selectorToExpressionBridge; - } - - /** - * isAggregate determines whether a qualifier is "aggregate" -- has - * children -- or "simple". - * - * @param qualifier - * - a Map containing the qualifier settings - * @return boolean indicating whether the qualifier is "aggregate" - * qualifier - */ - static boolean isAggregate(Map<String, ?> qualifier) { - boolean result = true; - - String theClass = (String) qualifier.get("class"); - if (theClass == null) { - return false; // should maybe throw an exception? - } - if (theClass.equalsIgnoreCase("EOKeyValueQualifier") - || theClass.equalsIgnoreCase("EOKeyComparisonQualifier")) { - result = false; - } - - return result; - } - - /** - * expressionTypeForQualifier looks at a qualifier containing the - * EOModeler FetchSpecification and returns the equivalent Cayenne - * Expression type for its selector. - * - * @param qualifierMap - * - a Map containing the qualifier settings to examine. - * @return int Expression type - */ - static int expressionTypeForQualifier(Map<String, ?> qualifierMap) { - // get selector - String selector = (String) qualifierMap.get("selectorName"); - return expressionTypeForSelector(selector); - } - - /** - * expressionTypeForSelector looks at a selector from an EOModeler - * FetchSpecification and returns the equivalent Cayenne Expression - * type. - * - * @param selector - * - a String containing the selector name. - * @return int Expression type - */ - static int expressionTypeForSelector(String selector) { - Integer expType = selectorToExpressionBridge().get(selector); - return (expType != null ? expType : -1); - } - - /** - * aggregateExpressionClassForQualifier looks at a qualifer and returns - * the aggregate type: one of Expression.AND, Expression.OR, or - * Expression.NOT - * - * @param qualifierMap - * - containing the qualifier to examine - * @return int aggregate Expression type - */ - static int aggregateExpressionClassForQualifier(Map<String, ?> qualifierMap) { - String qualifierClass = (String) qualifierMap.get("class"); - if (qualifierClass != null) { - if (qualifierClass.equalsIgnoreCase("EOAndQualifier")) { - return Expression.AND; - } else if (qualifierClass.equalsIgnoreCase("EOOrQualifier")) { - return Expression.OR; - } else if (qualifierClass.equalsIgnoreCase("EONotQualifier")) { - return Expression.NOT; - } - } - - return -1; // error - } - - /** - * makeQualifier recursively builds an Expression for each condition in - * the qualifierMap and assembles from them the complex Expression to - * represent the entire EOFetchSpecification. - * - * @param qualifierMap - * - Map representation of EOFetchSpecification - * @return Expression translation of the EOFetchSpecification - */ - static Expression makeQualifier(EOObjEntity entity, Map<String, ?> qualifierMap) { - if (isAggregate(qualifierMap)) { - // the fetch specification has more than one qualifier - int aggregateClass = aggregateExpressionClassForQualifier(qualifierMap); // AND, - // OR, - // NOT - - if (aggregateClass == Expression.NOT) { - // NOT qualifiers only have one child, keyed with - // "qualifier" - Map<String, ?> child = (Map<String, ?>) qualifierMap.get("qualifier"); - // build the child expression - Expression childExp = makeQualifier(entity, child); - - return childExp.notExp(); // add the "not" clause and return - // the - // result - } else { - // AND, OR qualifiers can have multiple children, keyed with - // "qualifiers" - // get the list of children - List<Map<String, ?>> children = (List<Map<String, ?>>) qualifierMap.get("qualifiers"); - if (children != null) { - ArrayList<Expression> childExpressions = new ArrayList<>(); - // build an Expression for each child - for (Map<String, ?> child : children) { - Expression childExp = makeQualifier(entity, child); - childExpressions.add(childExp); - } - // join the child expressions and return the result - return ExpressionFactory.joinExp(aggregateClass, childExpressions); - } - } - - } // end if isAggregate(qualifierMap)... - - // the query has a single qualifier - // get expression selector type - String qualifierClass = (String) qualifierMap.get("class"); - - // the key or key path we're comparing - String key = null; - // the key, keyPath, value, or parameterized value against which - // we're - // comparing the key - Object comparisonValue = null; - - if ("EOKeyComparisonQualifier".equals(qualifierClass)) { - // Comparing two keys or key paths - key = (String) qualifierMap.get("leftValue"); - comparisonValue = qualifierMap.get("rightValue"); - // FIXME: I think EOKeyComparisonQualifier style Expressions are not supported... - return null; - } else if ("EOKeyValueQualifier".equals(qualifierClass)) { - // Comparing key with a value or parameterized value - key = (String) qualifierMap.get("key"); - Object value = qualifierMap.get("value"); - - if (value instanceof Map) { - Map<String, String> valueMap = (Map<String, String>) value; - String objClass = valueMap.get("class"); // can be a - // qualifier class or java type - if ("EOQualifierVariable".equals(objClass) && valueMap.containsKey("_key")) { - // make a parameterized expression - String paramName = valueMap.get("_key"); - comparisonValue = new ExpressionParameter(paramName); - } else { - Object queryVal = valueMap.get("value"); - if ("NSNumber".equals(objClass)) { - // comparison to NSNumber -- cast - comparisonValue = queryVal; - } else if ("EONull".equals(objClass)) { - // comparison to null - comparisonValue = null; - } else { // Could there be other types? boolean, date, - // etc.??? - // no cast - comparisonValue = queryVal; - } - } - - } else if (value instanceof String) { - // value expression - comparisonValue = value; - } // end if (value instanceof Map) else... - } - - // check whether the key is an object path; if at least one - // component is not, - // switch to db path.. - - Expression keyExp = ExpressionFactory.exp(key); - try { - entity.lastPathComponent(keyExp, Collections.emptyMap()); - } catch (ExpressionException e) { - try { - keyExp = entity.translateToDbPath(keyExp); - } catch (Exception dbpathEx) { - LOGGER.warn("Couldn't find {} in {} in EOModel", keyExp, entity.getName()); - } - } - - try { - Expression exp = ExpressionFactory.expressionOfType(expressionTypeForQualifier(qualifierMap)); - - exp.setOperand(0, keyExp); - exp.setOperand(1, comparisonValue); - return exp; - } catch (ExpressionException e) { - LOGGER.warn(e.getUnlabeledMessage()); - return null; - } - } - } -} diff --git a/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOQueryTest.java b/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOFetchSpecificationParserTest.java similarity index 55% rename from cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOQueryTest.java rename to cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOFetchSpecificationParserTest.java index ab423976c..8761dd4cc 100644 --- a/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOQueryTest.java +++ b/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOFetchSpecificationParserTest.java @@ -20,48 +20,55 @@ package org.apache.cayenne.wocompat; import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.QueryDescriptor; +import org.apache.cayenne.map.SelectQueryDescriptor; +import org.apache.cayenne.query.ObjectSelect; +import org.apache.cayenne.query.Ordering; import org.apache.cayenne.query.PrefetchTreeNode; import org.junit.jupiter.api.Test; import java.net.URL; import java.util.Collection; -import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; -public class EOQueryTest { +public class EOFetchSpecificationParserTest { @Test - public void constructor() throws Exception { + public void loadedFetchSpecification() throws Exception { URL url = getClass().getClassLoader().getResource("wotests/fetchspec.eomodeld/"); assertNotNull(url); - EOModelProcessor processor = new EOModelProcessor(); - DataMap map = processor.loadEOModel(url); + DataMap map = new EOModelProcessor().loadEOModel(url); - @SuppressWarnings("unchecked") - Map<String, ?> fspecMap = (Map<String, ?>) PropertyListSerialization.propertyListFromStream(getClass() - .getClassLoader() - .getResourceAsStream("wotests/fetchspec.eomodeld/Entity1.fspec")); - assertNotNull(fspecMap); - assertNotNull(fspecMap.get("E1FS1")); + QueryDescriptor descriptor = map.getQueryDescriptor("Entity1_E1FS1"); + assertNotNull(descriptor); + SelectQueryDescriptor selectDescriptor = assertInstanceOf(SelectQueryDescriptor.class, descriptor); + assertSame(map.getObjEntity("Entity1"), selectDescriptor.getRoot()); - @SuppressWarnings("unchecked") - EOQuery query = new EOQuery(map.getObjEntity("Entity1"), (Map<String, ?>) fspecMap.get("E1FS1")); - - assertNotNull(query.getWhere()); + assertNotNull(selectDescriptor.getQualifier()); assertEquals( "(name = \"aa\") and (db:ID >= 7) and ((e2.name = \"bb\") or (db:e2.ID != 5))", - query.getWhere().toString()); + selectDescriptor.getQualifier().toString()); + + ObjectSelect<?> query = selectDescriptor.buildQuery(); assertNotNull(query.getPrefetches()); + Collection<PrefetchTreeNode> prefetches = query.getPrefetches().getChildren(); + assertEquals(1, prefetches.size()); + assertEquals("e2", prefetches.iterator().next().getName()); - Collection children = query.getPrefetches().getChildren(); - assertEquals(1, children.size()); - assertEquals("e2", ((PrefetchTreeNode) children.iterator().next()).getName()); + Collection<Ordering> orderings = query.getOrderings(); + assertEquals(1, orderings.size()); + Ordering ordering = orderings.iterator().next(); + assertEquals("name", ordering.getSortSpecString()); + assertFalse(ordering.isAscending()); assertTrue(query.isFetchingDataRows()); assertEquals(500, query.getLimit());
