diff --git a/.gitignore b/.gitignore index 2458c83..0fc686a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,11 @@ .DS_Store /build /captures +.idea /out /output /newApp /dts_gen_app -testparams.txt \ No newline at end of file +testparams.txt +/jars diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..75bb617 --- /dev/null +++ b/Makefile @@ -0,0 +1,66 @@ +android-platform-17: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-17/android.jar + mv out/android.d.ts out/android-platform-17.d.ts + +android-platform-18: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-18/android.jar + mv out/android.d.ts out/android-platform-18.d.ts + +android-platform-19: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-19/android.jar + mv out/android.d.ts out/android-platform-19.d.ts + +android-platform-20: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-20/android.jar + mv out/android.d.ts out/android-platform-20.d.ts + +android-platform-21: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-21/android.jar + mv out/android.d.ts out/android-platform-21.d.ts + +android-platform-22: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-22/android.jar + mv out/android.d.ts out/android-platform-22.d.ts + +android-platform-23: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-23/android.jar + mv out/android.d.ts out/android-platform-23.d.ts + +android-platform-24: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-24/android.jar + mv out/android.d.ts out/android-platform-24.d.ts + +android-platform-25: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-25/android.jar + mv out/android.d.ts out/android-platform-25.d.ts + +android-platform-26: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-26/android.jar + mv out/android.d.ts out/android-platform-26.d.ts + +android-platform-27: + java -jar dts-generator/build/libs/dts-generator.jar -input ~/Library/Android/sdk/platforms/android-27/android.jar + mv out/android.d.ts out/android-platform-27.d.ts + +android-platform-all: android-platform-17 android-platform-18 android-platform-19 android-platform-20 android-platform-21 \ + android-platform-22 android-platform-23 android-platform-24 android-platform-25 android-platform-26 android-platform-27 + +android-support-17: + java -jar dts-generator/build/libs/dts-generator.jar \ + -input libs/android-support/27.0.1 -input-generics libs/generics.txt \ + -super /Users/trifonov/Library/Android/sdk/platforms/android-17/android.jar -skip-declarations + mv out/android.d.ts out/android-support-17.d.ts + +android-support-23: + java -jar dts-generator/build/libs/dts-generator.jar \ + -input libs/android-support/27.0.1 -input-generics libs/generics.txt \ + -super /Users/trifonov/Library/Android/sdk/platforms/android-23/android.jar -skip-declarations + mv out/android.d.ts out/android-support-23.d.ts + +android-support-26: + java -jar dts-generator/build/libs/dts-generator.jar \ + -input libs/android-support/27.0.1 -input-generics libs/generics.txt \ + -super /Users/trifonov/Library/Android/sdk/platforms/android-26/android.jar -skip-declarations + mv out/android.d.ts out/android-support-26.d.ts + +android-support-all: android-support-17 android-support-23 android-support-26 \ No newline at end of file diff --git a/README.md b/README.md index b335183..c0086f5 100644 --- a/README.md +++ b/README.md @@ -20,21 +20,21 @@ Generate definitions following any of the approaches described below. Once you h ## Generate definitons for Android SDK ```shell cd dts-generator -gradlew jar +./gradlew jar java -jar build\libs\dts-generator.jar -input %ANDROID_HOME%\platforms\android-\android.jar ``` ## Generate definitions for any Jar ```shell cd dts-generator -gradlew jar +./gradlew jar java -jar build\libs\dts-generator.jar -input ``` ## Pass multiple jars to generator ```shell cd dts-generator -gradlew jar +./gradlew jar java -jar build\libs\dts-generator.jar -input ``` @@ -48,8 +48,67 @@ Rename classes.jar if necessary java -jar build\libs\dts-generator.jar -input classes.jar dependency-of-classes-jar.jar ``` -## Support libraries -In the [lib](lib) folder there are android support libraries jars which are get from the following git repos: -* [android-support-v4](https://github.com/dandar3/android-support-v4/tree/master/libs) -* [android-support-v7-appcompat](https://github.com/dandar3/android-support-v7-appcompat/tree/master/libs) -* [android-support-design](https://github.com/dandar3/android-support-design/tree/master/libs) \ No newline at end of file +## Complex typings generation +Generating the typings corresponding to the android and android-support jar files is a bit tricky operation, so here's a detailed explanation how to do it. +There are different andoid support versions and they depend on main android classes it is a little bit complicated to generate those typings. One option is to generate a big **d.ts** file with all the libraries inside. The downside of this approach is that you have to generate a big file for every API level and if the android support version is changed all those **d.ts** files need to be regenerated. +To avoid this there's some functionality in the tool for passing dependencies when generating typings. +There are two type of dependencies that can be passed: + +1. Super class jars - this is needed when the current jar has classes which are extending classes from another jar file, but we don't want to have all that jar files' typings in a single output file. To achieve this we can provide the super class jar with the **super** argument(which works the same way as **input** for multiple files). + For instance if we want to generate typings for **android.support.v4.view.ViewPager** and we don't pass the super classes jar the generated typings won't extend any class as there's no information in the jar that contains the ViewPager. However this class extends **android.view.ViewGroup** class which is a part of the android jar file(any of the API levels). So if we pass one of the android.jar files as a super class jar file the generated typing will contain `extends android.view.ViewGroup`. +2. Input generics - When trying to get the type of a parameter which is a generic class we cannot really get the generic types of that class, so we cannot generate working typings. To fix this we are adding information about the generics of each package at the end of the file with comments starting with `//Generics information:`. + So to fix this we need to provide a file with all the generic information for the packages the current jar relies on. You need to create a file and copy all the generic informations of the related packages and provide it in the **input-generics** argument. This will make all the generic classes referenced without passing types to pass **any** so that the ouput will be valid. + +## Adding all implements for generic types +There is an option **all-generic-implements** which is disabled by default which controls whether to add implements for all interfaces (if they are more than one) to generic type declarations. The problem is that in most of the cases one of those interfaces is actually an extend, so it should be manual reviewed and fixed after generation. + +## Finding package dependencies +If you want to generate typings of a package but you are not sure how you can get all the needed dependencies you can follow the steps bellow: + +1. Open [dts-generator/build.gradle](dts-generator/build.gradle) file and locate `dependencies` part. +2. Add as a `testCompileOnly` dependency the one that you want to generate typings for: + + ```groovy + dependencies { + compile 'org.apache.bcel:bcel:6.2' + compile 'commons-io:commons-io:2.6' + compile 'com.google.code.findbugs:findbugs:3.0.1' + + // add your dependency bellow + testCompileOnly "com.android.support:support-v4:27.0.1" + } + ``` + +3. Open the [dts-generator](dts-generator) folder in your terminal +4. Run the following command: + + ``` + ./gradlew extractAllJars + ``` + +5. The command above will get the needed jar files for your dependency and will output them in the [dts-generator/jar-files](dts-generator/jar-files) folder (or you can optionaly pass another output folder `-PjarsOutput=another-folder`) +6. You can run the following command to check what are the dependencies between the packages: + + ``` + ./gradlew dependencies --configuration testCompileOnly + ``` + +7. Run the dts-generator tool passing as **input** arguments the path to the output jars folder + +## Android support specifics +To get all the jar files for android support follow the steps above. You can find the **jar** files for android support 27.0.1 in [the current repository](libs/android-support/27.0.1) +As the android support needs the base android jar file to create its typings you need to pass the android.jar file as a **super** parameter to the generator. To avoid having typings for every different API level you can reuse typings built with API level 17 for all API levels until 23. It's quite easy to test this: + +1. Run the typings generator for android support passing **android-17/android.jar** as a supper jar +2. Add `/// ` at the top of the generated typings file where android-17.d.ts is the typings file of the android API level 17 +3. Run `tsc` passing the generated typings file and there shouldn't be errors +4. Now start replacing the reference file with the files from other API level while the `tsc` execution completes with no error +5. If there's an error this means that you need to generate the android support typings with the same android API level super jar + +By repeating the steps above we've found that: + +- Android support 17 typings(built with supper jar from android API 17) can be reused until android API 22 +- Android support 23 typings(built with supper jar from android API 23) can be reused until android API 25 +- Android support 26 typings(built with supper jar from android API 26) can be reused for API 26 and 27 + +The corresponding typings files can be found in the [tns-platform-declarations](https://github.com/NativeScript/NativeScript/tree/master/tns-platform-declarations) package. The repo's [Makefile](Makefile) can be used as a reference for creating these typings files \ No newline at end of file diff --git a/dts-generator/.idea/.name b/dts-generator/.idea/.name deleted file mode 100644 index d2bd5ea..0000000 --- a/dts-generator/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -dts-generator \ No newline at end of file diff --git a/dts-generator/.idea/compiler.xml b/dts-generator/.idea/compiler.xml deleted file mode 100644 index 96cc43e..0000000 --- a/dts-generator/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dts-generator/.idea/copyright/profiles_settings.xml b/dts-generator/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3..0000000 --- a/dts-generator/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/dts-generator/.idea/encodings.xml b/dts-generator/.idea/encodings.xml deleted file mode 100644 index 97626ba..0000000 --- a/dts-generator/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/dts-generator/.idea/gradle.xml b/dts-generator/.idea/gradle.xml deleted file mode 100644 index 47bd81f..0000000 --- a/dts-generator/.idea/gradle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/dts-generator/.idea/modules.xml b/dts-generator/.idea/modules.xml deleted file mode 100644 index bb2699b..0000000 --- a/dts-generator/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/dts-generator/build.gradle b/dts-generator/build.gradle index 6247548..9b31c20 100644 --- a/dts-generator/build.gradle +++ b/dts-generator/build.gradle @@ -1,7 +1,32 @@ -apply plugin: 'java' +apply plugin: 'java-library' + +def version = "2.0.0" + +project.ext.extractedDependenciesDir = "jar-files" +if(project.hasProperty("jarsOutput")) { + project.ext.extractedDependenciesDir = project.ext.jarsOutput +} + +repositories { + google() + jcenter() +} + +allprojects { + gradle.projectsEvaluated { + tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:all" << "-Werror" + } + } +} dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'org.apache.bcel:bcel:6.2' + compile 'commons-io:commons-io:2.6' + compile 'com.google.code.findbugs:findbugs:3.0.1' + + // add your dependency here as the example bellow, make sure you are using testCompileOnly + // testCompileOnly "com.android.support:support-v4:27.0.1" } jar { @@ -11,6 +36,8 @@ jar { //set main class for the jar manifest { attributes 'Main-Class': 'com.telerik.Main' + attributes 'Specification-Version': version + attributes 'Manifest-Version': version } } @@ -20,3 +47,51 @@ task copyJarToBuildTools (type: Copy) { } jar.finalizedBy(copyJarToBuildTools) + +task extractAllJars { + + outputs.dir extractedDependenciesDir + + doLast { + def iter = configurations.testCompileOnly.resolvedConfiguration.resolvedArtifacts.iterator() + def dependencyCounter = 0 + while (iter.hasNext()) { + //declaring variable as specific class for getting code completion in Android Studio + org.gradle.api.internal.artifacts.DefaultResolvedArtifact nextDependency = iter.next() + + def outputDir = java.nio.file.Paths.get(extractedDependenciesDir, nextDependency.toString()).normalize().toString() + explodeAar(nextDependency.file, outputDir) + dependencyCounter++ + } + } +} + +def explodeAar(File compileDependency, String outputDir) { + if (compileDependency.name.endsWith(".aar")) { + java.util.jar.JarFile jar = new java.util.jar.JarFile(compileDependency) + Enumeration enumEntries = jar.entries() + while (enumEntries.hasMoreElements()) { + java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement() + if (file.name.endsWith(".jar")) { + def f = new File(outputDir, file.name) + new File(f.parent).mkdirs() + InputStream is = jar.getInputStream(file) + FileOutputStream fos = new FileOutputStream(f) + while (is.available() > 0) { + fos.write(is.read()) + } + fos.close() + is.close() + } + if (file.isDirectory()) { + continue + } + } + jar.close() + } else if (compileDependency.name.endsWith(".jar")) { + copy { + from compileDependency.absolutePath + into outputDir + } + } +} \ No newline at end of file diff --git a/dts-generator/libs/bcel-5.2.jar b/dts-generator/libs/bcel-5.2.jar deleted file mode 100644 index 2fa90ce..0000000 Binary files a/dts-generator/libs/bcel-5.2.jar and /dev/null differ diff --git a/dts-generator/libs/commons-io-2.4.jar b/dts-generator/libs/commons-io-2.4.jar deleted file mode 100644 index 90035a4..0000000 Binary files a/dts-generator/libs/commons-io-2.4.jar and /dev/null differ diff --git a/dts-generator/src/main/java/com/telerik/InputParameters.java b/dts-generator/src/main/java/com/telerik/InputParameters.java index 04dda63..afc38c3 100644 --- a/dts-generator/src/main/java/com/telerik/InputParameters.java +++ b/dts-generator/src/main/java/com/telerik/InputParameters.java @@ -11,12 +11,19 @@ public class InputParameters { private File outputDir; private List inputJars; - private boolean generateMultipleFiles; + private List superJars; + private File inputGenerics; + private boolean allGenericImplements; + private boolean skipDeclarations; + private boolean classMode; public InputParameters() { this.outputDir = new File("out"); - this.inputJars = new ArrayList(); - this.generateMultipleFiles = false; + this.inputJars = new ArrayList<>(); + this.superJars = new ArrayList<>(); + this.allGenericImplements = false; + this.skipDeclarations = false; + this.classMode = false; } public File getOutputDir() { @@ -31,15 +38,29 @@ public List getInputJars() { return inputJars; } - public void setInputJars(List inputJars) { - this.inputJars = inputJars; + public List getSuperJars() { + return superJars; } - public boolean isGenerateMultipleFiles() { - return generateMultipleFiles; + public File getInputGenerics() { + return inputGenerics; } - public void setGenerateMultipleFiles(boolean generateMultipleFiles) { - this.generateMultipleFiles = generateMultipleFiles; + public void setInputGenerics(File inputGenerics) { + this.inputGenerics = inputGenerics; } + + public boolean isAllGenericImplementsEnabled() { + return allGenericImplements; + } + + public void setSkipDeclarations(boolean skipDeclarations) { this.skipDeclarations = skipDeclarations; } + + public boolean getSkipDeclarations() { return skipDeclarations; } + + public void setAllGenericImplements(boolean allGenericImplements) { this.allGenericImplements = allGenericImplements; } + + public boolean getClassMode() { return this.classMode; } + + public void setClassMode(boolean classMode) { this.classMode = classMode; } } diff --git a/dts-generator/src/main/java/com/telerik/Main.java b/dts-generator/src/main/java/com/telerik/Main.java index 9aa11ab..edff65f 100644 --- a/dts-generator/src/main/java/com/telerik/Main.java +++ b/dts-generator/src/main/java/com/telerik/Main.java @@ -10,7 +10,19 @@ public class Main { private static final String OUT_DIR = "-output"; private static final String INPUT_JARS = "-input"; - private static final String GENERATE_MULTIPLE_FILES = "-generate-multiple"; + private static final String SUPER_JARS = "-super"; + private static final String CLASS_MODE = "-class-mode"; + + // provide a file with rows in the following format - com.telerik.android.data.SelectionService:1 + // to know how many generic types uses a given generic + private static final String INPUT_GENERICS = "-input-generics"; + + // whether to generate implements for all interfaces implemented by the generic types + private static final String ALL_GENERIC_IMPLEMENTS = "-all-generic-implements"; + + // whether to skip the declarations file generation + private static final String SKIP_DECLARATIONS = "-skip-declarations"; + private static final String HELP = "-help"; public static void main(String[] args) throws Exception { @@ -18,6 +30,10 @@ public static void main(String[] args) throws Exception { long startTime = System.currentTimeMillis(); + Package p = Main.class.getPackage(); + System.out.println("Android d.ts Generator Version : " + + p.getSpecificationVersion()); + new Generator().start(inputParameters); long stopTime = System.currentTimeMillis(); @@ -25,40 +41,65 @@ public static void main(String[] args) throws Exception { System.out.println("Generation of definitions took " + elapsedTime + "ms."); } - public static InputParameters parseCommand(String[] args) { + public static InputParameters parseCommand(String[] args) throws Exception { InputParameters inputParameters = new InputParameters(); if (args != null) { - for(int i = 0; i < args.length; i++) { + for (int i = 0; i < args.length; i++) { String commandArg = args[i]; - if(commandArg.equals(HELP)) { + if (commandArg.equals(HELP)) { printHelpMessage(); } - if(commandArg.equals(GENERATE_MULTIPLE_FILES)) { - inputParameters.setGenerateMultipleFiles(true); + if (commandArg.equals(ALL_GENERIC_IMPLEMENTS)) { + inputParameters.setAllGenericImplements(true); + } + + if (commandArg.equals(SKIP_DECLARATIONS)) { + inputParameters.setSkipDeclarations(true); } - if(commandArg.equals(OUT_DIR)) { - if(i != (args.length -1)) { + if (commandArg.equals(CLASS_MODE)) { + inputParameters.setClassMode(true); + } + + if (commandArg.equals(OUT_DIR)) { + if (i != (args.length - 1)) { String nextParam = args[i + 1]; validateOutputDir(inputParameters, nextParam); } } - if(commandArg.equals(INPUT_JARS)) { - for(int jarIndex = i+1; jarIndex < args.length; jarIndex++) { + if (commandArg.equals(INPUT_GENERICS)) { + if (i != (args.length - 1)) { + String nextParam = args[i + 1]; + validateInputGenerics(inputParameters, nextParam); + } + } + + if (commandArg.equals(INPUT_JARS)) { + for (int jarIndex = i + 1; jarIndex < args.length; jarIndex++) { String currentArgument = args[jarIndex]; - if(currentArgument.startsWith("-")) { + if (currentArgument.startsWith("-")) { break; } inputParameters.getInputJars().add(new File(currentArgument)); } - if(inputParameters.getInputJars().size() <= 0) { + if (inputParameters.getInputJars().size() <= 0) { throw new IllegalArgumentException("You need to pass input jars to: " + OUT_DIR + " flag"); } } + + if (commandArg.equals(SUPER_JARS)) { + for (int jarIndex = i + 1; jarIndex < args.length; jarIndex++) { + String currentArgument = args[jarIndex]; + if (currentArgument.startsWith("-")) { + break; + } + inputParameters.getSuperJars().add(new File(currentArgument)); + } + } } inputParameters.getOutputDir().mkdir(); @@ -73,27 +114,41 @@ private static void printHelpMessage() { helpMessage.appendln("usage: java -jar dts-generator.jar []"); helpMessage.appendln("flags:\t\t"); helpMessage.appendln("\t(Optional)"); - helpMessage.appendln("\t\t[-output ]:\tThe direcory the d.ts files will be generated in."); - helpMessage.appendln("\t\t[-input]:\t\tThe input jars or class directories from which the d.ts files will be generated"); - helpMessage.appendln("\t\t[-generate-multiple]:\tPass this flag if you want multiple d.ts files to be generated instead of one."); + helpMessage.appendln("\t\t[-output ]:\t\tThe direcory the d.ts files will be generated in."); + helpMessage.appendln("\t\t[-input]:\t\tThe input jars or class directories from which the d.ts files will be generated."); + helpMessage.appendln("\t\t[-super]:\t\tProvide jar files from which to search for super classes."); + helpMessage.appendln("\t\t[-input-generics]:\tProvide a file with information for number of generic types per given generic class name."); + helpMessage.appendln("\t\t[-all-generic-implements]:\t\tAdd this flag to generate implements for all interfaces implemented by the generic types." + + " It is not enabled by default as when there are more than one implementation most probably one of them needs to be changed to extends, but this have to be made manually"); + helpMessage.appendln("\t\t[-skip-declarations]:\t\tProvide this flag if you don't want android-declarations.d.ts file to be generated and referenced."); + helpMessage.appendln("\t\t[--class-mode]:\t\tPass this argument if you want folders to be processed as class folders."); helpMessage.appendln("\t\t[-help]:\t\tPrints this help message."); System.out.println(helpMessage); } private static void validateOutputDir(InputParameters inputParameters, String nextParam) { - if(nextParam.startsWith("-")) { - throw new IllegalArgumentException("You need to pass output dir to " + OUT_DIR + " flag"); - } + if (nextParam.startsWith("-")) { + throw new IllegalArgumentException("You need to pass output dir to " + OUT_DIR + " flag"); + } File outputDir = new File(nextParam); - if (!outputDir.exists()) { -// System.out.println(String -// .format("We didn't find the folder you specified ( %s ), so it's going to be created!", -// inputParameters.getOutputDir().getAbsolutePath())); - } - inputParameters.setOutputDir(outputDir); } + + private static void validateInputGenerics(InputParameters inputParameters, String nextParam) throws Exception { + if (nextParam.startsWith("-")) { + throw new IllegalArgumentException("You need to pass output dir to " + INPUT_GENERICS + " flag"); + } + + File inputGenericsFile = new File(nextParam); + + if (!inputGenericsFile.exists()) { + throw new Exception(String + .format("Input generics file not found ( %s )!", inputGenericsFile.getAbsolutePath())); + } else { + inputParameters.setInputGenerics(inputGenericsFile); + } + } } \ No newline at end of file diff --git a/dts-generator/src/main/java/com/telerik/dts/ClassRepo.java b/dts-generator/src/main/java/com/telerik/dts/ClassRepo.java index e899e6d..b8e1b75 100644 --- a/dts-generator/src/main/java/com/telerik/dts/ClassRepo.java +++ b/dts-generator/src/main/java/com/telerik/dts/ClassRepo.java @@ -13,7 +13,8 @@ public class ClassRepo { private ClassRepo() { } - private static ArrayList cachedProviders = new ArrayList(); + private static ArrayList cachedProviders = new ArrayList<>(); + private static ArrayList cachedSuperProviders = new ArrayList<>(); private static HashSet classesSet = new HashSet<>(); private static List sortedClasses; private static int traversedFilesIdx = 0; @@ -22,18 +23,22 @@ public static void cacheJarFile(ClassMapProvider classMapProvider) { for (String className : classMapProvider.getClassMap().keySet()) { for (ClassMapProvider cachedProvider : cachedProviders) { JavaClass clazz = cachedProvider.getClassMap().get(className); - if (clazz != null) { -// return; -// String errMsg = "Class " + className + " conflict: " -// + classMapProvider.getPath() + " and " + cachedProvider.getPath(); -// throw new IllegalArgumentException(errMsg); - } } } cachedProviders.add(classMapProvider); } + public static void cacheSuperJarFile(ClassMapProvider classMapProvider) { + for (String className : classMapProvider.getClassMap().keySet()) { + for (ClassMapProvider cachedProvider : cachedProviders) { + JavaClass clazz = cachedProvider.getClassMap().get(className); + } + } + + cachedSuperProviders.add(classMapProvider); + } + public static void sortCachedProviders() { for(ClassMapProvider cmp : cachedProviders) { classesSet.addAll(cmp.getClassMap().keySet()); @@ -41,9 +46,7 @@ public static void sortCachedProviders() { sortedClasses = new ArrayList<>(classesSet); - Collections.sort(sortedClasses, new Comparator() { - @Override - public int compare(String className1, String className2) { + Collections.sort(sortedClasses, (className1, className2) -> { if(className1.startsWith(className2)) { return 1; } @@ -51,8 +54,7 @@ public int compare(String className1, String className2) { return -1; } return className1.compareTo(className2); - } - }); + }); } public static JavaClass findClass(String className) { @@ -63,6 +65,20 @@ public static JavaClass findClass(String className) { break; } } + if(clazz == null) { + return findSuperClass(className); + } + return clazz; + } + + public static JavaClass findSuperClass(String className) { + JavaClass clazz = null; + for (ClassMapProvider classMapProvider : cachedSuperProviders) { + clazz = classMapProvider.getClassMap().get(className); + if (clazz != null) { + break; + } + } return clazz; } diff --git a/dts-generator/src/main/java/com/telerik/dts/DtsApi.java b/dts-generator/src/main/java/com/telerik/dts/DtsApi.java index 2052722..014f502 100644 --- a/dts-generator/src/main/java/com/telerik/dts/DtsApi.java +++ b/dts-generator/src/main/java/com/telerik/dts/DtsApi.java @@ -1,32 +1,54 @@ package com.telerik.dts; +import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.Field; import org.apache.bcel.classfile.FieldOrMethod; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; +import org.apache.bcel.classfile.Signature; import org.apache.bcel.generic.ArrayType; import org.apache.bcel.generic.BasicType; import org.apache.bcel.generic.ObjectType; +import org.apache.bcel.generic.ReferenceType; import org.apache.bcel.generic.Type; import org.apache.bcel.util.BCELComparator; +import java.io.File; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.ba.generic.GenericObjectType; +import edu.umd.cs.findbugs.ba.generic.GenericSignatureParser; +import edu.umd.cs.findbugs.ba.generic.GenericUtilities; /** * Created by plamen5kov on 6/17/16. */ public class DtsApi { + public static List> externalGenerics = new ArrayList<>(); + public static List> generics = new ArrayList<>(); + public static List imports = new ArrayList<>(); + private static Map globalAliases = new HashMap<>(); + + private Map extendsOverrides = new HashMap<>(); + private Map superOverrides = new HashMap<>(); + private Map typeOverrides = new HashMap<>(); + private StringBuilder2 sbContent; - private StringBuilder2 sbHeaders; private Set references; private JavaClass prevClass; private String currentFileClassname; @@ -36,23 +58,27 @@ public class DtsApi { private Map aliasedTypes; private String[] namespaceParts; private int indent = 0; - private boolean writeMultipleFiles; + private boolean allGenericImplements; + private Pattern methodSignature = Pattern.compile("\\((?.*)\\)(?.*)"); + private Pattern isWordPattern = Pattern.compile("^[\\w\\d]+$"); - public DtsApi(boolean writeMultipleFiles) { - this.writeMultipleFiles = writeMultipleFiles; + public DtsApi(boolean allGenericImplements) { + this.allGenericImplements = allGenericImplements; this.indent = 0; overrideFieldComparator(); + setOverrides(); + setTypeOverrides(); + setGlobalAliases(); this.aliasedTypes = new HashMap<>(); } - public String generateDtsContent(List javaClasses) { + public String generateDtsContent(List javaClasses){ this.prevClass = null; if ((javaClasses != null) && (javaClasses.size() > 0)) { - references = new HashSet(); - sbHeaders = new StringBuilder2(); // javaClasses) { String tabs = getTabs(this.indent); - JavaClass superClass = getSuperClass(currClass); - List interfaces = getInterfaces(currClass); - String extendsLine = getExtendsLine(superClass, interfaces); + String extendsLine = getExtendsLine(currClass, typeDefinition); if (simpleClassName.equals("AccessibilityDelegate")) { - sbContent.appendln(tabs + "export class " + getFullClassNameConcatenated(currClass) + extendsLine + " {"); + sbContent.appendln(tabs + "export class " + getFullClassNameConcatenated(currClass) + getTypeSuffix(currentFileClassname, typeDefinition, extendsLine) + extendsLine + " {"); } else { - sbContent.appendln(tabs + "export" + (isAbstract && !isInterface ? " abstract " : " ") + "class " + simpleClassName + extendsLine + " {"); + sbContent.appendln(tabs + "export" + (isAbstract && !isInterface ? " abstract " : " ") + "class " + simpleClassName + getTypeSuffix(currentFileClassname, typeDefinition, extendsLine) + extendsLine + " {"); } // process member scope - mapNameMethod = new HashMap(); + mapNameMethod = new HashMap<>(); + + addClassField(currClass); // process constructors for interfaces if (isInterface) { @@ -104,22 +136,22 @@ public String generateDtsContent(List javaClasses) { List allInterfacesMethods = getAllInterfacesMethods(allInterfaces); Set allInterfaceFields = getAllInterfacesFields(allInterfaces); - processInterfaceConstructor(currClass, allInterfacesMethods); + processInterfaceConstructor(currClass, typeDefinition, allInterfacesMethods); for (Method m : allInterfacesMethods) { - processMethod(m, currClass, isInterface, methodsSet); + processMethod(m, currClass, typeDefinition, isInterface, methodsSet); } for (Field f : allInterfaceFields) { - processField(f, currClass); + processField(f, currClass, typeDefinition); } } else { List foms = getMembers(currClass, getAllInterfaces(currClass)); for (FieldOrMethod fom : foms) { if (fom instanceof Field) { - processField((Field) fom, currClass); + processField((Field) fom, currClass, typeDefinition); } else if (fom instanceof Method) { - processMethod((Method) fom, currClass, isInterface, methodsSet); + processMethod((Method) fom, currClass, typeDefinition, isInterface, methodsSet); } else { throw new IllegalArgumentException("Argument is not method or field"); } @@ -140,7 +172,7 @@ public String generateDtsContent(List javaClasses) { List allInterfacesMethods = getAllInterfacesMethods(allInterfaces); for (Method m : allInterfacesMethods) { - processMethod(m, currClass, isInterface, methodsSet); + processMethod(m, currClass, typeDefinition, isInterface, methodsSet); } } @@ -158,24 +190,132 @@ public String generateDtsContent(List javaClasses) { String[] refs = references.toArray(new String[references.size()]); Arrays.sort(refs); + } + + String conent = replaceIgnoredNamespaces(sbContent.toString()); + + return conent; + } + + private String replaceIgnoredNamespaces(String content) { + String regexFormat = "(?%s(?:(?:\\.[a-zA-Z\\d]*)|<[a-zA-Z\\d\\.<>]*>)*)(?[^a-zA-Z\\d]+)"; + // these namespaces are not known in some android api levels, so we cannot use them in android-support for instance, so we are replacing them with any + for (String ignoredNamespace: this.getIgnoredNamespaces()) { + String regexString = String.format(regexFormat, ignoredNamespace.replace(".", "\\.")); + content = content.replaceAll(regexString, "any$2"); + regexString = String.format(regexFormat, getGlobalAliasedClassName(ignoredNamespace).replace(".", "\\.")); + content = content.replaceAll(regexString, "any$2"); + } + + String javalangObject = "java.lang.Object"; + + // replace "extends any" with "extends java.lang.Object" + content = content.replace(" extends any ", String.format(" extends %s ", javalangObject)); - if(this.writeMultipleFiles) { - for (String r : refs) { - sbHeaders.append("/// "); + return content; + } + + public static String serializeGenerics() { + StringBuilder sb = new StringBuilder(); + sb.append("//Generics information:\n"); + for(Tuple generic: generics) { + sb.append(String.format("//%s:%s\n", generic.x, generic.y)); + } + return sb.toString(); + } + + public static void loadGenerics(File inputFile) throws Exception { + List lines = Files.readAllLines(inputFile.toPath()); + for(String line: lines) { + if(!line.equals("")) { + while(line.startsWith("/")){ + line = line.substring(1, line.length()); + } + String[] parts = line.split(":"); + if (parts.length != 2) { + throw new Exception(String.format("Invalid generic info(%s) in file %s", line, inputFile)); } + externalGenerics.add(new Tuple<>(parts[0], Integer.parseInt(parts[1]))); + } + } + } + + // Adds javalangObject types to all generics which are used without types + public static String replaceGenericsInText(String content) { + String any = "any"; + String result = content; + + List> allGenerics = Stream.concat(generics.stream(), externalGenerics.stream()).collect(Collectors.toList()); + + for(Tuple generic: allGenerics) { + result = replaceNonGenericUsage(result, generic.x, generic.y, any); + String globalAliasedClassName = getGlobalAliasedClassName(generic.x); + if(!generic.x.equals(globalAliasedClassName)) { + result = replaceNonGenericUsage(result, globalAliasedClassName, generic.y, any); } } - return sbHeaders.toString() + sbContent.toString(); + return result; } - private String getExtendsLine(JavaClass superClass, List interfaces) { - if (superClass == null) { - return ""; + private static String replaceNonGenericUsage(String content, String className, Integer occurencies, String javalangObject) { + String result = content; + Pattern usedAsNonGenericPattern = Pattern.compile("(?[^a-zA-Z\\d\\s:]*)" + className.replace(".", "\\.") + "(?[^a-zA-Z\\d^\\.^\\$^\\<])"); + Matcher matcher = usedAsNonGenericPattern.matcher(result); + if(matcher.find()) { + List arguments = new ArrayList<>(); + for (int i = 0; i < occurencies; i++) { + arguments.add(javalangObject); + } + String classSuffix = "<" + String.join(",", arguments) + ">"; + + System.out.println(String.format("Appending %s to occurrences of class %s without passed generic types", classSuffix, className)); + + String replaceString = String.format("$1%s%s$2", className, classSuffix); + result = matcher.replaceAll(replaceString); + } + return result; + } + + private String getExtendsLine(JavaClass currClass, TypeDefinition typeDefinition) { + String override = this.extendsOverrides.get(currClass.getClassName()); + if(override != null) { + System.out.println(String.format("Found extends override for class %s - %s", currClass.getClassName(), override)); + return " extends " + override; } + if (typeDefinition != null) { + StringBuilder result = new StringBuilder(); + ReferenceType parent = typeDefinition.getParent(); + + List interfaces = typeDefinition.getInterfaces(); + if(parent != null) { + result.append(" extends "); + result.append(getTypeScriptTypeFromJavaType(parent, typeDefinition)); + } + if(interfaces.size() == 1 || (this.allGenericImplements && interfaces.size() > 0)) { + result.append(" implements "); + + for (ReferenceType referenceType : interfaces) { + String tsType = getTypeScriptTypeFromJavaType(referenceType, typeDefinition); + if(!this.isPrimitiveTSType(tsType)) { + result.append(tsType + ", "); + } + } + result.deleteCharAt(result.lastIndexOf(",")); + } + return result.toString(); + } else { + JavaClass superClass = getSuperClass(currClass); + List interfaces = getInterfaces(currClass); + if(interfaces.size() == 1 && superClass == null && currClass.getSuperclassName().equals("java.lang.Object")) { + superClass = interfaces.get(0); + interfaces.clear(); + } + return getExtendsLine(superClass, interfaces); + } + } + private String getExtendsLine(JavaClass superClass, List interfaces) { StringBuilder implementsSegmentSb = new StringBuilder(); String implementsSegment = ""; if (interfaces.size() > 0) { @@ -194,36 +334,68 @@ private String getExtendsLine(JavaClass superClass, List interfaces) } - String extendedClass = superClass.getClassName().replaceAll("\\$", "\\."); - if (!typeBelongsInCurrentTopLevelNamespace(extendedClass)) { - extendedClass = getAliasedClassName(extendedClass); - } + if(superClass != null) { + String extendedClass = superClass.getClassName().replaceAll("\\$", "\\."); - return " extends " + extendedClass + implementsSegment; - } - - private String getAliasedClassName(String className) { - String aliasedType = aliasedTypes.get(className); - if (aliasedType == null) { - aliasedType = mangleClassname(className); - aliasedTypes.put(className, aliasedType); + if (!typeBelongsInCurrentTopLevelNamespace(extendedClass)) { + extendedClass = getAliasedClassName(extendedClass); + } - sbHeaders.append("import "); - sbHeaders.append(aliasedType); - sbHeaders.append(" = "); - sbHeaders.append(className); - sbHeaders.appendln(";"); + return " extends " + extendedClass + implementsSegment; + } else { + return implementsSegment; } + } - return aliasedType; + private String getAliasedClassName(String className) { + return mangleRootClassname(className); } private boolean typeBelongsInCurrentTopLevelNamespace(String className) { return className.startsWith(this.namespaceParts[0] + "."); } - private String mangleClassname(String className) { - return className.replaceAll("\\.", ""); + private static String getGlobalAliasedClassName(String className) { + String[] parts = className.split("\\."); + String rootNamespace = parts[0]; + if(globalAliases.containsKey(parts[0])) { + String aliasedNamespace = globalAliases.get(rootNamespace); + parts = Arrays.copyOfRange(parts, 1, parts.length); + String result = aliasedNamespace; + if(parts.length > 0) { + result += "." + String.join(".", parts); + } + return result; + } else { + return className; + } + } + + private static void addImport(String importToAdd) { + if(!imports.stream().anyMatch(x -> x.equals(importToAdd))){ + imports.add(importToAdd); + } + } + + private String mangleRootClassname(String className) { + String[] parts = className.split("\\."); + String rootNamespace = parts[0]; + if(globalAliases.containsKey(parts[0])) { + String aliasedNamespace = DtsApi.globalAliases.get(rootNamespace); + String aliasedType = aliasedTypes.get(rootNamespace); + if(aliasedType == null) { + aliasedTypes.put(rootNamespace, aliasedNamespace); + addImport(String.format("import %s = %s;\n", aliasedNamespace, rootNamespace)); + } + + parts = Arrays.copyOfRange(parts, 1, parts.length); + String result = aliasedNamespace; + if(parts.length > 0) { + result += "." + String.join(".", parts); + } + return result; + } + return className; } private int closePackage(JavaClass prevClass, JavaClass currClass) { @@ -323,32 +495,34 @@ private int openPackage(JavaClass prevClass, JavaClass currClass) { return indent; } - private void processInterfaceConstructor(JavaClass classInterface, List allInterfacesMethods) { + private void processInterfaceConstructor(JavaClass classInterface, TypeDefinition typeDefinition, List allInterfacesMethods) { String tabs = getTabs(this.indent + 1); - generateInterfaceConstructorContent(classInterface, tabs, allInterfacesMethods); + generateInterfaceConstructorContent(classInterface, typeDefinition, tabs, allInterfacesMethods); } - private void generateInterfaceConstructorContent(JavaClass classInterface, String tabs, List methods) { + private void generateInterfaceConstructorContent(JavaClass classInterface, TypeDefinition typeDefinition, String tabs, List methods) { generateInterfaceConstructorCommentBlock(classInterface, tabs); sbContent.appendln(tabs + "public constructor(implementation: {"); for (Method m : methods) { - sbContent.append(getTabs(this.indent + 2) + getMethodName(m) + getMethodParamSignature(classInterface, m)); + sbContent.append(getTabs(this.indent + 2) + getMethodName(m) + getMethodParamSignature(classInterface, typeDefinition, m)); String bmSig = ""; if (!isConstructor(m)) { - bmSig += ": " + getTypeScriptTypeFromJavaType(classInterface, m.getReturnType()); + bmSig += ": " + getTypeScriptTypeFromJavaType(this.getReturnType(m), typeDefinition); } sbContent.appendln(bmSig + ";"); } sbContent.appendln(tabs + "});"); + + sbContent.appendln(tabs + "public constructor();"); } private void generateInterfaceConstructorCommentBlock(JavaClass classInterface, String tabs) { sbContent.appendln(tabs + "/**"); - sbContent.appendln(tabs + " * Constructs a new instance of the " + classInterface.getClassName() + " interface with the provided implementation."); + sbContent.appendln(tabs + " * Constructs a new instance of the " + classInterface.getClassName() + " interface with the provided implementation. An empty constructor exists calling super() when extending the interface class."); // sbContent.appendln(tabs + " * @param implementation - allows implementor to define their own logic for all public methods."); // <- causes too much noise sbContent.appendln(tabs + " */"); } @@ -446,7 +620,7 @@ private Set getAllInterfacesFields(List interfaces) { } //method related - private void processMethod(Method m, JavaClass clazz, boolean isInterface, Set methodsSet) { + private void processMethod(Method m, JavaClass clazz, TypeDefinition typeDefinition, boolean isInterface, Set methodsSet) { String name = m.getName(); if (isPrivateGoogleApiMember(name)) return; @@ -473,16 +647,16 @@ private void processMethod(Method m, JavaClass clazz, boolean isInterface, Set referenceTypes = DtsApi.getTypeParameters(argumentsSignature); + Type[] types = new Type[referenceTypes.size()]; + types = referenceTypes.toArray(types); + return types; + } catch (ClassCastException classCast) { + return m.getArgumentTypes(); + } + } + } + return m.getArgumentTypes(); + } + + private static List getTypeParameters(String signature) { + GenericSignatureParser parser = new GenericSignatureParser("(" + signature + ")V"); + List types = new ArrayList<>(); + Iterator iter = parser.parameterSignatureIterator(); + + while(iter.hasNext()) { + String parameterString = iter.next(); + Type t = GenericUtilities.getType(parameterString); + if (t == null) { + return null; + } + + types.add(t); + } + + return types; + } + + // gets the full field type including generic types + private Type getFieldType(Field f) { + Signature signature = this.getSignature(f); + if(signature != null) { + String typeSignature = signature.getSignature(); + if(typeSignature.equals("")){ + return f.getType(); + } + try { + return GenericUtilities.getType(typeSignature); + } catch (ClassCastException classCast) { + return f.getType(); + } + } + return f.getType(); + } + + // gets the full method return type including generic types + private Type getReturnType(Method m) { + Signature signature = this.getSignature(m); + if(signature != null) { + Matcher matcher = methodSignature.matcher(signature.getSignature()); + if(matcher.matches()) { + String returnSignature = matcher.group(2); + if(returnSignature.equals("V")){ + return m.getReturnType(); // returning void + } + return GenericUtilities.getType(returnSignature); + } + } + return m.getReturnType(); + } + private void writeMethods(Set methodsSet) { for (String m : methodsSet) { sbContent.appendln(m); @@ -564,13 +829,15 @@ private JavaClass getSuperClass(JavaClass clazz) { } String scn = clazz.getSuperclassName(); - JavaClass currClass = ClassRepo.findClass(scn); + String override = this.superOverrides.get(clazz.getClassName()); + if(override != null) { + scn = override; + } - if (currClass == null) { + if(scn.equals("") || scn == null) { return null; -// throw new NoClassDefFoundError("Couldn't find class: " + scn + " required by class: " + clazz.getClassName() + ". You need to provide the jar containing the missing class: " + scn); } - + JavaClass currClass = ClassRepo.findClass(scn); return currClass; } @@ -593,11 +860,11 @@ private String getMethodName(Method m) { return name; } - private String getMethodParamSignature(JavaClass clazz, Method m) { + private String getMethodParamSignature(JavaClass clazz, TypeDefinition typeDefinition, Method m) { StringBuilder sb = new StringBuilder(); sb.append("("); int idx = 0; - for (Type type : m.getArgumentTypes()) { + for (Type type : this.getArgumentTypes(m)) { if (idx > 0) { sb.append(", "); } @@ -605,7 +872,7 @@ private String getMethodParamSignature(JavaClass clazz, Method m) { sb.append(idx++); sb.append(": "); - String paramTypeName = getTypeScriptTypeFromJavaType(clazz, type); + String paramTypeName = getTypeScriptTypeFromJavaType(type, typeDefinition); // TODO: Pete: if (paramTypeName.startsWith("java.util.function")) { @@ -621,7 +888,7 @@ private String getMethodParamSignature(JavaClass clazz, Method m) { } //field related - private void processField(Field f, JavaClass clazz) { + private void processField(Field f, JavaClass clazz, TypeDefinition typeDefinition) { String fieldName = f.getName(); if (isPrivateGoogleApiMember(fieldName)) return; @@ -631,10 +898,27 @@ private void processField(Field f, JavaClass clazz) { if (f.isStatic()) { sbContent.append("static "); } - sbContent.appendln(f.getName() + ": " + getTypeScriptTypeFromJavaType(clazz, f.getType()) + ";"); + sbContent.appendln(f.getName() + ": " + getTypeScriptTypeFromJavaType(this.getFieldType(f), typeDefinition) + ";"); + } + + private void addClassField(JavaClass clazz) { + String tabs = getTabs(this.indent + 1); + sbContent.append(String.format("%spublic static class: java.lang.Class<%s>;\n", tabs, clazz.getClassName().replace("$", "."))); + } + + private boolean isPrimitiveTSType(String tsType) { + switch (tsType) { + case "void": + case "string": + case "boolean": + case "number": + return true; + default: + return false; + } } - private String getTypeScriptTypeFromJavaType(JavaClass clazz, Type type) { + private String getTypeScriptTypeFromJavaType(Type type, TypeDefinition typeDefinition) { String tsType; String typeSig = type.getSignature(); @@ -662,7 +946,7 @@ private String getTypeScriptTypeFromJavaType(JavaClass clazz, Type type) { break; default: StringBuilder sb = new StringBuilder(); - convertToTypeScriptType(type, sb); + convertToTypeScriptType(type, typeDefinition, sb); tsType = sb.toString(); if (tsType.startsWith("java.util.function") || isPrivateGoogleApiClass(tsType)) { @@ -673,10 +957,11 @@ private String getTypeScriptTypeFromJavaType(JavaClass clazz, Type type) { return tsType; } - private void convertToTypeScriptType(Type type, StringBuilder tsType) { + private void convertToTypeScriptType(Type type, TypeDefinition typeDefinition, StringBuilder tsType) { boolean isPrimitive = type instanceof BasicType; boolean isArray = type instanceof ArrayType; boolean isObjectType = type instanceof ObjectType; + boolean isGenericObjectType = type instanceof GenericObjectType; if (isPrimitive) { if (type.equals(Type.BOOLEAN)) { @@ -693,29 +978,71 @@ private void convertToTypeScriptType(Type type, StringBuilder tsType) { } else if (isArray) { tsType.append("native.Array<"); Type elementType = ((ArrayType) type).getElementType(); - convertToTypeScriptType(elementType, tsType); + useAnyInsteadOfJavaLangObject(elementType, typeDefinition, tsType); tsType.append(">"); } else if (type.equals(Type.STRING)) { tsType.append("string"); } else if (isObjectType) { + if(isGenericObjectType) { + GenericObjectType genericObjectType = (GenericObjectType)type; + String genericVariable = genericObjectType.getVariable(); + if(genericVariable != null && isWordPattern.matcher(genericVariable).matches()) { + if(typeDefinition != null && typeDefinition.getGenericDefinitions() != null + && typeDefinition.getGenericDefinitions().stream() + .filter(definition -> definition.getLabel().equals(genericVariable)).count() > 0 + && ((ObjectType) typeDefinition.getParent()).getClassName().equals("java.lang.Object")){ + tsType.append(genericObjectType.getVariable()); + addReference(type); + return; + } + } + } ObjectType objType = (ObjectType) type; String typeName = objType.getClassName(); if (typeName.contains("$")) { typeName = typeName.replaceAll("\\$", "\\."); } + if(this.typeOverrides.containsKey(typeName)){ + typeName = this.typeOverrides.get(typeName); + } + if (!typeBelongsInCurrentTopLevelNamespace(typeName) && !typeName.startsWith("java.util.function.") && !isPrivateGoogleApiClass(typeName)) { tsType.append(getAliasedClassName(typeName)); } else { tsType.append(typeName); } + if(type instanceof GenericObjectType) { + GenericObjectType genericType = (GenericObjectType) type; + if (genericType.getNumParameters() > 0) { + tsType.append("<"); + for (ReferenceType refType: genericType.getParameters()){ + useAnyInsteadOfJavaLangObject(refType, typeDefinition, tsType); + tsType.append(','); + } + tsType.deleteCharAt(tsType.lastIndexOf(",")); + tsType.append(">"); + } + } + addReference(type); } else { throw new RuntimeException("Unhandled type=" + type.getSignature()); } } + private void useAnyInsteadOfJavaLangObject(Type refType, TypeDefinition typeDefinition, StringBuilder tsType) { +// if (refType instanceof ObjectType) { +// ObjectType currentType = (ObjectType)refType; +// if (currentType.getClassName().equals("java.lang.Object")) { +// tsType.append("any"); +// return; +// } +// } + this.convertToTypeScriptType(refType, typeDefinition, tsType); + } + private void addReference(Type type) { boolean isObjectType = type instanceof ObjectType; if (isObjectType) { @@ -731,8 +1058,8 @@ private void addReference(Type type) { } private List getMembers(JavaClass javaClass, List interfaces) { - Set methodNames = new HashSet(); - ArrayList members = new ArrayList(); + Set methodNames = new HashSet<>(); + ArrayList members = new ArrayList<>(); List allInterfacesMethods = getAllInterfacesMethods(interfaces); List methods = new ArrayList<>(); @@ -771,6 +1098,34 @@ private String getFullClassNameConcatenated(JavaClass javaClass) { return fullName; } + // gets the suffix like > + private String getTypeSuffix(String fullClassName, TypeDefinition typeDefinition, String extendsLine) { + if(typeDefinition == null) { + return ""; + } + List genericDefinitions = typeDefinition.getGenericDefinitions(); + if(genericDefinitions != null) { + List parts = new ArrayList<>(); + String genericClassName = fullClassName.replace("$", "."); + + // remove the current class name if it already exists + generics = generics.stream().filter(generic -> generic.x != genericClassName).collect(Collectors.toList()); + + generics.add(new Tuple<>(fullClassName.replace("$", "."), genericDefinitions.size())); + for (TypeDefinition.GenericDefinition definition: genericDefinitions) { + ObjectType genericObjectType = (ObjectType)definition.getType(); + String baseClassName = getAliasedClassName(genericObjectType.getClassName()); + String resultType = definition.getType().toString(); + String typeToExtend = resultType.replace(genericObjectType.getClassName(), baseClassName); + //parts.add(String.format("%s extends %s", definition.getLabel(), typeToExtend)); + parts.add(definition.getLabel()); + } + return "<" + String.join(", ", parts) + "> "; + } else { + return ""; + } + } + private String getTabs(int count) { String tabs = new String(new char[count]).replace("\0", "\t"); return tabs; @@ -800,4 +1155,58 @@ public int hashCode(Object o) { } }); } + + private void setOverrides() { + this.setTypeOverrides(); + this.setExtendsOverrides(); + this.setSuperOverrides(); + } + + private void setExtendsOverrides() { + // here we put extends overrides to avoid manual work to fix the generated .d.ts file + this.extendsOverrides.put("android.renderscript.ProgramFragmentFixedFunction$Builder", "android.renderscript.Program.BaseProgramBuilder"); // android-17 + this.extendsOverrides.put("android.renderscript.ProgramVertexFixedFunction$Builder", "android.renderscript.ProgramVertex.Builder"); // android-17 + this.extendsOverrides.put("android.support.v4.app.JobIntentService$JobServiceEngineImpl", "android.support.v4.app.JobIntentService.CompatJobEngine"); // android-support + } + + private void setSuperOverrides() { + // here we put super overrides + this.superOverrides.put("android.support.v4.view.GestureDetectorCompat", "android.view.GestureDetector"); // android-support + } + + private void setTypeOverrides() { + // here we put type overrides if we want to change return types of java.lang.Object for instance to any + this.typeOverrides.put("java.lang.Object", "any"); + this.typeOverrides.put("java.lang.CharSequence", "string"); + } + + private void setGlobalAliases() { + // here we put extends overrides to avoid manual work to fix the generated .d.ts file + globalAliases.put("android", "globalAndroid"); + } + + private List getIgnoredNamespaces(){ + // for some reason these namespaces are references but not existing, so we are replacing all types from these namespaces with "any" + List result = new ArrayList<>(); + + result.add("android.app.job"); + result.add("android.app.SharedElementCallback"); + result.add("android.arch"); + result.add("android.content.pm.ShortcutInfo"); + result.add("android.graphics.drawable.Icon"); + result.add("android.graphics.Outline"); + result.add("android.view.SearchEvent"); + result.add("android.view.KeyboardShortcutGroup"); + result.add("android.view.ViewStructure"); + result.add("android.media.browse"); + result.add("android.media.session"); + result.add("android.media.AudioAttributes"); + result.add("android.media.MediaMetadata"); + result.add("android.media.Rating"); + result.add("android.service.media"); + result.add("android.print"); + result.add("java.util.function"); + + return result; + } } diff --git a/dts-generator/src/main/java/com/telerik/dts/FileWriter.java b/dts-generator/src/main/java/com/telerik/dts/FileHelper.java similarity index 51% rename from dts-generator/src/main/java/com/telerik/dts/FileWriter.java rename to dts-generator/src/main/java/com/telerik/dts/FileHelper.java index 9472e5c..8990095 100644 --- a/dts-generator/src/main/java/com/telerik/dts/FileWriter.java +++ b/dts-generator/src/main/java/com/telerik/dts/FileHelper.java @@ -6,42 +6,38 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintStream; +import java.nio.file.Files; +import java.util.List; /** * Created by plamen5kov on 6/17/16. */ -public class FileWriter { - private final String DEFAULT_DTS_FILE_NAME = "android"; +public class FileHelper { + public static final String DEFAULT_DTS_FILE_NAME = "android.d.ts"; + public static final String DEFAULT_DECLARATIONS_FILE_NAME = "android-declarations.d.ts"; private String defaultDtsFileName; - private boolean writeMultipleFiles; private PrintStream ps; private File outDir; - public FileWriter(File outDir, boolean writeMultipleFiles) { - this.writeMultipleFiles = writeMultipleFiles; + public FileHelper(File outDir) { this.defaultDtsFileName = DEFAULT_DTS_FILE_NAME; // + java.util.UUID.randomUUID().toString(); this.outDir = outDir; } - public void write(String content, String fileName) { + public void writeToFile(String content, String fileName, Boolean append) { try { - if(this.writeMultipleFiles) { - this.ps = new PrintStream(new File(this.outDir.getAbsolutePath(), fileName + ".d.ts")); - ps.println("/// "); - this.ps.print(content); - } - else { - String outFile = this.outDir.getAbsolutePath() + File.separator + this.defaultDtsFileName + ".d.ts"; - this.ps = new PrintStream(new FileOutputStream(outFile, /*append*/true)); + String outFile = this.outDir.getAbsolutePath() + File.separator + fileName; + this.ps = new PrintStream(new FileOutputStream(outFile, /*append*/append)); - this.ps.print(content); - this.ps.println(); - } + this.ps.print(content); + this.ps.println(); } catch (FileNotFoundException e) { e.printStackTrace(); } + finally { if(this.ps != null) { this.ps.close(); @@ -49,10 +45,18 @@ public void write(String content, String fileName) { } } - public void writeHelperTypings(String content, boolean append) { + public String readFileContent(String fileName) { + try { + List lines = Files.readAllLines(new File(this.outDir.getAbsolutePath(), fileName).toPath()); + return String.join("\n", lines.toArray(new String[lines.size()])); + } catch (IOException ex) { + return null; + } + } + + public void writeHelperTypings(String content, String fileName, boolean append) { try { - String outFile = this.outDir.getAbsolutePath() + File.separator + - (this.writeMultipleFiles ? "_helpers.d.ts": (this.defaultDtsFileName + ".d.ts")); + String outFile = this.outDir.getAbsolutePath() + File.separator + fileName; this.ps = new PrintStream(new FileOutputStream(outFile, append)); this.ps.print(content); diff --git a/dts-generator/src/main/java/com/telerik/dts/Generator.java b/dts-generator/src/main/java/com/telerik/dts/Generator.java index e71147a..5970684 100644 --- a/dts-generator/src/main/java/com/telerik/dts/Generator.java +++ b/dts-generator/src/main/java/com/telerik/dts/Generator.java @@ -6,7 +6,9 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -14,45 +16,78 @@ */ public class Generator { - private static File outFolder; - private FileWriter fw; + private static File inputGenericsFile; + private FileHelper fileHelper; private DtsApi dtsApi; + private boolean allGenericImplements; + private boolean skipDeclarations; + private boolean classMode; + private String outFileName; + private String declarationsFileName; - public void start(InputParameters inputParameters) throws IOException { - outFolder = inputParameters.getOutputDir(); - this.fw = new FileWriter(inputParameters.getOutputDir(), inputParameters.isGenerateMultipleFiles()); - this.dtsApi = new DtsApi(inputParameters.isGenerateMultipleFiles()); + public void start(InputParameters inputParameters) throws Exception { + Generator.inputGenericsFile = inputParameters.getInputGenerics(); + this.allGenericImplements = inputParameters.isAllGenericImplementsEnabled(); + this.skipDeclarations = inputParameters.getSkipDeclarations(); + this.classMode = inputParameters.getClassMode(); + this.fileHelper = new FileHelper(inputParameters.getOutputDir()); + this.dtsApi = new DtsApi(allGenericImplements); + this.outFileName = FileHelper.DEFAULT_DTS_FILE_NAME; + this.declarationsFileName = FileHelper.DEFAULT_DECLARATIONS_FILE_NAME; loadJavaClasses(inputParameters.getInputJars()); + loadSuperClasses(inputParameters.getSuperJars()); ClassRepo.sortCachedProviders(); generateDts(); } - private void generateDts() { - writeHelperTypings(); + private void generateDts() throws Exception { + + if(inputGenericsFile != null){ + DtsApi.loadGenerics(inputGenericsFile); + } + + if(!this.skipDeclarations) { + this.fileHelper.writeToFile(String.format("/// \n", this.declarationsFileName), this.outFileName, false); + } while (ClassRepo.hasNext()) { List classFiles = ClassRepo.getNextClassGroup(); String generatedContent = this.dtsApi.generateDtsContent(classFiles); - this.fw.write(generatedContent, classFiles.get(0).getFileName()/*fileName*/); + this.fileHelper.writeToFile(generatedContent, this.outFileName, true); + } + + String content = this.fileHelper.readFileContent(this.outFileName); + if(content != null) { + String replacedContent = DtsApi.replaceGenericsInText(content); + if(!content.equals(replacedContent)) { + this.fileHelper.writeToFile(replacedContent, this.outFileName, false); + } + + String serializedGenerics = DtsApi.serializeGenerics(); + if(!serializedGenerics.equals("")) { + this.fileHelper.writeToFile(serializedGenerics, this.outFileName, true); + } + } + + if(!this.skipDeclarations) { + this.writeDeclarations(); } } - private void writeHelperTypings() { - List helperTypings = new ArrayList<>(); - helperTypings.add("declare module native {\n" + - "\texport class Array {\n" + - "\t\tpublic constructor();\n" + - "\t}\n" + - "}\n"); + private void writeDeclarations() { + List imports = DtsApi.imports; + imports.add(0, "declare module native {\texport class Array {\tconstructor(); length: number; [index: number]: T; } }\n"); - boolean append = false; + String existingContent = this.fileHelper.readFileContent(this.declarationsFileName); + existingContent = existingContent != null ? existingContent : ""; - for (String helper : helperTypings) { - this.fw.writeHelperTypings(helper, append); - append = true; + for (String item : imports) { + if(!existingContent.contains(item)) { + this.fileHelper.writeToFile(item, this.declarationsFileName, true); + } } } @@ -62,10 +97,32 @@ private void loadJavaClasses(List jars) throws IOException { if (file.isFile() && file.getName().endsWith(".jar")) { JarFile jar = JarFile.readJar(file.getAbsolutePath()); ClassRepo.cacheJarFile(jar); + } else if (file.isDirectory()) { + if(this.classMode) { + ClassDirectrory dir = ClassDirectrory.readDirectory(file.getAbsolutePath()); + ClassRepo.cacheJarFile(dir); + } else { + loadJavaClasses(Arrays.asList(file.listFiles())); + } + } + } else { + throw new IOException(String.format("File %s does not exist", file.getName())); + } + } + } + + private void loadSuperClasses(List jars) throws IOException { + for (File file : jars) { + if (file.exists()) { + if (file.isFile() && file.getName().endsWith(".jar")) { + JarFile jar = JarFile.readJar(file.getAbsolutePath()); + ClassRepo.cacheSuperJarFile(jar); } else if (file.isDirectory()) { ClassDirectrory dir = ClassDirectrory.readDirectory(file.getAbsolutePath()); - ClassRepo.cacheJarFile(dir); + ClassRepo.cacheSuperJarFile(dir); } + } else { + throw new IOException(String.format("File %s does not exist", file.getName())); } } } diff --git a/dts-generator/src/main/java/com/telerik/dts/Tuple.java b/dts-generator/src/main/java/com/telerik/dts/Tuple.java new file mode 100644 index 0000000..1f2e795 --- /dev/null +++ b/dts-generator/src/main/java/com/telerik/dts/Tuple.java @@ -0,0 +1,10 @@ +package com.telerik.dts; + +public class Tuple { + public final X x; + public final Y y; + public Tuple(X x, Y y) { + this.x = x; + this.y = y; + } +} \ No newline at end of file diff --git a/dts-generator/src/main/java/com/telerik/dts/TypeDefinition.java b/dts-generator/src/main/java/com/telerik/dts/TypeDefinition.java new file mode 100644 index 0000000..d93ce58 --- /dev/null +++ b/dts-generator/src/main/java/com/telerik/dts/TypeDefinition.java @@ -0,0 +1,103 @@ +package com.telerik.dts; + +import org.apache.bcel.generic.ObjectType; +import org.apache.bcel.generic.ReferenceType; +import org.apache.bcel.generic.Type; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import edu.umd.cs.findbugs.ba.generic.GenericObjectType; +import edu.umd.cs.findbugs.ba.generic.GenericUtilities; + +public class TypeDefinition { + private static Pattern typeSignature = Pattern.compile("^(<(?.*)>)?(?L.*)$"); + //private static Pattern extendsGenericType = Pattern.compile("(?\\w+)\\:+(?L.*?(?=;\\w+|;$))"); + private static Pattern extendsGenericType = Pattern.compile("(?\\w+)\\:+(?L.*?(?=(?=;\\w+|;$)|(?=<.*>)))"); + + private String signature; + private String className; + private ReferenceType parent; + private List genericDefinitions = null; + private List interfaces = null; + + public class GenericDefinition { + private String label; + private Type type; + + GenericDefinition(String label, Type type) { + this.label = label; + this.type = type; + } + + public String getLabel() { + return label; + } + + public Type getType() { + return type; + } + } + + public TypeDefinition(String signature, String className) { + this.signature = signature; + this.className = className; + this.checkSignature(); + } + + public ReferenceType getParent() { + return parent; + } + + public List getInterfaces() { + return interfaces; + } + + public List getGenericDefinitions() { + return genericDefinitions; + } + + private void checkSignature() { + Matcher matcher = typeSignature.matcher(this.signature); + if(matcher.matches()){ + String typeGenericsSignature = matcher.group(2); + String interfacesSignature = matcher.group(3); + if (typeGenericsSignature != null) { + this.setTypeGenerics(typeGenericsSignature); + } + if (interfacesSignature != null) { + this.interfaces = GenericUtilities.getTypeParameters(interfacesSignature); + if(this.interfaces.size() > 0){ + // we assume that the first type parameter is the base class and the others are interfaces + parent = this.interfaces.get(0); + if ((parent instanceof ObjectType) && + (((ObjectType)parent).getClassName().equals("java.lang.Enum") + || ((ObjectType)parent).getClassName().equals("java.lang.Object"))){ + if(((ObjectType)parent).getClassName().equals("java.lang.Enum")) { + parent = null; // we don't need extends for enum + } + // if we have only one another interface set it as a parent + // otherwise we don't know which one to set as a parent + if(this.interfaces.size() == 2) { + parent = this.interfaces.get(1); + this.interfaces.remove(1); + } + } + this.interfaces.remove(0); + } + } + } + } + + private void setTypeGenerics(String typeGenericsSignature) { + Matcher matcher = extendsGenericType.matcher(typeGenericsSignature); + this.genericDefinitions = new ArrayList<>(); + while (matcher.find()) { + String label = matcher.group(1); + Type type = GenericUtilities.getType(matcher.group(2) + ";"); + this.genericDefinitions.add(new GenericDefinition(label, type)); + } + } +} diff --git a/libs/android-support/27.0.1/android-support-animated-vector-drawable.jar b/libs/android-support/27.0.1/android-support-animated-vector-drawable.jar new file mode 100644 index 0000000..ad99751 Binary files /dev/null and b/libs/android-support/27.0.1/android-support-animated-vector-drawable.jar differ diff --git a/libs/android-support/27.0.1/android-support-annotations.jar b/libs/android-support/27.0.1/android-support-annotations.jar new file mode 100644 index 0000000..7813ce7 Binary files /dev/null and b/libs/android-support/27.0.1/android-support-annotations.jar differ diff --git a/libs/android-support/27.0.1/android-support-compat.jar b/libs/android-support/27.0.1/android-support-compat.jar new file mode 100644 index 0000000..6a38590 Binary files /dev/null and b/libs/android-support/27.0.1/android-support-compat.jar differ diff --git a/libs/android-support/27.0.1/android-support-core-ui.jar b/libs/android-support/27.0.1/android-support-core-ui.jar new file mode 100644 index 0000000..47b77e0 Binary files /dev/null and b/libs/android-support/27.0.1/android-support-core-ui.jar differ diff --git a/libs/android-support/27.0.1/android-support-core-utils.jar b/libs/android-support/27.0.1/android-support-core-utils.jar new file mode 100644 index 0000000..145d3bf Binary files /dev/null and b/libs/android-support/27.0.1/android-support-core-utils.jar differ diff --git a/libs/android-support/27.0.0/android-support-design.jar b/libs/android-support/27.0.1/android-support-design.jar similarity index 99% rename from libs/android-support/27.0.0/android-support-design.jar rename to libs/android-support/27.0.1/android-support-design.jar index b610908..ad56c19 100644 Binary files a/libs/android-support/27.0.0/android-support-design.jar and b/libs/android-support/27.0.1/android-support-design.jar differ diff --git a/libs/android-support/27.0.1/android-support-fragment.jar b/libs/android-support/27.0.1/android-support-fragment.jar new file mode 100644 index 0000000..aa116ce Binary files /dev/null and b/libs/android-support/27.0.1/android-support-fragment.jar differ diff --git a/libs/android-support/27.0.1/android-support-media-compat.jar b/libs/android-support/27.0.1/android-support-media-compat.jar new file mode 100644 index 0000000..46dc9a9 Binary files /dev/null and b/libs/android-support/27.0.1/android-support-media-compat.jar differ diff --git a/libs/android-support/27.0.1/android-support-multidex.jar b/libs/android-support/27.0.1/android-support-multidex.jar new file mode 100644 index 0000000..6b487de Binary files /dev/null and b/libs/android-support/27.0.1/android-support-multidex.jar differ diff --git a/libs/android-support/27.0.1/android-support-recyclerview-v7.jar b/libs/android-support/27.0.1/android-support-recyclerview-v7.jar new file mode 100644 index 0000000..b7fca42 Binary files /dev/null and b/libs/android-support/27.0.1/android-support-recyclerview-v7.jar differ diff --git a/libs/android-support/27.0.1/android-support-transition.jar b/libs/android-support/27.0.1/android-support-transition.jar new file mode 100644 index 0000000..ab741bc Binary files /dev/null and b/libs/android-support/27.0.1/android-support-transition.jar differ diff --git a/libs/android-support/27.0.0/android-support-v4.jar b/libs/android-support/27.0.1/android-support-v4.jar similarity index 79% rename from libs/android-support/27.0.0/android-support-v4.jar rename to libs/android-support/27.0.1/android-support-v4.jar index b9d87e7..b59e7d6 100644 Binary files a/libs/android-support/27.0.0/android-support-v4.jar and b/libs/android-support/27.0.1/android-support-v4.jar differ diff --git a/libs/android-support/27.0.0/android-support-v7-appcompat.jar b/libs/android-support/27.0.1/android-support-v7-appcompat.jar similarity index 99% rename from libs/android-support/27.0.0/android-support-v7-appcompat.jar rename to libs/android-support/27.0.1/android-support-v7-appcompat.jar index 2a6f146..2e69969 100644 Binary files a/libs/android-support/27.0.0/android-support-v7-appcompat.jar and b/libs/android-support/27.0.1/android-support-v7-appcompat.jar differ diff --git a/libs/android-support/27.0.1/android-support-vector-drawable.jar b/libs/android-support/27.0.1/android-support-vector-drawable.jar new file mode 100644 index 0000000..0598875 Binary files /dev/null and b/libs/android-support/27.0.1/android-support-vector-drawable.jar differ diff --git a/libs/generics.txt b/libs/generics.txt new file mode 100644 index 0000000..07d7797 --- /dev/null +++ b/libs/generics.txt @@ -0,0 +1,194 @@ +//android.accounts.AccountManagerCallback:1 +//android.accounts.AccountManagerFuture:1 +//android.animation.TypeEvaluator:1 +//android.app.LoaderManager.LoaderCallbacks:1 +//android.content.AsyncTaskLoader:1 +//android.content.ContentProvider.PipeDataWriter:1 +//android.content.Loader:1 +//android.content.Loader.OnLoadCanceledListener:1 +//android.content.Loader.OnLoadCompleteListener:1 +//android.database.Observable:1 +//android.os.AsyncTask:3 +//android.os.Parcelable.ClassLoaderCreator:1 +//android.os.Parcelable.Creator:1 +//android.os.RemoteCallbackList:1 +//android.test.ActivityInstrumentationTestCase:1 +//android.test.ActivityInstrumentationTestCase2:1 +//android.test.ActivityUnitTestCase:1 +//android.test.ApplicationTestCase:1 +//android.test.ProviderTestCase:1 +//android.test.ProviderTestCase2:1 +//android.test.ServiceTestCase:1 +//android.test.SingleLaunchActivityTestCase:1 +//android.util.LongSparseArray:1 +//android.util.LruCache:2 +//android.util.Pair:2 +//android.util.Property:2 +//android.util.SparseArray:1 +//android.webkit.ValueCallback:1 +//android.widget.AdapterView:1 +//android.widget.ArrayAdapter:1 +//com.android.internal.util.Predicate:1 +//java.lang.Class:1 +//java.lang.Comparable:1 +//java.lang.Enum:1 +//java.lang.InheritableThreadLocal:1 +//java.lang.Iterable:1 +//java.lang.ThreadLocal:1 +//java.lang.ref.PhantomReference:1 +//java.lang.ref.Reference:1 +//java.lang.ref.ReferenceQueue:1 +//java.lang.ref.SoftReference:1 +//java.lang.ref.WeakReference:1 +//java.lang.reflect.Constructor:1 +//java.lang.reflect.TypeVariable:1 +//java.security.PrivilegedAction:1 +//java.security.PrivilegedExceptionAction:1 +//java.util.AbstractCollection:1 +//java.util.AbstractList:1 +//java.util.AbstractMap:2 +//java.util.AbstractMap.SimpleEntry:2 +//java.util.AbstractMap.SimpleImmutableEntry:2 +//java.util.AbstractQueue:1 +//java.util.AbstractSequentialList:1 +//java.util.AbstractSet:1 +//java.util.ArrayDeque:1 +//java.util.ArrayList:1 +//java.util.Collection:1 +//java.util.Comparator:1 +//java.util.Deque:1 +//java.util.Dictionary:2 +//java.util.EnumMap:2 +//java.util.EnumSet:1 +//java.util.Enumeration:1 +//java.util.HashMap:2 +//java.util.HashSet:1 +//java.util.Hashtable:2 +//java.util.IdentityHashMap:2 +//java.util.Iterator:1 +//java.util.LinkedHashMap:2 +//java.util.LinkedHashSet:1 +//java.util.LinkedList:1 +//java.util.List:1 +//java.util.ListIterator:1 +//java.util.Map:2 +//java.util.Map.Entry:2 +//java.util.NavigableMap:2 +//java.util.NavigableSet:1 +//java.util.PriorityQueue:1 +//java.util.Queue:1 +//java.util.ServiceLoader:1 +//java.util.Set:1 +//java.util.SortedMap:2 +//java.util.SortedSet:1 +//java.util.Stack:1 +//java.util.TreeMap:2 +//java.util.TreeSet:1 +//java.util.Vector:1 +//java.util.WeakHashMap:2 +//java.util.concurrent.ArrayBlockingQueue:1 +//java.util.concurrent.BlockingDeque:1 +//java.util.concurrent.BlockingQueue:1 +//java.util.concurrent.Callable:1 +//java.util.concurrent.CompletionService:1 +//java.util.concurrent.ConcurrentHashMap:2 +//java.util.concurrent.ConcurrentLinkedQueue:1 +//java.util.concurrent.ConcurrentMap:2 +//java.util.concurrent.ConcurrentNavigableMap:2 +//java.util.concurrent.ConcurrentSkipListMap:2 +//java.util.concurrent.ConcurrentSkipListSet:1 +//java.util.concurrent.CopyOnWriteArrayList:1 +//java.util.concurrent.CopyOnWriteArraySet:1 +//java.util.concurrent.DelayQueue:1 +//java.util.concurrent.Exchanger:1 +//java.util.concurrent.ExecutorCompletionService:1 +//java.util.concurrent.Future:1 +//java.util.concurrent.FutureTask:1 +//java.util.concurrent.LinkedBlockingDeque:1 +//java.util.concurrent.LinkedBlockingQueue:1 +//java.util.concurrent.PriorityBlockingQueue:1 +//java.util.concurrent.RunnableFuture:1 +//java.util.concurrent.RunnableScheduledFuture:1 +//java.util.concurrent.ScheduledFuture:1 +//java.util.concurrent.SynchronousQueue:1 +//java.util.concurrent.atomic.AtomicIntegerFieldUpdater:1 +//java.util.concurrent.atomic.AtomicLongFieldUpdater:1 +//java.util.concurrent.atomic.AtomicMarkableReference:1 +//java.util.concurrent.atomic.AtomicReference:1 +//java.util.concurrent.atomic.AtomicReferenceArray:1 +//java.util.concurrent.atomic.AtomicReferenceFieldUpdater:2 +//java.util.concurrent.atomic.AtomicStampedReference:1 +//org.apache.http.client.ResponseHandler:1 +//android.support.design.widget.BaseTransientBottomBar:1 +//android.support.design.widget.BaseTransientBottomBar.BaseCallback:1 +//android.support.design.widget.BottomSheetBehavior:1 +//android.support.design.widget.CoordinatorLayout.Behavior:1 +//android.support.design.widget.DirectedAcyclicGraph:1 +//android.support.design.widget.HeaderBehavior:1 +//android.support.design.widget.SwipeDismissBehavior:1 +//android.support.design.widget.ViewOffsetBehavior:1 +//android.support.transition.PathProperty:1 +//android.support.v4.app.FragmentHostCallback:1 +//android.support.v4.app.LoaderManager.LoaderCallbacks:1 +//android.support.v4.content.AsyncTaskLoader:1 +//android.support.v4.content.Loader:1 +//android.support.v4.content.Loader.OnLoadCanceledListener:1 +//android.support.v4.content.Loader.OnLoadCompleteListener:1 +//android.support.v4.content.ModernAsyncTask:3 +//android.support.v4.content.ModernAsyncTask.AsyncTaskResult:1 +//android.support.v4.content.ModernAsyncTask.WorkerRunnable:2 +//android.support.v4.graphics.TypefaceCompatBaseImpl.StyleExtractor:1 +//android.support.v4.media.MediaBrowserCompatApi21.ConnectionCallbackProxy:1 +//android.support.v4.media.MediaBrowserCompatApi21.SubscriptionCallbackProxy:1 +//android.support.v4.media.MediaBrowserCompatApi23.ItemCallbackProxy:1 +//android.support.v4.media.MediaBrowserCompatApi26.SubscriptionCallbackProxy:1 +//android.support.v4.media.MediaBrowserServiceCompat.Result:1 +//android.support.v4.media.MediaBrowserServiceCompatApi21.ResultWrapper:1 +//android.support.v4.media.session.MediaControllerCompatApi21.CallbackProxy:1 +//android.support.v4.media.session.MediaSessionCompatApi21.CallbackProxy:1 +//android.support.v4.media.session.MediaSessionCompatApi23.CallbackProxy:1 +//android.support.v4.media.session.MediaSessionCompatApi24.CallbackProxy:1 +//android.support.v4.os.ParcelableCompat.ParcelableCompatCreatorHoneycombMR2:1 +//android.support.v4.os.ParcelableCompatCreatorCallbacks:1 +//android.support.v4.provider.SelfDestructiveThread.ReplyCallback:1 +//android.support.v4.util.ArrayMap:2 +//android.support.v4.util.ArraySet:1 +//android.support.v4.util.CircularArray:1 +//android.support.v4.util.LongSparseArray:1 +//android.support.v4.util.LruCache:2 +//android.support.v4.util.MapCollections:2 +//android.support.v4.util.MapCollections.ArrayIterator:1 +//android.support.v4.util.Pair:2 +//android.support.v4.util.Pools.Pool:1 +//android.support.v4.util.Pools.SimplePool:1 +//android.support.v4.util.Pools.SynchronizedPool:1 +//android.support.v4.util.SimpleArrayMap:2 +//android.support.v4.util.SparseArrayCompat:1 +//android.support.v4.widget.FocusStrategy.BoundsAdapter:1 +//android.support.v4.widget.FocusStrategy.CollectionAdapter:2 +//android.support.v4.widget.FocusStrategy.SequentialComparator:1 +//android.support.v7.util.AsyncListUtil:1 +//android.support.v7.util.AsyncListUtil.DataCallback:1 +//android.support.v7.util.MessageThreadUtil:1 +//android.support.v7.util.SortedList:1 +//android.support.v7.util.SortedList.BatchedCallback:1 +//android.support.v7.util.SortedList.Callback:1 +//android.support.v7.util.ThreadUtil:1 +//android.support.v7.util.ThreadUtil.BackgroundCallback:1 +//android.support.v7.util.ThreadUtil.MainThreadCallback:1 +//android.support.v7.util.TileList:1 +//android.support.v7.util.TileList.Tile:1 +//android.support.v7.view.menu.BaseMenuWrapper:1 +//android.support.v7.view.menu.BaseWrapper:1 +//android.support.v7.widget.PositionMap:1 +//android.support.v7.widget.RecyclerView.Adapter:1 +//android.support.v7.widget.util.SortedListAdapterCallback:1 +//com.telerik.android.common.CollectionChangeListener:1 +//com.telerik.android.common.CollectionChangedEvent:1 +//com.telerik.android.common.DataTuple:3 +//com.telerik.android.common.Function:2 +//com.telerik.android.common.Function2:3 +//com.telerik.android.common.Function2Async:3 +//com.telerik.android.common.ObservableCollection:1 +//com.telerik.android.common.Procedure:1 +//com.telerik.android.common.Procedure2:2 \ No newline at end of file