1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/samples/livedb/src/main/java/org/apidesign/livedb/impl/LiveDBProcessor.java Tue Nov 12 08:55:35 2019 +0100
1.3 @@ -0,0 +1,228 @@
1.4 +package org.apidesign.livedb.impl;
1.5 +
1.6 +import java.io.IOException;
1.7 +import java.io.Writer;
1.8 +import java.sql.CallableStatement;
1.9 +import java.sql.Connection;
1.10 +import java.sql.DatabaseMetaData;
1.11 +import java.sql.Driver;
1.12 +import java.sql.ResultSet;
1.13 +import java.sql.ResultSetMetaData;
1.14 +import java.sql.SQLException;
1.15 +import java.util.ArrayList;
1.16 +import java.util.Collections;
1.17 +import java.util.List;
1.18 +import java.util.Map.Entry;
1.19 +import java.util.Properties;
1.20 +import java.util.ServiceLoader;
1.21 +import java.util.Set;
1.22 +import javax.annotation.processing.AbstractProcessor;
1.23 +import javax.annotation.processing.Completion;
1.24 +import javax.annotation.processing.Completions;
1.25 +import javax.annotation.processing.Filer;
1.26 +import javax.annotation.processing.Processor;
1.27 +import javax.annotation.processing.RoundEnvironment;
1.28 +import javax.annotation.processing.SupportedAnnotationTypes;
1.29 +import javax.annotation.processing.SupportedSourceVersion;
1.30 +import javax.lang.model.SourceVersion;
1.31 +import javax.lang.model.element.AnnotationMirror;
1.32 +import javax.lang.model.element.AnnotationValue;
1.33 +import javax.lang.model.element.Element;
1.34 +import javax.lang.model.element.ExecutableElement;
1.35 +import javax.lang.model.element.PackageElement;
1.36 +import javax.lang.model.element.TypeElement;
1.37 +import javax.tools.Diagnostic;
1.38 +import javax.tools.JavaFileObject;
1.39 +import org.apidesign.livedb.LiveDB;
1.40 +import org.openide.util.lookup.ServiceProvider;
1.41 +
1.42 +/**
1.43 + *
1.44 + * @author Jaroslav Tulach <jtulach@netbeans.org>
1.45 + */
1.46 +// BEGIN: livedb.processor
1.47 +@SupportedAnnotationTypes("org.apidesign.livedb.LiveDB")
1.48 +@SupportedSourceVersion(SourceVersion.RELEASE_6)
1.49 +@ServiceProvider(service=Processor.class)
1.50 +public final class LiveDBProcessor extends AbstractProcessor {
1.51 + @Override
1.52 + public boolean process(
1.53 + Set<? extends TypeElement> annotations, RoundEnvironment roundEnv
1.54 + ) {
1.55 + final Filer filer = processingEnv.getFiler();
1.56 + for (Element e : roundEnv.getElementsAnnotatedWith(LiveDB.class)) {
1.57 + LiveDB db = e.getAnnotation(LiveDB.class);
1.58 + PackageElement pe = (PackageElement)e;
1.59 + String clsName = pe.getQualifiedName() + "." + db.classname();
1.60 + try {
1.61 + JavaFileObject src = filer.createSourceFile(clsName, pe);
1.62 + Writer w = src.openWriter();
1.63 + Connection c = getConnection(
1.64 + db.url(), db.user(), db.password()
1.65 + );
1.66 + CallableStatement s = c.prepareCall(db.query());
1.67 + ResultSet rs = s.executeQuery();
1.68 + ResultSetMetaData md = rs.getMetaData();
1.69 + generateClassHeader(w, pe, db);
1.70 + w.append("class " + db.classname() + " {\n");
1.71 + for (int i = 1; i <= md.getColumnCount(); i++) {
1.72 + generateField(w, md, i);
1.73 + }
1.74 + generateConstructor(w, db, md);
1.75 + generateQueryMethod(w, db, md);
1.76 + w.append("}");
1.77 + w.close();
1.78 + } catch (IOException | SQLException ex) {
1.79 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, ex.getMessage(), e);
1.80 + }
1.81 + }
1.82 + return true;
1.83 + }
1.84 +// FINISH: livedb.processor
1.85 +
1.86 + private void generateQueryMethod(Writer w, LiveDB db, ResultSetMetaData md) throws SQLException, IOException {
1.87 + w.append(" public static List<" + db.classname() + "> ")
1.88 + .append("query() throws SQLException {\n");
1.89 + w.append(" Connection c = DriverManager.getConnection(\"")
1.90 + .append(db.url()).append("\", \"")
1.91 + .append(db.user()).append("\", \"")
1.92 + .append(db.password()).append("\");\n");
1.93 + w.append(" List<").append(db.classname())
1.94 + .append("> res = new ArrayList<")
1.95 + .append(db.classname()).append(">();\n");
1.96 + w.append(" CallableStatement s = c.prepareCall(\"")
1.97 + .append(db.query()).append("\");\n");
1.98 + w.append(" ResultSet rs = s.executeQuery();\n");
1.99 + w.append(" ResultSetMetaData md = rs.getMetaData();\n");
1.100 + w.append(" while (rs.next()) {\n");
1.101 + w.append(" res.add(new " + db.classname() + "(\n");
1.102 + for (int i = 1; i <= md.getColumnCount(); i++) {
1.103 + w.append(" (")
1.104 + .append(md.getColumnClassName(i))
1.105 + .append(")rs.getObject(" + i).append(")");
1.106 + if (i < md.getColumnCount()) {
1.107 + w.append(",\n");
1.108 + } else {
1.109 + w.append("\n");
1.110 + }
1.111 + }
1.112 + w.append(" ));\n");
1.113 + w.append(" };\n");
1.114 + w.append(" return res;\n");
1.115 + w.append(" }");
1.116 + }
1.117 +
1.118 + private void generateConstructor(Writer w, LiveDB db, ResultSetMetaData md) throws SQLException, IOException {
1.119 + w.append(" private " + db.classname() + "(\n");
1.120 + for (int i = 1; i <= md.getColumnCount(); i++) {
1.121 + w.append(" ").append(md.getColumnClassName(i))
1.122 + .append(" ").append(md.getColumnName(i));
1.123 + if (i < md.getColumnCount()) {
1.124 + w.append(",\n");
1.125 + } else {
1.126 + w.append("\n");
1.127 + }
1.128 + }
1.129 + w.append(" ) {\n");
1.130 + for (int i = 1; i <= md.getColumnCount(); i++) {
1.131 + w.append(" this.")
1.132 + .append(md.getColumnName(i))
1.133 + .append(" = ")
1.134 + .append(md.getColumnName(i))
1.135 + .append(";\n");
1.136 + }
1.137 + w.append(" }\n");
1.138 + }
1.139 +
1.140 + private void generateField(Writer w, ResultSetMetaData md, int i) throws IOException, SQLException {
1.141 + w.append(" public final ")
1.142 + .append(md.getColumnClassName(i))
1.143 + .append(" ")
1.144 + .append(md.getColumnName(i))
1.145 + .append(";\n");
1.146 + }
1.147 +
1.148 + private void generateClassHeader(Writer w, PackageElement pe, LiveDB db) throws IOException {
1.149 + w.append("package " + pe.getQualifiedName() + ";\n");
1.150 + w.append("import java.util.List;\n");
1.151 + w.append("import java.util.ArrayList;\n");
1.152 + w.append("import java.sql.*;\n");
1.153 + }
1.154 + private static Connection getConnection(String url, String user, String password)
1.155 + throws SQLException {
1.156 + final ClassLoader cl = LiveDBProcessor.class.getClassLoader();
1.157 + for (Driver d : ServiceLoader.load(Driver.class, cl)) {
1.158 +// System.out.println("looked up: " + d);
1.159 + if (d.acceptsURL(url)) {
1.160 + //System.out.println("accepts: " + d);
1.161 + Properties p = new Properties();
1.162 + p.put("user", user);
1.163 + p.put("password", password);
1.164 + return d.connect(url, p);
1.165 + }
1.166 + }
1.167 + throw new SQLException("No driver found for " + url);
1.168 + }
1.169 +
1.170 +
1.171 +
1.172 + // BEGIN: livedb.completions
1.173 + @Override
1.174 + public Iterable<? extends Completion> getCompletions(
1.175 + Element element, AnnotationMirror annotation,
1.176 + ExecutableElement member, String userText
1.177 + ) {
1.178 + if (!"query".equals(member.getSimpleName().toString())) {
1.179 + return Collections.emptyList();
1.180 + }
1.181 + if (userText == null || userText.length() <= 1) {
1.182 + return Collections.singleton(Completions.of("\"SELECT "));
1.183 + }
1.184 + if (userText.toUpperCase().matches(".*FROM *")) {
1.185 + String user = extractValue(annotation, "user");
1.186 + String password = extractValue(annotation, "password");
1.187 + String url = extractValue(annotation, "url");
1.188 + if (user == null || password == null || url == null) {
1.189 + return Collections.emptyList();
1.190 + }
1.191 + try {
1.192 + List<Completion> arr = new ArrayList<Completion>();
1.193 + Connection c = getConnection(url, user, password);
1.194 + DatabaseMetaData meta = c.getMetaData();
1.195 + ResultSet res = meta.getTables(null, null, "%", null);
1.196 + boolean ok = res.first();
1.197 + while (ok) {
1.198 + String txt = userText + res.getString("TABLE_NAME");
1.199 + arr.add(Completions.of(txt));
1.200 + ok = res.next();
1.201 + }
1.202 + return arr;
1.203 + } catch (SQLException ex) {
1.204 + throw new IllegalStateException(ex);
1.205 + }
1.206 + }
1.207 + return Collections.emptyList();
1.208 + }
1.209 + // END: livedb.completions
1.210 +
1.211 + private static String extractValue(AnnotationMirror am, String param) {
1.212 + AnnotationValue av = null;
1.213 + for (Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am.getElementValues().entrySet()) {
1.214 + if (entry.getKey().toString().equals(param + "()")) {
1.215 + av = entry.getValue();
1.216 + break;
1.217 + }
1.218 + }
1.219 + if (av == null) {
1.220 + return null;
1.221 + }
1.222 + String s = av.toString();
1.223 + if (s.startsWith("\"")) {
1.224 + s = s.substring(1);
1.225 + }
1.226 + if (s.endsWith("\"")) {
1.227 + s = s.substring(0, s.length() - 1);
1.228 + }
1.229 + return s;
1.230 + }
1.231 +}