/**
* Copyright (C) 2012 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 co.jirm.orm.writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.text.StrLookup;
import org.apache.commons.lang3.text.StrSubstitutor;
import static co.jirm.core.util.JirmPrecondition.check;
import co.jirm.mapper.definition.SqlObjectDefinition;
import co.jirm.mapper.definition.SqlParameterDefinition;
import co.jirm.mapper.definition.SqlParameterObjectDefinition;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
public class SqlWriterStrategy {
protected final Joiner commaJoiner;
protected final String clauseSpaceSeparator;
protected SqlWriterStrategy(Joiner commaJoiner, String clauseSpaceSeparator) {
super();
this.commaJoiner = commaJoiner;
this.clauseSpaceSeparator = clauseSpaceSeparator;
}
public static SqlWriterStrategy newInstance(String sep) {
return new SqlWriterStrategy(Joiner.on("," + sep),sep);
}
public StringBuilder insertStatement(StringBuilder qb, final SqlObjectDefinition<?> definition, Map<String, Object> m) {
qb.append("INSERT INTO ").append(definition.getSqlName()).append(" (");
Joiner.on(", ").appendTo(qb, insertColumns(definition, m));
qb.append(") VALUES (");
Joiner.on(", ").appendTo(qb, valueMarks(m.values()));
qb.append(")");
return qb;
}
public StringBuilder selectStatementBeforeWhere(StringBuilder sb, final SqlObjectDefinition<?> definition) {
//sb.append(clauseSpaceSeparator);
sb.append("SELECT ");
selectParameters(sb, definition);
sb.append(clauseSpaceSeparator);
sb.append("FROM ").append(definition.getSqlName());
innerJoin(sb, definition);
return sb;
}
private Collection<String> sqlParameterNames(final SqlObjectDefinition<?> definition, final String tablePrefix, final String as) {
Collection<String> it = Collections2.transform(definition.getSimpleParameters().values(), new Function<SqlParameterDefinition, String>() {
@Override
public String apply(SqlParameterDefinition input) {
return input.sqlName(tablePrefix) + ((as == null) ? "" : " AS \"" + as + input.getParameterName() + "\"");
}
});
return it;
}
private void sqlSelectParameters(
List<String> parts, String prefix,
String as, SqlParameterDefinition parameter,
SqlParameterObjectDefinition od, int depth) {
parts.addAll(sqlParameterNames(od.getObjectDefintion(),prefix, as));
if (depth >= od.getMaximumLoadDepth()) return;
for (Entry<String, SqlParameterDefinition> defs : od.getObjectDefintion().getManyToOneParameters().entrySet()) {
String childAlias = prefix+"_"+ defs.getValue().getParameterName();
String newAs = as + defs.getValue().getParameterName() + ".";
sqlSelectParameters(parts, childAlias, newAs, defs.getValue(), defs.getValue().getObjectDefinition().get(), depth + 1);
}
}
public void selectParameters(StringBuilder b, SqlObjectDefinition<?> definition) {
List<String> params = Lists.newArrayList(sqlParameterNames(definition, definition.getSqlName(), null));
for (Entry<String, SqlParameterDefinition> defs : definition.getManyToOneParameters().entrySet()) {
String as = defs.getValue().getParameterName() + ".";
sqlSelectParameters(params, "_" + defs.getValue().getParameterName(), as, defs.getValue(), defs.getValue().getObjectDefinition().get(), 1);
}
commaJoiner.appendTo(b, params);
}
private void innerJoin(StringBuilder b, String parent, String prefix, SqlParameterDefinition parameter, SqlParameterObjectDefinition od, int depth) {
SqlParameterDefinition pd = od.getObjectDefintion().idParameter().get();
b.append(clauseSpaceSeparator).append("INNER JOIN ").append(od.getObjectDefintion().getSqlName()).append(" ").append(prefix)
.append(" ON ")
.append(pd.sqlName(prefix))
.append(" = ")
.append(parameter.sqlName(parent));
if (depth >= od.getMaximumLoadDepth()) return;
for (Entry<String, SqlParameterDefinition> defs : od.getObjectDefintion().getManyToOneParameters().entrySet()) {
String childAlias = prefix+"_"+defs.getValue().getParameterName();
SqlParameterDefinition childParameter = defs.getValue();
SqlParameterObjectDefinition childObjectDef = defs.getValue().getObjectDefinition().get();
innerJoin(b, prefix, childAlias, childParameter, childObjectDef, depth + 1);
}
}
public void innerJoin(StringBuilder b, SqlObjectDefinition<?> definition) {
for (Entry<String, SqlParameterDefinition> defs : definition.getManyToOneParameters().entrySet()) {
innerJoin(b, definition.getSqlName(), "_" + defs.getValue().getParameterName(),
defs.getValue(), defs.getValue().getObjectDefinition().get(), 1);
}
}
public StringBuilder deleteStatementBeforeWhere(StringBuilder sb, final SqlObjectDefinition<?> definition) {
sb.append("DELETE FROM ").append(definition.getSqlName());
return sb;
}
public StringBuilder updateStatementBeforeSet(StringBuilder sb, final SqlObjectDefinition<?> definition) {
sb.append("UPDATE ").append(definition.getSqlName());
return sb;
}
protected List<String> insertColumns(SqlObjectDefinition<?> definition, Map<String, Object> m) {
List<String> equalsKeys = new ArrayList<String>();
for (Entry<String, Object> e : m.entrySet()) {
Optional<String> sqlName = definition.parameterNameToSql(e.getKey());
check.state(sqlName.isPresent(),
"Property: {} not found in object.", e.getKey());
equalsKeys.add(sqlName.get());
}
return equalsKeys;
}
public void appendValues(List<Object> values, final SqlObjectDefinition<?> definition, Map<String, Object> keysAndValues) {
for (Entry<String, Object> e : keysAndValues.entrySet()) {
SqlParameterDefinition d = definition.getParameters().get(e.getKey());
if (d == null) {
values.add(e.getValue());
}
else {
values.add(d.convertToSql(e.getValue()));
}
}
}
public List<Object> fillValues(final SqlObjectDefinition<?> definition, Map<String, Object> keysAndValues) {
List<Object> values = Lists.newArrayListWithCapacity(keysAndValues.size());
appendValues(values, definition, keysAndValues);
return values;
}
protected List<String> valueMarks(Iterable<Object> values) {
List<String> marks = new ArrayList<String>();
for (@SuppressWarnings("unused") Object v : values) {
marks.add("?");
}
return marks;
}
public String replacePropertyPaths(final SqlObjectDefinition<?> definition, final String sql) {
StrLookup<String> lookup = new StrLookup<String>() {
@Override
public String lookup(String key) {
Optional<String> p = parameterPathToSql(definition,key);
check.state(p.isPresent(),
"Invalid object path: {}", key);
return p.get();
}
};
StrSubstitutor s = new StrSubstitutor(lookup, "{{", "}}", '$');
String result = s.replace(sql);
return result;
}
public String replaceProperties(final SqlObjectDefinition<?> definition, final String sql) {
StrLookup<String> lookup = new StrLookup<String>() {
@Override
public String lookup(String key) {
Optional<String> sqlName = definition.parameterNameToSql(key);
check.state(sqlName.isPresent(), "Property: {} not found in object.", key);
return sqlName.get();
}
};
StrSubstitutor s = new StrSubstitutor(lookup, "{{", "}}", '$');
String result = s.replace(sql);
return result;
}
public Optional<String> parameterPathToSql(final SqlObjectDefinition<?> definition, String parameterDotPath) {
List<SqlParameterDefinition> parts = definition.resolveParameterPath(parameterDotPath);
if (parts.isEmpty()) return Optional.absent();
StringBuilder sb = new StringBuilder();
boolean first = true;
for (SqlParameterDefinition p : parts) {
if (first) {
first = false;
if ( ! p.isComplex() )
sb.append(definition.getSqlName());
}
if (p.isComplex()) {
sb.append("_").append(p.getParameterName());
}
else {
sb.append(".").append(p.sqlName());
}
}
return Optional.of(sb.toString());
}
// public String selectConjuntionQuery() {
// StringBuilder qb = new StringBuilder();
// qb.append("SELECT ");
// definition.selectParameters(qb);
// qb.append(" FROM ").append(definition.getSqlName());
// definition.innerJoin(qb);
// qb.append(" WHERE ").append(this.andClause());
// if (limit.isPresent()) {
// qb.append(" ");
// qb.append("LIMIT ?");
// }
// if (offset.isPresent()) {
// qb.append(" ");
// qb.append("OFFSET ?");
// }
// String q = qb.toString();
// return q;
// }
//
}