package alien4cloud.tosca.parser.impl.base;
import java.util.Collection;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
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.tosca.parser.INodeParser;
import alien4cloud.tosca.parser.ParserUtils;
import alien4cloud.tosca.parser.ParsingContextExecution;
import lombok.AllArgsConstructor;
import lombok.Setter;
@AllArgsConstructor(suppressConstructorProperties = true)
@Setter
public abstract class CollectionParser<T> implements INodeParser<Collection<T>> {
private INodeParser<T> valueParser;
/** The tosca type of the list. */
private String toscaType;
/** In case the list is created from a map, optional value to inject the key into the value object. */
private String keyPath;
@Override
public Collection<T> parse(Node node, ParsingContextExecution context) {
if (node instanceof MappingNode) {
return doParseFromMap((MappingNode) node, context);
} else if (node instanceof SequenceNode) {
return doParse((SequenceNode) node, context);
} else if (node instanceof ScalarNode) {
// single value in the list
return doParse((ScalarNode) node, context);
}
ParserUtils.addTypeError(node, context.getParsingErrors(), toscaType);
return null;
}
private Collection<T> doParse(ScalarNode node, ParsingContextExecution context) {
Collection<T> collection = getCollectionInstance();
T value = valueParser.parse(node, context);
if (value != null) {
collection.add(value);
}
return collection;
}
private Collection<T> doParse(SequenceNode node, ParsingContextExecution context) {
Collection<T> collection = getCollectionInstance();
Object parent = context.getParent();
for (Node valueNode : node.getValue()) {
T value;
if (keyPath != null && valueNode instanceof MappingNode && ((MappingNode) valueNode).getValue().size() == 1) {
NodeTuple tuple = ((MappingNode) valueNode).getValue().get(0);
value = objectFromTuple(tuple, context);
} else {
value = valueParser.parse(valueNode, context);
}
if (value != null) {
context.setParent(parent, value);
collection.add(value);
}
context.setParent(parent);
}
return collection;
}
private Collection<T> doParseFromMap(MappingNode node, ParsingContextExecution context) {
Collection<T> collection = getCollectionInstance();
if (keyPath != null) { // we parse a map into a list and must
for (NodeTuple entry : node.getValue()) {
T value = objectFromTuple(entry, context);
if (value != null) {
collection.add(value);
}
}
} else { // we parse a list with a single value
T value = valueParser.parse(node, context);
collection.add(value);
}
return collection;
}
private T objectFromTuple(NodeTuple tuple, ParsingContextExecution context) {
String key = ParserUtils.getScalar(tuple.getKeyNode(), context);
T value;
value = valueParser.parse(tuple.getValueNode(), context);
if (value != null) {
BeanWrapper valueWrapper = new BeanWrapperImpl(value);
valueWrapper.setPropertyValue(keyPath, key);
}
return value;
}
protected abstract Collection<T> getCollectionInstance();
}