001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency.core;
010:
011: import com.completex.objective.util.StringUtil;
012: import com.completex.objective.util.PropertyMap;
013: import com.completex.objective.components.persistency.Mappable;
014:
015: import java.util.*;
016: import java.io.Serializable;
017:
018: /**
019: * Represents database join. Depending on the database policy it will produce either ANSI or proprietiry
020: * join SQL fragments
021: *
022: * @author Gennady Krizhevsky
023: */
024: public interface Join {
025:
026: public static final String SPC = " ";
027: public static final Type JOIN = Type.JOIN;
028: public static final Type INNER = Type.INNER;
029: public static final Type LEFT = Type.LEFT;
030: public static final Type RIGHT = Type.RIGHT;
031: public static final Type NULL_JOIN = Type.NULL_JOIN;
032:
033: /**
034: * @see TableIterator
035: *
036: * @return TableIterator
037: */
038: TableIterator iterator();
039:
040: /**
041: * Compiles join
042: *
043: * @param policy
044: * @return compiled Join (same reference)
045: */
046: Join compile(DatabasePolicy policy);
047:
048: /**
049: * Object representation of clause like
050: * ... LEFT JOIN <joinedTableName> ON (<firstTableColumns> = <firstTableColumns>)
051: *
052: * @param joinedTableName
053: * @param joinedTableAlias
054: * @param firstTableColumns
055: * @param joinedTableColumns
056: * @return this Join
057: */
058: Join addLeftJoin(String joinedTableName, String joinedTableAlias,
059: String[] firstTableColumns, String[] joinedTableColumns);
060:
061: /**
062: * Object representation of clause like
063: * ... RIGHT JOIN <joinedTableName> ON (<firstTableColumns> = <firstTableColumns>)
064: *
065: * @param joinedTableName
066: * @param joinedTableAlias
067: * @param firstTableColumns
068: * @param joinedTableColumns
069: * @return this Join
070: */
071: Join addRightJoin(String joinedTableName, String joinedTableAlias,
072: String[] firstTableColumns, String[] joinedTableColumns);
073:
074: /**
075: * Object representation of clause like
076: * ... INNER JOIN <joinedTableName> ON (<firstTableColumns> = <firstTableColumns>)
077: *
078: * @param joinedTableName
079: * @param joinedTableAlias
080: * @param firstTableColumns
081: * @param joinedTableColumns
082: * @return this Join
083: */
084: Join addInnerJoin(String joinedTableName, String joinedTableAlias,
085: String[] firstTableColumns, String[] joinedTableColumns);
086:
087: /**
088: * Add root table in join
089: *
090: * @param joinedTableName
091: * @param joinedTableAlias
092: * @return this Join
093: */
094: Join addInnerJoin(String joinedTableName, String joinedTableAlias);
095:
096: Join addJoin(Type type, String joinedTableName,
097: String joinedTableAlias, String[] firstTableColumns,
098: String[] joinedTableColumns);
099:
100: public Join addInnerJoin(String joinedTableName);
101:
102: public Join addInnerJoin(String joinedTableName,
103: String[] firstTableColumns, String[] joinedTableColumns);
104:
105: public Join addLeftJoin(String joinedTableName,
106: String[] firstTableColumns, String[] joinedTableColumns);
107:
108: public Join addRightJoin(String joinedTableName,
109: String[] firstTableColumns, String[] joinedTableColumns);
110:
111: public Join addJoin(Type type, String joinedTableName,
112: String[] firstTableColumns, String[] joinedTableColumns);
113:
114: /**
115: * This method will produce join of all tables excluding the very 1st one.
116: * It assumes that only the 2nd table is connected to the 1st one. Otherwise
117: * it will produce result that does not make sense
118: * @return join of all tables excluding the 1st one
119: */
120: Join joinMinusOne();
121:
122: /**
123: *
124: * @return from
125: */
126: String getFrom();
127:
128: /**
129: * Returns different result depending on whether ANSI or proprietory joing is used
130: *
131: * @return where
132: */
133: String getWhere();
134:
135: /**
136: * Returns different result depending on whether ANSI or proprietory joing is used
137: *
138: * @return true if Join is compiled
139: */
140: boolean isCompiled();
141:
142: /**
143: * @see Object#clone()
144: */
145: Object clone() throws CloneNotSupportedException;
146:
147: Table getLastAddedTable();
148:
149: Table getFirstAddedTable();
150:
151: void addTable(Table table);
152:
153: Join addInnerJoin(String joinedTableName, String firstTableName,
154: String joinedTableAlias, String[] firstTableColumns,
155: String[] joinedTableColumns);
156:
157: Join addLeftJoin(String joinedTableName, String firstTableName,
158: String joinedTableAlias, String[] firstTableColumns,
159: String[] joinedTableColumns);
160:
161: Join addRightJoin(String joinedTableName, String firstTableName,
162: String joinedTableAlias, String[] firstTableColumns,
163: String[] joinedTableColumns);
164:
165: Join addJoin(Type type, String firstTableName,
166: String joinedTableName, String joinedTableAlias,
167: String[] firstTableColumns, String[] joinedTableColumns);
168:
169: int size();
170:
171: /**
172: * Join type class
173: */
174: public static class Type implements Serializable {
175: public static final Type NULL_JOIN = new Type("NULL_JOIN");
176: public static final Type JOIN = new Type("JOIN");
177: public static final Type INNER = new Type("INNER");
178: public static final Type LEFT = new Type("LEFT");
179: public static final Type RIGHT = new Type("RIGHT");
180:
181: private static final HashMap TYPES = new HashMap();
182:
183: static {
184: TYPES.put(NULL_JOIN.getName(), NULL_JOIN);
185: TYPES.put(JOIN.getName(), JOIN);
186: TYPES.put(INNER.getName(), INNER);
187: TYPES.put(LEFT.getName(), LEFT);
188: TYPES.put(RIGHT.getName(), RIGHT);
189: }
190:
191: private String name;
192:
193: public Type(String name) {
194: this .name = name;
195: }
196:
197: /**
198: * Do not change it - for some stupid reason it is used in sql!!!
199: * @return type name
200: */
201: public String toString() {
202: return name;
203: }
204:
205: public String getName() {
206: return name;
207: }
208:
209: public static Type name2type(String name) {
210: return (Type) TYPES.get(name);
211: }
212:
213: }
214:
215: /**
216: * Join table type class
217: */
218: public static class Table implements Serializable, Mappable {
219: public static final String[] NULL_ARRAY = new String[0];
220: private Type type = NULL_JOIN;
221:
222: private String firstTableAlias;
223: private String joinedTableName;
224: private String joinedTableAlias;
225:
226: private String[] firstTableColumns;
227: private String[] joinedTableColumns;
228:
229: private String linkName;
230: private static final String TAG_JOINED_TABLE_NAME = "joinedTableName";
231: private static final String TAG_JOINED_TABLE_ALIAS = "joinedTableAlias";
232: private static final String TAG_FIRST_TABLE_COLUMNS = "firstTableColumns";
233: private static final String TAG_JOINED_TABLE_COLUMNS = "joinedTableColumns";
234: private static final String TAG_LINK_NAME = "linkName";
235: private static final String TAG_TYPE = "type";
236:
237: public Table(Map map) {
238: fromMap(map);
239: }
240:
241: public Table(Type type, String firstTableAlias,
242: String joinedTableName, String joinedTableAlias,
243: String[] firstTableColumns, String[] joinedTableColumns) {
244: if (type != null) {
245: this .type = type;
246: } else {
247: this .type = INNER;
248: }
249: this .firstTableAlias = firstTableAlias;
250: this .joinedTableName = joinedTableName;
251: this .joinedTableAlias = joinedTableAlias;
252: this .firstTableColumns = firstTableColumns == null ? NULL_ARRAY
253: : firstTableColumns;
254: this .joinedTableColumns = joinedTableColumns == null ? NULL_ARRAY
255: : joinedTableColumns;
256: }
257:
258: public Type getJoinType() {
259: return type;
260: }
261:
262: public String getJoinedTableName() {
263: return joinedTableName;
264: }
265:
266: public String getFullJoinedTableNameForFrom() {
267: if (StringUtil.isEmpty(joinedTableAlias)) {
268: return joinedTableName;
269: } else {
270: return new StringBuffer(joinedTableName).append(" ")
271: .append(joinedTableAlias).toString();
272: }
273: }
274:
275: public String getFullFirstColumnName(int index) {
276: return fullColumnName(firstTableAlias,
277: firstTableColumns[index]);
278: }
279:
280: public String getFullJoinedColumnName(int index) {
281: return fullColumnName(resolveJoinedTableName(),
282: joinedTableColumns[index]);
283: }
284:
285: protected String fullColumnName(String resolvedTableName,
286: String columnName) {
287: if (columnName.indexOf('.') > 0) {
288: return columnName;
289: } else {
290: return new StringBuffer(resolvedTableName).append(".")
291: .append(columnName).toString();
292: }
293: }
294:
295: public String resolveJoinedTableName() {
296: if (StringUtil.isEmpty(joinedTableAlias)) {
297: return joinedTableName;
298: } else {
299: return joinedTableAlias;
300: }
301: }
302:
303: public String getJoinedTableAlias() {
304: return joinedTableAlias;
305: }
306:
307: public void setJoinedTableAlias(String joinedTableAlias) {
308: this .joinedTableAlias = joinedTableAlias;
309: }
310:
311: public String getFirstTableAlias() {
312: return firstTableAlias;
313: }
314:
315: public void setFirstTableAlias(String firstTableAlias) {
316: this .firstTableAlias = firstTableAlias;
317: }
318:
319: public String[] getFirstTableColumns() {
320: return firstTableColumns;
321: }
322:
323: public String[] getJoinedTableColumns() {
324: return joinedTableColumns;
325: }
326:
327: public StringBuffer joinTableToString() {
328: StringBuffer joinTable = new StringBuffer();
329: joinTable.append(" (").append(joinedTableName).append(") ")
330: .append(joinedTableAlias).append(" ");
331: return joinTable;
332: }
333:
334: public String getLinkName() {
335: return linkName;
336: }
337:
338: public void setLinkName(String linkName) {
339: this .linkName = linkName;
340: }
341:
342: public boolean equals(Object value) {
343: if (this == value)
344: return true;
345: if (value == null || getClass() != value.getClass())
346: return false;
347:
348: final Table table = (Table) value;
349:
350: if (!Arrays.equals(firstTableColumns,
351: table.firstTableColumns))
352: return false;
353: if (joinedTableAlias != null ? !joinedTableAlias
354: .equals(table.joinedTableAlias)
355: : table.joinedTableAlias != null)
356: return false;
357: if (!Arrays.equals(joinedTableColumns,
358: table.joinedTableColumns))
359: return false;
360: if (joinedTableName != null ? !joinedTableName
361: .equals(table.joinedTableName)
362: : table.joinedTableName != null)
363: return false;
364: if (type != null ? !type.equals(table.type)
365: : table.type != null)
366: return false;
367:
368: return true;
369: }
370:
371: public int hashCode() {
372: int result;
373: result = (type != null ? type.hashCode() : 0);
374: result = 29
375: * result
376: + (joinedTableName != null ? joinedTableName
377: .hashCode() : 0);
378: result = 29
379: * result
380: + (joinedTableAlias != null ? joinedTableAlias
381: .hashCode() : 0);
382: result = 29 * result
383: + (linkName != null ? linkName.hashCode() : 0);
384: return result;
385: }
386:
387: public String toString() {
388: return " { " + "joinedTableName = " + joinedTableName
389: + "; joinedTableAlias = " + joinedTableAlias
390: + "; firstTableColumns = "
391: + Arrays.asList(firstTableColumns)
392: + "; joinedTableColumns = "
393: + Arrays.asList(joinedTableColumns)
394: + "; linkName = " + linkName + "; type = " + type
395: + " }"
396:
397: ;
398: }
399:
400: public Map toMap() {
401: HashMap map = new HashMap();
402: map.put(TAG_JOINED_TABLE_NAME, joinedTableName);
403: map.put(TAG_JOINED_TABLE_ALIAS, joinedTableAlias);
404: map.put(TAG_FIRST_TABLE_COLUMNS, Arrays
405: .asList(firstTableColumns));
406: map.put(TAG_JOINED_TABLE_COLUMNS, Arrays
407: .asList(joinedTableColumns));
408: map.put(TAG_LINK_NAME, linkName);
409: map.put(TAG_TYPE, type.getName());
410: return map;
411: }
412:
413: public void fromMap(Map map) {
414: PropertyMap propertyMap = PropertyMap.toPropertyMap(map);
415: joinedTableName = propertyMap.getProperty(
416: TAG_JOINED_TABLE_NAME, true);
417: joinedTableAlias = propertyMap
418: .getProperty(TAG_JOINED_TABLE_ALIAS);
419: firstTableColumns = extractColumns(propertyMap,
420: TAG_FIRST_TABLE_COLUMNS);
421: joinedTableColumns = extractColumns(propertyMap,
422: TAG_JOINED_TABLE_COLUMNS);
423: linkName = propertyMap.getProperty(TAG_LINK_NAME);
424: String typeName = propertyMap.getProperty(TAG_TYPE, true);
425: type = Type.name2type(typeName);
426: }
427:
428: private String[] extractColumns(PropertyMap propertyMap,
429: String tag) {
430: String[] firstTableColumns = null;
431: List firstTableColumnsList = propertyMap.getList(tag);
432: if (firstTableColumnsList != null) {
433: firstTableColumns = (String[]) firstTableColumnsList
434: .toArray(new String[firstTableColumnsList
435: .size()]);
436: }
437: return firstTableColumns;
438: }
439: }
440:
441: /**
442: * Join table iterator
443: */
444: public static class TableIterator {
445: private Iterator joinedTablesIterator;
446:
447: public TableIterator(LinkedHashMap joinedTables) {
448: if (joinedTables != null) {
449: this .joinedTablesIterator = joinedTables.entrySet()
450: .iterator();
451: }
452: }
453:
454: public boolean hasNext() {
455: return joinedTablesIterator != null
456: && joinedTablesIterator.hasNext();
457: }
458:
459: public Table next() {
460: Map.Entry entry = (Map.Entry) joinedTablesIterator.next();
461: return (Table) entry.getValue();
462: }
463: }
464:
465: }
|