AbstractTransformTask.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
1/*
2 * Copyright 2003,2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16package org.mockito.cglib.transform;
17
18import java.io.*;
19import java.net.MalformedURLException;
20import java.util.*;
21import java.util.zip.*;
22
23import org.apache.tools.ant.*;
24import org.mockito.asm.*;
25import org.mockito.cglib.core.*;
26
27abstract public class AbstractTransformTask extends AbstractProcessTask {
28    private static final int ZIP_MAGIC = 0x504B0304;
29
30    private static final int CLASS_MAGIC = 0xCAFEBABE;
31
32    private boolean verbose;
33
34    public void setVerbose(boolean verbose) {
35        this.verbose = verbose;
36    }
37
38    /**
39     * returns transformation for source class
40     *
41     * @param classInfo
42     *            class information
43     *            class name := classInfo[ 0 ]
44     *            super class  name := classInfo[ 1 ]
45     *            interfaces := classInfo[ >1 ]
46     */
47    abstract protected ClassTransformer getClassTransformer(String[] classInfo);
48
49    protected Attribute[] attributes() {
50        return null;
51    }
52
53    protected void processFile(File file) throws Exception {
54
55        if (isClassFile(file)) {
56
57            processClassFile(file);
58
59        } else if (isJarFile(file)) {
60
61            processJarFile(file);
62
63        } else {
64
65            log("ignoring " + file.toURL(), Project.MSG_WARN);
66
67        }
68    }
69
70    /**
71     * @param file
72     * @throws Exception
73     * @throws FileNotFoundException
74     * @throws IOException
75     * @throws MalformedURLException
76     */
77    private void processClassFile(File file) throws Exception,
78            FileNotFoundException, IOException, MalformedURLException {
79
80        ClassReader reader = getClassReader(file);
81        String name[] = ClassNameReader.getClassInfo(reader);
82        ClassWriter w = new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS);
83        ClassTransformer t = getClassTransformer(name);
84        if (t != null) {
85
86            if (verbose) {
87                log("processing " + file.toURL());
88            }
89            new TransformingClassGenerator(new ClassReaderGenerator(
90                    getClassReader(file), attributes(), getFlags()), t)
91                    .generateClass(w);
92            FileOutputStream fos = new FileOutputStream(file);
93            try {
94                fos.write(w.toByteArray());
95            } finally {
96                fos.close();
97            }
98
99        }
100
101    }
102
103    protected int getFlags() {
104        return 0;
105    }
106
107    private static ClassReader getClassReader(File file) throws Exception {
108        InputStream in = new BufferedInputStream(new FileInputStream(file));
109        try {
110            ClassReader r = new ClassReader(in);
111            return r;
112        } finally {
113            in.close();
114        }
115
116    }
117
118    protected boolean isClassFile(File file) throws IOException {
119
120        return checkMagic(file, CLASS_MAGIC);
121
122    }
123
124    protected void processJarFile(File file) throws Exception {
125
126        if (verbose) {
127            log("processing " + file.toURL());
128        }
129
130        File tempFile = File.createTempFile(file.getName(), null, new File(file
131                .getAbsoluteFile().getParent()));
132        try{
133
134            ZipInputStream zip = new ZipInputStream(new FileInputStream(file));
135            try {
136                FileOutputStream fout = new FileOutputStream(tempFile, false);
137                try{
138                 ZipOutputStream out = new ZipOutputStream(fout);
139
140                    ZipEntry entry;
141                    while ((entry = zip.getNextEntry()) != null) {
142
143
144                        byte bytes[] = getBytes(zip);
145
146                        if (!entry.isDirectory()) {
147
148                            DataInputStream din = new DataInputStream(
149                                              new ByteArrayInputStream(bytes)
150                                            );
151
152                            if (din.readInt() == CLASS_MAGIC) {
153
154                                bytes = process(bytes);
155
156                            } else {
157                                if (verbose) {
158                                 log("ignoring " + entry.toString());
159                                }
160                            }
161                        }
162
163                        ZipEntry outEntry = new ZipEntry(entry.getName());
164                        outEntry.setMethod(entry.getMethod());
165                        outEntry.setComment(entry.getComment());
166                        outEntry.setSize(bytes.length);
167
168
169                        if(outEntry.getMethod() == ZipEntry.STORED){
170                            CRC32 crc = new CRC32();
171                            crc.update(bytes);
172                            outEntry.setCrc( crc.getValue() );
173                            outEntry.setCompressedSize(bytes.length);
174                        }
175                        out.putNextEntry(outEntry);
176                        out.write(bytes);
177                        out.closeEntry();
178                        zip.closeEntry();
179
180                    }
181                    out.close();
182                }finally{
183                 fout.close();
184                }
185            } finally {
186                zip.close();
187            }
188
189
190            if(file.delete()){
191
192                File newFile = new File(tempFile.getAbsolutePath());
193
194                if(!newFile.renameTo(file)){
195                    throw new IOException("can not rename " + tempFile + " to " + file);
196                }
197
198            }else{
199                throw new IOException("can not delete " + file);
200            }
201
202        }finally{
203
204            tempFile.delete();
205
206        }
207
208    }
209
210    /**
211     * @param bytes
212     * @return
213     * @throws IOException
214     * @throws Exception
215     */
216    private byte[] process(byte[] bytes) throws Exception {
217
218        ClassReader reader = new ClassReader(new ByteArrayInputStream(bytes));
219        String name[] = ClassNameReader.getClassInfo(reader);
220        ClassWriter w = new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS);
221        ClassTransformer t = getClassTransformer(name);
222        if (t != null) {
223            if (verbose) {
224                log("processing " + name[0]);
225            }
226            new TransformingClassGenerator(new ClassReaderGenerator(
227                    new ClassReader(new ByteArrayInputStream(bytes)),
228                    attributes(), getFlags()), t).generateClass(w);
229            ByteArrayOutputStream out = new ByteArrayOutputStream();
230            out.write(w.toByteArray());
231            return out.toByteArray();
232        }
233        return bytes;
234    }
235
236    /**
237     * @param zip
238     * @return
239     * @throws IOException
240     */
241    private byte[] getBytes(ZipInputStream zip) throws IOException {
242
243        ByteArrayOutputStream bout = new ByteArrayOutputStream();
244        InputStream in = new BufferedInputStream(zip);
245        int b;
246        while ((b = in.read()) != -1) {
247            bout.write(b);
248        }
249        return bout.toByteArray();
250    }
251
252    private boolean checkMagic(File file, long magic) throws IOException {
253        DataInputStream in = new DataInputStream(new FileInputStream(file));
254        try {
255            int m = in.readInt();
256            return magic == m;
257        } finally {
258            in.close();
259        }
260    }
261
262    protected boolean isJarFile(File file) throws IOException {
263        return checkMagic(file, ZIP_MAGIC);
264    }
265
266}