diff --git a/src/main/java/org/eolang/jeo/improvement/ImprovementDistilledObjects.java b/src/main/java/org/eolang/jeo/improvement/ImprovementDistilledObjects.java index ca964406a..701015345 100644 --- a/src/main/java/org/eolang/jeo/improvement/ImprovementDistilledObjects.java +++ b/src/main/java/org/eolang/jeo/improvement/ImprovementDistilledObjects.java @@ -128,10 +128,6 @@ private static Representation replaceConstructors( * @param clazz Class where to replace. * @param target What should be replaced. * @param replacement Replacement. - * @todo #161:90min Refactor replace method. - * Right now it's a big method with a lot of repetition and high complexity. - * Moreover, some constants are hardcoded and it's not good. - * We need to refactor it into a set of smaller methods and remove all linter warnings. * @checkstyle ModifiedControlVariableCheck (200 lines) */ private static void replace( @@ -178,17 +174,19 @@ private static void replace( /** * Replace arguments. * @param clazz Class where to replace. + * @todo #157:90min Handle arguments correctly during inlining optimization. + * Right now we just replace all arguments with the new class name. + * It's not correct, because we need to handle arguments correctly. */ private static void replaceArguments(final XmlClass clazz) { - for (final XmlMethod method : clazz.methods()) { - for (final XmlInstruction instruction : method.instructions()) { - DecoratorPair.replaceArguments( - instruction.node(), - "org/eolang/jeo/B", - "org/eolang/jeo/A$B" - ); - } - } + clazz.methods() + .stream() + .map(XmlMethod::instructions) + .flatMap(Collection::stream) + .forEach( + instruction -> + instruction.replaceArguementsValues("org/eolang/jeo/B", "org/eolang/jeo/A$B") + ); } /** @@ -245,33 +243,29 @@ private static class DecoratorPair { * @return List of NEW instructions. */ private List targetNew() { - final String firstname = this.decorated.name(); - final Node first = new XMLDocument( - new StringBuilder() - .append("") - .append("") - .append(new HexData(firstname).value()) - .append("") - .append("") - .toString() - ).node().getFirstChild(); - final String secondname = this.decorator.name(); - final Node second = new XMLDocument( - new StringBuilder() - .append("") - .append("") - .append(new HexData(secondname).value()) - .append("") - .append("") - .toString() - ).node().getFirstChild(); - final Node dup = new XMLDocument("") - .node() - .getFirstChild(); + final String dup = ""; return Arrays.asList( - new XmlInstruction(second), + new XmlInstruction( + String.join( + "", + "", + "", + new HexData(this.decorator.name()).value(), + "", + "" + ) + ), new XmlInstruction(dup), - new XmlInstruction(first), + new XmlInstruction( + String.join( + "", + "", + "", + new HexData(this.decorated.name()).value(), + "", + "" + ) + ), new XmlInstruction(dup) ); } @@ -281,21 +275,18 @@ private List targetNew() { * @return Replacement. */ private List replacementNew() { - final Node second = new XMLDocument( - new StringBuilder() - .append("") - .append("") - .append(new DecoratorCompositionName(this.decorated, this.decorator).hex()) - .append("") - .append("") - .toString() - ).node().getFirstChild(); - final Node dup = new XMLDocument("") - .node() - .getFirstChild(); return Arrays.asList( - new XmlInstruction(second), - new XmlInstruction(dup) + new XmlInstruction( + String.join( + "", + "", + "", + new DecoratorCompositionName(this.decorated, this.decorator).hex(), + "", + "" + ) + ), + new XmlInstruction("") ); } @@ -306,38 +297,36 @@ private List replacementNew() { private List targetSpecial() { return Arrays.asList( new XmlInstruction( - new XMLDocument( - new StringBuilder() - .append("") - .append("") - .append(new HexData(this.decorated.name()).value()) - .append("") - .append("") - .append(new HexData("").value()) - .append("") - .append("") - .append(new HexData("(I)V").value()) - .append("") - .append("") - .toString() - ).node().getFirstChild() + String.join( + "", + "", + "", + new HexData(this.decorated.name()).value(), + "", + "", + new HexData("").value(), + "", + "", + new HexData("(I)V").value(), + "", + "" + ) ), new XmlInstruction( - new XMLDocument( - new StringBuilder() - .append("") - .append("") - .append(new HexData(this.decorator.name()).value()) - .append("") - .append("") - .append(new HexData("").value()) - .append("") - .append("") - .append(new HexData("(Lorg/eolang/jeo/A;)V").value()) - .append("") - .append("") - .toString() - ).node().getFirstChild() + String.join( + "", + "", + "", + new HexData(this.decorator.name()).value(), + "", + "", + new HexData("").value(), + "", + "", + new HexData("(Lorg/eolang/jeo/A;)V").value(), + "", + "" + ) ) ); } @@ -349,23 +338,20 @@ private List targetSpecial() { private List replacementSpecial() { return Collections.singletonList( new XmlInstruction( - new XMLDocument( - new StringBuilder() - .append("") - .append("") - .append( - new DecoratorCompositionName(this.decorated, this.decorator).hex() - ) - .append("") - .append("") - .append(new HexData("").value()) - .append("") - .append("") - .append(new HexData("(I)V").value()) - .append("") - .append("") - .toString() - ).node().getFirstChild() + String.join( + "", + "", + "", + new DecoratorCompositionName(this.decorated, this.decorator).hex(), + "", + "", + new HexData("").value(), + "", + "", + new HexData("(I)V").value(), + "", + "" + ) ) ); } @@ -377,7 +363,7 @@ private List replacementSpecial() { private Representation combine() { return new EoRepresentation( new XMLDocument( - this.skeleton( + this.combine( this.decorator.toEO(), new DecoratorCompositionName(this.decorated, this.decorator).value() ).toString() @@ -387,12 +373,12 @@ private Representation combine() { /** * Skeleton. - * @param decor Decorator. + * @param skeleton Decorator. * @param name Class name. * @return Combined XMIR representation. */ - private XML skeleton(final XML decor, final String name) { - final List roots = decor.nodes("/program"); + private XML combine(final XML skeleton, final String name) { + final List roots = skeleton.nodes("/program"); final Node root = roots.get(0).node(); final NamedNodeMap attributes = root.getAttributes(); attributes.getNamedItem("name").setNodeValue(name); @@ -454,11 +440,9 @@ private void handleRootObject(final Node root, final String bytename) { if (base.getNodeValue().equals("seq")) { final NodeList instructions = item.getChildNodes(); for (int inst = 0; inst < instructions.getLength(); ++inst) { - DecoratorPair.replaceArguments( - instructions.item(inst), - this.decorated.name(), - bytename - ); + new XmlInstruction( + instructions.item(inst) + ).replaceArguementsValues(this.decorated.name(), bytename); } } } @@ -526,11 +510,7 @@ private void replaceMethodContent( final Collection filtered = new ArrayList<>(0); for (final XmlInstruction xmlinstr : tadam) { final int codee = xmlinstr.code(); - DecoratorPair.replaceArguments( - xmlinstr.node(), - old, - bytename - ); + xmlinstr.replaceArguementsValues(old, bytename); if (codee != Opcodes.RETURN && codee != Opcodes.IRETURN && codee != Opcodes.ALOAD) { filtered.add(xmlinstr); @@ -545,32 +525,5 @@ private void replaceMethodContent( candidate.setInstructions(res); } } - - /** - * Replace arguments. - * @param node Instruction. - * @param oldname Old class name. - * @param newname New class name. - * @todo #157:90min Handle arguments correctly during inlining optimization. - * Right now we just replace all arguments with the new class name. - * It's not correct, because we need to handle arguments correctly. - */ - private static void replaceArguments( - final Node node, - final String oldname, - final String newname - ) { - final NodeList children = node.getChildNodes(); - for (int index = 0; index < children.getLength(); ++index) { - final Node child = children.item(index); - if (child.getNodeName().equals("o")) { - final String old = new HexData(oldname).value(); - final String content = child.getTextContent(); - if (old.equals(content)) { - child.setTextContent(new HexData(newname).value()); - } - } - } - } } } diff --git a/src/main/java/org/eolang/jeo/representation/xmir/XmlInstruction.java b/src/main/java/org/eolang/jeo/representation/xmir/XmlInstruction.java index bad1b5ad7..e558b1f74 100644 --- a/src/main/java/org/eolang/jeo/representation/xmir/XmlInstruction.java +++ b/src/main/java/org/eolang/jeo/representation/xmir/XmlInstruction.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Objects; import java.util.stream.IntStream; +import org.eolang.jeo.representation.HexData; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -46,6 +47,14 @@ public final class XmlInstruction { @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName") private final Node node; + /** + * Constructor. + * @param xml XML node as String. + */ + public XmlInstruction(final String xml) { + this(new XMLDocument(xml).node().getFirstChild()); + } + /** * Constructor. * @param node Instruction node. @@ -67,24 +76,28 @@ public int code() { } /** - * Instruction arguments. - * @return Arguments. + * Replace values of instruction arguments. + * @param old Old value. + * @param replacement Which value to set instead. */ - public Object[] arguments() { - return XmlInstruction.arguments(this.node); + public void replaceArguementsValues(final String old, final String replacement) { + final String oldname = new HexData(old).value(); + new XmlNode(this.node).children().forEach( + child -> { + final String content = child.text(); + if (oldname.equals(content)) { + child.withText(new HexData(replacement).value()); + } + } + ); } /** - * XML node. - * @return XML node. - * @todo #157:90min Hide internal node representation in XmlInstruction. - * This class should not expose internal node representation. - * We have to consider to add methods or classes in order to avoid - * exposing internal node representation. + * Instruction arguments. + * @return Arguments. */ - @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName") - public Node node() { - return this.node; + public Object[] arguments() { + return XmlInstruction.arguments(this.node); } @Override @@ -111,6 +124,19 @@ public String toString() { return new XMLDocument(this.node).toString(); } + /** + * XML node. + * @return XML node. + * @todo #157:90min Hide internal node representation in XmlInstruction. + * This class should not expose internal node representation. + * We have to consider to add methods or classes in order to avoid + * exposing internal node representation. + */ + @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName") + Node node() { + return this.node; + } + /** * Get opcode arguments. * @param node Node. diff --git a/src/main/java/org/eolang/jeo/representation/xmir/XmlNode.java b/src/main/java/org/eolang/jeo/representation/xmir/XmlNode.java index 991eb5f50..28d67a7be 100644 --- a/src/main/java/org/eolang/jeo/representation/xmir/XmlNode.java +++ b/src/main/java/org/eolang/jeo/representation/xmir/XmlNode.java @@ -24,6 +24,9 @@ package org.eolang.jeo.representation.xmir; import com.jcabi.xml.XMLDocument; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -66,6 +69,14 @@ XmlNode child(final String name) { throw this.notFound(name); } + /** + * Get all child nodes. + * @return Child nodes. + */ + Stream children() { + return this.objects().map(XmlNode::new); + } + /** * Convert to class. * @return Class. @@ -74,6 +85,22 @@ XmlClass toClass() { return new XmlClass(this.node); } + /** + * Retrieve node text content. + * @return Text content. + */ + String text() { + return this.node.getTextContent(); + } + + /** + * Set node text content. + * @param text Text content. + */ + void withText(final String text) { + this.node.setTextContent(text); + } + /** * Clean all child nodes. * @return The same node. @@ -109,4 +136,20 @@ private IllegalStateException notFound(final String name) { ) ); } + + /** + * Objects. + * @return Stream of class objects. + */ + private Stream objects() { + final NodeList children = this.node.getChildNodes(); + final List res = new ArrayList<>(children.getLength()); + for (int index = 0; index < children.getLength(); ++index) { + final Node child = children.item(index); + if (child.getNodeName().equals("o")) { + res.add(child); + } + } + return res.stream(); + } }