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) 2006 Srivathsan Varadarajan
011 * Copyright (C) 2008 Matt Cordes
012 * Copyright (C) 2008 John Lewis
013 * Copyright (C) 2010 Piotr Tabor
014 *
015 * Redistribution and use in source and binary forms, with or without
016 * modification, are permitted provided that the following conditions
017 * are met:
018 *
019 * 1. Redistributions of source code must retain the above copyright
020 *    notice, this list of conditions and the following disclaimer.
021 *
022 * 2. Redistributions in binary form must reproduce the above copyright
023 *    notice, this list of conditions and the following disclaimer in
024 *    the documentation and/or other materials provided with the
025 *    distribution.
026 *
027 * 3. The end-user documentation included with the redistribution, if
028 *    any, must include the following acknowlegement:
029 *       "This product includes software developed by the
030 *        Apache Software Foundation (http://www.apache.org/)."
031 *    Alternately, this acknowlegement may appear in the software itself,
032 *    if and wherever such third-party acknowlegements normally appear.
033 *
034 * 4. The names "Ant" and "Apache Software
035 *    Foundation" must not be used to endorse or promote products derived
036 *    from this software without prior written permission. For written
037 *    permission, please contact apache@apache.org.
038 *
039 * 5. Products derived from this software may not be called "Apache"
040 *    nor may "Apache" appear in their names without prior written
041 *    permission of the Apache Group.
042 *
043 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
044 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
045 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
046 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
047 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
048 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
049 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
050 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
051 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
052 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
053 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
054 * SUCH DAMAGE.
055 * ====================================================================
056 *
057 * This software consists of voluntary contributions made by many
058 * individuals on behalf of the Apache Software Foundation.  For more
059 * information on the Apache Software Foundation, please see
060 * <http://www.apache.org/>.
061 */
062
063package net.sourceforge.cobertura.ant;
064
065import java.io.File;
066import java.io.IOException;
067import java.net.URL;
068import java.net.URLClassLoader;
069import java.util.LinkedList;
070import java.util.List;
071
072import net.sourceforge.cobertura.util.CommandLineBuilder;
073import net.sourceforge.cobertura.util.StringUtil;
074
075import org.apache.tools.ant.AntClassLoader;
076import org.apache.tools.ant.BuildException;
077import org.apache.tools.ant.DirectoryScanner;
078import org.apache.tools.ant.Project;
079import org.apache.tools.ant.taskdefs.Java;
080import org.apache.tools.ant.taskdefs.MatchingTask;
081import org.apache.tools.ant.types.FileSet;
082import org.apache.tools.ant.types.AbstractFileSet;
083import org.apache.tools.ant.types.DirSet;
084import org.apache.tools.ant.types.Path;
085import org.apache.tools.ant.types.Reference;
086
087public abstract class CommonMatchingTask extends MatchingTask
088{
089
090        final String className;
091        final List<AbstractFileSet> fileSets = new LinkedList<AbstractFileSet>();
092
093        private Java java = null;
094        private String maxMemory = null;
095        private int forkedJVMDebugPort;
096        protected boolean failOnError = false;
097
098        public CommonMatchingTask(String className)
099        {
100                this.className = className;
101        }
102
103        private String getClassName()
104        {
105                return className;
106        }
107
108        protected Java getJava()
109        {
110                if (java == null)
111                {
112                        java = (Java)getProject().createTask("java");
113                        java.setTaskName(getTaskName());
114                        java.setClassname(getClassName());
115                        java.setFork(true);
116                        java.setFailonerror(failOnError);
117                        java.setDir(getProject().getBaseDir());
118                        if (maxMemory != null)
119                                java.setJvmargs("-Xmx" + maxMemory);
120                        if (forkedJVMDebugPort > 0)
121                        {
122                                java.setJvmargs("-Xdebug");
123                                java.setJvmargs("-Xrunjdwp:transport=dt_socket,address=" + forkedJVMDebugPort + ",server=y,suspend=y");
124                        }
125
126                        /**
127                         * We replace %20 with a space character because, for some
128                         * reason, when we call Cobertura from within CruiseControl,
129                         * the classpath here contains %20's instead of spaces.  I
130                         * don't know if this is our problem, or CruiseControl, or
131                         * ant, but this seems to fix it.  --Mark
132                         */
133                        if (getClass().getClassLoader() instanceof AntClassLoader)
134                        {
135                                String classpath = ((AntClassLoader)getClass()
136                                                .getClassLoader()).getClasspath();
137                                createClasspath().setPath(
138                                                StringUtil.replaceAll(classpath, "%20", " "));
139                        }
140                        else if (getClass().getClassLoader() instanceof URLClassLoader)
141                        {
142                                URL[] earls = ((URLClassLoader)getClass().getClassLoader())
143                                                .getURLs();
144                                for (int i = 0; i < earls.length; i++)
145                                {
146                                        String classpath = (new File(earls[i].getFile())).getAbsolutePath();
147                                        createClasspath().setPath(
148                                                        StringUtil.replaceAll(classpath, "%20", " "));
149                                }
150                        }
151                }
152
153                return java;
154        }
155
156        protected void createArgumentsForFilesets( CommandLineBuilder builder) throws IOException {
157                boolean filesetFound = false;
158                for (AbstractFileSet fileSet : fileSets)
159                {
160                        if (fileSet instanceof FileSet) {
161                                filesetFound = true;
162                                builder.addArg("--basedir", baseDir(fileSet));
163                                createArgumentsForFilenames( builder, getFilenames(fileSet));
164                        } else {
165                                if (filesetFound) {
166                                        /*
167                                         * Once --basedir has been used, it cannot be undone without changes to the
168                                         * Main methods.   So, any dirsets have to come before filesets.
169                                         */
170                                        throw new BuildException("Dirsets have to come before filesets");
171                                }
172                                createArgumentsForFilenames( builder, getDirectoryScanner(fileSet).getIncludedDirectories());
173                        }
174                }
175        }
176
177        private void createArgumentsForFilenames( CommandLineBuilder builder, String[] filenames) throws IOException
178        {
179                for (int i = 0; i < filenames.length; i++)
180                {
181                        getProject().log("Adding " + filenames[i] + " to list",
182                                        Project.MSG_VERBOSE);
183                        builder.addArg(filenames[i]);
184                }
185        }
186
187        public Path createClasspath()
188        {
189                return getJava().createClasspath().createPath();
190        }
191
192        public void setClasspath(Path classpath)
193        {
194                createClasspath().append(classpath);
195        }
196
197        public void setClasspathRef(Reference r)
198        {
199                createClasspath().setRefid(r);
200        }
201
202        DirectoryScanner getDirectoryScanner(AbstractFileSet fileSet)
203        {
204                return fileSet.getDirectoryScanner(getProject());
205        }
206
207        String[] getIncludedFiles(AbstractFileSet fileSet)
208        {
209                return getDirectoryScanner(fileSet).getIncludedFiles();
210        }
211
212        String[] getExcludedFiles(FileSet fileSet)
213        {
214                return getDirectoryScanner(fileSet).getExcludedFiles();
215        }
216
217        String[] getFilenames(AbstractFileSet fileSet)
218        {
219                String[] filesToReturn = getIncludedFiles(fileSet);
220
221                return filesToReturn;
222        }
223
224        String baseDir(AbstractFileSet fileSet)
225        {
226                return fileSet.getDirectoryScanner(getProject()).getBasedir()
227                                .toString();
228        }
229
230        public void addDirSet(DirSet dirSet)
231        {
232                fileSets.add(dirSet);
233        }
234        
235        public void addFileset(FileSet fileSet)
236        {
237                fileSets.add(fileSet);
238        }
239
240        /**
241         * @param maxMemory Assumed to be something along the lines of
242         *        100M or 50K or 1G.
243         */
244        public void setMaxMemory(String maxMemory)
245        {
246                this.maxMemory = maxMemory != null ? maxMemory.trim() : null;
247        }
248        
249        /**
250         * Used to debug the process that is forked to perform the operation.
251         * Setting this to a non-zero number will cause the process to open
252         * a debug port on that port number.   It will suspend until a 
253         * remote debugger is attached to the port.
254         * 
255         * @param forkedJVMDebugPort
256         */
257        public void setForkedJVMDebugPort(int forkedJVMDebugPort)
258        {
259                this.forkedJVMDebugPort = forkedJVMDebugPort;
260        }
261
262    /**
263     * If true, then fail if the command exits with a
264     * returncode other than zero.
265     *
266     * @param fail if true fail the build when the command exits with a
267     * nonzero returncode.
268     */
269    public void setFailonerror(boolean fail) {
270        failOnError = fail;
271    }
272
273}