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;
}
}