/
block.py
291 lines (222 loc) · 9.7 KB
/
block.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#------------------------------------------------------------------------------
#
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# Version 2, December 2004
#
# Copyright (C) 2013 Electronic Dreams <maverick.babylon.drifter@gmail.com>
#
# Everyone is permitted to copy and distribute verbatim or modified
# copies of this license document, and changing it is allowed as long
# as the name is changed.
#
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
#
# 0. You just DO WHAT THE FUCK YOU WANT TO.
#
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# block.py - Base class for blocks found in Massive files.
#------------------------------------------------------------------------------
import re
from scanf import sscanf
from scanf import IncompleteCaptureError
#------------------------------------------------------------------------------
# class Block
#------------------------------------------------------------------------------
class Block(object):
"""Base class for blocks of data in massive files.
"""
#--------------------------------------------------------------------------
# initialization
#--------------------------------------------------------------------------
def __init__(self):
pass
#--------------------------------------------------------------------------
# helper methods
#--------------------------------------------------------------------------
def _removeIndent(self, block, count=1):
"""Removes 4 space indents from the block.
"""
return re.compile(r"^%s" % " " * count, re.M).sub("", block)
#--------------------------------------------------------------------------
def _addIndent(self, block, count=1):
"""Adds 4 space indents to the block.
"""
return re.compile(r"^((?!$))", re.M).sub(" " * count, block)
#--------------------------------------------------------------------------
# attribute handler methods
#--------------------------------------------------------------------------
def _createAttributeFormattingMap(self, scanf_list, reformat=True):
"""Convert the scanf formatting list into a hash for easy lookup.
If multiple entrys are found for an attribute they are placed into a
list in the order found. Order of entries are also returned.
"""
order = []
scanf_map = {}
for entry in scanf_list:
# grab attribute
attribute = re.split('\s', entry)[0]
# add to order
if attribute.startswith('_') or (not attribute in order):
order.append(attribute)
# reformat entry since sscanf doesn't support %g
if reformat:
entry = entry.replace('%g', '%f')
# make format entry into list if multiple formats exist
if attribute in scanf_map:
formats = scanf_map[attribute]
if not isinstance(formats, list):
scanf_map[attribute] = [formats]
scanf_map[attribute].append(entry)
else:
scanf_map[attribute] = entry
return scanf_map, order
#--------------------------------------------------------------------------
def _setAttribute(self, attribute, value):
"""Sets the value for the given attribute.
If the attribute already exists, the attribute will be converted into
a list to accomadate multiple values.
"""
# if multiple values found
if hasattr(self, attribute):
# make sure attribute is a list
values = getattr(self, attribute)
if not isinstance(values, list):
setattr(self, attribute, [values])
# append value to list
getattr(self, attribute).append(value)
# single value found
else:
setattr(self, attribute, value)
#--------------------------------------------------------------------------
def _parseAttributeString(self, line):
"""Value after the attribute is treated as a single string.
"""
attribute, value = line.partition(' ')[::2]
self._setAttribute(attribute, value)
#--------------------------------------------------------------------------
def _parseAttributeScanf(self, line, formatting):
"""Use scanf to parse the data from the line.
Formatting can be a single entry or a list of entrys. If multiple
entrys are available the first matching entry will be returned.
"""
# multiple entrys
if isinstance(formatting, list):
for scanf_format in formatting:
try:
#print "<<<----", scanf_format, line
return sscanf(line, scanf_format)
except IncompleteCaptureError, e:
pass
# single entry
else:
return sscanf(line, formatting)
# problem if none of the formats worked
raise IncompleteCaptureError("Format error for %s" % line)
#--------------------------------------------------------------------------
def parseAttributes(self, block, scanf_list, special_list={}, skip_list=[]):
"""Parses block for attributes using the formatting.
"""
# remove trailing newlines
block = block.strip('\n')
# create a hash of the attributes for easy lookup
scanf_map, order = self._createAttributeFormattingMap(scanf_list)
# loop over the block line by line
index = 0
rest = []
lines = block.split('\n')
while index < len(lines):
# grab line and increment
line = lines[index]
index += 1
# gather up indented child lines
children = []
while (index < len(lines)) and re.match('^(?:\t| )', lines[index]):
children.append(lines[index])
index += 1
# add children to line
children.insert(0, line)
line = "\n".join(children)
# use proper seperator to grab the attribute name
attribute = re.split('\s', line)[0]
# skip attribute
if attribute in skip_list:
#print "skip_list-> ", attribute, line
rest.append(line)
# use special formatter
elif attribute in special_list:
#print "special_list-> ", attribute, line
special_list[attribute](line)
# use scanf formatter
elif attribute in scanf_map:
#print "scanf-> ", attribute, line
value = self._parseAttributeScanf(line, scanf_map[attribute])
# remove from tuple if single value
if len(value) == 1:
value = value[0]
# set attribute
self._setAttribute(attribute, value)
# attribute not found
else:
#print "rest-> ", attribute, line
rest.append(line)
# add default entires for missing attibutes
for attribute in scanf_map.keys():
if not hasattr(self, attribute):
setattr(self, attribute, None)
# return unused lines
return "\n".join(rest) + "\n"
#--------------------------------------------------------------------------
def _printAttributePrintf(self, formatting, value):
"""Use printf (%) to export the data from the line.
Formatting can be a single entry or a list of entrys. If multiple
entrys are available the first matching entry will be returned.
"""
# multiple entrys
if isinstance(formatting, list):
for scanf_format in formatting:
try:
#print "-->>", scanf_format, value
return scanf_format % value
except TypeError, e:
pass
# single entry
else:
return formatting % value
# problem if none of the formats worked
raise TypeError("Valid format not found for values.")
#--------------------------------------------------------------------------
def printAttributes(self, scanf_list, special_list={}):
"""Prints out all of the attributes in the list.
"""
# create a hash of the attributes for easy lookup
scanf_map, order = self._createAttributeFormattingMap(scanf_list, False)
# print out all of the attributes
block = ""
for attribute in order:
# seperator
if attribute == "_seperator_":
block += '\n'
continue
# use special formatter
if attribute in special_list:
special_block = special_list[attribute]()
if special_block != '':
block += special_block + '\n'
continue
# only process attributes that exist on the object
if not hasattr(self, attribute):
continue
# skip empty attributes
value = getattr(self, attribute)
if value == None:
continue
# add attribute to block
formatting = scanf_map[attribute]
if isinstance(value, list):
for entry in value:
block += self._printAttributePrintf(formatting, entry) + "\n"
else:
block += self._printAttributePrintf(formatting, value) + "\n"
return block