diff --git a/acropolis-persistence/src/main/kotlin/org/ephyra/acropolis/persistence/api/persistence/GraphicalAssetPersistence.kt b/acropolis-persistence/src/main/kotlin/org/ephyra/acropolis/persistence/api/persistence/GraphicalAssetPersistence.kt index 38c8a0b..508a94f 100644 --- a/acropolis-persistence/src/main/kotlin/org/ephyra/acropolis/persistence/api/persistence/GraphicalAssetPersistence.kt +++ b/acropolis-persistence/src/main/kotlin/org/ephyra/acropolis/persistence/api/persistence/GraphicalAssetPersistence.kt @@ -31,6 +31,12 @@ class GraphicalAssetPersistence { return repo.findAll() } + /** + * Find a graphical asset by name + * + * @param name The name of the asset + * @return the asset, if found + */ fun find(name: String): GraphicalAssetEntity? { val asset = repo.findByName(name) return if (asset.isPresent) asset.get() else null diff --git a/acropolis-persistence/src/main/kotlin/org/ephyra/acropolis/persistence/impl/GraphicalAssetRepository.kt b/acropolis-persistence/src/main/kotlin/org/ephyra/acropolis/persistence/impl/GraphicalAssetRepository.kt index 7f50572..094f620 100644 --- a/acropolis-persistence/src/main/kotlin/org/ephyra/acropolis/persistence/impl/GraphicalAssetRepository.kt +++ b/acropolis-persistence/src/main/kotlin/org/ephyra/acropolis/persistence/impl/GraphicalAssetRepository.kt @@ -8,5 +8,11 @@ import java.util.Optional * Repository for storing graphical assets */ interface GraphicalAssetRepository : CrudRepository { + /** + * Find a graphical asset by name + * + * @param name The name of the asset + * @return the asset, if found + */ fun findByName(name: String): Optional } diff --git a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/IImageSource.kt b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/IImageSource.kt index 26fc716..4832a88 100644 --- a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/IImageSource.kt +++ b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/IImageSource.kt @@ -2,6 +2,15 @@ package org.ephyra.acropolis.report.api import java.io.InputStream +/** + * Image source to provide images on request to the rendering process. + */ interface IImageSource { + /** + * Get an image by its resource name + * + * @param resourceName The resource name of the image + * @return An input stream for the image data to be read from + */ fun get(resourceName: String): InputStream } diff --git a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/IReportRunner.kt b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/IReportRunner.kt index e2bf131..cfaa22b 100644 --- a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/IReportRunner.kt +++ b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/IReportRunner.kt @@ -2,6 +2,15 @@ package org.ephyra.acropolis.report.api import org.ephyra.acropolis.report.api.model.GraphContainer +/** + * Interface for a report runner. This interface provides the entry point into this module. + */ interface IReportRunner { + /** + * Run a report based on the provided model + * + * @param graphContainer The container for the model to build the report from + * @param imageSource Source to allow images to be provided during the rendering process + */ fun run(graphContainer: GraphContainer, imageSource: IImageSource) } diff --git a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/model/Graph.kt b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/model/Graph.kt index fc0e096..222dad1 100644 --- a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/model/Graph.kt +++ b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/api/model/Graph.kt @@ -1,37 +1,78 @@ package org.ephyra.acropolis.report.api.model +/** + * Container for a graph model. + * Allows metadata to be provided with the model. + */ class GraphContainer( val graph: Graph ) { private val subGraphs: MutableList = ArrayList() + /** + * Define a named sub-graph of the graph held by this container. + * The provided list of nodes can later be used to extract a sub-graph. + * + * @param name The name of the sub-graph + * @param includeNodes The nodes to include in the sub-graph + */ fun defineSubgraph(name: String, includeNodes: List) { subGraphs.add(SubGraphSelector(name, includeNodes)) } } +/** + * Model to represent a graph, in the mathematical sense. + */ class Graph { private val nodes: MutableSet = HashSet() private val edges: MutableList = ArrayList() + /** + * Add a node to the graph + * + * @param node The node to add + */ fun addNode(node: Node) { nodes.add(node) } + /** + * Add an edge between two nodes + * + * @param n1 Node to connect + * @param n2 Node to connect + */ fun addEdge(n1: Node, n2: Node) { edges.add(Edge(n1, n2, false)) } + /** + * Add a directed edge between two nodes + * + * @param from The source node for the edge + * @param to The sink node for the edge + */ fun addDirectedEdge(from: Node, to: Node) { edges.add(Edge(from, to, true)) } + /** + * Find a node by its label + * + * @param label The label to search for + * @return The node, if found + */ fun findNode(label: String): Node? { return nodes.find { node -> node.label == label } } - // Only valid for di-graphs, otherwise much allow edges if incoming edges have a corresponding outgoing edge. + /** + * Find nodes, N, such that all edges which connect N to the graph, N is the source. + * + * Only valid for di-graphs, otherwise must allow edges if incoming edges have a corresponding outgoing edge. + */ fun findSourceNodes(): HashSet { val tempNodes = HashSet() tempNodes.addAll(nodes) @@ -44,10 +85,19 @@ class Graph { return tempNodes } + /** + * Return which ever node happens to be first. + * + * @return The first node in the graph, as it is stored + */ fun firstNode(): Node { return nodes.first() } + /** + * Finds edges such that the specified node is the source, and collects the set of sink nodes + * from these edges. + */ fun findNodesConnectedFrom(node: Node): HashSet { val tempNodes = HashSet() edges.forEach { edge -> @@ -60,10 +110,16 @@ class Graph { } } +/** + * Model to represent a node in a graph + */ class Node( val label: String ) +/** + * Model to represent an edge in a graph + */ class Edge( val source: Node, @@ -72,6 +128,9 @@ class Edge( val directed: Boolean = false ) +/** + * Selector for building a sub-graph from a subset of a graph's nodes. + */ class SubGraphSelector ( val name: String, diff --git a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/config/ReportConfiguration.kt b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/config/ReportConfiguration.kt index 1f3c6e8..a434dda 100644 --- a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/config/ReportConfiguration.kt +++ b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/config/ReportConfiguration.kt @@ -3,6 +3,10 @@ package org.ephyra.acropolis.report.config import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration +/** + * Configuration root for the module. By directing the Spring framework to load this class as configuration + * the entire module will be configured for use. + */ @Configuration @ComponentScan(basePackages = ["org.ephyra.acropolis.report.config", "org.ephyra.acropolis.report.impl"]) open class ReportConfiguration diff --git a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/impl/ReportRunner.kt b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/impl/ReportRunner.kt index 3c5b194..bc492ef 100644 --- a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/impl/ReportRunner.kt +++ b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/impl/ReportRunner.kt @@ -9,8 +9,12 @@ import org.ephyra.acropolis.report.impl.render.DiagramRenderer import org.springframework.stereotype.Component import java.lang.IllegalStateException +/** + * Implementation of the report runner interface. + */ +@Suppress("MagicNumber") @Component -private class ReportRunner : IReportRunner { +internal class ReportRunner : IReportRunner { override fun run(graphContainer: GraphContainer, imageSource: IImageSource) { println("Running report") val depthMap = buildNodeDepth(graphContainer.graph) @@ -28,7 +32,8 @@ private class ReportRunner : IReportRunner { val diagramPadding = 30 val diagramWidth = 2 * diagramPadding + (maxDepth + 1) * tileWidth + maxDepth * cardSeparationHorizontal - val diagramHeight = 2 * diagramPadding + (maxCountAtDepth + 1) * tileHeight + maxCountAtDepth * cardSeparationVertical + val diagramHeight = 2 * diagramPadding + (maxCountAtDepth + 1) * tileHeight + + maxCountAtDepth * cardSeparationVertical val tempDepthCounts = HashMap() depthCounts.forEach { depth, count -> @@ -41,7 +46,8 @@ private class ReportRunner : IReportRunner { val depthCount = depthCounts[depth] ?: throw IllegalStateException("missing depth count") - val y = ((diagramHeight - 2 * diagramPadding) / depthCount) * currentDepthCount + diagramPadding - 0.5 * diagramHeight + val y = ((diagramHeight - 2 * diagramPadding) / depthCount) * currentDepthCount + + diagramPadding - 0.5 * diagramHeight val position = Position( (diagramPadding + depth * cardSeparationHorizontal + depth * tileWidth).toFloat(), @@ -110,6 +116,9 @@ private class ReportRunner : IReportRunner { } } +/** + * Represents a position on a 2D plane + */ class Position( val x: Float, val y: Float diff --git a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/impl/render/DiagramRenderer.kt b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/impl/render/DiagramRenderer.kt index efd73a8..2f1b2d5 100644 --- a/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/impl/render/DiagramRenderer.kt +++ b/acropolis-report/src/main/kotlin/org/ephyra/acropolis/report/impl/render/DiagramRenderer.kt @@ -11,6 +11,11 @@ import java.io.File import java.lang.IllegalStateException import javax.imageio.ImageIO +/** + * Wrapper around Java2D API operations to provide a specific set of operations for use + * in diagram rendering. + */ +@Suppress("MagicNumber") class DiagramRenderer( private val width: Int, @@ -26,15 +31,30 @@ class DiagramRenderer( target.fillRect(0, 0, width, height) } + /** + * Add an image to the diagram at the specified coordinates + * + * @param positionX The offset of the left side of the image from the left side of the diagram + * @param positionY The offset of the top side of the image from the top side of the diagram + * @param source The source to fetch the image from + */ fun addImage(positionX: Int, positionY: Int, source: File) { val img = ImageIO.read(source) target.drawImage(img, positionX, positionY, img.width, img.height, null) } + /** + * Export the diagram to file + * + * @param outFile The file to write to + */ fun export(outFile: File) { ImageIO.write(targetImg, outFile.extension, outFile) } + /** + * Draw a connection between two tiles. + */ fun drawConnection() { target.stroke = BasicStroke(4f) target.color = Color.BLUE @@ -50,6 +70,12 @@ class DiagramRenderer( target.drawPolygon(polygon) } + /** + * Draw a string of text onto the diagram using the given font. + * + * @param str The text to draw + * @param fontFile The file from which to load a font for use in the rendering + */ fun drawString(str: String, fontFile: File) { if (fontFile.extension != "ttf") { throw IllegalStateException("Cannot use a font which is not TTF") diff --git a/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/api/IGraphicalAssetService.kt b/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/api/IGraphicalAssetService.kt index 7315560..d97577f 100644 --- a/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/api/IGraphicalAssetService.kt +++ b/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/api/IGraphicalAssetService.kt @@ -24,5 +24,11 @@ interface IGraphicalAssetService { */ fun findAll(): List + /** + * Find a graphical asset by name + * + * @param name The name of the asset to find + * @return The graphical asset, if found + */ fun find(name: String): GraphicalAssetEntity? } diff --git a/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/config/ServiceConfiguration.kt b/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/config/ServiceConfiguration.kt index e582c64..3b56ac4 100644 --- a/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/config/ServiceConfiguration.kt +++ b/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/config/ServiceConfiguration.kt @@ -8,5 +8,9 @@ import org.springframework.context.annotation.Configuration * in order to configure this module correctly. */ @Configuration -@ComponentScan(basePackages = ["org.ephyra.acropolis.persistence.config", "org.ephyra.acropolis.report.config", "org.ephyra.acropolis.service.impl"]) +@ComponentScan(basePackages = [ + "org.ephyra.acropolis.persistence.config", + "org.ephyra.acropolis.report.config", + "org.ephyra.acropolis.service.impl" +]) class ServiceConfiguration diff --git a/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/impl/GraphicalAssetImageSource.kt b/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/impl/GraphicalAssetImageSource.kt index 602f8ba..e3c6034 100644 --- a/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/impl/GraphicalAssetImageSource.kt +++ b/acropolis-service/src/main/kotlin/org/ephyra/acropolis/service/impl/GraphicalAssetImageSource.kt @@ -8,6 +8,10 @@ import java.io.ByteArrayInputStream import java.io.InputStream import java.lang.IllegalStateException +/** + * Implementation of the image source for use with the report module. + * The implementation makes use of the graphical asset service to load requested resources from the database. + */ @Component class GraphicalAssetImageSource : IImageSource { @Autowired