package alien4cloud.tosca.parser; import java.util.Map.Entry; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.keyvalue.DefaultMapEntry; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.NotWritablePropertyException; import org.yaml.snakeyaml.nodes.Node; import alien4cloud.tosca.parser.impl.ErrorCode; import static org.aspectj.weaver.tools.cache.SimpleCacheFactory.path; /** * Abstract class to work with Type Node Parsing. */ @Slf4j @Getter public abstract class AbstractTypeNodeParser { private final String toscaType; public AbstractTypeNodeParser(String toscaType) { this.toscaType = toscaType; } protected void parseAndSetValue(BeanWrapper target, String key, Node valueNode, ParsingContextExecution context, MappingTarget mappingTarget) { // let's store the parent in the context for future use context.setParent(target.getWrappedInstance()); if (mappingTarget.getPath().equals("null")) { // if the path is null, we just to do nothing with the stuff return; } Entry<BeanWrapper, String> entry = findWrapperPropertyByPath(context.getRoot(), target, mappingTarget.getPath()); BeanWrapper realTarget = entry.getKey(); String propertyName = entry.getValue(); Object value = ((INodeParser<?>) mappingTarget.getParser()).parse(valueNode, context); ParsingContextExecution.setParent(target, value); if (!propertyName.equals("void")) { // property named 'void' means : process the parsing but do not set anything try { realTarget.setPropertyValue(propertyName, value); } catch (NotWritablePropertyException e) { log.warn("Error while setting property for yaml parsing.", e); context.getParsingErrors().add(new ParsingError(ParsingErrorLevel.WARNING, ErrorCode.ALIEN_MAPPING_ERROR, "Invalid definition for type", valueNode.getStartMark(), "", valueNode.getEndMark(), toscaType)); } } if (mappingTarget instanceof KeyValueMappingTarget) { KeyValueMappingTarget kvmt = (KeyValueMappingTarget) mappingTarget; BeanWrapper keyBeanWrapper = realTarget; try { if (!(keyBeanWrapper.getPropertyValue(kvmt.getKeyPath()) != null && propertyName.equals(key))) { keyBeanWrapper.setPropertyValue(kvmt.getKeyPath(), key); } } catch (NotWritablePropertyException e) { log.warn("Error while setting key to property for yaml parsing.", e); context.getParsingErrors().add(new ParsingError(ParsingErrorLevel.WARNING, ErrorCode.ALIEN_MAPPING_ERROR, "Invalid definition for type", valueNode.getStartMark(), "", valueNode.getEndMark(), toscaType)); } } } /** * For example: * <ul> * <li>.something : the value will be set to the property of root named 'something' * <li>child1.child2.prop : the value will be mapped u getChild1().getChild2().setProp() * </ul> */ private Entry<BeanWrapper, String> findWrapperPropertyByPath(BeanWrapper root, BeanWrapper current, String path) { int dotIdx = path.indexOf("."); if (dotIdx < 0) { return new DefaultMapEntry<BeanWrapper, String>(current, path); } BeanWrapper base = current; String nextPath = path; if (path.startsWith("../")) { base = new BeanWrapperImpl(ParsingContextExecution.getParent(current)); nextPath = path.substring(3); } else if (path.startsWith(".")) { base = root; nextPath = path.substring(1); } else { String wrapperCandidateName = path.substring(0, path.indexOf(".")); Object wrapperCandidate = current.getPropertyValue(wrapperCandidateName); base = new BeanWrapperImpl(wrapperCandidate); nextPath = path.substring(path.indexOf(".") + 1); } return findWrapperPropertyByPath(root, base, nextPath); } }