1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2006-2007 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module contains classes to transform symbol names."""
23 from cvs2svn_lib
.log
import Log
24 from cvs2svn_lib
.common
import FatalError
25 from cvs2svn_lib
.common
import IllegalSVNPathError
26 from cvs2svn_lib
.common
import normalize_svn_path
29 class SymbolTransform
:
30 """Transform symbol names arbitrarily."""
32 def transform(self
, cvs_file
, symbol_name
, revision
):
33 """Possibly transform SYMBOL_NAME, which was found in CVS_FILE.
35 Return the transformed symbol name. If this SymbolTransform
36 doesn't apply, return the original SYMBOL_NAME. If this symbol
37 should be ignored entirely, return None. (Please note that
38 ignoring a branch via this mechanism only causes the branch *name*
39 to be ignored; the branch contents will still be converted.
40 Usually branches should be excluded using --exclude.)
42 REVISION contains the CVS revision number to which the symbol was
43 attached in the file as a string (with zeros removed).
45 This method is free to use the information in CVS_FILE (including
46 CVS_FILE.project) to decide whether and/or how to transform
49 raise NotImplementedError()
52 class ReplaceSubstringsSymbolTransform(SymbolTransform
):
53 """Replace specific substrings in symbol names.
55 If the substring occurs multiple times, replace all copies."""
57 def __init__(self
, old
, new
):
61 def transform(self
, cvs_file
, symbol_name
, revision
):
62 return symbol_name
.replace(self
.old
, self
.new
)
65 class NormalizePathsSymbolTransform(SymbolTransform
):
66 def transform(self
, cvs_file
, symbol_name
, revision
):
68 return normalize_svn_path(symbol_name
)
69 except IllegalSVNPathError
, e
:
70 raise FatalError('Problem with %s: %s' % (symbol_name
, e
,))
73 class CompoundSymbolTransform(SymbolTransform
):
74 """A SymbolTransform that applies other SymbolTransforms in series.
76 Each of the contained SymbolTransforms is applied, one after the
77 other. If any of them returns None, then None is returned (the
78 following SymbolTransforms are ignored)."""
80 def __init__(self
, symbol_transforms
):
81 """Ininitialize a CompoundSymbolTransform.
83 SYMBOL_TRANSFORMS is an iterable of SymbolTransform instances."""
85 self
.symbol_transforms
= list(symbol_transforms
)
87 def transform(self
, cvs_file
, symbol_name
, revision
):
88 for symbol_transform
in self
.symbol_transforms
:
89 symbol_name
= symbol_transform
.transform(
90 cvs_file
, symbol_name
, revision
92 if symbol_name
is None:
93 # Don't continue with other symbol transforms:
99 class RegexpSymbolTransform(SymbolTransform
):
100 """Transform symbols by using a regexp textual substitution."""
102 def __init__(self
, pattern
, replacement
):
103 """Create a SymbolTransform that transforms symbols matching PATTERN.
105 PATTERN is a regular expression that should match the whole symbol
106 name. REPLACEMENT is the replacement text, which may include
107 patterns like r'\1' or r'\g<1>' or r'\g<name>' (where 'name' is a
108 reference to a named substring in the pattern of the form
109 r'(?P<name>...)')."""
111 self
.pattern
= re
.compile('^' + pattern
+ '$')
112 self
.replacement
= replacement
114 def transform(self
, cvs_file
, symbol_name
, revision
):
115 return self
.pattern
.sub(self
.replacement
, symbol_name
)
118 class SymbolMapper(SymbolTransform
):
119 """A SymbolTransform that transforms specific symbol definitions.
121 The user has to specify the exact CVS filename, symbol name, and
122 revision number to be transformed, and the new name (or None if the
123 symbol should be ignored). The mappings can be set via a
124 constructor argument or by calling __setitem__()."""
126 def __init__(self
, items
=[]):
127 """Initialize the mapper.
129 ITEMS is a list of tuples (cvs_filename, symbol_name, revision,
130 new_name) which will be set as mappings."""
132 # A map {(cvs_filename, symbol_name, revision) : new_name}:
135 for (cvs_filename
, symbol_name
, revision
, new_name
) in items
:
136 self
[cvs_filename
, symbol_name
, revision
] = new_name
138 def __setitem__(self
, (cvs_filename
, symbol_name
, revision
), new_name
):
139 """Set a mapping for a particular file, symbol, and revision."""
141 key
= (cvs_filename
, symbol_name
, revision
)
144 'Overwriting symbol transform for\n'
145 ' filename=%r symbol=%s revision=%s'
146 % (cvs_filename
, symbol_name
, revision
,)
148 self
._map
[key
] = new_name
150 def transform(self
, cvs_file
, symbol_name
, revision
):
151 return self
._map
.get(
152 (cvs_file
.filename
, symbol_name
, revision
), symbol_name
156 class SubtreeSymbolMapper(SymbolTransform
):
157 """A SymbolTransform that transforms symbols within a whole repo subtree.
159 The user has to specify a CVS repository path (a filename or
160 directory) and the original symbol name. All symbols under that
161 path will be renamed to the specified new name (which can be None if
162 the symbol should be ignored). The mappings can be set via a
163 constructor argument or by calling __setitem__(). Only the most
164 specific rule is applied."""
166 def __init__(self
, items
=[]):
167 """Initialize the mapper.
169 ITEMS is a list of tuples (cvs_path, symbol_name, new_name)
170 which will be set as mappings. cvs_path is a string naming a
171 directory within the CVS repository."""
173 # A map {symbol_name : {cvs_path : new_name}}:
176 for (cvs_path
, symbol_name
, new_name
) in items
:
177 self
[cvs_path
, symbol_name
] = new_name
179 def __setitem__(self
, (cvs_path
, symbol_name
), new_name
):
180 """Set a mapping for a particular file and symbol."""
183 symbol_map
= self
._map
[symbol_name
]
186 self
._map
[symbol_name
] = symbol_map
188 if cvs_path
in symbol_map
:
190 'Overwriting symbol transform for\n'
191 ' directory=%r symbol=%s'
192 % (cvs_path
, symbol_name
,)
194 symbol_map
[cvs_path
] = new_name
196 def transform(self
, cvs_file
, symbol_name
, revision
):
198 symbol_map
= self
._map
[symbol_name
]
200 # No rules for that symbol name
203 cvs_path
= cvs_file
.filename
206 return symbol_map
[cvs_path
]
208 new_cvs_path
= os
.path
.dirname(cvs_path
)
209 if new_cvs_path
== cvs_path
:
210 # No rules found for that path; return symbol name unaltered.
213 cvs_path
= new_cvs_path
216 class IgnoreSymbolTransform(SymbolTransform
):
217 """Ignore symbols matching a specified regular expression."""
219 def __init__(self
, pattern
):
220 """Create an SymbolTransform that ignores symbols matching PATTERN.
222 PATTERN is a regular expression that should match the whole symbol
225 self
.pattern
= re
.compile('^' + pattern
+ '$')
227 def transform(self
, cvs_file
, symbol_name
, revision
):
228 if self
.pattern
.match(symbol_name
):