- Notifications
You must be signed in to change notification settings - Fork 183
[MDEP-799] tree: add optional output type json #391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fba398d
f708cdf
a988792
acbb5b4
8b95bb8
d1397ca
f5b595d
68357de
7184b7b
c8c50ed
7e679ee
48743b3
fe34bd3
ce41f60
07c3dc7
e96cd5c
bcdb729
0e45124
500cdb2
6c35efd
de7e7f9
f2ef84c
83fc3d6
1b7425c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
package org.apache.maven.plugins.dependency.tree; | ||
| ||
import java.io.Writer; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
| ||
import org.apache.maven.artifact.Artifact; | ||
import org.apache.maven.shared.dependency.graph.DependencyNode; | ||
import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor; | ||
| ||
/** | ||
* A dependency node visitor that serializes visited nodes to a writer using the JSON format. | ||
*/ | ||
public class JsonDependencyNodeVisitor extends AbstractSerializingVisitor implements DependencyNodeVisitor { | ||
| ||
private String indentChar = " "; | ||
| ||
/** | ||
* Creates a new instance of {@link JsonDependencyNodeVisitor}. The writer will be used to write the output. | ||
* | ||
* @param writer the writer to write to | ||
*/ | ||
public JsonDependencyNodeVisitor(Writer writer) { | ||
super(writer); | ||
} | ||
| ||
@Override | ||
public boolean visit(DependencyNode node) { | ||
if (node.getParent() == null || node.getParent() == node) { | ||
writeRootNode(node); | ||
} | ||
return true; | ||
} | ||
| ||
/** | ||
* Writes the node to the writer. This method is recursive and will write all children nodes. | ||
* | ||
* @param node the node to write | ||
*/ | ||
private void writeRootNode(DependencyNode node) { | ||
Set<DependencyNode> visited = new HashSet<DependencyNode>(); | ||
int indent = 2; | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append("{").append("\n"); | ||
writeNode(indent, node, sb, visited); | ||
sb.append("}").append("\n"); | ||
writer.write(sb.toString()); | ||
} | ||
/** | ||
* Appends the node and its children to the string builder. | ||
* | ||
* @param indent the current indent level | ||
LogFlames marked this conversation as resolved. Show resolved Hide resolved | ||
* @param node the node to write | ||
* @param sb the string builder to append to | ||
*/ | ||
private void writeNode(int indent, DependencyNode node, StringBuilder sb, Set<DependencyNode> visited) { | ||
if (visited.contains(node)) { | ||
// Circular dependency detected | ||
// Should an exception be thrown? | ||
return; | ||
} | ||
visited.add(node); | ||
appendNodeValues(sb, indent, node.getArtifact(), !node.getChildren().isEmpty()); | ||
if (!node.getChildren().isEmpty()) { | ||
writeChildren(indent, node, sb, visited); | ||
} | ||
} | ||
/** | ||
* Writes the children of the node to the string builder. And each children of each node will be written recursively. | ||
* | ||
* @param indent the current indent level | ||
* @param node the node to write | ||
* @param sb the string builder to append to | ||
*/ | ||
private void writeChildren(int indent, DependencyNode node, StringBuilder sb, Set<DependencyNode> visited) { | ||
sb.append(indent(indent)).append("\"children\": [").append("\n"); | ||
indent += 2; | ||
for (int i = 0; i < node.getChildren().size(); i++) { | ||
DependencyNode child = node.getChildren().get(i); | ||
sb.append(indent(indent)); | ||
sb.append("{").append("\n"); | ||
writeNode(indent + 2, child, sb, visited); | ||
sb.append(indent(indent)).append("}"); | ||
// we skip the comma for the last child | ||
if (i != node.getChildren().size() - 1) { | ||
sb.append(","); | ||
} | ||
sb.append("\n"); | ||
} | ||
sb.append(indent(indent)).append("]").append("\n"); | ||
} | ||
| ||
@Override | ||
public boolean endVisit(DependencyNode node) { | ||
return true; | ||
} | ||
/** | ||
* Appends the artifact values to the string builder. | ||
* | ||
* @param sb the string builder to append to | ||
LogFlames marked this conversation as resolved. Show resolved Hide resolved | ||
* @param indent the current indent level | ||
* @param artifact the artifact to write | ||
* @param hasChildren true if the artifact has children | ||
*/ | ||
private void appendNodeValues(StringBuilder sb, int indent, Artifact artifact, boolean hasChildren) { | ||
appendKeyValue(sb, indent, "groupId", artifact.getGroupId()); | ||
appendKeyValue(sb, indent, "artifactId", artifact.getArtifactId()); | ||
appendKeyValue(sb, indent, "version", artifact.getVersion()); | ||
appendKeyValue(sb, indent, "type", artifact.getType()); | ||
appendKeyValue(sb, indent, "scope", artifact.getScope()); | ||
appendKeyValue(sb, indent, "classifier", artifact.getClassifier()); | ||
if (hasChildren) { | ||
appendKeyValue(sb, indent, "optional", String.valueOf(artifact.isOptional())); | ||
} else { | ||
appendKeyWithoutComma(sb, indent, "optional", String.valueOf(artifact.isOptional())); | ||
} | ||
} | ||
/** | ||
* Appends a key value pair to the string builder. | ||
* | ||
* @param sb the string builder to append to | ||
* @param indent the current indent level | ||
* @param key the key used as json key | ||
* @param value the value used as json value | ||
*/ | ||
private void appendKeyValue(StringBuilder sb, int indent, String key, String value) { | ||
if (value == null) { | ||
value = ""; | ||
} | ||
| ||
sb.append(indent(indent)) | ||
.append("\"") | ||
.append(key) | ||
.append("\"") | ||
.append(":") | ||
.append(indentChar) | ||
.append("\"") | ||
.append(value) | ||
.append("\"") | ||
.append(",") | ||
.append("\n"); | ||
} | ||
/** | ||
* Appends a key value pair to the string builder without a comma at the end. This is used for the last children of a node. | ||
* | ||
* @param sb the string builder to append to | ||
* @param indent the current indent level | ||
* @param key the key used as json key | ||
* @param value the value used as json value | ||
*/ | ||
private void appendKeyWithoutComma(StringBuilder sb, int indent, String key, String value) { | ||
if (value == null) { | ||
value = ""; | ||
} | ||
| ||
sb.append(indent(indent)) | ||
.append("\"") | ||
.append(key) | ||
.append("\"") | ||
.append(":") | ||
.append(indentChar) | ||
.append("\"") | ||
.append(value) | ||
.append("\"") | ||
.append("\n"); | ||
} | ||
| ||
/** | ||
* Returns a string of {@link #indentChar} for the indent level. | ||
* | ||
* @param indent the number of indent levels | ||
* @return the string of indent characters | ||
*/ | ||
private String indent(int indent) { | ||
if (indent < 1) { | ||
return ""; | ||
} | ||
| ||
StringBuilder sb = new StringBuilder(); | ||
for (int i = 0; i < indent; i++) { | ||
sb.append(indentChar); | ||
} | ||
| ||
return sb.toString(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
| @@ -18,9 +18,19 @@ | |
*/ | ||
package org.apache.maven.plugins.dependency.tree; | ||
| ||
import javax.json.Json; | ||
import javax.json.JsonArray; | ||
import javax.json.JsonObject; | ||
import javax.json.JsonReader; | ||
| ||
import java.io.BufferedReader; | ||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.FileReader; | ||
import java.io.IOException; | ||
import java.io.OutputStreamWriter; | ||
import java.io.StringReader; | ||
import java.nio.file.Files; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Set; | ||
| @@ -32,6 +42,7 @@ | |
import org.apache.maven.plugins.dependency.AbstractDependencyMojoTestCase; | ||
import org.apache.maven.project.MavenProject; | ||
import org.apache.maven.shared.dependency.graph.DependencyNode; | ||
import org.apache.maven.shared.dependency.graph.internal.DefaultDependencyNode; | ||
| ||
/** | ||
* Tests <code>TreeMojo</code>. | ||
| @@ -137,6 +148,74 @@ public void testTreeTGFSerializing() throws Exception { | |
assertTrue(findString(contents, "testGroupId:release:jar:1.0:compile")); | ||
} | ||
| ||
/** | ||
* Test the JSON format serialization on DependencyNodes with circular dependence | ||
*/ | ||
public void testTreeJsonCircularDependency() throws IOException { | ||
String outputFileName = testDir.getAbsolutePath() + "tree1.json"; | ||
File outputFile = new File(outputFileName); | ||
Files.createDirectories(outputFile.getParentFile().toPath()); | ||
outputFile.createNewFile(); | ||
| ||
Artifact artifact1 = this.stubFactory.createArtifact("testGroupId", "project1", "1.0"); | ||
Artifact artifact2 = this.stubFactory.createArtifact("testGroupId", "project2", "1.0"); | ||
DefaultDependencyNode node1 = new DefaultDependencyNode(artifact1); | ||
DefaultDependencyNode node2 = new DefaultDependencyNode(artifact2); | ||
| ||
node1.setChildren(new ArrayList<DependencyNode>()); | ||
node2.setChildren(new ArrayList<DependencyNode>()); | ||
| ||
node1.getChildren().add(node2); | ||
node2.getChildren().add(node1); | ||
| ||
JsonDependencyNodeVisitor jsonDependencyNodeVisitor = | ||
new JsonDependencyNodeVisitor(new OutputStreamWriter(new FileOutputStream(outputFile))); | ||
| ||
jsonDependencyNodeVisitor.visit(node1); | ||
} | ||
| ||
/* | ||
* Test parsing of Json output and verify all key-value pairs | ||
*/ | ||
public void testTreeJsonParsing() throws Exception { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you declare a more specific exception? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This uses the prebuilt function | ||
List<String> contents = runTreeMojo("tree2.json", "json"); | ||
| ||
try (JsonReader reader = Json.createReader(new StringReader(String.join("\n", contents)))) { | ||
JsonObject root = reader.readObject(); | ||
| ||
assertEquals(root.getString("groupId"), "testGroupId"); | ||
assertEquals(root.getString("artifactId"), "project"); | ||
assertEquals(root.getString("version"), "1.0"); | ||
assertEquals(root.getString("type"), "jar"); | ||
assertEquals(root.getString("scope"), "compile"); | ||
assertEquals(root.getString("classifier"), ""); | ||
assertEquals(root.getString("optional"), "false"); | ||
| ||
JsonArray children = root.getJsonArray("children"); | ||
assertEquals(children.size(), 2); | ||
| ||
JsonObject child0 = children.getJsonObject(0); | ||
| ||
assertEquals(child0.getString("groupId"), "testGroupId"); | ||
assertEquals(child0.getString("artifactId"), "release"); | ||
assertEquals(child0.getString("version"), "1.0"); | ||
assertEquals(child0.getString("type"), "jar"); | ||
assertEquals(child0.getString("scope"), "compile"); | ||
assertEquals(child0.getString("classifier"), ""); | ||
assertEquals(child0.getString("optional"), "false"); | ||
| ||
JsonObject child1 = children.getJsonObject(1); | ||
| ||
assertEquals(child1.getString("groupId"), "testGroupId"); | ||
assertEquals(child1.getString("artifactId"), "snapshot"); | ||
assertEquals(child1.getString("version"), "2.0-SNAPSHOT"); | ||
assertEquals(child1.getString("type"), "jar"); | ||
assertEquals(child1.getString("scope"), "compile"); | ||
assertEquals(child1.getString("classifier"), ""); | ||
assertEquals(child1.getString("optional"), "false"); | ||
} | ||
} | ||
| ||
/** | ||
* Help finding content in the given list of string | ||
* | ||
|
Uh oh!
There was an error while loading. Please reload this page.