blob: bdd5648b3247237b1cc5a1be8fa9d0c42dacf5d0 [file] [log] [blame]
/*
* Copyright (c) 2010-2020 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
package org.eclipse.scout.sdk.core.s.nls.properties;
import static java.util.Collections.unmodifiableMap;
import static org.eclipse.scout.sdk.core.util.Ensure.newFail;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.eclipse.scout.sdk.core.generator.properties.PropertiesGenerator;
import org.eclipse.scout.sdk.core.s.environment.IEnvironment;
import org.eclipse.scout.sdk.core.s.environment.IProgress;
import org.eclipse.scout.sdk.core.s.nls.Language;
import org.eclipse.scout.sdk.core.util.Ensure;
import org.eclipse.scout.sdk.core.util.SdkException;
import org.eclipse.scout.sdk.core.util.Strings;
/**
* <h3>{@link AbstractTranslationPropertiesFile}</h3>
*
* @since 7.0.0
*/
public abstract class AbstractTranslationPropertiesFile implements ITranslationPropertiesFile {
public static final String FILE_SUFFIX = ".properties";
public static final Pattern FILE_PATTERN = Pattern.compile("^[^_]+(?:_(" + Language.LANGUAGE_REGEX + "))?\\" + FILE_SUFFIX + "$");
private final Language m_language;
private final Supplier<InputStream> m_inputSupplier;
private PropertiesGenerator m_fileContent;
protected AbstractTranslationPropertiesFile(Language language, Supplier<InputStream> contentSupplier) {
m_language = Ensure.notNull(language);
m_inputSupplier = Ensure.notNull(contentSupplier);
}
private Map<String, String> entries() {
if (m_fileContent == null) {
throw newFail("Properties file has not been loaded yet.");
}
return m_fileContent.properties();
}
@Override
public final Language language() {
return m_language;
}
@Override
public Map<String, String> allEntries() {
return unmodifiableMap(entries());
}
@Override
public final Stream<String> allKeys() {
return entries().keySet().stream();
}
@Override
public final Optional<String> translation(String key) {
return Optional.ofNullable(entries().get(key));
}
@Override
public boolean load(IProgress progress) {
PropertiesGenerator newContent = readEntries();
if (Objects.equals(m_fileContent, newContent)) {
return false;
}
m_fileContent = newContent;
return true;
}
private PropertiesGenerator readEntries() {
try (InputStream in = Ensure.notNull(m_inputSupplier.get())) {
return PropertiesGenerator.create(in);
}
catch (IOException e) {
throw new SdkException("Error reading properties file for language '{}'.", language(), e);
}
}
@Override
public boolean setTranslation(String key, String text) {
throwIfReadOnly();
Ensure.notBlank(key);
if (text == null) {
return removeTranslation(key);
}
String oldTranslation = entries().put(key, text);
return !text.equals(oldTranslation);
}
@Override
public boolean removeTranslation(String key) {
throwIfReadOnly();
return entries().remove(Ensure.notBlank(key)) != null;
}
@Override
public void flush(IEnvironment env, IProgress progress) {
throwIfReadOnly();
writeEntries(m_fileContent, env, progress);
}
protected void throwIfReadOnly() {
if (!isEditable()) {
throw new UnsupportedOperationException("Cannot modify a ready-only resource.");
}
}
protected abstract void writeEntries(PropertiesGenerator content, IEnvironment env, IProgress progress);
/**
* Parses the language from a translation .properties file respecting the given prefix.<br>
* The inverse operation is {@link #getPropertiesFileName(String, Language)}.
*
* @param fileName
* The file name to parse. E.g. {@code "Texts_en_GB.properties"}. May not be {@code null}.
* @param prefix
* An optional prefix the given file name must have to be accepted.
* @return An {@link Optional} holding the language of the given file name or an empty {@link Optional} if it cannot
* be parsed (which might be because the prefix does not match).
*/
public static Optional<Language> parseLanguageFromFileName(String fileName, String prefix) {
if (Strings.isBlank(fileName)) {
return Optional.empty();
}
if (Strings.hasText(prefix) && !fileName.startsWith(prefix)) {
return Optional.empty();
}
Matcher matcher = FILE_PATTERN.matcher(fileName);
if (!matcher.matches()) {
return Optional.empty();
}
String languagePart = matcher.group(1);
if (Strings.isBlank(languagePart)) {
return Optional.of(Language.LANGUAGE_DEFAULT);
}
return Language.parse(languagePart);
}
/**
* Gets the filename for a {@code .properties} file using the specified prefix and {@link Language}.<br>
* This the inverse operation is {@link #parseLanguageFromFileName(String, String)}.
*
* @param prefix
* The file prefix. Must not be {@code null}.
* @param language
* The language. Must not be {@code null}.
* @return The file name.
*/
public static String getPropertiesFileName(String prefix, Language language) {
return prefix + '_' + language.locale() + FILE_SUFFIX;
}
}