Fixing Maven-based build to properly process the component configuration properties for ONOS 2.x

Change-Id: I29c063939073852862f3c0e2f47540afe66a34aa
diff --git a/tools/package/maven-plugin/pom.xml b/tools/package/maven-plugin/pom.xml
index f9016d2..82a688f 100644
--- a/tools/package/maven-plugin/pom.xml
+++ b/tools/package/maven-plugin/pom.xml
@@ -26,7 +26,7 @@
 
     <groupId>org.onosproject</groupId>
     <artifactId>onos-maven-plugin</artifactId>
-    <version>2.1-SNAPSHOT</version>
+    <version>2.1</version>
     <packaging>maven-plugin</packaging>
 
     <description>Maven plugin for packaging ONOS applications or generating
diff --git a/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosCfgMojo.java b/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosCfgMojo.java
index 19628f8..f8ea71c 100644
--- a/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosCfgMojo.java
+++ b/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosCfgMojo.java
@@ -15,10 +15,15 @@
  */
 package org.onosproject.maven;
 
+import com.google.common.collect.Maps;
 import com.thoughtworks.qdox.JavaProjectBuilder;
 import com.thoughtworks.qdox.model.JavaAnnotation;
 import com.thoughtworks.qdox.model.JavaClass;
 import com.thoughtworks.qdox.model.JavaField;
+import com.thoughtworks.qdox.model.expression.Add;
+import com.thoughtworks.qdox.model.expression.AnnotationValue;
+import com.thoughtworks.qdox.model.expression.AnnotationValueList;
+import com.thoughtworks.qdox.model.expression.FieldRef;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
@@ -31,6 +36,9 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.jar.JarEntry;
 
 /**
  * Produces ONOS component configuration catalogue resources.
@@ -39,10 +47,6 @@
 @java.lang.SuppressWarnings("squid:S1148")
 public class OnosCfgMojo extends AbstractMojo {
 
-    private static final String COMPONENT = "org.apache.felix.scr.annotations.Component";
-    private static final String PROPERTY = "org.apache.felix.scr.annotations.Property";
-    private static final String SEP = "|";
-
     /**
      * The directory where the generated catalogue file will be put.
      */
@@ -59,84 +63,161 @@
     public void execute() throws MojoExecutionException {
         getLog().info("Generating ONOS component configuration catalogues...");
         try {
-            JavaProjectBuilder builder = new JavaProjectBuilder();
-            builder.addSourceTree(new File(srcDirectory, "src/main/java"));
-            builder.getClasses().forEach(this::processClass);
+            CfgDefGenerator gen = new CfgDefGenerator(dstDirectory, srcDirectory);
+            gen.analyze();
+            gen.generate();
         } catch (Exception e) {
             e.printStackTrace();
-            throw e;
+            throw new MojoExecutionException("Unable to generate property catalog", e);
         }
     }
 
-    private void processClass(JavaClass javaClass) {
-        boolean isComponent = javaClass.getAnnotations().stream()
-                .map(ja -> ja.getType().getName().equals(COMPONENT))
-                .findFirst().isPresent();
-        if (isComponent) {
-            List<String> lines = new ArrayList<>();
-            javaClass.getFields().forEach(field -> processField(lines, javaClass, field));
-            if (!lines.isEmpty()) {
-                writeCatalog(javaClass, lines);
+
+    private static class CfgDefGenerator {
+
+        private static final String COMPONENT = "org.osgi.service.component.annotations.Component";
+        private static final String PROPERTY = "property";
+        private static final String SEP = "|";
+        private static final String UTF_8 = "UTF-8";
+        private static final String JAVA = ".java";
+        private static final String EXT = ".cfgdef";
+        private static final String STRING = "STRING";
+        private static final String NO_DESCRIPTION = "no description provided";
+
+        private final File dstDir;
+        private final JavaProjectBuilder builder;
+
+        private final Map<String, String> constants = Maps.newHashMap();
+
+
+        private CfgDefGenerator(File dstDir, File srcDir) {
+            this.dstDir = dstDir;
+            this.builder = new JavaProjectBuilder();
+            builder.addSourceTree(new File(srcDir, "src/main/java"));
+
+//            Arrays.stream(sourceFilePaths).forEach(filename -> {
+//                try {
+//                    if (filename.endsWith(JAVA))
+//                        builder.addSource(new File(filename));
+//                } catch (ParseException e) {
+//                    // When unable to parse, skip the source; leave it to javac to fail.
+//                } catch (IOException e) {
+//                    throw new IllegalArgumentException("Unable to open file", e);
+//                }
+//            });
+        }
+
+        public void analyze() {
+            builder.getClasses().forEach(this::collectConstants);
+        }
+
+        private void collectConstants(JavaClass javaClass) {
+            javaClass.getFields().stream()
+                    .filter(f -> f.isStatic() && f.isFinal() && !f.isPrivate())
+                    .forEach(f -> constants.put(f.getName(), f.getInitializationExpression()));
+        }
+
+        public void generate() throws IOException {
+            for (JavaClass javaClass : builder.getClasses()) {
+                processClass(javaClass);
             }
         }
-    }
 
-    private void writeCatalog(JavaClass javaClass, List<String> lines) {
-        File dir = new File(dstDirectory, javaClass.getPackageName().replace('.', '/'));
-        dir.mkdirs();
+        private void processClass(JavaClass javaClass) throws IOException {
+            Optional<JavaAnnotation> annotation = javaClass.getAnnotations().stream()
+                    .filter(ja -> ja.getType().getName().equals(COMPONENT))
+                    .findFirst();
+            if (annotation.isPresent()) {
+                AnnotationValue property = annotation.get().getProperty(PROPERTY);
+                List<String> lines = new ArrayList<>();
+                if (property instanceof AnnotationValueList) {
+                    AnnotationValueList list = (AnnotationValueList) property;
+                    list.getValueList().forEach(v -> processProperty(lines, javaClass, v));
+                } else {
+                    processProperty(lines, javaClass, property);
+                }
 
-        File cfgDef = new File(dir, javaClass.getName().replace('.', '/') + ".cfgdef");
-        try (FileWriter fw = new FileWriter(cfgDef);
-             PrintWriter pw = new PrintWriter(fw)) {
-            pw.println("# This file is auto-generated by onos-maven-plugin");
-            lines.forEach(pw::println);
-        } catch (IOException e) {
-            System.err.println("Unable to write catalog for " + javaClass.getName());
-            e.printStackTrace();
-        }
-    }
-
-    private void processField(List<String> lines, JavaClass javaClass, JavaField field) {
-        field.getAnnotations().forEach(ja -> {
-            if (ja.getType().getName().equals(PROPERTY)) {
-                lines.add(expand(javaClass, ja.getNamedParameter("name").toString()) +
-                                  SEP + type(field) +
-                                  SEP + defaultValue(javaClass, field, ja) +
-                                  SEP + description(ja));
+                if (!lines.isEmpty()) {
+                    writeCatalog(javaClass, lines);
+                }
             }
-        });
+        }
+
+        private void processProperty(List<String> lines, JavaClass javaClass,
+                                     AnnotationValue value) {
+            String s = elaborate(value);
+            String[] pex = s.split("=", 2);
+
+            if (pex.length == 2) {
+                String[] rex = pex[0].split(":", 2);
+                String name = rex[0];
+                String type = rex.length == 2 ? rex[1].toUpperCase() : STRING;
+                String def = pex[1];
+                String desc = description(javaClass, name);
+
+                if (desc != null) {
+                    String line = name + SEP + type + SEP + def + SEP + desc + "\n";
+                    lines.add(line);
+                }
+            }
+        }
+
+        // Retrieve description from a comment preceding the field named the same
+        // as the property or
+        // TODO: from an annotated comment.
+        private String description(JavaClass javaClass, String name) {
+            if (name.startsWith("_")) {
+                // Static property - just leave it as is, not for inclusion in the cfg defs
+                return null;
+            }
+            JavaField field = javaClass.getFieldByName(name);
+            if (field != null) {
+                String comment = field.getComment();
+                return comment != null ? comment : NO_DESCRIPTION;
+            }
+            throw new IllegalStateException("cfgdef could not find a variable named " + name + " in " + javaClass.getName());
+        }
+
+        private String elaborate(AnnotationValue value) {
+            if (value instanceof Add) {
+                return elaborate(((Add) value).getLeft()) + elaborate(((Add) value).getRight());
+            } else if (value instanceof FieldRef) {
+                return elaborate((FieldRef) value);
+            } else if (value != null) {
+                return stripped(value.toString());
+            } else {
+                return "";
+            }
+        }
+
+        private String elaborate(FieldRef field) {
+            String name = field.getName();
+            String value = constants.get(name);
+            if (value != null) {
+                return stripped(value);
+            }
+            throw new IllegalStateException("Constant " + name + " cannot be elaborated;" +
+                                                    " value not in the same compilation context");
+        }
+
+        private String stripped(String s) {
+            return s.trim().replaceFirst("^[^\"]*\"", "").replaceFirst("\"$", "");
+        }
+
+        private void writeCatalog(JavaClass javaClass, List<String> lines) {
+            File dir = new File(dstDir, javaClass.getPackageName().replace('.', '/'));
+            dir.mkdirs();
+
+            File cfgDef = new File(dir, javaClass.getName().replace('.', '/') + ".cfgdef");
+            try (FileWriter fw = new FileWriter(cfgDef);
+                 PrintWriter pw = new PrintWriter(fw)) {
+                pw.println("# This file is auto-generated by onos-maven-plugin");
+                lines.forEach(pw::println);
+            } catch (IOException e) {
+                System.err.println("Unable to write catalog for " + javaClass.getName());
+                e.printStackTrace();
+            }
+        }
+
     }
-
-    // TODO: Stuff below is very much hack-ish and should be redone; it works for now though.
-
-    private String description(JavaAnnotation annotation) {
-        String description = (String) annotation.getNamedParameter("label");
-        return description.replaceAll("\" \\+ \"", "")
-                .replaceFirst("^[^\"]*\"", "").replaceFirst("\"$", "");
-    }
-
-    private String type(JavaField field) {
-        String ft = field.getType().getName().toUpperCase();
-        return ft.equals("INT") ? "INTEGER" : ft;
-    }
-
-    private String defaultValue(JavaClass javaClass, JavaField field,
-                                JavaAnnotation annotation) {
-        String ft = field.getType().getName().toLowerCase();
-        String defValueName = ft.equals("boolean") ? "boolValue" :
-                ft.equals("string") ? "value" : ft + "Value";
-        Object dv = annotation.getNamedParameter(defValueName);
-        return dv == null ? "" : expand(javaClass, dv.toString());
-    }
-
-    private String stripQuotes(String string) {
-        return string.trim().replaceFirst("^[^\"]*\"", "").replaceFirst("\"$", "");
-    }
-
-    private String expand(JavaClass javaClass, String value) {
-        JavaField field = javaClass.getFieldByName(value);
-        return field == null ? stripQuotes(value) :
-                stripQuotes(field.getCodeBlock().replaceFirst(".*=", "").replaceFirst(";$", ""));
-    }
-
-}
+}
\ No newline at end of file