# # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # # # Python parser for Subversion skels import string, re def parse(s): if s[0] != '(' and s[-1] != ')': raise ValueError("Improperly bounded skel: '%s'" % s) wholeskel = s s = s[1:-1].lstrip() prev_accums = [] accum = [] while True: if len(s) == 0: return accum if s[0] in string.digits: split_tuple = s.split(' ',1) count = int(split_tuple[0]) if len(split_tuple) > 1: s = split_tuple[1] else: s = "" accum.append(s[:count]) s = s[count:].lstrip() continue if s[0] in string.ascii_letters: i = 0 while (s[i] not in ' ()'): i += 1 if i == len(s): break accum.append(s[:i]) s = s[i:].lstrip() continue if s[0] == '(': new_accum = [] accum.append(new_accum) prev_accums.append(accum) accum = new_accum s = s[1:].lstrip() continue if s[0] == ')': accum = prev_accums.pop() s = s[1:].lstrip() continue if s[0] == ' ': s = str.lstrip(' ') continue raise ValueError("Unexpected contents in skel: '%s'\n'%s'" % (s, wholeskel)) _ok_implicit = re.compile(r'^[A-Za-z]([^\(\) \r\n\t\f]*)$') def unparse(struc): accum = [] for ent in struc: if isinstance(ent, str): if len(ent) > 0 and _ok_implicit.match(ent[0]): accum.append(ent) else: accum.append(str(len(ent))) accum.append(ent) else: accum.append(unparse(ent)) return "("+" ".join(accum)+")" class Rev: def __init__(self, skelstring="(revision null)"): sk = parse(skelstring) if len(sk) == 2 and sk[0] == "revision" and isinstance(sk[1], str): self.txn = sk[1] else: raise ValueError("Invalid revision skel: %s" % skelstring) def unparse(self): return unparse( ("revision", self.txn) ) class Change: def __init__(self, skelstring="(change null null null 0 0 )"): sk = parse(skelstring) if len(sk) == 6 and sk[0] == "change" and type(sk[1]) == type(sk[2]) \ == type(sk[3]) == type(sk[4]) == type(sk[5]) == str: self.path = sk[1] self.node = sk[2] self.kind = sk[3] self.textmod = sk[4] self.propmod = sk[5] else: raise ValueError("Invalid change skel: %s" % skelstring) def unparse(self): return unparse( ("change", self.path, self.node, self.kind, self.textmod and "1" or "", self.propmod and "1" or "") ) class Copy: def __init__(self, skelstring="(copy null null null)"): sk = parse(skelstring) if len(sk) == 4 and sk[0] in ("copy", "soft-copy") and type(sk[1]) \ == type(sk[2]) == type(sk[3]) == str: self.kind = sk[0] self.srcpath = sk[1] self.srctxn = sk[2] self.destnode = sk[3] else: raise ValueError("Invalid copy skel: %s" % skelstring) def unparse(self): return unparse( (self.kind, self.srcpath, self.srctxn, self.destnode) ) class Node: def __init__(self,skelstring="((file null null 1 0) null null)"): sk = parse(skelstring) if (len(sk) == 3 or (len(sk) == 4 and isinstance(sk[3], str))) \ and isinstance(sk[0], list) and isinstance(sk[1], str) \ and isinstance(sk[2], str) and sk[0][0] in ("file", "dir") \ and type(sk[0][1]) == type(sk[0][2]) == type(sk[0][3]) == str: self.kind = sk[0][0] self.createpath = sk[0][1] self.prednode = sk[0][2] self.predcount = int(sk[0][3]) self.proprep = sk[1] self.datarep = sk[2] if len(sk) > 3: self.editrep = sk[3] else: self.editrep = None else: raise ValueError("Invalid node skel: %s" % skelstring) def unparse(self): structure = [ (self.kind, self.createpath, self.prednode, str(self.predcount)), self.proprep, self.datarep ] if self.editrep: structure.append(self.editrep) return unparse( structure ) class Txn: def __init__(self,skelstring="(transaction null null () ())"): sk = parse(skelstring) if len(sk) == 5 and sk[0] in ("transaction", "committed", "dead") \ and type(sk[1]) == type(sk[2]) == str \ and type(sk[3]) == type(sk[4]) == list and len(sk[3]) % 2 == 0: self.kind = sk[0] self.rootnode = sk[1] if self.kind == "committed": self.rev = sk[2] else: self.basenode = sk[2] self.proplist = sk[3] self.copies = sk[4] else: raise ValueError("Invalid transaction skel: %s" % skelstring) def unparse(self): if self.kind == "committed": base_item = self.rev else: base_item = self.basenode return unparse( (self.kind, self.rootnode, base_item, self.proplist, self.copies) ) class SvnDiffWindow: def __init__(self, skelstructure): self.offset = skelstructure[0] self.svndiffver = skelstructure[1][0][1] self.str = skelstructure[1][0][2] self.size = skelstructure[1][1] self.vs_rep = skelstructure[1][2] def _unparse_structure(self): return ([ self.offset, [ [ 'svndiff', self.svndiffver, self.str ], self.size, self.vs_rep ] ]) class Rep: def __init__(self, skelstring="((fulltext 0 (md5 16 \0\0\0\0\0\0\0\0" \ "\0\0\0\0\0\0\0\0)) null)"): sk = parse(skelstring) if isinstance(sk[0], list) and len(sk[0]) == 3 \ and isinstance(sk[0][1], str) \ and isinstance(sk[0][2], list) and len(sk[0][2]) == 2 \ and type(sk[0][2][0]) == type(sk[0][2][1]) == str: self.kind = sk[0][0] self.txn = sk[0][1] self.cksumtype = sk[0][2][0] self.cksum = sk[0][2][1] if len(sk) == 2 and sk[0][0] == "fulltext": self.str = sk[1] elif len(sk) >= 2 and sk[0][0] == "delta": self.windows = list(map(SvnDiffWindow, sk[1:])) else: raise ValueError("Invalid representation skel: %s" % repr(skelstring)) def unparse(self): structure = [ [self.kind, self.txn, [self.cksumtype, self.cksum] ] ] if self.kind == "fulltext": structure.append(self.str) elif self.kind == "delta": for w in self.windows: structure.append(w._unparse_structure()) return unparse( structure )