Skip to content

Commit c3d6553

Browse files
authored
Replace Graphviz/DOT with a Java implementation (#7557)
1 parent b4a3650 commit c3d6553

20 files changed

Lines changed: 583 additions & 1109 deletions

File tree

api/build.gradle

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,32 @@ dependencies {
381381
)
382382
)
383383

384+
BuildUtils.addExternalDependency(
385+
project,
386+
new ExternalDependency(
387+
"org.graphper:graph-support-core:${graphSupportVersion}",
388+
"graph-support-core",
389+
"graph-support",
390+
"https://github.com/jamisonjiang/graph-support",
391+
ExternalDependency.APACHE_2_LICENSE_NAME,
392+
ExternalDependency.APACHE_2_LICENSE_URL,
393+
"Graphviz Java API",
394+
)
395+
)
396+
397+
BuildUtils.addExternalDependency(
398+
project,
399+
new ExternalDependency(
400+
"org.graphper:graph-support-dot:${graphSupportVersion}",
401+
"graph-support-dot",
402+
"graph-support",
403+
"https://github.com/jamisonjiang/graph-support",
404+
ExternalDependency.APACHE_2_LICENSE_NAME,
405+
ExternalDependency.APACHE_2_LICENSE_URL,
406+
"DOT parsing support",
407+
)
408+
)
409+
384410
BuildUtils.addExternalDependency(
385411
project,
386412
new ExternalDependency(

api/src/org/labkey/api/ApiModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@
172172
import org.labkey.api.util.SessionHelper;
173173
import org.labkey.api.util.StringExpressionFactory;
174174
import org.labkey.api.util.StringUtilsLabKey;
175+
import org.labkey.api.util.SvgUtil;
175176
import org.labkey.api.util.SystemMaintenance;
176177
import org.labkey.api.util.SystemMaintenanceStartupListener;
177178
import org.labkey.api.util.URIUtil;
@@ -439,6 +440,7 @@ public void registerServlets(ServletContext servletCtx)
439440
StringExpressionFactory.TestCase.class,
440441
StringUtilsLabKey.TestCase.class,
441442
SubfolderWriter.TestCase.class,
443+
SvgUtil.TestCase.class,
442444
SwapQueue.TestCase.class,
443445
TSVMapWriter.Tests.class,
444446
TSVWriter.TestCase.class,

api/src/org/labkey/api/action/SpringActionController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,16 @@ public static Collection<ActionDescriptor> getRegisteredActionDescriptors()
175175
return new ArrayList<>(_classToDescriptor.values());
176176
}
177177

178+
protected static String h(@Nullable CharSequence s)
179+
{
180+
return PageFlowUtil.filter(s);
181+
}
182+
183+
protected static String h(@Nullable Object o)
184+
{
185+
return PageFlowUtil.filter(o);
186+
}
187+
178188
// I don't think there is an interface for this
179189
public interface ActionResolver
180190
{

api/src/org/labkey/api/attachments/SvgSource.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import org.apache.commons.lang3.StringUtils;
66
import org.apache.commons.lang3.Strings;
77
import org.labkey.api.util.PageFlowUtil;
8+
import org.labkey.api.util.SvgUtil;
9+
import org.labkey.api.util.SvgUtil.Size;
810
import org.labkey.api.view.NotFoundException;
911

1012
import java.io.BufferedReader;
@@ -19,8 +21,7 @@
1921
public class SvgSource
2022
{
2123
private final String _filteredSvg;
22-
23-
private Float _height = null;
24+
private final Float _height;
2425

2526
public SvgSource(String svg)
2627
{
@@ -32,16 +33,11 @@ public SvgSource(String svg)
3233
if (!svg.contains("xmlns=\"" + SVGDOMImplementation.SVG_NAMESPACE_URI + "\"") && !svg.contains("xmlns='" + SVGDOMImplementation.SVG_NAMESPACE_URI + "'"))
3334
svg = svg.replace("<svg", "<svg xmlns='" + SVGDOMImplementation.SVG_NAMESPACE_URI + "'");
3435

35-
int idx = svg.indexOf("height=\"");
36-
if (idx != -1)
37-
{
38-
int heightStart = idx + 8;
39-
int end = svg.indexOf("\"", heightStart);
40-
_height = Float.parseFloat(svg.substring(heightStart, end));
41-
}
36+
Size size = SvgUtil.readHeight(svg);
37+
_height = size != null ? size.value() : null;
4238

4339
// remove xlink:title to prevent org.apache.batik.transcoder.TranscoderException (issue #16173)
44-
svg = svg.replaceAll("xlink:title", "title");
40+
svg = svg.replace("xlink:title", "title");
4541

4642
// Reject hrefs. See #45819.
4743
if (Strings.CI.contains(svg, "xlink:href"))

api/src/org/labkey/api/module/ModuleDependencySorter.java

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@
1818

1919
import org.apache.commons.collections4.MultiValuedMap;
2020
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
21-
import org.apache.logging.log4j.LogManager;
2221
import org.apache.logging.log4j.Logger;
22+
import org.graphper.api.GraphResource;
23+
import org.graphper.api.Graphviz;
24+
import org.graphper.parser.DotParser;
2325
import org.junit.Assert;
2426
import org.junit.Test;
2527
import org.labkey.api.collections.CaseInsensitiveHashSet;
26-
import org.labkey.api.util.DotRunner;
2728
import org.labkey.api.util.FileUtil;
2829
import org.labkey.api.util.Pair;
30+
import org.labkey.api.util.logging.LogHelper;
2931

3032
import java.io.File;
3133
import java.util.ArrayList;
@@ -34,12 +36,12 @@
3436
import java.util.stream.Collectors;
3537

3638
/**
37-
* Orders modules so that each module will always be after all of the modules it depends on.
38-
* User: jeckels
39-
* Date: Jun 6, 2006
39+
* Orders modules so that each module will always be after all the modules it depends on.
4040
*/
4141
public class ModuleDependencySorter
4242
{
43+
private static final Logger LOG = LogHelper.getLogger(ModuleDependencySorter.class, "Module dependency information");
44+
4345
public List<Module> sortModulesByDependencies(List<Module> modules)
4446
{
4547
List<Pair<Module, Set<String>>> dependencies = new ArrayList<>();
@@ -101,7 +103,7 @@ public List<Module> sortModulesByDependencies(List<Module> modules)
101103
if (module.getName().equalsIgnoreCase("core"))
102104
{
103105
result.remove(i);
104-
result.add(0, module);
106+
result.addFirst(module);
105107
break;
106108
}
107109
}
@@ -128,41 +130,36 @@ private Module findModuleWithoutDependencies(List<Pair<Module, Set<String>>> dep
128130
throw new IllegalArgumentException("Module '" + moduleName + "' (" + entry.getKey().getClass().getName() + ") is listed as being dependent on itself.");
129131
}
130132

131-
StringBuilder sb = new StringBuilder();
132-
for (Pair<Module, Set<String>> dependencyInfo : dependencies)
133-
{
134-
if (!sb.isEmpty())
135-
{
136-
sb.append(", ");
137-
}
138-
sb.append(dependencyInfo.getKey().getName());
139-
}
133+
String involved = dependencies.stream()
134+
.map(pair -> pair.getKey().getName())
135+
.collect(Collectors.joining(", "));
140136

141137
// Generate an SVG diagram that shows all remaining dependencies
142138
graphModuleDependencies(dependencies, "involved");
143139

144-
throw new IllegalArgumentException("Unable to resolve module dependencies. The following modules are somehow involved: " + sb);
140+
throw new IllegalArgumentException("Unable to resolve module dependencies. The following modules are somehow involved: " + involved);
145141
}
146142

147143

148144
private void graphModuleDependencies(List<Pair<Module, Set<String>>> dependencies, @SuppressWarnings("SameParameterValue") String adjective)
149145
{
150-
Logger log = LogManager.getLogger(ModuleDependencySorter.class);
151-
152146
try
153147
{
154148
File dir = FileUtil.getTempDirectory();
155149
String dot = buildDigraph(dependencies);
150+
Graphviz graph = DotParser.parse(dot);
156151
File svgFile = FileUtil.createTempFile("modules", ".svg", dir);
157-
DotRunner runner = new DotRunner(dir, dot);
158-
runner.addSvgOutput(svgFile);
159-
runner.execute();
160152

161-
log.info("For a diagram of " + adjective + " module dependencies, see " + svgFile.getAbsolutePath());
153+
try (GraphResource resource = graph.toSvg())
154+
{
155+
resource.save(svgFile.getParent(), svgFile.getName());
156+
}
157+
158+
LOG.info("For a diagram of {} module dependencies, see {}", adjective, svgFile.getAbsolutePath());
162159
}
163160
catch (Exception e)
164161
{
165-
log.error("Error running dot", e);
162+
LOG.error("Error running dot", e);
166163
}
167164
}
168165

0 commit comments

Comments
 (0)