From ab4ffb29931b1ebe377f3e795bf0b5352064a406 Mon Sep 17 00:00:00 2001 From: Cheryl King Date: Mon, 22 Jan 2024 17:41:43 -0600 Subject: [PATCH 1/2] Add info to hover for feature --- .../lemminx/LibertyHoverParticipant.java | 66 ++++++++++++++++++- .../lemminx/data/FeatureListGraph.java | 50 ++++++++++++-- .../lemminx/data/FeatureListNode.java | 28 +++++++- .../lemminx/services/FeatureService.java | 4 +- .../java/io/openliberty/LibertyHoverTest.java | 20 ++++-- 5 files changed, 152 insertions(+), 16 deletions(-) diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java index 290ee9e7..cc9c04af 100644 --- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java @@ -20,15 +20,24 @@ import org.eclipse.lsp4j.MarkupContent; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import io.openliberty.tools.langserver.lemminx.data.FeatureListGraph; +import io.openliberty.tools.langserver.lemminx.data.FeatureListNode; import io.openliberty.tools.langserver.lemminx.data.LibertyRuntime; import io.openliberty.tools.langserver.lemminx.models.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 io.openliberty.tools.langserver.lemminx.services.SettingsService; import io.openliberty.tools.langserver.lemminx.util.*; +import java.util.ArrayList; +import java.util.Collections; import java.util.Optional; +import java.util.Set; +import java.util.logging.Logger; public class LibertyHoverParticipant implements IHoverParticipant { + private static final Logger LOGGER = Logger.getLogger(LibertyHoverParticipant.class.getName()); @Override public Hover onAttributeName(IHoverRequest request, CancelChecker cancelChecker) { @@ -63,7 +72,7 @@ public Hover onText(IHoverRequest request, CancelChecker cancelChecker) { return null; } - private Hover getHoverFeatureDescription(String featureName, DOMDocument domDocument) { + private Hover getFeatureDescription(String featureName, DOMDocument domDocument) { LibertyRuntime runtimeInfo = LibertyUtils.getLibertyRuntimeInfo(domDocument); String libertyVersion = runtimeInfo == null ? null : runtimeInfo.getRuntimeVersion(); String libertyRuntime = runtimeInfo == null ? null : runtimeInfo.getRuntimeType(); @@ -76,4 +85,59 @@ private Hover getHoverFeatureDescription(String featureName, DOMDocument domDocu return null; } + + private Hover getHoverFeatureDescription(String featureName, DOMDocument document) { + LibertyWorkspace ws = LibertyProjectsManager.getInstance().getWorkspaceFolder(document.getDocumentURI()); + FeatureListGraph featureGraph = null; + if (ws == null) { + LOGGER.warning("Could not get workspace for: "+document.getDocumentURI() + ". Using cached feature list for hover."); + featureGraph = FeatureService.getInstance().getDefaultFeatureList(); + } else { + featureGraph = ws.getFeatureListGraph(); + } + + FeatureListNode flNode = featureGraph.get(featureName); + if (flNode == null) { + LOGGER.warning("Could not get full description for feature: "+featureName+" from cached feature list."); + return getFeatureDescription(featureName, document); + } + + StringBuilder sb = new StringBuilder(); + String description = flNode.getDescription(); + sb.append("Description: "); + sb.append(description); + sb.append(System.lineSeparator()); + + // getAllEnabledBy would return all transitive features but typically offers too much + Set featureEnabledBy = flNode.getEnabledBy(); + if (!featureEnabledBy.isEmpty()) { + sb.append("Enabled by: "); + // Need to sort the collection of features so that they are in a reliable order for tests. + ArrayList sortedFeatures = new ArrayList(); + sortedFeatures.addAll(featureEnabledBy); + Collections.sort(sortedFeatures); + for (String nextFeature : sortedFeatures) { + sb.append(nextFeature); + sb.append(", "); + } + sb.setLength(sb.length() - 2); + sb.append(System.lineSeparator()); + } + + Set featureEnables = flNode.getEnablesFeatures(); + if (!featureEnables.isEmpty()) { + sb.append("Enables: "); + // Need to sort the collection of features so that they are in a reliable order for tests. + ArrayList sortedFeatures = new ArrayList(); + sortedFeatures.addAll(featureEnables); + Collections.sort(sortedFeatures); + for (String nextFeature : sortedFeatures) { + sb.append(nextFeature); + sb.append(", "); + } + sb.setLength(sb.length() - 2); + } + + return new Hover(new MarkupContent("plaintext", sb.toString())); + } } \ No newline at end of file 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 index cb69b25c..bd53013c 100644 --- 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 @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2023 IBM Corporation and others. +* Copyright (c) 2023, 2024 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 @@ -22,11 +22,13 @@ public class FeatureListGraph { private String runtime = ""; private Map nodes; - private Map> enabledByCache; // storing in lower case to enable diagnostics with configured features + private Map> enabledByCache; + private Map> enabledByCacheLowerCase; // storing in lower case to enable diagnostics with configured features private Map> enablesCache; public FeatureListGraph() { nodes = new HashMap(); + enabledByCacheLowerCase = new HashMap>(); enabledByCache = new HashMap>(); enablesCache = new HashMap>(); } @@ -40,6 +42,19 @@ public FeatureListNode addFeature(String nodeName) { return node; } + public FeatureListNode addFeature(String nodeName, String description) { + if (nodes.containsKey(nodeName)) { + FeatureListNode node = nodes.get(nodeName); + if (node.getDescription().isEmpty()) { + node.setDescription(description); + } + return node; + } + FeatureListNode node = new FeatureListNode(nodeName, description); + nodes.put(nodeName, node); + return node; + } + public FeatureListNode addConfigElement(String nodeName) { if (nodes.containsKey(nodeName)) { return nodes.get(nodeName); @@ -79,9 +94,26 @@ public String getRuntime() { * @return */ public Set getAllEnabledBy(String elementName) { + return getAllEnabledBy(elementName, true); + } + + /** + * Returns a superset of 'owning' features that enable a given config element or feature. + * The features are returned in lower case if the 'lowerCase' boolean is true. Otherwise, + * the features are returned in their original case. + * @param elementName + * @return + */ + public Set getAllEnabledBy(String elementName, boolean lowerCase) { + + if (lowerCase && enabledByCacheLowerCase.containsKey(elementName)) { + return enabledByCacheLowerCase.get(elementName); + } + if (enabledByCache.containsKey(elementName)) { return enabledByCache.get(elementName); } + if (!nodes.containsKey(elementName)) { return null; } @@ -100,16 +132,22 @@ public Set getAllEnabledBy(String elementName) { allEnabledBy.addAll(enablers); queue.addAll(enablers); } - return addToEnabledByCacheInLowerCase(elementName, allEnabledBy); + return addToEnabledByCache(elementName, allEnabledBy, lowerCase); } - private Set addToEnabledByCacheInLowerCase(String configElement, Set allEnabledBy) { + private Set addToEnabledByCache(String configElement, Set allEnabledBy, boolean lowerCase) { Set lowercaseEnabledBy = new HashSet(); + Set originalcaseEnabledBy = new HashSet(); + originalcaseEnabledBy.addAll(allEnabledBy); + for (String nextFeature: allEnabledBy) { lowercaseEnabledBy.add(nextFeature.toLowerCase()); } - enabledByCache.put(configElement, lowercaseEnabledBy); - return lowercaseEnabledBy; + + enabledByCacheLowerCase.put(configElement, lowercaseEnabledBy); + enabledByCache.put(configElement, originalcaseEnabledBy); + + return lowerCase ? lowercaseEnabledBy : originalcaseEnabledBy; } /** 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 index 4c8e1ccb..fd554811 100644 --- 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 @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2023 IBM Corporation and others. +* Copyright (c) 2023, 2024 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 @@ -19,6 +19,7 @@ // Class to represent a feature OR config element in a feature list xml public class FeatureListNode { protected String nodeName; + protected String description; // only used for features protected Set enabledBy; protected Set enables; @@ -28,6 +29,21 @@ public FeatureListNode(String nodeName) { this.nodeName = nodeName; } + public FeatureListNode(String nodeName, String description) { + enabledBy = new HashSet(); + enables = new HashSet(); + this.nodeName = nodeName; + this.description = description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getDescription() { + return this.description == null ? "" : this.description; + } + public void addEnabledBy(String nodeName) { enabledBy.add(nodeName); } @@ -44,6 +60,16 @@ public Set getEnables() { return enables; } + public Set getEnablesFeatures() { + Set enablesFeatures = new HashSet(); + for (String next: enables) { + if (next.contains("-")) { + enablesFeatures.add(next); + } + } + return enablesFeatures; + } + // based on a heuristic that features use major versions and config elements don't use '.' public boolean isConfigElement() { return this.nodeName.indexOf('.') == -1; 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 f79a2437..469111d1 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 @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation and others. +* Copyright (c) 2020, 2024 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 @@ -494,7 +494,7 @@ public List readFeaturesFromFeatureListFile(List installedFeat String currentFeature = f.getName(); List enables = f.getEnables(); List configElements = f.getConfigElements(); - FeatureListNode currentFeatureNode = featureListGraph.addFeature(currentFeature); + FeatureListNode currentFeatureNode = featureListGraph.addFeature(currentFeature, f.getDescription()); if (enables != null) { for (String enabledFeature : enables) { FeatureListNode feature = featureListGraph.addFeature(enabledFeature); diff --git a/lemminx-liberty/src/test/java/io/openliberty/LibertyHoverTest.java b/lemminx-liberty/src/test/java/io/openliberty/LibertyHoverTest.java index db727c92..326d4408 100644 --- a/lemminx-liberty/src/test/java/io/openliberty/LibertyHoverTest.java +++ b/lemminx-liberty/src/test/java/io/openliberty/LibertyHoverTest.java @@ -26,10 +26,14 @@ public void testFeatureHover() throws BadLocationException { ); XMLAssert.assertHover(serverXML, serverXMLURI, - "This feature enables support for Java API for RESTful Web Services v2.1. " - + "JAX-RS annotations can be used to define web service clients and endpoints that comply with the REST architectural style. " - + "Endpoints are accessed through a common interface that is based on the HTTP standard methods.", - r(2, 24, 2, 33)); + "Description: This feature enables support for Java API for RESTful Web Services v2.1. " + + "JAX-RS annotations can be used to define web service clients and endpoints that comply with the REST architectural style. " + + "Endpoints are accessed through a common interface that is based on the HTTP standard methods." + + System.lineSeparator() + + "Enabled by: microProfile-2.0, microProfile-2.1, microProfile-2.2, microProfile-3.0, microProfile-3.2, microProfile-3.3, microProfile-4.0, microProfile-4.1, mpOpenAPI-2.0, opentracing-1.3, opentracing-2.0, webProfile-8.0" + + System.lineSeparator() + + "Enables: jaxrsClient-2.1, servlet-4.0", + r(2, 24, 2, 33)); } @@ -45,9 +49,13 @@ public void testFeatureHoverTrim() throws BadLocationException { ); XMLAssert.assertHover(serverXML, serverXMLURI, - "This feature enables support for Java API for RESTful Web Services v2.1. " + "Description: This feature enables support for Java API for RESTful Web Services v2.1. " + "JAX-RS annotations can be used to define web service clients and endpoints that comply with the REST architectural style. " - + "Endpoints are accessed through a common interface that is based on the HTTP standard methods.", + + "Endpoints are accessed through a common interface that is based on the HTTP standard methods." + + System.lineSeparator() + + "Enabled by: microProfile-2.0, microProfile-2.1, microProfile-2.2, microProfile-3.0, microProfile-3.2, microProfile-3.3, microProfile-4.0, microProfile-4.1, mpOpenAPI-2.0, opentracing-1.3, opentracing-2.0, webProfile-8.0" + + System.lineSeparator() + + "Enables: jaxrsClient-2.1, servlet-4.0", r(2, 24, 2, 34)); } From d4c4721adae385312097511c639a537e4136ef07 Mon Sep 17 00:00:00 2001 From: Cheryl King Date: Tue, 23 Jan 2024 13:08:53 -0600 Subject: [PATCH 2/2] Refactor FeatureListGraph --- .../lemminx/LibertyHoverParticipant.java | 24 ++-- .../lemminx/codeactions/AddFeature.java | 11 +- .../lemminx/data/ConfigElementNode.java | 21 ++++ .../lemminx/data/FeatureListGraph.java | 107 +++++------------- .../lemminx/data/FeatureListNode.java | 48 +++----- .../tools/langserver/lemminx/data/Node.java | 34 ++++++ .../lemminx/services/FeatureService.java | 7 +- .../io/openliberty/LibertyFeatureTest.java | 6 +- 8 files changed, 122 insertions(+), 136 deletions(-) create mode 100644 lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/ConfigElementNode.java create mode 100644 lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/Node.java diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java index cc9c04af..7554cea1 100644 --- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation and others. +* Copyright (c) 2020, 2024 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 @@ -25,8 +25,6 @@ import io.openliberty.tools.langserver.lemminx.data.LibertyRuntime; import io.openliberty.tools.langserver.lemminx.models.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 io.openliberty.tools.langserver.lemminx.services.SettingsService; import io.openliberty.tools.langserver.lemminx.util.*; @@ -87,18 +85,13 @@ private Hover getFeatureDescription(String featureName, DOMDocument domDocument) } private Hover getHoverFeatureDescription(String featureName, DOMDocument document) { - LibertyWorkspace ws = LibertyProjectsManager.getInstance().getWorkspaceFolder(document.getDocumentURI()); - FeatureListGraph featureGraph = null; - if (ws == null) { - LOGGER.warning("Could not get workspace for: "+document.getDocumentURI() + ". Using cached feature list for hover."); - featureGraph = FeatureService.getInstance().getDefaultFeatureList(); - } else { - featureGraph = ws.getFeatureListGraph(); - } - - FeatureListNode flNode = featureGraph.get(featureName); + // Choosing to use the default feature list to get the full enables/enabled by information to display, as quite often the generated + // feature list will only be a subset of the default one. If the feature is not found in the default feature list, this code will + // default to the original description only which is available from the downloaded features.json file. + FeatureListGraph featureGraph = FeatureService.getInstance().getDefaultFeatureList(); + FeatureListNode flNode = featureGraph.getFeatureListNode(featureName); if (flNode == null) { - LOGGER.warning("Could not get full description for feature: "+featureName+" from cached feature list."); + LOGGER.warning("Could not get full description for feature: "+featureName+" from cached feature list. Using description from features.json file."); return getFeatureDescription(featureName, document); } @@ -108,7 +101,7 @@ private Hover getHoverFeatureDescription(String featureName, DOMDocument documen sb.append(description); sb.append(System.lineSeparator()); - // getAllEnabledBy would return all transitive features but typically offers too much + // get features that directly enable this feature Set featureEnabledBy = flNode.getEnabledBy(); if (!featureEnabledBy.isEmpty()) { sb.append("Enabled by: "); @@ -124,6 +117,7 @@ private Hover getHoverFeatureDescription(String featureName, DOMDocument documen sb.append(System.lineSeparator()); } + // get features that this feature directly enables Set featureEnables = flNode.getEnablesFeatures(); if (!featureEnables.isEmpty()) { sb.append("Enables: "); 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 index 0266dfe5..d401e083 100644 --- 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 @@ -32,8 +32,8 @@ import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import io.openliberty.tools.langserver.lemminx.data.ConfigElementNode; import io.openliberty.tools.langserver.lemminx.data.FeatureListGraph; -import io.openliberty.tools.langserver.lemminx.data.FeatureListNode; import io.openliberty.tools.langserver.lemminx.services.FeatureService; import io.openliberty.tools.langserver.lemminx.services.LibertyProjectsManager; import io.openliberty.tools.langserver.lemminx.services.LibertyWorkspace; @@ -73,14 +73,15 @@ public void doCodeAction(ICodeActionRequest request, List codeAction featureGraph = ws.getFeatureListGraph(); } - FeatureListNode flNode = featureGraph.get(diagnostic.getSource()); - if (flNode == null) { + ConfigElementNode configElement = featureGraph.getConfigElementNode(diagnostic.getSource()); + if (configElement == null) { LOGGER.warning("Could not add quick fix for missing feature for config element due to missing information in the feature list: "+diagnostic.getSource()); return; } - // getAllEnabledBy would return all transitive features but typically offers too much - Set featureCandidates = flNode.getEnabledBy(); + // This is getting features that directly enable the config element and not all transitive features + // as that would typically be too much to display/offer in a quick fix. + Set featureCandidates = configElement.getEnabledBy(); if (featureCandidates.isEmpty()) { return; } diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/ConfigElementNode.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/ConfigElementNode.java new file mode 100644 index 00000000..8210be8c --- /dev/null +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/ConfigElementNode.java @@ -0,0 +1,21 @@ +/******************************************************************************* +* Copyright (c) 2024 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; + +// Class to represent a config element in a feature list xml +public class ConfigElementNode extends Node { + + public ConfigElementNode(String nodeName) { + super(nodeName); + } +} \ No newline at end of file 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 index bd53013c..f278ffd4 100644 --- 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 @@ -21,62 +21,68 @@ public class FeatureListGraph { private String runtime = ""; - private Map nodes; + private Map featureNodes; + private Map configElementNodes; + private Map nodes; private Map> enabledByCache; private Map> enabledByCacheLowerCase; // storing in lower case to enable diagnostics with configured features - private Map> enablesCache; - + public FeatureListGraph() { - nodes = new HashMap(); + nodes = new HashMap(); + featureNodes = new HashMap(); + configElementNodes = new HashMap(); enabledByCacheLowerCase = new HashMap>(); enabledByCache = new HashMap>(); - enablesCache = new HashMap>(); } public FeatureListNode addFeature(String nodeName) { - if (nodes.containsKey(nodeName)) { - return nodes.get(nodeName); + if (featureNodes.containsKey(nodeName)) { + return featureNodes.get(nodeName); } FeatureListNode node = new FeatureListNode(nodeName); + featureNodes.put(nodeName, node); nodes.put(nodeName, node); return node; } public FeatureListNode addFeature(String nodeName, String description) { - if (nodes.containsKey(nodeName)) { - FeatureListNode node = nodes.get(nodeName); + if (featureNodes.containsKey(nodeName)) { + FeatureListNode node = featureNodes.get(nodeName); if (node.getDescription().isEmpty()) { node.setDescription(description); } return node; } FeatureListNode node = new FeatureListNode(nodeName, description); + featureNodes.put(nodeName, node); nodes.put(nodeName, node); return node; } - public FeatureListNode addConfigElement(String nodeName) { - if (nodes.containsKey(nodeName)) { - return nodes.get(nodeName); + public ConfigElementNode addConfigElement(String nodeName) { + if (configElementNodes.containsKey(nodeName)) { + return configElementNodes.get(nodeName); } - FeatureListNode node = new FeatureListNode(nodeName); + ConfigElementNode node = new ConfigElementNode(nodeName); + configElementNodes.put(nodeName, node); nodes.put(nodeName, node); return node; } - public FeatureListNode get(String nodeName) { - return nodes.get(nodeName); + public FeatureListNode getFeatureListNode(String nodeName) { + return featureNodes.get(nodeName); + } + + public ConfigElementNode getConfigElementNode(String nodeName) { + return configElementNodes.get(nodeName); } public boolean isEmpty() { - return nodes.isEmpty(); + return configElementNodes.isEmpty() && featureNodes.isEmpty(); } - public boolean isConfigElement(String featureListNode) { - if (!nodes.containsKey(featureListNode)) { - return false; - } - return nodes.get(featureListNode).isConfigElement(); + public boolean isConfigElement(String name) { + return configElementNodes.containsKey(name); } public void setRuntime(String runtime) { @@ -113,12 +119,13 @@ public Set getAllEnabledBy(String elementName, boolean lowerCase) { 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()); + Set allEnabledBy = new HashSet(nodes.get(elementName).getEnabledBy());; Deque queue = new ArrayDeque(allEnabledBy); Set visited = new HashSet(); while (!queue.isEmpty()) { @@ -149,57 +156,5 @@ private Set addToEnabledByCache(String configElement, Set allEna return lowerCase ? lowercaseEnabledBy : originalcaseEnabledBy; } - - /** - * 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 index fd554811..8315ee0b 100644 --- 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 @@ -16,23 +16,20 @@ import java.util.Set; -// Class to represent a feature OR config element in a feature list xml -public class FeatureListNode { - protected String nodeName; - protected String description; // only used for features - protected Set enabledBy; - protected Set enables; +// Class to represent a feature in a feature list xml +public class FeatureListNode extends Node { + protected String description; + protected Set enablesFeatures; + protected Set enablesConfigElements; public FeatureListNode(String nodeName) { - enabledBy = new HashSet(); - enables = new HashSet(); - this.nodeName = nodeName; + super(nodeName); + enablesFeatures = new HashSet(); + enablesConfigElements = new HashSet(); } public FeatureListNode(String nodeName, String description) { - enabledBy = new HashSet(); - enables = new HashSet(); - this.nodeName = nodeName; + this(nodeName); this.description = description; } @@ -43,35 +40,20 @@ public void setDescription(String description) { public String getDescription() { return this.description == null ? "" : this.description; } - - public void addEnabledBy(String nodeName) { - enabledBy.add(nodeName); - } - - public void addEnables(String nodeName) { - enables.add(nodeName); - } - public Set getEnabledBy() { - return enabledBy; + public void addEnablesFeature(String nodeName) { + enablesFeatures.add(nodeName); } - public Set getEnables() { - return enables; + public void addEnablesConfigElement(String nodeName) { + enablesConfigElements.add(nodeName); } public Set getEnablesFeatures() { - Set enablesFeatures = new HashSet(); - for (String next: enables) { - if (next.contains("-")) { - enablesFeatures.add(next); - } - } return enablesFeatures; } - // based on a heuristic that features use major versions and config elements don't use '.' - public boolean isConfigElement() { - return this.nodeName.indexOf('.') == -1; + public Set getEnablesConfigElements() { + return enablesConfigElements; } } \ No newline at end of file diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/Node.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/Node.java new file mode 100644 index 00000000..62394cfa --- /dev/null +++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/data/Node.java @@ -0,0 +1,34 @@ +/******************************************************************************* +* Copyright (c) 2024 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; + +public class Node { + protected String nodeName; + protected Set enabledBy; + + public Node(String nodeName) { + enabledBy = new HashSet(); + this.nodeName = nodeName; + } + + public void addEnabledBy(String nodeName) { + enabledBy.add(nodeName); + } + + public Set getEnabledBy() { + return enabledBy; + } +} \ No newline at end of file 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 469111d1..943215e4 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 @@ -43,6 +43,7 @@ import com.google.gson.Gson; import com.google.gson.JsonParseException; +import io.openliberty.tools.langserver.lemminx.data.ConfigElementNode; 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; @@ -499,14 +500,14 @@ public List readFeaturesFromFeatureListFile(List installedFeat for (String enabledFeature : enables) { FeatureListNode feature = featureListGraph.addFeature(enabledFeature); feature.addEnabledBy(currentFeature); - currentFeatureNode.addEnables(enabledFeature); + currentFeatureNode.addEnablesFeature(enabledFeature); } } if (configElements != null) { for (String configElement : configElements) { - FeatureListNode configNode = featureListGraph.addConfigElement(configElement); + ConfigElementNode configNode = featureListGraph.addConfigElement(configElement); configNode.addEnabledBy(currentFeature); - currentFeatureNode.addEnables(configElement); + currentFeatureNode.addEnablesConfigElement(configElement); } } } diff --git a/lemminx-liberty/src/test/java/io/openliberty/LibertyFeatureTest.java b/lemminx-liberty/src/test/java/io/openliberty/LibertyFeatureTest.java index 86eb3a34..ec0349de 100644 --- a/lemminx-liberty/src/test/java/io/openliberty/LibertyFeatureTest.java +++ b/lemminx-liberty/src/test/java/io/openliberty/LibertyFeatureTest.java @@ -48,13 +48,11 @@ public void getInstalledFeaturesListTest() throws JAXBException { // 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(1, fg.getConfigElementNode("ssl").getEnabledBy().size()); + assertTrue(fg.getConfigElementNode("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")); } }