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