diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ConfigReader.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ConfigReader.java index 68da8a05610c..79d94c92c050 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ConfigReader.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ConfigReader.java @@ -26,7 +26,6 @@ import io.ballerina.compiler.syntax.tree.ModulePartNode; import io.ballerina.compiler.syntax.tree.ModuleVariableDeclarationNode; import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; @@ -44,6 +43,7 @@ import org.wso2.ballerinalang.util.Flags; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -60,7 +60,7 @@ public class ConfigReader { /** - * Retrieve configurable variables in a package as a per module map. + * Retrieve all the configurable variables for a package. * * @param packageInstance to read configurable variables * @return A map with ConfigModuleDetails(module details) as key and @@ -68,19 +68,112 @@ public class ConfigReader { */ public static Map> getConfigVariables(Package packageInstance) { Map> configDetails = new HashMap<>(); + // Get configurable variables of the current package Set validConfigs = getValidConfigs(packageInstance.getDefaultModule().moduleContext(). bLangPackage().symbol); for (Module module : packageInstance.modules()) { getConfigs(module, module.moduleContext().bLangPackage(), configDetails, validConfigs); } + // Get configurable variables of the direct imports + Collection dependencies = new ArrayList<>(); + getValidDependencies(packageInstance, packageInstance.getDefaultModule(), dependencies); + getImportedConfigVars(packageInstance, dependencies, configDetails); return configDetails; } + /** + * Retrieve configurable variables for all the direct imports for a package. + * + * @param currentPackage Current package instance + * @param moduleDependencies Used dependencies of the package + * @param configDetails Map to store the configurable variables against module + */ + private static void getImportedConfigVars(Package currentPackage, + Collection moduleDependencies, + Map> configDetails) { + currentPackage.modules().forEach(module -> + module.moduleContext().bLangPackage().symbol.imports.stream() + .filter(importSymbol -> isDirectDependency( + moduleDependencies, + importSymbol.descriptor.org().value(), + importSymbol.descriptor.packageName().value(), + importSymbol.descriptor.name().moduleNamePart() + )) + .forEach(importSymbol -> { + String orgName = importSymbol.descriptor.org().value(); + String packageName = importSymbol.descriptor.packageName().value(); + String moduleName = importSymbol.descriptor.name().moduleNamePart(); + + // If default module + if (moduleName == null) { + moduleName = packageName; + } + + List configVariables = new ArrayList<>(); + getConfigVars(module, importSymbol.scope.entries.values(), null, configVariables); + + if (!configVariables.isEmpty()) { + configDetails.put( + new ConfigModuleDetails(orgName, packageName, moduleName, + ProjectKind.BALA_PROJECT), configVariables); + } + }) + ); + } + + /** + * Checks whether it is a direct dependency. + * + * @param moduleDependencies collection of module dependencies + * @param orgName org name + * @param packageName package name + * @param moduleName module name + * @return boolean value indicating whether it is a direct dependency + */ + private static boolean isDirectDependency(Collection moduleDependencies, String orgName, + String packageName, String moduleName) { + return moduleDependencies.stream().anyMatch(dependency -> + dependency.descriptor().org().value().equals(orgName) && + dependency.descriptor().packageName().value().equals(packageName) && + (moduleName == null + ? dependency.descriptor().name().moduleNamePart() == null + : dependency.descriptor().name().moduleNamePart().equals(moduleName)) + ); + } + + private static String getDescriptionValue(MetadataNode metadataNode) { + for (AnnotationNode annotation : metadataNode.annotations()) { + Node annotReference = annotation.annotReference(); + if (annotReference.kind() == SyntaxKind.SIMPLE_NAME_REFERENCE && + ((SimpleNameReferenceNode) annotReference).name().text().equals("display") && + annotation.annotValue().isPresent()) { + + for (MappingFieldNode fieldNode : annotation.annotValue().get().fields()) { + if (fieldNode.kind() == SyntaxKind.SPECIFIC_FIELD) { + SpecificFieldNode specificField = (SpecificFieldNode) fieldNode; + if (specificField.fieldName().kind() == SyntaxKind.IDENTIFIER_TOKEN && + ((IdentifierToken) specificField.fieldName()).text().equals("description") && + specificField.valueExpr().isPresent()) { + + ExpressionNode valueNode = specificField.valueExpr().get(); + if (valueNode instanceof BasicLiteralNode) { + return ((BasicLiteralNode) valueNode).literalToken().text(); + } + } + } + } + } + } + return ""; + } + + + /** * Update provided map with the configurable variable details for the given module. * - * @param module Module to retrieve module details - * @param bLangPackage to retrieve configurable variable details + * @param module Module to retrieve module details + * @param bLangPackage to retrieve configurable variable details * @param configDetails Map to store the configurable variables against module */ private static void getConfigs(Module module, @@ -88,32 +181,41 @@ private static void getConfigs(Module module, List> configDetails, Set validConfigs) { List configVariables = new ArrayList<>(); PackageID currentPkgId = bLangPackage.symbol.pkgID; - for (Scope.ScopeEntry entry : bLangPackage.symbol.scope.entries.values()) { + getConfigVars(module, bLangPackage.symbol.scope.entries.values(), validConfigs, configVariables); + if (!configVariables.isEmpty()) { + // Add configurable variable details for the current package + configDetails.put(getConfigModuleDetails(module.moduleName(), currentPkgId, + module.project().kind()), configVariables); + } + } + + private static void getConfigVars(Module module, Collection scopeEntries, + Set validConfigs, List configVariables) { + for (Scope.ScopeEntry entry : scopeEntries) { BSymbol symbol = entry.symbol; // Filter configurable variables if (symbol != null && symbol.tag == SymTag.VARIABLE && Symbols.isFlagOn(symbol.flags, Flags.CONFIGURABLE)) { if (symbol instanceof BVarSymbol) { BVarSymbol varSymbol = (BVarSymbol) symbol; - if (validConfigs.contains(varSymbol)) { + if ((validConfigs != null && validConfigs.contains(varSymbol)) || validConfigs == null) { // Get description - String description = getDescription(varSymbol, module); - if (description.startsWith("\"") && description.endsWith("\"")) { - description = description.substring(1, description.length() - 1); - } - configVariables.add(new ConfigVariable(varSymbol.name.value.replace("\\", ""), varSymbol.type, + String description = getDescriptionValue(varSymbol, module); + configVariables.add(new ConfigVariable( + varSymbol.name.value.replace("\\", ""), varSymbol.type, Symbols.isFlagOn(varSymbol.flags, Flags.REQUIRED), description)); } } } } - if (!configVariables.isEmpty()) { - // Add configurable variable details for the current package - configDetails.put(getConfigModuleDetails(module.moduleName(), currentPkgId, - module.project().kind()), configVariables); - } } + /** + * Get the used configurable variables of the package. + * + * @param packageSymbol ballerina package symbol + * @return set of valid configurable variable symbols + */ private static Set getValidConfigs(BPackageSymbol packageSymbol) { Set configVars = new HashSet<>(); populateConfigVars(packageSymbol, configVars); @@ -125,6 +227,12 @@ private static Set getValidConfigs(BPackageSymbol packageSymbol) { return configVars; } + /** + * Populate the configurable variables for the package. + * + * @param pkgSymbol ballerina package symbol + * @param configVars set of configurable variable symbols + */ private static void populateConfigVars(BPackageSymbol pkgSymbol, Set configVars) { for (Scope.ScopeEntry entry : pkgSymbol.scope.entries.values()) { BSymbol symbol = entry.symbol; @@ -143,8 +251,8 @@ private static void populateConfigVars(BPackageSymbol pkgSymbol, Set /** * Get module details to store the configurable variables against. * - * @param moduleName to retrieve module details - * @param packageID to retrieve package details + * @param moduleName to retrieve module details + * @param packageID to retrieve package details * @param projectKind to retrieve information about the type of project * @return module details stored in object ConfigModuleDetails */ @@ -163,40 +271,19 @@ private static ConfigModuleDetails getConfigModuleDetails(ModuleName moduleName, * @param module to retrieve module details * @return configurable variable description */ - private static String getDescription(BVarSymbol symbol, Module module) { + private static String getDescriptionValue(BVarSymbol symbol, Module module) { Map syntaxTreeMap = getSyntaxTreeMap(module); Node variableNode = getVariableNode(symbol.getPosition().lineRange().startLine().line(), syntaxTreeMap); if (variableNode != null) { Optional optionalMetadataNode = ((ModuleVariableDeclarationNode) variableNode).metadata(); if (optionalMetadataNode.isPresent()) { - NodeList annotations = optionalMetadataNode.get().annotations(); - for (AnnotationNode annotation : annotations) { - Node annotReference = annotation.annotReference(); - if (annotReference.kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) { - SimpleNameReferenceNode simpleNameRef = (SimpleNameReferenceNode) annotReference; - if (simpleNameRef.name().text().equals("display") && annotation.annotValue().isPresent()) { - for (MappingFieldNode fieldNode : annotation.annotValue().get().fields()) { - if (fieldNode.kind() == SyntaxKind.SPECIFIC_FIELD) { - SpecificFieldNode specificField = (SpecificFieldNode) fieldNode; - if (specificField.fieldName().kind() == SyntaxKind.IDENTIFIER_TOKEN) { - if (((IdentifierToken) specificField.fieldName()).text(). - equals("description")) { - if (((SpecificFieldNode) fieldNode).valueExpr().isPresent()) { - ExpressionNode valueNode = - ((SpecificFieldNode) fieldNode).valueExpr().get(); - if (valueNode instanceof BasicLiteralNode) { - return ((BasicLiteralNode) valueNode).literalToken().text(); - } - } - } - } - } - - } - } - } + String description = getDescriptionValue(optionalMetadataNode.get()); + // Remove enclosing quotes if present + if (description.startsWith("\"") && description.endsWith("\"")) { + description = description.substring(1, description.length() - 1); } + return description; } } return ""; @@ -205,7 +292,7 @@ private static String getDescription(BVarSymbol symbol, Module module) { /** * Get Syntax tree node for the configurable variable in given position. * - * @param position position of configurable BVarSymbol + * @param position position of configurable BVarSymbol * @param syntaxTreeMap Syntax tree map for the specific module * @return Relevant syntax tree node for the variable */ @@ -243,4 +330,56 @@ private static Map getSyntaxTreeMap(Module module) { return syntaxTreeMap; } + /** + * Get all the valid dependencies for the package. + * + * @param packageInstance Package instance + * @param module module instance + * @param dependencies Collection of module dependencies + */ + private static void getValidDependencies(Package packageInstance, Module module, + Collection dependencies) { + Collection directDependencies = module.moduleContext().dependencies(); + for (ModuleDependency moduleDependency : directDependencies) { + if (!isDefaultScope(moduleDependency)) { + continue; + } + dependencies.add(moduleDependency); + if (isSamePackage(packageInstance, moduleDependency)) { + for (Module mod : packageInstance.modules()) { + String modName = mod.descriptor().name().moduleNamePart(); + if (modName != null && modName.equals( + moduleDependency.descriptor().name().moduleNamePart())) { + getValidDependencies(packageInstance, mod, dependencies); + } + } + } + } + } + + /** + * Check if the dependency has the default scope. + * + * @param moduleDependency Module dependency + * @return boolean value indicating whether the dependency has default scope or not + */ + private static boolean isDefaultScope(ModuleDependency moduleDependency) { + return moduleDependency.packageDependency().scope().getValue().equals( + PlatformLibraryScope.DEFAULT.getStringValue()); + } + + /** + * Check if the dependency is From the same package. + * + * @param packageInstance package instance + * @param moduleDependency Module dependency + * @return boolean value indicating whether the dependency is from the same package or not + */ + private static boolean isSamePackage(Package packageInstance, ModuleDependency moduleDependency) { + String orgValue = moduleDependency.descriptor().org().value(); + String packageVal = moduleDependency.descriptor().packageName().value(); + return orgValue.equals(packageInstance.packageOrg().value()) && + packageVal.equals(packageInstance.packageName().value()); + } + } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/ConfigSchemaBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/ConfigSchemaBuilder.java index f3ddba9a6b10..dcd4ae2ae3db 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/ConfigSchemaBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/ConfigSchemaBuilder.java @@ -147,9 +147,11 @@ private static JsonArray getRequiredConfigs(List configVariables private JsonObject setConfigVariables(List configVariables, JsonObject node) { for (ConfigVariable configVariable : configVariables) { - JsonObject typeNode = new TypeConverter().getType(configVariable.type()); - typeNode.addProperty("description", configVariable.description()); - node.add(configVariable.name(), typeNode); + if (configVariable.type() != null) { + JsonObject typeNode = new TypeConverter().getType(configVariable.type()); + typeNode.addProperty("description", configVariable.description()); + node.add(configVariable.name(), typeNode); + } } return node; }