blob: 9f90edc01a86492ab543d59e34d64f890b639545 [file] [log] [blame]
Dusan Klinec3880d232014-11-10 19:01:13 +01001#!/usr/bin/env python2
2"""
3Adds ObjectiveC prefixes to the Protocol Buffers entities.
4
5@author Ph4r05
6"""
7
8import sys
9import re
10import plyproto.parser
11import plyproto.model as m
12import argparse
13import traceback
14import os.path
15
16class MyVisitor(m.Visitor):
17 content=""
18 offset=0
19 statementsChanged=0
20 prefix=""
21
22 def prefixize(self, lu, oldId):
23 '''
24 Simple frefixization - with constant prefix to all identifiers (flat).
25 :param lu:
26 :return:
27 '''
28 return self.replace(lu, self.prefix + str(oldId))
29
30 def replace(self, lu, newCode):
31 '''
32 Replaces given LU string occurrence with the new one. Modifies local state.
33 :param lu:
34 :param newCode:
35 :return:
36 '''
37 if not hasattr(lu, "lexspan"):
38 raise Exception("LU does not implement lexspan, %s" % lu)
39 if lu.lexspan == None:
40 raise Exception("LU has None lexspan, %s" % lu)
41
42 # Computing code positions/lengths.
43 constCodePart=""
44 oldCodeLen=lu.lexspan[1] - (lu.lexspan[0]-len(constCodePart))
45 codeStart=self.offset+lu.lexspan[0]-1-len(constCodePart)
46 codeEnd=self.offset+lu.lexspan[1]-1
47
48 # Change the content, replace with the new version.
49 newCodeLen = len(newCode)
50 newContent = self.content[:codeStart] + newCode + self.content[codeEnd:]
51 self.content = newContent
52 self.offset += newCodeLen - oldCodeLen
53 self.statementsChanged+=1
54
55 def __init__(self):
56 super(MyVisitor, self).__init__()
57
58 self.first_field = True
59 self.first_method = True
60
61 def visit_PackageStatement(self, obj):
62 '''Ignore'''
63 return True
64
65 def visit_ImportStatement(self, obj):
66 '''Ignore'''
67 return True
68
69 def visit_OptionStatement(self, obj):
70 '''Ignore'''
71 return True
72
73 def visit_FieldDirective(self, obj):
74 '''Ignore, Field directive, e.g., default value.'''
75 return True
76
77 def visit_FieldType(self, obj):
78 '''Field type, if type is name, then it may need refactoring consistent with refactoring rules according to the table'''
79 return True
80
81 def visit_FieldDefinition(self, obj):
82 '''New field defined in a message, check type, if is name, prefixize.'''
83 if self.verbose > 3:
84 print "\tField: name=%s, lex=%s" % (obj.name, obj.lexspan)
85
86 if isinstance(obj.ftype, m.Name):
87 self.prefixize(obj.ftype, obj.ftype.value)
88
89 return True
90
91 def visit_EnumFieldDefinition(self, obj):
92 if self.verbose > 3:
93 print "\tEnumField: name=%s, %s" % (obj.name, obj)
94
95 return True
96
97 def visit_EnumDefinition(self, obj):
98 '''New enum definition, refactor name'''
99 if self.verbose > 3:
100 print "Enum, [%s] body=%s\n\n" % (obj.name, obj.body)
101
102 self.prefixize(obj.name, obj.name.value)
103 return True
104
105 def visit_MessageDefinition(self, obj):
106 '''New message, refactor name, w.r.t. path'''
107 if self.verbose > 3:
108 print "Message, [%s] lex=%s body=|%s|\n" % (obj.name, obj.lexspan, obj.body)
109
110 self.prefixize(obj.name, str(obj.name.value))
111 return True
112
113 def visit_MessageExtension(self, obj):
114 '''New message extension, refactor'''
115 if self.verbose > 3:
116 print "MessageEXT, [%s] body=%s\n\n" % (obj.name, obj.body)
117
118 self.prefixize(obj.name, obj.name.value)
119 return True
120
121 def visit_MethodDefinition(self, obj):
122 return True
123
124 def visit_ServiceDefinition(self, obj):
125 return True
126
127 def visit_ExtensionsDirective(self, obj):
128 return True
129
130 def visit_Literal(self, obj):
131 return True
132
133 def visit_Name(self, obj):
134 return True
135
136 def visit_Proto(self, obj):
137 return True
138
139# Main executable code
140if __name__ == '__main__':
141 parser = argparse.ArgumentParser(description='Log statements formating string converter.', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
142 parser.add_argument('-i','--in-place', help='Overwrites provided file with the new content', required=False, default=False, dest='inplace')
143 parser.add_argument('-p','--prefix', help='Constant prefix to be prepended to the entities found', required=False, default="", dest='prefix')
144 parser.add_argument('-o','--outdir', help='Output directory', required=False, default="", dest='outdir')
145 parser.add_argument('-e','--echo', help='Writes output to the standard output', required=False, default=False)
146 parser.add_argument('-v','--verbose', help='Writes output to the standard output', required=False, default=0, type=int)
147 parser.add_argument('file')
148 args = parser.parse_args()
149
150 # Load the file and instantiate the visitor object.
151 p = plyproto.parser.ProtobufAnalyzer()
152 if args.verbose>0:
153 print " [-] Processing file: %s" % (args.file)
154
155 # Start the parsing.
156 try:
157 v = MyVisitor()
158 v.offset = 0
159 v.prefix = args.prefix
160 with open(args.file, 'r') as content_file:
161 v.content = content_file.read()
162
163 tree = p.parse_file(args.file)
164 tree.accept(v)
165
166 # If here, probably no exception occurred.
167 if args.echo:
168 print v.content
169 if args.outdir != None and len(args.outdir)>0 and v.statementsChanged>0:
170 outfile = args.outdir + '/' + v.prefix + os.path.basename(args.file).capitalize()
171 with open(outfile, 'w') as f:
172 f.write(v.content)
173 if args.inplace and v.statementsChanged>0:
174 with open(args.file, 'w') as f:
175 f.write(v.content)
176
177 if args.verbose>0:
178 print " [-] Processing finished, changed=%d" % v.statementsChanged
179 except Exception as e:
180 print " Error occurred! file[%s]" % (args.file), e
181 if args.verbose>1:
182 print '-'*60
183 traceback.print_exc(file=sys.stdout)
184 print '-'*60
185 sys.exit(1)
186