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