michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.annotationProcessors.utils; michael@0: michael@0: import org.mozilla.gecko.annotationProcessors.AnnotationInfo; michael@0: import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; michael@0: michael@0: import java.lang.annotation.Annotation; michael@0: import java.lang.reflect.AnnotatedElement; michael@0: import java.lang.reflect.InvocationTargetException; michael@0: import java.lang.reflect.Member; michael@0: import java.lang.reflect.Method; michael@0: import java.util.Arrays; michael@0: import java.util.Iterator; michael@0: michael@0: /** michael@0: * Iterator over the methods in a given method list which have the WrappedJNIMethod michael@0: * annotation. Returns an object containing both the annotation (Which may contain interesting michael@0: * parameters) and the argument. michael@0: */ michael@0: public class GeneratableElementIterator implements Iterator { michael@0: private final Member[] mObjects; michael@0: private AnnotatableEntity mNextReturnValue; michael@0: private int mElementIndex; michael@0: michael@0: private boolean mIterateEveryEntry; michael@0: michael@0: public GeneratableElementIterator(Class aClass) { michael@0: // Get all the elements of this class as AccessibleObjects. michael@0: Member[] aMethods = aClass.getDeclaredMethods(); michael@0: Member[] aFields = aClass.getDeclaredFields(); michael@0: Member[] aCtors = aClass.getConstructors(); michael@0: michael@0: // Shove them all into one buffer. michael@0: Member[] objs = new Member[aMethods.length + aFields.length + aCtors.length]; michael@0: michael@0: int offset = 0; michael@0: System.arraycopy(aMethods, 0, objs, 0, aMethods.length); michael@0: offset += aMethods.length; michael@0: System.arraycopy(aFields, 0, objs, offset, aFields.length); michael@0: offset += aFields.length; michael@0: System.arraycopy(aCtors, 0, objs, offset, aCtors.length); michael@0: michael@0: // Sort the elements to ensure determinism. michael@0: Arrays.sort(objs, new AlphabeticAnnotatableEntityComparator()); michael@0: mObjects = objs; michael@0: michael@0: // Check for "Wrap ALL the things" flag. michael@0: for (Annotation annotation : aClass.getDeclaredAnnotations()) { michael@0: final String annotationTypeName = annotation.annotationType().getName(); michael@0: if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI")) { michael@0: mIterateEveryEntry = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: findNextValue(); michael@0: } michael@0: michael@0: /** michael@0: * Find and cache the next appropriately annotated method, plus the annotation parameter, if michael@0: * one exists. Otherwise cache null, so hasNext returns false. michael@0: */ michael@0: private void findNextValue() { michael@0: while (mElementIndex < mObjects.length) { michael@0: Member candidateElement = mObjects[mElementIndex]; michael@0: mElementIndex++; michael@0: for (Annotation annotation : ((AnnotatedElement) candidateElement).getDeclaredAnnotations()) { michael@0: // WrappedJNIMethod has parameters. Use Reflection to obtain them. michael@0: Class annotationType = annotation.annotationType(); michael@0: final String annotationTypeName = annotationType.getName(); michael@0: if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI")) { michael@0: String stubName = null; michael@0: boolean isStaticStub = false; michael@0: boolean isMultithreadedStub = false; michael@0: boolean noThrow = false; michael@0: try { michael@0: // Determine the explicitly-given name of the stub to generate, if any. michael@0: final Method stubNameMethod = annotationType.getDeclaredMethod("stubName"); michael@0: stubNameMethod.setAccessible(true); michael@0: stubName = (String) stubNameMethod.invoke(annotation); michael@0: michael@0: // Detemine if the generated stub should be static. michael@0: final Method staticStubMethod = annotationType.getDeclaredMethod("generateStatic"); michael@0: staticStubMethod.setAccessible(true); michael@0: isStaticStub = (Boolean) staticStubMethod.invoke(annotation); michael@0: michael@0: // Determine if the generated stub is to allow calls from multiple threads. michael@0: final Method multithreadedStubMethod = annotationType.getDeclaredMethod("allowMultithread"); michael@0: multithreadedStubMethod.setAccessible(true); michael@0: isMultithreadedStub = (Boolean) multithreadedStubMethod.invoke(annotation); michael@0: michael@0: // Determine if ignoring exceptions michael@0: final Method noThrowMethod = annotationType.getDeclaredMethod("noThrow"); michael@0: noThrowMethod.setAccessible(true); michael@0: noThrow = (Boolean) noThrowMethod.invoke(annotation); michael@0: michael@0: } catch (NoSuchMethodException e) { michael@0: System.err.println("Unable to find expected field on WrapElementForJNI annotation. Did the signature change?"); michael@0: e.printStackTrace(System.err); michael@0: System.exit(3); michael@0: } catch (IllegalAccessException e) { michael@0: System.err.println("IllegalAccessException reading fields on WrapElementForJNI annotation. Seems the semantics of Reflection have changed..."); michael@0: e.printStackTrace(System.err); michael@0: System.exit(4); michael@0: } catch (InvocationTargetException e) { michael@0: System.err.println("InvocationTargetException reading fields on WrapElementForJNI annotation. This really shouldn't happen."); michael@0: e.printStackTrace(System.err); michael@0: System.exit(5); michael@0: } michael@0: michael@0: // If the method name was not explicitly given in the annotation generate one... michael@0: if (stubName.isEmpty()) { michael@0: String aMethodName = candidateElement.getName(); michael@0: stubName = aMethodName.substring(0, 1).toUpperCase() + aMethodName.substring(1); michael@0: } michael@0: michael@0: AnnotationInfo annotationInfo = new AnnotationInfo( michael@0: stubName, isStaticStub, isMultithreadedStub, noThrow); michael@0: mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // If no annotation found, we might be expected to generate anyway using default arguments, michael@0: // thanks to the "Generate everything" annotation. michael@0: if (mIterateEveryEntry) { michael@0: AnnotationInfo annotationInfo = new AnnotationInfo( michael@0: candidateElement.getName(), false, false, false); michael@0: mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo); michael@0: return; michael@0: } michael@0: } michael@0: mNextReturnValue = null; michael@0: } michael@0: michael@0: @Override michael@0: public boolean hasNext() { michael@0: return mNextReturnValue != null; michael@0: } michael@0: michael@0: @Override michael@0: public AnnotatableEntity next() { michael@0: AnnotatableEntity ret = mNextReturnValue; michael@0: findNextValue(); michael@0: return ret; michael@0: } michael@0: michael@0: @Override michael@0: public void remove() { michael@0: throw new UnsupportedOperationException("Removal of methods from GeneratableElementIterator not supported."); michael@0: } michael@0: }