001    /*
002     * The Apache Software License, Version 1.1
003     *
004     * Copyright (C) 2000-2002 The Apache Software Foundation.  All rights
005     * reserved.
006     * Copyright (C) 2003 jcoverage ltd.
007     * Copyright (C) 2005 Mark Doliner
008     * Copyright (C) 2005 Joakim Erdfelt
009     * Copyright (C) 2005 Grzegorz Lukasik
010     * Copyright (C) 2005 Alexei Yudichev
011     * Copyright (C) 2006 John Lewis
012     * Copyright (C) 2006 Jiri Mares 
013     *
014     * Redistribution and use in source and binary forms, with or without
015     * modification, are permitted provided that the following conditions
016     * are met:
017     *
018     * 1. Redistributions of source code must retain the above copyright
019     *    notice, this list of conditions and the following disclaimer.
020     *
021     * 2. Redistributions in binary form must reproduce the above copyright
022     *    notice, this list of conditions and the following disclaimer in
023     *    the documentation and/or other materials provided with the
024     *    distribution.
025     *
026     * 3. The end-user documentation included with the redistribution, if
027     *    any, must include the following acknowlegement:
028     *       "This product includes software developed by the
029     *        Apache Software Foundation (http://www.apache.org/)."
030     *    Alternately, this acknowlegement may appear in the software itself,
031     *    if and wherever such third-party acknowlegements normally appear.
032     *
033     * 4. The names "Ant" and "Apache Software
034     *    Foundation" must not be used to endorse or promote products derived
035     *    from this software without prior written permission. For written
036     *    permission, please contact apache@apache.org.
037     *
038     * 5. Products derived from this software may not be called "Apache"
039     *    nor may "Apache" appear in their names without prior written
040     *    permission of the Apache Group.
041     *
042     * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
043     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
044     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
045     * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
046     * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
047     * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
048     * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
049     * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
050     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
051     * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
052     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
053     * SUCH DAMAGE.
054     * ====================================================================
055     *
056     * This software consists of voluntary contributions made by many
057     * individuals on behalf of the Apache Software Foundation.  For more
058     * information on the Apache Software Foundation, please see
059     * <http://www.apache.org/>.
060     */
061    
062    package net.sourceforge.cobertura.ant;
063    
064    import java.io.File;
065    import java.io.IOException;
066    import java.util.ArrayList;
067    import java.util.HashMap;
068    import java.util.List;
069    
070    import net.sourceforge.cobertura.util.CommandLineBuilder;
071    
072    import org.apache.tools.ant.BuildException;
073    import org.apache.tools.ant.Project;
074    import org.apache.tools.ant.types.FileSet;
075    import org.apache.tools.ant.types.Path;
076    
077    public class InstrumentTask extends CommonMatchingTask
078    {
079    
080            private String dataFile = null;
081    
082            private File toDir = null;
083    
084            List ignoreRegexs = new ArrayList();
085    
086            List ignoreBranchesRegexs = new ArrayList();
087            
088            List includeClassesRegexs = new ArrayList();
089    
090            List excludeClassesRegexs = new ArrayList();
091    
092            private Integer forkedJVMDebugPort;
093            
094            private Path instrumentationClasspath = null;
095    
096            private HashMap fileSetMap = new HashMap();
097    
098            public InstrumentTask()
099            {
100                    super("net.sourceforge.cobertura.instrument.Main");
101            }
102    
103            public Ignore createIgnore()
104            {
105                    Ignore ignoreRegex = new Ignore();
106                    ignoreRegexs.add(ignoreRegex);
107                    return ignoreRegex;
108            }
109    
110            public IgnoreBranches createIgnoreBranches()
111            {
112                    IgnoreBranches ignoreBranchesRegex = new IgnoreBranches();
113                    ignoreBranchesRegexs.add(ignoreBranchesRegex);
114                    return ignoreBranchesRegex;
115            }
116    
117            public IncludeClasses createIncludeClasses()
118            {
119                    IncludeClasses includeClassesRegex = new IncludeClasses();
120                    includeClassesRegexs.add(includeClassesRegex);
121                    return includeClassesRegex;
122            }
123    
124            public ExcludeClasses createExcludeClasses()
125            {
126                    ExcludeClasses excludeClassesRegex = new ExcludeClasses();
127                    excludeClassesRegexs.add(excludeClassesRegex);
128                    return excludeClassesRegex;
129            }
130    
131            public Path createInstrumentationClasspath()
132            {
133                    if (instrumentationClasspath == null) {
134                            instrumentationClasspath = new Path(getProject());
135                    }
136                    return instrumentationClasspath.createPath();
137            }
138    
139            /*
140             * TODO: Is the following method needed to use a classpath ref?  If so,
141             *       test it and uncomment it.
142             */
143            /*
144            public void setInstrumentationClasspathRef(Reference r)
145            {
146                    createInstrumentationClasspath().setRefid(r);
147            }
148            */
149    
150            public void execute() throws BuildException
151            {
152                    CommandLineBuilder builder = null;
153                    try {
154                            builder = new CommandLineBuilder();
155                            if (dataFile != null)
156                                    builder.addArg("--datafile", dataFile);
157                            if (toDir != null)
158                                    builder.addArg("--destination", toDir.getAbsolutePath());
159    
160                            for (int i = 0; i < ignoreRegexs.size(); i++) {
161                                    Ignore ignoreRegex = (Ignore)ignoreRegexs.get(i);
162                                    builder.addArg("--ignore", ignoreRegex.getRegex());
163                            }
164    
165                            for (int i = 0; i < ignoreBranchesRegexs.size(); i++) {
166                                    IgnoreBranches ignoreBranchesRegex = (IgnoreBranches)ignoreBranchesRegexs.get(i);
167                                    builder.addArg("--ignoreBranches", ignoreBranchesRegex.getRegex());
168                            }
169    
170                            for (int i = 0; i < includeClassesRegexs.size(); i++) {
171                                    IncludeClasses includeClassesRegex = (IncludeClasses)includeClassesRegexs.get(i);
172                                    builder.addArg("--includeClasses", includeClassesRegex.getRegex());
173                            }
174    
175                            for (int i = 0; i < excludeClassesRegexs.size(); i++) {
176                                    ExcludeClasses excludeClassesRegex = (ExcludeClasses)excludeClassesRegexs.get(i);
177                                    builder.addArg("--excludeClasses", excludeClassesRegex.getRegex());
178                            }
179    
180                            if (instrumentationClasspath != null) {
181                                    processInstrumentationClasspath();
182                            }
183                            createArgumentsForFilesets(builder);
184    
185                            builder.saveArgs();
186                    } catch (IOException ioe) {
187                            getProject().log("Error creating commands file.", Project.MSG_ERR);
188                            throw new BuildException("Unable to create the commands file.", ioe);
189                    }
190    
191                    // Execute GPL licensed code in separate virtual machine
192                    getJava().createArg().setValue("--commandsfile");
193                    getJava().createArg().setValue(builder.getCommandLineFile());
194                    if (forkedJVMDebugPort != null && forkedJVMDebugPort.intValue() > 0) {
195                            getJava().createJvmarg().setValue("-Xdebug");
196                            getJava().createJvmarg().setValue("-Xrunjdwp:transport=dt_socket,address=" + forkedJVMDebugPort + ",server=y,suspend=y");
197                    }
198                    AntUtil.transferCoberturaDataFileProperty(getJava());
199                    if (getJava().executeJava() != 0) {
200                            throw new BuildException(
201                                            "Error instrumenting classes. See messages above.");
202                    }
203    
204                    builder.dispose();
205            }
206    
207            private void processInstrumentationClasspath()
208            {
209                    if (includeClassesRegexs.size() == 0)
210                    {
211                            throw new BuildException("'includeClasses' is required when 'instrumentationClasspath' is used");
212                    }
213    
214                    String[] sources = instrumentationClasspath.list();
215                    for (int i = 0; i < sources.length; i++) {
216                            File fileOrDir = new File(sources[i]);
217                            if (fileOrDir.exists())
218                            {
219                                    if (fileOrDir.isDirectory()) {
220                                            createFilesetForDirectory(fileOrDir);
221                                    } else {
222                                            addFileToFilesets(fileOrDir);
223                                    }
224                            }
225                    }
226            }
227    
228            private void addFileToFilesets(File file)
229            {
230                    File dir = file.getParentFile();
231                    String filename = file.getName();
232                    FileSet fileSet = getFileSet(dir);
233                    fileSet.createInclude().setName(filename);
234            }
235    
236            private FileSet getFileSet(File dir)
237            {
238                    String key = dir.getAbsolutePath();
239                    FileSet fileSet = (FileSet)fileSetMap.get(key);
240                    if (fileSet == null)
241                    {
242                    fileSet = new FileSet();
243                    fileSet.setProject(getProject());
244                    fileSet.setDir(dir);
245    
246                    // Now add the new fileset to the map and to the fileSets list 
247                    fileSetMap.put(key, fileSet);
248                    addFileset(fileSet);
249                    }
250                    return fileSet;
251            }
252    
253            private void createFilesetForDirectory(File dir)
254            {
255                    FileSet fileSet = getFileSet(dir);
256                    fileSet.createInclude().setName("**/*.class");
257            }
258    
259            public void setDataFile(String dataFile)
260            {
261                    this.dataFile = dataFile;
262            }
263    
264            public void setToDir(File toDir)
265            {
266                    this.toDir = toDir;
267            }
268    
269            public void setForkedJVMDebugPort(Integer forkedJVMDebugPort)
270            {
271                    this.forkedJVMDebugPort = forkedJVMDebugPort;
272            }
273    
274    }