Skip to content

Commit

Permalink
fixing feature uniqueness diagnostic issue for renamed features (#305)
Browse files Browse the repository at this point in the history
* fixing feature uniqueness diagnostic issue for renamed features

Signed-off-by: Arun Venmany <Arun.Kumar.V.N@ibm.com>

* fixing feature uniqueness diagnostic issue for renamed features- incroparating review comments

Signed-off-by: Arun Venmany <Arun.Kumar.V.N@ibm.com>

* fixing diagnostic message case issues

Signed-off-by: Arun Venmany <Arun.Kumar.V.N@ibm.com>

* changing log location as per review comments

---------

Signed-off-by: Arun Venmany <Arun.Kumar.V.N@ibm.com>
  • Loading branch information
arunvenmany-ibm authored Oct 7, 2024
1 parent cb95f9f commit c8dc268
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static io.openliberty.tools.langserver.lemminx.util.LibertyConstants.changedFeatureNameDiagMessage;

public class LibertyDiagnosticParticipant implements IDiagnosticsParticipant {
private static final Logger LOGGER = Logger.getLogger(LibertyDiagnosticParticipant.class.getName());

Expand Down Expand Up @@ -117,6 +121,7 @@ private void validateFeaturesAndPlatforms(DOMDocument domDocument, List<Diagnost
Set<String> versionedFeatures = new HashSet<String>();
Set<String> preferredPlatforms = new HashSet<String>();
Set<String> preferredPlatformsWithoutVersion = new HashSet<String>();
Set<String> featureList = new HashSet<String>();
// Search for duplicate features
// or features that do not exist
List<DOMNode> features = featureManager.getChildren();
Expand All @@ -126,13 +131,13 @@ private void validateFeaturesAndPlatforms(DOMDocument domDocument, List<Diagnost
if (LibertyConstants.PLATFORM_ELEMENT.equals(featureNode.getLocalName())) {
validatePlatform(domDocument, list, featureTextNode, preferredPlatformsWithoutVersion, preferredPlatforms);
} else {
validateFeature(domDocument, list, includedFeatures, featureTextNode, libertyVersion, libertyRuntime, requestDelay, versionedFeatures, versionlessFeatures, featuresWithoutVersions);
validateFeature(domDocument, list, includedFeatures, featureTextNode, libertyVersion, libertyRuntime, requestDelay, versionedFeatures, versionlessFeatures, featuresWithoutVersions, featureList);
}
}
checkForPlatFormAndFeature(domDocument, list, versionlessFeatures, features, preferredPlatforms, versionedFeatures);
}

private void validateFeature(DOMDocument domDocument, List<Diagnostic> list, Set<String> includedFeatures, DOMNode featureTextNode, String libertyVersion, String libertyRuntime, int requestDelay, Set<String> versionedFeatures, Set<String> versionlessFeatures, Set<String> featuresWithoutVersions) {
private void validateFeature(DOMDocument domDocument, List<Diagnostic> list, Set<String> includedFeatures, DOMNode featureTextNode, String libertyVersion, String libertyRuntime, int requestDelay, Set<String> versionedFeatures, Set<String> versionlessFeatures, Set<String> featuresWithoutVersions, Set<String> featureList) {
// skip nodes that do not have any text value (ie. comments)
if (featureTextNode != null && featureTextNode.getTextContent() != null) {
String featureName = featureTextNode.getTextContent().trim();
Expand All @@ -144,14 +149,15 @@ private void validateFeature(DOMDocument domDocument, List<Diagnostic> list, Set
String message = "ERROR: The feature \"" + featureName + "\" does not exist.";
list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE, INCORRECT_FEATURE_CODE));
} else {
checkForFeatureUniqueness(domDocument, list, includedFeatures, featureTextNode, versionedFeatures, versionlessFeatures, featuresWithoutVersions, featureName);
checkForFeatureUniqueness(domDocument, list, includedFeatures, featureTextNode, versionedFeatures, versionlessFeatures, featuresWithoutVersions, featureName, featureList);
}
}
}

/**
* check whether feature name is unique
* throw error if another version of same feature exists as well
*
* @param domDocument
* @param list
* @param includedFeatures
Expand All @@ -160,17 +166,35 @@ private void validateFeature(DOMDocument domDocument, List<Diagnostic> list, Set
* @param versionlessFeatures
* @param featuresWithoutVersions
* @param featureName
* @param featureList
*/
private void checkForFeatureUniqueness(DOMDocument domDocument, List<Diagnostic> list, Set<String> includedFeatures, DOMNode featureTextNode, Set<String> versionedFeatures, Set<String> versionlessFeatures, Set<String> featuresWithoutVersions, String featureName) {
private void checkForFeatureUniqueness(DOMDocument domDocument, List<Diagnostic> list, Set<String> includedFeatures, DOMNode featureTextNode, Set<String> versionedFeatures, Set<String> versionlessFeatures, Set<String> featuresWithoutVersions, String featureName, Set<String> featureList) {
String featureNameLower = featureName.toLowerCase();
String featureNameNoVersionLower=featureNameLower;
String featureNameNoVersionLower;
if(featureNameLower.contains("-")){
featureNameNoVersionLower = featureNameLower.substring(0,
featureNameLower.lastIndexOf("-"));
versionedFeatures.add(featureName);
}else{
featureNameNoVersionLower = featureNameLower;
versionlessFeatures.add(featureName);
}
String featureNameNoVersion = LibertyUtils.stripVersion(featureName);
Map<String, String> changedFeatureNameMapLower = LibertyConstants.changedFeatureNameMap.entrySet().parallelStream().collect(
Collectors.toMap(entry -> entry.getKey().toLowerCase(),
entry -> entry.getValue().toLowerCase()));
Map<String, String> changedFeatureNameMapLowerReversed =
changedFeatureNameMapLower.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
Set<String> featuresWithOldNames = featuresWithoutVersions.stream().map(
v -> changedFeatureNameMapLower.get(v + "-"))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Set<String> featuresWithChangedNames = featuresWithoutVersions.stream().map(
v -> changedFeatureNameMapLowerReversed.get(v + "-"))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
// if this exact feature already exists, or another version of this feature already exists, then show a diagnostic
if (includedFeatures.contains(featureNameLower)) {
Range range = XMLPositionUtility.createRange(featureTextNode.getStart(),
Expand All @@ -180,11 +204,28 @@ private void checkForFeatureUniqueness(DOMDocument domDocument, List<Diagnostic>
} else if (featuresWithoutVersions.contains(featureNameNoVersionLower)) {
Range range = XMLPositionUtility.createRange(featureTextNode.getStart(),
featureTextNode.getEnd(), domDocument);
String featureNameNoVersion = LibertyUtils.stripVersion(featureName);
String message = "ERROR: More than one version of feature " + featureNameNoVersion + " is included. Only one version of a feature may be specified.";
list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE));
} else if (featuresWithOldNames.contains(featureNameNoVersionLower + "-")) {
String otherFeatureName = getOtherFeatureName(featureList, changedFeatureNameMapLowerReversed, featureNameNoVersionLower);
//check for features whose name is changed such as jsp is changed to pages
Range range = XMLPositionUtility.createRange(featureTextNode.getStart(),
featureTextNode.getEnd(), domDocument);
String message = String.format(changedFeatureNameDiagMessage, featureName, otherFeatureName,
LibertyUtils.stripVersion(otherFeatureName),
LibertyUtils.stripVersion(featureName));
list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE));
} else if (featuresWithChangedNames.contains(featureNameNoVersionLower + "-")) {
String otherFeatureName = getOtherFeatureName(featureList, changedFeatureNameMapLower, featureNameNoVersionLower);
Range range = XMLPositionUtility.createRange(featureTextNode.getStart(),
featureTextNode.getEnd(), domDocument);
String message = String.format(changedFeatureNameDiagMessage, featureName, otherFeatureName,
LibertyUtils.stripVersion(featureName),
LibertyUtils.stripVersion(otherFeatureName));
list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE));
}
includedFeatures.add(featureNameLower);
featureList.add(featureName);
featuresWithoutVersions.add(featureNameNoVersionLower);
}

Expand Down Expand Up @@ -520,4 +561,20 @@ private void checkForVersionlessPlatforms(DOMDocument domDocument,
list.add(new Diagnostic(range, message, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE, INCORRECT_FEATURE_CODE));
}
}

/**
* Get conflicting featurename for any feature
* @param includedFeatures
* @param changedFeatureNameMap
* @param featureNameNoVersionLower
* @return
*/
private static String getOtherFeatureName(Set<String> includedFeatures, Map<String, String> changedFeatureNameMapLowerReversed, String featureNameNoVersionLower) {
String otherFeatureNameWithoutVersion = changedFeatureNameMapLowerReversed.get(featureNameNoVersionLower + "-");
String otherFeatureName = includedFeatures
.stream()
.filter(f -> f.toLowerCase().contains(LibertyUtils.stripVersion(otherFeatureNameWithoutVersion)))
.findFirst().orElse(null);
return otherFeatureName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,7 @@ private LibertyConstants() {
put("wasJmsClient-", "messagingClient-");
put("wasJmsSecurity-", "messagingSecurity-");
}});

public static String changedFeatureNameDiagMessage="ERROR: The %s feature cannot be configured with the %s feature because they are two different versions of the same feature. " +
"The feature name changed from %s to %s for Jakarta EE. Remove one of the features.";
}
Original file line number Diff line number Diff line change
Expand Up @@ -687,4 +687,67 @@ public void testConfigElementVersionLess() throws JAXBException {
configForMissingFeature.setMessage(LibertyDiagnosticParticipant.MISSING_CONFIGURED_FEATURE_MESSAGE);
XMLAssert.testDiagnosticsFor(serverXML1, null, null, serverXMLURI,configForMissingFeature);
}

@Test
public void testUniquenessForNameChangedFeatureDiagnostic() throws BadLocationException {
String serverXML = String.join(newLine, //
"<server description=\"Sample Liberty server\">", //
" <featureManager>", //
" <feature>jms-2.0</feature>", //
" <feature>messaging-3.0</feature>", //
" </featureManager>", //
"</server>" //
);
Diagnostic invalid = new Diagnostic();
invalid.setRange(r(3, 24, 3, 37));
invalid.setMessage("ERROR: The messaging-3.0 feature cannot be configured with the jms-2.0 feature because they are two different versions of the same feature. The feature name changed from jms to messaging for Jakarta EE. Remove one of the features.");
XMLAssert.testDiagnosticsFor(serverXML, null, null, serverXMLURI,
invalid);

serverXML = String.join(newLine, //
"<server description=\"Sample Liberty server\">", //
" <featureManager>", //
" <feature>messaging-3.0</feature>", //
" <feature>jms-2.0</feature>", //
" </featureManager>", //
"</server>" //
);
invalid = new Diagnostic();
invalid.setRange(r(3, 24, 3, 31));
invalid.setMessage("ERROR: The jms-2.0 feature cannot be configured with the messaging-3.0 feature because they are two different versions of the same feature. The feature name changed from jms to messaging for Jakarta EE. Remove one of the features.");
XMLAssert.testDiagnosticsFor(serverXML, null, null, serverXMLURI,
invalid);

serverXML = String.join(newLine, //
"<server description=\"Sample Liberty server\">", //
" <featureManager>", //
" <platform>javaee-7.0</platform>", //
" <feature>enterpriseBeans-4.0</feature>", //
" <feature>ejb-3.2</feature>", //
" </featureManager>", //

"</server>" //
);
invalid = new Diagnostic();
invalid.setRange(r(4, 24, 4, 31));
invalid.setMessage("ERROR: The ejb-3.2 feature cannot be configured with the enterpriseBeans-4.0 feature because they are two different versions of the same feature. The feature name changed from ejb to enterpriseBeans for Jakarta EE. Remove one of the features.");
XMLAssert.testDiagnosticsFor(serverXML, null, null, serverXMLURI,
invalid);

serverXML = String.join(newLine, //
"<server description=\"Sample Liberty server\">", //
" <featureManager>", //
" <platform>javaee-7.0</platform>", //
" <feature>enterpriseBeans</feature>", //
" <feature>ejb</feature>", //
" </featureManager>", //

"</server>" //
);
invalid = new Diagnostic();
invalid.setRange(r(4, 24, 4, 27));
invalid.setMessage("ERROR: The ejb feature cannot be configured with the enterpriseBeans feature because they are two different versions of the same feature. The feature name changed from ejb to enterpriseBeans for Jakarta EE. Remove one of the features.");
XMLAssert.testDiagnosticsFor(serverXML, null, null, serverXMLURI,
invalid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,35 +29,17 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.CodeLensParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionParams;
import org.eclipse.lsp4j.DefinitionParams;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DocumentFormattingParams;
import org.eclipse.lsp4j.DocumentHighlight;
import org.eclipse.lsp4j.DocumentHighlightParams;
import org.eclipse.lsp4j.DocumentRangeFormattingParams;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.HoverParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.TextDocumentItem;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;

import io.openliberty.tools.langserver.completion.LibertyPropertiesCompletionProvider;
import io.openliberty.tools.langserver.diagnostic.DiagnosticRunner;
import io.openliberty.tools.langserver.diagnostic.LibertyPropertiesDiagnosticService;
import io.openliberty.tools.langserver.hover.LibertyPropertiesHoverProvider;

public class LibertyTextDocumentService implements TextDocumentService {
Expand All @@ -81,6 +63,9 @@ public LibertyTextDocument getOpenedDocument(String uri) {
public void didOpen(DidOpenTextDocumentParams params) {
LibertyTextDocument document = documents.onDidOpenTextDocument(params);
String uri = document.getUri();
if (uri == null) {
LOGGER.severe("Liberty text document URI is null for " + params);
}
validate(Arrays.asList(uri));
new DiagnosticRunner(libertyLanguageServer).compute(params);
}
Expand All @@ -89,6 +74,9 @@ public void didOpen(DidOpenTextDocumentParams params) {
public void didChange(DidChangeTextDocumentParams params) {
LibertyTextDocument document = documents.onDidChangeTextDocument(params);
String uri = document.getUri();
if (uri == null) {
LOGGER.severe("Liberty text document URI is null for " + params);
}
validate(Arrays.asList(uri));
new DiagnosticRunner(libertyLanguageServer).compute(params);
}
Expand All @@ -97,6 +85,9 @@ public void didChange(DidChangeTextDocumentParams params) {
public void didClose(DidCloseTextDocumentParams params) {
documents.onDidCloseTextDocument(params);
String uri = params.getTextDocument().getUri();
if (uri == null) {
LOGGER.severe("Liberty text document URI is null for " + params);
}
libertyLanguageServer.getLanguageClient()
.publishDiagnostics(new PublishDiagnosticsParams(uri, new ArrayList<Diagnostic>()));
}
Expand All @@ -117,8 +108,16 @@ private void validateAll() {
@Override
public CompletableFuture<Hover> hover(HoverParams hoverParams) {
String uri = hoverParams.getTextDocument().getUri();
if (uri == null) {
LOGGER.severe("Liberty text document URI is null for " + hoverParams);
}
LibertyTextDocument textDocumentItem = documents.get(uri);
return new LibertyPropertiesHoverProvider(textDocumentItem).getHover(hoverParams.getPosition());
if (textDocumentItem != null) {
return new LibertyPropertiesHoverProvider(textDocumentItem).getHover(hoverParams.getPosition());
} else {
LOGGER.severe("The document with uri " + uri + " has not been found in opened documents. Cannot provide hover.");
return CompletableFuture.completedFuture(new Hover());
}
}

@Override
Expand Down

0 comments on commit c8dc268

Please sign in to comment.