def _delete_value(self, root, lines, node): """Delete value and return start possition deleted value""" l1, c1, l2, c2 = node.span.start.line-1, node.span.start.column-1, \ node.span.end.line-1, node.span.end.column-1 l1, c1 = StructureChanger.skip_tar(lines, l1, c1, l2, c2) StructureChanger.delete_structure(lines, l1, c1, l2, c2) return l1, c1
def _add_key(self, root, lines, action): """Add key to abstract record""" try: node = root.get_node_at_path(action['parameters']['path']) except: return False l1, c1, l2, c2 = StructureChanger.node_pos(node) if node.implementation != DataNode.Implementation.mapping: raise TransformationFileFormatError( "Add path (" + self._get_paths_str(action, 'destination_path') + ") must be abstract record") key = action['parameters']['key'] if "value" in action['parameters']: value = action['parameters']['value'] else: value = None if "type" in action['parameters']: tag = action['parameters']['type'] else: tag = None if node.parent is None: add = StructureChanger.add_key(key, 0, value, tag) lines.insert(l1, add) else: indentation = re.search(r'^(\s*)(\S.*)$', lines[l1]) indentation = len(indentation.group(1)) + 2 add = StructureChanger.add_key(key, indentation, value, tag) lines.insert(l1 + 1, add) return True
def _rename_type(self, root, lines, action): """Rename type transformation""" try: node = root.get_node_at_path(action['parameters']['path']) except: return False old = action['parameters']['old_name'] new = action['parameters']['new_name'] StructureChanger.change_tag(lines, node, old, new) return True
def _change_value(self, root, lines, action): """Change value to speciffied""" try: node = root.get_node_at_path(action['parameters']['path']) except: return False if node.implementation != DataNode.Implementation.scalar: self._add_notification( "Specified path '{0}', is not scalar type node, action is ignored" .format(self._get_paths_str(action, 'path')), self._Severity.warning, action, node) return False old = action['parameters']['old_value'] new = action['parameters']['new_value'] l1, c1, l2, c2 = StructureChanger.value_pos(node) return StructureChanger.replace(lines, new, old, l1, c1, l2, c2)
def _replace_value(self, root, lines, action): """Rename type transformation""" try: node = root.get_node_at_path(action['parameters']['path']) except: return False if node.implementation != DataNode.Implementation.scalar: self._add_notification( "Specified path '{0}', is not scalar type node, action is ignored" .format(self._get_paths_str(action, 'path')), self._Severity.warning, action, node) return False pattern = action['parameters']['pattern'] replacement = action['parameters']['replacement'] old = str(node.value) new = re.sub(pattern, replacement, old) if new != old: l1, c1, l2, c2 = StructureChanger.value_pos(node) return StructureChanger.replace(lines, new, old, l1, c1, l2, c2) return False
def _add_array(self, root, lines, action): """Move key transformation""" try: parent2 = root.get_node_at_path( action['parameters']['addition_path']) except: return False if parent2.implementation != DataNode.Implementation.sequence: raise TransformationFileFormatError( "Specified addition path (" + self._get_paths_str(action, 'addition_path') + \ ") is not array type node." ) if parent2.parent is None: raise TransformationFileFormatError( "Specified addition path can't be in root node") try: parent1 = root.get_node_at_path( action['parameters']['source_path']) except: return False if parent1.implementation != DataNode.Implementation.sequence: raise TransformationFileFormatError( "Specified source path (" + self._get_paths_str(action, 'path') + \ ") is not array type node." ) if parent2 == parent1: raise TransformationFileFormatError( "Source and addition paths must be different (" + self._get_paths_str(action, 'source_path') + ")") sl1, sc1, sl2, sc2 = StructureChanger.value_pos(parent1) sl2, sc2 = self._fix_end(lines, sl1, sc1, sl2, sc2) al1, ac1, al2, ac2 = StructureChanger.value_pos(parent2) al2, ac2 = self._fix_end(lines, al1, ac1, al2, ac2) if (al1 >= sl1 and sl2 >= al2) or (sl1 >= al1 and al2 >= sl2): raise TransformationFileFormatError( "Source and addition nodes can't contains each other (" + self._get_paths_str(action, 'source_path') + ")") indentation1 = StructureChanger.get_indentation(lines, sl1) al1, ac1, al2, ac2 = StructureChanger.add_comments( lines, al1, ac1, al2, ac2) add = StructureChanger.copy_structure(lines, al1, ac1, al2, ac2, indentation1, False) if sl2 < al1 or (sl2 == al1 and sl2 < ac1): # addition after source, first delete action['parameters']['path'] = action['parameters'][ 'addition_path'] action['parameters']['deep'] = True self._delete_key(root, lines, action) self._add_comma(lines, sl1, sl2, sc2) StructureChanger.paste_structure(lines, sl2, add, True) if not (sl2 < al1 or (sl2 == al1 and sl2 < ac1)): action['parameters']['path'] = action['parameters'][ 'addition_path'] action['parameters']['deep'] = True self._delete_key(root, lines, action) return True
def _delete_key(self, root, lines, action): """Delete key transformation""" try: node = root.get_node_at_path(action['parameters']['path']) except: return False if "deep" not in action[ 'parameters'] or not action['parameters']['deep']: if len(node.children_keys) > 0: return False l1, c1, l2, c2 = StructureChanger.node_pos(node) anchors = [] for i in range(l1, l2 + 1): if l1 == l2: text = lines[l1][c1:c2] elif i == l1: text = lines[l1][c1:] elif i == l2: text = lines[l2][:c2] else: text = lines[i] anchor = re.search(r'\s+&\s*(\S+)\s+', text) if anchor is None: anchor = re.search(r'\s+&\s*(\S+)$', text) if anchor is not None: anchors.append(anchor.group(1)) if len(anchors) > 0: self._rewrite_first_ref(root, lines, anchors) # try delete with comma after or prev nl2, nc2 = self._next_spread(lines, l2, c2) if nl2 == l2 and nc2 == c2: l1, c1 = self._prev_spred(lines, l1, c1) else: l2 = nl2 c2 = nc2 # try exclude comments l1, c1, l2, c2 = StructureChanger.leave_comments(lines, l1, c1, l2, c2) l1, c1, l2, c2 = StructureChanger.add_delete_item_chars( lines, l1, c1, l2, c2) StructureChanger.delete_structure(lines, l1, c1, l2, c2) return True
def _copy_tree_from_anchor(self, lines, anchor_node, ref_node, remove_anchor=False): """Copy tree structure from anchor node to node with reference""" l1, c1, l2, c2 = anchor_node.span.start.line-1, anchor_node.span.start.column-1, \ anchor_node.span.end.line-1, anchor_node.span.end.column-1 l1, c1 = StructureChanger.skip_tar(lines, l1, c1, l2, c2) hlpl1, hlpc1, l2, c2 = StructureChanger.add_comments( lines, l1, c1, l2, c2) dl1, dc1, dl2, dc2 = StructureChanger.node_pos(ref_node) intend = re.search(r'^(\s*)(\S.*)$', lines[dl1]) intend = len(intend.group(1)) + 2 add = StructureChanger.copy_structure(lines, l1, c1, l2, c2, intend) while dl1 <= dl2: ref = re.search(r'^(.*\*' + anchor_node.anchor.value + r')(.*)$', lines[dl1]) if ref is not None: anchor = "&" + anchor_node.anchor.value if remove_anchor: anchor = "" lines[dl1] = re.sub(r'\*' + anchor_node.anchor.value + r"\s+", anchor + " ", lines[dl1]) lines[dl1] = re.sub(r'\*' + anchor_node.anchor.value + r"$", anchor, lines[dl1]) if not ref.group(2).isspace() and len(ref.group(2)) > 0: lines.insert(dl1 + 1, ref.group(2)) lines[dl1] = ref.group(1) StructureChanger.paste_structure(lines, dl1, add, True, True) break dl1 += 1
def _scale_value(self, root, lines, action): """Multiply value by set scale""" try: node = root.get_node_at_path(action['parameters']['path']) scale = float(action['parameters']['scale']) except: return False if node.implementation != DataNode.Implementation.scalar: self._add_notification( "Specified path '{0}', is not scalar type node, action is ignored" .format(self._get_paths_str(action, 'path')), self._Severity.warning, action, node) return False try: value = float(node.value) except ValueError: self._add_notification( "Type of value in specified path '{0}', is not numeric, action is ignored" .format(self._get_paths_str(action, 'path')), self._Severity.warning, action, node) return False l1, c1, l2, c2 = StructureChanger.value_pos(node) return StructureChanger.replace(lines, str(scale * value), lines[l1][c1:c2], l1, c1, l2, c2)
def _move_key_forward(self, root, lines, action): """Move key forward""" try: parent = re.search(r'^(.*)/([^/]*)$', action['parameters']['path']) node = root.get_node_at_path(action['parameters']['path']) if parent is None: raise TransformationFileFormatError( "Cannot find parent path for path (" + self._get_paths_str(action, 'path') + ")") is_root = False if len(parent.group(1)) == 0: parent_node = root is_root = True else: parent_node = root.get_node_at_path(parent.group(1)) except: return False l1, c1, l2, c2 = StructureChanger.node_pos(node) pl1, pc1, pl2, pc2 = parent_node.span.start.line-1, parent_node.span.start.column-1, \ parent_node.span.end.line-1, parent_node.span.end.column-1 if parent_node.implementation != DataNode.Implementation.mapping: raise TransformationFileFormatError( "Parent of path (" + self._get_paths_str(action, 'path') + ") must be abstract record") if is_root: indentation1 = 0 pl1 = 0 pc1 = 0 else: indentation1 = StructureChanger.get_indentation(lines, pl1) + 2 l1, c1, l2, c2 = StructureChanger.add_comments(lines, l1, c1, l2, c2) add = StructureChanger.copy_structure(lines, l1, c1, l2, c2, indentation1) pl1, pc1 = StructureChanger.skip_tar(lines, pl1, pc1, pl2, pc2) action['parameters']['deep'] = True self._delete_key(root, lines, action) StructureChanger.paste_structure(lines, pl1, add, pc1 != 0) return True
def _move_key(self, root, lines, action): """Move key transformation""" try: parent1 = root.get_node_at_path( action['parameters']['destination_path']) raise TransformationFileFormatError( "Destination path (" + self._get_paths_str(action, 'destination_path') + ") already exist") except: pass try: parent1 = re.search(r'^(.*)/([^/]*)$', action['parameters']['source_path']) node1 = root.get_node_at_path(action['parameters']['source_path']) except: return False try: parent2 = re.search(r'^(.*)/([^/]*)$', action['parameters']['destination_path']) new_node = parent2.group(2) node2 = root.get_node_at_path(parent2.group(1)) node_struct = [] except: if action['parameters']['create_path'] and parent2 is not None: node_struct, new_path = StructureChanger.copy_absent_path( root, lines, parent2.group(1)) if len(node_struct) == 0: raise TransformationFileFormatError( "Can't constract destination path (" + self._get_paths_str(action, 'destination_path') + ")") try: new_node = parent2.group(2) parent2 = re.search(r'^(.*)/([^/]*)$', new_path + "/" + new_node) node2 = root.get_node_at_path(parent2.group(1)) except: raise TransformationFileFormatError( "Constracted path error(" + self._get_paths_str(action, 'destination_path') + ")") else: raise TransformationFileFormatError( "Parent of destination path (" + self._get_paths_str(action, 'destination_path') + ") must exist") sl1, sc1, sl2, sc2 = StructureChanger.node_pos(node1) dl1, dc1, dl2, dc2 = StructureChanger.node_pos(node2) if parent1.group(1) == parent2.group(1) and len(node_struct) == 0 and \ not action['parameters']['keep-source']: # rename i = node1.key.span.start.line - 1 lines[i] = re.sub( parent1.group(2) + r"\s*:", parent2.group(2) + ":", lines[i]) return True if not action['parameters']['create_path']: # check only existing path if node2.implementation != DataNode.Implementation.mapping: raise TransformationFileFormatError( "Parent of destination path (" + self._get_paths_str(action, 'destination_path') + ") must be abstract record") if node1.parent is None: indentation1 = 0 else: indentation1 = StructureChanger.get_indentation(lines, dl1) + 2 sl1, sc1, sl2, sc2 = StructureChanger.add_comments( lines, sl1, sc1, sl2, sc2) add = StructureChanger.copy_structure(lines, sl1, sc1, sl2, sc2, indentation1) # rename key i = node1.key.span.start.line - sl1 - 1 add[i] = re.sub(parent1.group(2) + r"\s*:", new_node + ":", add[i]) if action['parameters']['create_path'] and len(node_struct) > 0: add = StructureChanger.paste_absent_path(add, node_struct) if sl1 < dl1 and sl2 > dl1: raise TransformationFileFormatError( "Destination block (" + self._get_paths_str(action, 'destination_path') + ") and source block (" + self._get_paths_str(action, 'source_path') + " is overlapped") if sl1 < dl2 or action['parameters']['keep-source']: # source before dest, first copy indentation2 = re.search(r'^(\s*)(\S.*)$', lines[dl2]) StructureChanger.paste_structure(lines, dl2, add, len(indentation2.group(1)) < dc2) action['parameters']['path'] = action['parameters']['source_path'] action['parameters']['deep'] = True if not action['parameters']['keep-source']: self._delete_key(root, lines, action) else: # source after dest, first delete action['parameters']['path'] = action['parameters']['source_path'] action['parameters']['deep'] = True self._delete_key(root, lines, action) indentation2 = re.search(r'^(\s*)(\S.*)$', lines[dl2]) StructureChanger.paste_structure(lines, dl2, add, len(indentation2.group(1)) < dc2) return True
def transform(self, yaml, cfg): """transform yaml file""" self.err = [] notification_handler = NotificationHandler() loader = Loader(notification_handler) root = loader.load(yaml) lines = yaml.splitlines(False) changes = False text = cfg.get_curr_format_text() root_input_type = get_root_input_type_from_json(text) for aaction in self._transformation['actions']: if changes: root, lines = self.refresh(root_input_type, yaml, notification_handler, loader) changes = False for action in self._replace_wildchars(root, aaction): if changes: root, lines = self.refresh(root_input_type, yaml, notification_handler, loader) if action['action'] == "delete-key": changes = self._delete_key(root, lines, action) elif action['action'] == "move-key": changes = self._move_key(root, lines, action) if changes and "set_type_path" in action['parameters']: yaml = "\n".join(lines) root, lines = self.refresh(root_input_type, yaml, notification_handler, loader) changes = False try: node = root.get_node_at_path( action['parameters']['set_type_path']) if node.type is not None: StructureChanger.change_tag( lines, node, node.type.value, action['parameters']['new_type'].strip()) else: StructureChanger.add_tag( lines, node, action['parameters']['new_type'].strip()) changes = True except: pass elif action['action'] == "rename-type": changes = self._rename_type(root, lines, action) elif action['action'] == "move-key-forward": changes = self._move_key_forward(root, lines, action) elif action['action'] == "change-value": changes = self._change_value(root, lines, action) elif action['action'] == "replace-value": changes = self._replace_value(root, lines, action) elif action['action'] == "merge-arrays": changes = self._add_array(root, lines, action) if 'destination_path' in action['parameters']: if changes: yaml = "\n".join(lines) root, lines = self.refresh(root_input_type, yaml, notification_handler, loader) action['parameters']['keep-source'] = False changes = self._move_key(root, lines, action) elif action['action'] == "add-key": changes = self._add_key(root, lines, action) elif action['action'] == "scale-value": changes = self._scale_value(root, lines, action) if changes: yaml = "\n".join(lines) yaml = "\n".join(lines) return yaml