From 9cd3b88e8bbf580e5f923f6e1681369e7d3c6edd Mon Sep 17 00:00:00 2001 From: David Shi Date: Tue, 10 Oct 2023 12:49:00 -0500 Subject: [PATCH] Diagnostics and Quick fixes for lost config elements (#220) * init push * revert featureservice, refactor, addfeature wip * AddFeature indentations, clarification * PR cleanup * move featureListGraph to workspaces * add feature clarification and cleanup * Use new surefire with junit 5 support, skip tests in build step GHA * Fix uri string in tests * graph cache. github actions still fail, pass local * correct trigger for empty featureManager * PR comments * reduce request delay 120 -> 10 * working state for "empty featureManager" * add logger to addFeature --------- Co-authored-by: Evie Lau --- .github/workflows/build.yml | 2 +- lemminx-liberty/pom.xml | 11 +- .../lemminx/LibertyCodeActionParticipant.java | 2 + .../lemminx/LibertyDiagnosticParticipant.java | 99 ++++++++--- .../lemminx/codeactions/AddFeature.java | 120 +++++++++++++ .../lemminx/codeactions/IndentUtil.java | 39 +++++ .../lemminx/data/FeatureListGraph.java | 158 ++++++++++++++++++ .../lemminx/data/FeatureListNode.java | 51 ++++++ .../lemminx/models/feature/Feature.java | 20 +++ .../lemminx/services/FeatureService.java | 41 ++++- .../lemminx/services/LibertyWorkspace.java | 26 ++- .../lemminx/services/SettingsService.java | 4 +- .../openliberty/CodeActionUtilitiesTest.java | 33 ++++ .../io/openliberty/LibertyDiagnosticTest.java | 89 +++++++++- .../io/openliberty/LibertyFeatureTest.java | 14 ++ .../java/io/openliberty/XmlReaderTest.java | 18 +- liberty-ls/pom.xml | 2 +- 17 files changed, 684 insertions(+), 45 deletions(-) create mode 100644 lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/AddFeature.java create mode 100644 lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/IndentUtil.java create mode 100644 lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/FeatureListGraph.java create mode 100644 lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/FeatureListNode.java create mode 100644 lemminx-liberty/src/test/java/io/openliberty/CodeActionUtilitiesTest.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d885f6f8..549113dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: java-version: 17 - name: Build Lemminx Liberty working-directory: ./lemminx-liberty - run: ./mvnw clean package -ntp + run: ./mvnw clean package -ntp -DskipTests - name: Test Lemminx Liberty working-directory: ./lemminx-liberty run: ./mvnw verify -ntp \ No newline at end of file diff --git a/lemminx-liberty/pom.xml b/lemminx-liberty/pom.xml index 28698ab8..d026891d 100644 --- a/lemminx-liberty/pom.xml +++ b/lemminx-liberty/pom.xml @@ -4,7 +4,7 @@ io.openliberty.tools liberty-langserver-lemminx jar - 2.0.2-SNAPSHOT + 2.1-SNAPSHOT lemminx-liberty https://github.com/OpenLiberty/liberty-language-server @@ -55,6 +55,15 @@ 17 + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + maven-failsafe-plugin + 3.1.2 + org.apache.maven.plugins maven-shade-plugin diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCodeActionParticipant.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCodeActionParticipant.java index 0d8a93fa..a2092722 100644 --- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCodeActionParticipant.java +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCodeActionParticipant.java @@ -24,6 +24,7 @@ import org.eclipse.lsp4j.jsonrpc.CancelChecker; import io.openliberty.tools.langserver.lemminx.codeactions.AddAttribute; +import io.openliberty.tools.langserver.lemminx.codeactions.AddFeature; import io.openliberty.tools.langserver.lemminx.codeactions.CreateFile; import io.openliberty.tools.langserver.lemminx.codeactions.EditAttribute; import io.openliberty.tools.langserver.lemminx.codeactions.ReplaceFeature; @@ -55,6 +56,7 @@ private void registerCodeActions() { codeActionParticipants.put(LibertyDiagnosticParticipant.NOT_OPTIONAL_CODE, new EditAttribute()); codeActionParticipants.put(LibertyDiagnosticParticipant.IMPLICIT_NOT_OPTIONAL_CODE, new AddAttribute()); codeActionParticipants.put(LibertyDiagnosticParticipant.INCORRECT_FEATURE_CODE, new ReplaceFeature()); + codeActionParticipants.put(LibertyDiagnosticParticipant.MISSING_CONFIGURED_FEATURE_CODE, new AddFeature()); } } } diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyDiagnosticParticipant.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyDiagnosticParticipant.java index b61941a0..736c359c 100644 --- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyDiagnosticParticipant.java +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyDiagnosticParticipant.java @@ -23,27 +23,41 @@ import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import io.openliberty.tools.langserver.lemminx.data.FeatureListGraph; import io.openliberty.tools.langserver.lemminx.data.LibertyRuntime; import io.openliberty.tools.langserver.lemminx.services.FeatureService; +import io.openliberty.tools.langserver.lemminx.services.LibertyProjectsManager; +import io.openliberty.tools.langserver.lemminx.services.LibertyWorkspace; import io.openliberty.tools.langserver.lemminx.services.SettingsService; import io.openliberty.tools.langserver.lemminx.util.*; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.logging.Logger; public class LibertyDiagnosticParticipant implements IDiagnosticsParticipant { + private static final Logger LOGGER = Logger.getLogger(LibertyDiagnosticParticipant.class.getName()); + + public static final String LIBERTY_LEMMINX_SOURCE = "liberty-lemminx"; + public static final String MISSING_FILE_MESSAGE = "The resource at the specified location could not be found."; public static final String MISSING_FILE_CODE = "missing_file"; + public static final String MISSING_CONFIGURED_FEATURE_MESSAGE = "This config element does not relate to a feature configured in the featureManager. Remove this element or add a relevant feature."; + public static final String MISSING_CONFIGURED_FEATURE_CODE = "lost_config_element"; + public static final String NOT_OPTIONAL_MESSAGE = "The specified resource cannot be skipped. Check location value or set optional to true."; public static final String NOT_OPTIONAL_CODE = "not_optional"; public static final String IMPLICIT_NOT_OPTIONAL_MESSAGE = "The specified resource cannot be skipped. Check location value or add optional attribute."; public static final String IMPLICIT_NOT_OPTIONAL_CODE = "implicit_not_optional"; public static final String INCORRECT_FEATURE_CODE = "incorrect_feature"; + + private Set includedFeatures; @Override public void doDiagnostics(DOMDocument domDocument, List diagnostics, @@ -53,22 +67,29 @@ public void doDiagnostics(DOMDocument domDocument, List diagnostics, try { validateDom(domDocument, diagnostics); } catch (IOException e) { - System.err.println("Error validating document " + domDocument.getDocumentURI()); - System.err.println(e.getMessage()); + LOGGER.severe("Error validating document " + domDocument.getDocumentURI()); + LOGGER.severe(e.getMessage()); } } - private void validateDom(DOMDocument domDocument, List list) throws IOException { + private void validateDom(DOMDocument domDocument, List diagnosticsList) throws IOException { List nodes = domDocument.getDocumentElement().getChildren(); - + List tempDiagnosticsList = new ArrayList(); + includedFeatures = new HashSet<>(); + LibertyWorkspace workspace = LibertyProjectsManager.getInstance().getWorkspaceFolder(domDocument.getDocumentURI()); + // TODO: Consider adding a cached feature list onto repo to optimize + FeatureListGraph featureGraph = (workspace == null) ? new FeatureListGraph() : workspace.getFeatureListGraph(); for (DOMNode node : nodes) { - if (LibertyConstants.FEATURE_MANAGER_ELEMENT.equals(node.getNodeName())) { - validateFeature(domDocument, list, node); - } else if (LibertyConstants.INCLUDE_ELEMENT.equals(node.getNodeName())) { - validateIncludeLocation(domDocument, list, node); + String nodeName = node.getNodeName(); + if (LibertyConstants.FEATURE_MANAGER_ELEMENT.equals(nodeName)) { + validateFeature(domDocument, diagnosticsList, node); + } else if (LibertyConstants.INCLUDE_ELEMENT.equals(nodeName)) { + validateIncludeLocation(domDocument, diagnosticsList, node); + } else if (featureGraph.isConfigElement(nodeName)) { // defaults to false + holdConfigElement(domDocument, node, tempDiagnosticsList); } } - + validateConfigElements(diagnosticsList, tempDiagnosticsList, featureGraph); } private void validateFeature(DOMDocument domDocument, List list, DOMNode featureManager) { @@ -80,7 +101,6 @@ private void validateFeature(DOMDocument domDocument, List list, DOM // Search for duplicate features // or features that do not exist - Set includedFeatures = new HashSet<>(); List features = featureManager.getChildren(); for (DOMNode featureNode : features) { DOMNode featureTextNode = (DOMNode) featureNode.getChildNodes().item(0); @@ -93,13 +113,13 @@ private void validateFeature(DOMDocument domDocument, List list, DOM Range range = XMLPositionUtility.createRange(featureTextNode.getStart(), featureTextNode.getEnd(), domDocument); String message = "ERROR: The feature \"" + featureName + "\" does not exist."; - list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, "liberty-lemminx", INCORRECT_FEATURE_CODE)); + list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE, INCORRECT_FEATURE_CODE)); } else { if (includedFeatures.contains(featureName)) { Range range = XMLPositionUtility.createRange(featureTextNode.getStart(), featureTextNode.getEnd(), domDocument); String message = "ERROR: " + featureName + " is already included."; - list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, "liberty-lemminx")); + list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE)); } else { includedFeatures.add(featureName); } @@ -117,7 +137,7 @@ private void validateFeature(DOMDocument domDocument, List list, DOM * 2) performed in isConfigXMLFile * 4) not yet implemented/determined */ - private void validateIncludeLocation(DOMDocument domDocument, List list, DOMNode node) { + private void validateIncludeLocation(DOMDocument domDocument, List diagnosticsList, DOMNode node) { String locAttribute = node.getAttribute("location"); if (locAttribute == null) { return; @@ -131,7 +151,7 @@ private void validateIncludeLocation(DOMDocument domDocument, List l Range range = XMLPositionUtility.createRange(locNode.getStart(), locNode.getEnd(), domDocument); if (!locAttribute.endsWith(".xml")) { String message = "The specified resource is not an XML file."; - list.add(new Diagnostic(range, message, DiagnosticSeverity.Warning, "liberty-lemminx")); + diagnosticsList.add(new Diagnostic(range, message, DiagnosticSeverity.Warning, LIBERTY_LEMMINX_SOURCE)); return; } @@ -144,15 +164,56 @@ private void validateIncludeLocation(DOMDocument domDocument, List l if (!configFile.exists()) { DOMAttr optNode = node.getAttributeNode("optional"); if (optNode == null) { - list.add(new Diagnostic(range, IMPLICIT_NOT_OPTIONAL_MESSAGE, DiagnosticSeverity.Error, "liberty-lemminx", IMPLICIT_NOT_OPTIONAL_CODE)); + diagnosticsList.add(new Diagnostic(range, IMPLICIT_NOT_OPTIONAL_MESSAGE, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE, IMPLICIT_NOT_OPTIONAL_CODE)); } else if (optNode.getValue().equals("false")) { Range optRange = XMLPositionUtility.createRange(optNode.getStart(), optNode.getEnd(), domDocument); - list.add(new Diagnostic(optRange, NOT_OPTIONAL_MESSAGE, DiagnosticSeverity.Error, "liberty-lemminx", NOT_OPTIONAL_CODE)); + diagnosticsList.add(new Diagnostic(optRange, NOT_OPTIONAL_MESSAGE, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE, NOT_OPTIONAL_CODE)); } - list.add(new Diagnostic(range, MISSING_FILE_MESSAGE, DiagnosticSeverity.Warning, "liberty-lemminx", MISSING_FILE_CODE)); + diagnosticsList.add(new Diagnostic(range, MISSING_FILE_MESSAGE, DiagnosticSeverity.Warning, LIBERTY_LEMMINX_SOURCE, MISSING_FILE_CODE)); } } catch (IllegalArgumentException e) { - list.add(new Diagnostic(range, MISSING_FILE_MESSAGE, DiagnosticSeverity.Warning, "liberty-lemminx-exception", MISSING_FILE_CODE)); + diagnosticsList.add(new Diagnostic(range, MISSING_FILE_MESSAGE, DiagnosticSeverity.Warning, "liberty-lemminx-exception", MISSING_FILE_CODE)); + } + } + + /** + * Create temporary diagnostics for validation for single pass-through. + * @param domDocument + * @param diagnosticsList + * @param configElementNode + * @param tempDiagnosticsList + */ + private void holdConfigElement(DOMDocument domDocument, DOMNode configElementNode, List tempDiagnosticsList) { + String configElementName = configElementNode.getNodeName(); + Range range = XMLPositionUtility + .createRange(configElementNode.getStart(), configElementNode.getEnd(), domDocument); + Diagnostic tempDiagnostic = new Diagnostic(range, MISSING_CONFIGURED_FEATURE_MESSAGE, null, LIBERTY_LEMMINX_SOURCE, MISSING_CONFIGURED_FEATURE_CODE); + tempDiagnostic.setSource(configElementName); + tempDiagnosticsList.add(tempDiagnostic); + } + + /** + * Compare the required feature set with included feature set for each config element. + * @param diagnosticsList + * @param tempDiagnosticsList + * @param featureGraph + */ + private void validateConfigElements(List diagnosticsList, List tempDiagnosticsList, FeatureListGraph featureGraph) { + if (featureGraph.isEmpty()) { + return; + } + if (includedFeatures.isEmpty()) { + diagnosticsList.addAll(tempDiagnosticsList); + return; + } + for (Diagnostic tempDiagnostic : tempDiagnosticsList) { + String configElement = tempDiagnostic.getSource(); + Set includedFeaturesCopy = new HashSet(includedFeatures); + Set compatibleFeaturesList = featureGraph.getAllEnabledBy(configElement); + includedFeaturesCopy.retainAll(compatibleFeaturesList); + if (includedFeaturesCopy.isEmpty()) { + diagnosticsList.add(tempDiagnostic); + } } } -} +} \ No newline at end of file diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/AddFeature.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/AddFeature.java new file mode 100644 index 00000000..38aae88b --- /dev/null +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/AddFeature.java @@ -0,0 +1,120 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package io.openliberty.tools.langserver.lemminx.codeactions; + +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lemminx.commons.CodeActionFactory; +import org.eclipse.lemminx.commons.TextDocument; +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.dom.DOMElement; +import org.eclipse.lemminx.dom.DOMNode; +import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionParticipant; +import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionRequest; +import org.eclipse.lemminx.utils.XMLPositionUtility; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; + +import io.openliberty.tools.langserver.lemminx.services.LibertyProjectsManager; +import io.openliberty.tools.langserver.lemminx.util.LibertyConstants; + +public class AddFeature implements ICodeActionParticipant { + Logger LOGGER = Logger.getLogger(AddFeature.class.getName()); + + /** This code action adresses 3 main situations: + * 1) Add a feature to an existing empty featureManager + * 2) Add a feature to an existing featureManager with children + * 3) Add a feature and new featureManager + * + * To calculate where to insert, each scenario will use a reference point to calculate range + * 1) The startTag of the featureManager + * 2) The last child of the featureManager + * 3) The startTag of the server.xml + */ + public static final String FEATURE_FORMAT = "%s"; + public static final String FEATUREMANAGER_FORMAT = + "\n\t"+ + "\n\t\t%s"+ + "\n\t"; + + @Override + public void doCodeAction(ICodeActionRequest request, List codeActions, CancelChecker cancelChecker) { + Diagnostic diagnostic = request.getDiagnostic(); + DOMDocument document = request.getDocument(); + TextDocument textDocument = document.getTextDocument(); + // getAllEnabledBy would return all transitive features but typically offers too much + Set featureCandidates = LibertyProjectsManager.getInstance() + .getWorkspaceFolder(document.getDocumentURI()) + .getFeatureListGraph().get(diagnostic.getSource()).getEnabledBy(); + if (featureCandidates.isEmpty()) { + return; + } + + String insertText = ""; + int referenceRangeStart = 0; + int referenceRangeEnd = 0; + + for (DOMNode node : document.getDocumentElement().getChildren()) { + if (LibertyConstants.FEATURE_MANAGER_ELEMENT.equals(node.getNodeName())) { + DOMNode lastChild = node.getLastChild(); + if (node.getChildren().size() > 1) { + // Situation 2 + insertText = "\n" + FEATURE_FORMAT; + referenceRangeStart = lastChild.getStart(); + referenceRangeEnd = lastChild.getEnd(); + } else { + if (lastChild != null && (lastChild.hasChildNodes() || lastChild.isComment())) { + // Situation 2 + insertText = "\n" + FEATURE_FORMAT; + referenceRangeStart = lastChild.getStart(); + referenceRangeEnd = lastChild.getEnd(); + } else { + // Situation 1 + insertText = "\n\t" + FEATURE_FORMAT; + DOMElement featureManager = (DOMElement) node; + referenceRangeStart = featureManager.getStartTagOpenOffset(); + referenceRangeEnd = featureManager.getStartTagCloseOffset()+1; + } + } + break; + } + } + // Situation 3 + if (insertText.isEmpty()) { + insertText = FEATUREMANAGER_FORMAT; + DOMElement server = document.getDocumentElement(); + referenceRangeStart = server.getStart(); + referenceRangeEnd = server.getStartTagCloseOffset()+1; + } + Range referenceRange = XMLPositionUtility.createRange(referenceRangeStart, referenceRangeEnd, document); + + String indent = " "; + try { + indent = request.getXMLGenerator().getWhitespacesIndent(); + } catch (BadLocationException e) { + LOGGER.info("Defaulting indent to four spaces."); + } + insertText = IndentUtil.formatText(insertText, indent, referenceRange.getStart().getCharacter()); + + for (String feature : featureCandidates) { + String title = "Add feature " + feature; + codeActions.add(CodeActionFactory.insert( + title, referenceRange.getEnd(), String.format(insertText, feature), textDocument, diagnostic)); + } + } +} diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/IndentUtil.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/IndentUtil.java new file mode 100644 index 00000000..205eecad --- /dev/null +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/IndentUtil.java @@ -0,0 +1,39 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package io.openliberty.tools.langserver.lemminx.codeactions; + +// Use this helper class to format your strings with indentation +public class IndentUtil { + public static final String NEW_LINE = System.lineSeparator(); + + public static String whitespaceBuffer(String indent, int column) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < column / indent.length(); ++i) { + sb.append(indent); + } + return sb.toString(); + } + + /** + * Will return a string where `\n` will be replaced with a proper line separator and match the + * indentation level for the passed in column number. Adding `\t` will add an indent level. + * @param text + * @param indent + * @param column + * @return + */ + public static String formatText(String text, String indent, int column) { + return text.replace("\n", System.lineSeparator() + whitespaceBuffer(indent, column)) + .replace("\t", indent); + } +} diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/FeatureListGraph.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/FeatureListGraph.java new file mode 100644 index 00000000..3d4a40ff --- /dev/null +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/FeatureListGraph.java @@ -0,0 +1,158 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package io.openliberty.tools.langserver.lemminx.data; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class FeatureListGraph { + private String runtime = ""; + private Map nodes; + private Map> enabledByCache; + private Map> enablesCache; + + public FeatureListGraph() { + nodes = new HashMap(); + enabledByCache = new HashMap>(); + enablesCache = new HashMap>(); + } + + public FeatureListNode addFeature(String nodeName) { + if (nodes.containsKey(nodeName)) { + return nodes.get(nodeName); + } + FeatureListNode node = new FeatureListNode(nodeName); + nodes.put(nodeName, node); + return node; + } + + public FeatureListNode addConfigElement(String nodeName) { + if (nodes.containsKey(nodeName)) { + return nodes.get(nodeName); + } + FeatureListNode node = new FeatureListNode(nodeName); + nodes.put(nodeName, node); + return node; + } + + public FeatureListNode get(String nodeName) { + return nodes.get(nodeName); + } + + public boolean isEmpty() { + return nodes.isEmpty(); + } + + public boolean isConfigElement(String featureListNode) { + if (!nodes.containsKey(featureListNode)) { + return false; + } + return nodes.get(featureListNode).isConfigElement(); + } + + public void setRuntime(String runtime) { + this.runtime = runtime; + } + + public String getRuntime() { + return this.runtime; + } + + /** + * Returns a superset of 'owning' features that enable a given config element or feature. + * @param elementName + * @return + */ + public Set getAllEnabledBy(String elementName) { + if (enabledByCache.containsKey(elementName)) { + return enabledByCache.get(elementName); + } + if (!nodes.containsKey(elementName)) { + return null; + } + // Implements a breadth-first-search on parent nodes + Set allEnabledBy = new HashSet(nodes.get(elementName).getEnabledBy()); + Deque queue = new ArrayDeque(allEnabledBy); + Set visited = new HashSet(); + while (!queue.isEmpty()) { + String node = queue.getFirst(); + queue.removeFirst(); + if (visited.contains(node)) { + continue; + } + Set enablers = nodes.get(node).getEnabledBy(); + visited.add(node); + allEnabledBy.addAll(enablers); + queue.addAll(enablers); + } + enabledByCache.put(elementName, allEnabledBy); + return allEnabledBy; + } + + /** + * Returns the set of supported features or config elements for a given feature. + * @param feature + * @return + */ + public Set getAllEnables(String feature) { + if (enablesCache.containsKey(feature)) { + return enablesCache.get(feature); + } + if (!nodes.containsKey(feature)) { + return null; + } + // Implements a breadth-first-search on child nodes + Set allEnables = new HashSet(nodes.get(feature).getEnables()); + Deque queue = new ArrayDeque(allEnables); + Set visited = new HashSet(); + while (!queue.isEmpty()) { + String node = queue.getFirst(); + queue.removeFirst(); + if (visited.contains(node)) { + continue; + } + Set enablers = nodes.get(node).getEnables(); + visited.add(node); + allEnables.addAll(enablers); + queue.addAll(enablers); + } + enablesCache.put(feature, allEnables); + return allEnables; + } + + /** Will be useful for future features **/ + + // public Set getAllConfigElements(String feature) { + // Set configElements = new HashSet(); + // for (String node : getAllEnables(feature)) { + // if (isConfigElement(node)) { + // configElements.add(node); + // } + // } + // return configElements; + // } + + // public Set getAllEnabledFeatures(String feature) { + // Set enabledFeatures = new HashSet(); + // for (String node : getAllEnables(feature)) { + // if (!isConfigElement(node)) { + // enabledFeatures.add(node); + // } + // } + // return enabledFeatures; + // } +} \ No newline at end of file diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/FeatureListNode.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/FeatureListNode.java new file mode 100644 index 00000000..4c8e1ccb --- /dev/null +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/FeatureListNode.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package io.openliberty.tools.langserver.lemminx.data; + +import java.util.HashSet; +import java.util.Set; + + +// Class to represent a feature OR config element in a feature list xml +public class FeatureListNode { + protected String nodeName; + protected Set enabledBy; + protected Set enables; + + public FeatureListNode(String nodeName) { + enabledBy = new HashSet(); + enables = new HashSet(); + this.nodeName = nodeName; + } + + public void addEnabledBy(String nodeName) { + enabledBy.add(nodeName); + } + + public void addEnables(String nodeName) { + enables.add(nodeName); + } + + public Set getEnabledBy() { + return enabledBy; + } + + public Set getEnables() { + return enables; + } + + // based on a heuristic that features use major versions and config elements don't use '.' + public boolean isConfigElement() { + return this.nodeName.indexOf('.') == -1; + } +} \ No newline at end of file diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/models/feature/Feature.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/models/feature/Feature.java index 1b1d798e..13c3943c 100644 --- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/models/feature/Feature.java +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/models/feature/Feature.java @@ -16,6 +16,7 @@ import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlRootElement; +import java.util.List; @XmlRootElement(name = "feature") @XmlAccessorType(XmlAccessType.FIELD) @@ -33,6 +34,9 @@ public class Feature { private String version; WlpInformation wlpInformation; + private List configElement; + private List enables; + // Getter Methods public String getDescription() { @@ -67,6 +71,14 @@ public WlpInformation getWlpInformation() { return wlpInformation; } + public List getConfigElements() { + return configElement; + } + + public List getEnables() { + return enables; + } + // Setter Methods public void setDescription(String description) { @@ -100,4 +112,12 @@ public void setVersion(String version) { public void setWlpInformation(WlpInformation wlpInformation) { this.wlpInformation = wlpInformation; } + + public void setConfigElements(List configElement) { + this.configElement = configElement; + } + + public void setEnables(List enables) { + this.enables = enables; + } } diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/FeatureService.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/FeatureService.java index b3ea4af4..9090b041 100644 --- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/FeatureService.java +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/FeatureService.java @@ -41,7 +41,8 @@ import com.google.gson.Gson; import com.google.gson.JsonParseException; - +import io.openliberty.tools.langserver.lemminx.data.FeatureListGraph; +import io.openliberty.tools.langserver.lemminx.data.FeatureListNode; import io.openliberty.tools.langserver.lemminx.models.feature.Feature; import io.openliberty.tools.langserver.lemminx.models.feature.FeatureInfo; import io.openliberty.tools.langserver.lemminx.models.feature.WlpInformation; @@ -178,7 +179,7 @@ public List getFeatures(String libertyVersion, String libertyRuntime, i if (!libertyVersion.endsWith("-beta")) { try { // verify that request delay (seconds) has gone by since last fetch request - // Note that the default delay is 120 seconds and can cause us to generate a feature list instead of download from MC when + // Note that the default delay is 10 seconds and can cause us to generate a feature list instead of download from MC when // switching back and forth between projects. long currentTime = System.currentTimeMillis(); if (this.featureUpdateTime == -1 || currentTime >= (this.featureUpdateTime + (requestDelay * 1000))) { @@ -295,10 +296,8 @@ public List collectExistingFeatures(DOMNode featureManager, String curre * @param libertyVersion must not be null and should be a valid Liberty version (e.g. 23.0.0.6) * @return list of installed features, or empty list */ - private List getInstalledFeaturesList(String documentURI, String libertyRuntime, String libertyVersion) { + public List getInstalledFeaturesList(LibertyWorkspace libertyWorkspace, String libertyRuntime, String libertyVersion) { List installedFeatures = new ArrayList(); - - LibertyWorkspace libertyWorkspace = LibertyProjectsManager.getInstance().getWorkspaceFolder(documentURI); if (libertyWorkspace == null || libertyWorkspace.getWorkspaceString() == null) { return installedFeatures; } @@ -313,7 +312,6 @@ private List getInstalledFeaturesList(String documentURI, String libert try { // Need to handle both local installation and container File featureListFile = null; - if (libertyWorkspace.isLibertyInstalled()) { Path featureListJAR = LibertyUtils.findLibertyFileForWorkspace(libertyWorkspace, Paths.get("bin", "tools", "ws-featurelist.jar")); if (featureListJAR != null && featureListJAR.toFile().exists()) { @@ -341,6 +339,11 @@ private List getInstalledFeaturesList(String documentURI, String libert LOGGER.info("Returning installed features: " + installedFeatures.size()); return installedFeatures; } + + public List getInstalledFeaturesList(String documentURI, String libertyRuntime, String libertyVersion) { + LibertyWorkspace libertyWorkspace = LibertyProjectsManager.getInstance().getWorkspaceFolder(documentURI); + return getInstalledFeaturesList(libertyWorkspace, libertyRuntime, libertyVersion); + } /** * Generate the featurelist file for a LibertyWorkspace using the ws-featurelist.jar in the corresponding Liberty installation @@ -400,21 +403,41 @@ public List readFeaturesFromFeatureListFile(List installedFeat // Note: Only the public features are loaded when unmarshalling the passed featureListFile. if ((featureInfo.getFeatures() != null) && (featureInfo.getFeatures().size() > 0)) { - for (int i = 0; i < featureInfo.getFeatures().size(); i++) { - Feature f = featureInfo.getFeatures().get(i); + FeatureListGraph featureListGraph = new FeatureListGraph(); + for (Feature f : featureInfo.getFeatures()) { f.setShortDescription(f.getDescription()); // The xml featureListFile does not have a wlpInformation element like the json does, but our code depends on looking up // features by the shortName found in wlpInformation. So create a WlpInformation object and initialize the shortName to // the feature name. WlpInformation wlpInfo = new WlpInformation(f.getName()); f.setWlpInformation(wlpInfo); + + String currentFeature = f.getName(); + List enables = f.getEnables(); + List configElements = f.getConfigElements(); + FeatureListNode currentFeatureNode = featureListGraph.addFeature(currentFeature); + if (enables != null) { + for (String enabledFeature : enables) { + FeatureListNode feature = featureListGraph.addFeature(enabledFeature); + feature.addEnabledBy(currentFeature); + currentFeatureNode.addEnables(enabledFeature); + } + } + if (configElements != null) { + for (String configElement : configElements) { + FeatureListNode configNode = featureListGraph.addConfigElement(configElement); + configNode.addEnabledBy(currentFeature); + currentFeatureNode.addEnables(configElement); + } + } } installedFeatures = featureInfo.getFeatures(); + libertyWorkspace.setFeatureListGraph(featureListGraph); libertyWorkspace.setInstalledFeatureList(installedFeatures); } else { LOGGER.warning("Unable to get installed features for current Liberty workspace: " + libertyWorkspace.getWorkspaceString()); + libertyWorkspace.setFeatureListGraph(new FeatureListGraph()); } return installedFeatures; } - } diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/LibertyWorkspace.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/LibertyWorkspace.java index 4022061a..50d95dbf 100644 --- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/LibertyWorkspace.java +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/LibertyWorkspace.java @@ -26,9 +26,11 @@ import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Unmarshaller; - +import io.openliberty.tools.langserver.lemminx.data.FeatureListGraph; +import io.openliberty.tools.langserver.lemminx.data.LibertyRuntime; import io.openliberty.tools.langserver.lemminx.models.feature.Feature; import io.openliberty.tools.langserver.lemminx.models.settings.DevcMetadata; +import io.openliberty.tools.langserver.lemminx.util.LibertyUtils; public class LibertyWorkspace { @@ -43,6 +45,7 @@ public class LibertyWorkspace { private List installedFeatureList; private String libertyInstallationDir; private boolean isExternalLibertyInstallation; + private FeatureListGraph featureListGraph; // devc vars private String containerName; @@ -65,6 +68,7 @@ public LibertyWorkspace(String workspaceFolderURI) { this.installedFeatureList = new ArrayList(); this.containerName = null; this.containerAlive = false; + this.featureListGraph = new FeatureListGraph(); } public String getWorkspaceString() { @@ -206,4 +210,24 @@ public String toString() { return workspaceFolderURI; } + public void setFeatureListGraph(FeatureListGraph featureListGraph) { + this.featureListGraph = featureListGraph; + if (isLibertyInstalled) { + this.featureListGraph.setRuntime(libertyRuntime + "-" + libertyVersion); + } + } + + public FeatureListGraph getFeatureListGraph() { + String workspaceRuntime = libertyRuntime + "-" + libertyVersion; + boolean generateGraph = featureListGraph.isEmpty() || !featureListGraph.getRuntime().equals(workspaceRuntime); + if (this.isLibertyInstalled && generateGraph) { + LOGGER.info("Generating installed features list and storing to cache for workspace " + workspaceFolderURI); + FeatureService.getInstance().getInstalledFeaturesList(this, libertyRuntime, libertyVersion); + if (!this.featureListGraph.isEmpty()) { + LOGGER.info("Config element validation enabled for workspace: " + workspaceFolderURI); + } + } + return this.featureListGraph; + } + } diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/SettingsService.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/SettingsService.java index d65806df..a768155a 100644 --- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/SettingsService.java +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/services/SettingsService.java @@ -26,8 +26,8 @@ public static SettingsService getInstance() { return instance; } - // default request delay is 120 seconds - private static int DEFAULT_REQUEST_DELAY = 120; + // default request delay is 10 seconds + private static int DEFAULT_REQUEST_DELAY = 10; private SettingsService() { } diff --git a/lemminx-liberty/src/test/java/io/openliberty/CodeActionUtilitiesTest.java b/lemminx-liberty/src/test/java/io/openliberty/CodeActionUtilitiesTest.java new file mode 100644 index 00000000..88513389 --- /dev/null +++ b/lemminx-liberty/src/test/java/io/openliberty/CodeActionUtilitiesTest.java @@ -0,0 +1,33 @@ +package io.openliberty; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import io.openliberty.tools.langserver.lemminx.codeactions.IndentUtil; + +public class CodeActionUtilitiesTest { + + @Test + public void indentSpacesTest() { + String indent = " "; // four spaces + String sampleText = "\n\ttest"; + int column = 4; + String expectedText = System.lineSeparator() + " test"; + String formatedText = IndentUtil.formatText(sampleText, indent, column); + assertEquals(expectedText, formatedText, "Expected length of " + expectedText.length() + ". Found " + formatedText.length()); + + int column2 = 3; + String expectedText2 = System.lineSeparator() + " test"; + assertEquals(expectedText2, IndentUtil.formatText(sampleText, indent, column2), "Incorrect detection starting indent."); + } + + @Test + public void indentTabsTest() { + String indent = " "; + String sampleText = "\n\ttest"; + int column = 1; + String expectedText = System.lineSeparator() + indent + indent + "test"; + assertEquals(expectedText, IndentUtil.formatText(sampleText, indent, column), "Incorrect whitespace buffer calculation."); + } +} diff --git a/lemminx-liberty/src/test/java/io/openliberty/LibertyDiagnosticTest.java b/lemminx-liberty/src/test/java/io/openliberty/LibertyDiagnosticTest.java index 7cdadcbf..c827b903 100644 --- a/lemminx-liberty/src/test/java/io/openliberty/LibertyDiagnosticTest.java +++ b/lemminx-liberty/src/test/java/io/openliberty/LibertyDiagnosticTest.java @@ -6,10 +6,15 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.WorkspaceFolder; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import io.openliberty.tools.langserver.lemminx.LibertyDiagnosticParticipant; +import io.openliberty.tools.langserver.lemminx.models.feature.Feature; +import io.openliberty.tools.langserver.lemminx.services.FeatureService; import io.openliberty.tools.langserver.lemminx.services.LibertyProjectsManager; +import io.openliberty.tools.langserver.lemminx.services.LibertyWorkspace; +import jakarta.xml.bind.JAXBException; import static org.eclipse.lemminx.XMLAssert.r; import static org.eclipse.lemminx.XMLAssert.ca; @@ -21,13 +26,26 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Collection; import java.util.Collections; public class LibertyDiagnosticTest { static String newLine = System.lineSeparator(); - static String serverXMLURI = "test/server.xml"; + + static File srcResourcesDir = new File("src/test/resources"); + static File featureList = new File("src/test/resources/featurelist-ol-23.0.0.1-beta.xml"); + static String serverXMLURI = new File(srcResourcesDir, "test/server.xml").toURI().toString(); + static List initList = new ArrayList(); + LibertyProjectsManager libPM; + LibertyWorkspace libWorkspace; + + @BeforeEach + public void setupWorkspace() { + initList.add(new WorkspaceFolder(srcResourcesDir.toURI().toString())); + libPM = LibertyProjectsManager.getInstance(); + libPM.setWorkspaceFolders(initList); + libWorkspace = libPM.getLibertyWorkspaceFolders().iterator().next(); + } @Test public void testFeatureDuplicateDiagnostic() { @@ -175,4 +193,71 @@ public void testDiagnosticsForInclude() throws IOException { XMLAssert.testDiagnosticsFor(serverXML, null, null, serverXMLFile.toURI().toString(), not_xml, multi_liner, not_optional, missing_xml, optional_not_defined, missing_xml2); } + + @Test + public void testConfigElementMissingFeatureManager() throws JAXBException { + assertTrue(featureList.exists()); + FeatureService.getInstance().readFeaturesFromFeatureListFile(new ArrayList(), libWorkspace, featureList); + + String serverXml = ""; + Diagnostic config_for_missing_feature = new Diagnostic(); + config_for_missing_feature.setRange(r(0, serverXml.indexOf("".length())); + config_for_missing_feature.setCode(LibertyDiagnosticParticipant.MISSING_CONFIGURED_FEATURE_CODE); + config_for_missing_feature.setMessage(LibertyDiagnosticParticipant.MISSING_CONFIGURED_FEATURE_MESSAGE); + + XMLAssert.testDiagnosticsFor(serverXml, null, null, serverXMLURI, config_for_missing_feature); + } + + @Test + public void testConfigElementDirect() throws JAXBException { + assertTrue(featureList.exists()); + FeatureService.getInstance().readFeaturesFromFeatureListFile(new ArrayList(), libWorkspace, featureList); + + String correctFeature = " ssl-1.0"; + String incorrectFeature = " jaxrs-2.0"; + String configElement = " "; + int diagnosticStart = configElement.indexOf("<"); + int diagnosticLength = configElement.trim().length(); + + String serverXML1 = String.join(newLine, + "", + " ", + correctFeature, + " ", + configElement, + "" + ); + XMLAssert.testDiagnosticsFor(serverXML1, null, null, serverXMLURI); + + String serverXML2 = String.join(newLine, + "", + " ", + incorrectFeature, + " ", + configElement, + "" + ); + + Diagnostic config_for_missing_feature = new Diagnostic(); + config_for_missing_feature.setRange(r(4, diagnosticStart, 4, diagnosticStart + diagnosticLength)); + config_for_missing_feature.setCode(LibertyDiagnosticParticipant.MISSING_CONFIGURED_FEATURE_CODE); + config_for_missing_feature.setMessage(LibertyDiagnosticParticipant.MISSING_CONFIGURED_FEATURE_MESSAGE); + + XMLAssert.testDiagnosticsFor(serverXML2, null, null, serverXMLURI, config_for_missing_feature); + } + + @Test + public void testConfigElementTransitive() throws JAXBException { + assertTrue(featureList.exists()); + FeatureService.getInstance().readFeaturesFromFeatureListFile(new ArrayList(), libWorkspace, featureList); + String serverXML1 = String.join(newLine, + "", + " ", + " microProfile-5.0", + " ", + " ", + "" + ); + XMLAssert.testDiagnosticsFor(serverXML1, null, null, serverXMLURI); + } } \ No newline at end of file diff --git a/lemminx-liberty/src/test/java/io/openliberty/LibertyFeatureTest.java b/lemminx-liberty/src/test/java/io/openliberty/LibertyFeatureTest.java index 74264808..c3971b37 100644 --- a/lemminx-liberty/src/test/java/io/openliberty/LibertyFeatureTest.java +++ b/lemminx-liberty/src/test/java/io/openliberty/LibertyFeatureTest.java @@ -1,5 +1,6 @@ package io.openliberty; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -11,6 +12,7 @@ import org.eclipse.lsp4j.WorkspaceFolder; import org.junit.jupiter.api.Test; +import io.openliberty.tools.langserver.lemminx.data.FeatureListGraph; import io.openliberty.tools.langserver.lemminx.models.feature.Feature; import io.openliberty.tools.langserver.lemminx.services.FeatureService; import io.openliberty.tools.langserver.lemminx.services.LibertyProjectsManager; @@ -41,5 +43,17 @@ public void getInstalledFeaturesListTest() throws JAXBException { assertTrue(installedFeatures.equals(libWorkspace.getInstalledFeatureList())); // Check that list contains a beta feature assertTrue(installedFeatures.removeIf(f -> (f.getName().equals("cdi-4.0")))); + + // Check if config map gets built + FeatureListGraph fg = libWorkspace.getFeatureListGraph(); + assertEquals(76, fg.getAllEnabledBy("ssl-1.0").size()); + assertEquals(1, fg.get("ssl").getEnabledBy().size()); + assertTrue(fg.get("ssl").getEnabledBy().contains("ssl-1.0")); + assertEquals(77, fg.getAllEnabledBy("ssl").size()); + assertEquals(235, fg.getAllEnabledBy("library").size()); + assertTrue(fg.getAllEnabledBy("ltpa").contains("adminCenter-1.0")); // direct enabler + assertTrue(fg.getAllEnabledBy("ssl").contains("microProfile-5.0")); // transitive enabler + assertTrue(fg.getAllEnables("microProfile-5.0").contains("ssl")); + assertTrue(fg.getAllEnables("jakartaee-8.0").contains("classloading")); } } diff --git a/lemminx-liberty/src/test/java/io/openliberty/XmlReaderTest.java b/lemminx-liberty/src/test/java/io/openliberty/XmlReaderTest.java index 011e5dec..05c81f8b 100644 --- a/lemminx-liberty/src/test/java/io/openliberty/XmlReaderTest.java +++ b/lemminx-liberty/src/test/java/io/openliberty/XmlReaderTest.java @@ -21,26 +21,26 @@ public class XmlReaderTest { @Test public void readEmptyXml() throws IOException { File emptyXml = new File(resourcesDir, "empty_server.xml"); - assertFalse(XmlReader.hasServerRoot(emptyXml.getCanonicalPath())); - assertFalse(LibertyUtils.isServerXMLFile(emptyXml.getCanonicalPath())); - assertFalse(LibertyUtils.isConfigXMLFile(emptyXml.getCanonicalPath())); + assertFalse(XmlReader.hasServerRoot(emptyXml.toURI().toString())); + assertFalse(LibertyUtils.isServerXMLFile(emptyXml.toURI().toString())); + assertFalse(LibertyUtils.isConfigXMLFile(emptyXml.toURI().toString())); } @Test public void readServerXml() throws IOException { File sampleServerXml = new File(resourcesDir, "sample/custom_server.xml"); - assertTrue(XmlReader.hasServerRoot(sampleServerXml.getCanonicalPath())); - assertFalse(LibertyUtils.isServerXMLFile(sampleServerXml.getCanonicalPath())); - assertFalse(LibertyUtils.isConfigDirFile(sampleServerXml.getCanonicalPath())); - assertTrue(LibertyUtils.isConfigXMLFile(sampleServerXml.getCanonicalPath())); + assertTrue(XmlReader.hasServerRoot(sampleServerXml.toURI().toString())); + assertFalse(LibertyUtils.isServerXMLFile(sampleServerXml.toURI().toString())); + assertFalse(LibertyUtils.isConfigDirFile(sampleServerXml.toURI().toString())); + assertTrue(LibertyUtils.isConfigXMLFile(sampleServerXml.toURI().toString())); } @Test public void readLibertyPluginConfigXml() throws IOException { File lpcXml = new File(resourcesDir, "sample/liberty-plugin-config.xml"); Path lpcXmlPath = lpcXml.toPath(); - assertFalse(XmlReader.hasServerRoot(lpcXml.getCanonicalPath())); - assertFalse(LibertyUtils.isConfigXMLFile(lpcXml.getCanonicalPath())); + assertFalse(XmlReader.hasServerRoot(lpcXml.toURI().toString())); + assertFalse(LibertyUtils.isConfigXMLFile(lpcXml.toURI().toString())); Set elementNames = new HashSet (); elementNames.add("configFile"); diff --git a/liberty-ls/pom.xml b/liberty-ls/pom.xml index 4ab1bf44..31e31b44 100644 --- a/liberty-ls/pom.xml +++ b/liberty-ls/pom.xml @@ -5,7 +5,7 @@ io.openliberty.tools liberty-langserver - 2.0.2-SNAPSHOT + 2.1-SNAPSHOT liberty.langserver https://openliberty.io/