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