SQLC.java
/***************************************************************************
Copyright 2008 Emily Estes
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 net.metanotion.sqlc;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.metanotion.util.reflect.GetInitializer;
import net.metanotion.scripting.Struct;
import net.metanotion.scripting.StructManager;
import net.metanotion.sqlc.setters.*;
/** SQL Compiler.
*/
public final class SQLC {
private static final Logger logger = LoggerFactory.getLogger(SQLC.class);
private static File getFilePath(final String outPath, final String[] packageName) {
String outFolder = outPath;
for(final String pe: packageName) { outFolder += File.separator + pe; }
logger.debug("::" + outFolder + " - " + packageName.length);
return new File(outFolder);
}
private static String mkPath(final String outPath, final String[] packageName, final String name) {
final File f = getFilePath(outPath, packageName);
f.mkdirs();
// String outFolder = outPath;
// for(final String pe: packageName) { outFolder += File.separator + pe; }
return (f.toString() + File.separator + name + ".java");
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final DoWrap qe,
final int level,
final int[] gensym,
final int[] braces) {
for(final QueryExpr e: (List<QueryExpr>) qe.exprs) {
makeMethod(writer, m, e, level + 1, gensym, braces);
}
if(level == 0) {
writer.println("\t\t\treturn _" + (gensym[0] - 1) + ";");
while(braces[0] > 0) {
writer.println("}");
braces[0]--;
}
}
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final ListWrap qe,
final int level,
final int[] gensym,
final int[] braces) {
makeMethod(writer, m, qe.e, level + 1, gensym, braces);
final int rsVar = gensym[0] - 1;
if( (qe.e.exprs.get(qe.e.exprs.size() - 1) instanceof Query) ||
(qe.e.exprs.get(qe.e.exprs.size() - 1) instanceof QueryMacro)) {
// convert result set to list.
final int list = gensym[0];
gensym[0]++;
writer.println("\t\t\tfinal java.util.List<" + m.finalType + "> _" + list
+ " = new java.util.ArrayList<" + m.finalType + ">();");
if(qe.g instanceof StructGetter) {
final StructGetter sg = (StructGetter) qe.g;
final int gi = gensym[0];
final int init = gensym[0] + 1;
gensym[0]+=2;
writer.println("\t\tfinal net.metanotion.util.reflect.GetInitializer<" + sg.struct + "> _" + gi
+ " = net.metanotion.util.reflect.ReflectiveFieldInitializer.getInitializer("
+ sg.struct + ".class, this.types);");
writer.println("\t\twhile(_" + rsVar + ".next()) {");
writer.println("\t\t\t\t\tfinal net.metanotion.util.reflect.Initializer<" + sg.struct + "> _" + init
+ " = _" + gi + ".initializer();");
for(final RSGetter rg: sg.fields) {
writer.println("\t\t\t\t\t_" + init + ".put(\"" + rg.name + "\", " + rg.getStatic("_" + rsVar) + ");");
}
writer.println("\t\t\t\t\t_" + list + ".add(_" + init + ".instance());");
} else if(qe.g instanceof ValueGetter) {
writer.println("\t\twhile(_" + rsVar + ".next()) {");
final RSGetter rg = ((ValueGetter) qe.g).field;
writer.println("\t\t\t\t\t_" + list + ".add(" + rg.getStatic("_" + rsVar, 1) + ");");
}
writer.println("\t\t}");
writer.println("\t\t\treturn _" + list + ";");
while(braces[0] > 0) {
writer.println("}");
braces[0]--;
}
} else {
throw new RuntimeException("Value[ ] type can only apply to queries not updates on function '" + m.name + "'");
}
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final Query qe,
final int level,
final int[] gensym,
final int[] braces) {
final int stmt = gensym[0];
gensym[0]++;
writer.println("\t\t\ttry (final java.sql.PreparedStatement _" + stmt
+ " = _0.prepareStatement(\"" + StringEscapeUtils.escapeJava(qe.sql.trim()) + "\")) {");
for(final SQLSetter s: qe.setters) {
int i=0;
for(int j=0;j < m.pList.length; j++) {
if(s.name.equals(m.pList[j])) {
i = j + 1;
break;
}
}
writer.println("\t\t\t" + s.setStatic("_" + stmt, "_" + i) + ";");
}
writer.println("\t\t\ttry (final java.sql.ResultSet _" + gensym[0] + " = _" + stmt + ".executeQuery()) {");
braces[0] = braces[0] + 2;
gensym[0]++;
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final QueryMacro qe,
final int level,
final int[] gensym,
final int[] braces) {
final int sb = gensym[0];
gensym[0]++;
final int stmt = gensym[0];
gensym[0]++;
writer.println("\t\t\tfinal StringBuilder _" + sb + " = new StringBuilder();");
for(final SQLElement e: qe.statement) {
if(e instanceof SEConstant) {
writer.println("\t\t\t_" + sb + ".append(\"" + StringEscapeUtils.escapeJava(((SEConstant) e).sql) + "\");");
} else {
final String vName = ((SEMacro) e).name;
for(int i=0; i < m.pList.length; i++) {
if(m.pList[i].equals(vName)) {
writer.println("\t\t\t_" + sb + ".append(_" + (i + 1) + ".toString());");
}
}
}
}
writer.println("\t\t\ttry (final java.sql.PreparedStatement _" + stmt
+ " = _0.prepareStatement(_" + sb + ".toString())) {");
for(final SQLSetter s: qe.setters) {
int i=0;
for(int j=0;j < m.pList.length; j++) {
if(s.name.equals(m.pList[j])) {
i = j + 1;
break;
}
}
writer.println("\t\t\t" + s.setStatic("_" + stmt, "_" + i) + ";");
}
writer.println("\t\t\ttry (final java.sql.ResultSet _" + gensym[0] + " = _" + stmt + ".executeQuery()) {");
braces[0] = braces[0] + 2;
gensym[0]++;
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final TXWrap qe,
final int level,
final int[] gensym,
final int[] braces) {
writer.println("\t\ttry {");
writer.println("\t\t\t_0.setAutoCommit(false);");
makeMethod(writer, m, qe.expr, level + 1, gensym, braces);
if(!(qe.expr instanceof VoidWrap)) {
writer.println("\t\t\treturn _" + (gensym[0] - 1) + ";");
}
while(braces[0] > 0) {
writer.println("}");
braces[0]--;
}
writer.println("\t\t} catch (final java.sql.SQLException sqle) {");
writer.println("\t\t\t_0.rollback();");
writer.println("\t\t\tthrow sqle;");
writer.println("\t\t} finally {");
writer.println("\t\t\t_0.setAutoCommit(true);");
writer.println("\t\t}");
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final Update qe,
final int level,
final int[] gensym,
final int[] braces) {
final int stmt = gensym[0];
gensym[0]++;
writer.println("\t\t\ttry (final java.sql.PreparedStatement _" + stmt
+ " = _0.prepareStatement(\"" + StringEscapeUtils.escapeJava(qe.sql.trim()) + "\")) {");
for(final SQLSetter s: qe.setters) {
int i=0;
for(int j=0;j < m.pList.length; j++) {
if(s.name.equals(m.pList[j])) {
i = j + 1;
break;
}
}
writer.println("\t\t\t" + s.setStatic("_" + stmt, "_" + i) + ";");
}
writer.println("\t\t\tfinal int _" + gensym[0] + " = _" + stmt + ".executeUpdate();");
braces[0]++;
gensym[0]++;
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final UpdateMacro qe,
final int level,
final int[] gensym,
final int[] braces) {
// TODO
throw new RuntimeException("Update Macros not supported yet.");
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final ValueWrap qe,
final int level,
final int[] gensym,
final int[] braces) {
makeMethod(writer, m, qe.e, level + 1, gensym, braces);
final int rsVar = gensym[0] - 1;
if( (qe.e.exprs.get(qe.e.exprs.size() - 1) instanceof Query) ||
(qe.e.exprs.get(qe.e.exprs.size() - 1) instanceof QueryMacro)) {
writer.println("\t\t_" + rsVar + ".next();");
if(qe.g instanceof StructGetter) {
final StructGetter sg = (StructGetter) qe.g;
final int gi = gensym[0];
final int init = gensym[0] + 1;
gensym[0]+=2;
writer.println("\t\tfinal net.metanotion.util.reflect.GetInitializer<" + sg.struct + "> _" + gi
+ " = net.metanotion.util.reflect.ReflectiveFieldInitializer.getInitializer("
+ sg.struct + ".class, this.types);");
writer.println("\t\tfinal net.metanotion.util.reflect.Initializer<" + sg.struct + "> _" + init
+ " = _" + gi + ".initializer();");
for(final RSGetter rg: sg.fields) {
writer.println("\t\t_" + init + ".put(\"" + rg.name + "\", " + rg.getStatic("_" + rsVar) + ");");
}
writer.println("\t\treturn _" + init + ".instance();");
} else if(qe.g instanceof ValueGetter) {
final RSGetter rg = ((ValueGetter) qe.g).field;
writer.println("\t\t\treturn " + rg.getStatic("_" + rsVar, 1) + ";");
}
while(braces[0] > 0) {
writer.println("}");
braces[0]--;
}
} else {
throw new RuntimeException("Value[ ] type can only apply to queries not updates on function '" + m.name + "'");
}
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final VoidWrap qe,
final int level,
final int[] gensym,
final int[] braces) {
makeMethod(writer, m, qe.e, level + 1, gensym, braces);
}
public static void makeMethod(final PrintWriter writer,
final SQLMethod m,
final QueryExpr qe,
final int level,
final int[] gensym,
final int[] braces) {
if(qe instanceof DoWrap) {
makeMethod(writer, m, (DoWrap) qe, level, gensym, braces);
} else if(qe instanceof ListWrap) {
makeMethod(writer, m, (ListWrap) qe, level, gensym, braces);
} else if(qe instanceof Query) {
makeMethod(writer, m, (Query) qe, level, gensym, braces);
} else if(qe instanceof QueryMacro) {
makeMethod(writer, m, (QueryMacro) qe, level, gensym, braces);
} else if(qe instanceof TXWrap) {
makeMethod(writer, m, (TXWrap) qe, level, gensym, braces);
} else if(qe instanceof Update) {
makeMethod(writer, m, (Update) qe, level, gensym, braces);
} else if(qe instanceof UpdateMacro) {
makeMethod(writer, m, (UpdateMacro) qe, level, gensym, braces);
} else if(qe instanceof ValueWrap) {
makeMethod(writer, m, (ValueWrap) qe, level, gensym, braces);
} else if(qe instanceof VoidWrap) {
makeMethod(writer, m, (VoidWrap) qe, level, gensym, braces);
} else {
throw new RuntimeException("Invalid QueryExpr type");
}
}
private static boolean needToGenerateStruct(final String clsName,
final GetInitializer gi,
final Iterable<String> srcPathes) {
logger.debug("Checking {}", clsName);
if(!(gi instanceof Struct)) { return false; }
final String[] name = clsName.split("\\.");
final String[] pkg = new String[name.length - 1];
for(int i=0;i<pkg.length;i++) {
pkg[i] = name[i];
}
for(final String path: srcPathes) {
try {
final File javaSrc = new File(getFilePath(path, pkg).toString() + File.separator + name[pkg.length] + ".java");
logger.debug(" Trying {} with {}", path, javaSrc);
if(javaSrc.exists()) {
logger.debug(" Exists!");
return false;
}
} catch (Exception e) {
logger.debug("fail", e);
}
}
return true;
}
public static void compile(final String outputFolder,
final SQLClass q,
final StructManager sm,
final Iterable<String> srcPathes,
final Set<String> implicits,
final boolean generateStructs) throws IOException {
final String visibility = q.isPublic() ? "public ": " ";
final String[] name = q.getName().split("\\.");
final String[] pkg = new String[name.length - 1];
for(int i=0;i<pkg.length;i++) {
pkg[i] = name[i];
}
logger.debug("package name:" + q.getName() + " - " + name.length);
try (final FileOutputStream fos = new FileOutputStream(mkPath(outputFolder, pkg, name[pkg.length]))) {
try (final PrintWriter writer = new PrintWriter(new OutputStreamWriter(fos, "UTF-8"))) {
if(pkg.length > 0) {
writer.print("package ");
String sep = "";
for(final String pe: pkg) {
writer.print(sep + pe);
sep = ".";
}
writer.println(";");
writer.println("");
}
writer.println("/** <i>This is a SQL query class generated by the SQLC compiler.</i> */");
writer.println("@javax.annotation.Generated(\"net.metanotion.sqlc.SQLC\") "
+ visibility + "final class " + name[pkg.length] +" {");
writer.println("\tprivate final net.metanotion.util.Dictionary<Class,net.metanotion.util.types.Parser> types;");
writer.println("\tpublic " + name[pkg.length]
+ "() { this.types = new net.metanotion.util.types.TypeDictionary(); }");
writer.println("\tpublic " + name[pkg.length]
+ "(net.metanotion.util.Dictionary<Class,net.metanotion.util.types.Parser> types) { this.types = types; }");
for(final SQLMethod m: q.getMethods()) {
final int[] gensym = new int[] { 1 };
final String type = (m.modifier == null) ? m.finalType : (m.modifier + "<" + m.finalType + ">");
writer.print("\tpublic " + type + " " + m.name + "(final java.sql.Connection _0");
for(final String p: m.pList) {
writer.print(", final @javax.inject.Named(\"" + p + "\") " + m.parameterTypes.get(p) + " _" + gensym[0]);
gensym[0]++;
}
writer.println(") throws Exception {");
makeMethod(writer, m, m.body, 0, gensym, new int[] { 0 });
writer.println("\t}");
}
writer.println("}");
}
}
if(!generateStructs && implicits.size() == 0) { return; }
generateStructs(outputFolder, sm, implicits, visibility, srcPathes, generateStructs);
}
private static void generateStructs(final String outputFolder,
final StructManager sm,
final Set<String> implicits,
final String visibility,
final Iterable<String> srcPathes,
final boolean generateStructs) throws IOException {
for(final Map.Entry<String,GetInitializer> e: sm) {
if((!generateStructs) && (!implicits.contains(e.getKey()))) { continue; }
final GetInitializer gi = e.getValue();
if(needToGenerateStruct(e.getKey(), gi, srcPathes)) {
final Struct s = (Struct) gi;
final String[] name = e.getKey().split("\\.");
final String[] pkg = new String[name.length - 1];
for(int i=0;i<pkg.length;i++) {
pkg[i] = name[i];
}
final FileOutputStream fos = new FileOutputStream(mkPath(outputFolder, pkg, name[pkg.length]));
final PrintWriter writer = new PrintWriter(new OutputStreamWriter(fos, "UTF-8"));
if(pkg.length > 0) {
writer.print("package ");
String sep = "";
for(String pe: pkg) {
writer.print(sep + pe);
sep = ".";
}
writer.println(";");
writer.println("");
}
writer.println("/** <i>This is a data/struct/value class generated by the SQLC compiler.</i> */");
writer.println("@javax.annotation.Generated(\"net.metanotion.sqlc.SQLC\") "
+ visibility + "final class " + name[pkg.length] +" {");
for(final String p: s.listProperties()) {
writer.print("\tpublic ");
writer.print(s.getType(p));
writer.println(" " + p + ";");
}
writer.println("}");
writer.close();
fos.close();
}
}
}
public static void processFile(final Load loader,
final String outputFolder,
final String sqlFileName,
final Iterable<String> srcPathes,
final boolean generateStructs) throws Exception {
final StructManager sm = new StructManager();
final HashSet<String> implicits = new HashSet<>();
final SQLClass q = loader.load(new FileInputStream(sqlFileName), sm, implicits);
SQLC.compile(outputFolder, q, sm, srcPathes, implicits, generateStructs);
}
public static void treewalk(final File folder,
final Load loader,
final String outputFolder,
final Iterable<String> srcPathes,
final boolean generateStructs) throws Exception {
for(final File file: folder.listFiles()) {
if(file.isFile()) {
final String sqlFileName = file.toString();
final int len = sqlFileName.length();
if((len > 4) && (".sql".equals(sqlFileName.substring(len - 4, len).toLowerCase()))) {
processFile(loader, outputFolder, sqlFileName, srcPathes, generateStructs);
}
} else if(file.isDirectory()) {
treewalk(file, loader, outputFolder, srcPathes, generateStructs);
}
}
}
public static void main(final String[] args) throws Exception {
final Load loader = new Load();
final String outputFolder = args[0];
final String sqlFileName = args[1];
final boolean generateStructs = args.length > 2;
Iterable<String> srcPathes = java.util.Collections.emptyList();
if(args.length > 3) {
srcPathes = java.util.Arrays.asList(args[3].split(File.pathSeparator));
}
final File f = new File(sqlFileName);
if(f.isDirectory()) {
treewalk(f, loader, outputFolder, srcPathes, generateStructs);
} else {
processFile(loader, outputFolder, sqlFileName, srcPathes, generateStructs);
}
}
}