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