001: /**********************************************************************
002: Copyright (c) 2002 Mike Martin (TJDO) 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:
016: Contributors:
017: 2003 Erik Bengtson - fixed bug [833915] QueryResult passed as parameter
018: for another Query
019: 2003 Andy Jefferson - coding standards
020: 2005 Andy Jefferson - added embedded PC handling, and checks for non-existent fields
021: 2005 Andy Jefferson - added nested embedded field capability
022: ...
023: **********************************************************************/package org.jpox.store.expression;
024:
025: import java.util.HashMap;
026: import java.util.Map;
027:
028: import org.jpox.exceptions.JPOXUserException;
029: import org.jpox.store.DatastoreClass;
030: import org.jpox.store.DatastoreCollection;
031: import org.jpox.store.DatastoreContainerObject;
032: import org.jpox.store.DatastoreElementContainer;
033: import org.jpox.store.DatastoreField;
034: import org.jpox.store.DatastoreIdentifier;
035: import org.jpox.store.DatastoreMap;
036: import org.jpox.store.DatastoreObject;
037: import org.jpox.store.exceptions.NoSuchPersistentFieldException;
038: import org.jpox.store.mapping.EmbeddedElementPCMapping;
039: import org.jpox.store.mapping.EmbeddedKeyPCMapping;
040: import org.jpox.store.mapping.EmbeddedMapping;
041: import org.jpox.store.mapping.EmbeddedPCMapping;
042: import org.jpox.store.mapping.EmbeddedValuePCMapping;
043: import org.jpox.store.mapping.JavaTypeMapping;
044: import org.jpox.util.Localiser;
045:
046: /**
047: * Represents a Set. A collection of objects. For in-depth details read about the mathematical "set" theory.
048: *
049: * Pragmatically, it represents a SQL table expression as might be listed
050: * in the FROM clause of a SELECT statement.
051: * A table expression is a fragment of a larger containing QueryStatement.
052: * <p>
053: * A table expression has a base "main" table.
054: * If that table serves as backing for a Java class, and that class has persistence-capable superclasses,
055: * then the table expression may include joins to superclass tables, or may cause such joins to occur in its
056: * surrounding QueryStatement.
057: *
058: * @see QueryExpression
059: */
060: public abstract class LogicSetExpression {
061: /** Localiser for messages */
062: protected static final Localiser LOCALISER = Localiser
063: .getInstance("org.jpox.store.Localisation");
064:
065: /** Query Expression that this is part of. */
066: protected final QueryExpression qs;
067:
068: /** The datastore table underlying this expression. */
069: protected final DatastoreContainerObject mainTable;
070:
071: /** Alias for this table. */
072: protected final DatastoreIdentifier mainAlias;
073:
074: /** The SQL text. */
075: protected String sqlText = null;
076:
077: /** Collection of field expressions already created for this table expression. Used by nested embedded queries. */
078: protected Map embeddedFieldMappings = null;
079:
080: /**
081: * Constructor.
082: * @param qs Query Expression
083: * @param mainTable The main table for this query
084: * @param alias Table alias
085: */
086: protected LogicSetExpression(QueryExpression qs,
087: DatastoreContainerObject mainTable,
088: DatastoreIdentifier alias) {
089: this .qs = qs;
090: this .mainTable = mainTable;
091: this .mainAlias = alias;
092: }
093:
094: protected void assertNotFrozen() {
095: if (sqlText != null) {
096: // throw new JPOXUserException("A table expression cannot be modified after being output");
097: }
098: }
099:
100: /**
101: * Accessor for the main table for this expression.
102: * @return The main table
103: */
104: public final DatastoreObject getMainTable() {
105: return mainTable;
106: }
107:
108: /**
109: * Accessor for the alias for this table expression.
110: * @return The alias
111: */
112: public final DatastoreIdentifier getAlias() {
113: return mainAlias;
114: }
115:
116: /**
117: * Accessor for a field expression on this table where the field is actually present in this table.
118: * @param fieldName Name of the field
119: * @return The field expression
120: */
121: public ScalarExpression newFieldExpression(String fieldName) {
122: if (mainTable instanceof DatastoreClass) {
123: // Field of class in its primary/secondary table
124: DatastoreClass ct = (DatastoreClass) mainTable;
125: JavaTypeMapping m = null;
126: if (fieldName.equals(qs.getCandidateAlias())) {
127: // Candidate table so return id mapping
128: m = ct.getIDMapping();
129: return m.newScalarExpression(qs, this );
130: } else {
131: if (fieldName.indexOf(".") > 0) {
132: String baseField = fieldName.substring(0, fieldName
133: .indexOf("."));
134: try {
135: m = ct.getFieldMapping(baseField);
136: } catch (NoSuchPersistentFieldException npfe) {
137: // Check if this field is a previously utilised embedded field
138: if (embeddedFieldMappings != null) {
139: m = (JavaTypeMapping) embeddedFieldMappings
140: .get(baseField);
141: }
142: if (m == null) {
143: // Field is not valid for this class, and we have no known embedded mapping for it so its a user error
144: throw npfe;
145: }
146: }
147: if (m == null) {
148: throw new JPOXUserException(LOCALISER.msg(
149: "037001", fieldName, ct.toString()));
150: }
151:
152: if (m instanceof EmbeddedPCMapping) {
153: // Embedded PC field
154: String subField = fieldName.substring(fieldName
155: .indexOf(".") + 1);
156: m = getMappingForEmbeddedField(
157: (EmbeddedPCMapping) m, subField);
158: if (m == null) {
159: throw new JPOXUserException(LOCALISER.msg(
160: "037002", fieldName, subField,
161: baseField));
162: }
163: // Save this embedded mapping in case the user has nested subobjects within it
164: // TODO This doesnt allow for embedded "subfields" having the same name as other embedded subfields
165: // Currently we just keep on saving these against the subfield name, but maybe we only to keep the
166: // most recent since the field will be processed straight away if it's part of a JDOQL query
167: if (embeddedFieldMappings == null) {
168: embeddedFieldMappings = new HashMap();
169: }
170: embeddedFieldMappings.put(subField, m);
171: }
172:
173: ScalarExpression expr = m.newScalarExpression(qs,
174: this );
175: if (expr instanceof ObjectExpression) {
176: ((ObjectExpression) expr).setFieldDefinition(m
177: .getFieldMetaData().getName(), m
178: .getFieldMetaData().getTypeName());
179: }
180: return expr;
181: } else {
182: // Field of main table
183: m = ct.getFieldMapping(fieldName);
184: if (m == null) {
185: throw new JPOXUserException(LOCALISER.msg(
186: "037001", fieldName, ct.toString()));
187: }
188:
189: ScalarExpression expr = m.newScalarExpression(qs,
190: this );
191: if (expr instanceof ObjectExpression) {
192: ((ObjectExpression) expr).setFieldDefinition(
193: fieldName, m.getType());
194: }
195: return expr;
196: }
197: }
198: } else if (mainTable instanceof DatastoreCollection
199: || mainTable instanceof DatastoreMap) {
200: // User has an embedded element/key/value and has a constraint on it
201: String fld = fieldName;
202: if (fieldName.indexOf(".") > 0) {
203: // TODO Process the base field - typically is "null". When is it not "null" ?
204: String subField = fieldName.substring(fieldName
205: .indexOf(".") + 1);
206: fld = subField;
207: }
208:
209: if (mainTable instanceof DatastoreElementContainer) {
210: // collection/array - element mapping
211: DatastoreElementContainer join = (DatastoreElementContainer) mainTable;
212: JavaTypeMapping m = join.getElementMapping();
213: if (m instanceof EmbeddedElementPCMapping) {
214: JavaTypeMapping fieldMapping = ((EmbeddedMapping) m)
215: .getJavaTypeMapping(fld);
216: if (fieldMapping != null) {
217: return fieldMapping.newScalarExpression(qs,
218: this );
219: } else {
220: throw new JPOXUserException(
221: "'"
222: + fieldName
223: + "' was not found as a field stored in the join table "
224: + mainTable);
225: }
226: }
227: } else if (mainTable instanceof DatastoreMap) {
228: // Check for a key field first
229: DatastoreMap join = (DatastoreMap) mainTable;
230: JavaTypeMapping m = join.getKeyMapping();
231: if (m instanceof EmbeddedKeyPCMapping) {
232: JavaTypeMapping fieldMapping = ((EmbeddedMapping) m)
233: .getJavaTypeMapping(fld);
234: if (fieldMapping != null) {
235: return fieldMapping.newScalarExpression(qs,
236: this );
237: }
238: }
239:
240: // Check for a value field next
241: m = join.getValueMapping();
242: if (m instanceof EmbeddedValuePCMapping) {
243: JavaTypeMapping fieldMapping = ((EmbeddedMapping) m)
244: .getJavaTypeMapping(fld);
245: if (fieldMapping != null) {
246: return fieldMapping.newScalarExpression(qs,
247: this );
248: }
249: }
250: }
251:
252: throw new JPOXUserException(
253: "'"
254: + fieldName
255: + "' was not found as an embedded element/key/value field stored in the join table "
256: + mainTable);
257: } else {
258: throw new JPOXUserException(
259: "'"
260: + fieldName
261: + "' can't be referenced in "
262: + mainTable.toString()
263: + ": table does not store a persistence-capable class or a join table storing a persistence-capable class");
264: }
265: }
266:
267: /**
268: * Convenience method to find the JavaTypeMapping for an embedded field.
269: * @param m The embedded field mapping
270: * @param fieldName The field name to find
271: * @return The JavaTypeMapping for the (embedded) field
272: */
273: private JavaTypeMapping getMappingForEmbeddedField(
274: EmbeddedPCMapping m, String fieldName) {
275: if (m == null || fieldName == null) {
276: return null;
277: }
278:
279: if (fieldName.indexOf(".") < 0) {
280: return m.getJavaTypeMapping(fieldName);
281: }
282:
283: String field = fieldName.substring(0, fieldName.indexOf("."));
284: String subField = fieldName
285: .substring(fieldName.indexOf(".") + 1);
286: JavaTypeMapping mapping = m.getJavaTypeMapping(field);
287: if (mapping instanceof EmbeddedPCMapping && subField != null) {
288: return getMappingForEmbeddedField(
289: (EmbeddedPCMapping) mapping, subField);
290: }
291: return mapping;
292: }
293:
294: /**
295: * Return an identifier/reference to the datastore field/column.
296: * @param col the column to have a reference
297: * @return identifier or fully qualified identifier
298: */
299: public abstract String referenceColumn(DatastoreField col);
300:
301: public abstract String toString();
302:
303: public int hashCode() {
304: return mainTable.hashCode() ^ mainAlias.hashCode();
305: }
306:
307: public boolean equals(Object obj) {
308: if (obj == this ) {
309: return true;
310: }
311: if (obj == null) {
312: return false;
313: }
314: if (!(obj instanceof LogicSetExpression)) {
315: return false;
316: }
317: LogicSetExpression expr = (LogicSetExpression) obj;
318: return this.mainAlias.equals(expr.mainAlias)
319: && this.mainTable.equals(expr.mainTable);
320: }
321: }
|