Skip to content

Commit

Permalink
refactor: split resources bundle
Browse files Browse the repository at this point in the history
Signed-off-by: Aman Prashant <aman.prashant@broadcom.com>
  • Loading branch information
ap891843 committed Jan 6, 2023
1 parent 528b29e commit 2617fd6
Show file tree
Hide file tree
Showing 11 changed files with 397 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ public interface MessageService {
* @return localized string
*/
String localizeTemplate(MessageTemplate template);

/** Reload and updates the messages for Cobol and its dialect. */
void reloadMessages();
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* Provides files in the working folder
*/
@Singleton
class WorkingFolderService {
public class WorkingFolderService {

private static final String DIALECTS_PATH_SYSTEM_PROPERTY = "dialect.path";

Expand Down Expand Up @@ -65,6 +65,7 @@ public URI getWorkingFolder() {

/**
* Returns a working folder uri
* @param path
* @return a working folder uri
*/
@SneakyThrows
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*
* Copyright (c) 2022 Broadcom.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*
*/
package org.eclipse.lsp.cobol.core.messages;

import com.google.common.annotations.VisibleForTesting;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.eclipse.lsp.cobol.core.engine.dialects.WorkingFolderService;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
* This is a multiple properties resource bundle. Resource bundles contain locale-specific objects.
* When your program needs a locale-specific resource, a String for example, your program can load
* it from the resource bundle that is appropriate for the current user's locale.
*/
@Slf4j
public class CobolLSPropertiesResourceBundle extends ResourceBundle {
private final String baseName;
public static final char BUNDLE_SEPARATOR = '_';
private final Properties properties;
private final WorkingFolderService workingFolderService;
private final Locale locale;

public CobolLSPropertiesResourceBundle(
String basename, Locale locale, WorkingFolderService workingFolderService) {
this.workingFolderService = workingFolderService;
this.baseName = basename;
this.properties = new Properties();
this.locale = locale;
setParent(ResourceBundle.getBundle(basename, locale));
}

/**
* Updates resource bundle for a supplied dialect.
*
* @param dialectName dialect for which resource needs to be updated
* @throws IOException when resources for a dialect is not found
*/
public void updateMessageResourceBundle(String dialectName) throws IOException {
properties.putAll(load(dialectName, locale));
}

private Properties load(String dialectName, Locale locale) throws IOException {
Properties properties = new Properties();
List<String> resourceName = toSuspectedBundleNames(locale);
Collections.reverse(resourceName);
URI workingFolder = this.workingFolderService.getWorkingFolder();
InputStream validResources =
getDialectResources(resourceName, workingFolder, getJarName(dialectName));
properties.load(validResources);
return properties;
}

private String getJarName(String dialectName) {
return "dialect-" + dialectName + ".jar";
}

@VisibleForTesting
public InputStream getDialectResources(
List<String> resourceName, URI workingFolder, String jarName) throws IOException {
for (String s : resourceName) {
URL url = new URL("jar:" + workingFolder + "/" + jarName + "!/" + s);
if (jarUrlExists(url)) {
return url.openStream();
} else {
LOG.debug("Resource Bundle " + s + " not available at " + jarName);
}
}
return IOUtils.toInputStream("", StandardCharsets.UTF_8);
}

private boolean jarUrlExists(URL url) {
try {
IOUtils.toString(url.openStream(), StandardCharsets.UTF_8);
return true;
} catch (IOException e) {
return false;
}
}

private List<String> toSuspectedBundleNames(String baseName, Locale locale) {
String language = locale.getLanguage();
String script = locale.getScript();
String country = locale.getCountry();
String variant = locale.getVariant();

if (Objects.equals(language, "")
&& Objects.equals(country, "")
&& Objects.equals(variant, "")) {
return Collections.singletonList(baseName);
}

StringBuilder sb = new StringBuilder(baseName);
sb.append(BUNDLE_SEPARATOR);
if (!Objects.equals(script, "")) {
if (!Objects.equals(variant, "")) {
sb.append(language)
.append(BUNDLE_SEPARATOR)
.append(script)
.append(BUNDLE_SEPARATOR)
.append(country)
.append(BUNDLE_SEPARATOR)
.append(variant);
} else if (!Objects.equals(country, "")) {
sb.append(language)
.append(BUNDLE_SEPARATOR)
.append(script)
.append(BUNDLE_SEPARATOR)
.append(country);
} else {
sb.append(language).append(BUNDLE_SEPARATOR).append(script);
}
} else {
if (!Objects.equals(variant, "")) {
sb.append(language)
.append(BUNDLE_SEPARATOR)
.append(country)
.append(BUNDLE_SEPARATOR)
.append(variant);
} else if (!Objects.equals(country, "")) {
sb.append(language).append(BUNDLE_SEPARATOR).append(country);
} else {
sb.append(language);
}
}
return possibleResources(sb);
}

private List<String> toSuspectedBundleNames(Locale locale) {
return toSuspectedBundleNames(this.baseName, locale);
}

private List<String> possibleResources(StringBuilder sb) {
List<String> result = new ArrayList<>();
String[] s = sb.toString().split("_");
final String[] prevStr = {""};
Arrays.stream(s)
.forEach(
str -> {
if (Objects.equals(prevStr[0], "")) {
prevStr[0] = str;
} else {
prevStr[0] = String.join("_", prevStr[0], str);
}
result.add(prevStr[0] + ".properties");
});
return result;
}

/**
* Gets an object for the given key from this resource bundle. Returns null if this resource
* bundle does not contain an object for the given key.
*
* @param key the key for the desired object
* @return the object for the given key, or Key
*/
@Override
protected Object handleGetObject(@NonNull String key) {
try {
return parent.getObject(key);
} catch (MissingResourceException e) {
if (properties != null && properties.get(key) != null) {
return properties.get(key);
} else {
return key;
}
}
}

/**
* Returns an enumeration of the keys.
*
* @return an <code>Enumeration</code> of the keys contained in this <code>ResourceBundle</code>
* and its parent bundles.
*/
@Override
public Enumeration<String> getKeys() {
Set<String> parentKeys = parent.keySet();
Set<String> propKeys = properties.keySet().stream().map(o -> (String) o).collect(Collectors.toSet());
parentKeys.addAll(propKeys);
return Collections.enumeration(parentKeys);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,78 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.lsp.cobol.common.message.LocaleStore;
import org.eclipse.lsp.cobol.common.message.MessageService;
import org.eclipse.lsp.cobol.common.message.MessageTemplate;
import org.eclipse.lsp.cobol.core.engine.dialects.WorkingFolderService;
import org.eclipse.lsp.cobol.service.settings.SettingsService;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.stream.Stream;

import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.joining;
import static org.eclipse.lsp.cobol.service.settings.SettingsParametersEnum.DIALECTS;

/**
* This class is an properties file implementation of {@link MessageService} . It loads messages
* from a properties file into memory to be used latter on for logging or messaging.
*/
@Singleton
@Slf4j
public class PropertiesMessageService implements MessageService {

private final String baseName;
private final LocaleStore localeStore;
private ResourceBundle resourceBundle;
private CobolLSPropertiesResourceBundle resourceBundle;
private final SettingsService settingsService;
private final WorkingFolderService workingFolderService;

@Inject
public PropertiesMessageService(
@Named("resourceFileLocation") String baseName, LocaleStore localeStore) {
@Named("resourceFileLocation") String baseName,
LocaleStore localeStore,
SettingsService settingsService,
WorkingFolderService workingFolderService) {
this.baseName = baseName;
this.localeStore = localeStore;
resourceBundle = ResourceBundle.getBundle(baseName, localeStore.getApplicationLocale());
this.settingsService = settingsService;
this.workingFolderService = workingFolderService;
resourceBundle =
new CobolLSPropertiesResourceBundle(
baseName, localeStore.getApplicationLocale(), workingFolderService);
subscribeToLocaleStore();
}

private void subscribeToLocaleStore() {
localeStore.subscribeToLocaleChange(this::reloadResourceBundle);
}

@Override
public void reloadMessages() {
reloadResourceBundle(this.localeStore.getApplicationLocale());
}

private void reloadResourceBundle(Locale locale) {
ResourceBundle.clearCache();
resourceBundle = ResourceBundle.getBundle(baseName, locale);
resourceBundle =
new CobolLSPropertiesResourceBundle(
baseName,
localeStore.getApplicationLocale(),
this.workingFolderService);
updateResourceBundle();
}

@Override
public String getMessage(String key, Object... parameters) {
try {
return String.format(resourceBundle.getString(key), parameters);
return String.format((String) resourceBundle.handleGetObject(key), parameters);
} catch (MissingResourceException e) {
return key;
}
Expand All @@ -81,6 +107,23 @@ public String localizeTemplate(MessageTemplate template) {
.orElse(parameters));
}

private void updateResourceBundle() {
reloadResourceBundle(this.localeStore.getApplicationLocale());
this.settingsService
.fetchTextConfiguration(DIALECTS.label)
.thenAccept(this::updateResourceBundle);
}

private void updateResourceBundle(List<String> dialects) {
for (String dialectName : dialects) {
try {
this.resourceBundle.updateMessageResourceBundle(dialectName);
} catch (IOException e) {
LOG.error("Issue while loading resource bundle for " + dialectName);
}
}
}

private Object[] processArgs(Object[] args) {
return Stream.of(args)
.map(arg -> arg instanceof MessageTemplate ? localizeTemplate((MessageTemplate) arg) : arg)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import lombok.extern.slf4j.Slf4j;
import org.eclipse.lsp.cobol.common.error.ErrorCode;
import org.eclipse.lsp.cobol.common.message.LocaleStore;
import org.eclipse.lsp.cobol.common.message.MessageService;
import org.eclipse.lsp.cobol.common.utils.LogLevelUtils;
import org.eclipse.lsp.cobol.core.engine.dialects.DialectService;
import org.eclipse.lsp.cobol.lsp.DisposableLSPStateService;
Expand Down Expand Up @@ -65,6 +66,7 @@ public class CobolLanguageServer implements LanguageServer {
private final CopybookNameService copybookNameService;
private final Keywords keywords;
private final DialectService dialectService;
private final MessageService messageService;

@Inject
@SuppressWarnings("squid:S107")
Expand All @@ -79,7 +81,8 @@ public class CobolLanguageServer implements LanguageServer {
ConfigurationService configurationService,
CopybookNameService copybookNameService,
Keywords keywords,
DialectService dialectService) {
DialectService dialectService,
MessageService messageService) {
this.textService = textService;
this.workspaceService = workspaceService;
this.watchingService = watchingService;
Expand All @@ -91,6 +94,7 @@ public class CobolLanguageServer implements LanguageServer {
this.copybookNameService = copybookNameService;
this.keywords = keywords;
this.dialectService = dialectService;
this.messageService = messageService;
}

@Override
Expand Down Expand Up @@ -150,6 +154,7 @@ public void initialized(@Nullable InitializedParams params) {
getLogLevelFromClient();
copybookNameService.collectLocalCopybookNames();
keywords.updateStorage();
messageService.reloadMessages();
notifyConfiguredCopybookExtensions();
}

Expand Down

0 comments on commit 2617fd6

Please sign in to comment.