001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.jdbc.metadata;
023:
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Collection;
027:
028: import org.jboss.deployment.DeploymentException;
029: import org.jboss.metadata.MetaData;
030: import org.w3c.dom.Element;
031:
032: /**
033: * Imutable class which holds a map between Java Classes and JDBCMappingMetaData.
034: *
035: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
036: * @author <a href="sebastien.alborini@m4x.org">Sebastien Alborini</a>
037: * @author <a href="mailto:loubyansky@ua.fm">Alex Loubyansky</a>
038: * @version $Revision: 63343 $
039: */
040: public final class JDBCTypeMappingMetaData {
041: private static final String[] PRIMITIVES = { "boolean", "byte",
042: "char", "short", "int", "long", "float", "double" };
043:
044: private static final String[] PRIMITIVE_CLASSES = {
045: "java.lang.Boolean", "java.lang.Byte",
046: "java.lang.Character", "java.lang.Short",
047: "java.lang.Integer", "java.lang.Long", "java.lang.Float",
048: "java.lang.Double" };
049:
050: public static final String CONCAT = "concat";
051: public static final String SUBSTRING = "substring";
052: public static final String LCASE = "lcase";
053: public static final String UCASE = "ucase";
054: public static final String LENGTH = "length";
055: public static final String LOCATE = "locate";
056: public static final String ABS = "abs";
057: public static final String SQRT = "sqrt";
058: public static final String COUNT = "count";
059: public static final String MOD = "mod";
060:
061: public static JDBCFunctionMappingMetaData COUNT_FUNC;
062: public static JDBCFunctionMappingMetaData MAX_FUNC;
063: public static JDBCFunctionMappingMetaData MIN_FUNC;
064: public static JDBCFunctionMappingMetaData AVG_FUNC;
065: public static JDBCFunctionMappingMetaData SUM_FUNC;
066:
067: static {
068: try {
069: COUNT_FUNC = new JDBCFunctionMappingMetaData("count",
070: "count(?1 ?2)");
071: MAX_FUNC = new JDBCFunctionMappingMetaData("max",
072: "max(?1 ?2)");
073: MIN_FUNC = new JDBCFunctionMappingMetaData("min",
074: "min(?1 ?2)");
075: AVG_FUNC = new JDBCFunctionMappingMetaData("avg",
076: "avg(?1 ?2)");
077: SUM_FUNC = new JDBCFunctionMappingMetaData("sum",
078: "sum(?1 ?2)");
079: } catch (DeploymentException e) {
080: throw new IllegalStateException(e.getMessage());
081: }
082: }
083:
084: private final String name;
085:
086: private final HashMap mappings = new HashMap();
087:
088: private final HashMap functionMappings = new HashMap();
089:
090: private final String aliasHeaderPrefix;
091: private final String aliasHeaderSuffix;
092: private final int aliasMaxLength;
093:
094: private final boolean subquerySupported;
095:
096: private final String trueMapping;
097: private final String falseMapping;
098: private final int maxKeysInDelete;
099:
100: private JDBCFunctionMappingMetaData rowLocking = null;
101: private JDBCFunctionMappingMetaData fkConstraint = null;
102: private JDBCFunctionMappingMetaData pkConstraint = null;
103: private JDBCFunctionMappingMetaData autoIncrement = null;
104: private JDBCFunctionMappingMetaData addColumn = null;
105: private JDBCFunctionMappingMetaData dropColumn = null;
106: private JDBCFunctionMappingMetaData alterColumn = null;
107:
108: /**
109: * Constructs a mapping with the data contained in the type-mapping xml
110: * element from a jbosscmp-jdbc xml file.
111: *
112: * @param element the xml Element which contains the metadata about
113: * this type mapping
114: * @throws DeploymentException if the xml element is not semantically correct
115: */
116: public JDBCTypeMappingMetaData(Element element)
117: throws DeploymentException {
118: // get the name of this type-mapping
119: name = MetaData.getUniqueChildContent(element, "name");
120:
121: // row-locking (i.e., select for update)
122: String rowLockingSQL = MetaData.getUniqueChildContent(element,
123: "row-locking-template");
124: if (rowLockingSQL != null && !rowLockingSQL.trim().equals("")) {
125: rowLocking = new JDBCFunctionMappingMetaData("row-locking",
126: rowLockingSQL);
127: }
128:
129: // pk constraint
130: String pkConstraintSQL = MetaData.getUniqueChildContent(
131: element, "pk-constraint-template");
132: if (pkConstraintSQL != null
133: && !pkConstraintSQL.trim().equals("")) {
134: pkConstraint = new JDBCFunctionMappingMetaData(
135: "pk-constraint", pkConstraintSQL);
136: }
137:
138: // fk constraint
139: String fkConstraintSQL = MetaData.getUniqueChildContent(
140: element, "fk-constraint-template");
141: if (fkConstraintSQL != null
142: && !fkConstraintSQL.trim().equals("")) {
143: fkConstraint = new JDBCFunctionMappingMetaData(
144: "fk-constraint", fkConstraintSQL);
145: }
146:
147: // alter table templates
148: String alterColumnSQL = MetaData.getOptionalChildContent(
149: element, "add-column-template");
150: if (alterColumnSQL != null && !alterColumnSQL.trim().equals("")) {
151: addColumn = new JDBCFunctionMappingMetaData(
152: "add-column-template", alterColumnSQL);
153: } else {
154: addColumn = new JDBCFunctionMappingMetaData(
155: "add-column-template", "ALTER TABLE ?1 ADD ?2 ?3");
156: }
157: alterColumnSQL = MetaData.getOptionalChildContent(element,
158: "alter-column-template");
159: if (alterColumnSQL != null && !alterColumnSQL.trim().equals("")) {
160: alterColumn = new JDBCFunctionMappingMetaData(
161: "alter-column-template", alterColumnSQL);
162: } else {
163: alterColumn = new JDBCFunctionMappingMetaData(
164: "alter-column-template",
165: "ALTER TABLE ?1 ALTER ?2 TYPE ?3");
166: }
167: alterColumnSQL = MetaData.getOptionalChildContent(element,
168: "drop-column-template");
169: if (alterColumnSQL != null && !alterColumnSQL.trim().equals("")) {
170: dropColumn = new JDBCFunctionMappingMetaData(
171: "drop-column-template", alterColumnSQL);
172: } else {
173: dropColumn = new JDBCFunctionMappingMetaData(
174: "drop-column-template", "ALTER TABLE ?1 DROP ?2");
175: }
176:
177: // auto increment
178: // WARN: it's optional
179: String autoIncrementSQL = MetaData.getOptionalChildContent(
180: element, "auto-increment-template");
181: if (autoIncrementSQL != null
182: && !autoIncrementSQL.trim().equals("")) {
183: autoIncrement = new JDBCFunctionMappingMetaData(
184: "auto-increment", autoIncrementSQL);
185: }
186:
187: // get the mappings
188: Iterator iterator = MetaData.getChildrenByTagName(element,
189: "mapping");
190: while (iterator.hasNext()) {
191: Element mappingElement = (Element) iterator.next();
192: JDBCMappingMetaData mapping = new JDBCMappingMetaData(
193: mappingElement);
194: mappings.put(mapping.getJavaType(), mapping);
195: }
196:
197: addDefaultFunctionMapping();
198:
199: // get the mappings
200: Iterator functions = MetaData.getChildrenByTagName(element,
201: "function-mapping");
202: while (functions.hasNext()) {
203: Element mappingElement = (Element) functions.next();
204: JDBCFunctionMappingMetaData functionMapping = new JDBCFunctionMappingMetaData(
205: mappingElement);
206: functionMappings.put(functionMapping.getFunctionName()
207: .toLowerCase(), functionMapping);
208: }
209:
210: aliasHeaderPrefix = MetaData.getUniqueChildContent(element,
211: "alias-header-prefix");
212:
213: aliasHeaderSuffix = MetaData.getUniqueChildContent(element,
214: "alias-header-suffix");
215:
216: String aliasMaxLengthString = MetaData.getUniqueChildContent(
217: element, "alias-max-length");
218: try {
219: aliasMaxLength = Integer.parseInt(aliasMaxLengthString);
220: } catch (NumberFormatException e) {
221: throw new DeploymentException("Invalid number format in "
222: + "alias-max-length " + aliasMaxLengthString
223: + "': " + e);
224: }
225:
226: String subquerySupportedStr = MetaData.getUniqueChildContent(
227: element, "subquery-supported");
228: subquerySupported = Boolean.valueOf(subquerySupportedStr)
229: .booleanValue();
230: trueMapping = MetaData.getUniqueChildContent(element,
231: "true-mapping");
232: falseMapping = MetaData.getUniqueChildContent(element,
233: "false-mapping");
234:
235: String str = MetaData.getOptionalChildContent(element,
236: "max-keys-in-delete");
237: if (str != null) {
238: try {
239: maxKeysInDelete = Integer.parseInt(str);
240: } catch (NumberFormatException e) {
241: throw new DeploymentException(
242: "Failed to parse int value '" + str
243: + "' for max-keys-in-delete", e);
244: }
245:
246: if (maxKeysInDelete < 0) {
247: throw new DeploymentException(
248: "The value of max-keys-in-delete cannot be less than 0: "
249: + maxKeysInDelete);
250: }
251: } else {
252: maxKeysInDelete = 0;
253: }
254: }
255:
256: /**
257: * Gets the name of this mapping. The mapping name used to differentiate this
258: * mapping from other mappings and the mapping the application used is
259: * retrieved by name.
260: * @return the name of this mapping.
261: */
262: public String getName() {
263: return name;
264: }
265:
266: /**
267: * Gets the prefix for that is used when generating an alias header. An
268: * alias header is prepended to a generated table alias to prevent name
269: * collisions. An alias header is constructed as folows:
270: * aliasHeaderPrefix + int_counter + aliasHeaderSuffix
271: *
272: * @return the prefix for alias headers
273: */
274: public String getAliasHeaderPrefix() {
275: return aliasHeaderPrefix;
276: }
277:
278: /**
279: * Gets the suffix for that is used when generating an alias header. An
280: * alias header is prepended to a generated table alias to prevent name
281: * collisions. An alias header is constructed as folows:
282: * aliasHeaderPrefix + int_counter + aliasHeaderSuffix
283: *
284: * @return the suffix for alias headers
285: */
286: public String getAliasHeaderSuffix() {
287: return aliasHeaderSuffix;
288: }
289:
290: /**
291: * Gets maximum length of a table alias.
292: * An alias is constructed as folows: aliasHeader + ejb_ql_identifier_path
293: * @return the maximum length that a table alias can be
294: */
295: public int getAliasMaxLength() {
296: return aliasMaxLength;
297: }
298:
299: /**
300: * Does this type mapping support subqueries?
301: */
302: public boolean isSubquerySupported() {
303: return subquerySupported;
304: }
305:
306: /**
307: * Gets the value to which the boolean true value in EJB-QL will be mapped.
308: */
309: public String getTrueMapping() {
310: return trueMapping;
311: }
312:
313: /**
314: * Gets the value to which the boolean false value in EJB-QL will be mapped.
315: */
316: public String getFalseMapping() {
317: return falseMapping;
318: }
319:
320: public int getMaxKeysInDelete() {
321: return maxKeysInDelete;
322: }
323:
324: public JDBCMappingMetaData getTypeMappingMetaData(Class type) {
325: String javaType = type.getName();
326:
327: // Check primitive first
328: for (int i = 0; i < PRIMITIVES.length; i++) {
329: if (javaType.equals(PRIMITIVES[i])) {
330: // Translate into class
331: javaType = PRIMITIVE_CLASSES[i];
332: break;
333: }
334: }
335:
336: // Check other types
337: JDBCMappingMetaData mapping = (JDBCMappingMetaData) mappings
338: .get(javaType);
339:
340: // if not found, return mapping for java.lang.object
341: if (mapping == null) {
342: mapping = (JDBCMappingMetaData) mappings
343: .get("java.lang.Object");
344: }
345:
346: return mapping;
347: }
348:
349: public JDBCFunctionMappingMetaData getFunctionMapping(String name) {
350: JDBCFunctionMappingMetaData funcMapping = (JDBCFunctionMappingMetaData) functionMappings
351: .get(name.toLowerCase());
352: if (funcMapping == null)
353: throw new IllegalStateException("Function " + name
354: + " is not defined for " + this .name);
355: return funcMapping;
356: }
357:
358: /**
359: * Returns rowLocking SQL template.
360: */
361: public JDBCFunctionMappingMetaData getRowLockingTemplate() {
362: return rowLocking;
363: }
364:
365: /**
366: * Returns pk constraint SQL template.
367: */
368: public JDBCFunctionMappingMetaData getPkConstraintTemplate() {
369: return pkConstraint;
370: }
371:
372: /**
373: * Returns fk constraint SQL template.
374: */
375: public JDBCFunctionMappingMetaData getFkConstraintTemplate() {
376: return fkConstraint;
377: }
378:
379: /**
380: * Returns auto increment SQL template.
381: */
382: public JDBCFunctionMappingMetaData getAutoIncrementTemplate() {
383: return autoIncrement;
384: }
385:
386: /**
387: * Returns add column SQL template.
388: */
389: public JDBCFunctionMappingMetaData getAddColumnTemplate() {
390: return addColumn;
391: }
392:
393: /**
394: * Returns auto increment SQL template.
395: */
396: public JDBCFunctionMappingMetaData getDropColumnTemplate() {
397: return dropColumn;
398: }
399:
400: /**
401: * Returns auto increment SQL template.
402: */
403: public JDBCFunctionMappingMetaData getAlterColumnTemplate() {
404: return alterColumn;
405: }
406:
407: public Collection getMappings() {
408: return mappings.values();
409: }
410:
411: private void addDefaultFunctionMapping() throws DeploymentException {
412: JDBCFunctionMappingMetaData function;
413:
414: // concat
415: function = new JDBCFunctionMappingMetaData("concat",
416: new String[] { "{fn concat(", ", ", ")}" }, new int[] {
417: 0, 1 });
418: functionMappings.put(function.getFunctionName().toLowerCase(),
419: function);
420:
421: // substring
422: function = new JDBCFunctionMappingMetaData("substring",
423: new String[] { "{fn substring(", ", ", ", ", ")}" },
424: new int[] { 0, 1, 2 });
425: functionMappings.put(function.getFunctionName().toLowerCase(),
426: function);
427:
428: // lcase
429: function = new JDBCFunctionMappingMetaData("lcase",
430: new String[] { "{fn lcase(", ")}" }, new int[] { 0 });
431: functionMappings.put(function.getFunctionName().toLowerCase(),
432: function);
433:
434: // ucase
435: function = new JDBCFunctionMappingMetaData("ucase",
436: new String[] { "{fn ucase(", ")}" }, new int[] { 0 });
437: functionMappings.put(function.getFunctionName().toLowerCase(),
438: function);
439:
440: // length
441: function = new JDBCFunctionMappingMetaData("length",
442: new String[] { "{fn length(", ")}" }, new int[] { 0 });
443: functionMappings.put(function.getFunctionName().toLowerCase(),
444: function);
445:
446: // locate
447: function = new JDBCFunctionMappingMetaData("locate",
448: new String[] { "{fn locate(", ", ", ", ", ")}" },
449: new int[] { 0, 1, 2 });
450: functionMappings.put(function.getFunctionName().toLowerCase(),
451: function);
452:
453: // abs
454: function = new JDBCFunctionMappingMetaData("abs", new String[] {
455: "{fn abs(", ")}" }, new int[] { 0 });
456: functionMappings.put(function.getFunctionName().toLowerCase(),
457: function);
458:
459: // sqrt
460: function = new JDBCFunctionMappingMetaData("sqrt",
461: new String[] { "{fn sqrt(", ")}" }, new int[] { 0 });
462: functionMappings.put(function.getFunctionName().toLowerCase(),
463: function);
464:
465: // mod
466: function = new JDBCFunctionMappingMetaData("mod", "mod(?1, ?2)");
467: functionMappings.put(function.getFunctionName().toLowerCase(),
468: function);
469: }
470: }
|