1 #!/usr/bin/env python |
|
2 # coding: utf-8 |
|
3 |
|
4 ''' |
|
5 Provides facilities to converting identifier cases. |
|
6 ''' |
|
7 |
|
8 # |
|
9 # Copyright 2015 Teemu Piippo |
|
10 # All rights reserved. |
|
11 # |
|
12 # Redistribution and use in source and binary forms, with or without |
|
13 # modification, are permitted provided that the following conditions |
|
14 # are met: |
|
15 # |
|
16 # 1. Redistributions of source code must retain the above copyright |
|
17 # notice, this list of conditions and the following disclaimer. |
|
18 # 2. Redistributions in binary form must reproduce the above copyright |
|
19 # notice, this list of conditions and the following disclaimer in the |
|
20 # documentation and/or other materials provided with the distribution. |
|
21 # 3. Neither the name of the copyright holder nor the names of its |
|
22 # contributors may be used to endorse or promote products derived from |
|
23 # this software without specific prior written permission. |
|
24 # |
|
25 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
26 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
|
27 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
|
28 # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER |
|
29 # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
30 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
31 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
32 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
33 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
34 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
35 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
36 # |
|
37 |
|
38 import string |
|
39 import re |
|
40 |
|
41 def to_one_case (name, tolower): |
|
42 ''' |
|
43 Convers name to either all uppercase or all lowercase (depending on the truth value of tolower), using underscores |
|
44 as delimiters. |
|
45 ''' |
|
46 result = "" |
|
47 targetSet = (string.ascii_lowercase if tolower else string.ascii_uppercase) + string.digits |
|
48 inverseSet = (string.ascii_uppercase if tolower else string.ascii_lowercase) |
|
49 isUnderscorable = lambda ch: ch in string.ascii_uppercase \ |
|
50 or ch in string.whitespace or ch in string.punctuation |
|
51 previousWasUnderscorable = isUnderscorable (name[0]) |
|
52 |
|
53 for ch in name: |
|
54 if isUnderscorable (ch) and result != "" and not previousWasUnderscorable: |
|
55 result += "_" |
|
56 |
|
57 if ch in inverseSet: |
|
58 result += (ch.lower() if tolower else ch.upper()) |
|
59 elif ch in targetSet: |
|
60 result += ch |
|
61 previousWasUnderscorable = isUnderscorable (ch) |
|
62 |
|
63 return result |
|
64 |
|
65 def to_camel_case (name, java = False): |
|
66 ''' |
|
67 Converts name to camelcase. If java is False, the first letter will be lowercase, otherwise it will be uppercase. |
|
68 ''' |
|
69 result = "" |
|
70 wantUpperCase = False |
|
71 |
|
72 # If it's all uppercase, make it all lowercase so that this algorithm can digest it |
|
73 if name == name.upper(): |
|
74 name = name.lower() |
|
75 |
|
76 for ch in name: |
|
77 if ch == '_': |
|
78 wantUpperCase = True |
|
79 continue |
|
80 |
|
81 if wantUpperCase: |
|
82 if ch in string.ascii_lowercase: |
|
83 ch = ch.upper() |
|
84 wantUpperCase = False |
|
85 |
|
86 result += ch |
|
87 |
|
88 if java: |
|
89 match = re.match (r'^([A-Z]+)([A-Z].+)$', result) |
|
90 if match: |
|
91 result = match.group (1).lower() + match.group (2) |
|
92 else: |
|
93 result = result[0].lower() + result[1:] |
|
94 else: |
|
95 result = result[0].upper() + result[1:] |
|
96 return result |
|
97 |
|
98 case_styles = { |
|
99 'lower': lambda name: to_one_case (name, tolower=True), |
|
100 'upper': lambda name: to_one_case (name, tolower=False), |
|
101 'camel': lambda name: to_camel_case (name, java=False), |
|
102 'java': lambda name: to_camel_case (name, java=True), |
|
103 } |
|
104 |
|
105 def convert_case (name, style): |
|
106 ''' |
|
107 Converts name to the given style. Style may be one of: |
|
108 - 'lower': 'foo_barBaz' -> 'foo_bar_baz' |
|
109 - 'upper': 'foo_barBaz' -> 'FOO_BAR_BAZ' |
|
110 - 'camel': 'foo_barBaz' -> 'FooBarBaz' |
|
111 - 'java': 'foo_barBaz' -> 'fooBarBaz' |
|
112 ''' |
|
113 try: |
|
114 stylefunc = case_styles[style] |
|
115 except KeyError: |
|
116 validstyles = ', '.join (sorted (case_styles.keys())) |
|
117 raise ValueError ('Unknown style "%s", should be one of: %s' % (style, validstyles)) |
|
118 else: |
|
119 return stylefunc (name) |
|