EMMA Coverage Report (generated Thu Nov 26 15:54:18 CST 2009)
[all classes][org.eclipse.pde.api.tools.internal.model]

COVERAGE SUMMARY FOR SOURCE FILE [TypeStructureBuilder.java]

nameclass, %method, %block, %line, %
TypeStructureBuilder.java100% (6/6)96%  (22/23)86%  (708/822)86%  (156.6/182)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TypeStructureBuilder100% (1/1)90%  (9/10)82%  (504/615)81%  (106.6/131)
toString (): String 0%   (0/1)0%   (0/23)0%   (0/4)
buildStubTypeStructure (byte [], IApiComponent, StubArchiveApiTypeContainer$A... 100% (1/1)65%  (150/231)64%  (29.6/46)
setEnclosingMethod (IApiType, ApiType): void 100% (1/1)85%  (39/46)69%  (9/13)
TypeStructureBuilder (ClassVisitor, IApiComponent, IApiTypeRoot): void 100% (1/1)100% (10/10)100% (4/4)
buildTypeStructure (byte [], IApiComponent, IApiTypeRoot): IApiType 100% (1/1)100% (25/25)100% (6/6)
visit (int, int, String, String, String, String []): void 100% (1/1)100% (113/113)100% (22/22)
visitField (int, String, String, String, Object): FieldVisitor 100% (1/1)100% (25/25)100% (6/6)
visitInnerClass (String, String, String, int): void 100% (1/1)100% (71/71)100% (17/17)
visitMethod (int, String, String, String, String []): MethodVisitor 100% (1/1)100% (65/65)100% (11/11)
visitOuterClass (String, String, String): void 100% (1/1)100% (6/6)100% (2/2)
     
class TypeStructureBuilder$EnclosingMethodSetter100% (1/1)100% (2/2)96%  (69/72)94%  (16/17)
visitMethod (int, String, String, String, String []): MethodVisitor 100% (1/1)95%  (56/59)92%  (12/13)
TypeStructureBuilder$EnclosingMethodSetter (ClassVisitor, String): void 100% (1/1)100% (13/13)100% (4/4)
     
class TypeStructureBuilder$1100% (1/1)100% (2/2)100% (17/17)100% (3/3)
TypeStructureBuilder$1 (TypeStructureBuilder, MethodVisitor, ApiMethod): void 100% (1/1)100% (10/10)100% (2/2)
visitAnnotationDefault (): AnnotationVisitor 100% (1/1)100% (7/7)100% (1/1)
     
class TypeStructureBuilder$2100% (1/1)100% (2/2)100% (36/36)100% (11/11)
TypeStructureBuilder$2 (TypeStructureBuilder$1, ApiMethod): void 100% (1/1)100% (9/9)100% (2/2)
visitEnd (): void 100% (1/1)100% (27/27)100% (9/9)
     
class TypeStructureBuilder$TypeNameFinder100% (1/1)100% (2/2)100% (18/18)100% (6/6)
TypeStructureBuilder$TypeNameFinder (MethodVisitor, TypeStructureBuilder$Encl... 100% (1/1)100% (7/7)100% (3/3)
visitTypeInsn (int, String): void 100% (1/1)100% (11/11)100% (3/3)
     
class TypeStructureBuilder$TypeNameFinderInConstructor100% (1/1)100% (5/5)100% (64/64)100% (17/17)
TypeStructureBuilder$TypeNameFinderInConstructor (MethodVisitor, TypeStructur... 100% (1/1)100% (8/8)100% (3/3)
visitEnd (): void 100% (1/1)100% (19/19)100% (4/4)
visitFieldInsn (int, String, String, String): void 100% (1/1)100% (7/7)100% (2/2)
visitLineNumber (int, Label): void 100% (1/1)100% (11/11)100% (4/4)
visitTypeInsn (int, String): void 100% (1/1)100% (19/19)100% (4/4)

1/*******************************************************************************
2 * Copyright (c) 2007, 2009 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *     IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 
12package org.eclipse.pde.api.tools.internal.model;
13 
14import java.io.ByteArrayInputStream;
15import java.io.DataInputStream;
16import java.io.IOException;
17import java.io.PrintWriter;
18import java.io.StringWriter;
19import java.util.HashMap;
20import java.util.Map;
21 
22import org.eclipse.core.runtime.CoreException;
23import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
24import org.eclipse.pde.api.tools.internal.model.StubArchiveApiTypeContainer.ArchiveApiTypeRoot;
25import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
26import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
27import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
28import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot;
29import org.objectweb.asm.AnnotationVisitor;
30import org.objectweb.asm.ClassAdapter;
31import org.objectweb.asm.ClassReader;
32import org.objectweb.asm.ClassVisitor;
33import org.objectweb.asm.FieldVisitor;
34import org.objectweb.asm.Label;
35import org.objectweb.asm.MethodAdapter;
36import org.objectweb.asm.MethodVisitor;
37import org.objectweb.asm.Opcodes;
38import org.objectweb.asm.tree.ClassNode;
39import org.objectweb.asm.util.TraceAnnotationVisitor;
40 
41/**
42 * Class adapter used to create an API type structure
43 */
44public class TypeStructureBuilder extends ClassAdapter {
45        ApiType fType;
46        IApiComponent fComponent;
47        IApiTypeRoot fFile;
48 
49        /**
50         * Builds a type structure for a class file. Note that if an API 
51         * component is not specified, then some operations on the resulting
52         * {@link IApiType} will not be available (navigating super types,
53         * member types, etc).
54         * 
55         * @param cv class file visitor
56         * @param component originating API component or <code>null</code> if unknown
57         */
58        TypeStructureBuilder(ClassVisitor cv, IApiComponent component, IApiTypeRoot file) {
59                super(cv);
60                fComponent = component;
61                fFile = file;
62        }
63        
64        /**
65         * @see org.objectweb.asm.ClassAdapter#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
66         */
67        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
68                StringBuffer simpleSig = new StringBuffer();
69                simpleSig.append('L');
70                simpleSig.append(name);
71                simpleSig.append(';');
72                String enclosingName = null;
73                int index = name.lastIndexOf('$');
74                if (index > -1) {
75                        enclosingName = name.substring(0, index).replace('/', '.');
76                }
77                int laccess = access;
78                // TODO: inner types should be have enclosing type as parent instead of component
79                if ((laccess & Opcodes.ACC_DEPRECATED) != 0) {
80                        laccess &= ~Opcodes.ACC_DEPRECATED;
81                        laccess |= ClassFileConstants.AccDeprecated;
82                }
83                fType = new ApiType(fComponent, name.replace('/', '.'), simpleSig.toString(), signature, laccess, enclosingName, fFile);
84                if (superName != null) {
85                        fType.setSuperclassName(superName.replace('/', '.'));
86                }
87                if (interfaces != null && interfaces.length > 0) {
88                        String[] names = new String[interfaces.length];
89                        for (int i = 0; i < names.length; i++) {
90                                names[i] = interfaces[i].replace('/', '.');
91                        }
92                        fType.setSuperInterfaceNames(names);
93                }
94                super.visit(version, laccess, name, signature, superName, interfaces);
95        }
96        /**
97         * @see org.objectweb.asm.ClassAdapter#visitInnerClass(java.lang.String, java.lang.String, java.lang.String, int)
98         */
99        public void visitInnerClass(String name, String outerName, String innerName, int access) {
100                super.visitInnerClass(name, outerName, innerName, access);
101                String currentName = name.replace('/', '.');
102                if (currentName.equals(fType.getName())) {
103                        if (innerName == null) {
104                                fType.setAnonymous();
105                        }
106                        else if(outerName == null) {
107                                fType.setLocal();
108                                fType.setSimpleName(innerName);
109                        }
110                }
111                if (outerName != null && innerName != null) {
112                        // technically speaking innerName != null is not necessary, but this is a workaround for some
113                        // bogus synthetic types created by another compiler
114                        String currentOuterName = outerName.replace('/', '.');
115                        if (currentOuterName.equals(fType.getName())) {
116                                // this is a real type member defined in the descriptor (not just a reference to a type member)
117                                fType.addMemberType(currentName, access);
118                        } else if (currentName.equals(fType.getName())) {
119                                fType.setModifiers(access);
120                                fType.setSimpleName(innerName);
121                                fType.setMemberType();
122                        }
123                }
124        }
125        
126        /* (non-Javadoc)
127         * @see org.objectweb.asm.ClassAdapter#visitOuterClass(java.lang.String, java.lang.String, java.lang.String)
128         */
129        public void visitOuterClass(String owner, String name, String desc) {
130                fType.setEnclosingMethodInfo(name, desc);
131        }
132 
133        /* (non-Javadoc)
134         * @see org.objectweb.asm.ClassAdapter#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
135         */
136        public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
137                int laccess = access;
138                if ((access & Opcodes.ACC_DEPRECATED) != 0) {
139                        laccess &= ~Opcodes.ACC_DEPRECATED;
140                        laccess |= ClassFileConstants.AccDeprecated;
141                }
142                fType.addField(name, desc, signature, laccess, value);
143                return null;
144        }
145 
146        /* (non-Javadoc)
147         * @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
148         */
149        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
150                String[] names = null;
151                int laccess = access;
152                if ((laccess & Opcodes.ACC_DEPRECATED) != 0) {
153                        laccess &= ~Opcodes.ACC_DEPRECATED;
154                        laccess |= ClassFileConstants.AccDeprecated;
155                }
156                if (exceptions != null && exceptions.length > 0) {
157                        names = new String[exceptions.length];
158                        for (int i = 0; i < names.length; i++) {
159                                names[i] = exceptions[i].replace('/', '.');
160                        }
161                }
162                final ApiMethod method = fType.addMethod(name, desc, signature, laccess, names);
163                return new MethodAdapter(super.visitMethod(laccess, name, desc, signature, exceptions)) {
164                        public AnnotationVisitor visitAnnotationDefault() {
165                                return new TraceAnnotationVisitor() {
166                                        public void visitEnd() {
167                                                super.visitEnd();
168                                                StringWriter stringWriter = new StringWriter();
169                                                PrintWriter writer = new PrintWriter(stringWriter);
170                                                print(writer);
171                                                writer.flush();
172                                                writer.close();
173                                                String def = String.valueOf(stringWriter.getBuffer());
174                                                method.setDefaultValue(def);
175                                        }
176                                };
177                        }
178                };
179        }
180        
181        /**
182         * Builds a type structure with the given .class file bytes in the specified
183         * API component.
184         * 
185         * @param bytes class file bytes
186         * @param component originating API component
187         * @param file associated class file
188         * @return
189         */
190        public static IApiType buildTypeStructure(byte[] bytes, IApiComponent component, IApiTypeRoot file) {
191                TypeStructureBuilder visitor = new TypeStructureBuilder(new ClassNode(), component, file);
192                try {
193                        ClassReader classReader = new ClassReader(bytes);
194                        classReader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
195                } catch (ArrayIndexOutOfBoundsException e) {
196                        ApiPlugin.log(e);
197                }
198                return visitor.fType;
199        }
200        /**
201         * Builds a type structure with the given .class file bytes in the specified
202         * API component.
203         * 
204         * @param bytes class file bytes
205         * @param component originating API component
206         * @param file associated class file
207         * @return
208         */
209        public static void setEnclosingMethod(IApiType enclosingType, ApiType currentAnonymousLocalType) {
210                IApiTypeRoot typeRoot = enclosingType.getTypeRoot();
211                if (typeRoot instanceof AbstractApiTypeRoot) {
212                        AbstractApiTypeRoot abstractApiTypeRoot = (AbstractApiTypeRoot) typeRoot;
213                        EnclosingMethodSetter visitor = new EnclosingMethodSetter(new ClassNode(), currentAnonymousLocalType.getName());
214                        try {
215                                ClassReader classReader = new ClassReader(abstractApiTypeRoot.getContents());
216                                classReader.accept(visitor, ClassReader.SKIP_FRAMES);
217                        } catch (ArrayIndexOutOfBoundsException e) {
218                                ApiPlugin.log(e);
219                        } catch(CoreException e) {
220                                // bytes could not be retrieved for abstractApiTypeRoot
221                                ApiPlugin.log(e);
222                        }
223                        if (visitor.found) {
224                                currentAnonymousLocalType.setEnclosingMethodInfo(visitor.name, visitor.signature);
225                        }
226                }
227        }
228        static class EnclosingMethodSetter extends ClassAdapter {
229                String name;
230                String signature;
231                boolean found = false;
232                String typeName;
233 
234                public EnclosingMethodSetter(ClassVisitor cv, String typeName) {
235                        super(cv);
236                        this.typeName = typeName.replace('.', '/');
237                }
238                public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
239                        if ("<clinit>".equals(name)) { //$NON-NLS-1$
240                                return null;
241                        }
242                        if (!this.found) {
243                                if ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) == 0) {
244                                        this.name = name;
245                                        this.signature = desc;
246                                        if (signature != null) {
247                                                this.signature = signature;
248                                        }
249                                        MethodVisitor mv;
250                                        if ("<init>".equals(name)) { //$NON-NLS-1$
251                                                mv = new TypeNameFinderInConstructor(cv.visitMethod(access, name, desc, signature, exceptions), this);
252                                        } else {
253                                                mv = new TypeNameFinder(cv.visitMethod(access, name, desc, signature, exceptions), this);
254                                        }
255                                        return mv;
256                                }
257                        }
258                        return null;
259                }
260        }
261        static class TypeNameFinder extends MethodAdapter {
262                protected EnclosingMethodSetter setter;
263                
264                public TypeNameFinder(MethodVisitor mv, EnclosingMethodSetter enclosingMethodSetter) {
265                        super(mv);
266                        this.setter = enclosingMethodSetter;
267                }
268                public void visitTypeInsn(int opcode, String type) {
269                        if (setter.typeName.equals(type)) {
270                                setter.found = true;
271                        }
272                }
273        }
274        static class TypeNameFinderInConstructor extends TypeNameFinder {
275                int lineNumberStart;
276                int matchingLineNumber;
277                int currentLineNumber = -1;
278                
279                public TypeNameFinderInConstructor(MethodVisitor mv, EnclosingMethodSetter enclosingMethodSetter) {
280                        super(mv, enclosingMethodSetter);
281                }
282                /* (non-Javadoc)
283                 * @see org.objectweb.asm.MethodAdapter#visitFieldInsn(int, java.lang.String, java.lang.String, java.lang.String)
284                 */
285                public void visitFieldInsn(int opcode, String owner, String name,
286                                String desc) {
287                        super.visitFieldInsn(opcode, owner, name, desc);
288                }
289                public void visitTypeInsn(int opcode, String type) {
290                        if (!setter.found && setter.typeName.equals(type)) {
291                                this.matchingLineNumber = this.currentLineNumber;
292                                setter.found = true;
293                        }
294                }
295                public void visitLineNumber(int line, Label start) {
296                        if (this.currentLineNumber == -1) {
297                                this.lineNumberStart = line;
298                        }
299                        this.currentLineNumber = line;
300                }
301                public void visitEnd() {
302                        if (setter.found) {
303                                // check that the line number is between the constructor bounds
304                                if (this.matchingLineNumber < this.lineNumberStart || this.matchingLineNumber > this.currentLineNumber) {
305                                        setter.found = false;
306                                }
307                        }
308                }
309        }
310        /* (non-Javadoc)
311         * @see java.lang.Object#toString()
312         */
313        public String toString() {
314                StringBuffer buffer = new StringBuffer();
315                buffer.append("Type structure builder for: ").append(fType.getName()); //$NON-NLS-1$
316                buffer.append("\nBacked by file: ").append(fFile.getName()); //$NON-NLS-1$
317                return buffer.toString();
318        }
319 
320        public static IApiType buildStubTypeStructure(byte[] contents,
321                        IApiComponent apiComponent, ArchiveApiTypeRoot archiveApiTypeRoot) {
322                // decode the byte[]
323                DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(contents));
324                ApiType type = null;
325                try {
326                        Map pool = new HashMap();
327                        short currentVersion = inputStream.readShort(); // read file version (for now there is only one version)
328                        short poolSize = inputStream.readShort();
329                        for (int i = 0; i < poolSize; i++) {
330                                String readUtf = inputStream.readUTF();
331                                int index = inputStream.readShort();
332                                pool.put(new Integer(index), readUtf);
333                        }
334                        int access = 0;
335                        // access flag was added in version 2 of the stub format
336                        if (currentVersion == 2) {
337                                access = inputStream.readChar();
338                        }
339                        int classIndex = inputStream.readShort();
340                        String name = (String) pool.get(new Integer(classIndex));
341                        StringBuffer simpleSig = new StringBuffer();
342                        simpleSig.append('L');
343                        simpleSig.append(name);
344                        simpleSig.append(';');
345                        type = new ApiType(apiComponent, name.replace('/', '.'), simpleSig.toString(), null, access, null, archiveApiTypeRoot);
346                        int superclassNameIndex = inputStream.readShort();
347                        if (superclassNameIndex != -1) {
348                                String superclassName = (String) pool.get(new Integer(superclassNameIndex));
349                                type.setSuperclassName(superclassName.replace('/', '.'));
350                        }
351                        int interfacesLength = inputStream.readShort();
352                        if (interfacesLength != 0) {
353                                String[] names = new String[interfacesLength];
354                                for (int i = 0; i < names.length; i++) {
355                                        String interfaceName = (String) pool.get(new Integer(inputStream.readShort()));
356                                        names[i] = interfaceName.replace('/', '.');
357                                }
358                                type.setSuperInterfaceNames(names);
359                        }
360                        int fieldsLength = inputStream.readShort();
361                        for (int i = 0; i < fieldsLength; i++) {
362                                String fieldName = (String) pool.get(new Integer(inputStream.readShort()));
363                                type.addField(fieldName, null, null, 0, null);
364                        }
365                        int methodsLength = inputStream.readShort();
366                        for (int i = 0; i < methodsLength; i++) {
367                                String methodSelector = (String) pool.get(new Integer(inputStream.readShort()));
368                                String methodSignature = (String) pool.get(new Integer(inputStream.readShort()));
369                                type.addMethod(methodSelector, methodSignature, null, 0, null);
370                        }
371                } catch (IOException e) {
372                        ApiPlugin.log(e);
373                } finally {
374                        try {
375                                inputStream.close();
376                        } catch (IOException e) {
377                                // ignore
378                        }
379                }
380                return type;
381        }
382}

[all classes][org.eclipse.pde.api.tools.internal.model]
EMMA 2.0.5312 EclEmma Fix 1 (C) Vladimir Roubtsov