From 7c140642ecbc7698930302521d490a5e1e73f04f Mon Sep 17 00:00:00 2001 From: azerr Date: Fri, 24 May 2019 17:46:17 +0200 Subject: [PATCH] Improve memory (see #389) Add scanner & parser main test performance + use int instead of using Integer. Signed-off-by: azerr --- .../org/eclipse/lsp4xml/dom/DOMElement.java | 65 +++++++++++++------ .../java/org/eclipse/lsp4xml/dom/DOMNode.java | 10 ++- .../lsp4xml/dom/DOMProcessingInstruction.java | 11 +++- .../org/eclipse/lsp4xml/dom/DTDDeclNode.java | 7 -- .../lsp4xml/services/XMLCompletions.java | 2 +- .../lsp4xml/utils/XMLPositionUtility.java | 2 +- .../eclipse/lsp4xml/dom/DOMDocumentTest.java | 7 +- .../eclipse/lsp4xml/dom/DOMParserTest.java | 19 ++---- .../performance/DOMParserPerformance.java | 40 ++++++++++++ .../performance/XMLScannerPerformance.java | 43 ++++++++++++ .../org/eclipse/lsp4xml/utils/IOUtils.java | 36 ++++++++++ 11 files changed, 191 insertions(+), 51 deletions(-) create mode 100644 org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/performance/DOMParserPerformance.java create mode 100644 org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/performance/XMLScannerPerformance.java create mode 100644 org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/utils/IOUtils.java diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMElement.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMElement.java index 60a419f98..5d4e4e88c 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMElement.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMElement.java @@ -32,11 +32,11 @@ public class DOMElement extends DOMNode implements org.w3c.dom.Element { boolean selfClosed; //DomElement.start == startTagOpenOffset - Integer startTagOpenOffset; // | - Integer startTagCloseOffset; // + int startTagOpenOffset = NULL_VALUE; // | + int startTagCloseOffset = NULL_VALUE; // - Integer endTagOpenOffset; // | - Integer endTagCloseOffset;// + int endTagOpenOffset = NULL_VALUE; // | + int endTagCloseOffset = NULL_VALUE;// //DomElement.end = | , is always scanner.getTokenEnd() public DOMElement(int start, int end, DOMDocument ownerDocument) { @@ -312,7 +312,7 @@ public boolean isSameTag(String tagInLowerCase) { } public boolean isInStartTag(int offset) { - if (startTagOpenOffset == null || startTagCloseOffset == null) { + if (startTagOpenOffset == NULL_VALUE || startTagCloseOffset == NULL_VALUE) { // case <| return true; } @@ -324,7 +324,7 @@ public boolean isInStartTag(int offset) { } public boolean isInEndTag(int offset) { - if (endTagOpenOffset == null) { + if (endTagOpenOffset == NULL_VALUE) { // case >| return false; } @@ -335,23 +335,48 @@ public boolean isInEndTag(int offset) { return false; } - public boolean hasStartTagClose() { - return startTagCloseOffset != null; - } - - public Integer getStartTagOpenOffset() { + /** + * Returns the start tag open offset and {@link DOMNode#NULL_VALUE} if it + * doesn't exist. + * + * @return the start tag open offset and {@link DOMNode#NULL_VALUE} if it + * doesn't exist. + */ + public int getStartTagOpenOffset() { return startTagOpenOffset; } - public Integer getStartTagCloseOffset() { + /** + * Returns the start tag close offset and {@link DOMNode#NULL_VALUE} if it + * doesn't exist. + * + * @return the start tag close offset and {@link DOMNode#NULL_VALUE} if it + * doesn't exist. + */ + public int getStartTagCloseOffset() { return startTagCloseOffset; } - public Integer getEndTagOpenOffset() { + /** + * Returns the end tag open offset and {@link DOMNode#NULL_VALUE} if it doesn't + * exist. + * + * @return the end tag open offset and {@link DOMNode#NULL_VALUE} if it doesn't + * exist. + */ + public int getEndTagOpenOffset() { return endTagOpenOffset; } - public Integer getEndTagCloseOffset() { + + /** + * Returns the end tag close offset and {@link DOMNode#NULL_VALUE} if it doesn't + * exist. + * + * @return the end tag close offset and {@link DOMNode#NULL_VALUE} if it doesn't + * exist. + */ + public int getEndTagCloseOffset() { return endTagCloseOffset; } @@ -364,7 +389,7 @@ public Integer getEndTagCloseOffset() { * @return true if has a start tag. */ public boolean hasStartTag() { - return startTagOpenOffset != null; + return getStartTagOpenOffset() != NULL_VALUE; } /** @@ -376,27 +401,27 @@ public boolean hasStartTag() { * @return true if has an end tag. */ public boolean hasEndTag() { - return endTagOpenOffset != null; + return getEndTagOpenOffset() != NULL_VALUE; } /** * If '>' exists in */ public boolean isStartTagClosed() { - return startTagCloseOffset != null; + return getStartTagCloseOffset() != NULL_VALUE; } /** * If '>' exists in */ public boolean isEndTagClosed() { - return endTagCloseOffset != null; + return getEndTagCloseOffset() != NULL_VALUE; } - - @Override + /** * If Element has a closing end tag eg: -> true , -> false */ + @Override public boolean isClosed() { return super.isClosed(); } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMNode.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMNode.java index 94f98a4df..f2ae11800 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMNode.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMNode.java @@ -27,6 +27,11 @@ */ public abstract class DOMNode implements Node { + /** + * Null value used for offset. + */ + public static final int NULL_VALUE = -1; + /** * The node is a DTD Element Declaration. */ @@ -190,7 +195,7 @@ private String toString(int indent) { } /** - * Returns the node before + * Returns the node before */ public DOMNode findNodeBefore(int offset) { List children = getChildren(); @@ -292,7 +297,7 @@ public DOMAttr getAttributeNode(String name) { */ public DOMAttr getAttributeNode(String prefix, String suffix) { StringBuilder sb = new StringBuilder(); - if(prefix != null) { + if (prefix != null) { sb.append(prefix); sb.append(":"); } @@ -441,7 +446,6 @@ public boolean isDoctype() { public boolean isGenericDTDDecl() { return getNodeType() == DOMNode.DTD_DECL_NODE; } - public boolean isElement() { return getNodeType() == DOMNode.ELEMENT_NODE; diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMProcessingInstruction.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMProcessingInstruction.java index bd0c14984..36e5ee97b 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMProcessingInstruction.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMProcessingInstruction.java @@ -24,7 +24,7 @@ public class DOMProcessingInstruction extends DOMCharacterData implements org.w3 boolean processingInstruction = false; int startContent; int endContent; - Integer endTagOpenOffset; + int endTagOpenOffset = NULL_VALUE; public DOMProcessingInstruction(int start, int end, DOMDocument ownerDocument) { super(start, end, ownerDocument); @@ -46,7 +46,14 @@ public int getEndContent() { return endContent; } - public Integer getEndTagStart() { + /** + * Returns the end tag start offset and {@link DOMNode#NULL_VALUE} if it doesn't + * exist. + * + * @return the end tag start offset and {@link DOMNode#NULL_VALUE} if it doesn't + * exist. + */ + public int getEndTagStart() { return endTagOpenOffset; } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DTDDeclNode.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DTDDeclNode.java index 1e3efd497..31367d5e7 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DTDDeclNode.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DTDDeclNode.java @@ -63,13 +63,6 @@ public String getUnrecognized() { public void setUnrecognized(int start, int end) { unrecognized = addNewParameter(start, end); - } - - public static String getValueFromOffsets(DOMDocumentType document, String value, Integer start, Integer end) { - if(value == null && start != null && end != null) { - return document.getSubstring(start, end); - } - return value; } public DTDDeclParameter addNewParameter(int start, int end) { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java index 614da2356..ff60c44f2 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java @@ -383,7 +383,7 @@ public AutoCloseTagResponse doTagComplete(DOMDocument xmlDocument, Position posi // Case: ' after slash - if(element1.getStartTagCloseOffset() != null) { // tag has closing '>', but slash is in incorrect area (not directly before the '>') + if(element1.isStartTagClosed()) { // tag has closing '>', but slash is in incorrect area (not directly before the '>') return null; } snippet = ">$0"; diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLPositionUtility.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLPositionUtility.java index 89e94331d..b33407d86 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLPositionUtility.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLPositionUtility.java @@ -349,7 +349,7 @@ public static Range selectPreviousNodesEndTag(int offset, DOMDocument document) } if(node != null) { DOMElement element = (DOMElement) node; - if(element.isClosed() && element.getEndTagCloseOffset() == null) { + if(element.isClosed() && !element.isEndTagClosed()) { return selectEndTag(element.getEnd(), document); } } diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMDocumentTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMDocumentTest.java index dc3ee32f6..90556ced0 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMDocumentTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMDocumentTest.java @@ -1,5 +1,7 @@ package org.eclipse.lsp4xml.dom; +import static org.eclipse.lsp4xml.utils.IOUtils.convertStreamToString; + import java.io.InputStream; import javax.xml.xpath.XPath; @@ -151,11 +153,6 @@ public void findElementListWithXPath() throws XPathExpressionException { } - static String convertStreamToString(InputStream is) { - java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); - return s.hasNext() ? s.next() : ""; - } - @Test public void testUsesSchemaTrue1WithNamespace() { String text = diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMParserTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMParserTest.java index 56d313f26..be8dd7fab 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMParserTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMParserTest.java @@ -14,7 +14,6 @@ import java.util.ArrayList; -import org.eclipse.lsp4j.FormattingOptions; import org.eclipse.lsp4xml.dom.DOMDocumentType.DocumentTypeKind; import org.junit.Assert; import org.junit.Ignore; @@ -96,8 +95,7 @@ public void singleEndTag() { assertDocument("", meta); Assert.assertFalse(meta.hasStartTag()); Assert.assertTrue(meta.hasEndTag()); - Assert.assertNotNull(meta.getEndTagOpenOffset()); - Assert.assertEquals(meta.getEndTagOpenOffset().intValue(), 0); // | + Assert.assertEquals(meta.getEndTagOpenOffset(), 0); // | } @Test @@ -109,8 +107,7 @@ public void insideEndTag() { assertDocument("", html); Assert.assertFalse(meta.hasStartTag()); Assert.assertTrue(meta.hasEndTag()); - Assert.assertNotNull(meta.getEndTagOpenOffset()); - Assert.assertEquals(meta.getEndTagOpenOffset().intValue(), 6); // | + Assert.assertEquals(meta.getEndTagOpenOffset(), 6); // | } @Test @@ -447,11 +444,9 @@ public void elementOffsets() { Assert.assertNotNull(a); Assert.assertEquals(a.getTagName(), "a"); Assert.assertEquals(a.getStart(), 0); // | - Assert.assertNotNull(a.getStartTagOpenOffset()); // | - Assert.assertEquals(a.getStartTagOpenOffset().intValue(), 0); // | - Assert.assertNotNull(a.getStartTagCloseOffset()); // - Assert.assertEquals(a.getStartTagCloseOffset().intValue(), 2); // - Assert.assertEquals(a.getEndTagOpenOffset().intValue(), 3); // | + Assert.assertEquals(a.getStartTagOpenOffset(), 0); // | + Assert.assertEquals(a.getStartTagCloseOffset(), 2); // + Assert.assertEquals(a.getEndTagOpenOffset(), 3); // | Assert.assertEquals(a.getEnd(), 7); // | Assert.assertFalse(a.isInStartTag(0)); // | @@ -1003,10 +998,10 @@ private static DOMNode createNode(short nodeType, int start, int end) { private static void setRestOfNode(DOMNode n, String tag, Integer endTagStart, boolean closed) { if (n.isElement()) { ((DOMElement) n).tag = tag; - ((DOMElement) n).endTagOpenOffset = endTagStart; + ((DOMElement) n).endTagOpenOffset = endTagStart != null ? endTagStart : DOMNode.NULL_VALUE; } else if (n instanceof DOMProcessingInstruction) { ((DOMProcessingInstruction) n).target = tag; - ((DOMProcessingInstruction) n).endTagOpenOffset = endTagStart; + ((DOMProcessingInstruction) n).endTagOpenOffset = endTagStart != null ? endTagStart : DOMNode.NULL_VALUE; } n.closed = closed; } diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/performance/DOMParserPerformance.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/performance/DOMParserPerformance.java new file mode 100644 index 000000000..d2e307055 --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/performance/DOMParserPerformance.java @@ -0,0 +1,40 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4xml.performance; + +import static org.eclipse.lsp4xml.utils.IOUtils.convertStreamToString; + +import java.io.InputStream; + +import org.eclipse.lsp4xml.commons.TextDocument; +import org.eclipse.lsp4xml.dom.DOMDocument; +import org.eclipse.lsp4xml.dom.DOMParser; + +/** + * This utility class is used to check the memory usage of {@link DOMParser}, + * loading the large nasa.xml file. + * + * @author Angelo ZERR + * + */ +public class DOMParserPerformance { + + public static void main(String[] args) { + InputStream in = DOMParserPerformance.class.getResourceAsStream("/xml/nasa.xml"); + String text = convertStreamToString(in); + TextDocument document = new TextDocument(text, "nasa.xml"); + // Continuously parses the large nasa.xml file with the DOM parser. + while (true) { + long start = System.currentTimeMillis(); + DOMDocument xmlDocument = DOMParser.getInstance().parse(document, null); + System.err.println("Parsed 'nasa.xml' with DOMParser in " + (System.currentTimeMillis() - start) + " ms."); + } + } +} diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/performance/XMLScannerPerformance.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/performance/XMLScannerPerformance.java new file mode 100644 index 000000000..724e20095 --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/performance/XMLScannerPerformance.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4xml.performance; + +import static org.eclipse.lsp4xml.utils.IOUtils.convertStreamToString; + +import java.io.InputStream; + +import org.eclipse.lsp4xml.dom.parser.Scanner; +import org.eclipse.lsp4xml.dom.parser.TokenType; +import org.eclipse.lsp4xml.dom.parser.XMLScanner; + +/** + * This utility class is used to check the memory usage of {@link XMLScanner}, + * loading the large nasa.xml file + * + * @author Angelo ZERR + * + */ +public class XMLScannerPerformance { + + public static void main(String[] args) { + InputStream in = XMLScannerPerformance.class.getResourceAsStream("/xml/nasa.xml"); + String text = convertStreamToString(in); + // Continuously parses the large nasa.xml file with the XML scanner + while (true) { + long start = System.currentTimeMillis(); + Scanner scanner = XMLScanner.createScanner(text); + TokenType token = scanner.scan(); + while (token != TokenType.EOS) { + token = scanner.scan(); + } + System.err.println("Parsed 'nasa.xml' with XMLScanner in " + (System.currentTimeMillis() - start) + " ms."); + } + } +} diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/utils/IOUtils.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/utils/IOUtils.java new file mode 100644 index 000000000..b5bbc0740 --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/utils/IOUtils.java @@ -0,0 +1,36 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4xml.utils; + +import java.io.InputStream; +import java.util.Scanner; + +/** + * IO utilities class + * + * @author Angelo ZERR + * + */ +public class IOUtils { + + /** + * Convert the given {@link InputStream} into a String. The source InputStream + * will then be closed. + * + * @param is the input stream + * @return the given input stream in a String. + */ + public static String convertStreamToString(InputStream is) { + try (Scanner s = new java.util.Scanner(is)) { + s.useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } + } +}