Skip to content

Commit

Permalink
fix: fixed usage of reserved "no" language code
Browse files Browse the repository at this point in the history
  • Loading branch information
yevheniyJ committed Oct 14, 2024
1 parent 32ea5f6 commit 26501f8
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 29 deletions.
42 changes: 32 additions & 10 deletions src/main/java/com/crowdin/cli/properties/FileBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
@Data
public class FileBean {

public static final String LANGUAGES_META = "__meta_" + LANGUAGES_MAPPING;

static FileBeanConfigurator CONFIGURATOR = new FileBeanConfigurator();

private String source;
Expand Down Expand Up @@ -63,7 +65,27 @@ public FileBean buildFromMap(Map<String, Object> map) {
PropertiesBuilder.setPropertyIfExists(fileBean::setScheme, map, SCHEME, String.class);
PropertiesBuilder.setPropertyIfExists(fileBean::setIgnore, map, IGNORE, List.class);
PropertiesBuilder.setPropertyIfExists(fileBean::setTranslatableElements, map, TRANSLATABLE_ELEMENTS, List.class);
PropertiesBuilder.setPropertyIfExists(fileBean::setLanguagesMapping, map, LANGUAGES_MAPPING, Map.class);

if (map.containsKey(LANGUAGES_META) && map.containsKey(LANGUAGES_MAPPING)) {
Map<String, Map<String, String>> languagesRaw = PropertiesBuilder.checkProperty(map, LANGUAGES_META, Map.class, null);
var languagesParsed = PropertiesBuilder.checkProperty(map, LANGUAGES_MAPPING, Map.class, null);
if (languagesRaw.size() == languagesParsed.size()) {
var equalSize = languagesRaw.entrySet().stream()
.allMatch(entry -> languagesParsed.containsKey(entry.getKey())
&& languagesParsed.get(entry.getKey()) instanceof Map
&& entry.getValue().size() == ((Map<?, ?>) languagesParsed.get(entry.getKey())).size()
);
//if raw languages and parsed are the same in terms of size then let's just use raw data to prevent any parsing failures (e.g. when "no" lang code is used)
if (equalSize) {
fileBean.setLanguagesMapping(languagesRaw);
} else {
PropertiesBuilder.setPropertyIfExists(fileBean::setLanguagesMapping, map, LANGUAGES_MAPPING, Map.class);
}
}
} else {
PropertiesBuilder.setPropertyIfExists(fileBean::setLanguagesMapping, map, LANGUAGES_MAPPING, Map.class);
}

PropertiesBuilder.setPropertyIfExists(fileBean::setTranslationReplace, map, TRANSLATION_REPLACE, Map.class);
PropertiesBuilder.setPropertyIfExists(fileBean::setEscapeQuotes, map, ESCAPE_QUOTES, Integer.class);
PropertiesBuilder.setPropertyIfExists(fileBean::setEscapeSpecialCharacters, map, ESCAPE_SPECIAL_CHARACTERS, Integer.class);
Expand Down Expand Up @@ -96,8 +118,8 @@ public void populateWithDefaultValues(FileBean bean) {
if (bean.getTranslation() != null) {
bean.setTranslation(Utils.normalizePath(bean.getTranslation()));
if (!PlaceholderUtil.containsLangPlaceholders(bean.getTranslation())
&& (bean.getScheme() != null
|| (bean.getMultilingual() != null && bean.getMultilingual()))) {
&& (bean.getScheme() != null
|| (bean.getMultilingual() != null && bean.getMultilingual()))) {
bean.setTranslation(Utils.noSepAtStart(bean.getTranslation()));
} else {
bean.setTranslation(Utils.sepAtStart(bean.getTranslation()));
Expand Down Expand Up @@ -161,8 +183,8 @@ public List<String> checkProperties(FileBean bean) {
}

if (!PlaceholderUtil.containsLangPlaceholders(bean.getTranslation())
&& bean.getScheme() == null
&& (bean.getMultilingual() == null || !bean.getMultilingual())) {
&& bean.getScheme() == null
&& (bean.getMultilingual() == null || !bean.getMultilingual())) {
errors.add(RESOURCE_BUNDLE.getString("error.config.translation_has_no_language_placeholders"));
}

Expand Down Expand Up @@ -196,7 +218,7 @@ public List<String> checkProperties(FileBean bean) {
}

if (bean.getSkipTranslatedOnly() != null && bean.getSkipUntranslatedFiles() != null
&& bean.getSkipTranslatedOnly() && bean.getSkipUntranslatedFiles()) {
&& bean.getSkipTranslatedOnly() && bean.getSkipUntranslatedFiles()) {
errors.add(RESOURCE_BUNDLE.getString("error.skip_untranslated_both_strings_and_files"));
}

Expand All @@ -206,10 +228,10 @@ public List<String> checkProperties(FileBean bean) {

private static boolean checkDest(String dest, String source) {
boolean destContainsPlaceholders = dest.contains(PlaceholderUtil.PLACEHOLDER_FILE_NAME)
|| dest.contains(PlaceholderUtil.PLACEHOLDER_ORIGINAL_FILE_NAME)
|| dest.contains(PlaceholderUtil.PLACEHOLDER_ORIGINAL_PATH)
|| dest.contains(PlaceholderUtil.PLACEHOLDER_FILE_EXTENSION)
|| dest.contains(PlaceholderUtil.DOUBLED_ASTERISK);
|| dest.contains(PlaceholderUtil.PLACEHOLDER_ORIGINAL_FILE_NAME)
|| dest.contains(PlaceholderUtil.PLACEHOLDER_ORIGINAL_PATH)
|| dest.contains(PlaceholderUtil.PLACEHOLDER_FILE_EXTENSION)
|| dest.contains(PlaceholderUtil.DOUBLED_ASTERISK);
boolean sourceContainsPlaceholders = PlaceholderUtil.containsFilePlaceholders(source) || SourcesUtils.containsPattern(source);
return !sourceContainsPlaceholders || destContainsPlaceholders;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ static <T> void setPropertyIfExists(Consumer<T> setter, Map<String, Object> prop
}
}

private static <T> T checkProperty(Map<String, Object> properties, String key, Class<T> clazz, T defaultValue) {
static <T> T checkProperty(Map<String, Object> properties, String key, Class<T> clazz, T defaultValue) {
try {
Object param = properties.getOrDefault(key, defaultValue);
if (param != null && !clazz.isAssignableFrom(param.getClass())) {
Expand Down
84 changes: 75 additions & 9 deletions src/main/java/com/crowdin/cli/properties/PropertiesWithFiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

import com.crowdin.cli.client.Clients;
import com.crowdin.cli.client.ProjectClient;
import com.crowdin.cli.utils.file.FileUtils;
import com.crowdin.client.languages.model.Language;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;

import java.util.*;
import java.util.stream.Collectors;

import static com.crowdin.cli.BaseCli.IGNORE_HIDDEN_FILES_PATTERN;
import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE;
import static com.crowdin.cli.properties.PropertiesBuilder.FILES;
import static com.crowdin.cli.properties.PropertiesBuilder.PRESERVE_HIERARCHY;
import static com.crowdin.cli.properties.PropertiesBuilder.PSEUDO_LOCALIZATION;
import static com.crowdin.cli.properties.PropertiesBuilder.*;

@EqualsAndHashCode(callSuper = true)
@Data
Expand All @@ -35,10 +38,73 @@ private PropertiesWithFilesConfigurator() {
@Override
public void populateWithValues(PropertiesWithFiles props, Map<String, Object> map) {
PropertiesBuilder.setBooleanPropertyIfExists(props::setPreserveHierarchy, map, PRESERVE_HIERARCHY);
props.setFiles(PropertiesBuilder.getListOfMaps(map, FILES)
.stream()
.map(FileBean.CONFIGURATOR::buildFromMap)
.collect(Collectors.toList()));
var ymlMeta = map.getOrDefault(FileUtils.YML_META, null);
//raw files node in yml
var filesMeta = Optional
.ofNullable(ymlMeta)
.filter(yml -> yml instanceof MappingNode)
.map(MappingNode.class::cast)
.map(MappingNode::getValue)
.flatMap(values -> values.stream()
.filter(v -> v.getKeyNode() instanceof ScalarNode)
.filter(v -> ScalarNode.class.cast(v.getKeyNode()).getValue().equals(FILES))
.map(NodeTuple::getValueNode)
.filter(v -> v instanceof SequenceNode)
.map(SequenceNode.class::cast)
.map(SequenceNode::getValue)
.findFirst()
)
.orElse(null);

List<FileBean> fileBeans = new ArrayList<>();
List<Map<String, Object>> listOfMaps = PropertiesBuilder.getListOfMaps(map, FILES);

for (int i = 0; i < listOfMaps.size(); i++) {
var fileMap = listOfMaps.get(i);
int finalI = i;
//raw languages node in yml
var languageMappingMeta = Optional.ofNullable(filesMeta)
.filter(l -> l.size() > finalI)
.map(l -> l.get(finalI))
.filter(n -> n instanceof MappingNode)
.map(MappingNode.class::cast)
.map(MappingNode::getValue)
.flatMap(values-> values.stream()
.filter(v -> v.getKeyNode() instanceof ScalarNode)
.filter(v -> ScalarNode.class.cast(v.getKeyNode()).getValue().equals(LANGUAGES_MAPPING))
.map(NodeTuple::getValueNode)
.filter(n -> n instanceof MappingNode)
.map(MappingNode.class::cast)
.map(MappingNode::getValue)
.map(v -> {
Map<String, Map<String, String>> actualValues = new HashMap<>();
v.stream()
.filter(e -> e.getKeyNode() instanceof ScalarNode)
.filter(e -> e.getValueNode() instanceof MappingNode)
.forEach(e -> {
var key = ScalarNode.class.cast(e.getKeyNode()).getValue();
var val = MappingNode.class.cast(e.getValueNode());
Map<String, String> subMap = new HashMap<>();
val.getValue()
.stream()
.filter(e2 -> e2.getKeyNode() instanceof ScalarNode)
.filter(e2 -> e2.getValueNode() instanceof ScalarNode)
.forEach(e2 -> subMap.put(
ScalarNode.class.cast(e2.getKeyNode()).getValue(),
ScalarNode.class.cast(e2.getValueNode()).getValue()
));
actualValues.put(key, subMap);
});
return actualValues;
})
.findFirst()
);
languageMappingMeta.ifPresent(meta -> fileMap.put(FileBean.LANGUAGES_META, meta));
var fileBean = FileBean.CONFIGURATOR.buildFromMap(fileMap);
fileBeans.add(fileBean);
}
props.setFiles(fileBeans);

props.setPseudoLocalization(PseudoLocalization.CONFIGURATOR.buildFromMap(PropertiesBuilder.getMap(map, PSEUDO_LOCALIZATION)));
}

Expand Down Expand Up @@ -86,8 +152,8 @@ public PropertiesBuilder.Messages checkProperties(PropertiesWithFiles props, Che
Set<String> langCodes = languages.stream().map(lang -> lang.getId().toLowerCase()).collect(Collectors.toSet());

boolean hasInvalidCode = fileBean.getLanguagesMapping().values().stream()
.flatMap(innerMap -> innerMap.keySet().stream())
.anyMatch(langCode -> !langCodes.contains(langCode.toLowerCase()));
.flatMap(innerMap -> innerMap.keySet().stream())
.anyMatch(langCode -> !langCodes.contains(langCode.toLowerCase()));
if (hasInvalidCode) {
messages.addError(RESOURCE_BUNDLE.getString("error.config.languages_mapping"));
}
Expand Down
18 changes: 9 additions & 9 deletions src/main/java/com/crowdin/cli/utils/file/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@

import org.apache.commons.io.IOUtils;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -22,6 +16,8 @@ public class FileUtils {
private static final String YAML_EXTENSION = ".yaml";
private static final String YML_EXTENSION = ".yml";

public static final String YML_META = "__yml__meta";

public static Map<String, Object> readYamlFile(File fileCfg) {
if (fileCfg == null) {
throw new NullPointerException("FileReader.readCliConfig has null args");
Expand All @@ -31,8 +27,12 @@ public static Map<String, Object> readYamlFile(File fileCfg) {
}

Yaml yaml = new Yaml();
try (InputStream inputStream = new FileInputStream(fileCfg)) {
return yaml.load(inputStream);

try {
var content = Files.readString(fileCfg.toPath());
Map<String, Object> map = yaml.load(content);
map.put(YML_META, yaml.compose(new StringReader(content)));
return map;
} catch (FileNotFoundException e) {
throw new RuntimeException(RESOURCE_BUNDLE.getString("error.configuration_file_not_exist"));
} catch (Exception e) {
Expand Down

0 comments on commit 26501f8

Please sign in to comment.