content: package alien4cloud.tosca.parser.impl.advanced;
  
  import java.util.Map;
  import java.util.Map.Entry;
  
  import javax.annotation.Resource;
  
  import com.google.common.collect.Maps;
  import org.springframework.stereotype.Component;
  import org.yaml.snakeyaml.nodes.MappingNode;
  import org.yaml.snakeyaml.nodes.Node;
  import org.yaml.snakeyaml.nodes.ScalarNode;
  
  import org.alien4cloud.tosca.model.templates.AbstractPolicy;
  import org.alien4cloud.tosca.model.templates.GenericPolicy;
  import org.alien4cloud.tosca.model.templates.HaPolicy;
  import org.alien4cloud.tosca.model.templates.LocationPlacementPolicy;
  import alien4cloud.tosca.parser.*;
  import alien4cloud.tosca.parser.impl.ErrorCode;
  import alien4cloud.tosca.parser.impl.base.ScalarParser;
  
  @Component
  public class GroupPolicyParser implements INodeParser<AbstractPolicy> {
  
      @Resource
      private ScalarParser scalarParser;
  
      public static final String NAME = "name";
      public static final String TYPE = "type";
      public static final String VALUE = "value";
  
      private static final Map<String, Class<? extends AbstractPolicy>> POLICY_TYPES = Maps.newLinkedHashMap();
  
      static {
          POLICY_TYPES.put(HaPolicy.HA_POLICY, HaPolicy.class);
      }
  
      @Override
      public AbstractPolicy parse(Node node, ParsingContextExecution context) {
          if (node instanceof ScalarNode) {
              // Spec at A.8.1.5.1 says it is a "list of names of policies"
              // though the examples treat it as maps, and in some cases it seems the type might be specified;
              // accept all syntaxes for now
              Map<String, Object> nodeMap = Maps.newHashMap();
              String name = scalarParser.parse(node, context);
              if (POLICY_TYPES.containsKey(name)) {
                  nodeMap.put(TYPE, name);
              } else {
                  nodeMap.put(NAME, name);
              }
              return buildPolicy(nodeMap, node, context);
          }
  
          if (!(node instanceof MappingNode)) {
              // we expect a MappingNode
              context.getParsingErrors().add(new ParsingError(ErrorCode.YAML_MAPPING_NODE_EXPECTED, null, node.getStartMark(), null, node.getEndMark(), null));
              return null;
          }
          Map<String, Object> nodeMap = ParserUtils.parseMap((MappingNode) node);
  
          Object nameO = (Object) nodeMap.get(NAME);
          Object typeO = (Object) nodeMap.get(TYPE);
  
          if (nodeMap.size() == 1 && nameO == null && typeO == null) {
              // short notation '<key>: <value>' where (in priority order)
              // - <value> is a map and <key> matches a known pre-defined type, then <value> is a map of data passed to the type
              // - <value> matches a known pre-defined type, then <key> is taken as a name
              // - else taken as a generic policy with name <key>, with <value> set as the map (if it's a map) or as a `value` in the map (if it's not a map)
              Entry<String, Object> e = nodeMap.entrySet().iterator().next();
  
              nodeMap.clear();
              if (e.getValue() instanceof Map) {
                  @SuppressWarnings("unchecked")
                  Map<String, Object> v = (Map<String, Object>) e.getValue();
                  nodeMap.putAll(v);
                  if (POLICY_TYPES.containsKey(e.getKey())) {
                      nodeMap.put(TYPE, e.getKey());
                  } else {
                      nodeMap.put(NAME, e.getKey());
                  }
              } else if (e.getValue() instanceof CharSequence) {
                  if (POLICY_TYPES.containsKey(e.getValue())) {
                      nodeMap.put(TYPE, e.getValue().toString());
                      nodeMap.put(NAME, e.getKey());
                  } else {
                      nodeMap.put(NAME, e.getKey());
                      nodeMap.put(VALUE, e.getValue().toString());
                  }
              }
          } else {
              if (!(nameO instanceof String)) {
                  context.getParsingErrors().add(new ParsingError(ParsingErrorLevel.ERROR, ErrorCode.SYNTAX_ERROR, null, node.getStartMark(), null,
                          node.getEndMark(), nameO.toString()));
                  return null;
              }
              if (!(typeO instanceof String)) {
                  context.getParsingErrors().add(new ParsingError(ParsingErrorLevel.ERROR, ErrorCode.SYNTAX_ERROR, null, node.getStartMark(), null,
                          node.getEndMark(), nameO.toString()));
                  return null;
              }
          }
  
          return buildPolicy(nodeMap, node, context);
      }
  
      private AbstractPolicy buildPolicy(Map<String, Object> nodeMap, Node node, ParsingContextExecution context) {
          String type = (String) nodeMap.get(TYPE);
          AbstractPolicy result = null;
  
          if (type != null) {
              switch (type) {
              case HaPolicy.HA_POLICY:
  
                  result = new HaPolicy(nodeMap);
                  break;
              case LocationPlacementPolicy.LOCATION_PLACEMENT_POLICY:
                  Object locationO = nodeMap.get(LocationPlacementPolicy.LOCATION_ID_PROPERTY);
                  if (locationO instanceof String) {
                      result = new LocationPlacementPolicy();
                  } else {
                      context.getParsingErrors().add(new ParsingError(ParsingErrorLevel.ERROR, ErrorCode.SYNTAX_ERROR, null, node.getStartMark(),
                              "Location id should be a string.", node.getEndMark(), locationO.toString()));
                      return null;
                  }
                  break;
              }
          }
          if (result == null) {
              result = new GenericPolicy(nodeMap);
          }
          return result;
      }
  
  }