From 988595e38b915164bfa49b578cbf231bdcdc1821 Mon Sep 17 00:00:00 2001 From: Roman Korostinskiy <70313618+c71n93@users.noreply.github.com> Date: Mon, 16 Oct 2023 17:24:10 +0300 Subject: [PATCH 1/3] #34 move Graph and all its logic from ddr --- .../org/objectionary/deog/GraphBuilder.kt | 176 ---------------- .../{steps => graph}/ClosedCycleProcessor.kt | 20 +- .../objectionary/deog/graph/GraphBuilder.kt | 193 ++++++++++++++++++ .../deog/{ => graph}/repr/Attributes.kt | 4 +- .../deog/{ => graph}/repr/DeogGraph.kt | 4 +- .../deog/{ => graph}/repr/Nodes.kt | 6 +- .../org/objectionary/deog/launch/Workflow.kt | 2 +- .../deog/steps/AttributesSetter.kt | 19 +- .../deog/steps/CondAttributesSetter.kt | 53 +++-- .../deog/steps/InnerPropagator.kt | 51 +++-- .../{AttributesUtil.kt => util/XmirUtil.kt} | 106 +++++----- .../objectionary/deog/unit/graph/TestBase.kt | 3 + .../deog/unit/graph/attr/AttrBase.kt | 4 +- .../deog/unit/graph/builder/BuilderBase.kt | 4 +- .../deog/unit/graph/inner/InnerBase.kt | 4 +- 15 files changed, 333 insertions(+), 316 deletions(-) delete mode 100644 src/main/kotlin/org/objectionary/deog/GraphBuilder.kt rename src/main/kotlin/org/objectionary/deog/{steps => graph}/ClosedCycleProcessor.kt (79%) create mode 100644 src/main/kotlin/org/objectionary/deog/graph/GraphBuilder.kt rename src/main/kotlin/org/objectionary/deog/{ => graph}/repr/Attributes.kt (94%) rename src/main/kotlin/org/objectionary/deog/{ => graph}/repr/DeogGraph.kt (94%) rename src/main/kotlin/org/objectionary/deog/{ => graph}/repr/Nodes.kt (93%) rename src/main/kotlin/org/objectionary/deog/{AttributesUtil.kt => util/XmirUtil.kt} (51%) diff --git a/src/main/kotlin/org/objectionary/deog/GraphBuilder.kt b/src/main/kotlin/org/objectionary/deog/GraphBuilder.kt deleted file mode 100644 index 224b677..0000000 --- a/src/main/kotlin/org/objectionary/deog/GraphBuilder.kt +++ /dev/null @@ -1,176 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2022 Olesia Subbotina - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.objectionary.deog - -import org.objectionary.deog.repr.DGraphNode -import org.objectionary.deog.repr.DeogGraph -import org.objectionary.deog.steps.processClosedCycles -import org.slf4j.LoggerFactory -import org.w3c.dom.Document -import org.w3c.dom.Node -import java.nio.file.Path - -typealias GraphAbstracts = MutableMap> - -/** - * Builds decoration hierarchy graph - * - * @todo #29:30m/DEV Refactor this class. Current version of GraphBuilder from ddr repository looks nicer. - * It need to be simple copy-pasted with some fixes. - */ -class GraphBuilder(private val documents: MutableMap) { - private val logger = LoggerFactory.getLogger(this.javaClass.name) - private val abstracts: GraphAbstracts = mutableMapOf() - - /** - * Graph of the program to be analysed - */ - val deogGraph = DeogGraph() - - /** - * Aggregates the process of graph creation: - * Constructs inheritance graph, sets heads and leaves and processes cycles - * - * @return created graph - */ - fun createGraph(): DeogGraph { - try { - constructInheritance() - setLeaves() - deogGraph.leaves.forEach { setHeads(it, mutableMapOf()) } - val thinnedOutHeads: MutableSet = mutableSetOf() - deogGraph.heads.forEach { - val found = mutableListOf(false) - thinOutHeads(it, thinnedOutHeads, mutableSetOf(), found) - if (!found[0]) { - thinnedOutHeads.add(it) - } - } - deogGraph.heads.clear() - thinnedOutHeads.forEach { deogGraph.heads.add(it) } - processClosedCycles(deogGraph) - } catch (e: Exception) { - logger.error(e.printStackTrace().toString()) - } - return deogGraph - } - @Suppress("PARAMETER_NAME_IN_OUTER_LAMBDA") - private fun abstracts(objects: MutableList, packageName: String) = - objects.forEach { - val name = name(it) - if (abstract(it) != null && name != null) { - abstracts.getOrPut(name) { mutableSetOf() }.add(it) - deogGraph.dgNodes.add(DGraphNode(it, packageName)) - } - } - - private fun getAbstractViaRef( - baseName: String?, - baseRef: String? - ): Node? = - if (baseName != null && abstracts.contains(baseName)) { - abstracts[baseName]!!.find { - line(it) == baseRef - } - } else { - null - } - - private fun getAbstractViaPackage(baseNodeName: String?): DGraphNode? { - val packageName = baseNodeName?.substringBeforeLast('.') - val nodeName = baseNodeName?.substringAfterLast('.') - return deogGraph.dgNodes.find { it.name.equals(nodeName) && it.packageName == packageName } - } - - private fun constructInheritance() { - documents.forEach { - val objects: MutableList = mutableListOf() - val docObjects = it.key.getElementsByTagName("o") - val packageName = packageName(docObjects.item(0)) - for (i in 0 until docObjects.length) { - objects.add(docObjects.item(i)) - } - abstracts(objects, packageName) - deogGraph.initialObjects.addAll(objects) - } - for (node in deogGraph.initialObjects) { - val name = name(node) ?: continue - if (name == "@") { - // check that @ attribute's base has an abstract object in this program - val baseNodeName = base(node) - val baseNodeRef = ref(node) - val abstractBaseNode = - getAbstractViaRef(baseNodeName, baseNodeRef) ?: getAbstractViaPackage(baseNodeName)?.body - abstractBaseNode?.let { - val parentNode = node.parentNode ?: return - deogGraph.dgNodes.find { it.body.attributes == parentNode.attributes } - ?: run { deogGraph.dgNodes.add(DGraphNode(parentNode, packageName(parentNode))) } - val igChild = deogGraph.dgNodes.find { it.body.attributes == parentNode.attributes }!! - deogGraph.dgNodes.find { it.body.attributes == abstractBaseNode.attributes } - ?: run { deogGraph.dgNodes.add(DGraphNode(abstractBaseNode, packageName(abstractBaseNode))) } - val dgParent = deogGraph.dgNodes.find { it.body.attributes == abstractBaseNode.attributes }!! - deogGraph.connect(igChild, dgParent) - } - } - } - } - - private fun checkNodes(node: DGraphNode): DGraphNode? = - deogGraph.dgNodes.find { it.body.attributes == node.body.attributes } - - private fun setLeaves() = - deogGraph.dgNodes.filter { it.children.isEmpty() }.forEach { deogGraph.leaves.add(it) } - - private fun setHeads( - node: DGraphNode, - visited: MutableMap - ) { - if (visited.containsKey(node) || node.parents.isEmpty()) { - deogGraph.heads.add(node) - } else { - visited[node] = true - node.parents.forEach { setHeads(it, visited) } - } - } - - private fun thinOutHeads( - node: DGraphNode, - toBeRemoved: MutableSet, - visited: MutableSet, - found: MutableList - ) { - if (found[0] || toBeRemoved.contains(node)) { - found[0] = true - return - } - if (visited.contains(node)) { - toBeRemoved.add(node) - found[0] = true - return - } - visited.add(node) - node.children.forEach { thinOutHeads(it, toBeRemoved, visited, found) } - } -} diff --git a/src/main/kotlin/org/objectionary/deog/steps/ClosedCycleProcessor.kt b/src/main/kotlin/org/objectionary/deog/graph/ClosedCycleProcessor.kt similarity index 79% rename from src/main/kotlin/org/objectionary/deog/steps/ClosedCycleProcessor.kt rename to src/main/kotlin/org/objectionary/deog/graph/ClosedCycleProcessor.kt index a00c448..dd512d6 100644 --- a/src/main/kotlin/org/objectionary/deog/steps/ClosedCycleProcessor.kt +++ b/src/main/kotlin/org/objectionary/deog/graph/ClosedCycleProcessor.kt @@ -22,32 +22,32 @@ * SOFTWARE. */ -package org.objectionary.deog.steps +package org.objectionary.deog.graph -import org.objectionary.deog.repr.DGraphNode -import org.objectionary.deog.repr.DeogGraph +import org.objectionary.deog.graph.repr.DGraphNode +import org.objectionary.deog.graph.repr.DeogGraph /** * Processes decoration cycles * - * @param deogGraph graph to be analysed + * @param graph graph to be analysed * @throws IllegalStateException if after the analysis it turns out that not all nodes of the graph were traversed */ @Throws(IllegalStateException::class) -internal fun processClosedCycles(deogGraph: DeogGraph) { +fun processClosedCycles(graph: DeogGraph) { val reached: MutableMap = mutableMapOf() - deogGraph.dgNodes.forEach { reached[it] = false } - deogGraph.heads.forEach { dfsReachable(it, reached) } + graph.dgNodes.forEach { reached[it] = false } + graph.heads.forEach { dfsReachable(it, reached) } for (entry in reached) { if (!entry.value) { - deogGraph.heads.add(entry.key) + graph.heads.add(entry.key) traverseCycles(entry.key, reached) } } - if (deogGraph.dgNodes.size != reached.filter { it.value }.size) { + if (graph.dgNodes.size != reached.filter { it.value }.size) { throw IllegalStateException( "Not all nodes of inheritance graph were reached.\n " + - "Graph size: ${deogGraph.dgNodes.size}, reached nodes: ${reached.filter { it.value }.size}" + "Graph size: ${graph.dgNodes.size}, reached nodes: ${reached.filter { it.value }.size}" ) } } diff --git a/src/main/kotlin/org/objectionary/deog/graph/GraphBuilder.kt b/src/main/kotlin/org/objectionary/deog/graph/GraphBuilder.kt new file mode 100644 index 0000000..c6b73e9 --- /dev/null +++ b/src/main/kotlin/org/objectionary/deog/graph/GraphBuilder.kt @@ -0,0 +1,193 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Olesia Subbotina + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.objectionary.deog.graph + +import org.objectionary.deog.graph.repr.DGraphNode +import org.objectionary.deog.graph.repr.DeogGraph +import org.objectionary.deog.util.containsAttr +import org.objectionary.deog.util.getAttrContent +import org.objectionary.deog.util.packageName +import org.objectionary.deog.util.toMutableList +import org.apache.commons.lang3.mutable.MutableBoolean +import org.w3c.dom.Document +import org.w3c.dom.Node +import java.nio.file.Path + +/** + * Builds decoration hierarchy graph + * + * @property documents all XMIR documents + */ +class GraphBuilder(private val documents: MutableMap) { + /** + * Graph of the program to be analysed + */ + val graph = DeogGraph() + + /** + * Aggregates the process of graph creation: + * Constructs inheritance graph, sets heads and leaves and processes cycles + * + * @return built [graph] + */ + fun createGraph(): DeogGraph { + initializeGraph() + constructInheritance() + setLeaves() + setHeads() + return graph + } + + /** + * Initializes [graph] by adding all initial and abstract objects from the [documents] + */ + fun initializeGraph() { + documents.forEach { + val objects = it.key.getElementsByTagName("o").toMutableList() + val packageName = it.key.packageName() + graph.dgNodes.addAll(collectAbstracts(objects, packageName)) + graph.initialObjects.addAll(objects) + } + } + + /** + * Puts all nodes representing abstract objects in a set of nodes [graph] + */ + private fun constructInheritance() { + graph.initialObjects.filter { isDecoration(it) }.forEach { decor -> + val abstractBaseNode = getAbstractBaseObject(decor) + abstractBaseNode?.let { + val igChild = graph.dgNodes.find { it.body == decor.parentNode }!! + graph.connect(igChild, abstractBaseNode) + } + } + } + + /** + * Puts all nodes representing abstract objects in a set of nodes [graph] + * + * @param objects list of objects + * @param packageName name of the package in which the described object is located + */ + private fun collectAbstracts(objects: MutableList, packageName: String): MutableSet { + val abstracts: MutableSet = mutableSetOf() + objects.filter { + it.containsAttr("abstract") && it.containsAttr("name") + }.forEach { obj -> + abstracts.add(DGraphNode(obj, packageName)) + } + return abstracts + } + + /** + * Finds the definition of an abstract object, which is the object from "base" attribute of given decoration node + * + * @param node decoration node + * @return found abstract base object of given decoration node or `null` + */ + private fun getAbstractBaseObject(node: Node): DGraphNode? { + val baseObjName = node.getAttrContent("base") + val baseObjRef = node.getAttrContent("ref") + return getAbstractViaRef(baseObjName, baseObjRef) ?: getAbstractViaPackage(baseObjName) + } + + private fun getAbstractViaRef( + baseName: String?, + baseRef: String? + ): DGraphNode? = graph.dgNodes.find { + it.body.getAttrContent("line") == baseRef && it.body.getAttrContent("name") == baseName + } + + private fun getAbstractViaPackage(baseNodeName: String?): DGraphNode? { + val packageName = baseNodeName?.substringBeforeLast('.') + val nodeName = baseNodeName?.substringAfterLast('.') + return graph.dgNodes.find { it.name == nodeName && it.packageName == packageName } + } + + /** + * Check if given [node] is decoration statement + * + * @param node node to be inspected + * @return `true` if [node] is decoration and `false` otherwise + */ + private fun isDecoration(node: Node): Boolean { + val name = node.getAttrContent("name") ?: return false + return name == "@" + } + + private fun setLeaves() = + graph.dgNodes.filter { it.children.isEmpty() }.forEach { graph.leaves.add(it) } + + /** + * @todo #34:90m/DEV Current algorithm of finding graph heads is very inefficient and requires refactoring. + * To reimplement `setHeads()`, `findHeadsExcessively()` and `thinOutHeads()` functions. + * To reimplement all functions from `ClosedCycleProcessor.kt`. + */ + private fun setHeads() { + graph.leaves.forEach { findHeadsExcessively(it, mutableMapOf()) } + val thinnedOutHeads: MutableSet = mutableSetOf() + graph.heads.forEach { + val found = MutableBoolean(false) + thinOutHeads(it, thinnedOutHeads, mutableSetOf(), found) + if (found.isFalse) { + thinnedOutHeads.add(it) + } + } + graph.heads.clear() + thinnedOutHeads.forEach { graph.heads.add(it) } + processClosedCycles(graph) + } + + private fun findHeadsExcessively( + node: DGraphNode, + visited: MutableMap + ) { + if (visited.containsKey(node) || node.parents.isEmpty()) { + graph.heads.add(node) + } else { + visited[node] = true + node.parents.forEach { findHeadsExcessively(it, visited) } + } + } + + private fun thinOutHeads( + node: DGraphNode, + toBeRemoved: MutableSet, + visited: MutableSet, + found: MutableBoolean + ) { + if (found.isTrue || toBeRemoved.contains(node)) { + found.setTrue() + return + } + if (visited.contains(node)) { + toBeRemoved.add(node) + found.setTrue() + return + } + visited.add(node) + node.children.forEach { thinOutHeads(it, toBeRemoved, visited, found) } + } +} diff --git a/src/main/kotlin/org/objectionary/deog/repr/Attributes.kt b/src/main/kotlin/org/objectionary/deog/graph/repr/Attributes.kt similarity index 94% rename from src/main/kotlin/org/objectionary/deog/repr/Attributes.kt rename to src/main/kotlin/org/objectionary/deog/graph/repr/Attributes.kt index ac384df..d6cd633 100644 --- a/src/main/kotlin/org/objectionary/deog/repr/Attributes.kt +++ b/src/main/kotlin/org/objectionary/deog/graph/repr/Attributes.kt @@ -1,4 +1,4 @@ -package org.objectionary.deog.repr +package org.objectionary.deog.graph.repr import org.w3c.dom.Node @@ -16,7 +16,7 @@ open class DGraphAttr( open val body: Node ) { /** - * Todo + * @property freeVars */ val freeVars: MutableSet = mutableSetOf() } diff --git a/src/main/kotlin/org/objectionary/deog/repr/DeogGraph.kt b/src/main/kotlin/org/objectionary/deog/graph/repr/DeogGraph.kt similarity index 94% rename from src/main/kotlin/org/objectionary/deog/repr/DeogGraph.kt rename to src/main/kotlin/org/objectionary/deog/graph/repr/DeogGraph.kt index dbfe9e1..9c3e8dd 100644 --- a/src/main/kotlin/org/objectionary/deog/repr/DeogGraph.kt +++ b/src/main/kotlin/org/objectionary/deog/graph/repr/DeogGraph.kt @@ -22,7 +22,7 @@ * SOFTWARE. */ -package org.objectionary.deog.repr +package org.objectionary.deog.graph.repr import org.w3c.dom.Node @@ -31,7 +31,7 @@ import org.w3c.dom.Node */ class DeogGraph { /** - * Collection of all graph nodes + * Collection of all graph nodes (it can be only abstract objects) */ val dgNodes: MutableSet = mutableSetOf() diff --git a/src/main/kotlin/org/objectionary/deog/repr/Nodes.kt b/src/main/kotlin/org/objectionary/deog/graph/repr/Nodes.kt similarity index 93% rename from src/main/kotlin/org/objectionary/deog/repr/Nodes.kt rename to src/main/kotlin/org/objectionary/deog/graph/repr/Nodes.kt index cda3fe8..b7b40df 100644 --- a/src/main/kotlin/org/objectionary/deog/repr/Nodes.kt +++ b/src/main/kotlin/org/objectionary/deog/graph/repr/Nodes.kt @@ -1,6 +1,6 @@ -package org.objectionary.deog.repr +package org.objectionary.deog.graph.repr -import org.objectionary.deog.name +import org.objectionary.deog.util.getAttrContent import org.w3c.dom.Node /** @@ -17,7 +17,7 @@ open class DGraphNode( /** * Name of the node */ - val name: String? by lazy { name(body) } + val name: String? by lazy { body.getAttrContent("name") } /** * Children of this node diff --git a/src/main/kotlin/org/objectionary/deog/launch/Workflow.kt b/src/main/kotlin/org/objectionary/deog/launch/Workflow.kt index bd57a4d..518e80a 100644 --- a/src/main/kotlin/org/objectionary/deog/launch/Workflow.kt +++ b/src/main/kotlin/org/objectionary/deog/launch/Workflow.kt @@ -1,6 +1,6 @@ package org.objectionary.deog.launch -import org.objectionary.deog.GraphBuilder +import org.objectionary.deog.graph.GraphBuilder import org.objectionary.deog.sources.SrsTransformed import org.objectionary.deog.sources.XslTransformer import org.objectionary.deog.steps.AttributesSetter diff --git a/src/main/kotlin/org/objectionary/deog/steps/AttributesSetter.kt b/src/main/kotlin/org/objectionary/deog/steps/AttributesSetter.kt index f7007e2..a84c216 100644 --- a/src/main/kotlin/org/objectionary/deog/steps/AttributesSetter.kt +++ b/src/main/kotlin/org/objectionary/deog/steps/AttributesSetter.kt @@ -24,23 +24,24 @@ package org.objectionary.deog.steps -import org.objectionary.deog.abstract -import org.objectionary.deog.name -import org.objectionary.deog.repr.DGraphAttr -import org.objectionary.deog.repr.DGraphNode -import org.objectionary.deog.repr.DeogGraph +import org.objectionary.deog.graph.repr.DGraphAttr +import org.objectionary.deog.graph.repr.DGraphNode +import org.objectionary.deog.graph.repr.DeogGraph +import org.objectionary.deog.util.getAttr +import org.objectionary.deog.util.getAttrContent import org.w3c.dom.Node /** * Sets all default attributes of nodes and propagates attributes through the [deogGraph] */ -internal class AttributesSetter(private val deogGraph: DeogGraph) { +class AttributesSetter(private val deogGraph: DeogGraph) { /** * Aggregate the process of attributes pushing */ fun setAttributes() { setDefaultAttributes() pushAttributes() + // processFreeVars() } /** @@ -51,9 +52,9 @@ internal class AttributesSetter(private val deogGraph: DeogGraph) { val attributes = node.body.childNodes for (j in 0 until attributes.length) { val attr: Node = attributes.item(j) - abstract(attr)?.let { - name(attr)?.let { - node.attributes.add(DGraphAttr(name(attr)!!, 0, attr)) + attr.getAttr("abstract")?.let { + attr.getAttrContent("name")?.let { + node.attributes.add(DGraphAttr(attr.getAttrContent("name")!!, 0, attr)) } } } diff --git a/src/main/kotlin/org/objectionary/deog/steps/CondAttributesSetter.kt b/src/main/kotlin/org/objectionary/deog/steps/CondAttributesSetter.kt index 13dc8bc..809ec7f 100644 --- a/src/main/kotlin/org/objectionary/deog/steps/CondAttributesSetter.kt +++ b/src/main/kotlin/org/objectionary/deog/steps/CondAttributesSetter.kt @@ -1,14 +1,13 @@ package org.objectionary.deog.steps -import org.objectionary.deog.abstract -import org.objectionary.deog.base -import org.objectionary.deog.line -import org.objectionary.deog.name -import org.objectionary.deog.packageName -import org.objectionary.deog.repr.DGraphCondAttr -import org.objectionary.deog.repr.DGraphCondNode -import org.objectionary.deog.repr.DeogGraph -import org.objectionary.deog.repr.DgNodeCondition +import org.objectionary.deog.graph.repr.DGraphCondAttr +import org.objectionary.deog.graph.repr.DGraphCondNode +import org.objectionary.deog.graph.repr.DeogGraph +import org.objectionary.deog.graph.repr.DgNodeCondition +import org.objectionary.deog.util.containsAttr +import org.objectionary.deog.util.getAttr +import org.objectionary.deog.util.getAttrContent +import org.objectionary.deog.util.packageName import org.w3c.dom.Node /** @@ -16,7 +15,7 @@ import org.w3c.dom.Node * * @property deogGraph graph of the program */ -internal class CondAttributesSetter( +class CondAttributesSetter( private val deogGraph: DeogGraph ) { private val conditions: MutableSet = mutableSetOf() @@ -32,7 +31,7 @@ internal class CondAttributesSetter( private fun collectConditions() { val objects = deogGraph.initialObjects for (node in objects) { - val base = base(node) ?: continue + val base = node.getAttrContent("base") ?: continue if (base == ".if") { conditions.add(node) } @@ -42,47 +41,47 @@ internal class CondAttributesSetter( private fun processApplications() { conditions.forEach { node -> var tmpNode = node.firstChild.nextSibling - var line = line(tmpNode) + var line = tmpNode.getAttrContent("line") val cond: MutableList = mutableListOf(tmpNode) - while (line(tmpNode.nextSibling.nextSibling) == line) { + while (tmpNode.nextSibling.nextSibling.getAttrContent("line") == line) { cond.add(tmpNode.nextSibling.nextSibling) tmpNode = tmpNode.nextSibling.nextSibling - line = line(tmpNode) + line = tmpNode.getAttrContent("line") } tmpNode = tmpNode.nextSibling.nextSibling val fstOption: MutableList = mutableListOf(tmpNode) - while (line(tmpNode.nextSibling.nextSibling) == line) { + while (tmpNode.nextSibling.nextSibling.getAttrContent("line") == line) { fstOption.add(tmpNode.nextSibling.nextSibling) tmpNode = tmpNode.nextSibling.nextSibling - line = line(tmpNode) + line = tmpNode.getAttrContent("line") } tmpNode = tmpNode.nextSibling.nextSibling val sndOption: MutableList = mutableListOf(tmpNode) - while (line(tmpNode.nextSibling.nextSibling) == line) { + while (tmpNode.nextSibling.nextSibling.getAttrContent("line") == line) { sndOption.add(tmpNode.nextSibling.nextSibling) tmpNode = tmpNode.nextSibling.nextSibling - line = line(tmpNode) + line = tmpNode.getAttrContent("line") } - val dgCond = DgNodeCondition(cond) - traverseParents(node.parentNode, dgCond.freeVars) - name(node)?.let { name -> + val igCond = DgNodeCondition(cond) + traverseParents(node.parentNode, igCond.freeVars) + node.getAttrContent("name")?.let { name -> if (name != "@") { - deogGraph.dgNodes.add(DGraphCondNode(node, packageName(node), dgCond, fstOption, sndOption)) + deogGraph.dgNodes.add(DGraphCondNode(node, node.packageName(), igCond, fstOption, sndOption)) val parent = deogGraph.dgNodes.find { it.body == node.parentNode } - parent?.attributes?.add(DGraphCondAttr(name, 0, node, dgCond, fstOption, sndOption)) + parent?.attributes?.add(DGraphCondAttr(name, 0, node, igCond, fstOption, sndOption)) } else { val parent = deogGraph.dgNodes.find { it.body == node.parentNode } - parent?.attributes?.add(DGraphCondAttr(name, 0, node, dgCond, fstOption, sndOption)) + parent?.attributes?.add(DGraphCondAttr(name, 0, node, igCond, fstOption, sndOption)) } } } } private fun traverseParents(node: Node, freeVars: MutableSet) { - abstract(node) ?: return + node.getAttr("abstract") ?: return var sibling = node.firstChild?.nextSibling - while (base(sibling) == null && abstract(sibling) == null && sibling != null) { - name(sibling)?.let { freeVars.add(it) } + while (!sibling.containsAttr("base") && !sibling.containsAttr("abstract") && sibling != null) { + sibling.getAttrContent("name")?.let { freeVars.add(it) } sibling = sibling?.nextSibling } traverseParents(node.parentNode, freeVars) diff --git a/src/main/kotlin/org/objectionary/deog/steps/InnerPropagator.kt b/src/main/kotlin/org/objectionary/deog/steps/InnerPropagator.kt index 6b7d600..947b7bc 100644 --- a/src/main/kotlin/org/objectionary/deog/steps/InnerPropagator.kt +++ b/src/main/kotlin/org/objectionary/deog/steps/InnerPropagator.kt @@ -1,13 +1,13 @@ package org.objectionary.deog.steps -import org.objectionary.deog.abstract -import org.objectionary.deog.base -import org.objectionary.deog.findRef -import org.objectionary.deog.name -import org.objectionary.deog.packageName -import org.objectionary.deog.repr.DGraphAttr -import org.objectionary.deog.repr.DGraphNode -import org.objectionary.deog.repr.DeogGraph +import org.objectionary.deog.graph.repr.DGraphAttr +import org.objectionary.deog.graph.repr.DGraphNode +import org.objectionary.deog.graph.repr.DeogGraph +import org.objectionary.deog.util.containsAttr +import org.objectionary.deog.util.findRef +import org.objectionary.deog.util.getAttr +import org.objectionary.deog.util.getAttrContent +import org.objectionary.deog.util.packageName import org.w3c.dom.Node typealias Abstracts = MutableMap> @@ -15,7 +15,7 @@ typealias Abstracts = MutableMap> /** * Propagates inner attributes */ -internal class InnerPropagator( +class InnerPropagator( private val deogGraph: DeogGraph ) { private val decorators: MutableMap = mutableMapOf() @@ -32,16 +32,21 @@ internal class InnerPropagator( private fun collectDecorators() { val objects = deogGraph.initialObjects for (node in objects) { - val name = name(node) + val name = node.getAttrContent("name") if (name != null && name == "@") { - decorators[DGraphNode(node, packageName(node))] = false + decorators[DGraphNode(node, node.packageName())] = false } - if (abstract(node) != null && name != null) { - abstracts.getOrPut(name) { mutableSetOf() }.add(DGraphNode(node, packageName(node))) + if (node.containsAttr("abstract") && name != null) { + abstracts.getOrPut(name) { mutableSetOf() }.add(DGraphNode(node, node.packageName())) } } } + /** + * @todo #34:30min this solution is naive, optimize it (see snippet below) + * while (decorators.containsValue(false)) { ... } + * (this todo has moved here from ddr repository) + */ @Suppress("MAGIC_NUMBER") private fun processDecorators() { val repetitions = 5 @@ -58,13 +63,13 @@ internal class InnerPropagator( @Suppress("AVOID_NULL_CHECKS") private fun getBaseAbstract(key: DGraphNode) { var tmpKey = key.body - while (base(tmpKey)?.startsWith('.') == true) { + while (tmpKey.getAttrContent("base")?.startsWith('.') == true) { if (tmpKey.previousSibling.previousSibling == null) { break } tmpKey = tmpKey.previousSibling.previousSibling } - when (base(tmpKey)) { + when (tmpKey.getAttrContent("base")) { "^" -> { val abstract = tmpKey.parentNode.parentNode resolveAttrs(tmpKey, abstract, key) @@ -86,7 +91,7 @@ internal class InnerPropagator( * Finds an actual definition of an object that was copied into given [node] */ private fun resolveRefs(node: Node): Node? { - abstract(node)?.let { return node } + node.getAttr("abstract")?.let { return node } return findRef(node, deogGraph.initialObjects, deogGraph) } @@ -101,10 +106,10 @@ internal class InnerPropagator( ) { var tmpAbstract = deogGraph.dgNodes.find { it.body == abstract } ?: return var tmpNode: Node? = node.nextSibling.nextSibling ?: return - while (name(tmpAbstract.body) != base(key.body)?.substring(1)) { + while (tmpAbstract.body.getAttrContent("name") != key.body.getAttrContent("base")?.substring(1)) { tmpAbstract = deogGraph.dgNodes.find { graphNode -> tmpAbstract.attributes.find { - base(tmpNode)?.substring(1) == name(it.body) + tmpNode.getAttrContent("base")?.substring(1) == it.body.getAttrContent("name") }?.body == graphNode.body } ?: return tmpNode = tmpNode?.nextSibling?.nextSibling @@ -114,20 +119,14 @@ internal class InnerPropagator( deogGraph.dgNodes.add( DGraphNode( parent, - packageName(parent) + parent.packageName() ) ) } val dgParent = deogGraph.dgNodes.find { it.body == parent } ?: return tmpAbstract.attributes.forEach { graphNode -> dgParent.attributes.find { graphNode.body == it.body } - ?: dgParent.attributes.add( - DGraphAttr( - graphNode.name, - graphNode.parentDistance + 1, - graphNode.body - ) - ) + ?: dgParent.attributes.add(DGraphAttr(graphNode.name, graphNode.parentDistance + 1, graphNode.body)) } deogGraph.connect(dgParent, tmpAbstract) return diff --git a/src/main/kotlin/org/objectionary/deog/AttributesUtil.kt b/src/main/kotlin/org/objectionary/deog/util/XmirUtil.kt similarity index 51% rename from src/main/kotlin/org/objectionary/deog/AttributesUtil.kt rename to src/main/kotlin/org/objectionary/deog/util/XmirUtil.kt index 796ec8e..b2d5133 100644 --- a/src/main/kotlin/org/objectionary/deog/AttributesUtil.kt +++ b/src/main/kotlin/org/objectionary/deog/util/XmirUtil.kt @@ -22,68 +22,61 @@ * SOFTWARE. */ -package org.objectionary.deog +package org.objectionary.deog.util -import org.objectionary.deog.repr.DGraphNode -import org.objectionary.deog.repr.DeogGraph +import org.objectionary.deog.graph.repr.DGraphNode +import org.objectionary.deog.graph.repr.DeogGraph +import org.w3c.dom.Document import org.w3c.dom.Node +import org.w3c.dom.NodeList /** - * Finds abstract attribute of the [node] + * Finds attribute of this node by [name] * - * @param node to be analysed - * @return found abstract name + * @param [name] name of attribute to find + * @return found attribute */ -fun abstract(node: Node?) = node?.attributes?.getNamedItem("abstract") +fun Node?.getAttr(name: String) = this?.attributes?.getNamedItem(name)?.textContent /** - * Finds name attribute of the [node] + * Finds attribute of this node by [name] * - * @param node to be analysed - * @return found name + * @param [name] name of attribute to find + * @return content of found attribute */ -fun name(node: Node?) = node?.attributes?.getNamedItem("name")?.textContent +fun Node?.getAttrContent(name: String) = this?.attributes?.getNamedItem(name)?.textContent /** - * Finds base attribute of the [node] + * Finds out whether this node contains named attribute * - * @param node to be analysed - * @return found base + * @param [name] name of attribute to find + * @return `true` if this node contains an attribute named [name] and, `false` if it doesn't */ -fun base(node: Node?) = node?.attributes?.getNamedItem("base")?.textContent +fun Node?.containsAttr(name: String) = this?.getAttr(name) != null /** - * Finds ref attribute of the [node] + * Finds package name of the xmir file that contains this node * - * @param node to be analysed - * @return found ref - */ -fun ref(node: Node?) = node?.attributes?.getNamedItem("ref")?.textContent - -/** - * Finds line attribute of the [node] - * - * @param node to be analysed - * @return found line - */ -fun line(node: Node?) = node?.attributes?.getNamedItem("line")?.textContent - -/** - * Finds pos attribute of the [node] - * - * @param node to be analysed - * @return found pos + * @return found package name */ -fun pos(node: Node?) = node?.attributes?.getNamedItem("pos")?.textContent +fun Node?.packageName(): String { + val heads = this?.ownerDocument?.getElementsByTagName("head") ?: return "" + for (i in 0 until heads.length) { + val head = heads.item(i) + if (head.textContent.equals("package")) { + return head.nextSibling.nextSibling.textContent + } + } + return "" +} /** - * Finds package name of the [node] + * Finds package name of this xmir file * - * @param node to be analysed * @return found package name */ -fun packageName(node: Node?): String { - val heads = node?.ownerDocument?.getElementsByTagName("head") ?: return "" +fun Document?.packageName(): String { + val heads = this?.getElementsByTagName("head") ?: return "" for (i in 0 until heads.length) { val head = heads.item(i) if (head.textContent.equals("package")) { @@ -94,39 +87,44 @@ fun packageName(node: Node?): String { } /** - * Finds atom attribute of the [node] + * Converts `NodeList` to `MutableList` of nodes * - * @param node to be analysed - * @return found atom name + * @return `MutableList` of nodes */ -fun atom(node: Node?) = node?.attributes?.getNamedItem("atom")?.textContent +fun NodeList.toMutableList(): MutableList { + val mutList: MutableList = mutableListOf() + for (i in 0 until this.length) { + mutList.add(this.item(i)!!) + } + return mutList +} /** * Either finds an abstract object that the [node] is referring to or looks for the abstract node in the packages * * @param node node to be handled * @param objects list of xml objects - * @param deogGraph graph + * @param graph graph * @return node found through refs */ @Suppress("AVOID_NULL_CHECKS") fun findRef( node: Node?, objects: MutableSet, - deogGraph: DeogGraph + graph: DeogGraph ): Node? { - val ref = ref(node) ?: return getAbstractViaPackage(base(node), deogGraph)?.body + val ref = node.getAttrContent("ref") ?: return getAbstractViaPackage(node.getAttrContent("base"), graph)?.body objects.forEach { - if (line(it) == ref) { - if (abstract(it) != null && packageName(node) == packageName(it)) { + if (it.getAttrContent("line") == ref) { + if (it.containsAttr("abstract") && node.packageName() == it.packageName()) { return it } - if (abstract(it) == null && packageName(node) == packageName(it)) { + if (!it.containsAttr("abstract") && node.packageName() == it.packageName()) { val traversed = walkDotChain(it) return if (traversed == null) { - findRef(it, objects, deogGraph) + findRef(it, objects, graph) } else { - findRef(traversed, objects, deogGraph) + findRef(traversed, objects, graph) } } } @@ -138,7 +136,7 @@ private fun walkDotChain( node: Node ): Node? { var sibling = node.nextSibling?.nextSibling - while (base(sibling)?.startsWith(".") == true) { + while (sibling.getAttrContent("base")?.startsWith(".") == true) { sibling = sibling?.nextSibling sibling?.attributes ?: run { sibling = sibling?.nextSibling } } @@ -147,8 +145,8 @@ private fun walkDotChain( return sibling } -private fun getAbstractViaPackage(baseNodeName: String?, deogGraph: DeogGraph): DGraphNode? { +private fun getAbstractViaPackage(baseNodeName: String?, graph: DeogGraph): DGraphNode? { val packageName = baseNodeName?.substringBeforeLast('.') val nodeName = baseNodeName?.substringAfterLast('.') - return deogGraph.dgNodes.find { it.name.equals(nodeName) && it.packageName == packageName } + return graph.dgNodes.find { it.name.equals(nodeName) && it.packageName == packageName } } diff --git a/src/test/kotlin/org/objectionary/deog/unit/graph/TestBase.kt b/src/test/kotlin/org/objectionary/deog/unit/graph/TestBase.kt index 734f4e9..b46b54f 100644 --- a/src/test/kotlin/org/objectionary/deog/unit/graph/TestBase.kt +++ b/src/test/kotlin/org/objectionary/deog/unit/graph/TestBase.kt @@ -76,6 +76,9 @@ interface TestBase { /** * @return name of the test being executed + * + * @todo #34:90min/DEV use @ParameterizedTest here instead of looking into stackTrace. Current approach has some + * limitations. For example, if stack depth will increase, this function will output wrong name. */ fun getTestName() = Thread.currentThread().stackTrace[4].methodName .substring(5) diff --git a/src/test/kotlin/org/objectionary/deog/unit/graph/attr/AttrBase.kt b/src/test/kotlin/org/objectionary/deog/unit/graph/attr/AttrBase.kt index c969d32..b9d8ed8 100644 --- a/src/test/kotlin/org/objectionary/deog/unit/graph/attr/AttrBase.kt +++ b/src/test/kotlin/org/objectionary/deog/unit/graph/attr/AttrBase.kt @@ -24,8 +24,8 @@ package org.objectionary.deog.unit.graph.attr -import org.objectionary.deog.GraphBuilder -import org.objectionary.deog.repr.DGraphNode +import org.objectionary.deog.graph.GraphBuilder +import org.objectionary.deog.graph.repr.DGraphNode import org.objectionary.deog.sources.SrsTransformed import org.objectionary.deog.sources.XslTransformer import org.objectionary.deog.steps.AttributesSetter diff --git a/src/test/kotlin/org/objectionary/deog/unit/graph/builder/BuilderBase.kt b/src/test/kotlin/org/objectionary/deog/unit/graph/builder/BuilderBase.kt index 7aade78..c2c9947 100644 --- a/src/test/kotlin/org/objectionary/deog/unit/graph/builder/BuilderBase.kt +++ b/src/test/kotlin/org/objectionary/deog/unit/graph/builder/BuilderBase.kt @@ -24,8 +24,8 @@ package org.objectionary.deog.unit.graph.builder -import org.objectionary.deog.GraphBuilder -import org.objectionary.deog.repr.DGraphNode +import org.objectionary.deog.graph.GraphBuilder +import org.objectionary.deog.graph.repr.DGraphNode import org.objectionary.deog.sources.SrsTransformed import org.objectionary.deog.sources.XslTransformer import org.objectionary.deog.unit.graph.TestBase diff --git a/src/test/kotlin/org/objectionary/deog/unit/graph/inner/InnerBase.kt b/src/test/kotlin/org/objectionary/deog/unit/graph/inner/InnerBase.kt index c0aef3b..bdb9cb2 100644 --- a/src/test/kotlin/org/objectionary/deog/unit/graph/inner/InnerBase.kt +++ b/src/test/kotlin/org/objectionary/deog/unit/graph/inner/InnerBase.kt @@ -24,8 +24,8 @@ package org.objectionary.deog.unit.graph.inner -import org.objectionary.deog.GraphBuilder -import org.objectionary.deog.repr.DGraphNode +import org.objectionary.deog.graph.GraphBuilder +import org.objectionary.deog.graph.repr.DGraphNode import org.objectionary.deog.sources.SrsTransformed import org.objectionary.deog.sources.XslTransformer import org.objectionary.deog.steps.AttributesSetter From 55d0650e8e7a534caab1bd08e0b014160c0f668e Mon Sep 17 00:00:00 2001 From: Roman Korostinskiy <70313618+c71n93@users.noreply.github.com> Date: Mon, 16 Oct 2023 17:30:17 +0300 Subject: [PATCH 2/3] #34 add required dependency --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index bd30526..be61d72 100644 --- a/pom.xml +++ b/pom.xml @@ -288,6 +288,11 @@ SOFTWARE. annotations 23.0.0 + + org.apache.commons + commons-lang3 + 3.13.0 + org.slf4j slf4j-api From 46c3534e288fcb263b1165938e8859f229823b75 Mon Sep 17 00:00:00 2001 From: Roman Korostinskiy <70313618+c71n93@users.noreply.github.com> Date: Tue, 17 Oct 2023 12:29:15 +0300 Subject: [PATCH 3/3] #34 fix variables naming --- .../kotlin/org/objectionary/deog/graph/GraphBuilder.kt | 4 ++-- .../objectionary/deog/steps/CondAttributesSetter.kt | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/objectionary/deog/graph/GraphBuilder.kt b/src/main/kotlin/org/objectionary/deog/graph/GraphBuilder.kt index c6b73e9..c3f927f 100644 --- a/src/main/kotlin/org/objectionary/deog/graph/GraphBuilder.kt +++ b/src/main/kotlin/org/objectionary/deog/graph/GraphBuilder.kt @@ -79,8 +79,8 @@ class GraphBuilder(private val documents: MutableMap) { graph.initialObjects.filter { isDecoration(it) }.forEach { decor -> val abstractBaseNode = getAbstractBaseObject(decor) abstractBaseNode?.let { - val igChild = graph.dgNodes.find { it.body == decor.parentNode }!! - graph.connect(igChild, abstractBaseNode) + val dgChild = graph.dgNodes.find { it.body == decor.parentNode }!! + graph.connect(dgChild, abstractBaseNode) } } } diff --git a/src/main/kotlin/org/objectionary/deog/steps/CondAttributesSetter.kt b/src/main/kotlin/org/objectionary/deog/steps/CondAttributesSetter.kt index 809ec7f..21b4119 100644 --- a/src/main/kotlin/org/objectionary/deog/steps/CondAttributesSetter.kt +++ b/src/main/kotlin/org/objectionary/deog/steps/CondAttributesSetter.kt @@ -62,16 +62,16 @@ class CondAttributesSetter( tmpNode = tmpNode.nextSibling.nextSibling line = tmpNode.getAttrContent("line") } - val igCond = DgNodeCondition(cond) - traverseParents(node.parentNode, igCond.freeVars) + val condNode = DgNodeCondition(cond) + traverseParents(node.parentNode, condNode.freeVars) node.getAttrContent("name")?.let { name -> if (name != "@") { - deogGraph.dgNodes.add(DGraphCondNode(node, node.packageName(), igCond, fstOption, sndOption)) + deogGraph.dgNodes.add(DGraphCondNode(node, node.packageName(), condNode, fstOption, sndOption)) val parent = deogGraph.dgNodes.find { it.body == node.parentNode } - parent?.attributes?.add(DGraphCondAttr(name, 0, node, igCond, fstOption, sndOption)) + parent?.attributes?.add(DGraphCondAttr(name, 0, node, condNode, fstOption, sndOption)) } else { val parent = deogGraph.dgNodes.find { it.body == node.parentNode } - parent?.attributes?.add(DGraphCondAttr(name, 0, node, igCond, fstOption, sndOption)) + parent?.attributes?.add(DGraphCondAttr(name, 0, node, condNode, fstOption, sndOption)) } } }