org.dbflute.cbean.chelper.HpFixedConditionQueryResolver Maven / Gradle / Ivy
/*
* Copyright 2014-2020 the original author or authors.
*
* 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 org.dbflute.cbean.chelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.dbflute.cbean.ConditionQuery;
import org.dbflute.cbean.sqlclause.join.FixedConditionResolver;
import org.dbflute.cbean.sqlclause.subquery.SubQueryIndentProcessor;
import org.dbflute.dbmeta.DBMeta;
import org.dbflute.dbmeta.DBMetaProvider;
import org.dbflute.dbmeta.info.ColumnInfo;
import org.dbflute.dbmeta.info.ForeignInfo;
import org.dbflute.dbmeta.name.TableSqlName;
import org.dbflute.exception.DBMetaNotFoundException;
import org.dbflute.exception.FixedConditionIllegalOverRelationException;
import org.dbflute.helper.message.ExceptionMessageBuilder;
import org.dbflute.system.DBFluteSystem;
import org.dbflute.util.Srl;
import org.dbflute.util.Srl.IndexOfInfo;
/**
* @author jflute
* @since 0.9.7.5 (2010/10/11 Monday)
*/
public class HpFixedConditionQueryResolver implements FixedConditionResolver {
// ===================================================================================
// Definition
// ==========
public static final String LOCAL_ALIAS_MARK = "$$localAlias$$";
public static final String FOREIGN_ALIAS_MARK = "$$foreignAlias$$";
public static final String SQ_BEGIN_MARK = "$$sqbegin$$";
public static final String SQ_END_MARK = "$$sqend$$";
public static final String INLINE_MARK = "$$inline$$";
public static final String LOCATION_BASE_MARK = "$$locationBase$$";
public static final String OPTIMIZED_MARK = "$$optimized$$";
// ===================================================================================
// Attribute
// =========
protected final ConditionQuery _localCQ;
protected final ConditionQuery _foreignCQ;
protected final DBMetaProvider _dbmetaProvider;
// analyzing result in variable resolution (internal bridge variables for next step)
protected String _resolvedFixedCondition;
protected Map _inlineViewResourceMap;
protected String _inlineViewOptimizedCondition;
protected boolean _inlineViewOptimizationWholeCondition;
protected Set _inlineViewOptimizedLineNumberSet;
// ===================================================================================
// Constructor
// ===========
public HpFixedConditionQueryResolver(ConditionQuery localCQ, ConditionQuery foreignCQ, DBMetaProvider dbmetaProvider) {
_localCQ = localCQ;
_foreignCQ = foreignCQ;
_dbmetaProvider = dbmetaProvider;
}
// ===================================================================================
// Resolve Variable
// ================
/**
* {@inheritDoc}
*/
public String resolveVariable(String fixedCondition, boolean fixedInline) {
// should be called before optimization
// because analyzing process has saving the fixed condition
// so it needs to filter it before saved
fixedCondition = filterLocationMark(fixedCondition, fixedInline);
analyzeInlineViewOptimization(fixedCondition, fixedInline);
fixedCondition = filterBasicMark(fixedCondition, fixedInline);
fixedCondition = filterSubQueryIndentMark(fixedCondition, fixedInline, false);
fixedCondition = resolveOverRelation(fixedCondition, fixedInline);
final String resolvedFixedCondition;
final String delimiter = getInlineMark();
if (fixedCondition.contains(delimiter)) { // mark optimization
final String inlineCondition = Srl.substringFirstFront(fixedCondition, delimiter);
final String filtered = Srl.rtrim(inlineCondition);
if (filtered.trim().length() > 0) {
resolvedFixedCondition = filtered;
} else { // mark exists at first line (same as whole actually)
resolvedFixedCondition = OPTIMIZED_MARK;
}
} else {
if (_inlineViewOptimizationWholeCondition) { // whole optimization
resolvedFixedCondition = OPTIMIZED_MARK; // as dummy
} else { // part optimization or no optimized
resolvedFixedCondition = fixedCondition;
}
}
_resolvedFixedCondition = resolvedFixedCondition;
return _resolvedFixedCondition;
}
// ===================================================================================
// Analyze Optimization
// ====================
protected void analyzeInlineViewOptimization(String fixedCondition, boolean fixedInline) {
if (fixedInline) {
return;
}
if (!hasFixedInlineView(fixedCondition)) {
return;
}
if (doAnalyzeInlineViewOptimizationMark(fixedCondition)) {
return;
}
if (doAnalyzeInlineViewOptimizationWhole(fixedCondition)) {
return;
}
if (doAnalyzeInlineViewOptimizationPart(fixedCondition)) {
return;
}
}
protected boolean hasFixedInlineView(String fixedCondition) {
final String relationBeginMark = getRelationBeginMark();
final String foreignTableMark = getForeignTableMark();
final String foreignOverMark = relationBeginMark + foreignTableMark;
return fixedCondition.contains(foreignOverMark);
}
protected boolean doAnalyzeInlineViewOptimizationMark(String fixedCondition) {
final String delimiter = getInlineMark();
if (fixedCondition.contains(delimiter)) {
final String inlineCondition = Srl.substringFirstRear(fixedCondition, delimiter);
final String filtered = removePrefixConnector(inlineCondition);
// null means no in-line, while it means suppressing optimization
_inlineViewOptimizedCondition = filtered.trim().length() > 0 ? filtered : null;
return true;
}
return false;
}
protected boolean doAnalyzeInlineViewOptimizationWhole(String fixedCondition) {
_inlineViewOptimizationWholeCondition = canBeInlineViewOptimization(fixedCondition);
if (_inlineViewOptimizationWholeCondition) {
_inlineViewOptimizedCondition = fixedCondition;
return true;
}
return false;
}
protected boolean doAnalyzeInlineViewOptimizationPart(String fixedCondition) {
final String ifCommentMark = "/*IF";
if (fixedCondition.contains(ifCommentMark)) {
return false; // optimization part is unsupported when IF comment exists
}
final List lineList = Srl.splitList(fixedCondition, ln());
String previous = null; // optimization candidate value
final List optLineList = new ArrayList();
final List optLineNumberList = new ArrayList(lineList.size());
int lineNumber = 1;
for (String current : lineList) {
if (isCandidateOfOptimization(previous) && isOptimizationHitLine(previous, current, lineNumber)) {
// and ... (previous) or ...
// and ... (current)
optLineList.add(previous); // previous line can be optimized
optLineNumberList.add(lineNumber - 1);
}
previous = current;
++lineNumber;
}
if (isCandidateOfOptimization(previous)) {
if (startsWithConnector(previous)) {
// ...
// and ... (previous)
optLineList.add(previous);
optLineNumberList.add(lineNumber - 1);
}
}
if (!optLineNumberList.isEmpty()) {
_inlineViewOptimizedLineNumberSet = new HashSet(optLineNumberList);
}
if (!optLineList.isEmpty()) {
final StringBuilder optSb = new StringBuilder();
for (String line : optLineList) {
if (optSb.length() > 0) {
optSb.append(ln());
}
optSb.append(line);
}
_inlineViewOptimizedCondition = removePrefixConnector(optSb.toString());
return true;
}
return false;
}
protected boolean isCandidateOfOptimization(String previous) {
return previous != null && canBeInlineViewOptimization(previous) && !hasUnclosedBrace(previous);
}
protected boolean isOptimizationHitLine(String previous, String current, int lineNumber) {
if (lineNumber == 2 || (lineNumber >= 3 && startsWithConnector(previous))) { // first line or 'and'
return startsWithConnector(current); // current (means next) line should start with 'and'
}
return false;
}
protected boolean canBeInlineViewOptimization(String fixedCondition) {
final String relationBeginMark = getRelationBeginMark();
final String foreignTableMark = getForeignTableMark();
final String foreignOverMark = relationBeginMark + foreignTableMark;
final String foreignAliasMark = getForeignAliasMark();
if (!Srl.containsAny(fixedCondition, foreignOverMark, foreignAliasMark)) {
return false;
}
final String removedCondition = replaceString(fixedCondition, foreignOverMark, "");
if (removedCondition.contains(relationBeginMark)) { // other over relation exists
return false;
}
final String localAliasMark = getLocalAliasMark();
if (removedCondition.contains(localAliasMark)) { // local element exists
return false;
}
// has (foreign over relation or foreign alias) and no local elements
return true;
}
// ===================================================================================
// Filter Mark and Variable
// ========================
protected String filterBasicMark(String fixedCondition, boolean fixedInline) {
final String localAliasName = _localCQ.xgetAliasName();
final String foreignAliasName = _foreignCQ.xgetAliasName();
fixedCondition = replaceString(fixedCondition, "$$alias$$", foreignAliasName); // for compatibility
fixedCondition = replaceString(fixedCondition, getLocalAliasMark(), localAliasName);
if (!_inlineViewOptimizationWholeCondition) {
fixedCondition = replaceString(fixedCondition, getForeignAliasMark(), foreignAliasName);
}
return fixedCondition;
}
protected String filterSubQueryIndentMark(String fixedCondition, boolean fixedInline, boolean optimized) {
final String sqBeginMark = getSqBeginMark();
final String sqEndMark = getSqEndMark();
if (!fixedCondition.contains(sqBeginMark) || !fixedCondition.contains(sqEndMark)) {
return fixedCondition;
}
final String sqEndIndent = calculateSqEndIndent(fixedInline, optimized);
final String indentFrom = "\n)" + sqEndMark;
final String indentTo = "\n" + sqEndIndent + ")" + sqEndMark;
fixedCondition = Srl.replace(fixedCondition, indentFrom, indentTo);
final SubQueryIndentProcessor processor = new SubQueryIndentProcessor();
final String foreignAliasName = _foreignCQ.xgetAliasName();
final String subQueryIdentity = "fixed_" + foreignAliasName;
final String beginMark = processor.resolveSubQueryBeginMark(subQueryIdentity);
fixedCondition = Srl.replace(fixedCondition, sqBeginMark, beginMark);
final String endMark = processor.resolveSubQueryEndMark(subQueryIdentity);
fixedCondition = Srl.replace(fixedCondition, sqEndMark, endMark);
return fixedCondition;
}
protected String calculateSqEndIndent(boolean fixedInline, boolean optimized) {
final String indent;
if (fixedInline) {
// ------"select ..."
// ------" from ..."
// ------" left outer join (select ..."
// ------" where ..."
indent = " ";
// *inner-join gives up
} else if (optimized) {
// ------"(select ..."
// ------" from ..."
// ------" left outer join ..."
// ------" on ..."
// ------" where ..."
indent = " ";
} else { // normal
// ------"select ..."
// ------" from ..."
// ------" left outer join ..."
// ------" on ..."
indent = " ";
}
return indent;
}
protected String filterLocationMark(String fixedCondition, boolean fixedInline) {
final String locationBase = _localCQ.xgetLocationBase();
return replaceString(fixedCondition, getLocationBaseMark() + ".", "pmb." + locationBase);
}
// ===================================================================================
// Resolve Over Relation
// =====================
protected String resolveOverRelation(String fixedCondition, boolean fixedInline) {
// analyze:
// - "$$over($localTable.memberSecurity)$$.REMINDER_QUESTION"
// - "$$over($foreignTable.memberStatus, DISPLAY_ORDER)$$.ORDER_NO"
// - "$$over(PURCHASE.product.productStatus)$$.PRODUCT_STATUS_NAME"
final String relationBeginMark = getRelationBeginMark();
final String relationEndMark = getRelationEndMark();
String resolvedClause = fixedCondition;
String remainder = resolvedClause;
while (true) {
// "|$$over(|$localTable.memberSecurity)$$.REMINDER_QUESTION"
final IndexOfInfo relationBeginIndex = Srl.indexOfFirst(remainder, relationBeginMark);
if (relationBeginIndex == null) {
break;
}
remainder = relationBeginIndex.substringRear();
// "$localTable.memberSecurity|)$$|.REMINDER_QUESTION"
final IndexOfInfo relationEndIndex = Srl.indexOfFirst(remainder, relationEndMark);
if (relationEndIndex == null) {
break;
}
// remainder is e.g. "$localTable.memberSecurity)$$" now
// e.g. "$localTable.memberSecurity" or "$foreignTable.memberStatus, DISPLAY_ORDER"
final String relationExp = relationEndIndex.substringFront();
// e.g. "$$over($localTable.memberSecurity)$$" or "$$over($foreignTable.memberStatus, DISPLAY_ORDER)$$"
final String relationVariable = relationBeginMark + relationExp + relationEndMark;
final String pointTable; // e.g. "$localTable" or "$foreignTable" or "PURCHASE"
final String targetRelation; // e.g. "memberSecurity" or "product.productStatus" or null (means base only)
final String secondArg; // e.g. DISPLAY_ORDER or null (means no argument)
{
final IndexOfInfo separatorIndex = Srl.indexOfFirst(relationExp, ".");
if (separatorIndex != null) { // normally here
pointTable = separatorIndex.substringFrontTrimmed(); // e.g. $localTable
final String separatorRear = separatorIndex.substringRearTrimmed();
final IndexOfInfo argIndex = Srl.indexOfFirst(separatorRear, ",");
targetRelation = argIndex != null ? argIndex.substringFrontTrimmed() : separatorRear;
secondArg = argIndex != null ? argIndex.substringRearTrimmed() : null;
} else { // e.g. "$$over(PURCHASE)$$"
final IndexOfInfo argIndex = Srl.indexOfFirst(relationExp, ",");
pointTable = argIndex != null ? argIndex.substringFrontTrimmed() : Srl.trim(relationExp);
targetRelation = null;
secondArg = argIndex != null ? argIndex.substringRearTrimmed() : null;
}
}
final ConditionQuery relationPointCQ;
final ConditionQuery columnTargetCQ;
if (Srl.equalsPlain(pointTable, getLocalTableMark())) { // local table
relationPointCQ = _localCQ;
if (targetRelation != null) {
columnTargetCQ = invokeColumnTargetCQ(relationPointCQ, targetRelation);
} else {
String notice = "The relation on fixed condition is required if the table is not referrer.";
throwIllegalFixedConditionOverRelationException(notice, pointTable, null, fixedCondition);
return null; // unreachable
}
} else if (Srl.equalsPlain(pointTable, getForeignTableMark())) { // foreign table
relationPointCQ = _foreignCQ;
columnTargetCQ = relationPointCQ;
if (targetRelation == null) {
String notice = "The relation on fixed condition is required if the table is not referrer.";
throwIllegalFixedConditionOverRelationException(notice, pointTable, null, fixedCondition);
return null; // unreachable
}
// prepare fixed InlineView
if (_inlineViewResourceMap == null) {
_inlineViewResourceMap = new LinkedHashMap();
}
final InlineViewResource resource;
if (_inlineViewResourceMap.containsKey(targetRelation)) {
resource = _inlineViewResourceMap.get(targetRelation);
} else {
resource = new InlineViewResource();
_inlineViewResourceMap.put(targetRelation, resource);
}
final String columnName;
{
// e.g. "$$over($localTable.memberSecurity)$$|.|REMINDER_QUESTION = ..."
final IndexOfInfo rearIndex = Srl.indexOfFirst(relationEndIndex.substringRearTrimmed(), ".");
if (rearIndex == null || rearIndex.getIndex() > 0) {
String notice = "The OverRelation variable should continue to column after the variable.";
throwIllegalFixedConditionOverRelationException(notice, pointTable, targetRelation, fixedCondition);
return null; // unreachable
}
final String columnStart = rearIndex.substringRear(); // e.g. REMINDER_QUESTION = ...
final IndexOfInfo indexInfo = Srl.indexOfFirst(columnStart, " ", ",", ")", "\n", "\t");
columnName = indexInfo != null ? indexInfo.substringFront() : columnStart; // REMINDER_QUESTION
}
// the secondArg should be a column DB name, and then rear column is alias name
final String resolvedColumn = secondArg != null ? secondArg + " as " + columnName : columnName;
resource.addAdditionalColumn(resolvedColumn); // selected in in-line view
if (!resource.hasJoinInfo()) { // first analyze
final List traceList = Srl.splitList(targetRelation, ".");
DBMeta currentDBMeta = _dbmetaProvider.provideDBMeta(_foreignCQ.asTableDbName());
for (String trace : traceList) {
final ForeignInfo foreignInfo = currentDBMeta.findForeignInfo(trace);
resource.addJoinInfo(foreignInfo);
currentDBMeta = foreignInfo.getForeignDBMeta();
}
}
final List joinInfoList = resource.getJoinInfoList();
if (!joinInfoList.isEmpty()) { // basically true (but just in case)
final ForeignInfo latestForeignInfo = joinInfoList.get(joinInfoList.size() - 1);
resource.addOptimizedVariable(relationVariable, latestForeignInfo);
}
} else { // referrer table
final DBMeta pointDBMeta;
try {
pointDBMeta = _dbmetaProvider.provideDBMeta(pointTable);
} catch (DBMetaNotFoundException e) {
String notice = "The table for relation on fixed condition does not exist.";
throwIllegalFixedConditionOverRelationException(notice, pointTable, targetRelation, fixedCondition, e);
return null; // unreachable
}
ConditionQuery referrerQuery = _localCQ.xgetReferrerQuery();
while (true) {
if (referrerQuery == null) { // means not found
break;
}
if (Srl.equalsPlain(pointDBMeta.getTableDbName(), referrerQuery.asTableDbName())) {
break;
}
referrerQuery = referrerQuery.xgetReferrerQuery();
}
relationPointCQ = referrerQuery;
if (relationPointCQ == null) {
String notice = "The table for relation on fixed condition was not found in the scope.";
throwIllegalFixedConditionOverRelationException(notice, pointTable, targetRelation, fixedCondition);
return null; // unreachable
}
if (targetRelation != null) {
columnTargetCQ = invokeColumnTargetCQ(relationPointCQ, targetRelation);
} else {
columnTargetCQ = relationPointCQ;
}
}
// resolve over-relation variables in clause
final String relationAlias = columnTargetCQ.xgetAliasName(); // e.g. "dfrel_4"
resolvedClause = replaceString(resolvedClause, relationVariable, relationAlias);
// after case for loop
remainder = relationEndIndex.substringRear();
// no replace even if same relation because of additional column
//// to prevent from processing same one
//remainder = replaceString(remainder, relationVariable, relationAlias);
}
resolvedClause = adjustOptimizedLine(resolvedClause);
return resolvedClause;
}
protected ConditionQuery invokeColumnTargetCQ(ConditionQuery relationPointCQ, String targetRelation) {
return relationPointCQ.invokeForeignCQ(targetRelation);
}
protected String adjustOptimizedLine(String resolvedClause) {
if (_inlineViewOptimizedLineNumberSet == null) {
return resolvedClause;
}
final List lineList = Srl.splitList(resolvedClause, ln());
final List filteredList = new ArrayList();
int lineNumber = 1;
for (String line : lineList) {
if (!_inlineViewOptimizedLineNumberSet.contains(lineNumber)) {
filteredList.add(line);
}
++lineNumber;
}
final StringBuilder filteredSb = new StringBuilder();
for (String line : filteredList) {
if (filteredSb.length() > 0) {
filteredSb.append(ln());
}
filteredSb.append(line);
}
return removePrefixConnector(filteredSb.toString());
}
// ===================================================================================
// Resolve Fixed InlineView
// ========================
public String resolveFixedInlineView(String foreignTableSqlName, boolean treatedAsInnerJoin) {
// it is precondition that the fixed condition has already been resolved here
// so it can uses bridge variables here
if (_inlineViewResourceMap == null || _inlineViewResourceMap.isEmpty()) {
return foreignTableSqlName; // not uses InlineView
}
// alias is required because foreignTableSqlName may be (normal) InlineView
final String baseAlias = "dffixedbase";
final String baseIndent;
if (treatedAsInnerJoin) {
// ----------" inner join "
baseIndent = " ";
} else {
// ----------" left outer join "
baseIndent = " ";
}
final StringBuilder joinSb = new StringBuilder();
final Map relationMap = new HashMap();
final List additionalRealColumnList = new ArrayList();
final String resolvedFixedCondition = _resolvedFixedCondition; // basically not null
String optimizedCondition = _inlineViewOptimizedCondition;
int groupIndex = 0;
for (InlineViewResource resource : _inlineViewResourceMap.values()) {
final List joinInfoList = resource.getJoinInfoList();
final String aliasBase = "dffixedjoin";
String preForeignAlias = null;
String foreignAlias = null;
int joinIndex = 0;
final Map foreignAliasMap = new HashMap(joinInfoList.size());
for (ForeignInfo joinInfo : joinInfoList) {
if (relationMap.containsKey(joinInfo)) { // already joined
preForeignAlias = relationMap.get(joinInfo); // update previous alias
continue;
}
final TableSqlName foreignTable;
final String localAlias;
{
final DBMeta foreignDBMeta = joinInfo.getForeignDBMeta();
foreignTable = foreignDBMeta.getTableSqlName();
localAlias = (preForeignAlias != null ? preForeignAlias : baseAlias);
foreignAlias = aliasBase + "_" + groupIndex + "_" + joinIndex;
preForeignAlias = foreignAlias;
}
joinSb.append(ln()).append(baseIndent);
joinSb.append(" left outer join ").append(foreignTable).append(" ").append(foreignAlias);
joinSb.append(" on ");
final Map columnInfoMap = joinInfo.getLocalForeignColumnInfoMap();
int columnIndex = 0;
for (Entry localForeignEntry : columnInfoMap.entrySet()) {
final ColumnInfo localColumnInfo = localForeignEntry.getKey();
final ColumnInfo foreignColumninfo = localForeignEntry.getValue();
if (columnIndex > 0) {
joinSb.append(" and ");
}
joinSb.append(localAlias).append(".").append(localColumnInfo.getColumnSqlName());
joinSb.append(" = ").append(foreignAlias).append(".").append(foreignColumninfo.getColumnSqlName());
++columnIndex;
}
foreignAliasMap.put(joinInfo, foreignAlias);
relationMap.put(joinInfo, foreignAlias);
++joinIndex;
}
if (optimizedCondition != null) {
optimizedCondition = resolvedOptimizedCondition(optimizedCondition, resource, foreignAliasMap);
}
collectAdditionalRealColumnList(additionalRealColumnList, resolvedFixedCondition, resource, foreignAlias);
++groupIndex;
}
if (optimizedCondition != null) { // foreign alias for in-line view is resolved here
optimizedCondition = replaceString(optimizedCondition, getForeignAliasMark(), baseAlias);
optimizedCondition = filterSubQueryIndentMark(optimizedCondition, false, true);
}
final StringBuilder sqlSb = new StringBuilder();
sqlSb.append("(select ").append(baseAlias).append(".*");
for (String columnName : additionalRealColumnList) {
sqlSb.append(", ").append(columnName);
}
sqlSb.append(ln()).append(baseIndent);
sqlSb.append(" from ").append(foreignTableSqlName).append(" ").append(baseAlias);
sqlSb.append(joinSb);
if (optimizedCondition != null) {
buildOptimizedInlineWhereClause(optimizedCondition, baseIndent, sqlSb);
}
sqlSb.append(ln()).append(baseIndent);
sqlSb.append(")");
return sqlSb.toString();
}
protected String resolvedOptimizedCondition(String optimizedCondition, InlineViewResource resource,
Map foreignAliasMap) {
final Set additionalColumnSet = resource.getAdditionalColumnSet();
if (additionalColumnSet == null) { // basically no way here (but just in case)
return optimizedCondition;
}
Map optimizedReverseColumnMap = null;
for (String columnName : additionalColumnSet) {
final String delimiter = " as ";
if (columnName.contains(delimiter)) {
if (optimizedReverseColumnMap == null) {
optimizedReverseColumnMap = new HashMap(additionalColumnSet.size());
}
// e.g. MEMBER_STATUS_NAME as STATUS
final String physicalName = Srl.substringLastFront(columnName, delimiter);
final String logicalName = Srl.substringLastRear(columnName, delimiter);
optimizedReverseColumnMap.put(logicalName, physicalName); // e.g. STATUS : MEMBER_STATUS_NAME
}
}
optimizedCondition = resolveOptimizedForeignAlias(optimizedCondition, resource, foreignAliasMap);
optimizedCondition = reverseOptimizedColumnAlias(optimizedCondition, optimizedReverseColumnMap);
return optimizedCondition;
}
protected String resolveOptimizedForeignAlias(String optimizedCondition, InlineViewResource resource,
Map foreignAliasMap) {
if (optimizedCondition == null) {
return null;
}
final Map optimizedVariableMap = resource.getOptimizedVariableMap();
if (optimizedVariableMap == null) {
return optimizedCondition;
}
for (Entry entry : optimizedVariableMap.entrySet()) {
final String relationVariable = entry.getKey();
final ForeignInfo foreignInfo = entry.getValue();
final String inlineAlias = foreignAliasMap.get(foreignInfo);
optimizedCondition = replaceString(optimizedCondition, relationVariable, inlineAlias);
}
return optimizedCondition;
}
protected String reverseOptimizedColumnAlias(String optimizedCondition, Map optimizedReverseColumnMap) {
if (optimizedReverseColumnMap != null) {
for (Entry entry : optimizedReverseColumnMap.entrySet()) {
final String logicalName = entry.getKey();
final String physicalName = entry.getValue();
optimizedCondition = replaceString(optimizedCondition, "." + logicalName, "." + physicalName);
}
}
return optimizedCondition;
}
protected void collectAdditionalRealColumnList(List additionalRealColumnList, String resolvedFixedCondition,
InlineViewResource resource, String foreignAlias) {
final Set additionalColumnSet = resource.getAdditionalColumnSet();
if (additionalColumnSet == null) { // basically no way here (but just in case)
return;
}
for (String columnName : additionalColumnSet) {
final String delimiter = " as ";
final String columnMark;
if (columnName.contains(delimiter)) {
final String logicalName = Srl.substringLastRear(columnName, delimiter);
columnMark = "." + logicalName;
} else {
columnMark = "." + columnName;
}
if (resolvedFixedCondition != null && resolvedFixedCondition.contains(columnMark)) {
additionalRealColumnList.add(foreignAlias + "." + columnName);
}
}
}
protected void buildOptimizedInlineWhereClause(String optimizedCondition, String baseIndent, StringBuilder sqlSb) {
sqlSb.append(ln()).append(baseIndent);
sqlSb.append(" where ");
// sub-query marks are already replaced here so it uses indent processor
final String sqBeginMark = SubQueryIndentProcessor.BEGIN_MARK_PREFIX;
final String sqEndMark = SubQueryIndentProcessor.END_MARK_PREFIX;
final List lineList = Srl.splitList(optimizedCondition, ln());
boolean subQueryIndentScope = false;
int index = 0;
for (String line : lineList) {
if (line.contains(sqEndMark)) {
subQueryIndentScope = false;
}
if (index == 0) {
sqlSb.append(line);
} else {
sqlSb.append(ln());
if (!subQueryIndentScope) { // no sub-query: sub-query has own formatting
sqlSb.append(baseIndent);
}
final String trimmedLine = line.trim();
if (trimmedLine.startsWith("and ")) {
sqlSb.append(" ").append(trimmedLine);
} else {
sqlSb.append(line);
}
}
if (line.contains(sqBeginMark)) {
subQueryIndentScope = true;
}
++index;
}
}
// ===================================================================================
// InlineView Class
// ================
protected static class InlineViewResource {
protected Set _additionalColumnSet;
protected List _joinInfoList;
protected Map _optimizedVariableMap;
public Set getAdditionalColumnSet() {
return _additionalColumnSet;
}
public void addAdditionalColumn(String additionalColumn) {
if (_additionalColumnSet == null) {
_additionalColumnSet = new LinkedHashSet();
}
_additionalColumnSet.add(additionalColumn);
}
public boolean hasJoinInfo() {
return _joinInfoList != null && !_joinInfoList.isEmpty();
}
public List getJoinInfoList() {
return _joinInfoList;
}
public void addJoinInfo(ForeignInfo joinInfo) {
if (_joinInfoList == null) {
_joinInfoList = new ArrayList();
}
_joinInfoList.add(joinInfo);
}
public Map getOptimizedVariableMap() {
return _optimizedVariableMap;
}
public void addOptimizedVariable(String relationVariable, ForeignInfo foreignInfo) {
if (_optimizedVariableMap == null) {
_optimizedVariableMap = new HashMap();
}
_optimizedVariableMap.put(relationVariable, foreignInfo);
}
}
// ===================================================================================
// Exception Handling
// ==================
protected void throwIllegalFixedConditionOverRelationException(String notice, String tableName, String relationName,
String fixedCondition) {
throwIllegalFixedConditionOverRelationException(notice, tableName, relationName, fixedCondition, null);
}
protected void throwIllegalFixedConditionOverRelationException(String notice, String pointTable, String targetRelation,
String fixedCondition, Exception e) {
final ExceptionMessageBuilder br = new ExceptionMessageBuilder();
br.addNotice(notice);
br.addItem("Point Table");
br.addElement(pointTable);
br.addItem("Target Relation");
br.addElement(targetRelation);
br.addItem("Fixed Condition");
br.addElement(fixedCondition);
br.addItem("BizOneToOne's Local");
br.addElement(_localCQ.asTableDbName());
final String msg = br.buildExceptionMessage();
throw new FixedConditionIllegalOverRelationException(msg, e);
}
// ===================================================================================
// Determination
// =============
/**
* {@inheritDoc}
*/
public boolean hasOverRelation(String fixedCondition) {
final String relationBeginMark = getRelationBeginMark();
final String relationEndMark = getRelationEndMark();
return Srl.containsAll(fixedCondition, relationBeginMark, relationEndMark);
}
// ===================================================================================
// Variable Mark
// =============
protected String getLocalAliasMark() {
return LOCAL_ALIAS_MARK;
}
protected String getForeignAliasMark() {
return FOREIGN_ALIAS_MARK;
}
protected String getSqBeginMark() {
return SQ_BEGIN_MARK;
}
protected String getSqEndMark() {
return SQ_END_MARK;
}
protected String getInlineMark() {
return INLINE_MARK;
}
protected String getLocationBaseMark() {
return LOCATION_BASE_MARK;
}
protected String getRelationBeginMark() {
return "$$over(";
}
protected String getRelationEndMark() {
return ")$$";
}
protected String getLocalTableMark() {
return "$localTable";
}
protected String getForeignTableMark() {
return "$foreignTable";
}
// ===================================================================================
// Clause Helper
// =============
protected boolean isOneLine(String fixedCondition) {
return !fixedCondition.contains(ln());
}
protected boolean hasUnclosedBrace(String line) {
return line.contains("(") && !line.contains(")");
}
protected boolean startsWithConnector(String line) {
return line.trim().startsWith("and ");
}
protected String removePrefixConnector(String clause) {
return Srl.ltrim(Srl.ltrim(Srl.ltrim(clause), "and "));
}
// ===================================================================================
// General Helper
// ==============
protected final String replaceString(String text, String fromText, String toText) {
return Srl.replace(text, fromText, toText);
}
protected String ln() {
return DBFluteSystem.ln();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy