content: package alien4cloud.tosca.parser.impl.base;
  
  import java.util.Map;
  import java.util.Set;
  
  import org.springframework.beans.factory.config.ConfigurableBeanFactory;
  import org.springframework.context.annotation.Scope;
  import org.springframework.stereotype.Component;
  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 com.google.common.collect.Maps;
  import com.google.common.collect.Sets;
  
  import alien4cloud.tosca.parser.INodeParser;
  import alien4cloud.tosca.parser.ParsingContextExecution;
  import alien4cloud.tosca.parser.ParsingError;
  import alien4cloud.tosca.parser.impl.ErrorCode;
  
  /**
   * Map using a child parser based on a discriminator key (valid only for MappingNode).
   */
  @Component
  @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  public class KeyDiscriminatorParser<T> implements INodeParser<T> {
      private static final String MAPPING_NODE_FALLBACK_KEY = "__";
  
      private Map<String, INodeParser<T>> parserByExistKey;
      private INodeParser<T> fallbackParser;
  
      /**
       * Create a new key discriminator parser instance.
       * 
       * @param parserByExistKey A map of existing keys to the parser to use in case the key exists.
       * @param fallbackParser The parser to use if none of the key is actually found or if the node type is not a MappingNode.
       */
      public KeyDiscriminatorParser(Map<String, INodeParser<T>> parserByExistKey, INodeParser<T> fallbackParser) {
          if (parserByExistKey == null) {
              this.parserByExistKey = Maps.newLinkedHashMap();
          } else {
              this.parserByExistKey = parserByExistKey;
          }
          this.fallbackParser = fallbackParser;
      }
  
      @Override
      public T parse(Node node, ParsingContextExecution context) {
          Set<String> keySet = Sets.newHashSet();
          if (node instanceof MappingNode) {
              // create a set of available keys
              MappingNode mappingNode = (MappingNode) node;
              for (NodeTuple tuple : mappingNode.getValue()) {
                  keySet.add(((ScalarNode) tuple.getKeyNode()).getValue());
              }
              INodeParser<T> mappingNodeFallbackParser = null;
              // check if one of the discriminator key exists and if so use it for parsing.
              for (Map.Entry<String, INodeParser<T>> entry : parserByExistKey.entrySet()) {
                  if (keySet.contains(entry.getKey())) {
                      return entry.getValue().parse(node, context);
                  } else if (MAPPING_NODE_FALLBACK_KEY.equals(entry.getKey())) {
                      mappingNodeFallbackParser = entry.getValue();
                  }
              }
  
              // if not we should use the mapping node fallback parser.
              if (mappingNodeFallbackParser != null) {
                  return mappingNodeFallbackParser.parse(node, context);
              }
          }
          if (fallbackParser != null) {
              return fallbackParser.parse(node, context);
          } else {
              context.getParsingErrors().add(new ParsingError(ErrorCode.UNKNWON_DISCRIMINATOR_KEY, "Invalid scalar value.", node.getStartMark(),
                      "Tosca type cannot be expressed with the given scalar value.", node.getEndMark(), keySet.toString()));
          }
          return null;
      }
  }