001: /**********************************************************************
002: Copyright (c) 2007 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: ...
017: **********************************************************************/package org.jpox.store.query;
018:
019: import java.util.Collection;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import org.jpox.ObjectManager;
024: import org.jpox.exceptions.JPOXUserException;
025:
026: /**
027: * Abstract representation of a JDOQL query used by JPOX.
028: * The query can be specified via method calls, or via a single-string form.
029: *
030: * @see Query
031: * @version $Revision: 1.5 $
032: */
033: public abstract class AbstractJDOQLQuery extends AbstractJavaQuery {
034: /** Keywords used in single-string JDOQL. Uppercase variants specified here, but we allow the lowercase form. */
035: public static final String[] SINGLE_STRING_KEYWORDS = { "SELECT",
036: "UNIQUE", "INTO", "FROM", "EXCLUDE", "SUBCLASSES", "WHERE",
037: "VARIABLES", "PARAMETERS", "GROUP", "ORDER", "BY", "RANGE" };
038: /** Keywords in lowercase (we avoid calling toLowerCase() multiple times, which is expensive operation) **/
039: public static final String[] SINGLE_STRING_KEYWORDS_LOWERCASE = {
040: "select", "unique", "into", "from", "exclude",
041: "subclasses", "where", "variables", "parameters", "group",
042: "order", "by", "range" };
043:
044: /**
045: * Constructor.
046: * @param om ObjectManager
047: */
048: public AbstractJDOQLQuery(ObjectManager om) {
049: super (om);
050: }
051:
052: /**
053: * Set the grouping specification for the result Collection.
054: * @param grouping the grouping specification.
055: */
056: public void setGrouping(String grouping) {
057: discardCompiled();
058: assertIsModifiable();
059:
060: //discard previous values
061: this .grouping = null;
062: setHaving(null);
063:
064: if (grouping != null && grouping.length() > 0) {
065: // The "grouping" string will be of the form "...., ...., ... HAVING ...."
066: // so we parse it into the former part as a grouping clause, and the latter part as a having clause
067: if (grouping.indexOf("HAVING") >= 0) {
068: setHaving(grouping
069: .substring(grouping.indexOf("HAVING") + 7));
070: this .grouping = grouping.substring(0, grouping
071: .indexOf("HAVING") - 1);
072: } else if (grouping.indexOf("having") >= 0) {
073: setHaving(grouping
074: .substring(grouping.indexOf("having") + 7));
075: this .grouping = grouping.substring(0, grouping
076: .indexOf("having") - 1);
077: } else {
078: this .grouping = grouping.trim();
079: }
080: }
081: }
082:
083: /**
084: * Method to take the defined parameters for the query and form a single string.
085: * This is used to print out the query for logging.
086: * @return The single string
087: */
088: public String getSingleStringQuery() {
089: if (singleString != null) {
090: return singleString;
091: }
092:
093: StringBuffer str = new StringBuffer("SELECT ");
094: if (unique) {
095: str.append("UNIQUE ");
096: }
097: if (result != null) {
098: str.append(result + " ");
099: }
100: if (resultClassName != null) {
101: str.append("INTO " + resultClassName + " ");
102: }
103: if (from != null) {
104: // Subquery is of the form "<candidate-expression> alias"
105: str.append("FROM " + from + " ");
106: } else if (candidateClassName != null) {
107: // Query is of the form "<candidate-class-name> [EXCLUDE-SUBCLASSES]"
108: str.append("FROM " + candidateClassName + " ");
109: if (!subclasses) {
110: str.append("EXCLUDE SUBCLASSES ");
111: }
112: }
113: if (filter != null) {
114: str.append("WHERE " + filter + " ");
115: }
116: if (explicitVariables != null) {
117: str.append("VARIABLES " + explicitVariables + " ");
118: }
119: if (explicitParameters != null) {
120: str.append("PARAMETERS " + explicitParameters + " ");
121: }
122: if (imports != null) {
123: str.append(imports + " ");
124: }
125: if (grouping != null) {
126: str.append("GROUP BY " + grouping + " ");
127: }
128: if (having != null) {
129: str.append("HAVING " + having + " ");
130: }
131: if (ordering != null) {
132: str.append("ORDER BY " + ordering + " ");
133: }
134:
135: if (range != null) {
136: str.append("RANGE " + range);
137: } else if (fromInclNo > 0 || toExclNo != Long.MAX_VALUE) {
138: str.append("RANGE " + fromInclNo + "," + toExclNo);
139: }
140:
141: singleString = str.toString().trim();
142: return singleString;
143: }
144:
145: /**
146: * Execute the query to delete persistent objects.
147: * @param parameters the Map containing all of the parameters.
148: * @return the number of deleted objects.
149: */
150: protected long performDeletePersistentAll(Map parameters) {
151: // Save the required value for unique (find the instances without it)
152: boolean requiresUnique = unique;
153: if (unique) {
154: unique = false;
155: }
156:
157: Collection results = (Collection) performExecute(parameters);
158: int number = 0;
159: if (results == null) {
160: number = 0;
161: } else {
162: // TODO : To make this most efficient we shouldn't instantiate things into memory
163: // but instead check if the object to be deleted implements DeleteCallback, or there are
164: // any lifecycle listeners for this object type, or if there are any dependent fields
165: // and only then do we instantiate it.
166: number = results.size();
167: if (requiresUnique && number > 1) {
168: throw new JPOXUserException(LOCALISER.msg("021032"));
169: }
170:
171: // Instances to be deleted are flushed first (JDO2 [14.8-4])
172: Iterator resultsIter = results.iterator();
173: while (resultsIter.hasNext()) {
174: om.findStateManager(resultsIter.next()).flush();
175: }
176:
177: // Perform the deletion
178: om.deleteObjects(results);
179:
180: if (results instanceof QueryResult) {
181: // TODO Do we need to remove these from the PM.queryRun store and this.queryResults ?
182: ((QueryResult) results).close();
183: } else {
184: // TODO Clean up for other datastore result types?
185: }
186: }
187: return number;
188: }
189:
190: /**
191: * Convenience method returning if the supplied name is a keyword for this query language.
192: * @param name Name to check
193: * @return Whether it is a keyword
194: */
195: public static boolean isKeyword(String name) {
196: for (int i = 0; i < SINGLE_STRING_KEYWORDS.length; i++) {
197: // JDOQL is case-sensitive - lowercase or UPPERCASE only
198: if (name.equals(SINGLE_STRING_KEYWORDS[i])
199: || name.equals(SINGLE_STRING_KEYWORDS_LOWERCASE[i])) {
200: return true;
201: }
202: }
203: if (name.equals("IMPORT") || name.equals("import")) {
204: return true;
205: }
206: return false;
207: }
208: }
|