Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 405865 | Differences between
and this patch

Collapse All | Expand All

(-)file_not_specified_in_diff (-108 / +193 lines)
Line  Link Here
0
-- src/py/xml-rewrite-2.py
0
++ src/py/xml-rewrite-2.py
Lines 8-17 Link Here
8
# Authors:
8
# Authors:
9
#	Saleem Abdulrasool <compnerd@compnerd.org>
9
#	Saleem Abdulrasool <compnerd@compnerd.org>
10
#	Petteri Räty <betelgeuse@gentoo.org>
10
#	Petteri Räty <betelgeuse@gentoo.org>
11
#       Karsten Merkle <gentoo@karsten-merkle.de>
11
# Maintainer: Gentoo Java Herd <java@gentoo.org>
12
# Maintainer: Gentoo Java Herd <java@gentoo.org>
12
# Python based XML modifier
13
# Python based XML modifier
13
14
14
# ChangeLog
15
# ChangeLog
16
# Karsten Merkle <gentoo@karsten-merkle.de>
17
#         Februar 13, 2012 - elements can now be deleted.
18
#         Februar 19, 2012 - paths for matches can now be defined
15
# Petteri Räty <betelgeuse@gentoo.org
19
# Petteri Räty <betelgeuse@gentoo.org
16
#	   December 06, 2006 - Changed to use xml.parsers.expat and basically rewrote the whole file
20
#	   December 06, 2006 - Changed to use xml.parsers.expat and basically rewrote the whole file
17
#	   December 29, 2006 - Added a SAX based implementation to handle entities etc ( test on dev-java/skinlf )
21
#	   December 29, 2006 - Added a SAX based implementation to handle entities etc ( test on dev-java/skinlf )
Lines 54-75 Link Here
54
	be in StreamRewriterBase subclasses as they are much faster.
58
	be in StreamRewriterBase subclasses as they are much faster.
55
	"""
59
	"""
56
	from xml.dom import NotFoundErr
60
	from xml.dom import NotFoundErr
61
	import re
57
62
58
	def __init__(self, modifyElems, attributes, values=None, index=None):
63
	def __init__(self, options):
59
		self.modify = modifyElems
64
		self.doAdd = options.doAdd
60
		self.attributes = attributes
65
		self.modify = options.elements
61
		self.values = values
66
		self.attributes = options.attributes
62
		self.index = index
67
		self.values = options.values
68
		self.index = options.index
69
		self.pathRe = re.compile('(?P<ex>(?P<root>//)?/?(?P<elem>[^/\[]+)(?P<attr>\[@(?P<attrName>[^=\]]+)(=\"?(?P<attrValue>[^\"]*)\"?)?\])?)')
70
		self.matches = options.matches
71
		self.qualifier = options.qualifier
63
72
64
	def change_elem(self, elem):
73
	def change_elem(self, elem):
65
		for i,attr in enumerate(self.attributes):
74
		if self.attributes:
66
			if self.values:
75
			for i,attr in enumerate(self.attributes):
67
				elem.setAttribute(attr, self.values[i])
76
				if self.doAdd:
68
			else:
77
					elem.setAttribute(attr, self.values[i])
69
				try:
78
				else:
70
					elem.removeAttribute(attr)
79
					try:
71
				except DomRewriter.NotFoundErr:
80
						elem.removeAttribute(attr)
81
					except DomRewriter.NotFoundErr:
82
						continue
83
		else:
84
			elem.parentNode.removeChild(elem)	
85
86
	def match_elem(self, position, index, parent):
87
		if len(self.matches[index]) == position:
88
			return True
89
		match = self.pathRe.match(self.matches[index], position)
90
		if not match:
91
			return False
92
		new_position = match.end()
93
		target = match.groupdict()
94
		t_elemet = target["elem"]
95
		t_attribute = target["attrName"]
96
		t_value = target["attrValue"]
97
		t_root = target["root"]
98
		if t_root and not position==0:
99
			error("missformed expression at "+ target["ex"] + " in self.matches[index]")
100
		if not t_elemet:
101
			error("Missing element in " + options.matches[index])
102
		for elem in parent.getElementsByTagName(t_elemet):
103
			if not position == 0 or t_root:
104
				if not elem.parentNode == parent:
105
					continue
106
			if t_attribute:
107
				if not elem.hasAttribute(t_attribute):
72
					continue
108
					continue
109
				if t_value:
110
					attr = elem.getAttributeNode(t_attribute)
111
					if not attr.value == t_value:
112
						continue
113
			if self.match_elem(new_position, index, elem) and self.qualify_elem(0, index, elem):
114
				if self.doAdd:
115
					if self.modify:
116
						elemNew = self.document.createElement(self.modify[index])
117
						elem.appendChild(elemNew)
118
						elem = elemNew
119
					if self.attributes:
120
						elem.setAttribute(self.attributes[index], self.values[index])
121
					else:
122
						if (t_attribute and self.values):
123
							elem.setAttribute(t_attribute, self.values[index])
124
				else:
125
					if t_attribute:
126
						elem.removeAttribute(t_attribute)
127
					else:
128
						elem.parentNode.removeChild(elem)
129
		return False;
130
131
	def qualify_elem(self, position, index, parent):
132
		if not self.qualifier:
133
			return True
134
		if len(self.qualifier[index]) == position:
135
			return True
136
		match = self.pathRe.match(self.qualifier[index], position)
137
		if not match:
138
			return False
139
		new_position = match.end()
140
		target = match.groupdict()
141
		t_elemet = target["elem"]
142
		t_attribute = target["attrName"]
143
		t_value = target["attrValue"]
144
		t_root = target["root"]
145
		if t_root:
146
			error("qualifier must not start with root //. Qualifier uses always match path as root.")
147
		if not t_elemet:
148
			error("Missing element in " + options.matches[index])
149
		for elem in parent.getElementsByTagName(t_elemet):
150
			if t_attribute:
151
				if not elem.hasAttribute(t_attribute):
152
					return False
153
				if t_value:
154
					attr = elem.getAttributeNode(t_attribute)
155
					if not attr.value == t_value:
156
						return False
157
			if self.qualify_elem(new_position, index, elem):
158
				return True
73
159
74
	def process(self,in_stream,callback=None):
160
	def process(self,in_stream,callback=None):
75
		from xml.dom.minidom import parse
161
		from xml.dom.minidom import parse
Lines 79-115 Link Here
79
		if callback:
165
		if callback:
80
			callback(self.document)
166
			callback(self.document)
81
167
82
		if not self.modify:
168
		if self.matches:
83
			return
169
			for i in range(len(self.matches)):
84
170
				self.match_elem(0, i, self.document)
85
		for tag in self.modify:
171
		else:
86
			matches = self.document.getElementsByTagName(tag)
172
			if not self.modify:
87
			if matches:
173
				return
88
				if self.index == None:
174
			for tag in self.modify:
89
					for match in matches:
175
				matches = self.document.getElementsByTagName(tag)
90
						self.change_elem(match)
176
				if matches:
91
				else:
177
					if self.index == None:
92
					self.change_elem(matches[self.index])
178
						for match in matches:
179
							self.change_elem(match)
180
					else:
181
						self.change_elem(matches[self.index])
93
182
94
	def write(self,stream):
183
	def write(self,stream):
95
		stream.write(self.document.toxml())
184
		stream.write(self.document.toxml())
96
185
97
class StreamRewriterBase:
186
class StreamRewriterBase:
98
187
99
	def __init__(self, elems, attributes, values, index,
188
	def __init__(self, elems, attributes, values, index):
100
	      sourceElems = [], sourceAttributes = [], sourceValues = [],
101
	      targetElems = [], targetAttributes = [], targetValues = []  ):
102
		self.buffer = StringIO.StringIO()
189
		self.buffer = StringIO.StringIO()
103
		self.__write = self.buffer.write
190
		self.__write = self.buffer.write
104
		self.elems = elems		or []
191
		self.elems = elems		or []
105
		self.attributes = attributes	or []
192
		self.attributes = attributes	or []
106
		self.values = values		or []
193
		self.values = values		or []
107
		self.sourceElems = sourceElems	or []
108
		self.sourceAttributes = sourceAttributes	or []
109
		self.sourceValues = sourceValues		or []
110
		self.targetElems = targetElems			or []
111
		self.targetAttributes = targetAttributes	or []
112
		self.targetValues = targetValues		or []
113
194
114
	def p(self,str):
195
	def p(self,str):
115
		self.__write(str.encode('utf8'))
196
		self.__write(str.encode('utf8'))
Lines 126-179 Link Here
126
		self.p(u'<%s ' % name)
207
		self.p(u'<%s ' % name)
127
208
128
		match = ( name in self.elems )
209
		match = ( name in self.elems )
129
		matchSource = ( name in self.sourceElems )
130
		matchTarget = ( name in self.targetElems )
131
210
132
		for a,v in attrs:
211
		for a,v in attrs:
133
			if not (
212
			if not (match and a in self.attributes):
134
				(match and a in self.attributes)
135
				or (matchSource and a in self.sourceAttributes)
136
				or (matchTarget and a in self.targetAttributes)
137
			):
138
				self.write_attr(a,v)
213
				self.write_attr(a,v)
139
214
140
		if matchSource:
141
			for i, attr in enumerate(self.sourceAttributes):
142
				self.write_attr(attr, self.sourceValues[i])
143
144
		if matchTarget:
145
			for i, attr in enumerate(self.targetAttributes):
146
				self.write_attr(attr, self.targetValues[i])
147
148
		if match:
215
		if match:
149
			for i, attr in enumerate(self.attributes):
216
			for i, attr in enumerate(self.attributes):
150
				self.write_attr(attr, self.values[i])
217
				self.write_attr(attr, self.values[i])
151
218
152
		self.p(u'>')
219
		self.p(u'>')
153
220
154
class ExpatRewriter(StreamRewriterBase):
221
#class ExpatRewriter(StreamRewriterBase):
155
	"""
222
#	"""
156
	The only problem with this Expat based implementation is that it does not
223
#	The only problem with this Expat based implementation is that it does not
157
	handle entities doctypes etc properly so for example dev-java/skinlf fails.
224
#	handle entities doctypes etc properly so for example dev-java/skinlf fails.
158
	"""
225
#	"""
159
	def process(self, in_stream):
226
#	def process(self, in_stream):
160
		from xml.parsers.expat import ParserCreate
227
#		from xml.parsers.expat import ParserCreate
161
		parser = ParserCreate()
228
#		parser = ParserCreate()
162
229
#
163
		parser.StartElementHandler = self.start_element
230
#		parser.StartElementHandler = self.start_element
164
		parser.EndElementHandler = self.end_element
231
#		parser.EndElementHandler = self.end_element
165
		parser.CharacterDataHandler = self.char_data
232
#		parser.CharacterDataHandler = self.char_data
166
		parser.ParseFile(in_stream)
233
#		parser.ParseFile(in_stream)
167
		self.p(u'\n')
234
#		self.p(u'\n')
168
235
#
169
	def start_element(self, name, attrs):
236
#	def start_element(self, name, attrs):
170
		StreamRewriterBase(self, name, attrs.iteritems())
237
#		StreamRewriterBase(self, name, attrs.iteritems())
171
238
#
172
	def end_element(self,name):
239
#	def end_element(self,name):
173
		self.p(u'</%s>' % name)
240
#		self.p(u'</%s>' % name)
174
241
#
175
	def char_data(self,data):
242
#	def char_data(self,data):
176
		self.p(escape(data))
243
#		self.p(escape(data))
177
244
178
from xml.sax.saxutils import XMLGenerator
245
from xml.sax.saxutils import XMLGenerator
179
class SaxRewriter(XMLGenerator, StreamRewriterBase):
246
class SaxRewriter(XMLGenerator, StreamRewriterBase):
Lines 181-192 Link Here
181
	Using Sax gives us the support for writing back doctypes and all easily
248
	Using Sax gives us the support for writing back doctypes and all easily
182
	and is only marginally slower than expat as it is just a tight layer over it
249
	and is only marginally slower than expat as it is just a tight layer over it
183
	"""
250
	"""
184
	def __init__(self, elems, attributes, values, index,
251
	def __init__(self, elems, attributes, values, index):
185
	      sourceElems = [], sourceAttributes = [], sourceValues = [],
252
		StreamRewriterBase.__init__(self, elems, attributes, values, index)
186
	      targetElems = [], targetAttributes = [], targetValues = []):
187
		StreamRewriterBase.__init__(self, elems, attributes, values, index,
188
			      sourceElems, sourceAttributes, sourceValues,
189
			      targetElems, targetAttributes, targetValues)
190
		XMLGenerator.__init__(self, self.buffer, 'UTF-8')
253
		XMLGenerator.__init__(self, self.buffer, 'UTF-8')
191
254
192
	def process(self, in_stream):
255
	def process(self, in_stream):
Lines 198-226 Link Here
198
		self.start_element(name, attrs.items())
261
		self.start_element(name, attrs.items())
199
262
200
if __name__ == '__main__':
263
if __name__ == '__main__':
264
	import re
265
201
	usage = "XML Rewrite Python Module Version " + __version__ + "\n"
266
	usage = "XML Rewrite Python Module Version " + __version__ + "\n"
202
	usage += "Copyright 2004,2006,2007 Gentoo Foundation\n"
267
	usage += "Copyright 2004,2006,2007 Gentoo Foundation\n"
203
	usage += "Distributed under the terms of the GNU General Public Lincense v2\n"
268
	usage += "Distributed under the terms of the GNU General Public License v2\n"
204
	usage += "Please contact the Gentoo Java Team <java@gentoo.org> with problems.\n"
269
	usage += "Please contact the Gentoo Java Team <java@gentoo.org> with problems.\n"
205
	usage += "\n"
270
	usage += "\n"
206
	usage += "Usage:\n"
271
	usage += "Usage:\n"
207
	usage += "	xml-rewrite.py [-f file] --delete [-g] -e tag [-e tag] -a attribute [-a attribute] [-i index]\n"
272
	usage += "      xml-rewrite.py [-f file]... --delete [-g] -e tag [-e tag]... [-a attribute]... [-i index] [-m <path> [-q <path>]]...\n"
208
	usage += "	xml-rewrite.py [-f file] --change [-g] -e tag [-e tag] -a attribute -v value [-a attribute -v value] \\\n"
273
	usage += "	xml-rewrite.py [-f file]... --change [-g] -e tag [-e tag]... -a attribute -v value [-a attribute -v value]... [-m <path> [-q <path>]]... \\\n"
209
	usage += "			[--source-element tag] [--source-attribute attribute --source-value value] \\\n"
210
	usage += "			[--target-element tag] [--target-attribute attribute --target-value value] [-i index]\n"
211
	usage += "Or:\n"
274
	usage += "Or:\n"
212
	usage += "	xml-rewrite.py [-f file] -g\n"
275
	usage += "	xml-rewrite.py [-f file] -g\n"
213
	usage += "\n"
276
	usage += "\n"
214
	usage += "If the -f parameter is not utilized, the script will read and\n"
277
	usage += "If the -f parameter is not utilized, the script will read and\n"
215
	usage += "write to stdin and stdout respectively.  The use of quotes on\n"
278
	usage += "write to stdin and stdout respectively.  The use of quotes on\n"
216
	usage += "parameters will break the script.\n"
279
	usage += "parameters will break the script.\n"
280
	usage += "<path> -'xy[@tz=\"main\"/qr` will match all 'qr' elements of all 'xy' elements which 'tz' attributes value is main\n"
281
	usage += "Matching:\n"
282
	usage += "-c with -v only    : only existing attribute defined by match-path will be changed\n"
283
	usage += "-c with -v and -a  : the given attribute is added/changed to the element defined by match-path\n"
284
	usage += "-c with -e only    : the given element is added to the element defined by match-path\n"
217
285
218
286
219
	def error(message):
287
	def error(message):
220
		print "ERROR: " + message
288
		print "ERROR: " + message
221
		sys.exit(1)
289
		sys.exit(1)
222
290
223
224
#	if len(sys.argv) == 1:
291
#	if len(sys.argv) == 1:
225
#		usage(True)
292
#		usage(True)
226
293
Lines 232-249 Link Here
232
		make_option ("-e", "--element", action="append", dest="elements", help="Tag of the element of which the attributes to be changed.  These can be chained for multiple elements."),
299
		make_option ("-e", "--element", action="append", dest="elements", help="Tag of the element of which the attributes to be changed.  These can be chained for multiple elements."),
233
		make_option ("-a", "--attribute", action="append", dest="attributes", help="Attribute of the matching elements to change. These can be chained for multiple value-attribute pairs"),
300
		make_option ("-a", "--attribute", action="append", dest="attributes", help="Attribute of the matching elements to change. These can be chained for multiple value-attribute pairs"),
234
		make_option ("-v", "--value", action="append", dest="values", help="Value to set the attribute to."),
301
		make_option ("-v", "--value", action="append", dest="values", help="Value to set the attribute to."),
235
		make_option ("-r", "--source-element", action="append", dest="source_elements", help="Tag of the element of which the attributes to be changed just in source scope.  These can be chained for multiple elements."),
302
#		make_option ("-r", "--source-element", action="append", dest="source_elements", help="Tag of the element of which the attributes to be changed just in source scope.  These can be chained for multiple elements."),
236
		make_option ("-t","--source-attribute", action="append", dest="source_attributes", help="Attribute of the matching elements to change. These can be chained for multiple value-attribute pairs (for source only)"),
303
#		make_option ("-t", "--source-attribute", action="append", dest="source_attributes", help="Attribute of the matching elements to change. These can be chained for multiple value-attribute pairs (for source only)"),
237
		make_option ("-y", "--source-value", action="append", dest="source_values", help="Value to set the attribute to. (sourceonly)"),
304
#		make_option ("-y", "--source-value", action="append", dest="source_values", help="Value to set the attribute to. (sourceonly)"),
238
		make_option ("-j", "--target-element", action="append", dest="target_elements", help="Tag of the element of which the attributes to be changed just in target scope.  These can be chained for multiple elements."),
305
#		make_option ("-j", "--target-element", action="append", dest="target_elements", help="Tag of the element of which the attributes to be changed just in target scope.  These can be chained for multiple elements."),
239
		make_option ("-k", "--target-attribute", action="append", dest="target_attributes", help="Attribute of the matching elements to change. These can be chained for multiple value-attribute pairs (for targetonly)"),
306
#		make_option ("-k", "--target-attribute", action="append", dest="target_attributes", help="Attribute of the matching elements to change. These can be chained for multiple value-attribute pairs (for targetonly)"),
240
		make_option ("-l", "--target-value", action="append", dest="target_values", help="Value to set the attribute to (targeronly)."),
307
#		make_option ("-l", "--target-value", action="append", dest="target_values", help="Value to set the attribute to (targeronly)."),
241
		make_option ("-i", "--index", type="int", dest="index", help="Index of the match.  If none is specified, the changes will be applied to all matches within the document. Starts from zero.")
308
		make_option ("-r", "--source-element", action="append", dest="elements", help="Deprecated. please use -e or --element"),
309
		make_option ("-t", "--source-attribute", action="append", dest="attributes", help="Deprecated. please use -a or --attribute"),
310
		make_option ("-y", "--source-value", action="append", dest="values", help="Deprecated. please use -v or --value"),
311
		make_option ("-j", "--target-element", action="append", dest="elements", help="Deprecated. please use -e or --element"),
312
		make_option ("-k", "--target-attribute", action="append", dest="attributes", help="Deprecated. please use -a or --attribute"),
313
		make_option ("-l", "--target-value", action="append", dest="values", help="Deprecated. please use -v or --value"),
314
		make_option ("-i", "--index", type="int", dest="index", help="Index of the match.  If none is specified, the changes will be applied to all matches within the document. Starts from zero."),
315
		make_option ("-m", "--match", action="append", dest="matches", help="match a node by the given path. If -c is set, -e and/or -a with -v are set/appended to path. If -d is set nodes matching path are deleted"),
316
		make_option ("-q", "--qualify", type="string", action="append", dest="qualifier", help="qualifies any node if given path matches to -m matched node as base. All other -m matching nodes are ignored")
242
	]
317
	]
243
318
244
	parser = OptionParser(usage, options_list)
319
	parser = OptionParser(usage, options_list)
245
	(options, args) = parser.parse_args()
320
	(options, args) = parser.parse_args()
246
321
322
	# TODO  -r -j can be appended to elements since nothing would change
323
	# TODO  -t -k can be appended to attributes since nothing would change
324
	# TODO  -y -l can be appended to values since nothing would change
247
325
248
	# Invalid Arguments Must be smited!
326
	# Invalid Arguments Must be smited!
249
	if not options.doAdd and not options.doDelete and not options.gentoo_classpath:
327
	if not options.doAdd and not options.doDelete and not options.gentoo_classpath:
Lines 255-287 Link Here
255
		if options.doAdd and options.doDelete:
333
		if options.doAdd and options.doDelete:
256
			error("Unable to perform multiple actions simultaneously.")
334
			error("Unable to perform multiple actions simultaneously.")
257
335
258
		if not options.elements and not options.target_elements and not options.source_elements:
336
		if not options.elements and not options.matches:
259
			error("At least one element (global, source only or target only) and attribute must be specified.")
337
			error("At least one element must be specified or given by match")
260
338
261
		for elem in ( options.source_attributes or [] ):
339
		if options.doAdd and not options.matches and len(options.values or []) != len(options.attributes or []):
262
			if elem in ( options.attributes or [] ):
263
				error("You can't set an attribute in global and source scope at the same time")
264
265
		for elem in ( options.target_attributes or [] ):
266
			if elem in ( options.attributes or [] ):
267
				error("You can't set an attribute in global and target scope at the same time")
268
269
		if options.doAdd and (len(options.values or []) != len(options.attributes or [])
270
			or len(options.source_values or [] ) != len(options.source_attributes or [])
271
			or len(options.target_values or [] ) != len(options.target_attributes or [])):
272
			error("You must give attribute(s)/value(s) for every element you are changing.")
340
			error("You must give attribute(s)/value(s) for every element you are changing.")
273
341
342
		if options.doDelete and options.matches and (options.elements or options.attributes or options.values):
343
			error("You can't set -e, -a or -v while using a match to delete")
344
345
		if options.matches and options.elements and (len(options.matches or []) != len(options.elements or [])):
346
			error("You must give a element for every match you define")
347
348
		if options.matches and options.values and options.elements and not options.attributes:
349
			error("you can't define new Element and value without an attribute")
350
351
		if options.matches and options.values and (len(options.matches or []) != len(options.values or [])):
352
			error("You must give a value for every match you define")
353
354
		if options.matches and options.qualifier and (len(options.matches or []) != len(options.qualifier or [])):
355
			error("You must give a qualifier for every match you define")
356
357
		if options.matches and options.attributes and (len(options.attributes or []) != len(options.values or [])):
358
			error("You must give a value for each attribute you give")
359
			
360
274
	# End Invalid Arguments Check
361
	# End Invalid Arguments Check
275
362
276
	def get_rewriter(options):
363
	def get_rewriter(options):
277
		if options.index or options.doDelete or options.gentoo_classpath:
364
		if options.index or options.doDelete or options.gentoo_classpath or options.matches:
278
			# java-ant-2.eclass does not use these options so we can optimize the ExpatWriter
365
			# java-ant-2.eclass does not use these options so we can optimize the ExpatWriter
279
			# and let the DomRewriter do these. Also keeps the index option compatible for sure.
366
			# and let the DomRewriter do these. Also keeps the index option compatible for sure.
280
			rewriter = DomRewriter(options.elements, options.attributes, options.values, options.index)
367
			rewriter = DomRewriter(options)
281
		else:
368
		else:
282
			rewriter = SaxRewriter(options.elements, options.attributes, options.values, options.index,
369
			rewriter = SaxRewriter(options.elements, options.attributes, options.values, options.index)
283
			  options.source_elements, options.source_attributes, options.source_values,
284
			  options.target_elements, options.target_attributes, options.target_values)
285
370
286
		return rewriter
371
		return rewriter
287
372

Return to bug 405865