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