names = new ArrayList<>();
87 | while (!element.getKind().equals(ElementKind.PACKAGE)) {
88 | names.add(0, element.getSimpleName().toString());
89 | element = element.getEnclosingElement();
90 | }
91 | if (names.contains(null)) {
92 | return null;
93 | }
94 |
95 | return String.join(".", names);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | org.jenkins-ci
6 | jenkins
7 | 1.142
8 |
9 |
10 | extension-indexer
11 |
12 | ${revision}${changelist}
13 | Extension Indexer
14 | List extension points and their implementations.
15 |
16 |
17 | 1.0
18 | -SNAPSHOT
19 | jenkins-infra/backend-${project.artifactId}
20 | ${project.basedir}/src/spotbugs/spotbugs-excludes.xml
21 |
22 |
23 |
24 |
25 |
26 | maven-jar-plugin
27 |
28 |
29 |
30 | true
31 | org.jenkinsci.extension_indexer.ExtensionPointListGenerator
32 |
33 |
34 |
35 |
36 |
37 | maven-assembly-plugin
38 |
39 |
40 |
41 | make-assembly
42 |
43 | single
44 |
45 |
46 | package
47 |
48 |
49 | src/assembly.xml
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | args4j
62 | args4j
63 | 2.37
64 |
65 |
66 | org.jsoup
67 | jsoup
68 | 1.21.2
69 |
70 |
71 | org.kohsuke.stapler
72 | json-lib
73 | 2.4-jenkins-15
74 |
75 |
76 | commons-io
77 | commons-io
78 | 2.21.0
79 |
80 |
81 | org.apache.commons
82 | commons-lang3
83 | 3.20.0
84 |
85 |
86 | org.apache.maven.resolver
87 | maven-resolver-api
88 | 1.9.20
89 |
90 |
91 | com.github.spotbugs
92 | spotbugs-annotations
93 | true
94 |
95 |
96 |
97 |
98 |
99 | repo.jenkins-ci.org
100 | https://repo.jenkins-ci.org/public/
101 |
102 |
103 |
104 |
105 |
106 | repo.jenkins-ci.org
107 | https://repo.jenkins-ci.org/public/
108 |
109 |
110 |
111 |
112 | scm:git:https://github.com/${gitHubRepo}.git
113 | scm:git:git@github.com:${gitHubRepo}.git
114 | ${scmTag}
115 | https://github.com/${gitHubRepo}
116 |
117 |
118 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/extension_indexer/ClassOfInterest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.extension_indexer;
2 |
3 | import com.sun.source.tree.ClassTree;
4 | import com.sun.source.tree.CompilationUnitTree;
5 | import com.sun.source.tree.ExpressionTree;
6 | import com.sun.source.util.JavacTask;
7 | import com.sun.source.util.TreePath;
8 | import com.sun.source.util.Trees;
9 | import net.sf.json.JSONObject;
10 | import org.jsoup.Jsoup;
11 | import org.jsoup.safety.Safelist;
12 |
13 | import javax.lang.model.element.TypeElement;
14 | import java.io.File;
15 | import java.util.HashMap;
16 | import java.util.HashSet;
17 | import java.util.Map;
18 | import java.util.Set;
19 | import java.util.regex.Matcher;
20 | import java.util.regex.Pattern;
21 |
22 | /**
23 | * Interesting thing that we pick up from plugin source code.
24 | * Common parts between {@link Extension} and {@link Action}.
25 | *
26 | * @author Vivek Pandey
27 | */
28 | public abstract class ClassOfInterest {
29 | /**
30 | * Back reference to the module where this implementation was found.
31 | */
32 | public final Module module;
33 |
34 | /**
35 | * The compiler session where this information was determined.
36 | */
37 | public final JavacTask javac;
38 |
39 | /**
40 | * {@link TreePath} that leads to {@link #implementation}
41 | */
42 | public final TreePath implPath;
43 |
44 | /**
45 | * Class of interest whose metadata is described in this object.
46 | *
47 | * This is an implementation of an extension point with {@code @Extension} on (for {@link Extension}
48 | * or it is a class that implements {@code Action}.
49 | */
50 | public final TypeElement implementation;
51 |
52 | /**
53 | * {@link Trees} object for {@link #javac}
54 | */
55 | public final Trees trees;
56 |
57 | /**
58 | * Jelly/groovy views associated to this class, including those defined for the ancestor types.
59 | *
60 | * Keyed by the view name (which is the base portion of the view file name). The value is the fully qualified
61 | * resource name.
62 | */
63 | public final Map views;
64 |
65 | ClassOfInterest(Module module, JavacTask javac, Trees trees, TypeElement implementation, TreePath implPath, Map views) {
66 | this.module = module;
67 | this.javac = javac;
68 | this.implPath = implPath;
69 | this.implementation = implementation;
70 | this.trees = trees;
71 | this.views = views;
72 | }
73 |
74 |
75 | /**
76 | * Returns the {@link ClassTree} representation of {@link #implementation}.
77 | */
78 | public ClassTree getClassTree() {
79 | return (ClassTree) implPath.getLeaf();
80 | }
81 |
82 | public CompilationUnitTree getCompilationUnit() {
83 | return implPath.getCompilationUnit();
84 | }
85 |
86 | /**
87 | * Gets the source file name that contains this definition, including directories
88 | * that match the package name portion.
89 | */
90 | public String getSourceFile() {
91 | ExpressionTree packageName = getCompilationUnit().getPackageName();
92 | String pkg = packageName == null ? "" : packageName.toString().replace('.', '/') + '/';
93 |
94 | String name = new File(getCompilationUnit().getSourceFile().getName()).getName();
95 | return pkg + name;
96 | }
97 |
98 | /**
99 | * Gets the line number in the source file where this implementation was defined.
100 | */
101 | public long getLineNumber() {
102 | return getCompilationUnit().getLineMap().getLineNumber(
103 | trees.getSourcePositions().getStartPosition(getCompilationUnit(), getClassTree()));
104 | }
105 |
106 | public String getJavadoc() {
107 | return javac.getElements().getDocComment(implementation);
108 | }
109 |
110 | /**
111 | * Javadoc excerpt converted to jenkins.io flavored Asciidoc markup.
112 | */
113 | public String getDocumentation() {
114 | String javadoc = getJavadoc();
115 | if (javadoc == null) return null;
116 |
117 | StringBuilder output = new StringBuilder(javadoc.length());
118 | for (String line : javadoc.split("\n")) {
119 | if (line.trim().length() == 0) break;
120 |
121 | if (line.trim().startsWith("@")) continue;
122 |
123 | {// replace @link
124 | Matcher m = LINK.matcher(line);
125 | StringBuilder sb = new StringBuilder();
126 | sb.append("+++");
127 | while (m.find()) {
128 | String simpleName = m.group(1);
129 | m.appendReplacement(sb, "+++ jenkinsdoc:"+simpleName+"[] +++");
130 | }
131 | m.appendTail(sb);
132 | sb.append("+++");
133 | line = sb.toString();
134 | }
135 |
136 | output.append(line).append(' ');
137 | }
138 |
139 | return Jsoup.clean(output.toString(), Safelist.basic());
140 | }
141 |
142 | /**
143 | * Returns the module Id of the plugin that it came from.
144 | */
145 | public String getArtifactId() {
146 | return module.artifactId;
147 | }
148 |
149 | private static final Pattern LINK = Pattern.compile("\\s*\\{@link ([^}]+)}\\s*");
150 |
151 | public String getImplementationName(){
152 | return implementation.getQualifiedName().toString();
153 | }
154 |
155 | public void addViews(Map views){
156 | this.views.putAll(views);
157 | }
158 |
159 | /**
160 | * Returns true if there are jelly files
161 | */
162 | public boolean hasView(){
163 | return views.size() > 0;
164 | }
165 |
166 |
167 | public JSONObject toJSON(){
168 | JSONObject i = new JSONObject();
169 | i.put("className",getImplementationName());
170 | i.put("module",module.gav);
171 | i.put("javadoc",getJavadoc());
172 | i.put("documentation", getDocumentation());
173 | i.put("sourceFile",getSourceFile());
174 | i.put("lineNumber",getLineNumber());
175 | i.put("hasView", hasView());
176 | Set