content: package alien4cloud.tosca.parser;
  
  import java.util.List;
  import java.util.Map;
  
  import org.apache.commons.lang3.ArrayUtils;
  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 alien4cloud.exception.InvalidArgumentException;
  import alien4cloud.tosca.parser.impl.ErrorCode;
  
  import com.google.common.collect.Lists;
  import com.google.common.collect.Maps;
  
  /**
   * Utility class to help with parsing.
   */
  public final class ParserUtils {
  
      /**
       * Utility to get a scalar.
       * 
       * @param node The node from which to get a scalar value.
       * @param context The parsing execution context in which to add errors in which to add errors in case the node is not a scalar node.
       * @return The Scalar value or null if the node is not a scalar node.
       */
      public static String getScalar(Node node, ParsingContextExecution context) {
          if (node instanceof ScalarNode) {
              return ((ScalarNode) node).getValue().trim();
          }
          addTypeError(node, context.getParsingErrors(), "scalar");
          return null;
      }
  
      /**
       * Build a map while parsing a {@link MappingNode} assuming that all tuples are scalars (both key and value). Other entries are ignored but warned.
       * 
       * @param ignoredKeys ignore these keys (no warning for them !)
       */
      public static Map<String, String> parseStringMap(MappingNode mappingNode, ParsingContextExecution context, String... ignoredKeys) {
          Map<String, String> result = Maps.newHashMap();
          List<NodeTuple> mappingNodeValues = mappingNode.getValue();
          for (NodeTuple entry : mappingNodeValues) {
              if (!(entry.getKeyNode() instanceof ScalarNode)) {
                  ParsingError err = new ParsingError(ParsingErrorLevel.WARNING, ErrorCode.UNRECOGNIZED_PROPERTY, "Parsing a MappingNode as a Map", entry
                          .getKeyNode().getStartMark(), "The key of this tuple should be a scalar", entry.getKeyNode().getEndMark(), "");
                  context.getParsingErrors().add(err);
                  continue;
              }
              if (!(entry.getValueNode() instanceof ScalarNode)) {
                  if (!ArrayUtils.contains(ignoredKeys, getScalar(entry.getKeyNode(), context))) {
                      ParsingError err = new ParsingError(ParsingErrorLevel.WARNING, ErrorCode.UNRECOGNIZED_PROPERTY, "Parsing a MappingNode as a Map", entry
                              .getKeyNode().getStartMark(), "The value of this tuple should be a scalar", entry.getValueNode().getEndMark(),
                              ((ScalarNode) entry.getKeyNode()).getValue());
                      context.getParsingErrors().add(err);
                  }
                  continue;
              }
              // ok both key and value are scalar
              String k = ((ScalarNode) entry.getKeyNode()).getValue();
              String v = ((ScalarNode) entry.getValueNode()).getValue();
              result.put(k, v);
          }
          return result;
      }
  
      public static Object parse(Node node) {
          if (node == null) {
              return null;
          } else if (node instanceof ScalarNode) {
              return ((ScalarNode) node).getValue();
          } else if (node instanceof SequenceNode) {
              return parseSequence((SequenceNode) node);
          } else if (node instanceof MappingNode) {
              return parseMap((MappingNode) node);
          } else {
              throw new InvalidArgumentException("Unknown type of node " + node.getClass().getName());
          }
      }
  
      public static List<Object> parseSequence(SequenceNode sequenceNode) {
          List<Object> result = Lists.newArrayList();
          for (Node node : sequenceNode.getValue()) {
              result.add(parse(node));
          }
          return result;
      }
  
      public static Object parseComplex(Node node, ParsingContextExecution context) {
          if (node == null) {
              return null;
          } else if (node instanceof ScalarNode) {
              return ((ScalarNode) node).getValue();
          } else if (node instanceof SequenceNode) {
              return parseComplexSequence((SequenceNode) node,context);
          } else if (node instanceof MappingNode) {
              INodeParser<Object> parser = ParsingContextExecution.getRegistry().get("complex_node_template_property");
              if (parser != null) {
                  return parser.parse(node, context);
              } else {
                  return  parseComplexMap((MappingNode) node,context);
              }
          } else {
              throw new InvalidArgumentException("Unknown type of node " + node.getClass().getName());
          }
      }
  
      public static List<Object> parseComplexSequence(SequenceNode sequenceNode, ParsingContextExecution context) {
          List<Object> result = Lists.newArrayList();
          for (Node node : sequenceNode.getValue()) {
              result.add(parseComplex(node,context));
          }
          return result;
      }
  
      public static Map<String, Object> parseComplexMap(MappingNode mappingNode, ParsingContextExecution context) {
          Map<String, Object> result = Maps.newHashMap();
          for (NodeTuple entry : mappingNode.getValue()) {
              String key = ((ScalarNode) entry.getKeyNode()).getValue();
              result.put(key, parseComplex(entry.getValueNode(),context));
          }
          return result;
      }
  
      public static Map<String, Object> parseMap(MappingNode mappingNode) {
          Map<String, Object> result = Maps.newHashMap();
          for (NodeTuple entry : mappingNode.getValue()) {
              String key = ((ScalarNode) entry.getKeyNode()).getValue();
              result.put(key, parse(entry.getValueNode()));
          }
          return result;
      }
  
      /**
       * Add an invalid type {@link ParsingError} to the given parsing errors list.
       * 
       * @param node The node that is causing the type error.
       * @param parsingErrors The parsing errors in which to add the error.
       * @param expectedType The type that was actually expected.
       */
      public static void addTypeError(Node node, List<ParsingError> parsingErrors, String expectedType) {
          parsingErrors.add(new ParsingError(ErrorCode.SYNTAX_ERROR, "Invalid type syntax", node.getStartMark(), "Expected the type to match tosca type", node
                  .getEndMark(), expectedType));
      }
  }