jtulach@355: package org.apidesign.livedb.impl; jtulach@355: jtulach@355: import java.io.IOException; jtulach@355: import java.io.Writer; jtulach@357: import java.sql.CallableStatement; jtulach@357: import java.sql.Connection; jtulach@365: import java.sql.DatabaseMetaData; jtulach@357: import java.sql.Driver; jtulach@357: import java.sql.ResultSet; jtulach@357: import java.sql.ResultSetMetaData; jtulach@357: import java.sql.SQLException; jtulach@365: import java.util.ArrayList; jtulach@365: import java.util.Collections; jtulach@365: import java.util.List; jtulach@365: import java.util.Map.Entry; jtulach@358: import java.util.Properties; jtulach@358: import java.util.ServiceLoader; jtulach@355: import java.util.Set; jtulach@355: import javax.annotation.processing.AbstractProcessor; jtulach@365: import javax.annotation.processing.Completion; jtulach@365: import javax.annotation.processing.Completions; jtulach@363: import javax.annotation.processing.Filer; jtulach@355: import javax.annotation.processing.Processor; jtulach@355: import javax.annotation.processing.RoundEnvironment; jtulach@355: import javax.annotation.processing.SupportedAnnotationTypes; jtulach@355: import javax.annotation.processing.SupportedSourceVersion; jtulach@355: import javax.lang.model.SourceVersion; jtulach@365: import javax.lang.model.element.AnnotationMirror; jtulach@365: import javax.lang.model.element.AnnotationValue; jtulach@355: import javax.lang.model.element.Element; jtulach@365: import javax.lang.model.element.ExecutableElement; jtulach@355: import javax.lang.model.element.PackageElement; jtulach@355: import javax.lang.model.element.TypeElement; jtulach@355: import javax.tools.JavaFileObject; jtulach@355: import org.apidesign.livedb.LiveDB; jtulach@355: import org.openide.util.lookup.ServiceProvider; jtulach@355: jtulach@355: /** jtulach@355: * jtulach@355: * @author Jaroslav Tulach jtulach@355: */ jtulach@361: // BEGIN: livedb.processor jtulach@355: @SupportedAnnotationTypes("org.apidesign.livedb.LiveDB") jtulach@355: @SupportedSourceVersion(SourceVersion.RELEASE_6) jtulach@355: @ServiceProvider(service=Processor.class) jtulach@361: public final class LiveDBProcessor extends AbstractProcessor { jtulach@355: @Override jtulach@363: public boolean process( jtulach@363: Set annotations, RoundEnvironment roundEnv jtulach@363: ) { jtulach@363: final Filer filer = processingEnv.getFiler(); jtulach@355: for (Element e : roundEnv.getElementsAnnotatedWith(LiveDB.class)) { jtulach@355: LiveDB db = e.getAnnotation(LiveDB.class); jtulach@355: PackageElement pe = (PackageElement)e; jtulach@363: String clsName = pe.getQualifiedName() + "." + db.classname(); jtulach@355: try { jtulach@363: JavaFileObject src = filer.createSourceFile(clsName, pe); jtulach@355: Writer w = src.openWriter(); jtulach@363: Connection c = getConnection( jtulach@363: db.url(), db.user(), db.password() jtulach@363: ); jtulach@357: CallableStatement s = c.prepareCall(db.query()); jtulach@357: ResultSet rs = s.executeQuery(); jtulach@357: ResultSetMetaData md = rs.getMetaData(); jtulach@363: generateClassHeader(w, pe, db); jtulach@355: w.append("class " + db.classname() + " {\n"); jtulach@357: for (int i = 1; i <= md.getColumnCount(); i++) { jtulach@363: generateField(w, md, i); jtulach@357: } jtulach@363: generateConstructor(w, db, md); jtulach@363: generateQueryMethod(w, db, md); jtulach@355: w.append("}"); jtulach@355: w.close(); jtulach@355: } catch (IOException ex) { jtulach@355: throw new IllegalStateException(ex); jtulach@357: } catch (SQLException ex) { jtulach@357: throw new IllegalStateException(ex); jtulach@355: } jtulach@355: } jtulach@355: return true; jtulach@355: } jtulach@361: // FINISH: livedb.processor jtulach@363: jtulach@363: private void generateQueryMethod(Writer w, LiveDB db, ResultSetMetaData md) throws SQLException, IOException { jtulach@363: w.append(" public static List<" + db.classname() + "> ") jtulach@363: .append("query() throws SQLException {\n"); jtulach@363: w.append(" Connection c = DriverManager.getConnection(\"") jtulach@363: .append(db.url()).append("\", \"") jtulach@363: .append(db.user()).append("\", \"") jtulach@363: .append(db.password()).append("\");\n"); jtulach@363: w.append(" List<").append(db.classname()) jtulach@363: .append("> res = new ArrayList<") jtulach@363: .append(db.classname()).append(">();\n"); jtulach@363: w.append(" CallableStatement s = c.prepareCall(\"") jtulach@363: .append(db.query()).append("\");\n"); jtulach@363: w.append(" ResultSet rs = s.executeQuery();\n"); jtulach@363: w.append(" ResultSetMetaData md = rs.getMetaData();\n"); jtulach@363: w.append(" while (rs.next()) {\n"); jtulach@363: w.append(" res.add(new " + db.classname() + "(\n"); jtulach@363: for (int i = 1; i <= md.getColumnCount(); i++) { jtulach@363: w.append(" (") jtulach@363: .append(md.getColumnClassName(i)) jtulach@363: .append(")rs.getObject(" + i).append(")"); jtulach@363: if (i < md.getColumnCount()) { jtulach@363: w.append(",\n"); jtulach@363: } else { jtulach@363: w.append("\n"); jtulach@363: } jtulach@363: } jtulach@363: w.append(" ));\n"); jtulach@363: w.append(" };\n"); jtulach@363: w.append(" return res;\n"); jtulach@363: w.append(" }"); jtulach@363: } jtulach@363: jtulach@363: private void generateConstructor(Writer w, LiveDB db, ResultSetMetaData md) throws SQLException, IOException { jtulach@363: w.append(" private " + db.classname() + "(\n"); jtulach@363: for (int i = 1; i <= md.getColumnCount(); i++) { jtulach@363: w.append(" ").append(md.getColumnClassName(i)) jtulach@363: .append(" ").append(md.getColumnName(i)); jtulach@363: if (i < md.getColumnCount()) { jtulach@363: w.append(",\n"); jtulach@363: } else { jtulach@363: w.append("\n"); jtulach@363: } jtulach@363: } jtulach@363: w.append(" ) {\n"); jtulach@363: for (int i = 1; i <= md.getColumnCount(); i++) { jtulach@363: w.append(" this.") jtulach@363: .append(md.getColumnName(i)) jtulach@363: .append(" = ") jtulach@363: .append(md.getColumnName(i)) jtulach@363: .append(";\n"); jtulach@363: } jtulach@363: w.append(" }\n"); jtulach@363: } jtulach@363: jtulach@363: private void generateField(Writer w, ResultSetMetaData md, int i) throws IOException, SQLException { jtulach@363: w.append(" public final ") jtulach@363: .append(md.getColumnClassName(i)) jtulach@363: .append(" ") jtulach@363: .append(md.getColumnName(i)) jtulach@363: .append(";\n"); jtulach@363: } jtulach@363: jtulach@363: private void generateClassHeader(Writer w, PackageElement pe, LiveDB db) throws IOException { jtulach@363: w.append("package " + pe.getQualifiedName() + ";\n"); jtulach@363: w.append("import java.util.List;\n"); jtulach@363: w.append("import java.util.ArrayList;\n"); jtulach@363: w.append("import java.sql.*;\n"); jtulach@363: } jtulach@358: private static Connection getConnection(String url, String user, String password) jtulach@358: throws SQLException { jtulach@358: final ClassLoader cl = LiveDBProcessor.class.getClassLoader(); jtulach@358: for (Driver d : ServiceLoader.load(Driver.class, cl)) { jtulach@358: // System.out.println("looked up: " + d); jtulach@358: if (d.acceptsURL(url)) { jtulach@358: //System.out.println("accepts: " + d); jtulach@358: Properties p = new Properties(); jtulach@358: p.put("user", user); jtulach@358: p.put("password", password); jtulach@358: return d.connect(url, p); jtulach@358: } jtulach@357: } jtulach@358: throw new SQLException("No driver found for " + url); jtulach@357: } jtulach@365: jtulach@365: jtulach@365: jtulach@365: // BEGIN: livedb.completions jtulach@365: @Override jtulach@365: public Iterable getCompletions( jtulach@365: Element element, AnnotationMirror annotation, jtulach@365: ExecutableElement member, String userText jtulach@365: ) { jtulach@365: if (!"query".equals(member.getSimpleName().toString())) { jtulach@365: return Collections.emptyList(); jtulach@365: } jtulach@365: if (userText == null || userText.length() <= 1) { jtulach@365: return Collections.singleton(Completions.of("\"SELECT ")); jtulach@365: } jtulach@365: if (userText.toUpperCase().matches(".*FROM *")) { jtulach@365: String user = extractValue(annotation, "user"); jtulach@365: String password = extractValue(annotation, "password"); jtulach@365: String url = extractValue(annotation, "url"); jtulach@365: if (user == null || password == null || url == null) { jtulach@365: return Collections.emptyList(); jtulach@365: } jtulach@365: try { jtulach@365: List arr = new ArrayList(); jtulach@365: Connection c = getConnection(url, user, password); jtulach@365: DatabaseMetaData meta = c.getMetaData(); jtulach@365: ResultSet res = meta.getTables(null, null, "%", null); jtulach@365: boolean ok = res.first(); jtulach@365: while (ok) { jtulach@365: String txt = userText + res.getString("TABLE_NAME"); jtulach@365: arr.add(Completions.of(txt)); jtulach@365: ok = res.next(); jtulach@365: } jtulach@365: return arr; jtulach@365: } catch (SQLException ex) { jtulach@365: throw new IllegalStateException(ex); jtulach@365: } jtulach@365: } jtulach@365: return Collections.emptyList(); jtulach@365: } jtulach@365: // END: livedb.completions jtulach@365: jtulach@365: private static String extractValue(AnnotationMirror am, String param) { jtulach@365: AnnotationValue av = null; jtulach@365: for (Entry entry : am.getElementValues().entrySet()) { jtulach@365: if (entry.getKey().toString().equals(param + "()")) { jtulach@365: av = entry.getValue(); jtulach@365: break; jtulach@365: } jtulach@365: } jtulach@365: if (av == null) { jtulach@365: return null; jtulach@365: } jtulach@365: String s = av.toString(); jtulach@365: if (s.startsWith("\"")) { jtulach@365: s = s.substring(1); jtulach@365: } jtulach@365: if (s.endsWith("\"")) { jtulach@365: s = s.substring(0, s.length() - 1); jtulach@365: } jtulach@365: return s; jtulach@365: } jtulach@355: }