Skip to content

Commit 90a7c39

Browse files
authored
Merge pull request #216 from KristianKarl/add_possibility_to_pickup_generators_from_classpath
Scan and use plugin generators
2 parents eacebec + 8babe9b commit 90a7c39

File tree

7 files changed

+218
-1
lines changed

7 files changed

+218
-1
lines changed

graphwalker-dsl/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929
<groupId>org.antlr</groupId>
3030
<artifactId>antlr4-runtime</artifactId>
3131
</dependency>
32+
<dependency>
33+
<groupId>org.reflections</groupId>
34+
<artifactId>reflections</artifactId>
35+
</dependency>
36+
<dependency>
37+
<groupId>commons-io</groupId>
38+
<artifactId>commons-io</artifactId>
39+
</dependency>
3240
</dependencies>
3341

3442
<build>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.graphwalker.dsl.antlr.generator;
2+
3+
/*
4+
* #%L
5+
* GraphWalker Input/Output
6+
* %%
7+
* Copyright (C) 2005 - 2014 GraphWalker
8+
* %%
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in
17+
* all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
* #L%
27+
*/
28+
29+
public class GeneratorFactoryException extends RuntimeException {
30+
31+
public GeneratorFactoryException() {
32+
super();
33+
}
34+
35+
public GeneratorFactoryException(String message) {
36+
super(message);
37+
}
38+
39+
public GeneratorFactoryException(String message, Throwable t) {
40+
super(message, t);
41+
}
42+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.graphwalker.dsl.antlr.generator;
2+
3+
/*
4+
* #%L
5+
* GraphWalker Input/Output
6+
* %%
7+
* Copyright (C) 2005 - 2014 GraphWalker
8+
* %%
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in
17+
* all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
* #L%
27+
*/
28+
29+
import org.apache.commons.io.FilenameUtils;
30+
import org.graphwalker.core.generator.PathGeneratorBase;
31+
import org.reflections.Reflections;
32+
import org.reflections.scanners.SubTypesScanner;
33+
import org.reflections.util.ClasspathHelper;
34+
import org.reflections.util.ConfigurationBuilder;
35+
import org.slf4j.Logger;
36+
import org.slf4j.LoggerFactory;
37+
38+
import java.net.URL;
39+
import java.util.Collection;
40+
import java.util.HashSet;
41+
import java.util.Set;
42+
43+
public final class GeneratorFactoryScanner {
44+
45+
private GeneratorFactoryScanner() {
46+
}
47+
48+
private static final Logger logger = LoggerFactory.getLogger(GeneratorFactoryScanner.class);
49+
50+
static {
51+
Reflections.log = null;
52+
}
53+
54+
private static boolean valid(URL url) {
55+
String extension = FilenameUtils.getExtension(url.getPath());
56+
return "".equals(extension) || "jar".equals(extension);
57+
}
58+
59+
private static Collection<URL> getUrls() {
60+
Set<URL> filteredUrls = new HashSet<>();
61+
Set<URL> urls = new HashSet<>();
62+
urls.addAll(ClasspathHelper.forClassLoader());
63+
urls.addAll(ClasspathHelper.forJavaClassPath());
64+
for (URL url : urls) {
65+
if (valid(url)) {
66+
logger.debug(url.toString());
67+
filteredUrls.add(url);
68+
}
69+
}
70+
return filteredUrls;
71+
}
72+
73+
public static Class get(String generator) {
74+
return get(new Reflections(new ConfigurationBuilder().addUrls(getUrls()).addScanners(new SubTypesScanner())), generator);
75+
}
76+
77+
public static Class get(Reflections reflections, String generatorString) {
78+
for (Class<? extends PathGeneratorBase> generatorClass : reflections.getSubTypesOf(PathGeneratorBase.class)) {
79+
if (generatorClass.getSimpleName().equalsIgnoreCase(generatorString)) {
80+
return generatorClass;
81+
}
82+
}
83+
throw new GeneratorFactoryException("No suitable generator found with name: " + generatorString);
84+
}
85+
}

graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* #L%
2727
*/
2828

29+
import java.lang.reflect.InvocationTargetException;
2930
import java.util.ArrayList;
3031
import java.util.List;
3132
import java.util.concurrent.TimeUnit;
@@ -110,6 +111,13 @@ public void exitGenerator(GeneratorParser.GeneratorContext context) {
110111
pathGenerators.add(new AStarPath((ReachedStopCondition) stopCondition));
111112
} else if ("shortest_all_paths".equals(generatorName) || "shortestallpaths".equals(generatorName)) {
112113
pathGenerators.add(new ShortestAllPaths(stopCondition));
114+
} else {
115+
Class generatorClass = GeneratorFactoryScanner.get(generatorName);
116+
try {
117+
pathGenerators.add((PathGenerator) generatorClass.getConstructor(StopCondition.class).newInstance(stopCondition));
118+
} catch (Exception e) {
119+
throw new GeneratorFactoryException("No suitable generator found with name: " + generatorName + ", because: " + e.getCause());
120+
}
113121
}
114122
stopConditions.clear();
115123
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.graphwalker.dsl;
2+
3+
import org.graphwalker.dsl.antlr.generator.GeneratorFactoryScanner;
4+
import org.junit.Test;
5+
6+
import static org.junit.Assert.assertNotNull;
7+
8+
public class GeneratorFactoryScannerTest {
9+
@Test
10+
public void validPluginGenerator() {
11+
assertNotNull(GeneratorFactoryScanner.get("PluginGenerator"));
12+
}
13+
}

graphwalker-dsl/src/test/java/org/graphwalker/dsl/GeneratorFactoryTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,17 @@
5050
import org.graphwalker.core.generator.WeightedRandomPath;
5151
import org.graphwalker.dsl.antlr.DslException;
5252
import org.graphwalker.dsl.antlr.generator.GeneratorFactory;
53+
import org.graphwalker.dsl.antlr.generator.GeneratorFactoryException;
54+
import org.graphwalker.generator.PluginGenerator;
55+
import org.junit.Assert;
5356
import org.junit.Test;
5457

5558
/**
5659
* Created by krikar on 5/14/14.
5760
*/
5861
public class GeneratorFactoryTest {
5962

60-
@Test(expected = DslException.class)
63+
@Test(expected = GeneratorFactoryException.class)
6164
public void unvalidGenerator() {
6265
GeneratorFactory.parse("kskskdhfh(edge_coverage(100))");
6366
}
@@ -310,4 +313,15 @@ public void multipleAlternativeStopConditions() {
310313
assertThat(generator.getStopCondition(), instanceOf(AlternativeCondition.class));
311314
assertThat(((AlternativeCondition) generator.getStopCondition()).getStopConditions().size(), is(3));
312315
}
316+
317+
/**
318+
* Tries to load a plugin generator that should be found in the classpath
319+
*/
320+
@Test
321+
public void plugin_generator() {
322+
PathGenerator generator = GeneratorFactory.parse("pluginGenerator(edge_coverage(100))");
323+
Assert.assertThat(generator, instanceOf(PluginGenerator.class));
324+
Assert.assertThat(generator.getStopCondition(), instanceOf(EdgeCoverage.class));
325+
Assert.assertThat(((EdgeCoverage) generator.getStopCondition()).getPercent(), is(100));
326+
}
313327
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.graphwalker.generator;
2+
3+
import org.graphwalker.core.condition.StopCondition;
4+
import org.graphwalker.core.generator.NoPathFoundException;
5+
import org.graphwalker.core.generator.PathGeneratorBase;
6+
import org.graphwalker.core.machine.Context;
7+
import org.graphwalker.core.model.Element;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
import java.util.List;
12+
import java.util.Random;
13+
14+
/**
15+
* A generator that is a code copy of the RandomPathGenerator.
16+
* It serves the purpose of verifying the runtime loading of an plugin generator
17+
*/
18+
public class PluginGenerator extends PathGeneratorBase<StopCondition> {
19+
20+
private static final Logger LOG = LoggerFactory.getLogger(PluginGenerator.class);
21+
22+
private final Random random = new Random(System.nanoTime());
23+
24+
public PluginGenerator(StopCondition stopCondition) {
25+
setStopCondition(stopCondition);
26+
}
27+
28+
@Override
29+
public Context getNextStep() {
30+
Context context = super.getNextStep();
31+
Element currentElement = context.getCurrentElement();
32+
List<Element> elements = context.filter(context.getModel().getElements(currentElement));
33+
if (elements.isEmpty()) {
34+
LOG.error("currentElement: " + currentElement);
35+
LOG.error("context.getModel().getElements(): " + context.getModel().getElements());
36+
throw new NoPathFoundException(context.getCurrentElement());
37+
}
38+
context.setCurrentElement(elements.get(random.nextInt(elements.size())));
39+
return context;
40+
}
41+
42+
@Override
43+
public boolean hasNextStep() {
44+
return !getStopCondition().isFulfilled();
45+
}
46+
47+
}

0 commit comments

Comments
 (0)