blob: 696918d190d12a277d4a5fb7b008ba54c659dff7 [file] [log] [blame]
Thomas Vachuska4a347632018-09-11 11:53:08 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.cfgdef;
17
18import com.google.common.collect.Maps;
19import com.thoughtworks.qdox.JavaProjectBuilder;
20import com.thoughtworks.qdox.model.JavaAnnotation;
21import com.thoughtworks.qdox.model.JavaClass;
22import com.thoughtworks.qdox.model.JavaField;
23import com.thoughtworks.qdox.model.expression.Add;
24import com.thoughtworks.qdox.model.expression.AnnotationValue;
25import com.thoughtworks.qdox.model.expression.AnnotationValueList;
26import com.thoughtworks.qdox.model.expression.FieldRef;
27
28import java.io.File;
29import java.io.FileOutputStream;
30import java.io.IOException;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.List;
34import java.util.Map;
35import java.util.Optional;
36import java.util.jar.JarEntry;
37import java.util.jar.JarOutputStream;
38
39/**
40 * Produces ONOS component configuration catalogue resources.
41 */
42public class CfgDefGenerator {
43
44 private static final String COMPONENT = "org.osgi.service.component.annotations.Component";
45 private static final String PROPERTY = "property";
46 private static final String SEP = "|";
47 private static final String UTF_8 = "UTF-8";
48 private static final String EXT = ".cfgdef";
49 private static final String STRING = "STRING";
50
51 private final File resourceJar;
52 private final JavaProjectBuilder builder;
53
54 private final Map<String, String> constants = Maps.newHashMap();
55
56 public static void main(String[] args) throws IOException {
57 if (args.length < 2) {
58 System.err.println("usage: cfgdef outputJar javaSource javaSource ...");
59 System.exit(1);
60 }
61 CfgDefGenerator gen = new CfgDefGenerator(args[0], Arrays.copyOfRange(args, 1, args.length));
62 gen.analyze();
63 gen.generate();
64 }
65
66 private CfgDefGenerator(String resourceJarPath, String[] sourceFilePaths) {
67 this.resourceJar = new File(resourceJarPath);
68 this.builder = new JavaProjectBuilder();
69 Arrays.stream(sourceFilePaths).forEach(filename -> {
70 try {
71 builder.addSource(new File(filename));
72 } catch (IOException e) {
73 throw new IllegalArgumentException("Unable to open file", e);
74 }
75 });
76 }
77
78 public void analyze() {
79 builder.getClasses().forEach(this::collectConstants);
80 }
81
82 private void collectConstants(JavaClass javaClass) {
83 javaClass.getFields().stream()
84 .filter(f -> f.isStatic() && f.isFinal() && !f.isPrivate())
85 .forEach(f -> constants.put(f.getName(), f.getInitializationExpression()));
86 }
87
88 public void generate() throws IOException {
89 JarOutputStream jar = new JarOutputStream(new FileOutputStream(resourceJar));
90 for (JavaClass javaClass : builder.getClasses()) {
91 processClass(jar, javaClass);
92 }
93 jar.close();
94 }
95
96 private void processClass(JarOutputStream jar, JavaClass javaClass) throws IOException {
97 Optional<JavaAnnotation> annotation = javaClass.getAnnotations().stream()
98 .filter(ja -> ja.getType().getName().equals(COMPONENT))
99 .findFirst();
100 if (annotation.isPresent()) {
101 AnnotationValue property = annotation.get().getProperty(PROPERTY);
102 List<String> lines = new ArrayList<>();
103 if (property instanceof AnnotationValueList) {
104 AnnotationValueList list = (AnnotationValueList) property;
105 list.getValueList().forEach(v -> processProperty(lines, javaClass, v));
106 } else {
107 processProperty(lines, javaClass, property);
108 }
109
110 if (!lines.isEmpty()) {
111 writeCatalog(jar, javaClass, lines);
112 }
113 }
114 }
115
116 private void processProperty(List<String> lines, JavaClass javaClass,
117 AnnotationValue value) {
118 String s = elaborate(value);
119 String pex[] = s.split("=", 2);
120
121 if (pex.length == 2) {
122 String rex[] = pex[0].split(":", 2);
123 String name = rex[0];
124 String type = rex.length == 2 ? rex[1].toUpperCase() : STRING;
125 String def = pex[1];
126 String desc = description(javaClass, name);
127
128 String line = name + SEP + type + SEP + def + SEP + desc + "\n";
129 lines.add(line);
130 System.err.print(line);
131 }
132 }
133
134 // Retrieve description from a comment preceeding the field named the same
135 // as the property or
136 // TODO: from an annotated comment.
137 private String description(JavaClass javaClass, String name) {
138 JavaField field = javaClass.getFieldByName(name);
139 if (field != null) {
140 return field.getComment();
141 }
142 return "no description provided";
143 }
144
145 private String elaborate(AnnotationValue value) {
146 if (value instanceof Add) {
147 return elaborate(((Add) value).getLeft()) + elaborate(((Add) value).getRight());
148 } else if (value instanceof FieldRef) {
149 return stripped(constants.get(((FieldRef) value).getName()));
150 } else {
151 return stripped(value.toString());
152 }
153 }
154
155 private String stripped(String s) {
156 return s.trim().replaceFirst("^[^\"]*\"", "").replaceFirst("\"$", "");
157 }
158
159 private void writeCatalog(JarOutputStream jar, JavaClass javaClass, List<String> lines)
160 throws IOException {
161 String name = javaClass.getPackageName().replace('.', '/') + "/" + javaClass.getName() + EXT;
162 jar.putNextEntry(new JarEntry(name));
163 jar.write("# This file is auto-generated\n".getBytes(UTF_8));
164 for (String line : lines) {
165 jar.write(line.getBytes(UTF_8));
166 }
167 jar.closeEntry();
168 }
169
170}