package alien4cloud.tosca.parser.postprocess;
import static alien4cloud.utils.AlienUtils.safe;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.alien4cloud.tosca.model.types.DataType;
import alien4cloud.tosca.normative.ToscaType;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.nodes.Node;
import org.alien4cloud.tosca.model.types.AbstractInheritableToscaType;
import alien4cloud.model.components.IndexedModelUtils;
import org.alien4cloud.tosca.model.types.PrimitiveDataType;
import alien4cloud.tosca.context.ToscaContext;
import alien4cloud.tosca.parser.ParsingContextExecution;
import alien4cloud.tosca.parser.ParsingError;
import alien4cloud.tosca.parser.impl.ErrorCode;
import lombok.extern.slf4j.Slf4j;
/**
* Derived from post processor checks that the defined parent
*/
@Slf4j
@Component
public class DerivedFromPostProcessor implements IPostProcessor<Map<String, ? extends AbstractInheritableToscaType>> {
@Override
public void process(Map<String, ? extends AbstractInheritableToscaType> instances) {
// Detect cyclic derived from
Map<AbstractInheritableToscaType, String> processed = new IdentityHashMap<>();
Map<AbstractInheritableToscaType, String> processing = new IdentityHashMap<>();
// Then process to get the list of derived from and merge instances
for (AbstractInheritableToscaType instance : safe(instances).values()) {
process(processed, processing, instance, instances);
}
}
private void process(Map<AbstractInheritableToscaType, String> processed, Map<AbstractInheritableToscaType, String> processing,
AbstractInheritableToscaType instance, Map<String, ? extends AbstractInheritableToscaType> instances) {
if (processed.containsKey(instance)) {
// Already processed
return;
}
if (processing.containsKey(instance)) {
// Cyclic dependency as parent is currently being processed...
Node node = ParsingContextExecution.getObjectToNodeMap().get(instance);
ParsingContextExecution.getParsingErrors()
.add(new ParsingError(ErrorCode.CYCLIC_DERIVED_FROM, "Cyclic derived from has been detected", node.getStartMark(),
"The type specified as parent or one of it's parent type refers the current type as parent, invalid cycle detected.",
node.getEndMark(), instance.getElementId()));
processing.remove(instance);
return;
}
List<String> derivedFrom = instance.getDerivedFrom();
if (derivedFrom == null || derivedFrom.isEmpty() || derivedFrom.size() > 1) {
// Either the type has no parents, either it has been already processed.
return;
}
String parentElementType = derivedFrom.get(0);
// Merge the type with it's parent except for primitive data types.
if (instance instanceof DataType && ToscaType.isSimple(parentElementType)) {
if (instance instanceof PrimitiveDataType) {
log.debug("Do not merge data type instance with parent as it extends from a primitive type.");
} else {
Node node = ParsingContextExecution.getObjectToNodeMap().get(instance);
// type has not been parsed as primitive because it has some properties
ParsingContextExecution.getParsingErrors()
.add(new ParsingError(ErrorCode.SYNTAX_ERROR, "Primitive types cannot define properties.", node.getStartMark(),
"The defined type inherit from a primitive type but defines some properties.", node.getEndMark(), parentElementType));
}
return;
}
AbstractInheritableToscaType parent = instances.get(parentElementType);
if (parent == null) {
parent = ToscaContext.get(instance.getClass(), parentElementType);
} else {
// first process the parent type
processing.put(instance, null);
process(processed, processing, parent, instances);
processing.remove(instance);
}
if (parent == null) {
Node node = ParsingContextExecution.getObjectToNodeMap().get(derivedFrom);
ParsingContextExecution.getParsingErrors().add(new ParsingError(ErrorCode.TYPE_NOT_FOUND, "Derived_from type not found", node.getStartMark(),
"The type specified as parent is not found neither in the archive or its dependencies.", node.getEndMark(), parentElementType));
return;
}
// Merge with parent type
IndexedModelUtils.mergeInheritableIndex(parent, instance);
processed.put(instance, null);
}
}