com.atlassian.bamboo.specs.util.Yamlizator Maven / Gradle / Ivy
package com.atlassian.bamboo.specs.util;
import com.atlassian.bamboo.specs.api.model.AtlassianModuleProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.introspector.BeanAccess;
import org.yaml.snakeyaml.introspector.FieldProperty;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
public final class Yamlizator {
public static final String BAMBOO_SPECS_MAX_ALIASES_FOR_COLLECTIONS_PROPERTY = "bamboo.specs.max.aliases.for.collections";
public static final String BAMBOO_SPECS_CODE_POINTS_LIMIT_PROPERTY = "bamboo.specs.code.point.limit";
private static final DumperOptions BLOCK_STYLE_DUMPER;
private static final Field FIELD_FIELD;
private static final Comparator CLASS_SCOPED_ORDER = (o1, o2) -> {
final FieldProperty fp1 = asFieldProperty(o1);
final FieldProperty fp2 = asFieldProperty(o2);
if (fp1 == null && fp2 != null) {
return -1;
}
if (fp1 != null && fp2 == null) {
return 1;
}
if (fp1 == null) {
return 0;
}
final Class> class1 = getDeclaringClass(fp1);
final Class> class2 = getDeclaringClass(fp2);
if (!class1.equals(class2)) { //fields from parent class first
if (class2.isAssignableFrom(class1)) {
return 1;
} else if (class1.isAssignableFrom(class2)) {
return -1;
}
}
final int classCompare = class1.getName().compareTo(class2.getName());
if (classCompare != 0) {
return classCompare;
}
return fp1.getName().compareTo(fp2.getName());
};
static {
BLOCK_STYLE_DUMPER = new DumperOptions();
BLOCK_STYLE_DUMPER.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
BLOCK_STYLE_DUMPER.setExplicitStart(true);
BLOCK_STYLE_DUMPER.setExplicitEnd(true);
BLOCK_STYLE_DUMPER.setAllowReadOnlyProperties(true);
try {
FIELD_FIELD = FieldProperty.class.getDeclaredField("field");
FIELD_FIELD.setAccessible(true);
} catch (final NoSuchFieldException | SecurityException e) {
throw new RuntimeException(e);
}
}
private Yamlizator() {
}
public static Yaml getYaml() {
LoaderOptions loadingConfig = getLoaderOptions();
return getYamlInternal(new BambooYamlConstructor(loadingConfig), loadingConfig);
}
public static Yaml getYamlWithRepositoryIncludes(int maxDepth, final Path yamlDirectory) {
return getYamlWithRepositoryIncludes(maxDepth, 0, yamlDirectory);
}
static Yaml getYamlWithRepositoryIncludes(int maxDepth, int depth, final Path yamlDirectory) {
LoaderOptions loadingConfig = getLoaderOptions();
return getYamlInternal(getConstructorWithIncludes(maxDepth, depth, yamlDirectory, loadingConfig), loadingConfig);
}
@NotNull
private static Yaml getYamlInternal(@NotNull SafeConstructor constructor, LoaderOptions loadingConfig) {
final Yaml yaml = new Yaml(constructor, getRepresenter(), BLOCK_STYLE_DUMPER, loadingConfig);
yaml.setBeanAccess(BeanAccess.FIELD);
return yaml;
}
@NotNull
private static LoaderOptions getLoaderOptions() {
LoaderOptions loadingConfig = new LoaderOptions();
final int maxAliasesForCollections = Integer.parseInt(System.getProperty(BAMBOO_SPECS_MAX_ALIASES_FOR_COLLECTIONS_PROPERTY, "1500"));
loadingConfig.setMaxAliasesForCollections(maxAliasesForCollections);
loadingConfig.setAllowDuplicateKeys(false);
final int maxCodePointLimit = Integer.parseInt(System.getProperty(BAMBOO_SPECS_CODE_POINTS_LIMIT_PROPERTY, "6291456" /*twice bigger then default*/));
loadingConfig.setCodePointLimit(maxCodePointLimit);
loadingConfig.setTagInspector(new BambooSpecsTagInspector());
return loadingConfig;
}
private static SafeConstructor getConstructorWithIncludes(final int maxDepth, final int depth, final Path yamlDirectory, LoaderOptions loadingConfig) {
return new BambooYamlWithIncludesConstructor(maxDepth, depth, yamlDirectory, loadingConfig);
}
@NotNull
private static Representer getRepresenter() {
return new Representer(BLOCK_STYLE_DUMPER) {
{
for (final CustomYamlers.CustomYamler customYamler : CustomYamlers.YAMLERS) {
this.representers.put(customYamler.getYamledClass(), customYamler.getRepresenter());
}
}
@Override
protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) {
Object newPropertyValue = propertyValue;
if (Collections.emptyList().equals(propertyValue)) {
newPropertyValue = new ArrayList<>(0);
} else if (propertyValue instanceof AtlassianModuleProperties) {
AtlassianModuleProperties atlassianModuleProperties = (AtlassianModuleProperties) propertyValue;
newPropertyValue = new AtlassianModuleProperties(atlassianModuleProperties.getCompleteModuleKey());
}
return super.representJavaBeanProperty(javaBean, property, newPropertyValue, customTag);
}
@Override
protected Set getProperties(final Class extends Object> type) {
final Set properties = getPropertyUtils().getProperties(type);
return properties.stream()
.sorted(CLASS_SCOPED_ORDER)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
};
}
@Nullable
private static FieldProperty asFieldProperty(final Property property) {
if (property instanceof FieldProperty) {
return (FieldProperty) property;
}
return null;
}
@NotNull
private static Class> getDeclaringClass(final FieldProperty fieldProperty) {
try {
return ((Field) FIELD_FIELD.get(fieldProperty)).getDeclaringClass();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}