diff --git a/README.md b/README.md index 9ecedee8..1916a42f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,12 @@ Requirements ------------ * Java 8 +1.0.5 +----- +* Single Maven plugin, glossary functionality moved into the same plugin that provides execution and report building, + original glossary-builder kept in place to avoid breakages. Removed XML variant of the Glossary in favour of json. + + 1.0.4 ----- * Added a checkbox to the report to optionally hide skipped steps diff --git a/core/src/main/java/com/technophobia/substeps/glossary/StepDescriptor.java b/api/src/main/java/com/technophobia/substeps/glossary/StepDescriptor.java similarity index 100% rename from core/src/main/java/com/technophobia/substeps/glossary/StepDescriptor.java rename to api/src/main/java/com/technophobia/substeps/glossary/StepDescriptor.java diff --git a/core/src/main/java/com/technophobia/substeps/glossary/StepImplementationsDescriptor.java b/api/src/main/java/com/technophobia/substeps/glossary/StepImplementationsDescriptor.java similarity index 100% rename from core/src/main/java/com/technophobia/substeps/glossary/StepImplementationsDescriptor.java rename to api/src/main/java/com/technophobia/substeps/glossary/StepImplementationsDescriptor.java diff --git a/core/src/main/java/com/technophobia/substeps/model/exception/SubstepsRuntimeException.java b/api/src/main/java/com/technophobia/substeps/model/exception/SubstepsRuntimeException.java similarity index 100% rename from core/src/main/java/com/technophobia/substeps/model/exception/SubstepsRuntimeException.java rename to api/src/main/java/com/technophobia/substeps/model/exception/SubstepsRuntimeException.java diff --git a/api/src/main/java/org/substeps/report/IReportBuilder.java b/api/src/main/java/org/substeps/report/IReportBuilder.java index 02aeb816..a06d8632 100644 --- a/api/src/main/java/org/substeps/report/IReportBuilder.java +++ b/api/src/main/java/org/substeps/report/IReportBuilder.java @@ -8,5 +8,6 @@ public interface IReportBuilder { void buildFromDirectory(File sourceDataDir); + void buildFromDirectory(File sourceDataDir, File stepImplsJson); } diff --git a/core/src/main/java/com/technophobia/substeps/glossary/XMLSubstepsGlossarySerializer.java b/core/src/main/java/com/technophobia/substeps/glossary/XMLSubstepsGlossarySerializer.java deleted file mode 100644 index 3172fa3f..00000000 --- a/core/src/main/java/com/technophobia/substeps/glossary/XMLSubstepsGlossarySerializer.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Technophobia Ltd 2012 - * - * This file is part of Substeps. - * - * Substeps is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Substeps is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Substeps. If not, see . - */ -package com.technophobia.substeps.glossary; - -import com.thoughtworks.xstream.XStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.jar.JarFile; -import java.util.zip.ZipEntry; - -/* - * Copyright Technophobia Ltd 2012 - * - * This file is part of Substeps. - * - * Substeps is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Substeps is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Substeps. If not, see . - */ -public class XMLSubstepsGlossarySerializer { - - public static final String XML_FILE_NAME = "substeps-metainfo.xml"; - - private final Logger log = LoggerFactory.getLogger(XMLSubstepsGlossarySerializer.class); - - - public String toXML(final List classStepTags) { - - final XStream xstream = new XStream(); - return xstream.toXML(classStepTags); - } - - - public List fromXML(final InputStream inputStream) { - final XStream xstream = new XStream(); - return (List) xstream.fromXML(inputStream); - } - - - public List loadStepImplementationsDescriptorFromJar( - final JarFile jarFileForClass) { - - List classStepTagList = null; - - final ZipEntry entry = jarFileForClass - .getEntry(XMLSubstepsGlossarySerializer.XML_FILE_NAME); - - if (entry != null) { - - try { - final InputStream is = jarFileForClass.getInputStream(entry); - - classStepTagList = fromXML(is); - - } catch (final IOException e) { - log.error("Error loading from jarfile: ", e); - } - } else { - log.error("couldn't locate file in jar: " + XMLSubstepsGlossarySerializer.XML_FILE_NAME); - } - - return classStepTagList; - } - -} diff --git a/core/src/main/resources/static/css/glossary.css b/core/src/main/resources/static/css/glossary.css new file mode 100644 index 00000000..a453767b --- /dev/null +++ b/core/src/main/resources/static/css/glossary.css @@ -0,0 +1,7 @@ + .dataTables_filter{ + visibility:hidden; + } + + #glossary-table_wrapper div.row-fluid:nth-of-type(1) { + display:none; + } \ No newline at end of file diff --git a/core/src/main/resources/static/js/glossary.js b/core/src/main/resources/static/js/glossary.js new file mode 100644 index 00000000..6704bd08 --- /dev/null +++ b/core/src/main/resources/static/js/glossary.js @@ -0,0 +1,55 @@ +$(document).ready(function() { + + var glossaryTable = $('#glossary-table').DataTable({ + paging: false, + searching: true, + "data": glossary, + "columns": [ + {data: 'section', title: "Section"}, + {data: 'expression', title: "Expression"}, + {data: 'description', title: "Description"}, + {data: 'example', title: "Example"} + ], + "dom" : "<'row-fluid'<'col-sm-6'l><'col-sm-6'f>>" + + "<'row-fluid'<'col-sm-12'tr>>" + + "<'row-fluid'<'col-sm-5'i><'col-sm-7'p>>" + // , + // sDom: '<"search-box"r><"H"lf>t<"F"ip>' + }); + +$('#glossarySearchField').keyup(function(){ + glossaryTable.search($(this).val()).draw() ; +}) + + + + +}); + + + + + +$('#substep-usage-show-hide').on('click', function () { + + toggleShowHide(this); + +}); + +$('#stepimpl-usage-show-hide').on('click', function () { + + toggleShowHide(this); + +}); + +function toggleShowHide(elem){ + if ($(elem).text() == 'Hide') { + $(elem).text('Show') + } + else { + $(elem).text('Hide'); + } +} + + + diff --git a/core/src/main/scala/org/substeps/report/GlossaryTemplate.scala b/core/src/main/scala/org/substeps/report/GlossaryTemplate.scala new file mode 100644 index 00000000..29514846 --- /dev/null +++ b/core/src/main/scala/org/substeps/report/GlossaryTemplate.scala @@ -0,0 +1,102 @@ +package org.substeps.report + +/** + * Created by ian on 06/02/17. + */ +trait GlossaryTemplate { + + def buildGlossaryReport(dateTimeString :String) ={ + + s""" + + + + + Substeps Glossary + + + + + + + + + + + + + + + +
+ +
+
+ +
+
+

Glossary

+
+
+
+
+ +
+
+ + +
+
+
+
+
+ +
+ + + + + + + + + + +
SectionExpressionDescriptionExample
+
+
+
+ + + + + + + + + + + """ + + } +} diff --git a/core/src/main/scala/org/substeps/report/Model.scala b/core/src/main/scala/org/substeps/report/Model.scala index ab233d7c..9f865e34 100644 --- a/core/src/main/scala/org/substeps/report/Model.scala +++ b/core/src/main/scala/org/substeps/report/Model.scala @@ -76,3 +76,25 @@ case object State { case class JsTreeNode(id : String, text: String, icon : String, children : Option[List[JsTreeNode]], state : State, li_attr : Option[Map[String,String]] = None) +case class StepImplDesc(expressions : List[StepDesc],className: String ) + +case class StepDesc(expression: String , + regex: String , + example: String , + section: String , + description: String , + parameterNames: List[String] , + parameterClassNames: List[String] + ) + +case class GlossaryElement( + section: String , + expression: String , + + className: String, + regex: String , + example: String , + description: String , + parameterNames: List[String] , + parameterClassNames: List[String] + ) \ No newline at end of file diff --git a/core/src/main/scala/org/substeps/report/ReportBuilder.scala b/core/src/main/scala/org/substeps/report/ReportBuilder.scala index 604e1c0f..ca646db8 100644 --- a/core/src/main/scala/org/substeps/report/ReportBuilder.scala +++ b/core/src/main/scala/org/substeps/report/ReportBuilder.scala @@ -71,7 +71,7 @@ object ReportBuilder { /** * Created by ian on 30/06/16. */ -class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTreeTemplate { +class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTreeTemplate with GlossaryTemplate { @BeanProperty var reportDir : File = new File(".") @@ -89,8 +89,26 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr } } + def buildGlossaryData(sourceJsonFile : File) = { + implicit val formats = Serialization.formats(NoTypeHints) + + val data = read[List[StepImplDesc]](sourceJsonFile) + + val glossaryElements = + data.map(sid => sid.expressions.map(sd => { + GlossaryElement(sd.section, sd.expression, sid.className, sd.regex, sd.example, sd.description, sd.parameterNames, sd.parameterClassNames) + })).flatten + + glossaryElements + } def buildFromDirectory(sourceDataDir: File): Unit = { + buildFromDirectory(sourceDataDir, null) + } + + def buildFromDirectory(sourceDataDir: File, stepImplsJson : File): Unit = { + + reportDir.mkdir() @@ -116,10 +134,12 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr val stats : ExecutionStats = buildExecutionStats(srcData) + val localDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(srcData._1.timestamp), ZoneId.systemDefault()); + val dateTimeString = localDate.format(DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss")) + - val reportFrameHtml = buildReportFrame(srcData._1, stats - ) + val reportFrameHtml = buildReportFrame(srcData._1, stats, dateTimeString) withWriter(reportFrameHTML, writer => writer.append(reportFrameHtml)) @@ -142,10 +162,35 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr withWriter(usgaeTreeHTMLFile, writer => writer.append(usageTreeHtml) ) + createGlossary(stepImplsJson, dateTimeString) + } + + def createGlossary(stepImplsJson : File, dateTimeString :String) = { + + if (Option(stepImplsJson).isDefined) { + val glossaryHTML = createFile("glossary.html") + val glossaryContent = buildGlossaryReport(dateTimeString) + + // TODO - pass in the path to step impls.json + val srcJsonFile = new File("/home/ian/projects/github/substeps-webdriver/target/classes/stepimplementations.json") + + val glossaryData = buildGlossaryData(srcJsonFile) + val glossaryJsFile = createFile("glossary-data.js") + writeGlossaryJs(glossaryJsFile, glossaryData) + withWriter(glossaryHTML, writer => writer.append(glossaryContent)) + } } + def writeGlossaryJs(glossaryJSFile : File, glossaryData : List[GlossaryElement]) = { + withWriter(glossaryJSFile, writer => { + implicit val formats = Serialization.formats(NoTypeHints) + writer.append("var glossary=") + writer.append(writePretty(glossaryData)) + writer.append(";\n") + }) + } def writeStatsJs(statsJsFile: File, stats: (List[Counters], List[Counters])) = { diff --git a/core/src/main/scala/org/substeps/report/ReportFrame.scala b/core/src/main/scala/org/substeps/report/ReportFrame.scala index 3b7c73bb..7f24233c 100644 --- a/core/src/main/scala/org/substeps/report/ReportFrame.scala +++ b/core/src/main/scala/org/substeps/report/ReportFrame.scala @@ -34,7 +34,7 @@ trait ReportFrameTemplate { } - def buildReportFrame(rootNodeSummary: RootNodeSummary, stats : ExecutionStats) = { + def buildReportFrame(rootNodeSummary: RootNodeSummary, stats : ExecutionStats, dateTimeString : String) = { val featureProgressBlock = buildStatsBlock("Features", stats.featuresCounter) val scenarioProgressBlock = buildStatsBlock("Scenarios", stats.scenarioCounters) @@ -43,9 +43,6 @@ trait ReportFrameTemplate { val reportTitle = Option(rootNodeSummary.description).getOrElse("Substeps Test Report") - val localDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(rootNodeSummary.timestamp), ZoneId.systemDefault()); - val dateTimeString = localDate.format(DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss")) - // TODO pull out some of the other things from the node summary - tags, nonfatal tags and environment @@ -103,6 +100,7 @@ trait ReportFrameTemplate { |
  • Scenario tag summary
  • |
  • Test detail
  • |
  • Usage Beta
  • + |
  • Glossary
  • | | | diff --git a/core/src/main/scala/org/substeps/report/UsageTreeTemplate.scala b/core/src/main/scala/org/substeps/report/UsageTreeTemplate.scala index a2da2ba6..cc9a097c 100644 --- a/core/src/main/scala/org/substeps/report/UsageTreeTemplate.scala +++ b/core/src/main/scala/org/substeps/report/UsageTreeTemplate.scala @@ -46,8 +46,8 @@ trait UsageTreeTemplate { diff --git a/core/src/test/scala/org/substeps/report/ReportBuilderTest.scala b/core/src/test/scala/org/substeps/report/ReportBuilderTest.scala index e5aac94d..105c433b 100644 --- a/core/src/test/scala/org/substeps/report/ReportBuilderTest.scala +++ b/core/src/test/scala/org/substeps/report/ReportBuilderTest.scala @@ -7,11 +7,11 @@ import java.time.format.DateTimeFormatter import com.google.common.base.Strings import com.google.common.io.Files +import com.technophobia.substeps.glossary.StepImplementationsDescriptor import scala.collection.JavaConversions._ import scala.collection.JavaConverters._ import org.scalatest._ - import org.hamcrest.text.IsEqualIgnoringWhiteSpace.equalToIgnoringWhiteSpace /** @@ -121,24 +121,7 @@ class ReportBuilderTest extends FlatSpec with ShouldMatchers{ substepsStatsjs shouldBe defined - - } - -// case class Attr(id : String) -// case class Data (title : String, attr : Attr, icon : String) -// -// case class DataHolder(id : String, title : String, icon : String) { -// def toMap = Map("data" -> Map("title" -> title, "attr" -> Map("id" -> id), "icon" -> icon)) -// } -// -// -// case class State(opened : Boolean) -// case class JsTreeNode(id : String, text: String, icon : String, children : Option[List[JsTreeNode]], state : State) -// -// case class Node(state:String, children : List[DataHolder]) - - } diff --git a/glossary/src/main/java/com/technophobia/substeps/glossary/SubstepsGlossaryMojo.java b/glossary/src/main/java/com/technophobia/substeps/glossary/SubstepsGlossaryMojo.java index b1a41724..b209329b 100644 --- a/glossary/src/main/java/com/technophobia/substeps/glossary/SubstepsGlossaryMojo.java +++ b/glossary/src/main/java/com/technophobia/substeps/glossary/SubstepsGlossaryMojo.java @@ -79,7 +79,7 @@ public class SubstepsGlossaryMojo extends AbstractMojo { @Parameter private final GlossaryPublisher glossaryPublisher = null; - private final XMLSubstepsGlossarySerializer serializer = new XMLSubstepsGlossarySerializer(); +// private final XMLSubstepsGlossarySerializer serializer = new XMLSubstepsGlossarySerializer(); private List runJavaDoclet(final String classToDocument) { @@ -154,6 +154,16 @@ private String resolveClassToPath(final String classToDocument, final String dir @Override public void execute() throws MojoExecutionException, MojoFailureException { + log.warn("********************************************\n\n" + + "Substeps Glossary Mojo is now deprecated, an HTML glossary can be produced as part of the execution report by adding this execution to the substeps-maven-plugin:\n\n" + + "\n" + + " Build SubSteps Glossary\n" + + " process-test-resources\n" + + " \n" + + " generate-docs\n" + + " \n" + + ""); + final HashSet loadedClasses = new HashSet(); final List classStepTags = new ArrayList(); @@ -193,7 +203,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { } // always do this - saveXMLFile(classStepTags); +// saveXMLFile(classStepTags); // and this! saveJsonFile(classStepTags); @@ -214,17 +224,17 @@ private void saveJsonFile(final List classStepTag writeOutputFile(json, "stepimplementations.json"); } - /** - * @param classStepTags - */ - private void saveXMLFile(final List classStepTags) { - // got them all now serialize - - final String xml = serializer.toXML(classStepTags); - - writeOutputFile(xml, XMLSubstepsGlossarySerializer.XML_FILE_NAME); - - } +// /** +// * @param classStepTags +// */ +// private void saveXMLFile(final List classStepTags) { +// // got them all now serialize +// +// final String xml = serializer.toXML(classStepTags); +// +// writeOutputFile(xml, XMLSubstepsGlossarySerializer.XML_FILE_NAME); +// +// } private void writeOutputFile(String xml, String filename) { final File output = new File(outputDirectory, filename); @@ -254,22 +264,22 @@ private void loadStepTagsFromJar(final JarFile jarFileForClass, // TODO - change this to load from the json version - final ZipEntry entry = jarFileForClass - .getEntry(XMLSubstepsGlossarySerializer.XML_FILE_NAME); - - if (entry != null) { - - final List classStepTagList = serializer - .loadStepImplementationsDescriptorFromJar(jarFileForClass); - - classStepTags.addAll(classStepTagList); - - for (final StepImplementationsDescriptor descriptor : classStepTagList) { - loadedClasses.add(descriptor.getClassName()); - } - } else { - log.error("couldn't locate file in jar: " + XMLSubstepsGlossarySerializer.XML_FILE_NAME); - } +// final ZipEntry entry = jarFileForClass +// .getEntry(XMLSubstepsGlossarySerializer.XML_FILE_NAME); +// +// if (entry != null) { +// +// final List classStepTagList = serializer +// .loadStepImplementationsDescriptorFromJar(jarFileForClass); +// +// classStepTags.addAll(classStepTagList); +// +// for (final StepImplementationsDescriptor descriptor : classStepTagList) { +// loadedClasses.add(descriptor.getClassName()); +// } +// } else { +// log.error("couldn't locate file in jar: " + XMLSubstepsGlossarySerializer.XML_FILE_NAME); +// } } diff --git a/runner/Maven/pom.xml b/runner/Maven/pom.xml index 748c79e3..9791b5e5 100644 --- a/runner/Maven/pom.xml +++ b/runner/Maven/pom.xml @@ -110,13 +110,27 @@ test + + + sun.jdk + tools + ${java.version} + system + ${java.home}/../lib/tools.jar + - + + com.google.code.gson + gson + + + + diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/glossary/CustomDoclet.java b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/CustomDoclet.java new file mode 100644 index 00000000..56ee6eeb --- /dev/null +++ b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/CustomDoclet.java @@ -0,0 +1,184 @@ +/* + * Copyright Technophobia Ltd 2012 + * + * This file is part of Substeps. + * + * Substeps is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Substeps is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substeps. If not, see . + */ +package com.technophobia.substeps.glossary; + +import com.sun.javadoc.Doclet; +import com.sun.javadoc.*; +import com.technophobia.substeps.model.SubSteps.Step; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * TODO + * + * @author imoore + */ +public class CustomDoclet extends Doclet { + + private static final Logger log = LoggerFactory.getLogger(CustomDoclet.class); + private static List classStepTagsList; + + + public static void setExpressionList(final List expressionList) { + classStepTagsList = expressionList; + } + + + public static List getExpressions() { + return classStepTagsList; + } + + + public static boolean start(final RootDoc root) { + + final ClassDoc[] classes = root.classes(); + + for (final ClassDoc cd : classes) { + + final StepImplementationsDescriptor classStepTags = new StepImplementationsDescriptor( + cd.qualifiedName()); + + classStepTagsList.add(classStepTags); + + Class implClass = null; + Method[] implMethods = null; + try { + + implClass = root.getClass().getClassLoader().loadClass(cd.qualifiedTypeName()); + implMethods = implClass.getMethods(); + + final MethodDoc[] methods = cd.methods(); + + for (final MethodDoc md : methods) { + + final Method underlyingMethod = getMethod(implMethods, md); + + if (underlyingMethod != null) { + final Step annotation = underlyingMethod.getAnnotation(Step.class); + + if (annotation != null) + + { + final StepDescriptor expression = new StepDescriptor(); + + classStepTags.addStepTags(expression); + + expression.setDescription(md.commentText().replaceAll("\n", " ")); + + expression.setExample(getSingleJavadocTagValue(md, "example")); + expression.setSection(getSingleJavadocTagValue(md, "section")); + + String line = annotation.value(); + expression.setRegex(line); + + final Parameter[] parameters = md.parameters(); + if (parameters != null && parameters.length > 0) { + + String[] paramNames = new String[parameters.length]; + String[] paramTypes = new String[parameters.length]; + + int i = 0; + for (final Parameter p : parameters) { + // replace any captures with + + line = line.replaceFirst("\\([^\\)]*\\)", "<" + p.name() + ">"); + + paramNames[i] = p.name(); + paramTypes[i] = p.typeName(); + i++; + } + + expression.setParameterClassNames(paramTypes); + expression.setParameterNames(paramNames); + } + line = line.replaceAll("\\?", ""); + line = line.replaceAll("\\\\", ""); + expression.setExpression(line); + + } + } + } + } catch (final ClassNotFoundException e) { + log.error("ClassNotFoundException", e); + } + + } + + return true; + } + + + /** + * @param md + * @param tagName + */ + private static String getSingleJavadocTagValue(final MethodDoc md, final String tagName) { + String rtn = null; + final Tag[] tags = md.tags(tagName); + if (tags != null && tags.length > 0) { + rtn = tags[0].text().replace("@" + tagName, ""); + rtn.replaceAll("\n", " "); + } + + return rtn != null ? rtn : ""; + } + + + /** + * @param implMethods + * @param md + * @return + */ + private static Method getMethod(final Method[] implMethods, final MethodDoc md) { + Method rtn = null; + + int desiredNumberOfParams = 0; + + final Parameter[] parameters = md.parameters(); + if (parameters != null) { + desiredNumberOfParams = parameters.length; + } + + final List candidateMethods = new ArrayList(); + + // try and match by name + for (final Method m : implMethods) { + if (m.getName().equals(md.name())) { + if (m.getParameterTypes().length == desiredNumberOfParams) { + candidateMethods.add(m); + } + } + } + + if (candidateMethods.size() > 1) { + throw new IllegalStateException("need to impl parameter type matching"); + } + + if (!candidateMethods.isEmpty()) { + rtn = candidateMethods.get(0); + } + + return rtn; + } + +} diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/glossary/FileBasedGlossaryPublisher.java b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/FileBasedGlossaryPublisher.java new file mode 100644 index 00000000..9fccb414 --- /dev/null +++ b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/FileBasedGlossaryPublisher.java @@ -0,0 +1,86 @@ +package com.technophobia.substeps.glossary; + +import com.google.common.collect.Ordering; +import com.google.common.collect.TreeMultimap; +import com.google.common.io.Files; +import com.technophobia.substeps.model.exception.SubstepsRuntimeException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * Created by ian on 25/02/16. + */ +public abstract class FileBasedGlossaryPublisher implements GlossaryPublisher { + + private static final Logger log = LoggerFactory.getLogger(FileBasedGlossaryPublisher.class); + + /** + * @parameter + */ + private File outputFile; + + + @Override + public void publish(final List stepimplementationDescriptors) { + + final Map> sectionSorted = sortStepDescriptions(stepimplementationDescriptors); + + final String output = buildFileContents(sectionSorted); + + writeOutputFile(output, outputFile != null ? outputFile : new File(getDefaultFileName())); + } + + public abstract String buildFileContents(final Map> sectionSorted); + + public abstract String getDefaultFileName(); + + + private Comparator expressionComparator = (s1, s2) -> s1.getExpression().compareTo(s2.getExpression()); + + private String getSection(StepDescriptor stepTag) { + boolean noTag = stepTag.getSection() == null || stepTag.getSection().isEmpty(); + return noTag ? "Miscellaneous" : stepTag.getSection(); + } + + + private Map> sortStepDescriptions(List stepimplementationDescriptors) { + + TreeMultimap sections = TreeMultimap.create(Ordering.natural(), expressionComparator); + + for (final StepImplementationsDescriptor descriptor : stepimplementationDescriptors) { + for (final StepDescriptor step : descriptor.getExpressions()) { + sections.put(getSection(step), step); + } + } + + return sections.asMap(); + } + + + private void writeOutputFile(String content, File outputFile) { + if (outputFile.exists() && !outputFile.delete()){ + throw new SubstepsRuntimeException("failed to delete output file: " + outputFile.getAbsolutePath()); + } + + // write out + try { + if (outputFile.createNewFile()) { + Files.write(content, outputFile, Charset.defaultCharset()); + } else { + + log.error("unable to create new file: " + outputFile.getAbsolutePath()); + + } + } catch (final IOException e) { + log.error("IOException writing file", e); + } + } +} diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/glossary/GlossaryPublisher.java b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/GlossaryPublisher.java new file mode 100644 index 00000000..7d3f999f --- /dev/null +++ b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/GlossaryPublisher.java @@ -0,0 +1,33 @@ +/* + * Copyright Technophobia Ltd 2012 + * + * This file is part of Substeps. + * + * Substeps is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Substeps is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substeps. If not, see . + */ +package com.technophobia.substeps.glossary; + +import java.util.List; + +/** + * @author ian + */ +public interface GlossaryPublisher { + + /** + * @param classStepTags + */ + void publish(List classStepTags); + +} diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/glossary/HTMLSubstepsPublisher.java b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/HTMLSubstepsPublisher.java new file mode 100644 index 00000000..da82613d --- /dev/null +++ b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/HTMLSubstepsPublisher.java @@ -0,0 +1,84 @@ +/* + * Copyright Technophobia Ltd 2012 + * + * This file is part of Substeps. + * + * Substeps is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Substeps is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substeps. If not, see . + */ +package com.technophobia.substeps.glossary; + +import org.apache.commons.lang3.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author ian + */ +public class HTMLSubstepsPublisher extends FileBasedGlossaryPublisher implements GlossaryPublisher { + + private static final Logger log = LoggerFactory.getLogger(HTMLSubstepsPublisher.class); + + @Override + public String getDefaultFileName() { + return "stepimplementations.html"; + } + + /** + * @param sectionSorted + */ + @Override + public String buildFileContents(final Map> sectionSorted) { + final StringBuilder buf = new StringBuilder(); + + buf.append(" \n\n"); + + final Set>> entrySet = sectionSorted.entrySet(); + + for (final Entry> e : entrySet) { + buf.append(String.format(TABLE_ROW_SECTION_FORMAT, e.getKey())).append("\n"); + + buildStepTagRows(buf, e.getValue()); + } + + buf.append("
    Keyword Example Description
    "); + return buf.toString(); + } + + + private void buildStepTagRows(final StringBuilder buf, final Collection infos) { + + + for (final StepDescriptor info : infos) { + + log.debug("info non escaped: " + info.getExpression() + "\n\tescaped:\n" + + StringEscapeUtils.escapeHtml4(info.getExpression())); + + buf.append( + String.format(TABLE_ROW_FORMAT, + StringEscapeUtils.escapeHtml4(info.getExpression()), info.getExample(), + StringEscapeUtils.escapeHtml4(info.getDescription()))).append("\n"); + + } + } + + private static final String TABLE_ROW_SECTION_FORMAT = "%s"; + + private static final String TABLE_ROW_FORMAT = "%s%s%s"; + +} diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/glossary/IncludeProjectDependenciesComponentConfigurator.java b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/IncludeProjectDependenciesComponentConfigurator.java new file mode 100644 index 00000000..6a1719dc --- /dev/null +++ b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/IncludeProjectDependenciesComponentConfigurator.java @@ -0,0 +1,111 @@ +/* + * NB. Although this file is part of the Technophobia SubSteps glossary builder, this implementation was taken from + * http://maven.40175.n5.nabble.com/Adding-project-dependencies-and-generated-classes-to-classpath-of-my-plugin-td110119.html + */ +package com.technophobia.substeps.glossary; + +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.component.configurator.AbstractComponentConfigurator; +import org.codehaus.plexus.component.configurator.ComponentConfigurationException; +import org.codehaus.plexus.component.configurator.ConfigurationListener; +import org.codehaus.plexus.component.configurator.converters.composite.ObjectWithFieldsConverter; +import org.codehaus.plexus.component.configurator.converters.special.ClassRealmConverter; +import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; +import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; +import org.codehaus.plexus.configuration.PlexusConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * A custom ComponentConfigurator which adds the project's runtime classpath elements to the + * + * @author Brian Jackson + * @plexus.component role="org.codehaus.plexus.component.configurator.ComponentConfigurator" + * role-hint="include-project-dependencies" + * @plexus.requirement role="org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup" + * role-hint="default" + * @since Aug 1, 2008 3:04:17 PM + */ +public class IncludeProjectDependenciesComponentConfigurator extends AbstractComponentConfigurator { + + private static final Logger logger = LoggerFactory + .getLogger(IncludeProjectDependenciesComponentConfigurator.class); + + + @Override + public void configureComponent(final Object component, + final PlexusConfiguration configuration, + final ExpressionEvaluator expressionEvaluator, + final ClassRealm containerRealm, + final ConfigurationListener listener) + throws ComponentConfigurationException { + + addProjectDependenciesToClassRealm(expressionEvaluator, containerRealm); + + converterLookup.registerConverter(new ClassRealmConverter(containerRealm)); + + final ObjectWithFieldsConverter converter = new ObjectWithFieldsConverter(); + + converter.processConfiguration(converterLookup, component, containerRealm, + configuration, expressionEvaluator, listener); + } + + + private void addProjectDependenciesToClassRealm(final ExpressionEvaluator expressionEvaluator, + final ClassRealm containerRealm) throws ComponentConfigurationException { + + List testClasspathElements = null; + + try { + // noinspection unchecked + + testClasspathElements = (List) expressionEvaluator + .evaluate("${project.testClasspathElements}"); + + } catch (final ExpressionEvaluationException e) { + throw new ComponentConfigurationException( + "There was a problem evaluating: ${project.runtimeClasspathElements}", e); + } + + if (testClasspathElements != null) { + // Add the project test dependencies to the ClassRealm + final URL[] testUrls = buildURLs(testClasspathElements); + for (final URL url : testUrls) { + containerRealm.addURL(url); + + } + } + + } + + + private URL[] buildURLs(final List runtimeClasspathElements) + throws ComponentConfigurationException { + // Add the projects classes and dependencies + final List urls = new ArrayList(runtimeClasspathElements.size()); + for (final String element : runtimeClasspathElements) { + try { + final URL url = new File(element).toURI().toURL(); + urls.add(url); + + // System.out.println("Added to project class loader: " + url); + if (logger.isDebugEnabled()) { + logger.debug("Added to project class loader: " + url); + } + } catch (final MalformedURLException e) { + throw new ComponentConfigurationException("Unable to access project dependency: " + + element, e); + } + } + + // Add the plugin's dependencies (so Trove stuff works if Trove isn't on + return urls.toArray(new URL[urls.size()]); + } + +} \ No newline at end of file diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/glossary/JsonSubstepsPublisher.java b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/JsonSubstepsPublisher.java new file mode 100644 index 00000000..4586a825 --- /dev/null +++ b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/JsonSubstepsPublisher.java @@ -0,0 +1,33 @@ +package com.technophobia.substeps.glossary; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.Collection; +import java.util.Map; + +/** + * Contributed to substeps by Andrew Lee + *

    + * A publisher which produces a json representation of the glossary. + *

    + * The resultant json can be used by Api Viewer + * + * @author Andrew Lee + */ +public class JsonSubstepsPublisher extends FileBasedGlossaryPublisher implements GlossaryPublisher { + + + @Override + public String getDefaultFileName() { + return "stepimplementations.json"; + } + + @Override + public String buildFileContents(final Map> sectionSorted) { + Gson gson = new GsonBuilder().create(); + return gson.toJson(sectionSorted); + } + +} \ No newline at end of file diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/glossary/MarkdownSubstepsPublisher.java b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/MarkdownSubstepsPublisher.java new file mode 100644 index 00000000..a0532825 --- /dev/null +++ b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/MarkdownSubstepsPublisher.java @@ -0,0 +1,87 @@ +/* + * Copyright Technophobia Ltd 2012 + * + * This file is part of Substeps. + * + * Substeps is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Substeps is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substeps. If not, see . + */ +package com.technophobia.substeps.glossary; + +import org.apache.commons.lang3.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author ian + */ +public class MarkdownSubstepsPublisher extends FileBasedGlossaryPublisher implements GlossaryPublisher { + + private static final Logger log = LoggerFactory.getLogger(MarkdownSubstepsPublisher.class); + + @Override + public String getDefaultFileName() { + return "stepimplementations.md"; + } + + + /** + * @param sectionSorted + */ + @Override + public String buildFileContents(final Map> sectionSorted) { + final StringBuilder buf = new StringBuilder(); + + final Set>> entrySet = sectionSorted.entrySet(); + + for (final Entry> e : entrySet) { + buf.append(String.format(TABLE_ROW_SECTION_FORMAT, e.getKey())).append("\n"); + + buildStepTagRows(buf, e.getValue()); + } + + buf.append(""); + return buf.toString(); + } + + + private void buildStepTagRows(final StringBuilder buf, final Collection infos) { + + for (final StepDescriptor info : infos) { + + log.debug("info non escaped: " + info.getExpression() + "\n\tescaped:\n" + + StringEscapeUtils.escapeHtml4(info.getExpression())); + + + + buf.append( + String.format(TABLE_ROW_FORMAT, + StringEscapeUtils.escapeHtml4(info.getExpression()), info.getExample().replaceAll("\n", " "), + StringEscapeUtils.escapeHtml4(info.getDescription()).replaceAll("\n", " "))).append("\n"); + + } + } + + private static final String TABLE_ROW_SECTION_FORMAT = "%s\n" + + "==========\n" + + "| **Keyword** | **Example** | **Description** |\n" + + "| :------------ |:---------------| :-----|"; + + private static final String TABLE_ROW_FORMAT = "| %s | %s | %s |"; + +} diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/glossary/SubstepsGlossaryMojo.java b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/SubstepsGlossaryMojo.java new file mode 100644 index 00000000..a3a05347 --- /dev/null +++ b/runner/Maven/src/main/java/com/technophobia/substeps/glossary/SubstepsGlossaryMojo.java @@ -0,0 +1,365 @@ +/* + * Copyright Technophobia Ltd 2012 + * + * This file is part of Substeps. + * + * Substeps is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Substeps is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substeps. If not, see . + */ +package com.technophobia.substeps.glossary; + +import com.google.common.io.CharStreams; +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.sun.tools.javadoc.Main; +import com.technophobia.substeps.model.SubSteps; +import com.technophobia.substeps.runner.BaseSubstepsMojo; +import com.technophobia.substeps.runner.ExecutionConfig; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +/** + * A Maven plugin to generate a json representation of the step implementations in this library. That json is then used in verious plugins and IDE's etc. + */ +@Mojo(name = "generate-docs", + defaultPhase = LifecyclePhase.GENERATE_RESOURCES, + requiresDependencyResolution = ResolutionScope.TEST, + requiresProject = true, + configurator = "include-project-dependencies") +public class SubstepsGlossaryMojo extends BaseSubstepsMojo { + + private final Logger log = LoggerFactory.getLogger(SubstepsGlossaryMojo.class); + + + /** + */ + @Parameter(defaultValue = "${project}", required = true, readonly = true) + private MavenProject project; + + /** + */ +// @Parameter(required = true) +// private String[] stepImplementationClassNames; + + /** + * @parameter + */ + @Parameter + private final GlossaryPublisher glossaryPublisher = null; + +// private final XMLSubstepsGlossarySerializer serializer = new XMLSubstepsGlossarySerializer(); + + + private List runJavaDoclet(final String classToDocument) { + + final List classStepTagList = new ArrayList(); + + project.getBasedir(); + + String sourceRoot = null; + // what's the path to this class ? + String path = resolveClassToPath(classToDocument, project.getBuild().getSourceDirectory()); + if (path != null) { + // file is in the source path + sourceRoot = project.getBuild().getSourceDirectory(); + } else { + path = resolveClassToPath(classToDocument, project.getBuild().getTestSourceDirectory()); + if (path != null) { + // file is in the test tree + sourceRoot = project.getBuild().getTestSourceDirectory(); + } + } + + if (sourceRoot == null || path == null) { + + log.error("unabled to locate source file"); + // TODO exception ? + } else { + + CustomDoclet.setExpressionList(classStepTagList); + + final String[] args = {"-doclet", "com.technophobia.substeps.glossary.CustomDoclet", + // "-docletpath", + // "/home/ian/projects/webdriverbdd-utils/target/classes", + // // path to this jar ? + "-sourcepath", sourceRoot, // "./src/main/java", // path to + // the step impls / classpath ? + path // javadocStr + // //"/home/ian/projects/github/substeps-webdriver/src/main/java/com/technophobia/webdriver/substeps/impl/AssertionWebDriverSubStepImplementations.java" + }; // list of step impls to have a butcher sat + // "/home/ian/projects/github/substeps-webdriver/src/main/java/com/technophobia/webdriver/substeps/impl/AssertionWebDriverSubStepImplementations.java" + + Main.execute(args); + + } + + return classStepTagList; + } + + + /** + * @param classToDocument = fqn of a class, dotted syntax + * @param dir the directory of the source + * @return + */ + private String resolveClassToPath(final String classToDocument, final String dir) { + String fullpath = null; + log.debug("resolving class to path: " + classToDocument + " in " + dir); + + final String filepath = dir + File.separator + + classToDocument.replace('.', File.separator.charAt(0)) + ".java"; + + log.debug("looking for file: " + filepath); + + final File f = new File(filepath); + + if (f.exists()) { + fullpath = f.getAbsolutePath(); + } + return fullpath; + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + final HashSet loadedClasses = new HashSet(); + + final List classStepTags = new ArrayList(); + + final PluginDescriptor pluginDescriptor = (PluginDescriptor) getPluginContext().get("pluginDescriptor"); + final ClassRealm classRealm = pluginDescriptor.getClassRealm(); + final File classes = new File(project.getBuild().getOutputDirectory()); + try + { + classRealm.addURL(classes.toURI().toURL()); + } + catch (MalformedURLException e) + { + log.error("MalformedURLException adding outputdir", e); +// e.printStackTrace(); + } + + + for(ExecutionConfig cfg : executionConfigs) { + + + for (final String classToDocument : cfg.getStepImplementationClassNames()) { + + log.debug("documenting: " + classToDocument); + + // have we loaded info for this class already ? + if (!loadedClasses.contains(classToDocument)) { + + // where is this class ? + final JarFile jarFileForClass = getJarFileForClass(classToDocument); + if (jarFileForClass != null) { + + log.debug("loading info from jar"); + + // look for the xml file in the jar, load up from + // there + loadStepTagsFromJar(jarFileForClass, classStepTags, loadedClasses); + } else { + log.debug("loading step info from paths"); + // if it's in the project, run the javadoc and collect the + // details + + // TODO - if this class is annotated with AdditionalStepImplementations, lookup those instead.. + + try { + Class stepImplClass = classRealm.loadClass(classToDocument); + + SubSteps.AdditionalStepImplementations additionalStepImpls = stepImplClass.getDeclaredAnnotation(SubSteps.AdditionalStepImplementations.class); + + if (additionalStepImpls != null){ + for(Class c : additionalStepImpls.value()){ + + classStepTags.addAll(runJavaDoclet(c.getCanonicalName())); + } + } + + } catch (ClassNotFoundException e) { + log.error("failed to load class: " + classToDocument, e); + + } + + classStepTags.addAll(runJavaDoclet(classToDocument)); + } + } + } + } + + + if (!classStepTags.isEmpty()) { + + if (glossaryPublisher != null) { + + glossaryPublisher.publish(classStepTags); + + } + + // always do this +// saveXMLFile(classStepTags); + + // and this! + saveJsonFile(classStepTags); + } else { + log.error("no results to write out"); + } + } + + /** + * @param classStepTags + */ + private void saveJsonFile(final List classStepTags) { + + Gson gson = new GsonBuilder().create(); + + final String json = gson.toJson(classStepTags); + + writeOutputFile(json, STEP_IMPLS_JSON_FILENAME); + } + +// /** +// * @param classStepTags +// */ +// private void saveXMLFile(final List classStepTags) { +// // got them all now serialize +// +// final String xml = serializer.toXML(classStepTags); +// +// writeOutputFile(xml, XMLSubstepsGlossarySerializer.XML_FILE_NAME); +// +// } + + private void writeOutputFile(String xml, String filename) { + final File output = new File(outputDirectory, filename); + + if (!outputDirectory.exists()) { + if (!outputDirectory.mkdirs()) { + + throw new IllegalStateException("unable to create output directory"); + } + } + + try { + Files.write(xml, output, Charset.forName("UTF-8")); + } catch (final IOException e) { + log.error("error writing file", e); + } + } + + + /** + * @param jarFileForClass + * @param classStepTags + * @param loadedClasses + */ + private void loadStepTagsFromJar(final JarFile jarFileForClass, + final List classStepTags, final Set loadedClasses) { + + // TODO - change this to load from the json version + + final ZipEntry entry = jarFileForClass + .getEntry("stepimplementations.json"); + + if (entry != null) { + + + try { + final InputStream is = jarFileForClass.getInputStream(entry); + InputStreamReader isr = new InputStreamReader(is); + String src = CharStreams.toString(isr); + + Gson gson = new GsonBuilder().create(); + ListstepDescriptors = gson.fromJson(isr, new TypeToken>() { + }.getType()); + + classStepTags.addAll(stepDescriptors); + + + } catch (final IOException e) { + log.error("Error loading from jarfile: ", e); + } + +// final List classStepTagList = serializer +// .loadStepImplementationsDescriptorFromJar(jarFileForClass); + +// classStepTags.addAll(classStepTagList); + + for (final StepImplementationsDescriptor descriptor : classStepTags) { + loadedClasses.add(descriptor.getClassName()); + } + } else { + log.error("couldn't locate file in jar: stepimplementations.json"); + } + } + + + private String convertClassNameToPath(final String className) { + return className.replace('.', '/') + ".class"; + } + + + private JarFile getJarFileForClass(final String className) { + + JarFile jarFile = null; + final Set artifacts = project.getArtifacts(); + if (artifacts != null) { + for (final Artifact a : artifacts) { + // does this jar contain this class? + try { + final JarFile tempJarFile = new JarFile(a.getFile()); + + final JarEntry jarEntry = tempJarFile + .getJarEntry(convertClassNameToPath(className)); + + if (jarEntry != null) { + jarFile = tempJarFile; + break; + } + } catch (final IOException e) { + log.error("IO Exception opening jar file", e); + } + } + } + return jarFile; + } +} diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/runner/BaseSubstepsMojo.java b/runner/Maven/src/main/java/com/technophobia/substeps/runner/BaseSubstepsMojo.java index af69c2d6..d385c1fa 100644 --- a/runner/Maven/src/main/java/com/technophobia/substeps/runner/BaseSubstepsMojo.java +++ b/runner/Maven/src/main/java/com/technophobia/substeps/runner/BaseSubstepsMojo.java @@ -8,6 +8,7 @@ import org.substeps.report.IExecutionResultsCollector; import org.substeps.report.IReportBuilder; +import java.io.File; import java.util.List; /** @@ -15,6 +16,8 @@ */ public abstract class BaseSubstepsMojo extends AbstractMojo { + protected static final String STEP_IMPLS_JSON_FILENAME = "stepimplementations.json"; + @Parameter(defaultValue = "${session}") protected MavenSession session; @@ -51,6 +54,11 @@ public abstract class BaseSubstepsMojo extends AbstractMojo { @Parameter(defaultValue = "${project}", readonly = true) protected MavenProject project; + /** + * Location of the file. + */ + @Parameter(defaultValue = "${project.build.outputDirectory}", required = true) + protected File outputDirectory; /** * diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/runner/SubstepsReportBuilderMojo.java b/runner/Maven/src/main/java/com/technophobia/substeps/runner/SubstepsReportBuilderMojo.java index 7f91305f..6b3c6d24 100644 --- a/runner/Maven/src/main/java/com/technophobia/substeps/runner/SubstepsReportBuilderMojo.java +++ b/runner/Maven/src/main/java/com/technophobia/substeps/runner/SubstepsReportBuilderMojo.java @@ -35,6 +35,7 @@ import org.substeps.report.IExecutionResultsCollector; import org.substeps.report.IReportBuilder; +import java.io.File; import java.util.List; /** @@ -63,9 +64,9 @@ public void execute() throws MojoExecutionException, MojoFailureException { this.getLog().info("this.session.getGoals(): " + buf.toString()); + File stepImplsJsonFile = new File(outputDirectory, STEP_IMPLS_JSON_FILENAME); - - reportBuilder.buildFromDirectory(this.executionResultsCollector.getDataDir()); + reportBuilder.buildFromDirectory(this.executionResultsCollector.getDataDir(), stepImplsJsonFile); diff --git a/runner/Maven/src/test/java/com/technophobia/substeps/mojo/runner/FakeReportBuilder.java b/runner/Maven/src/test/java/com/technophobia/substeps/mojo/runner/FakeReportBuilder.java index cb7cfc81..91fc96a7 100644 --- a/runner/Maven/src/test/java/com/technophobia/substeps/mojo/runner/FakeReportBuilder.java +++ b/runner/Maven/src/test/java/com/technophobia/substeps/mojo/runner/FakeReportBuilder.java @@ -12,4 +12,9 @@ public class FakeReportBuilder implements IReportBuilder{ public void buildFromDirectory(File sourceDataDir) { } + + @Override + public void buildFromDirectory(File sourceDataDir, File stepImplsJson) { + + } }