/* * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.jenkins.plugins.dsl.util; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Collections; import java.util.List; import java.util.Map; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.emitter.ScalarAnalysis; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.representer.Represent; import org.yaml.snakeyaml.representer.Representer; import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; /** * This class is basically a no-op, since Yaml 1.2 is a superset * of JSON; however, this scrubs some of the excess quoting and * scoping away, so that it looks more like Yaml and less like JSON. */ public abstract class JsonToYaml { /** * This method is the workhorse method that implementations provide for * translating json into yaml. */ public abstract String toYaml(InputStream inputStream); /** * @see #toYaml(InputStream) */ public final String toYaml(String input) { return toYaml(new ByteArrayInputStream(input.getBytes(UTF_8))); } /** * A basic implementation that parses and dumps the yaml using Snake Yaml. */ public static class Default extends JsonToYaml { public Default() { this(ImmutableList.<YamlTransform>of()); } public Default(List<YamlTransform> transforms) { this.transforms = checkNotNull(transforms); } /** {@inheritDoc} */ @Override public String toYaml(InputStream inputStream) { DumperOptions options = new DumperOptions() { /** Force usage of PLAIN style */ @Override public DumperOptions.ScalarStyle calculateScalarStyle( ScalarAnalysis analysis, DumperOptions.ScalarStyle style) { return DumperOptions.ScalarStyle.PLAIN; } }; options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); // NOTE: This is inadequate, thus the above hack. options.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN); final Yaml yaml = new Yaml(new CustomRepresenter(transforms), options); return yaml.dump(yaml.load(inputStream)); } private final List<YamlTransform> transforms; } private static class CustomRepresenter extends Representer { public CustomRepresenter(List<YamlTransform> transforms) { this.representers.put(String.class, new RepresentTransforms(transforms)); } private class RepresentTransforms implements Represent { public RepresentTransforms(final List<YamlTransform> inputTransforms) { final Map<String, YamlTransform> transforms = Maps.newHashMap(); final Map<String, Class> classes = Maps.newHashMap(); for (YamlTransform xform : inputTransforms) { for (Class clazz : xform.getClasses()) { final String text = xform.construct(xform.represent(clazz)); // The first transform "wins" if (classes.containsKey(text)) { continue; } transforms.put(text, xform); classes.put(text, clazz); } } // Store to the final fields as an immutable version this.transforms = Collections.unmodifiableMap(transforms); this.classes = Collections.unmodifiableMap(classes); } public Node representData(Object data) { final String element = (String) data; if (transforms.containsKey(element)) { final YamlTransform xform = transforms.get(element); final Class clazz = classes.get(element); return representScalar(new Tag(xform.getTag()), xform.represent(clazz)); } return representScalar(Tag.STR, element); } private final Map<String, YamlTransform> transforms; private final Map<String, Class> classes; } } }