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 / +195 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 not elem.parentNode == parent:
151
				continue
152
			if t_attribute:
153
				if not elem.hasAttribute(t_attribute):
154
					return False
155
				if t_value:
156
					attr = elem.getAttributeNode(t_attribute)
157
					if not attr.value == t_value:
158
						return False
159
			if self.qualify_elem(new_position, index, elem):
160
				return True
73
161
74
	def process(self,in_stream,callback=None):
162
	def process(self,in_stream,callback=None):
75
		from xml.dom.minidom import parse
163
		from xml.dom.minidom import parse
Lines 79-115 Link Here
79
		if callback:
167
		if callback:
80
			callback(self.document)
168
			callback(self.document)
81
169
82
		if not self.modify:
170
		if self.matches:
83
			return
171
			for i in range(len(self.matches)):
84
172
				self.match_elem(0, i, self.document)
85
		for tag in self.modify:
173
		else:
86
			matches = self.document.getElementsByTagName(tag)
174
			if not self.modify:
87
			if matches:
175
				return
88
				if self.index == None:
176
			for tag in self.modify:
89
					for match in matches:
177
				matches = self.document.getElementsByTagName(tag)
90
						self.change_elem(match)
178
				if matches:
91
				else:
179
					if self.index == None:
92
					self.change_elem(matches[self.index])
180
						for match in matches:
181
							self.change_elem(match)
182
					else:
183
						self.change_elem(matches[self.index])
93
184
94
	def write(self,stream):
185
	def write(self,stream):
95
		stream.write(self.document.toxml())
186
		stream.write(self.document.toxml())
96
187
97
class StreamRewriterBase:
188
class StreamRewriterBase:
98
189
99
	def __init__(self, elems, attributes, values, index,
190
	def __init__(self, elems, attributes, values, index):
100
	      sourceElems = [], sourceAttributes = [], sourceValues = [],
101
	      targetElems = [], targetAttributes = [], targetValues = []  ):
102
		self.buffer = StringIO.StringIO()
191
		self.buffer = StringIO.StringIO()
103
		self.__write = self.buffer.write
192
		self.__write = self.buffer.write
104
		self.elems = elems		or []
193
		self.elems = elems		or []
105
		self.attributes = attributes	or []
194
		self.attributes = attributes	or []
106
		self.values = values		or []
195
		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
196
114
	def p(self,str):
197
	def p(self,str):
115
		self.__write(str.encode('utf8'))
198
		self.__write(str.encode('utf8'))
Lines 126-179 Link Here
126
		self.p(u'<%s ' % name)
209
		self.p(u'<%s ' % name)
127
210
128
		match = ( name in self.elems )
211
		match = ( name in self.elems )
129
		matchSource = ( name in self.sourceElems )
130
		matchTarget = ( name in self.targetElems )
131
212
132
		for a,v in attrs:
213
		for a,v in attrs:
133
			if not (
214
			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)
215
				self.write_attr(a,v)
139
216
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:
217
		if match:
149
			for i, attr in enumerate(self.attributes):
218
			for i, attr in enumerate(self.attributes):
150
				self.write_attr(attr, self.values[i])
219
				self.write_attr(attr, self.values[i])
151
220
152
		self.p(u'>')
221
		self.p(u'>')
153
222
154
class ExpatRewriter(StreamRewriterBase):
223
#class ExpatRewriter(StreamRewriterBase):
155
	"""
224
#	"""
156
	The only problem with this Expat based implementation is that it does not
225
#	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.
226
#	handle entities doctypes etc properly so for example dev-java/skinlf fails.
158
	"""
227
#	"""
159
	def process(self, in_stream):
228
#	def process(self, in_stream):
160
		from xml.parsers.expat import ParserCreate
229
#		from xml.parsers.expat import ParserCreate
161
		parser = ParserCreate()
230
#		parser = ParserCreate()
162
231
#
163
		parser.StartElementHandler = self.start_element
232
#		parser.StartElementHandler = self.start_element
164
		parser.EndElementHandler = self.end_element
233
#		parser.EndElementHandler = self.end_element
165
		parser.CharacterDataHandler = self.char_data
234
#		parser.CharacterDataHandler = self.char_data
166
		parser.ParseFile(in_stream)
235
#		parser.ParseFile(in_stream)
167
		self.p(u'\n')
236
#		self.p(u'\n')
168
237
#
169
	def start_element(self, name, attrs):
238
#	def start_element(self, name, attrs):
170
		StreamRewriterBase(self, name, attrs.iteritems())
239
#		StreamRewriterBase(self, name, attrs.iteritems())
171
240
#
172
	def end_element(self,name):
241
#	def end_element(self,name):
173
		self.p(u'</%s>' % name)
242
#		self.p(u'</%s>' % name)
174
243
#
175
	def char_data(self,data):
244
#	def char_data(self,data):
176
		self.p(escape(data))
245
#		self.p(escape(data))
177
246
178
from xml.sax.saxutils import XMLGenerator
247
from xml.sax.saxutils import XMLGenerator
179
class SaxRewriter(XMLGenerator, StreamRewriterBase):
248
class SaxRewriter(XMLGenerator, StreamRewriterBase):
Lines 181-192 Link Here
181
	Using Sax gives us the support for writing back doctypes and all easily
250
	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
251
	and is only marginally slower than expat as it is just a tight layer over it
183
	"""
252
	"""
184
	def __init__(self, elems, attributes, values, index,
253
	def __init__(self, elems, attributes, values, index):
185
	      sourceElems = [], sourceAttributes = [], sourceValues = [],
254
		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')
255
		XMLGenerator.__init__(self, self.buffer, 'UTF-8')
191
256
192
	def process(self, in_stream):
257
	def process(self, in_stream):
Lines 198-226 Link Here
198
		self.start_element(name, attrs.items())
263
		self.start_element(name, attrs.items())
199
264
200
if __name__ == '__main__':
265
if __name__ == '__main__':
266
	import re
267
201
	usage = "XML Rewrite Python Module Version " + __version__ + "\n"
268
	usage = "XML Rewrite Python Module Version " + __version__ + "\n"
202
	usage += "Copyright 2004,2006,2007 Gentoo Foundation\n"
269
	usage += "Copyright 2004,2006,2007 Gentoo Foundation\n"
203
	usage += "Distributed under the terms of the GNU General Public Lincense v2\n"
270
	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"
271
	usage += "Please contact the Gentoo Java Team <java@gentoo.org> with problems.\n"
205
	usage += "\n"
272
	usage += "\n"
206
	usage += "Usage:\n"
273
	usage += "Usage:\n"
207
	usage += "	xml-rewrite.py [-f file] --delete [-g] -e tag [-e tag] -a attribute [-a attribute] [-i index]\n"
274
	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"
275
	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"
276
	usage += "Or:\n"
212
	usage += "	xml-rewrite.py [-f file] -g\n"
277
	usage += "	xml-rewrite.py [-f file] -g\n"
213
	usage += "\n"
278
	usage += "\n"
214
	usage += "If the -f parameter is not utilized, the script will read and\n"
279
	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"
280
	usage += "write to stdin and stdout respectively.  The use of quotes on\n"
216
	usage += "parameters will break the script.\n"
281
	usage += "parameters will break the script.\n"
282
	usage += "<path> -'xy[@tz=\"main\"/qr` will match all 'qr' elements of all 'xy' elements which 'tz' attributes value is main\n"
283
	usage += "Matching:\n"
284
	usage += "-c with -v only    : only existing attribute defined by match-path will be changed\n"
285
	usage += "-c with -v and -a  : the given attribute is added/changed to the element defined by match-path\n"
286
	usage += "-c with -e only    : the given element is added to the element defined by match-path\n"
217
287
218
288
219
	def error(message):
289
	def error(message):
220
		print "ERROR: " + message
290
		print "ERROR: " + message
221
		sys.exit(1)
291
		sys.exit(1)
222
292
223
224
#	if len(sys.argv) == 1:
293
#	if len(sys.argv) == 1:
225
#		usage(True)
294
#		usage(True)
226
295
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."),
301
		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"),
302
		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."),
303
		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."),
304
#		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)"),
305
#		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)"),
306
#		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."),
307
#		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)"),
308
#		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)."),
309
#		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.")
310
		make_option ("-r", "--source-element", action="append", dest="elements", help="Deprecated. please use -e or --element"),
311
		make_option ("-t", "--source-attribute", action="append", dest="attributes", help="Deprecated. please use -a or --attribute"),
312
		make_option ("-y", "--source-value", action="append", dest="values", help="Deprecated. please use -v or --value"),
313
		make_option ("-j", "--target-element", action="append", dest="elements", help="Deprecated. please use -e or --element"),
314
		make_option ("-k", "--target-attribute", action="append", dest="attributes", help="Deprecated. please use -a or --attribute"),
315
		make_option ("-l", "--target-value", action="append", dest="values", help="Deprecated. please use -v or --value"),
316
		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."),
317
		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"),
318
		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
	]
319
	]
243
320
244
	parser = OptionParser(usage, options_list)
321
	parser = OptionParser(usage, options_list)
245
	(options, args) = parser.parse_args()
322
	(options, args) = parser.parse_args()
246
323
324
	# TODO  -r -j can be appended to elements since nothing would change
325
	# TODO  -t -k can be appended to attributes since nothing would change
326
	# TODO  -y -l can be appended to values since nothing would change
247
327
248
	# Invalid Arguments Must be smited!
328
	# Invalid Arguments Must be smited!
249
	if not options.doAdd and not options.doDelete and not options.gentoo_classpath:
329
	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:
335
		if options.doAdd and options.doDelete:
256
			error("Unable to perform multiple actions simultaneously.")
336
			error("Unable to perform multiple actions simultaneously.")
257
337
258
		if not options.elements and not options.target_elements and not options.source_elements:
338
		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.")
339
			error("At least one element must be specified or given by match")
260
340
261
		for elem in ( options.source_attributes or [] ):
341
		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.")
342
			error("You must give attribute(s)/value(s) for every element you are changing.")
273
343
344
		if options.doDelete and options.matches and (options.elements or options.attributes or options.values):
345
			error("You can't set -e, -a or -v while using a match to delete")
346
347
		if options.matches and options.elements and (len(options.matches or []) != len(options.elements or [])):
348
			error("You must give a element for every match you define")
349
350
		if options.matches and options.values and options.elements and not options.attributes:
351
			error("you can't define new Element and value without an attribute")
352
353
		if options.matches and options.values and (len(options.matches or []) != len(options.values or [])):
354
			error("You must give a value for every match you define")
355
356
		if options.matches and options.qualifier and (len(options.matches or []) != len(options.qualifier or [])):
357
			error("You must give a qualifier for every match you define")
358
359
		if options.matches and options.attributes and (len(options.attributes or []) != len(options.values or [])):
360
			error("You must give a value for each attribute you give")
361
			
362
274
	# End Invalid Arguments Check
363
	# End Invalid Arguments Check
275
364
276
	def get_rewriter(options):
365
	def get_rewriter(options):
277
		if options.index or options.doDelete or options.gentoo_classpath:
366
		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
367
			# 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.
368
			# 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)
369
			rewriter = DomRewriter(options)
281
		else:
370
		else:
282
			rewriter = SaxRewriter(options.elements, options.attributes, options.values, options.index,
371
			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
372
286
		return rewriter
373
		return rewriter
287
374

Return to bug 405865