package alien4cloud.tosca.parser.impl.advanced; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Resource; import org.elasticsearch.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; } }