comparison fixivy.groovy @ 89:15f4da8bdbd3

add ability to control order of attributes, whitespace. more cleanup up of dependencies and exclusions
author smith@nwoca.org
date Fri, 17 Feb 2012 15:09:35 +0000
parents fix-ivy.groovy@9f2ab59a5333
children c207cdcaf13e
comparison
equal deleted inserted replaced
88:9f2ab59a5333 89:15f4da8bdbd3
1
2 import groovy.xml.QName;
3 import org.codehaus.groovy.runtime.InvokerHelper;
4
5 import java.io.OutputStreamWriter;
6 import java.io.PrintWriter;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 import groovy.xml.*
12
13 //args = [ '-r']
14 println args
15
16 ant = new AntBuilder()
17
18
19 if (args.any { it.toUpperCase() == '-R'} ) {
20
21 new File('.').traverse ([ type: groovy.io.FileType.DIRECTORIES, maxDepth: 3 ]) { project ->
22 if (new File(project,'ivy.xml').exists() ) {
23 processIvy(new File(project,'ivy.xml'))
24 }
25 }
26
27 } else {
28 processIvy(new File('ivy.xml'))
29 }
30
31 def processIvy(file) {
32 println "-" * 60
33 println "processing: $file"
34 def xml = new XmlSlurper()
35 def ivy = xml.parse(file)
36
37 def cfgs = ivy.configurations
38
39 // default mapping for most dependencies:
40 cfgs.@defaultconfmapping = 'compile->default(*)'
41
42 // Add default config, if missing:
43 if (!cfgs.children().find { it.@name == 'default' } ) {
44 println "adding default config"
45 cfgs.appendNode {
46 conf(name:"default",extends:'compile',visibility:'public')
47 }
48 }
49
50 if (ivy.dependencies.children().find { it.@name == 'org.springframework.web.servlet' } &&
51 !ivy.dependencies.children().find { it.@name == 'servlet-api' } ) {
52 ivy.dependencies.children().findAll { it.name() == 'dependency' }.list().last() + {
53 dependency(name:'servlet-api', org: 'javax.servlet', rev: '2.5')
54 dependency(name:'jsp-api', org: 'javax.servlet', rev: '2.0')
55 }
56
57 }
58
59 ivy.dependencies.children().each { dep ->
60 if ( dep.@conf.text()?.contains('test') ) {
61 dep.@conf = 'compile-test->default(*)'
62 } else if ( dep.@conf.text().contains('castor-ant') ) {
63 dep.@conf = 'castor-ant->default(*)'
64 }
65 else {
66 dep.attributes().remove('conf')
67 }
68 }
69
70 // find groovy dependencies (or null)
71 groovy = ivy.dependencies.children().find { it.@org == 'org.codehaus.groovy' && it.@name.text().startsWith('groovy') }
72
73 if (groovy) {
74 // Add a groovy conig for groovy projects:
75 if (!cfgs.children().any { it.@name == 'groovy' } ) {
76 println "added private groovy conf"
77 cfgs.appendNode {
78 conf(name:'groovy',visibility:'private',transitive: true)
79 }
80 }
81 // Adjust 'compile' to exend from 'groovy' conf
82 cfgs.children().find { it.@name == 'compile' }.@extends = 'groovy'
83 cfgs.children().find { it.@name == 'groovy' }.@transitive = 'true'
84 groovy.@conf = "groovy->default"
85 println "Groovy ${groovy.@rev} conf changed to ${groovy.@conf}"
86 // Switch project to use groovy-all instead of groovy + deps
87 if (groovy.@name == 'groovy') {
88 println "changed groovy to groovy-all"
89 groovy.@name = 'groovy-all'
90 }
91
92 def template = new File(file.parent ?: ".",'template.mf')
93 if (template.exists()) {
94 println "updating groovy in $template"
95 ant.replace(file: template, token: 'org.codehaus.groovy-groovy:major',value:'org.codehaus.groovy-groovy-all:major')
96 }
97
98 }
99
100
101 ensure(ivy.dependencies, 'hibernate-validator', ['org.slf4j:slf4j-api:1.5.6','org.slf4j:slf4j-log4j12:1.5.6'])
102
103 ensure(ivy.dependencies, 'org.springframework.test', ['junit:junit:4.8.2'])
104
105 // remove exclude for groovy-all
106 ivy.dependencies.children().find { it.name() == 'exclude' && it.@module.text() == 'groovy-all' }.replaceNode {}
107
108 // Add exclude for regular groovy:
109 ensureExcluded(ivy.dependencies,"org.codehaus.groovy:groovy")
110
111 // ensure modules covered by springsource are exluded;
112 ensureSpringSourceExcludes(ivy.dependencies)
113
114 ensureConfiguration(ivy.dependencies,'org.springframework.test','compile-test')
115
116 //remove bad junit dependencies and excludes
117 ivy.dependencies.children().find { it.@org == 'org.junit' && it.@module.text() == 'org.junit' }.replaceNode {}
118 ivy.dependencies.children().find { it.@org == 'org.junit' && it.@name.text() == 'org.junit' }.replaceNode {}
119
120
121 cfgs.children().each { it.@visibility = 'public' } // Should be 'private' after initial conversions.
122
123 cfgs.children().find {it.@name == 'runtime' }.@extends = 'compile'
124 cfgs.children().find {it.@name == 'compile-test' }.@extends = 'compile'
125 cfgs.children().find {it.@name == 'runtime-test' }.@extends = 'runtime,compile-test'
126 cfgs.children().find {it.@name == 'default' }.@visibility = 'public'
127 cfgs.children().find {it.@name == 'default' }.@extends = 'runtime'
128
129 if (!cfgs.children().any { it.@name == 'archives' } ) {
130 println "added public 'archives' conf"
131 cfgs.appendNode {
132 conf(name:'archives',visibility:'public')
133 }
134 }
135
136 cfgs.children().find {it.@name == 'archives' }.@visibility = 'public'
137
138
139 def outputBuilder = new StreamingMarkupBuilder()
140 //new File('ivy.xml').write( XmlUtil.serialize( outputBuilder.bind { mkp.yield ivy }).replaceAll('>','>'))
141
142 def stringWriter = new StringWriter()
143
144
145 def node = new XmlParser().parseText( XmlUtil.serialize( outputBuilder.bind { mkp.yield ivy }))
146 new MyNodePrinter(new IndentPrinter( new PrintWriter(stringWriter))).print(node)
147 file.write(stringWriter.toString())
148
149 }
150
151 def ensureConfiguration(depends,name,cfg) {
152 depends.children().findAll { it.name() == 'dependency' && it.@name == name}.each {
153 it.@conf = "$cfg->default(*)"
154 }
155 }
156
157 def ensureExcluded(depends,excludeModule) {
158 def (o,m) = excludeModule.split(':')
159 if (!depends.children().any { it.name() == 'exclude' && it.@module.text() == m}) {
160 depends.appendNode {
161 println "excluding $o:$m"
162 exclude(org: o, module: m)
163 }
164 }
165 }
166
167 def ensure(depends,exists,requires) {
168
169 if (depends.children().any { it.name() == 'dependency' && it.@name == exists } ) {
170 requires.each {
171 def (org,name,rev) = it.split(":")
172 if (!depends.children().any { it.@name == name } ) {
173 println " adding requirement $org:$name for $exists"
174 depends.children().findAll { it.name() == 'dependency' }.list().last() + {
175 dependency(name: name, org: org, rev: rev)
176 }
177 }
178 }
179 }
180 }
181
182 def ensureSpringSourceExcludes(depends) {
183 def exclusions = []
184 depends.children().findAll { it.name() == 'dependency' && it.@name.text().startsWith('com.springsource.') }.each {
185 exclusions << "${it.@org}:${it.@name.text() - 'com.springsource.'}"
186 }
187 exclusions.each { ensureExcluded(depends,it) }
188
189 }
190
191 /*
192 The class below is a copy/paste of groovy's XmlNodePrinter customized to order Ivy.xml attributes in
193 the desired order and to add some whitespace.
194 */
195 class MyNodePrinter {
196
197 protected void printNameAttributes(Map attributes, ctx) {
198
199 if (attributes == null || attributes.isEmpty()) {
200 return;
201 }
202 def writer = new StringBuffer()
203 attributes.entrySet().sort{
204 switch (it.key ) {
205 case 'org':
206 1
207 break
208 case 'name': 2
209 break
210 case 'rev': 3
211 break
212 case 'extends': 5
213 break
214 default: 99
215 }
216 }.each { p ->
217 def tmp = new StringBuffer()
218 Map.Entry entry = (Map.Entry) p;
219
220 tmp << " "
221 tmp << getName(entry.getKey())
222 tmp << "="
223 Object value = entry.getValue();
224 tmp << quote
225 tmp << value
226 // if (value instanceof String) {
227 // printEscaped((String) value);
228 // } else {
229 // printEscaped(InvokerHelper.toString(value));
230 // }
231 tmp << quote
232 def size = entry.getKey() in ['org','name'] ? 35 : 22
233 writer << String.format(" %-${size}s",tmp.toString())
234 // printNamespace(entry.getKey(), ctx);
235 }
236 out.print(" " + writer.toString().trim())
237 }
238
239 protected final IndentPrinter out;
240 private String quote;
241 private boolean namespaceAware = true;
242 private boolean preserveWhitespace = false;
243
244 public MyNodePrinter(PrintWriter out) {
245 this(out, " ");
246 }
247
248 public MyNodePrinter(PrintWriter out, String indent) {
249 this(out, indent, "\"");
250 }
251
252 public MyNodePrinter(PrintWriter out, String indent, String quote) {
253 this(new IndentPrinter(out, indent), quote);
254 }
255
256 public MyNodePrinter(IndentPrinter out) {
257 this(out, "\"");
258 }
259
260 public MyNodePrinter(IndentPrinter out, String quote) {
261 if (out == null) {
262 throw new IllegalArgumentException("Argument 'IndentPrinter out' must not be null!");
263 }
264 this.out = out;
265 this.quote = quote;
266 }
267
268 public MyNodePrinter() {
269 this(new PrintWriter(new OutputStreamWriter(System.out)));
270 }
271
272 public void print(Node node) {
273 print(node, new NamespaceContext());
274 }
275
276 /**
277 * Check if namespace handling is enabled.
278 * Defaults to <code>true</code>.
279 *
280 * @return true if namespace handling is enabled
281 */
282 public boolean isNamespaceAware() {
283 return namespaceAware;
284 }
285
286 /**
287 * Enable and/or disable namespace handling.
288 *
289 * @param namespaceAware the new desired value
290 */
291 public void setNamespaceAware(boolean namespaceAware) {
292 this.namespaceAware = namespaceAware;
293 }
294
295 /**
296 * Check if whitespace preservation is enabled.
297 * Defaults to <code>false</code>.
298 *
299 * @return true if whitespaces are honoured when printing simple text nodes
300 */
301 public boolean isPreserveWhitespace() {
302 return preserveWhitespace;
303 }
304
305 /**
306 * Enable and/or disable preservation of whitespace.
307 *
308 * @param preserveWhitespace the new desired value
309 */
310 public void setPreserveWhitespace(boolean preserveWhitespace) {
311 this.preserveWhitespace = preserveWhitespace;
312 }
313
314 /**
315 * Get Quote to use when printing attributes.
316 *
317 * @return the quote character
318 */
319 public String getQuote() {
320 return quote;
321 }
322
323 /**
324 * Set Quote to use when printing attributes.
325 *
326 * @param quote the quote character
327 */
328 public void setQuote(String quote) {
329 this.quote = quote;
330 }
331
332 protected void print(Node node, NamespaceContext ctx) {
333 /*
334 * Handle empty elements like '<br/>', '<img/> or '<hr noshade="noshade"/>.
335 */
336 if (isEmptyElement(node)) {
337 printLineBegin();
338 out.print("<");
339 out.print(getName(node));
340 if (ctx != null) {
341 printNamespace(node, ctx);
342 }
343 printNameAttributes(node.attributes(), ctx);
344 out.print("/>");
345 printLineEnd();
346 out.flush();
347 return;
348 }
349
350 /*
351 * Hook for extra processing, e.g. GSP tag element!
352 */
353 if (printSpecialNode(node)) {
354 out.flush();
355 return;
356 }
357
358 /*
359 * Handle normal element like <html> ... </html>.
360 */
361 Object value = node.value();
362 if (value instanceof List) {
363 printName(node, ctx, true, isListOfSimple((List) value));
364 printList((List) value, ctx);
365 printName(node, ctx, false, isListOfSimple((List) value));
366 out.flush();
367 return;
368 }
369
370 // treat as simple type - probably a String
371 printName(node, ctx, true, preserveWhitespace);
372 printSimpleItemWithIndent(value);
373 printName(node, ctx, false, preserveWhitespace);
374 out.flush();
375 }
376
377 private boolean isListOfSimple(List value) {
378 for (Object p : value) {
379 if (p instanceof Node) return false;
380 }
381 return preserveWhitespace;
382 }
383
384 protected void printLineBegin() {
385 out.printIndent();
386 }
387
388 protected void printLineEnd() {
389 printLineEnd(null);
390 }
391
392 protected void printLineEnd(String comment) {
393 if (comment != null) {
394 out.print(" <!-- ");
395 out.print(comment);
396 out.print(" -->");
397 }
398 out.println();
399 out.flush();
400 }
401
402 protected void printList(List list, NamespaceContext ctx) {
403 out.incrementIndent();
404 for (Object value : list) {
405 NamespaceContext context = new NamespaceContext(ctx);
406 /*
407 * If the current value is a node, recurse into that node.
408 */
409 if (value instanceof Node) {
410 print((Node) value, context);
411 continue;
412 }
413 printSimpleItem(value);
414 }
415 out.decrementIndent();
416 }
417
418 protected void printSimpleItem(Object value) {
419 if (!preserveWhitespace) printLineBegin();
420 printEscaped(InvokerHelper.toString(value));
421 if (!preserveWhitespace) printLineEnd();
422 }
423
424 protected void printName(Node node, NamespaceContext ctx, boolean begin, boolean preserve) {
425 if (node == null) {
426 throw new NullPointerException("Node must not be null.");
427 }
428 Object name = node.name();
429 if (name == null) {
430 throw new NullPointerException("Name must not be null.");
431 }
432 if (!preserve || begin) printLineBegin();
433 out.print("<");
434 if (!begin) {
435 out.print("/");
436 }
437 out.print(getName(node));
438 if (ctx != null) {
439 printNamespace(node, ctx);
440 }
441 if (begin) {
442 printNameAttributes(node.attributes(), ctx);
443 }
444 out.print(">");
445 if (!preserve || !begin) printLineEnd();
446 }
447
448 protected boolean printSpecialNode(Node node) {
449 return false;
450 }
451
452 protected void printNamespace(Object object, NamespaceContext ctx) {
453 if (namespaceAware) {
454 if (object instanceof Node) {
455 printNamespace(((Node) object).name(), ctx);
456 } else if (object instanceof QName) {
457 QName qname = (QName) object;
458 String namespaceUri = qname.getNamespaceURI();
459 if (namespaceUri != null) {
460 String prefix = qname.getPrefix();
461 if (!ctx.isPrefixRegistered(prefix, namespaceUri)) {
462 ctx.registerNamespacePrefix(prefix, namespaceUri);
463 out.print(" ");
464 out.print("xmlns");
465 if (prefix.length() > 0) {
466 out.print(":");
467 out.print(prefix);
468 }
469 out.print("=" + quote);
470 out.print(namespaceUri);
471 out.print(quote);
472 }
473 }
474 }
475 }
476 }
477
478
479
480 private boolean isEmptyElement(Node node) {
481 if (node == null) {
482 throw new IllegalArgumentException("Node must not be null!");
483 }
484 if (!node.children().isEmpty()) {
485 return false;
486 }
487 return node.text().length() == 0;
488 }
489
490 private String getName(Object object) {
491 if (object instanceof String) {
492 return (String) object;
493 } else if (object instanceof QName) {
494 QName qname = (QName) object;
495 if (!namespaceAware) {
496 return qname.getLocalPart();
497 }
498 return qname.getQualifiedName();
499 } else if (object instanceof Node) {
500 Object name = ((Node) object).name();
501 return getName(name);
502 }
503 return object.toString();
504 }
505
506 private void printSimpleItemWithIndent(Object value) {
507 if (!preserveWhitespace) out.incrementIndent();
508 printSimpleItem(value);
509 if (!preserveWhitespace) out.decrementIndent();
510 }
511
512 // For ' and " we only escape if needed. As far as XML is concerned,
513 // we could always escape if we wanted to.
514 private void printEscaped(String s) {
515 for (int i = 0; i < s.length(); i++) {
516 char c = s.charAt(i);
517 switch (c) {
518 case '<':
519 out.print("&lt;");
520 break;
521 case '>':
522 out.print("&gt;");
523 break;
524 case '&':
525 out.print("&amp;");
526 break;
527 case '\'':
528 if (quote.equals("'"))
529 out.print("&apos;");
530 else
531 out.print(c);
532 break;
533 case '"':
534 if (quote.equals("\""))
535 out.print("&quot;");
536 else
537 out.print(c);
538 break;
539 default:
540 out.print(c);
541 }
542 }
543 }
544
545 protected class NamespaceContext {
546 private final Map<String, String> namespaceMap;
547
548 public NamespaceContext() {
549 namespaceMap = new HashMap<String, String>();
550 }
551
552 public NamespaceContext(NamespaceContext context) {
553 this();
554 namespaceMap.putAll(context.namespaceMap);
555 }
556
557 public boolean isPrefixRegistered(String prefix, String uri) {
558 return namespaceMap.containsKey(prefix) && namespaceMap.get(prefix).equals(uri);
559 }
560
561 public void registerNamespacePrefix(String prefix, String uri) {
562 if (!isPrefixRegistered(prefix, uri)) {
563 namespaceMap.put(prefix, uri);
564 }
565 }
566
567 public String getNamespace(String prefix) {
568 Object uri = namespaceMap.get(prefix);
569 return (uri == null) ? null : uri.toString();
570 }
571 }
572 }