view 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
line wrap: on
line source

import groovy.xml.QName;
import org.codehaus.groovy.runtime.InvokerHelper;

import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import groovy.xml.*

//args = [ '-r']
println args

ant = new AntBuilder()


if (args.any { it.toUpperCase() == '-R'} ) {
    
    new File('.').traverse ([ type: groovy.io.FileType.DIRECTORIES, maxDepth: 3 ]) { project -> 
        if (new File(project,'ivy.xml').exists() ) {
            processIvy(new File(project,'ivy.xml'))
        }
    }

} else {
    processIvy(new File('ivy.xml'))
} 

def processIvy(file) {
    println "-" * 60
    println "processing: $file"
    def xml = new XmlSlurper()
    def ivy = xml.parse(file)

    def cfgs =  ivy.configurations
    
    // default mapping for most dependencies:
    cfgs.@defaultconfmapping = 'compile->default(*)'
    
    // Add default config, if missing:
    if (!cfgs.children().find { it.@name == 'default' } ) {
        println "adding default config"
        cfgs.appendNode {
            conf(name:"default",extends:'compile',visibility:'public')
        }
    }    
    
    if (ivy.dependencies.children().find { it.@name == 'org.springframework.web.servlet' } &&
        !ivy.dependencies.children().find { it.@name == 'servlet-api' } ) {
        ivy.dependencies.children().findAll { it.name() == 'dependency' }.list().last() + {
            dependency(name:'servlet-api', org: 'javax.servlet', rev: '2.5')
            dependency(name:'jsp-api', org: 'javax.servlet', rev: '2.0')
        }
        
    }
    
    ivy.dependencies.children().each { dep ->
       if ( dep.@conf.text()?.contains('test') ) {
           dep.@conf = 'compile-test->default(*)' 
       } else if ( dep.@conf.text().contains('castor-ant') ) {
            dep.@conf = 'castor-ant->default(*)'
       }
       else {
           dep.attributes().remove('conf')
       }
    }
    
    // find groovy dependencies (or null)
    groovy = ivy.dependencies.children().find { it.@org == 'org.codehaus.groovy' && it.@name.text().startsWith('groovy') }
    
    if (groovy) {
        // Add a groovy conig for groovy projects:
        if (!cfgs.children().any { it.@name == 'groovy' } ) {
            println "added private groovy conf"
            cfgs.appendNode { 
                conf(name:'groovy',visibility:'private',transitive: true)
            }
        }
        // Adjust 'compile' to exend from 'groovy' conf
        cfgs.children().find { it.@name == 'compile' }.@extends = 'groovy'
        cfgs.children().find { it.@name == 'groovy' }.@transitive = 'true'
        groovy.@conf = "groovy->default"
        println "Groovy ${groovy.@rev} conf changed to ${groovy.@conf}"    
        // Switch project to use groovy-all instead of groovy + deps
        if (groovy.@name == 'groovy') {
            println "changed groovy to groovy-all"
            groovy.@name = 'groovy-all'
        }
        
        def template = new File(file.parent ?: ".",'template.mf')
        if (template.exists()) {
            println "updating groovy in $template"
            ant.replace(file: template, token: 'org.codehaus.groovy-groovy:major',value:'org.codehaus.groovy-groovy-all:major')
        }
        
    }
    
    
    ensure(ivy.dependencies, 'hibernate-validator', ['org.slf4j:slf4j-api:1.5.6','org.slf4j:slf4j-log4j12:1.5.6'])
    
    ensure(ivy.dependencies, 'org.springframework.test', ['junit:junit:4.8.2'])
    
    // remove exclude for groovy-all
    ivy.dependencies.children().find { it.name() == 'exclude' && it.@module.text() == 'groovy-all' }.replaceNode {}
   
   // Add exclude for regular groovy:
    ensureExcluded(ivy.dependencies,"org.codehaus.groovy:groovy")
    
    // ensure modules covered by springsource are exluded;
    ensureSpringSourceExcludes(ivy.dependencies)
    
    ensureConfiguration(ivy.dependencies,'org.springframework.test','compile-test')

    //remove bad junit dependencies and excludes
    ivy.dependencies.children().find { it.@org == 'org.junit' && it.@module.text() == 'org.junit' }.replaceNode {}
    ivy.dependencies.children().find { it.@org == 'org.junit' && it.@name.text() == 'org.junit' }.replaceNode {}

    
    cfgs.children().each { it.@visibility = 'public' }  // Should be 'private' after initial conversions.
        
    cfgs.children().find {it.@name == 'runtime' }.@extends = 'compile'
    cfgs.children().find {it.@name == 'compile-test' }.@extends = 'compile'
    cfgs.children().find {it.@name == 'runtime-test' }.@extends = 'runtime,compile-test'
    cfgs.children().find {it.@name == 'default' }.@visibility = 'public'
    cfgs.children().find {it.@name == 'default' }.@extends = 'runtime'
    
    if (!cfgs.children().any { it.@name == 'archives' } ) {
        println "added public 'archives' conf"
        cfgs.appendNode { 
           conf(name:'archives',visibility:'public')
        }
    }
    
    cfgs.children().find {it.@name == 'archives' }.@visibility = 'public'
    
    
    def outputBuilder = new StreamingMarkupBuilder()
    //new File('ivy.xml').write(  XmlUtil.serialize( outputBuilder.bind { mkp.yield ivy }).replaceAll('>','>'))
    
    def stringWriter = new StringWriter()
    
    
    def node = new XmlParser().parseText( XmlUtil.serialize( outputBuilder.bind { mkp.yield ivy }))
    new MyNodePrinter(new IndentPrinter( new PrintWriter(stringWriter))).print(node)
    file.write(stringWriter.toString())
    
}

def ensureConfiguration(depends,name,cfg) {
    depends.children().findAll { it.name() == 'dependency' && it.@name == name}.each {
        it.@conf = "$cfg->default(*)"
    }
}

def ensureExcluded(depends,excludeModule) {
    def (o,m) = excludeModule.split(':')
    if (!depends.children().any { it.name() == 'exclude' && it.@module.text() == m}) {
        depends.appendNode {
            println "excluding $o:$m"
            exclude(org: o, module: m)
        }
    }
}

def ensure(depends,exists,requires) {

    if (depends.children().any { it.name() == 'dependency' && it.@name == exists } ) {
        requires.each {
            def (org,name,rev) = it.split(":")
            if (!depends.children().any { it.@name == name } ) {   
                println "  adding requirement $org:$name for $exists"
                depends.children().findAll { it.name() == 'dependency' }.list().last() + {
                    dependency(name: name, org: org, rev: rev)
                }           
            }
        }        
    }
}

def ensureSpringSourceExcludes(depends) {
    def exclusions = []
    depends.children().findAll { it.name() == 'dependency' && it.@name.text().startsWith('com.springsource.') }.each {
        exclusions << "${it.@org}:${it.@name.text() - 'com.springsource.'}"        
    }   
    exclusions.each {  ensureExcluded(depends,it) }        

}

/*
The class below is a copy/paste of groovy's XmlNodePrinter customized to order Ivy.xml attributes in
the desired order and to add some whitespace.
*/
class MyNodePrinter  {

    protected void printNameAttributes(Map attributes,  ctx) {
     
       if (attributes == null || attributes.isEmpty()) {
           return;
       }
      def writer = new StringBuffer()
      attributes.entrySet().sort{
           switch (it.key ) {
               case 'org': 
                   1
                   break
               case 'name': 2
                   break
               case 'rev': 3
                   break
               case 'extends': 5
                   break
               default: 99
           }
       }.each { p ->
          def tmp = new StringBuffer()
           Map.Entry entry = (Map.Entry) p;
        
           tmp << " "
           tmp << getName(entry.getKey())
           tmp << "="
           Object value = entry.getValue();
           tmp << quote
           tmp << value         
//           if (value instanceof String) {
//               printEscaped((String) value);
//           } else {
//               printEscaped(InvokerHelper.toString(value));
//           }
           tmp << quote
           def size = entry.getKey() in ['org','name'] ? 35 : 22
           writer << String.format(" %-${size}s",tmp.toString())
//           printNamespace(entry.getKey(), ctx);
       }
       out.print(" " + writer.toString().trim())
   }

    protected final IndentPrinter out;
    private String quote;
    private boolean namespaceAware = true;
    private boolean preserveWhitespace = false;

    public MyNodePrinter(PrintWriter out) {
        this(out, "  ");
    }

    public MyNodePrinter(PrintWriter out, String indent) {
        this(out, indent, "\"");
    }

    public MyNodePrinter(PrintWriter out, String indent, String quote) {
        this(new IndentPrinter(out, indent), quote);
    }

    public MyNodePrinter(IndentPrinter out) {
        this(out, "\"");
    }

    public MyNodePrinter(IndentPrinter out, String quote) {
        if (out == null) {
            throw new IllegalArgumentException("Argument 'IndentPrinter out' must not be null!");
        }
        this.out = out;
        this.quote = quote;
    }

    public MyNodePrinter() {
        this(new PrintWriter(new OutputStreamWriter(System.out)));
    }

    public void print(Node node) {
        print(node, new NamespaceContext());
    }

    /**
     * Check if namespace handling is enabled.
     * Defaults to <code>true</code>.
     *
     * @return true if namespace handling is enabled
     */
    public boolean isNamespaceAware() {
        return namespaceAware;
    }

    /**
     * Enable and/or disable namespace handling.
     *
     * @param namespaceAware the new desired value
     */
    public void setNamespaceAware(boolean namespaceAware) {
        this.namespaceAware = namespaceAware;
    }

    /**
     * Check if whitespace preservation is enabled.
     * Defaults to <code>false</code>.
     *
     * @return true if whitespaces are honoured when printing simple text nodes
     */
    public boolean isPreserveWhitespace() {
        return preserveWhitespace;
    }

    /**
     * Enable and/or disable preservation of whitespace.
     *
     * @param preserveWhitespace the new desired value
     */
    public void setPreserveWhitespace(boolean preserveWhitespace) {
        this.preserveWhitespace = preserveWhitespace;
    }

    /**
     * Get Quote to use when printing attributes.
     *
     * @return the quote character
     */
    public String getQuote() {
        return quote;
    }

    /**
     * Set Quote to use when printing attributes.
     *
     * @param quote the quote character
     */
    public void setQuote(String quote) {
        this.quote = quote;
    }

    protected void print(Node node, NamespaceContext ctx) {
        /*
         * Handle empty elements like '<br/>', '<img/> or '<hr noshade="noshade"/>.
         */
        if (isEmptyElement(node)) {
            printLineBegin();
            out.print("<");
            out.print(getName(node));
            if (ctx != null) {
                printNamespace(node, ctx);
            }
            printNameAttributes(node.attributes(), ctx);
            out.print("/>");
            printLineEnd();
            out.flush();
            return;
        }

        /*
         * Hook for extra processing, e.g. GSP tag element!
         */
        if (printSpecialNode(node)) {
            out.flush();
            return;
        }

        /*
         * Handle normal element like <html> ... </html>.
         */
        Object value = node.value();
        if (value instanceof List) {
            printName(node, ctx, true, isListOfSimple((List) value));
            printList((List) value, ctx);
            printName(node, ctx, false, isListOfSimple((List) value));
            out.flush();
            return;
        }

        // treat as simple type - probably a String
        printName(node, ctx, true, preserveWhitespace);
        printSimpleItemWithIndent(value);
        printName(node, ctx, false, preserveWhitespace);
        out.flush();
    }

    private boolean isListOfSimple(List value) {
        for (Object p : value) {
            if (p instanceof Node) return false;
        }
        return preserveWhitespace;
    }

    protected void printLineBegin() {
        out.printIndent();
    }

    protected void printLineEnd() {
        printLineEnd(null);
    }

    protected void printLineEnd(String comment) {
        if (comment != null) {
            out.print(" <!-- ");
            out.print(comment);
            out.print(" -->");
        }
        out.println();
        out.flush();
    }

    protected void printList(List list, NamespaceContext ctx) {
        out.incrementIndent();
        for (Object value : list) {
            NamespaceContext context = new NamespaceContext(ctx);
            /*
             * If the current value is a node, recurse into that node.
             */
            if (value instanceof Node) {
                print((Node) value, context);
                continue;
            }
            printSimpleItem(value);
        }
        out.decrementIndent();
    }

    protected void printSimpleItem(Object value) {
        if (!preserveWhitespace) printLineBegin();
        printEscaped(InvokerHelper.toString(value));
        if (!preserveWhitespace) printLineEnd();
    }

    protected void printName(Node node, NamespaceContext ctx, boolean begin, boolean preserve) {
        if (node == null) {
            throw new NullPointerException("Node must not be null.");
        }
        Object name = node.name();
        if (name == null) {
            throw new NullPointerException("Name must not be null.");
        }
        if (!preserve || begin) printLineBegin();
        out.print("<");
        if (!begin) {
            out.print("/");
        }
        out.print(getName(node));
        if (ctx != null) {
            printNamespace(node, ctx);
        }
        if (begin) {
            printNameAttributes(node.attributes(), ctx);
        }
        out.print(">");
        if (!preserve || !begin) printLineEnd();
    }

    protected boolean printSpecialNode(Node node) {
        return false;
    }

    protected void printNamespace(Object object, NamespaceContext ctx) {
        if (namespaceAware) {
            if (object instanceof Node) {
                printNamespace(((Node) object).name(), ctx);
            } else if (object instanceof QName) {
                QName qname = (QName) object;
                String namespaceUri = qname.getNamespaceURI();
                if (namespaceUri != null) {
                    String prefix = qname.getPrefix();
                    if (!ctx.isPrefixRegistered(prefix, namespaceUri)) {
                        ctx.registerNamespacePrefix(prefix, namespaceUri);
                        out.print(" ");
                        out.print("xmlns");
                        if (prefix.length() > 0) {
                            out.print(":");
                            out.print(prefix);
                        }
                        out.print("=" + quote);
                        out.print(namespaceUri);
                        out.print(quote);
                    }
                }
            }
        }
    }

  

    private boolean isEmptyElement(Node node) {
        if (node == null) {
            throw new IllegalArgumentException("Node must not be null!");
        }
        if (!node.children().isEmpty()) {
            return false;
        }
        return node.text().length() == 0;
    }

    private String getName(Object object) {
        if (object instanceof String) {
            return (String) object;
        } else if (object instanceof QName) {
            QName qname = (QName) object;
            if (!namespaceAware) {
                return qname.getLocalPart();
            }
            return qname.getQualifiedName();
        } else if (object instanceof Node) {
            Object name = ((Node) object).name();
            return getName(name);
        }
        return object.toString();
    }

    private void printSimpleItemWithIndent(Object value) {
        if (!preserveWhitespace) out.incrementIndent();
        printSimpleItem(value);
        if (!preserveWhitespace) out.decrementIndent();
    }

    // For ' and " we only escape if needed. As far as XML is concerned,
    // we could always escape if we wanted to.
    private void printEscaped(String s) {
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
                case '<':
                    out.print("&lt;");
                    break;
                case '>':
                    out.print("&gt;");
                    break;
                case '&':
                    out.print("&amp;");
                    break;
                case '\'':
                    if (quote.equals("'"))
                        out.print("&apos;");
                    else
                        out.print(c);
                    break;
                case '"':
                    if (quote.equals("\""))
                        out.print("&quot;");
                    else
                        out.print(c);
                    break;
                default:
                    out.print(c);
            }
        }
    }

    protected class NamespaceContext {
        private final Map<String, String> namespaceMap;

        public NamespaceContext() {
            namespaceMap = new HashMap<String, String>();
        }

        public NamespaceContext(NamespaceContext context) {
            this();
            namespaceMap.putAll(context.namespaceMap);
        }

        public boolean isPrefixRegistered(String prefix, String uri) {
            return namespaceMap.containsKey(prefix) && namespaceMap.get(prefix).equals(uri);
        }

        public void registerNamespacePrefix(String prefix, String uri) {
            if (!isPrefixRegistered(prefix, uri)) {
                namespaceMap.put(prefix, uri);
            }
        }

        public String getNamespace(String prefix) {
            Object uri = namespaceMap.get(prefix);
            return (uri == null) ? null : uri.toString();
        }
    }
}