001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Florent Guillaume
011 */
012package org.nuxeo.ecm.core.storage.sql.jdbc.db;
013
014import java.io.Serializable;
015import java.util.LinkedList;
016import java.util.List;
017
018import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect;
019
020/**
021 * A SQL JOIN.
022 */
023public class Join implements Serializable, Comparable<Join> {
024
025    private static final long serialVersionUID = 1L;
026
027    public static final int INNER = 1;
028
029    public static final int LEFT = 2;
030
031    public static final int RIGHT = 3;
032
033    public static final int IMPLICIT = 4;
034
035    /** INNER / LEFT / RIGHT / IMPLICIT */
036    public final int kind;
037
038    /** Table name. */
039    public final String table;
040
041    /** Table alias, or {@code null}. */
042    public final String tableAlias;
043
044    /**
045     * Parameter if table name is an expression that contains a "?", or {@code null}.
046     */
047    public final String tableParam;
048
049    /** Left part of equijoin. */
050    public Column column1;
051
052    /** Right part of equijoin. */
053    public Column column2;
054
055    /** Left part of equijoin. */
056    public String on1;
057
058    /** Right part of equijoin. */
059    public String on2;
060
061    /** Additional WHERE clauses. */
062    public final List<String> whereClauses = new LinkedList<String>();
063
064    /** Additional WHERE clauses parameters. */
065    public final List<Serializable> whereParams = new LinkedList<Serializable>();
066
067    private Join(int kind, String table, String tableAlias, String tableParam) {
068        this.kind = kind;
069        this.table = table;
070        this.tableAlias = tableAlias;
071        this.tableParam = tableParam;
072    }
073
074    public Join(int kind, String table, String tableAlias, String tableParam, Column column1, Column column2) {
075        this(kind, table, tableAlias, tableParam);
076        this.column1 = column1;
077        this.column2 = column2;
078    }
079
080    public Join(int kind, String table, String tableAlias, String tableParam, String on1, String on2) {
081        this(kind, table, tableAlias, tableParam);
082        this.on1 = on1;
083        this.on2 = on2;
084    }
085
086    public void addWhereClause(String whereClause, Serializable whereParam) {
087        whereClauses.add(whereClause);
088        whereParams.add(whereParam);
089    }
090
091    // make sure IMPLICIT joins are last
092    @Override
093    public int compareTo(Join other) {
094        if (kind == IMPLICIT && other.kind == IMPLICIT) {
095            return 0;
096        }
097        if (kind == IMPLICIT) {
098            return 1;
099        }
100        if (other.kind == IMPLICIT) {
101            return -1;
102        }
103        return 0;
104    }
105
106    public String getTable(Dialect dialect) {
107        if (tableAlias == null) {
108            return table;
109        } else {
110            return table + " " + dialect.openQuote() + tableAlias + dialect.closeQuote();
111        }
112    }
113
114    public String getClause(Dialect dialect) {
115        if (on1 == null && on2 == null) {
116            on1 = column1.getFullQuotedName();
117            on2 = column2.getFullQuotedName();
118            boolean isid1 = column1.getType().isId();
119            boolean isid2 = column2.getType().isId();
120            if (dialect != null && isid1 != isid2) {
121                // temporary fix cast uuid to varchar because relation table
122                // has varchar source and target field
123                if (isid1) {
124                    on1 = dialect.castIdToVarchar(on1);
125                } else {
126                    on2 = dialect.castIdToVarchar(on2);
127                }
128            }
129        }
130        return on1 + " = " + on2;
131    }
132
133    /**
134     * Does not return the WHERE clause.
135     * <p>
136     * {@inheritDoc}
137     */
138    public String toSql(Dialect dialect) {
139        switch (kind) {
140        case INNER:
141            return String.format(" JOIN %s ON %s", getTable(dialect), getClause(dialect));
142        case LEFT:
143            return String.format(" LEFT JOIN %s ON %s", getTable(dialect), getClause(dialect));
144        case RIGHT:
145            return String.format(" RIGHT JOIN %s ON %s", getTable(dialect), getClause(dialect));
146        case IMPLICIT:
147            return String.format(", %s", getTable(dialect));
148        default:
149            throw new AssertionError();
150        }
151    }
152
153    @Override
154    public String toString() {
155        String k;
156        switch (kind) {
157        case INNER:
158            k = "INNER";
159            break;
160        case LEFT:
161            k = "LEFT";
162            break;
163        case RIGHT:
164            k = "RIGHT";
165            break;
166        case IMPLICIT:
167            k = "IMPLICIT";
168            break;
169        default:
170            throw new AssertionError();
171        }
172        StringBuilder buf = new StringBuilder();
173        buf.append("<");
174        buf.append(k);
175        buf.append(" JOIN ");
176        buf.append(table);
177        if (tableAlias != null) {
178            buf.append(" ");
179            buf.append(tableAlias);
180        }
181        buf.append(" ON ");
182        buf.append(getClause(null));
183        if (!whereClauses.isEmpty()) {
184            buf.append(" WHERE ");
185            buf.append(whereClauses);
186            buf.append(" % ");
187            buf.append(whereParams);
188        }
189        buf.append(">");
190        return buf.toString();
191    }
192
193}