InstrProcessor.java revision f6fe897e173f4e4bda72a7dddb091b667066764a
1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2 *
3 * This program and the accompanying materials are made available under
4 * the terms of the Common Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/cpl-v10.html
6 *
7 * $Id: InstrProcessor.java,v 1.1.1.1.2.3 2004/07/17 16:57:14 vlad_r Exp $
8 */
9package com.vladium.emma.instr;
10
11import java.io.File;
12
13import com.vladium.util.Files;
14import com.vladium.util.IConstants;
15import com.vladium.util.IPathEnumerator;
16import com.vladium.util.asserts.$assert;
17import com.vladium.emma.IAppErrorCodes;
18import com.vladium.emma.EMMARuntimeException;
19import com.vladium.emma.Processor;
20import com.vladium.emma.filter.IInclExclFilter;
21
22// ----------------------------------------------------------------------------
23/*
24 * This class was not meant to be public by design. It is made to to work around
25 * access bugs in reflective invocations.
26 */
27/**
28 * @author Vlad Roubtsov, (C) 2003
29 */
30public
31abstract class InstrProcessor extends Processor
32                              implements IPathEnumerator.IPathHandler
33{
34    // public: ................................................................
35
36
37    public static final String PROPERTY_EXCLUDE_SYNTHETIC_METHODS   = "instr.exclude_synthetic_methods";
38    public static final String PROPERTY_EXCLUDE_BRIDGE_METHODS      = "instr.exclude_bridge_methods";
39    public static final String PROPERTY_DO_SUID_COMPENSATION        = "instr.do_suid_compensation";
40
41    public static final String DEFAULT_EXCLUDE_SYNTHETIC_METHODS    = "true";
42    public static final String DEFAULT_EXCLUDE_BRIDGE_METHODS       = "true";
43    public static final String DEFAULT_DO_SUID_COMPENSATION         = "true";
44
45    public static InstrProcessor create ()
46    {
47        return new InstrProcessorST ();
48    }
49
50    /**
51     *
52     * @param path [null is equivalent to an empty array]
53     * @param canonical
54     */
55    public synchronized final void setInstrPath (final String [] path, final boolean canonical)
56    {
57        if ((path == null) || (path.length == 0))
58            m_instrPath = IConstants.EMPTY_FILE_ARRAY;
59        else
60            m_instrPath = Files.pathToFiles (path, canonical);
61
62        m_canonical = canonical;
63    }
64
65    public synchronized final void setDependsMode (final boolean enable)
66    {
67        m_dependsMode = enable;
68    }
69
70    /**
71     *
72     * @param specs [null is equivalent to no filtering (everything is included)]
73     */
74    public synchronized final void setInclExclFilter (final String [] specs)
75    {
76        if (specs == null)
77            m_coverageFilter = null;
78        else
79            m_coverageFilter = IInclExclFilter.Factory.create (specs);
80    }
81
82    /**
83     *
84     * @param fileName [null unsets the previous override setting]
85     */
86    public synchronized final void setMetaOutFile (final String fileName)
87    {
88        if (fileName == null)
89            m_mdataOutFile = null;
90        else
91        {
92            final File _file = new File (fileName);
93
94            if (_file.exists () && ! _file.isFile ())
95                throw new IllegalArgumentException ("not a file: [" + _file.getAbsolutePath () + "]");
96
97            m_mdataOutFile = _file;
98        }
99    }
100
101    /**
102     *
103     * @param merge [null unsets the previous override setting]
104     */
105    public synchronized final void setMetaOutMerge (final Boolean merge)
106    {
107        m_mdataOutMerge = merge;
108    }
109
110    /**
111     *
112     * @param dir [null unsets the previous setting]
113     */
114    public synchronized final void setInstrOutDir (final String dir)
115    {
116        if (dir == null)
117            m_outDir = null;
118        else
119        {
120            final File _outDir = new File (dir);
121            if (_outDir.exists () && ! _outDir.isDirectory ())
122                throw new IllegalArgumentException ("not a directory: [" + _outDir.getAbsolutePath () + "]");
123
124            m_outDir = _outDir;
125        }
126    }
127
128    /**
129     *
130     * @param mode [may not be null]
131     */
132    public synchronized final void setOutMode (final OutMode mode)
133    {
134        if (mode == null)
135            throw new IllegalArgumentException ("null input: mode");
136
137        m_outMode = mode;
138    }
139
140    // protected: .............................................................
141
142
143    protected InstrProcessor ()
144    {
145        m_dependsMode = true;
146    }
147
148
149    protected void validateState ()
150    {
151        super.validateState ();
152
153        if ((m_instrPath == null) || (m_instrPath.length == 0))
154            throw new IllegalStateException ("instrumentation path not set");
155
156        // [m_coverageFilter can be null]
157
158        if (m_outMode == null)
159            throw new IllegalStateException ("output mode not set");
160
161        if (m_outMode != OutMode.OUT_MODE_OVERWRITE)
162        {
163            if (m_outDir == null)
164                throw new IllegalStateException ("output directory not set");
165
166            // for non-overwrite modes output directory must not overlap
167            // with the instr path:
168
169            // [the logic below does not quite catch all possibilities due to
170            // Class-Path: manifest attributes and dir nesting, but it should
171            // intercept most common mistakes]
172
173            if ($assert.ENABLED)
174            {
175                $assert.ASSERT (m_outDir != null, "m_outDir = null");
176                $assert.ASSERT (m_instrPath != null, "m_instrPath = null");
177            }
178
179            final File canonicalOutDir = Files.canonicalizeFile (m_outDir);
180            final File [] canonicalInstrPath;
181
182            if (m_canonical)
183                canonicalInstrPath = m_instrPath;
184            else
185            {
186                canonicalInstrPath = new File [m_instrPath.length];
187
188                for (int ip = 0; ip < canonicalInstrPath.length; ++ ip)
189                {
190                    canonicalInstrPath [ip] = Files.canonicalizeFile (m_instrPath [ip]);
191                }
192            }
193
194            // FR_SF988785: detect if the user attempted to use a parent of m_outDir as one of
195            // the input directories (prevents spurious "already instrumented" errors)
196
197            final int instrPathLength = canonicalInstrPath.length;
198            for (File dir = canonicalOutDir; dir != null; dir = dir.getParentFile ()) // getParentFile() does no real I/O
199            {
200                for (int ip = 0; ip < instrPathLength; ++ ip)
201                {
202                    if (dir.equals (canonicalInstrPath [ip]))
203                        throw new IllegalStateException ("output directory [" + canonicalOutDir + "] cannot be one of the instrumentation path directories (or a child thereof)");
204                }
205            }
206        }
207
208        // [m_mdataOutFile can be null]
209        // [m_mdataOutMerge can be null]
210    }
211
212    protected void reset ()
213    {
214        m_classCopies = m_classInstrs = 0;
215    }
216
217    protected final void createDir (final File dir, final boolean mkall)
218        throws EMMARuntimeException
219    {
220        if (mkall)
221        {
222            if (! dir.mkdirs () && ! dir.exists ())
223                throw new EMMARuntimeException (IAppErrorCodes.OUT_MKDIR_FAILURE, new Object [] {dir.getAbsolutePath ()});
224        }
225        else
226        {
227            if (! dir.mkdir () && ! dir.exists ())
228                throw new EMMARuntimeException (IAppErrorCodes.OUT_MKDIR_FAILURE, new Object [] {dir.getAbsolutePath ()});
229        }
230    }
231
232    protected final File getFullOutDir (final File pathDir, final boolean isClass)
233    {
234        if (m_outMode == OutMode.OUT_MODE_OVERWRITE)
235        {
236            return pathDir;
237        }
238        else if (m_outMode == OutMode.OUT_MODE_COPY)
239        {
240            return m_outDir;
241        }
242        else if (m_outMode == OutMode.OUT_MODE_FULLCOPY)
243        {
244            return isClass ? Files.newFile (m_outDir, CLASSES) : Files.newFile (m_outDir, LIB);
245        }
246        else throw new IllegalStateException ("invalid out mode state: " + m_outMode);
247    }
248
249    protected final File getFullOutFile (final File pathDir, final File file, final boolean isClass)
250    {
251        return Files.newFile (getFullOutDir (pathDir, isClass), file.getPath ());
252    }
253
254
255    // caller-settable state [scoped to this runner instance]:
256
257    protected File [] m_instrPath; // required to be non-null/non-empty for run()
258    protected boolean m_dependsMode;
259    protected boolean m_canonical;
260    protected IInclExclFilter m_coverageFilter; // can be null for run()
261    protected OutMode m_outMode; // required to be set for run()
262    protected File m_outDir; // required to be non-null for run(), unless output mode is 'overwrite'
263    protected File m_mdataOutFile; // user override; can be null for run()
264    protected Boolean m_mdataOutMerge; // user override; can be null for run()
265
266    // internal run()-scoped state:
267
268    protected int m_classCopies, m_classInstrs;
269
270    protected static final String CLASSES   = "classes";
271    protected static final String LIB       = "lib";
272    protected static final boolean IN_CLASSES   = true;
273    protected static final boolean IN_LIB       = ! IN_CLASSES;
274
275    // package: ...............................................................
276
277// TODO: access level [public to workaround Sun's bugs in access level in reflective invocations]
278    public static final class OutMode
279    {
280        public static final OutMode OUT_MODE_COPY = new OutMode ("copy");
281        public static final OutMode OUT_MODE_FULLCOPY = new OutMode ("fullcopy");
282        public static final OutMode OUT_MODE_OVERWRITE = new OutMode ("overwrite");
283
284        public String getName ()
285        {
286            return m_name;
287        }
288
289        public String toString ()
290        {
291            return m_name;
292        }
293
294        public static OutMode nameToMode (final String name)
295        {
296            if (OUT_MODE_COPY.m_name.equals (name))
297                return OUT_MODE_COPY;
298            else if (OUT_MODE_FULLCOPY.m_name.equals (name))
299                return OUT_MODE_FULLCOPY;
300            else if (OUT_MODE_OVERWRITE.m_name.equals (name))
301                return OUT_MODE_OVERWRITE;
302
303            return null;
304        }
305
306        private OutMode (final String name)
307        {
308            m_name = name;
309        }
310
311
312        private final String m_name;
313
314    } // end of nested class
315
316    // private: ...............................................................
317
318} // end of class
319// ----------------------------------------------------------------------------