/* * Copyright 2007-2012 Amazon Technologies, Inc. or its affiliates. * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks * of Amazon Technologies, Inc. or its affiliates. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.amazon.carbonado.repo.jdbc; import java.util.LinkedHashMap; import java.util.Map; import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.info.ChainedProperty; import com.amazon.carbonado.info.StorableProperty; /** * Node in a tree structure describing how tables are joined together. * * @author Brian S O'Neill * @see JoinNodeBuilder */ class JoinNode { // Joined property which led to this node. For root node, it is null. private final JDBCStorableProperty mProperty; private final boolean mOuterJoin; private final JDBCStorableInfo mInfo; private final String mAlias; private final Map mSubNodes; private boolean mAliasRequired; /** * @param alias table alias in SQL statement, i.e. "T1" */ JoinNode(JDBCStorableInfo info, String alias) { this(null, false, info, alias); } private JoinNode(JDBCStorableProperty property, boolean outerJoin, JDBCStorableInfo info, String alias) { mProperty = property; mOuterJoin = outerJoin; mInfo = info; mAlias = alias; mSubNodes = new LinkedHashMap(); } /** * Returns the table alias to use in SQL statement, i.e. "T1" */ public String getAlias() { return mAlias; } public String findAliasFor(ChainedProperty chained) { return findAliasFor(chained, 0); } private String findAliasFor(ChainedProperty chained, int offset) { if ((chained.getChainCount() - offset) <= 0) { // At this point in the chain, there are no more joins. return mAlias; } StorableProperty property; if (offset == 0) { property = chained.getPrimeProperty(); } else { property = chained.getChainedProperty(offset - 1); } boolean outerJoin = chained.isOuterJoin(offset); SubNodeKey key = new SubNodeKey(property.getName(), outerJoin); JoinNode subNode = mSubNodes.get(key); if (subNode != null) { return subNode.findAliasFor(chained, offset + 1); } return null; } public boolean isAliasRequired() { return mAliasRequired || mSubNodes.size() > 0; } /** * Appends table name to the given FROM clause builder. */ public void appendTableNameTo(SQLStatementBuilder fromClause) { fromClause.append(' '); fromClause.append(mInfo.getQualifiedTableName()); } /** * Appends table name and alias to the given FROM clause builder. */ public void appendTableNameAndAliasTo(SQLStatementBuilder fromClause) { appendTableNameTo(fromClause); fromClause.append(' '); fromClause.append(mAlias); } /** * Appends table names, aliases, and joins to the given FROM clause * builder. */ public void appendFullJoinTo(SQLStatementBuilder fromClause) { appendTableNameAndAliasTo(fromClause); appendTailJoinTo(fromClause); } private void appendTailJoinTo(SQLStatementBuilder fromClause) { for (JoinNode jn : mSubNodes.values()) { if (jn.mOuterJoin) { fromClause.append(" LEFT OUTER JOIN"); } else { fromClause.append(" INNER JOIN"); } jn.appendTableNameAndAliasTo(fromClause); fromClause.append(" ON "); int count = jn.mProperty.getJoinElementCount(); for (int i=0; i 0) { fromClause.append(" AND "); } fromClause.append(mAlias); fromClause.append('.'); fromClause.append(jn.mProperty.getInternalJoinElement(i).getColumnName()); fromClause.append('='); fromClause.append(jn.mAlias); fromClause.append('.'); fromClause.append(jn.mProperty.getExternalJoinElement(i).getColumnName()); } jn.appendTailJoinTo(fromClause); } } public void addJoin(JDBCRepository repository, ChainedProperty chained, TableAliasGenerator aliasGenerator) throws RepositoryException { addJoin(repository, chained, aliasGenerator, 0); } private void addJoin(JDBCRepository repository, ChainedProperty chained, TableAliasGenerator aliasGenerator, int offset) throws RepositoryException { if ((chained.getChainCount() - offset) <= 0) { // At this point in the chain, there are no more joins. return; } StorableProperty property; if (offset == 0) { property = chained.getPrimeProperty(); } else { property = chained.getChainedProperty(offset - 1); } boolean outerJoin = chained.isOuterJoin(offset); SubNodeKey key = new SubNodeKey(property.getName(), outerJoin); JoinNode subNode = mSubNodes.get(key); if (subNode == null) { JDBCStorableInfo info = repository.examineStorable(property.getJoinedType()); JDBCStorableProperty jProperty = repository.getJDBCStorableProperty(property); subNode = new JoinNode(jProperty, outerJoin, info, aliasGenerator.nextAlias()); mSubNodes.put(key, subNode); } subNode.addJoin(repository, chained, aliasGenerator, offset + 1); } public void aliasIsRequired() { mAliasRequired = true; } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append("{table="); b.append(mInfo.getQualifiedTableName()); b.append(", alias="); b.append(mAlias); if (mSubNodes.size() > 0) { b.append(", subNodes="); b.append(mSubNodes); } b.append('}'); return b.toString(); } private static class SubNodeKey { final String mPropertyName; final boolean mOuterJoin; SubNodeKey(String propertyName, boolean outerJoin) { mPropertyName = propertyName; mOuterJoin = outerJoin; } @Override public int hashCode() { return mPropertyName.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof SubNodeKey) { SubNodeKey other = (SubNodeKey) obj; return mPropertyName.equals(other.mPropertyName) && mOuterJoin == other.mOuterJoin; } return false; } @Override public String toString() { return "propertyName=" + mPropertyName + ", outerJoin=" + mOuterJoin; } } }