samples/livedb/src/org/apidesign/livedb/impl/LiveDBProcessor.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 30 Oct 2014 21:30:10 +0100
changeset 409 40cabcdcd2be
parent 363 cbeeeb6ae9c6
permissions -rw-r--r--
Updating to NBMs from NetBeans 8.0.1 as some of them are required to run on JDK8
     1 package org.apidesign.livedb.impl;
     2 
     3 import java.io.IOException;
     4 import java.io.Writer;
     5 import java.sql.CallableStatement;
     6 import java.sql.Connection;
     7 import java.sql.DatabaseMetaData;
     8 import java.sql.Driver;
     9 import java.sql.ResultSet;
    10 import java.sql.ResultSetMetaData;
    11 import java.sql.SQLException;
    12 import java.util.ArrayList;
    13 import java.util.Collections;
    14 import java.util.List;
    15 import java.util.Map.Entry;
    16 import java.util.Properties;
    17 import java.util.ServiceLoader;
    18 import java.util.Set;
    19 import javax.annotation.processing.AbstractProcessor;
    20 import javax.annotation.processing.Completion;
    21 import javax.annotation.processing.Completions;
    22 import javax.annotation.processing.Filer;
    23 import javax.annotation.processing.Processor;
    24 import javax.annotation.processing.RoundEnvironment;
    25 import javax.annotation.processing.SupportedAnnotationTypes;
    26 import javax.annotation.processing.SupportedSourceVersion;
    27 import javax.lang.model.SourceVersion;
    28 import javax.lang.model.element.AnnotationMirror;
    29 import javax.lang.model.element.AnnotationValue;
    30 import javax.lang.model.element.Element;
    31 import javax.lang.model.element.ExecutableElement;
    32 import javax.lang.model.element.PackageElement;
    33 import javax.lang.model.element.TypeElement;
    34 import javax.tools.JavaFileObject;
    35 import org.apidesign.livedb.LiveDB;
    36 import org.openide.util.lookup.ServiceProvider;
    37 
    38 /**
    39  *
    40  * @author Jaroslav Tulach <jtulach@netbeans.org>
    41  */
    42 // BEGIN: livedb.processor
    43 @SupportedAnnotationTypes("org.apidesign.livedb.LiveDB")
    44 @SupportedSourceVersion(SourceVersion.RELEASE_6)
    45 @ServiceProvider(service=Processor.class)
    46 public final class LiveDBProcessor extends AbstractProcessor {
    47     @Override
    48     public boolean process(
    49         Set<? extends TypeElement> annotations, RoundEnvironment roundEnv
    50     ) {
    51         final Filer filer = processingEnv.getFiler();
    52         for (Element e : roundEnv.getElementsAnnotatedWith(LiveDB.class)) {
    53             LiveDB db = e.getAnnotation(LiveDB.class);
    54             PackageElement pe = (PackageElement)e;
    55             String clsName = pe.getQualifiedName() + "." + db.classname();
    56             try {
    57                 JavaFileObject src = filer.createSourceFile(clsName, pe);
    58                 Writer w = src.openWriter();
    59                 Connection c = getConnection(
    60                     db.url(), db.user(), db.password()
    61                 );
    62                 CallableStatement s = c.prepareCall(db.query());
    63                 ResultSet rs = s.executeQuery();
    64                 ResultSetMetaData md = rs.getMetaData();
    65                 generateClassHeader(w, pe, db);
    66                 w.append("class " + db.classname() + " {\n");
    67                 for (int i = 1; i <= md.getColumnCount(); i++) {
    68                     generateField(w, md, i);
    69                 }
    70                 generateConstructor(w, db, md);
    71                 generateQueryMethod(w, db, md);
    72                 w.append("}");
    73                 w.close();
    74             } catch (IOException ex) {
    75                 throw new IllegalStateException(ex);
    76             } catch (SQLException ex) {
    77                 throw new IllegalStateException(ex);
    78             }
    79         }
    80         return true;
    81     }
    82 // FINISH: livedb.processor
    83 
    84     private void generateQueryMethod(Writer w, LiveDB db, ResultSetMetaData md) throws SQLException, IOException {
    85         w.append("  public static List<" + db.classname() + "> ")
    86          .append("query() throws SQLException {\n");
    87         w.append("    Connection c = DriverManager.getConnection(\"")
    88          .append(db.url()).append("\", \"")
    89          .append(db.user()).append("\", \"")
    90          .append(db.password()).append("\");\n");
    91         w.append("    List<").append(db.classname())
    92          .append("> res = new ArrayList<")
    93          .append(db.classname()).append(">();\n");
    94         w.append("    CallableStatement s = c.prepareCall(\"")
    95          .append(db.query()).append("\");\n");
    96         w.append("    ResultSet rs = s.executeQuery();\n");
    97         w.append("    ResultSetMetaData md = rs.getMetaData();\n");
    98         w.append("    while (rs.next()) {\n");
    99         w.append("      res.add(new " + db.classname() + "(\n");
   100         for (int i = 1; i <= md.getColumnCount(); i++) {
   101             w.append("        (")
   102              .append(md.getColumnClassName(i))
   103              .append(")rs.getObject(" + i).append(")");
   104             if (i < md.getColumnCount()) {
   105                 w.append(",\n");
   106             } else {
   107                 w.append("\n");
   108             }
   109         }
   110         w.append("      ));\n");
   111         w.append("    };\n");
   112         w.append("    return res;\n");
   113         w.append("  }");
   114     }
   115 
   116     private void generateConstructor(Writer w, LiveDB db, ResultSetMetaData md) throws SQLException, IOException {
   117         w.append("  private " + db.classname() + "(\n");
   118         for (int i = 1; i <= md.getColumnCount(); i++) {
   119             w.append("    ").append(md.getColumnClassName(i))
   120              .append(" ").append(md.getColumnName(i));
   121             if (i < md.getColumnCount()) {
   122                 w.append(",\n");
   123             } else {
   124                 w.append("\n");
   125             }
   126         }
   127         w.append("  ) {\n");
   128         for (int i = 1; i <= md.getColumnCount(); i++) {
   129             w.append("    this.")
   130              .append(md.getColumnName(i))
   131              .append(" = ")
   132              .append(md.getColumnName(i))
   133              .append(";\n");
   134         }
   135         w.append("  }\n");
   136     }
   137 
   138     private void generateField(Writer w, ResultSetMetaData md, int i) throws IOException, SQLException {
   139         w.append("  public final ")
   140          .append(md.getColumnClassName(i))
   141          .append(" ")
   142          .append(md.getColumnName(i))
   143          .append(";\n");
   144     }
   145 
   146     private void generateClassHeader(Writer w, PackageElement pe, LiveDB db) throws IOException {
   147         w.append("package " + pe.getQualifiedName() + ";\n");
   148         w.append("import java.util.List;\n");
   149         w.append("import java.util.ArrayList;\n");
   150         w.append("import java.sql.*;\n");
   151     }
   152     private static Connection getConnection(String url, String user, String password) 
   153     throws SQLException {
   154         final ClassLoader cl = LiveDBProcessor.class.getClassLoader();
   155         for (Driver d : ServiceLoader.load(Driver.class, cl)) {
   156 //            System.out.println("looked up: " + d);
   157             if (d.acceptsURL(url)) {
   158                 //System.out.println("accepts: " + d);
   159                 Properties p = new Properties();
   160                 p.put("user", user);
   161                 p.put("password", password);
   162                 return d.connect(url, p);
   163             }
   164         }
   165         throw new SQLException("No driver found for " + url);
   166     }
   167 
   168     
   169     
   170     // BEGIN: livedb.completions
   171     @Override
   172     public Iterable<? extends Completion> getCompletions(
   173         Element element, AnnotationMirror annotation, 
   174         ExecutableElement member, String userText
   175     ) {
   176         if (!"query".equals(member.getSimpleName().toString())) {
   177             return Collections.emptyList();
   178         }
   179         if (userText == null || userText.length() <= 1) {
   180             return Collections.singleton(Completions.of("\"SELECT "));
   181         }
   182         if (userText.toUpperCase().matches(".*FROM *")) {
   183             String user = extractValue(annotation, "user");
   184             String password = extractValue(annotation, "password");
   185             String url = extractValue(annotation, "url");
   186             if (user == null || password == null || url == null) {
   187                 return Collections.emptyList();
   188             }
   189             try {
   190                 List<Completion> arr = new ArrayList<Completion>();
   191                 Connection c = getConnection(url, user, password);
   192                 DatabaseMetaData meta = c.getMetaData();
   193                 ResultSet res = meta.getTables(null, null, "%", null);
   194                 boolean ok = res.first();
   195                 while (ok) {
   196                     String txt = userText + res.getString("TABLE_NAME");
   197                     arr.add(Completions.of(txt));
   198                     ok = res.next();
   199                 }
   200                 return arr;
   201             } catch (SQLException ex) {
   202                 throw new IllegalStateException(ex);
   203             }
   204         }
   205         return Collections.emptyList();
   206     }
   207     // END: livedb.completions
   208     
   209     private static String extractValue(AnnotationMirror am, String param) {
   210         AnnotationValue av = null;
   211         for (Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am.getElementValues().entrySet()) {
   212             if (entry.getKey().toString().equals(param + "()")) {
   213                 av = entry.getValue();
   214                 break;
   215             }
   216         }
   217         if (av == null) {
   218             return null;
   219         }
   220         String s = av.toString();
   221         if (s.startsWith("\"")) {
   222             s = s.substring(1);
   223         }
   224         if (s.endsWith("\"")) {
   225             s = s.substring(0, s.length() - 1);
   226         }
   227         return s;
   228     }
   229 }