content: /*
   * Copyright 1999-2018 Alibaba Group Holding Ltd.
   *
   * 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.alibaba.nacos.config.server.utils;
  
  import com.alibaba.nacos.api.exception.NacosException;
  import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
  import com.alibaba.nacos.common.utils.CollectionUtils;
  import com.alibaba.nacos.config.server.model.ConfigMetadata;
  import org.yaml.snakeyaml.DumperOptions;
  import org.yaml.snakeyaml.LoaderOptions;
  import org.yaml.snakeyaml.constructor.AbstractConstruct;
  import org.yaml.snakeyaml.constructor.SafeConstructor;
  import org.yaml.snakeyaml.introspector.Property;
  import org.yaml.snakeyaml.Yaml;
  import org.yaml.snakeyaml.nodes.MappingNode;
  import org.yaml.snakeyaml.nodes.Node;
  import org.yaml.snakeyaml.nodes.NodeTuple;
  import org.yaml.snakeyaml.nodes.ScalarNode;
  import org.yaml.snakeyaml.nodes.SequenceNode;
  import org.yaml.snakeyaml.nodes.Tag;
  import org.yaml.snakeyaml.representer.Representer;
  
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.stream.Collectors;
  
  /**
   * YamlParserUtil.
   *
   * @author Nacos
   */
  public class YamlParserUtil {
      
      /**
       * Serialize a Java object into a YAML string.
       *
       * @param object Java object.
       * @return YAML string.
       */
      public static String dumpObject(Object object) {
          return new Yaml(new YamlParserConstructor(), new CustomRepresenter()).dumpAsMap(object);
      }
      
      /**
       * Parse YAML String and produce the corresponding Java object (Standard Java classes and in YamlParserConstructor
       * specified Construct).
       *
       * @param content YAML String
       * @param type    Java object.
       * @param <T>     Java object type.
       * @return Java object.
       */
      public static <T> T loadObject(String content, Class<T> type) {
          return new Yaml(new YamlParserConstructor(), new CustomRepresenter()).loadAs(content, type);
      }
      
      public static class YamlParserConstructor extends SafeConstructor {
          
          public static Tag configMetadataTag = new Tag(ConfigMetadata.class);
          
          public YamlParserConstructor() {
              super(new LoaderOptions());
              yamlConstructors.put(configMetadataTag, new ConstructYamlConfigMetadata());
          }
      }
      
      public static class CustomRepresenter extends Representer {
      
          public CustomRepresenter() {
              super(new DumperOptions());
          }
      
          @Override
          protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue,
                  Tag customTag) {
              if (propertyValue == null) {
                  return null;
              } else {
                  return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
              }
          }
      }
      
      public static class ConstructYamlConfigMetadata extends AbstractConstruct {
          
          @Override
          public Object construct(Node node) {
              if (!YamlParserConstructor.configMetadataTag.getValue().equals(node.getTag().getValue())) {
                  throw new NacosRuntimeException(NacosException.INVALID_PARAM,
                          "could not determine a constructor for the tag " + node.getTag() + node.getStartMark());
              }
              
              MappingNode mNode = (MappingNode) node;
              List<NodeTuple> value = mNode.getValue();
              if (CollectionUtils.isEmpty(value)) {
                  return null;
              }
              NodeTuple nodeTuple = value.get(0);
              ConfigMetadata configMetadata = new ConfigMetadata();
              SequenceNode sequenceNode = (SequenceNode) nodeTuple.getValueNode();
              if (CollectionUtils.isEmpty(sequenceNode.getValue())) {
                  return configMetadata;
              }
              
              List<ConfigMetadata.ConfigExportItem> exportItems = sequenceNode.getValue().stream().map(itemValue -> {
                  ConfigMetadata.ConfigExportItem configExportItem = new ConfigMetadata.ConfigExportItem();
                  MappingNode itemMap = (MappingNode) itemValue;
                  List<NodeTuple> propertyValues = itemMap.getValue();
                  Map<String, String> metadataMap = new HashMap<>(propertyValues.size());
                  propertyValues.forEach(metadata -> {
                      ScalarNode keyNode = (ScalarNode) metadata.getKeyNode();
                      ScalarNode valueNode = (ScalarNode) metadata.getValueNode();
                      metadataMap.put(keyNode.getValue(), valueNode.getValue());
                  });
                  configExportItem.setDataId(metadataMap.get("dataId"));
                  configExportItem.setGroup(metadataMap.get("group"));
                  configExportItem.setType(metadataMap.get("type"));
                  configExportItem.setDesc(metadataMap.get("desc"));
                  configExportItem.setAppName(metadataMap.get("appName"));
                  configExportItem.setConfigTags(metadataMap.get("configTags"));
                  return configExportItem;
              }).collect(Collectors.toList());
              
              configMetadata.setMetadata(exportItems);
              return configMetadata;
          }
      }
      
  }
  