001: package xdoclet.modules.ojb.constraints;
002:
003: import xdoclet.modules.ojb.CommaListIterator;
004: import xdoclet.modules.ojb.model.ClassDescriptorDef;
005: import xdoclet.modules.ojb.model.CollectionDescriptorDef;
006: import xdoclet.modules.ojb.model.FieldDescriptorDef;
007: import xdoclet.modules.ojb.model.ModelDef;
008: import xdoclet.modules.ojb.model.PropertyHelper;
009:
010: /* Copyright 2004-2005 The Apache Software Foundation
011: *
012: * Licensed under the Apache License, Version 2.0 (the "License");
013: * you may not use this file except in compliance with the License.
014: * You may obtain a copy of the License at
015: *
016: * http://www.apache.org/licenses/LICENSE-2.0
017: *
018: * Unless required by applicable law or agreed to in writing, software
019: * distributed under the License is distributed on an "AS IS" BASIS,
020: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
021: * See the License for the specific language governing permissions and
022: * limitations under the License.
023: */
024:
025: /**
026: * Checks constraints for collection descriptors. Note that constraints may modify the collection descriptor.
027: * For checks of the relationships (e.g. foreignkey) see ModelConstraints.
028: *
029: * @author <a href="mailto:tomdz@users.sourceforge.net">Thomas Dudziak (tomdz@users.sourceforge.net)</a>
030: */
031: public class CollectionDescriptorConstraints extends
032: FeatureDescriptorConstraints {
033: /** The collection interface (this type or subtypes of it can be handled by OJB) */
034: private final static String JAVA_COLLECTION_INTERFACE = "java.util.Collection";
035: /** The interface that user-defined collection classes must implement */
036: private final static String MANAGEABLE_COLLECTION_INTERFACE = "org.apache.ojb.broker.ManageableCollection";
037: /** The interface that user-defined query customizers must implement */
038: private final static String QUERY_CUSTOMIZER_INTERFACE = "org.apache.ojb.broker.accesslayer.QueryCustomizer";
039:
040: /**
041: * Checks the given collection descriptor.
042: *
043: * @param collDef The collection descriptor
044: * @param checkLevel The amount of checks to perform
045: * @exception ConstraintException If a constraint has been violated
046: */
047: public void check(CollectionDescriptorDef collDef, String checkLevel)
048: throws ConstraintException {
049: ensureElementClassRef(collDef, checkLevel);
050: checkInheritedForeignkey(collDef, checkLevel);
051: ensureCollectionClass(collDef, checkLevel);
052: checkProxyPrefetchingLimit(collDef, checkLevel);
053: checkOrderby(collDef, checkLevel);
054: checkQueryCustomizer(collDef, checkLevel);
055: }
056:
057: /**
058: * Ensures that the given collection descriptor has a valid element-class-ref property.
059: *
060: * @param collDef The collection descriptor
061: * @param checkLevel The current check level (this constraint is checked in basic and strict)
062: * @exception ConstraintException If element-class-ref could not be determined or is invalid
063: */
064: private void ensureElementClassRef(CollectionDescriptorDef collDef,
065: String checkLevel) throws ConstraintException {
066: if (CHECKLEVEL_NONE.equals(checkLevel)) {
067: return;
068: }
069:
070: String arrayElementClassName = collDef
071: .getProperty(PropertyHelper.OJB_PROPERTY_ARRAY_ELEMENT_CLASS_REF);
072:
073: if (!collDef
074: .hasProperty(PropertyHelper.OJB_PROPERTY_ELEMENT_CLASS_REF)) {
075: if (arrayElementClassName != null) {
076: // we use the array element type
077: collDef.setProperty(
078: PropertyHelper.OJB_PROPERTY_ELEMENT_CLASS_REF,
079: arrayElementClassName);
080: } else {
081: throw new ConstraintException("Collection "
082: + collDef.getName() + " in class "
083: + collDef.getOwner().getName()
084: + " does not specify its element class");
085: }
086: }
087:
088: // now checking the element type
089: ModelDef model = (ModelDef) collDef.getOwner().getOwner();
090: String elementClassName = collDef
091: .getProperty(PropertyHelper.OJB_PROPERTY_ELEMENT_CLASS_REF);
092: ClassDescriptorDef elementClassDef = model
093: .getClass(elementClassName);
094:
095: if (elementClassDef == null) {
096: throw new ConstraintException("Collection "
097: + collDef.getName() + " in class "
098: + collDef.getOwner().getName()
099: + " references an unknown class "
100: + elementClassName);
101: }
102: if (!elementClassDef.getBooleanProperty(
103: PropertyHelper.OJB_PROPERTY_OJB_PERSISTENT, false)) {
104: throw new ConstraintException("The element class "
105: + elementClassName + " of the collection "
106: + collDef.getName() + " in class "
107: + collDef.getOwner().getName()
108: + " is not persistent");
109: }
110: if (CHECKLEVEL_STRICT.equals(checkLevel)
111: && (arrayElementClassName != null)) {
112: // specified element class must be a subtype of the element type
113: try {
114: InheritanceHelper helper = new InheritanceHelper();
115:
116: if (!helper.isSameOrSubTypeOf(elementClassDef,
117: arrayElementClassName, true)) {
118: throw new ConstraintException(
119: "The element class "
120: + elementClassName
121: + " of the collection "
122: + collDef.getName()
123: + " in class "
124: + collDef.getOwner().getName()
125: + " is not the same or a subtype of the array base type "
126: + arrayElementClassName);
127: }
128: } catch (ClassNotFoundException ex) {
129: throw new ConstraintException(
130: "Could not find the class "
131: + ex.getMessage()
132: + " on the classpath while checking the collection "
133: + collDef.getName() + " in class "
134: + collDef.getOwner().getName());
135: }
136: }
137: // we're adjusting the property to use the classloader-compatible form
138: collDef.setProperty(
139: PropertyHelper.OJB_PROPERTY_ELEMENT_CLASS_REF,
140: elementClassDef.getName());
141: }
142:
143: /**
144: * Checks that the foreignkey is not modified in an inherited/nested m:n collection descriptor.
145: *
146: * @param collDef The collection descriptor
147: * @param checkLevel The current check level (this constraint is checked in basic and strict)
148: */
149: private void checkInheritedForeignkey(
150: CollectionDescriptorDef collDef, String checkLevel)
151: throws ConstraintException {
152: if (CHECKLEVEL_NONE.equals(checkLevel)) {
153: return;
154: }
155: if (!collDef.isInherited() && !collDef.isNested()) {
156: return;
157: }
158: if (!collDef
159: .hasProperty(PropertyHelper.OJB_PROPERTY_INDIRECTION_TABLE)) {
160: return;
161: }
162:
163: String localFk = collDef
164: .getProperty(PropertyHelper.OJB_PROPERTY_FOREIGNKEY);
165: String inheritedFk = collDef.getOriginal().getProperty(
166: PropertyHelper.OJB_PROPERTY_FOREIGNKEY);
167:
168: if (!CommaListIterator.sameLists(localFk, inheritedFk)) {
169: throw new ConstraintException(
170: "The foreignkey property has been changed for the m:n collection "
171: + collDef.getName() + " in class "
172: + collDef.getOwner().getName());
173: }
174: }
175:
176: /**
177: * Ensures that the given collection descriptor has the collection-class property if necessary.
178: *
179: * @param collDef The collection descriptor
180: * @param checkLevel The current check level (this constraint is checked in basic (partly) and strict)
181: * @exception ConstraintException If collection-class is given for an array or if no collection-class is given but required
182: */
183: private void ensureCollectionClass(CollectionDescriptorDef collDef,
184: String checkLevel) throws ConstraintException {
185: if (CHECKLEVEL_NONE.equals(checkLevel)) {
186: return;
187: }
188:
189: if (collDef
190: .hasProperty(PropertyHelper.OJB_PROPERTY_ARRAY_ELEMENT_CLASS_REF)) {
191: // an array cannot have a collection-class specified
192: if (collDef
193: .hasProperty(PropertyHelper.OJB_PROPERTY_COLLECTION_CLASS)) {
194: throw new ConstraintException(
195: "Collection "
196: + collDef.getName()
197: + " in class "
198: + collDef.getOwner().getName()
199: + " is an array but does specify collection-class");
200: } else {
201: // no further processing necessary as its an array
202: return;
203: }
204: }
205:
206: if (CHECKLEVEL_STRICT.equals(checkLevel)) {
207: InheritanceHelper helper = new InheritanceHelper();
208: ModelDef model = (ModelDef) collDef.getOwner().getOwner();
209: String specifiedClass = collDef
210: .getProperty(PropertyHelper.OJB_PROPERTY_COLLECTION_CLASS);
211: String variableType = collDef
212: .getProperty(PropertyHelper.OJB_PROPERTY_VARIABLE_TYPE);
213:
214: try {
215: if (specifiedClass != null) {
216: // if we have a specified class then it has to implement the manageable collection and be a sub type of the variable type
217: if (!helper.isSameOrSubTypeOf(specifiedClass,
218: variableType)) {
219: throw new ConstraintException(
220: "The type "
221: + specifiedClass
222: + " specified as collection-class of the collection "
223: + collDef.getName()
224: + " in class "
225: + collDef.getOwner().getName()
226: + " is not a sub type of the variable type "
227: + variableType);
228: }
229: if (!helper.isSameOrSubTypeOf(specifiedClass,
230: MANAGEABLE_COLLECTION_INTERFACE)) {
231: throw new ConstraintException(
232: "The type "
233: + specifiedClass
234: + " specified as collection-class of the collection "
235: + collDef.getName()
236: + " in class "
237: + collDef.getOwner().getName()
238: + " does not implement "
239: + MANAGEABLE_COLLECTION_INTERFACE);
240: }
241: } else {
242: // no collection class specified so the variable type has to be a collection type
243: if (helper.isSameOrSubTypeOf(variableType,
244: MANAGEABLE_COLLECTION_INTERFACE)) {
245: // we can specify it as a collection-class as it is an manageable collection
246: collDef
247: .setProperty(
248: PropertyHelper.OJB_PROPERTY_COLLECTION_CLASS,
249: variableType);
250: } else if (!helper.isSameOrSubTypeOf(variableType,
251: JAVA_COLLECTION_INTERFACE)) {
252: throw new ConstraintException(
253: "The collection "
254: + collDef.getName()
255: + " in class "
256: + collDef.getOwner().getName()
257: + " needs the collection-class attribute as its variable type does not implement "
258: + JAVA_COLLECTION_INTERFACE);
259: }
260: }
261: } catch (ClassNotFoundException ex) {
262: throw new ConstraintException(
263: "Could not find the class "
264: + ex.getMessage()
265: + " on the classpath while checking the collection "
266: + collDef.getName() + " in class "
267: + collDef.getOwner().getName());
268: }
269: }
270: }
271:
272: /**
273: * Checks the orderby attribute.
274: *
275: * @param collDef The collection descriptor
276: * @param checkLevel The current check level (this constraint is checked in basic and strict)
277: * @exception ConstraintException If the value for orderby is invalid (unknown field or ordering)
278: */
279: private void checkOrderby(CollectionDescriptorDef collDef,
280: String checkLevel) throws ConstraintException {
281: if (CHECKLEVEL_NONE.equals(checkLevel)) {
282: return;
283: }
284:
285: String orderbySpec = collDef
286: .getProperty(PropertyHelper.OJB_PROPERTY_ORDERBY);
287:
288: if ((orderbySpec == null) || (orderbySpec.length() == 0)) {
289: return;
290: }
291:
292: ClassDescriptorDef ownerClass = (ClassDescriptorDef) collDef
293: .getOwner();
294: String elementClassName = collDef.getProperty(
295: PropertyHelper.OJB_PROPERTY_ELEMENT_CLASS_REF).replace(
296: '$', '.');
297: ClassDescriptorDef elementClass = ((ModelDef) ownerClass
298: .getOwner()).getClass(elementClassName);
299: FieldDescriptorDef fieldDef;
300: String token;
301: String fieldName;
302: String ordering;
303: int pos;
304:
305: for (CommaListIterator it = new CommaListIterator(orderbySpec); it
306: .hasNext();) {
307: token = it.getNext();
308: pos = token.indexOf('=');
309: if (pos == -1) {
310: fieldName = token;
311: ordering = null;
312: } else {
313: fieldName = token.substring(0, pos);
314: ordering = token.substring(pos + 1);
315: }
316: fieldDef = elementClass.getField(fieldName);
317: if (fieldDef == null) {
318: throw new ConstraintException(
319: "The field "
320: + fieldName
321: + " specified in the orderby attribute of the collection "
322: + collDef.getName()
323: + " in class "
324: + ownerClass.getName()
325: + " hasn't been found in the element class "
326: + elementClass.getName());
327: }
328: if ((ordering != null) && (ordering.length() > 0)
329: && !"ASC".equals(ordering)
330: && !"DESC".equals(ordering)) {
331: throw new ConstraintException(
332: "The ordering "
333: + ordering
334: + " specified in the orderby attribute of the collection "
335: + collDef.getName() + " in class "
336: + ownerClass.getName() + " is invalid");
337: }
338: }
339: }
340:
341: /**
342: * Checks the query-customizer setting of the given collection descriptor.
343: *
344: * @param collDef The collection descriptor
345: * @param checkLevel The current check level (this constraint is only checked in strict)
346: * @exception ConstraintException If the constraint has been violated
347: */
348: private void checkQueryCustomizer(CollectionDescriptorDef collDef,
349: String checkLevel) throws ConstraintException {
350: if (!CHECKLEVEL_STRICT.equals(checkLevel)) {
351: return;
352: }
353:
354: String queryCustomizerName = collDef
355: .getProperty(PropertyHelper.OJB_PROPERTY_QUERY_CUSTOMIZER);
356:
357: if (queryCustomizerName == null) {
358: return;
359: }
360:
361: try {
362: InheritanceHelper helper = new InheritanceHelper();
363:
364: if (!helper.isSameOrSubTypeOf(queryCustomizerName,
365: QUERY_CUSTOMIZER_INTERFACE)) {
366: throw new ConstraintException(
367: "The class "
368: + queryCustomizerName
369: + " specified as query-customizer of collection "
370: + collDef.getName() + " in class "
371: + collDef.getOwner().getName()
372: + " does not implement the interface "
373: + QUERY_CUSTOMIZER_INTERFACE);
374: }
375: } catch (ClassNotFoundException ex) {
376: throw new ConstraintException("The class "
377: + ex.getMessage()
378: + " specified as query-customizer of collection "
379: + collDef.getName() + " in class "
380: + collDef.getOwner().getName()
381: + " was not found on the classpath");
382: }
383: }
384: }
|