├── gen-gradle.win.bat ├── .vscode ├── .BROWSE.VC.DB └── .BROWSE.VC.DB-wal ├── _helpers.d.ts ├── libs └── androidx │ └── 1.0.0 │ ├── activity.aar.jar │ ├── loader-1.0.0.jar │ ├── media-1.0.0.jar │ ├── print-1.0.0.jar │ ├── cardview-1.0.0.jar │ ├── viewpager-1.0.0.jar │ ├── annotation-1.0.0.jar │ ├── core-common-2.0.0.jar │ ├── customview-1.0.0.jar │ ├── transition-1.0.0.jar │ ├── core-1.1.0-alpha01.jar │ ├── core-runtime-2.0.0.jar │ ├── cursoradapter-1.0.0.jar │ ├── documentfile-1.0.0.jar │ ├── drawerlayout-1.0.0.jar │ ├── interpolator-1.0.0.jar │ ├── recyclerview-1.0.0.jar │ ├── vectordrawable-1.0.1.jar │ ├── appcompat-11.0-alpha01.jar │ ├── fragment-1.1.0-alpha02.jar │ ├── lifecycle-common-2.0.0.jar │ ├── material-1.1.0-alpha01.jar │ ├── asynclayoutinflater-1.0.0.jar │ ├── collection-1.1.0-alpha01.jar │ ├── coordinatorlayout-1.0.0.jar │ ├── legacy-support-v4-1.0.0.jar │ ├── lifecycle-livedata-2.0.0.jar │ ├── lifecycle-runtime-2.0.0.jar │ ├── lifecycle-viewmodel-2.0.0.jar │ ├── slidingpanelayout-1.0.0.jar │ ├── swiperefreshlayout-1.0.0.jar │ ├── localbroadcastmanager-1.0.0.jar │ ├── legacy-support-core-ui-1.0.0.jar │ ├── lifecycle-livedata-core-2.0.0.jar │ ├── vectordrawable-animated-1.0.0.jar │ ├── legacy-support-core-utils-1.0.0.jar │ └── versionedparcelable-1.1.0-alpha01.jar ├── dts-generator ├── .gitignore ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── telerik │ │ │ ├── dts │ │ │ ├── Tuple.java │ │ │ ├── ClassMapProvider.java │ │ │ ├── StringBuilder2.java │ │ │ ├── ClassDirectrory.java │ │ │ ├── JarFile.java │ │ │ ├── FileHelper.java │ │ │ ├── ClassRepo.java │ │ │ ├── TypeDefinition.java │ │ │ ├── Generator.java │ │ │ └── DtsApi.java │ │ │ ├── InputParameters.java │ │ │ └── Main.java │ │ └── resources │ │ └── generics.txt ├── gradlew.bat ├── build.gradle └── gradlew ├── test └── expected-output │ └── android-declarations.d.ts ├── .gitignore ├── Makefile └── README.md /gen-gradle.win.bat: -------------------------------------------------------------------------------- 1 | cd dts-generator 2 | 3 | gradlew jar -------------------------------------------------------------------------------- /.vscode/.BROWSE.VC.DB: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/.vscode/.BROWSE.VC.DB -------------------------------------------------------------------------------- /_helpers.d.ts: -------------------------------------------------------------------------------- 1 | declare module androidNative { 2 | export class Array { 3 | public constructor(); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/.BROWSE.VC.DB-wal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/.vscode/.BROWSE.VC.DB-wal -------------------------------------------------------------------------------- /libs/androidx/1.0.0/activity.aar.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/activity.aar.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/loader-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/loader-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/media-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/media-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/print-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/print-1.0.0.jar -------------------------------------------------------------------------------- /dts-generator/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | local.properties 3 | .idea/gradle.xml 4 | .idea/modules.xml 5 | /out 6 | *.jar 7 | /**/*.d.ts 8 | /jars/**/*.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/cardview-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/cardview-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/viewpager-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/viewpager-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/annotation-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/annotation-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/core-common-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/core-common-2.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/customview-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/customview-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/transition-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/transition-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/core-1.1.0-alpha01.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/core-1.1.0-alpha01.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/core-runtime-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/core-runtime-2.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/cursoradapter-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/cursoradapter-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/documentfile-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/documentfile-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/drawerlayout-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/drawerlayout-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/interpolator-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/interpolator-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/recyclerview-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/recyclerview-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/vectordrawable-1.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/vectordrawable-1.0.1.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/appcompat-11.0-alpha01.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/appcompat-11.0-alpha01.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/fragment-1.1.0-alpha02.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/fragment-1.1.0-alpha02.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/lifecycle-common-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/lifecycle-common-2.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/material-1.1.0-alpha01.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/material-1.1.0-alpha01.jar -------------------------------------------------------------------------------- /dts-generator/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/dts-generator/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/asynclayoutinflater-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/asynclayoutinflater-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/collection-1.1.0-alpha01.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/collection-1.1.0-alpha01.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/coordinatorlayout-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/coordinatorlayout-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/legacy-support-v4-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/legacy-support-v4-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/lifecycle-livedata-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/lifecycle-livedata-2.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/lifecycle-runtime-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/lifecycle-runtime-2.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/lifecycle-viewmodel-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/lifecycle-viewmodel-2.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/slidingpanelayout-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/slidingpanelayout-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/swiperefreshlayout-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/swiperefreshlayout-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/localbroadcastmanager-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/localbroadcastmanager-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/legacy-support-core-ui-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/legacy-support-core-ui-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/lifecycle-livedata-core-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/lifecycle-livedata-core-2.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/vectordrawable-animated-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/vectordrawable-animated-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/legacy-support-core-utils-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/legacy-support-core-utils-1.0.0.jar -------------------------------------------------------------------------------- /libs/androidx/1.0.0/versionedparcelable-1.1.0-alpha01.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/android-dts-generator/HEAD/libs/androidx/1.0.0/versionedparcelable-1.1.0-alpha01.jar -------------------------------------------------------------------------------- /test/expected-output/android-declarations.d.ts: -------------------------------------------------------------------------------- 1 | declare module androidNative { export class Array { constructor(); length: number; [index: number]: T; } } 2 | 3 | import globalAndroid = android; 4 | 5 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/Tuple.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | public class Tuple { 4 | public final X x; 5 | public final Y y; 6 | public Tuple(X x, Y y) { 7 | this.x = x; 8 | this.y = y; 9 | } 10 | } -------------------------------------------------------------------------------- /dts-generator/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/ClassMapProvider.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | import org.apache.bcel.classfile.JavaClass; 4 | 5 | import java.util.Map; 6 | 7 | public interface ClassMapProvider { 8 | Map getClassMap(); 9 | 10 | String getPath(); 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /dts-generator/.idea/workspace.xml 5 | /dts-generator/.idea/misc.xml 6 | /dts-generator/.idea/libraries 7 | /.idea/workspace.xml 8 | /.idea/libraries 9 | .DS_Store 10 | /build 11 | /captures 12 | .idea 13 | .classpath 14 | .settings 15 | .project 16 | 17 | /out 18 | /output 19 | /newApp 20 | /dts_gen_app 21 | testparams.txt 22 | /jars 23 | bin/ 24 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/StringBuilder2.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | public class StringBuilder2 { 4 | private final StringBuilder sb; 5 | 6 | private final String ENDL = "\n"; 7 | 8 | public StringBuilder2() 9 | { 10 | sb = new StringBuilder(); 11 | } 12 | 13 | public StringBuilder2 append(String s) 14 | { 15 | sb.append(s); 16 | return this; 17 | } 18 | 19 | public StringBuilder2 appendln(String s) 20 | { 21 | append(s); 22 | sb.append(ENDL); 23 | return this; 24 | } 25 | 26 | public String toString() 27 | { 28 | return sb.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/ClassDirectrory.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | import org.apache.bcel.classfile.ClassParser; 4 | import org.apache.bcel.classfile.JavaClass; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class ClassDirectrory implements ClassMapProvider { 14 | private final String path; 15 | private final Map classMap; 16 | private static final String CLASS_EXT = ".class"; 17 | 18 | private ClassDirectrory(String path) { 19 | this.path = path; 20 | this.classMap = new HashMap(); 21 | } 22 | 23 | public Map getClassMap() { 24 | return classMap; 25 | } 26 | 27 | public String getPath() { 28 | return path; 29 | } 30 | 31 | public static ClassDirectrory readDirectory(String path) throws IOException { 32 | ClassDirectrory dir = new ClassDirectrory(path); 33 | readDirectory(dir, path); 34 | return dir; 35 | } 36 | 37 | public static void readDirectory(ClassDirectrory dir, String path) throws IOException { 38 | List subDirs = new ArrayList(); 39 | File currentDir = new File(path); 40 | for (File file : currentDir.listFiles()) { 41 | if (file.isFile()) { 42 | String name = file.getName(); 43 | if (name.endsWith(CLASS_EXT)) { 44 | ClassParser cp = new ClassParser(file.getAbsolutePath()); 45 | JavaClass clazz = cp.parse(); 46 | dir.classMap.put(clazz.getClassName(), clazz); 47 | } 48 | } else if (file.isDirectory()) { 49 | subDirs.add(file); 50 | } 51 | } 52 | for (File sd: subDirs) { 53 | readDirectory(dir, sd.getAbsolutePath()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/JarFile.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.jar.JarInputStream; 9 | import java.util.zip.ZipEntry; 10 | 11 | import org.apache.bcel.classfile.ClassFormatException; 12 | import org.apache.bcel.classfile.ClassParser; 13 | import org.apache.bcel.classfile.JavaClass; 14 | 15 | public class JarFile implements ClassMapProvider { 16 | private final String path; 17 | private final Map classMap; 18 | private static final String CLASS_EXT = ".class"; 19 | 20 | private JarFile(String path) { 21 | this.path = path; 22 | this.classMap = new HashMap(); 23 | } 24 | 25 | public String getPath() { 26 | return path; 27 | } 28 | 29 | public Map getClassMap() { 30 | return classMap; 31 | } 32 | 33 | public static JarFile readJarInputStream(String path, InputStream stream) throws ClassFormatException,IOException { 34 | JarFile jar; 35 | JarInputStream jis = null; 36 | try { 37 | jis = new JarInputStream(stream); 38 | 39 | jar = new JarFile(path); 40 | 41 | for (ZipEntry ze = jis.getNextEntry(); ze != null; ze = jis.getNextEntry()) { 42 | String name = ze.getName(); 43 | if (name.endsWith(CLASS_EXT)) { 44 | name = name.substring(0, name.length() - CLASS_EXT.length()).replace('/', '.'); 45 | ClassParser cp = new ClassParser(jis, name); 46 | JavaClass clazz = cp.parse(); 47 | jar.classMap.put(name, clazz); 48 | } 49 | } 50 | } finally { 51 | if (jis != null) { 52 | jis.close(); 53 | } 54 | } 55 | return jar; 56 | } 57 | public static JarFile readJar(String path) throws ClassFormatException,IOException { 58 | return readJarInputStream(path, new FileInputStream(path)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/InputParameters.java: -------------------------------------------------------------------------------- 1 | package com.telerik; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * Created by plamen5kov on 6/23/16. 9 | */ 10 | public class InputParameters { 11 | 12 | private File outputDir; 13 | private List inputJars; 14 | private List superJars; 15 | private File inputGenerics; 16 | private boolean allGenericImplements; 17 | private boolean skipDeclarations; 18 | private boolean classMode; 19 | private int ignoreObfuscatedNameLength; 20 | 21 | public InputParameters() { 22 | this.outputDir = new File("out"); 23 | this.inputJars = new ArrayList<>(); 24 | this.superJars = new ArrayList<>(); 25 | this.allGenericImplements = false; 26 | this.skipDeclarations = false; 27 | this.classMode = false; 28 | this.ignoreObfuscatedNameLength = 0; 29 | } 30 | 31 | public File getOutputDir() { 32 | return outputDir; 33 | } 34 | 35 | public void setOutputDir(File outputDir) { 36 | this.outputDir = outputDir; 37 | } 38 | 39 | public List getInputJars() { 40 | return inputJars; 41 | } 42 | 43 | public List getSuperJars() { 44 | return superJars; 45 | } 46 | 47 | public File getInputGenerics() { 48 | return inputGenerics; 49 | } 50 | 51 | public void setInputGenerics(File inputGenerics) { 52 | this.inputGenerics = inputGenerics; 53 | } 54 | 55 | public boolean isAllGenericImplementsEnabled() { 56 | return allGenericImplements; 57 | } 58 | 59 | public void setSkipDeclarations(boolean skipDeclarations) { this.skipDeclarations = skipDeclarations; } 60 | 61 | public boolean getSkipDeclarations() { return skipDeclarations; } 62 | 63 | public void setAllGenericImplements(boolean allGenericImplements) { this.allGenericImplements = allGenericImplements; } 64 | 65 | public boolean getClassMode() { return this.classMode; } 66 | 67 | public void setClassMode(boolean classMode) { this.classMode = classMode; } 68 | 69 | public int getIgnoreObfuscatedNameLength() { return this.ignoreObfuscatedNameLength; } 70 | 71 | public void setIgnoreObfuscatedNameLength(int nameLength) { this.ignoreObfuscatedNameLength = nameLength; } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/FileHelper.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.PrintStream; 8 | import java.nio.file.Files; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by plamen5kov on 6/17/16. 13 | */ 14 | public class FileHelper { 15 | public static final String DEFAULT_DTS_FILE_NAME = "android.d.ts"; 16 | public static final String DEFAULT_DECLARATIONS_FILE_NAME = "android-declarations.d.ts"; 17 | 18 | private String defaultDtsFileName; 19 | private PrintStream ps; 20 | private File outDir; 21 | 22 | public FileHelper(File outDir) { 23 | this.defaultDtsFileName = DEFAULT_DTS_FILE_NAME; // + java.util.UUID.randomUUID().toString(); 24 | this.outDir = outDir; 25 | } 26 | 27 | public void writeToFile(String content, String fileName, Boolean append) { 28 | try { 29 | String outFile = this.outDir.getAbsolutePath() + File.separator + fileName; 30 | this.ps = new PrintStream(new FileOutputStream(outFile, /*append*/append)); 31 | 32 | this.ps.print(content); 33 | this.ps.println(); 34 | } catch (FileNotFoundException e) { 35 | e.printStackTrace(); 36 | } 37 | 38 | finally { 39 | if(this.ps != null) { 40 | this.ps.close(); 41 | } 42 | } 43 | } 44 | 45 | public String readFileContent(String fileName) { 46 | try { 47 | List lines = Files.readAllLines(new File(this.outDir.getAbsolutePath(), fileName).toPath()); 48 | return String.join("\n", lines.toArray(new String[lines.size()])); 49 | } catch (IOException ex) { 50 | return null; 51 | } 52 | } 53 | 54 | public void writeHelperTypings(String content, String fileName, boolean append) { 55 | try { 56 | String outFile = this.outDir.getAbsolutePath() + File.separator + fileName; 57 | this.ps = new PrintStream(new FileOutputStream(outFile, append)); 58 | 59 | this.ps.print(content); 60 | this.ps.println(); 61 | } catch (FileNotFoundException e) { 62 | e.printStackTrace(); 63 | } 64 | finally { 65 | if(this.ps != null) { 66 | this.ps.close(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /dts-generator/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/ClassRepo.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | import org.apache.bcel.classfile.JavaClass; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.Comparator; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | 12 | public class ClassRepo { 13 | private ClassRepo() { 14 | } 15 | 16 | private static ArrayList cachedProviders = new ArrayList<>(); 17 | private static ArrayList cachedSuperProviders = new ArrayList<>(); 18 | private static HashSet classesSet = new HashSet<>(); 19 | private static List sortedClasses; 20 | private static int traversedFilesIdx = 0; 21 | 22 | public static void cacheJarFile(ClassMapProvider classMapProvider) { 23 | for (String className : classMapProvider.getClassMap().keySet()) { 24 | for (ClassMapProvider cachedProvider : cachedProviders) { 25 | JavaClass clazz = cachedProvider.getClassMap().get(className); 26 | } 27 | } 28 | 29 | cachedProviders.add(classMapProvider); 30 | } 31 | 32 | public static void cacheSuperJarFile(ClassMapProvider classMapProvider) { 33 | for (String className : classMapProvider.getClassMap().keySet()) { 34 | for (ClassMapProvider cachedProvider : cachedProviders) { 35 | JavaClass clazz = cachedProvider.getClassMap().get(className); 36 | } 37 | } 38 | 39 | cachedSuperProviders.add(classMapProvider); 40 | } 41 | 42 | public static void sortCachedProviders() { 43 | for(ClassMapProvider cmp : cachedProviders) { 44 | classesSet.addAll(cmp.getClassMap().keySet()); 45 | } 46 | 47 | sortedClasses = new ArrayList<>(classesSet); 48 | 49 | Collections.sort(sortedClasses, (className1, className2) -> { 50 | if(className1.startsWith(className2)) { 51 | return 1; 52 | } 53 | if(className2.startsWith(className1)) { 54 | return -1; 55 | } 56 | return className1.compareTo(className2); 57 | }); 58 | } 59 | 60 | public static JavaClass findClass(String className) { 61 | JavaClass clazz = null; 62 | for (ClassMapProvider classMapProvider : cachedProviders) { 63 | clazz = classMapProvider.getClassMap().get(className); 64 | if (clazz != null) { 65 | break; 66 | } 67 | } 68 | if(clazz == null) { 69 | return findSuperClass(className); 70 | } 71 | return clazz; 72 | } 73 | 74 | public static JavaClass findSuperClass(String className) { 75 | JavaClass clazz = null; 76 | for (ClassMapProvider classMapProvider : cachedSuperProviders) { 77 | clazz = classMapProvider.getClassMap().get(className); 78 | if (clazz != null) { 79 | break; 80 | } 81 | } 82 | return clazz; 83 | } 84 | 85 | public static List getNextClassGroup() { 86 | ArrayList lst = new ArrayList(); 87 | boolean first = true; 88 | 89 | while ((traversedFilesIdx < sortedClasses.size()) && keepGoing(first)) { 90 | JavaClass f = getCurrentJavaClass(); 91 | String name = f.getClassName(); 92 | if (!name.contains("$0") && !name.contains("$1") 93 | && !name.contains("$2") && !name.contains("$3") 94 | && !name.contains("$4") && !name.contains("$5") 95 | && !name.contains("$6") && !name.contains("$7") 96 | && !name.contains("$8") && !name.contains("$9") 97 | && !name.contains("$string")) { 98 | lst.add(f); 99 | } 100 | first = false; 101 | ++traversedFilesIdx; 102 | } 103 | 104 | return lst; 105 | } 106 | 107 | public static boolean hasNext() { 108 | return traversedFilesIdx < sortedClasses.size(); 109 | } 110 | 111 | private static JavaClass getCurrentJavaClass() { 112 | String currentClassName = sortedClasses.get(traversedFilesIdx); 113 | return ClassRepo.findClass(currentClassName); 114 | } 115 | 116 | private static boolean keepGoing(boolean first) { 117 | JavaClass file = getCurrentJavaClass(); 118 | boolean isNestedClass = file.getClassName().contains("$"); 119 | boolean res = first ? !isNestedClass : isNestedClass; 120 | return res; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/TypeDefinition.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | import org.apache.bcel.generic.ObjectType; 4 | import org.apache.bcel.generic.ReferenceType; 5 | import org.apache.bcel.generic.Type; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | import edu.umd.cs.findbugs.ba.generic.GenericObjectType; 13 | import edu.umd.cs.findbugs.ba.generic.GenericUtilities; 14 | 15 | public class TypeDefinition { 16 | private static Pattern typeSignature = Pattern.compile("^(<(?.*)>)?(?L.*)$"); 17 | //private static Pattern extendsGenericType = Pattern.compile("(?\\w+)\\:+(?L.*?(?=;\\w+|;$))"); 18 | private static Pattern extendsGenericType = Pattern.compile("(?\\w+)\\:+(?L.*?(?=(?=;\\w+|;$)|(?=<.*>)))"); 19 | 20 | private String signature; 21 | private String className; 22 | private ReferenceType parent; 23 | private List genericDefinitions = null; 24 | private List interfaces = null; 25 | 26 | public class GenericDefinition { 27 | private String label; 28 | private Type type; 29 | 30 | GenericDefinition(String label, Type type) { 31 | this.label = label; 32 | this.type = type; 33 | } 34 | 35 | public String getLabel() { 36 | return label; 37 | } 38 | 39 | public Type getType() { 40 | return type; 41 | } 42 | } 43 | 44 | public TypeDefinition(String signature, String className) { 45 | this.signature = signature; 46 | this.className = className; 47 | this.checkSignature(); 48 | } 49 | 50 | public ReferenceType getParent() { 51 | return parent; 52 | } 53 | 54 | public List getInterfaces() { 55 | return interfaces; 56 | } 57 | 58 | public List getGenericDefinitions() { 59 | return genericDefinitions; 60 | } 61 | 62 | public String getClassName() { return this.className; } 63 | 64 | private void checkSignature() { 65 | Matcher matcher = typeSignature.matcher(this.signature); 66 | if(matcher.matches()){ 67 | String typeGenericsSignature = matcher.group(2); 68 | String interfacesSignature = matcher.group(3); 69 | if (typeGenericsSignature != null) { 70 | this.setTypeGenerics(typeGenericsSignature); 71 | } 72 | if (interfacesSignature != null) { 73 | this.interfaces = GenericUtilities.getTypeParameters(interfacesSignature); 74 | if(this.interfaces.size() > 0){ 75 | // we assume that the first type parameter is the base class and the others are interfaces 76 | parent = this.interfaces.get(0); 77 | if ((parent instanceof ObjectType) && 78 | (((ObjectType)parent).getClassName().equals("java.lang.Enum") 79 | || ((ObjectType)parent).getClassName().equals(DtsApi.JavaLangObject))){ 80 | if(((ObjectType)parent).getClassName().equals("java.lang.Enum")) { 81 | parent = null; // we don't need extends for enum 82 | } 83 | // if we have only one another interface set it as a parent 84 | // otherwise we don't know which one to set as a parent 85 | if(this.interfaces.size() == 2) { 86 | parent = this.interfaces.get(1); 87 | this.interfaces.remove(1); 88 | } 89 | } 90 | this.interfaces.remove(0); 91 | } 92 | } 93 | } 94 | } 95 | 96 | private void setTypeGenerics(String typeGenericsSignature) { 97 | Matcher matcher = extendsGenericType.matcher(typeGenericsSignature); 98 | this.genericDefinitions = new ArrayList<>(); 99 | while (matcher.find()) { 100 | String label = matcher.group(1); 101 | Type type = GenericUtilities.getType(matcher.group(2) + ";"); 102 | this.genericDefinitions.add(new GenericDefinition(label, type)); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /dts-generator/build.gradle: -------------------------------------------------------------------------------- 1 | import java.nio.file.Paths 2 | import java.util.jar.JarEntry 3 | import java.util.jar.JarFile 4 | 5 | apply plugin: 'java-library' 6 | 7 | java { 8 | sourceCompatibility = JavaVersion.VERSION_17 9 | targetCompatibility = JavaVersion.VERSION_17 10 | } 11 | 12 | def version = "4.0.0" 13 | 14 | def hasProject = file("$project.rootDir/gradle.properties").exists(); 15 | 16 | repositories { 17 | google() 18 | mavenCentral() 19 | maven { 20 | url = 'https://maven.google.com/' 21 | name = 'Google' 22 | } 23 | maven { url = "https://repo1.maven.org/maven2/" } 24 | maven { url = "https://repo.maven.apache.org/maven2/" } 25 | } 26 | 27 | if (!project.hasProperty("loadedProjectDeps") && hasProject) { 28 | Properties projectDeps = new Properties() 29 | projectDeps.load(new FileInputStream("$project.rootDir/gradle.properties")) 30 | projectDeps.each { prop -> 31 | project.ext.set(prop.key, prop.value) 32 | } 33 | project.ext.loadedProjectDeps = true 34 | } 35 | 36 | if(!hasProject){ 37 | project.ext.set("ns_default_bcel_version", "6.10.0"); 38 | project.ext.set("ns_default_commons_io_version", "2.6"); 39 | project.ext.set("ns_default_spotbugs_version", "3.1.12"); 40 | } 41 | 42 | 43 | 44 | // todo: check if still needed 45 | // if(!project.hasProperty("loadedProjectDeps")){ 46 | // Properties projectDeps = new Properties() 47 | // projectDeps.load(new FileInputStream("$project.rootDir/gradle.properties")) 48 | // projectDeps.each { prop -> 49 | // project.ext.set(prop.key, prop.value) 50 | // } 51 | // project.ext.loadedProjectDeps = true 52 | // } 53 | 54 | project.ext.extractedDependenciesDir = "jar-files" 55 | if (project.hasProperty("jarsOutput")) { 56 | project.ext.extractedDependenciesDir = project.ext.jarsOutput 57 | } 58 | 59 | 60 | allprojects { 61 | gradle.projectsEvaluated { 62 | tasks.withType(JavaCompile).tap { 63 | configureEach { 64 | options.compilerArgs << "-Xlint:all" << "-Werror" 65 | } 66 | } 67 | } 68 | } 69 | 70 | dependencies { 71 | // println "\t ~ [DEBUG][dts-generator] build.gradle - ns_default_bcel_version = ${ns_default_bcel_version}..." 72 | 73 | implementation "org.apache.bcel:bcel:${ns_default_bcel_version}" 74 | implementation "commons-io:commons-io:${ns_default_commons_io_version}" 75 | implementation "com.github.spotbugs:spotbugs:${ns_default_spotbugs_version}" 76 | 77 | // add your dependency here as the example bellow, make sure you are using testCompileOnly 78 | // testCompileOnly "com.android.support:support-v4:27.0.1" 79 | 80 | //AndroidX 81 | // testCompileOnly "androidx.legacy:legacy-support-v4:1.0.0" 82 | // testCompileOnly "androidx.appcompat:appcompat:1.0.0" 83 | // testCompileOnly "com.google.android.material:material:1.0.0" 84 | 85 | // def androidXLegacyVersion = "1.0.0" 86 | // def androidXAppCompatVersion = "1.6.1" 87 | // def androidXMaterialVersion = "1.8.0" 88 | // def androidXExifInterfaceVersion = "1.3.7" 89 | // def androidXViewPagerVersion = "1.0.0" 90 | // def androidXFragmentVersion = "1.4.1" 91 | // def androidXTransitionVersion = "1.4.1" 92 | // 93 | // testCompileOnly "androidx.legacy:legacy-support-v4:$androidXLegacyVersion" 94 | // testCompileOnly "androidx.appcompat:appcompat:$androidXAppCompatVersion" 95 | // testCompileOnly "com.google.android.material:material:$androidXMaterialVersion" 96 | // testCompileOnly "androidx.exifinterface:exifinterface:$androidXExifInterfaceVersion" 97 | // testCompileOnly "androidx.viewpager2:viewpager2:$androidXViewPagerVersion" 98 | // testCompileOnly "androidx.fragment:fragment:$androidXFragmentVersion" 99 | // testCompileOnly "androidx.transition:transition:$androidXTransitionVersion" 100 | } 101 | 102 | configurations.create("dtsGeneratorImplementation") { 103 | extendsFrom configurations.implementation 104 | setCanBeResolved(true) 105 | } 106 | 107 | configurations.create("dtsGeneratorTestCompileOnly") { 108 | extendsFrom configurations.testCompileOnly 109 | setCanBeResolved(true) 110 | } 111 | 112 | jar { 113 | //pack jar dependencies into the final jar 114 | from { 115 | configurations.dtsGeneratorImplementation.collect { it.isDirectory() ? it : zipTree(it) } 116 | } 117 | from('src/main/resources') { 118 | include 'generics.txt' 119 | } 120 | //set main class for the jar 121 | manifest { 122 | attributes 'Main-Class': 'com.telerik.Main' 123 | attributes 'Specification-Version': version 124 | attributes 'Manifest-Version': version 125 | } 126 | 127 | duplicatesStrategy = 'include' 128 | } 129 | 130 | task extractAllJars { 131 | 132 | outputs.dir extractedDependenciesDir 133 | 134 | doLast { 135 | def iter = configurations.dtsGeneratorTestCompileOnly.resolvedConfiguration.resolvedArtifacts.iterator() 136 | def dependencyCounter = 0 137 | while (iter.hasNext()) { 138 | //declaring variable as specific class for getting code completion in Android Studio 139 | def nextDependency = iter.next() 140 | 141 | def outputDir = Paths.get(extractedDependenciesDir, nextDependency.toString()).normalize().toString().replace(':', '_') 142 | explodeAar(nextDependency.file, outputDir) 143 | dependencyCounter++ 144 | } 145 | } 146 | } 147 | 148 | def explodeAar(File compileDependency, String outputDir) { 149 | if (compileDependency.name.endsWith(".aar")) { 150 | JarFile jar = new JarFile(compileDependency) 151 | Enumeration enumEntries = jar.entries() 152 | while (enumEntries.hasMoreElements()) { 153 | JarEntry file = (JarEntry) enumEntries.nextElement() 154 | if (file.name.endsWith(".jar")) { 155 | def f = new File(outputDir, file.name) 156 | new File(f.parent).mkdirs() 157 | InputStream is = jar.getInputStream(file) 158 | FileOutputStream fos = new FileOutputStream(f) 159 | while (is.available() > 0) { 160 | fos.write(is.read()) 161 | } 162 | fos.close() 163 | is.close() 164 | } 165 | if (file.isDirectory()) { 166 | continue 167 | } 168 | } 169 | jar.close() 170 | } else if (compileDependency.name.endsWith(".jar")) { 171 | copy { 172 | from compileDependency.absolutePath 173 | into outputDir 174 | } 175 | } 176 | } 177 | 178 | // def buildMetadata = tasks.findByPath(":app:buildMetadata") 179 | 180 | // if (buildMetadata != null) { 181 | // processResources.dependsOn(buildMetadata) 182 | // compileJava.dependsOn(buildMetadata) 183 | // } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | android-platform-17: 2 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-17/android.jar 3 | mv out/android.d.ts out/android-platform-17.d.ts 4 | 5 | android-platform-18: 6 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-18/android.jar 7 | mv out/android.d.ts out/android-platform-18.d.ts 8 | 9 | android-platform-19: 10 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-19/android.jar 11 | mv out/android.d.ts out/android-platform-19.d.ts 12 | 13 | android-platform-20: 14 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-20/android.jar 15 | mv out/android.d.ts out/android-platform-20.d.ts 16 | 17 | android-platform-21: 18 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-21/android.jar 19 | mv out/android.d.ts out/android-platform-21.d.ts 20 | 21 | android-platform-22: 22 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-22/android.jar 23 | mv out/android.d.ts out/android-platform-22.d.ts 24 | 25 | android-platform-23: 26 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-23/android.jar 27 | mv out/android.d.ts out/android-platform-23.d.ts 28 | 29 | android-platform-24: 30 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-24/android.jar 31 | mv out/android.d.ts out/android-platform-24.d.ts 32 | 33 | android-platform-25: 34 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-25/android.jar 35 | mv out/android.d.ts out/android-platform-25.d.ts 36 | 37 | android-platform-26: 38 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-26/android.jar 39 | mv out/android.d.ts out/android-platform-26.d.ts 40 | 41 | android-platform-27: 42 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-27/android.jar 43 | mv out/android.d.ts out/android-platform-27.d.ts 44 | 45 | android-platform-28: 46 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-28/android.jar 47 | mv out/android.d.ts out/android-platform-28.d.ts 48 | 49 | android-platform-29: 50 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-29/android.jar 51 | mv out/android.d.ts out/android-platform-29.d.ts 52 | 53 | android-platform-30: 54 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-30/android.jar 55 | mv out/android.d.ts out/android-platform-30.d.ts 56 | 57 | android-platform-31: 58 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-31/android.jar 59 | mv out/android.d.ts out/android-platform-31.d.ts 60 | 61 | android-platform-32: 62 | java -jar dts-generator/build/libs/dts-generator.jar -input ${ANDROID_HOME}/platforms/android-32/android.jar 63 | mv out/android.d.ts out/android-platform-32.d.ts 64 | 65 | android-platform-all: android-platform-17 android-platform-18 android-platform-19 android-platform-20 android-platform-21 \ 66 | android-platform-22 android-platform-23 android-platform-24 android-platform-25 android-platform-26 android-platform-27 \ 67 | android-platform-28 android-platform-29 android-platform-30 android-platform-31 android-platform-32 68 | 69 | androidx-17: 70 | java -jar dts-generator/build/libs/dts-generator.jar \ 71 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 72 | -super ${ANDROID_HOME}/platforms/android-17/android.jar -skip-declarations 73 | mv out/android.d.ts out/androidx-17.d.ts 74 | 75 | androidx-23: 76 | java -jar dts-generator/build/libs/dts-generator.jar \ 77 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 78 | -super ${ANDROID_HOME}/platforms/android-23/android.jar -skip-declarations 79 | mv out/android.d.ts out/androidx-23.d.ts 80 | 81 | androidx-26: 82 | java -jar dts-generator/build/libs/dts-generator.jar \ 83 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 84 | -super ${ANDROID_HOME}/platforms/android-26/android.jar -skip-declarations 85 | mv out/android.d.ts out/androidx-26.d.ts 86 | 87 | androidx-28: 88 | java -jar dts-generator/build/libs/dts-generator.jar \ 89 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 90 | -super ${ANDROID_HOME}/platforms/android-28/android.jar -skip-declarations 91 | mv out/android.d.ts out/androidx-28.d.ts 92 | 93 | androidx-29: 94 | java -jar dts-generator/build/libs/dts-generator.jar \ 95 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 96 | -super ${ANDROID_HOME}/platforms/android-29/android.jar -skip-declarations 97 | mv out/android.d.ts out/androidx-29.d.ts 98 | 99 | androidx-30: 100 | java -jar dts-generator/build/libs/dts-generator.jar \ 101 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 102 | -super ${ANDROID_HOME}/platforms/android-30/android.jar -skip-declarations 103 | mv out/android.d.ts out/androidx-30.d.ts 104 | 105 | androidx-31: 106 | java -jar dts-generator/build/libs/dts-generator.jar \ 107 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 108 | -super ${ANDROID_HOME}/platforms/android-31/android.jar -skip-declarations 109 | mv out/android.d.ts out/androidx-31.d.ts 110 | 111 | androidx-32: 112 | java -jar dts-generator/build/libs/dts-generator.jar \ 113 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 114 | -super ${ANDROID_HOME}/platforms/android-32/android.jar -skip-declarations 115 | mv out/android.d.ts out/androidx-32.d.ts 116 | 117 | androidx-33: 118 | java -jar dts-generator/build/libs/dts-generator.jar \ 119 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 120 | -super ${ANDROID_HOME}/platforms/android-33/android.jar -skip-declarations 121 | mv out/android.d.ts out/androidx-33.d.ts 122 | 123 | androidx-34: 124 | java -jar dts-generator/build/libs/dts-generator.jar \ 125 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 126 | -super ${ANDROID_HOME}/platforms/android-34/android.jar -skip-declarations 127 | mv out/android.d.ts out/androidx-34.d.ts 128 | 129 | androidx-35: 130 | java -jar dts-generator/build/libs/dts-generator.jar \ 131 | -input dts-generator/jar-files/ -input-generics libs/generics.txt \ 132 | -super ${ANDROID_HOME}/platforms/android-35/android.jar -skip-declarations 133 | mv out/android.d.ts out/androidx-35.d.ts 134 | 135 | test-compare-output: 136 | java -jar dts-generator/build/libs/dts-generator.jar \ 137 | -input libs 138 | cmp out/android.d.ts test/expected-output/android.d.ts 139 | cmp out/android-declarations.d.ts test/expected-output/android-declarations.d.ts 140 | 141 | androidx-all: androidx-17 androidx-23 androidx-26 androidx-28 androidx-29 androidx-30 androidx-31 androidx-32 androidx-33 androidx-34 androidx-35 142 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/Main.java: -------------------------------------------------------------------------------- 1 | package com.telerik; 2 | 3 | import com.telerik.dts.Generator; 4 | import com.telerik.dts.StringBuilder2; 5 | 6 | import java.io.File; 7 | import java.security.InvalidParameterException; 8 | 9 | public class Main { 10 | 11 | private static final String OUT_DIR = "-output"; 12 | private static final String INPUT_JARS = "-input"; 13 | private static final String SUPER_JARS = "-super"; 14 | private static final String CLASS_MODE = "-class-mode"; 15 | 16 | // provide a file with rows in the following format - com.telerik.android.data.SelectionService:1 17 | // to know how many generic types uses a given generic 18 | private static final String INPUT_GENERICS = "-input-generics"; 19 | 20 | // whether to generate implements for all interfaces implemented by the generic types 21 | private static final String ALL_GENERIC_IMPLEMENTS = "-all-generic-implements"; 22 | 23 | // whether to skip the declarations file generation 24 | private static final String SKIP_DECLARATIONS = "-skip-declarations"; 25 | 26 | // whether to ignore obfuscated classes/namespaces/methods 27 | // the parameter defines the length of obfuscated names to detect 28 | private static final String IGNORE_OBFUSCATED = "-ignore-obfuscated"; 29 | 30 | private static final String HELP = "-help"; 31 | 32 | public static void main(String[] args) { 33 | try { 34 | InputParameters inputParameters = parseCommand(args); 35 | 36 | long startTime = System.currentTimeMillis(); 37 | 38 | Package p = Main.class.getPackage(); 39 | System.out.println("Android d.ts Generator Version : " 40 | + p.getSpecificationVersion()); 41 | 42 | new Generator().start(inputParameters); 43 | 44 | long stopTime = System.currentTimeMillis(); 45 | long elapsedTime = stopTime - startTime; 46 | System.out.println("Generation of definitions took " + elapsedTime + "ms."); 47 | } catch (Throwable ex) { 48 | System.err.println(String.format("Error executing Typescript Definitions Generator: %s", ex.getMessage())); 49 | ex.printStackTrace(System.out); 50 | System.exit(1); 51 | } 52 | } 53 | 54 | public static InputParameters parseCommand(String[] args) throws Exception { 55 | InputParameters inputParameters = new InputParameters(); 56 | 57 | if (args != null) { 58 | for (int i = 0; i < args.length; i++) { 59 | String commandArg = args[i]; 60 | 61 | if (commandArg.equals(HELP)) { 62 | printHelpMessage(); 63 | } 64 | 65 | if (commandArg.equals(ALL_GENERIC_IMPLEMENTS)) { 66 | inputParameters.setAllGenericImplements(true); 67 | } 68 | 69 | if (commandArg.equals(SKIP_DECLARATIONS)) { 70 | inputParameters.setSkipDeclarations(true); 71 | } 72 | 73 | if (commandArg.equals(CLASS_MODE)) { 74 | inputParameters.setClassMode(true); 75 | } 76 | 77 | if (commandArg.equals(IGNORE_OBFUSCATED)) { 78 | if (i != (args.length - 1)) { 79 | String nextParam = args[i + 1]; 80 | inputParameters.setIgnoreObfuscatedNameLength(Integer.valueOf(nextParam)); 81 | } 82 | } 83 | 84 | if (commandArg.equals(OUT_DIR)) { 85 | if (i != (args.length - 1)) { 86 | String nextParam = args[i + 1]; 87 | validateOutputDir(inputParameters, nextParam); 88 | } 89 | } 90 | 91 | if (commandArg.equals(INPUT_GENERICS)) { 92 | if (i != (args.length - 1)) { 93 | String nextParam = args[i + 1]; 94 | validateInputGenerics(inputParameters, nextParam); 95 | } 96 | } 97 | 98 | if (commandArg.equals(INPUT_JARS)) { 99 | for (int jarIndex = i + 1; jarIndex < args.length; jarIndex++) { 100 | String currentArgument = args[jarIndex]; 101 | if (currentArgument.startsWith("-")) { 102 | break; 103 | } 104 | inputParameters.getInputJars().add(new File(currentArgument)); 105 | } 106 | if (inputParameters.getInputJars().size() <= 0) { 107 | throw new IllegalArgumentException("You need to pass input jars to: " + OUT_DIR + " flag"); 108 | } 109 | } 110 | 111 | if (commandArg.equals(SUPER_JARS)) { 112 | for (int jarIndex = i + 1; jarIndex < args.length; jarIndex++) { 113 | String currentArgument = args[jarIndex]; 114 | if (currentArgument.startsWith("-")) { 115 | break; 116 | } 117 | inputParameters.getSuperJars().add(new File(currentArgument)); 118 | } 119 | } 120 | } 121 | 122 | inputParameters.getOutputDir().mkdir(); 123 | } 124 | 125 | return inputParameters; 126 | } 127 | 128 | private static void printHelpMessage() { 129 | StringBuilder2 helpMessage = new StringBuilder2(); 130 | helpMessage.appendln("This is help for dts generator"); 131 | helpMessage.appendln("usage: java -jar dts-generator.jar []"); 132 | helpMessage.appendln("flags:\t\t"); 133 | helpMessage.appendln("\t(Optional)"); 134 | helpMessage.appendln("\t\t[-output ]:\t\tThe direcory the d.ts files will be generated in."); 135 | helpMessage.appendln("\t\t[-input]:\t\tThe input jars or class directories from which the d.ts files will be generated."); 136 | helpMessage.appendln("\t\t[-super]:\t\tProvide jar files from which to search for super classes."); 137 | helpMessage.appendln("\t\t[-input-generics]:\tProvide a file with information for number of generic types per given generic class name."); 138 | helpMessage.appendln("\t\t[-all-generic-implements]:\t\tAdd this flag to generate implements for all interfaces implemented by the generic types." + 139 | " 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"); 140 | 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."); 141 | helpMessage.appendln("\t\t[--class-mode]:\t\tPass this argument if you want folders to be processed as class folders."); 142 | helpMessage.appendln("\t\t[-help]:\t\tPrints this help message."); 143 | 144 | System.out.println(helpMessage); 145 | } 146 | 147 | private static void validateOutputDir(InputParameters inputParameters, String nextParam) { 148 | if (nextParam.startsWith("-")) { 149 | throw new IllegalArgumentException("You need to pass output dir to " + OUT_DIR + " flag"); 150 | } 151 | 152 | File outputDir = new File(nextParam); 153 | 154 | inputParameters.setOutputDir(outputDir); 155 | } 156 | 157 | private static void validateInputGenerics(InputParameters inputParameters, String nextParam) throws Exception { 158 | if (nextParam.startsWith("-")) { 159 | throw new IllegalArgumentException("You need to pass output dir to " + INPUT_GENERICS + " flag"); 160 | } 161 | 162 | File inputGenericsFile = new File(nextParam); 163 | 164 | if (!inputGenericsFile.exists()) { 165 | throw new Exception(String 166 | .format("Input generics file not found ( %s )!", inputGenericsFile.getAbsolutePath())); 167 | } else { 168 | inputParameters.setInputGenerics(inputGenericsFile); 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/Generator.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | import com.telerik.InputParameters; 4 | import com.telerik.Main; 5 | 6 | import org.apache.bcel.classfile.JavaClass; 7 | 8 | import java.io.File; 9 | import java.io.FileInputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.net.URL; 13 | import java.nio.file.Files; 14 | import java.nio.file.Paths; 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.Enumeration; 18 | import java.util.List; 19 | import java.util.zip.ZipEntry; 20 | import java.util.zip.ZipFile; 21 | import java.util.zip.ZipInputStream; 22 | 23 | /** 24 | * Created by plamen5kov on 6/17/16. 25 | */ 26 | public class Generator { 27 | 28 | private static File inputGenericsFile; 29 | private FileHelper fileHelper; 30 | private DtsApi dtsApi; 31 | private boolean allGenericImplements; 32 | private boolean skipDeclarations; 33 | private boolean classMode; 34 | private String outFileName; 35 | private String declarationsFileName; 36 | 37 | public void start(InputParameters inputParameters) throws Exception { 38 | Generator.inputGenericsFile = inputParameters.getInputGenerics(); 39 | this.allGenericImplements = inputParameters.isAllGenericImplementsEnabled(); 40 | this.skipDeclarations = inputParameters.getSkipDeclarations(); 41 | this.classMode = inputParameters.getClassMode(); 42 | this.fileHelper = new FileHelper(inputParameters.getOutputDir()); 43 | this.dtsApi = new DtsApi(allGenericImplements, inputParameters); 44 | this.outFileName = FileHelper.DEFAULT_DTS_FILE_NAME; 45 | this.declarationsFileName = FileHelper.DEFAULT_DECLARATIONS_FILE_NAME; 46 | 47 | loadJavaClasses(inputParameters.getInputJars()); 48 | loadSuperClasses(inputParameters.getSuperJars()); 49 | ClassRepo.sortCachedProviders(); 50 | 51 | generateDts(); 52 | } 53 | 54 | private void generateDts() throws Exception { 55 | if(inputGenericsFile == null) { 56 | InputStream stream = Main.class.getClassLoader().getResourceAsStream("generics.txt"); 57 | DtsApi.loadGenericsFromStream(stream); 58 | } 59 | if(inputGenericsFile != null){ 60 | DtsApi.loadGenerics(inputGenericsFile); 61 | } 62 | 63 | if(!this.skipDeclarations) { 64 | this.fileHelper.writeToFile(String.format("/// \n", this.declarationsFileName), this.outFileName, false); 65 | } 66 | 67 | while (ClassRepo.hasNext()) { 68 | List classFiles = ClassRepo.getNextClassGroup(); 69 | String generatedContent = this.dtsApi.generateDtsContent(classFiles); 70 | if (generatedContent.length() > 0) { 71 | this.fileHelper.writeToFile(generatedContent, this.outFileName, true); 72 | } 73 | } 74 | 75 | String content = this.fileHelper.readFileContent(this.outFileName); 76 | if(content != null) { 77 | String replacedContent = DtsApi.replaceGenericsInText(content); 78 | if(!content.equals(replacedContent)) { 79 | this.fileHelper.writeToFile(replacedContent, this.outFileName, false); 80 | } 81 | 82 | String serializedGenerics = DtsApi.serializeGenerics(); 83 | if(!serializedGenerics.equals("")) { 84 | this.fileHelper.writeToFile(serializedGenerics, this.outFileName, true); 85 | } 86 | } 87 | 88 | if(!this.skipDeclarations) { 89 | this.writeDeclarations(); 90 | } 91 | } 92 | 93 | private void writeDeclarations() { 94 | List imports = DtsApi.imports; 95 | imports.add(0, "declare module androidNative {\texport class Array {\tconstructor(); length: number; [index: number]: T; } }\n"); 96 | 97 | String existingContent = this.fileHelper.readFileContent(this.declarationsFileName); 98 | existingContent = existingContent != null ? existingContent : ""; 99 | 100 | for (String item : imports) { 101 | if(!existingContent.contains(item)) { 102 | this.fileHelper.writeToFile(item, this.declarationsFileName, true); 103 | } 104 | } 105 | } 106 | 107 | private void loadJavaClasses(List jars) throws IOException { 108 | for (File file : jars) { 109 | if (file.exists()) { 110 | if (file.isFile() && file.getName().endsWith(".aar")) { 111 | try { 112 | ZipFile zipFile = new ZipFile(file); 113 | 114 | Enumeration entries = zipFile.entries(); 115 | while(entries.hasMoreElements()){ 116 | ZipEntry entry = entries.nextElement(); 117 | if (entry.getName().equals("classes.jar")) { 118 | JarFile jar = JarFile.readJarInputStream(file.getAbsolutePath(), zipFile.getInputStream(entry)); 119 | ClassRepo.cacheJarFile(jar); 120 | break; 121 | } 122 | } 123 | } catch (Exception e) { 124 | e.printStackTrace(); 125 | } 126 | } 127 | if (file.isFile() && file.getName().endsWith(".jar")) { 128 | JarFile jar = JarFile.readJar(file.getAbsolutePath()); 129 | ClassRepo.cacheJarFile(jar); 130 | } else if (file.isDirectory()) { 131 | if(this.classMode) { 132 | ClassDirectrory dir = ClassDirectrory.readDirectory(file.getAbsolutePath()); 133 | ClassRepo.cacheJarFile(dir); 134 | } else { 135 | loadJavaClasses(Arrays.asList(file.listFiles())); 136 | } 137 | } 138 | } else { 139 | throw new IOException(String.format("File %s does not exist", file.getName())); 140 | } 141 | } 142 | } 143 | 144 | private void loadSuperClasses(List jars) throws IOException { 145 | for (File file : jars) { 146 | if (file.exists()) { 147 | if (file.isFile() && file.getName().endsWith(".jar")) { 148 | JarFile jar = JarFile.readJar(file.getAbsolutePath()); 149 | ClassRepo.cacheSuperJarFile(jar); 150 | } else if (file.isDirectory()) { 151 | if(this.classMode) { 152 | ClassDirectrory dir = ClassDirectrory.readDirectory(file.getAbsolutePath()); 153 | ClassRepo.cacheSuperJarFile(dir); 154 | } else { 155 | loadSuperClasses(Arrays.asList(file.listFiles())); 156 | } 157 | } 158 | } else { 159 | throw new IOException(String.format("File %s does not exist", file.getName())); 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android d.ts Generator 2 | A tool that generates TypeScript declaration files (.d.ts) from Jars/Aars 3 | 4 | > Because there are certain incompatibilities between Java and TypeScript, definitions MAY NOT always be completely accurate. They will however compile and provide auto complete feature inside a modern text editor supporting typings. 5 | 6 | ## Quirks 7 | - Interfaces are represented by classes, this will allow you to extend them by using the `new` syntax, as well as show you all its members that need to be implemented 8 | - Classes from the package `java.util.function` don't have their typings generated, as `function` is a reserved keyword in TypeScript and JavaScript 9 | - Classes returning values of Type in the package `java.util.function` will return `any` instead 10 | - Classes using methods with parameters of Type in the package `java.util.function` will take `any` instead 11 | - Classes implementing `java.util.Iterator`, `android.animation.TypeEvaluator`, `java.lang.Comparable` do not implement those interfaces in the generated definitions for compatibility-related issues 12 | 13 | 14 | ## Prerequisites 15 | - [Gradle](https://docs.gradle.org/current/userguide/gradle_wrapper.html) 16 | - Built gradle wrapper. [Read more](https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:adding_wrapper) 17 | 18 | 19 | Generate definitions following any of the approaches described below. Once you have run any of commands, you can find the generated typings in `dts-generator/out` directory. 20 | ## Generate definitons for Android SDK 21 | ```shell 22 | cd dts-generator 23 | ./gradlew jar 24 | java -jar build/libs/dts-generator.jar -input %ANDROID_HOME%/platforms/android-/android.jar 25 | ``` 26 | 27 | ## Generate definitions for any Jar/Aar 28 | ```shell 29 | cd dts-generator 30 | ./gradlew jar 31 | java -jar build/libs/dts-generator.jar -input 32 | ``` 33 | 34 | ## Pass multiple jars/aars to generator 35 | ```shell 36 | cd dts-generator 37 | ./gradlew jar 38 | java -jar build/libs/dts-generator.jar -input 39 | ``` 40 | Another option is to pass the folder containing the jars you want to pass 41 | ```shell 42 | cd dts-generator 43 | ./gradlew jar 44 | java -jar build/libs/dts-generator.jar -input 45 | ``` 46 | 47 | > Note: Check the [Makefile](Makefile) for sample usage 48 | 49 | ## Test 50 | 51 | In order to test the tool there are some predefined androuidx packages. We are running the tool and comparing the output with the expected output commited in the repo. 52 | To run the test: 53 | 54 | ```shell 55 | make test-compare-output 56 | ``` 57 | 58 | Then check whether the make command will return an error comparing the files. 59 | 60 | > Note: If the libs folder content is changed, the expected output should also be changed 61 | 62 | ## Generate definitions for .aar 63 | Pass the aar as input as you would normally do 64 | 65 | ## Complex typings generation 66 | 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. 67 | 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. 68 | To avoid this there's some functionality in the tool for passing dependencies when generating typings. 69 | There are two type of dependencies that can be passed: 70 | 71 | 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). 72 | 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`. 73 | 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:`. 74 | 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. 75 | 76 | ## Adding all implements for generic types 77 | 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. 78 | 79 | ## Finding package dependencies 80 | 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: 81 | 82 | 1. Open [dts-generator/build.gradle](dts-generator/build.gradle) file and locate `dependencies` part. 83 | 2. Add as a `testCompileOnly` dependency the one that you want to generate typings for: 84 | 85 | ```groovy 86 | dependencies { 87 | implementation 'org.apache.bcel:bcel:6.2' 88 | implementation 'commons-io:commons-io:2.6' 89 | implementation 'com.google.code.findbugs:findbugs:3.0.1' 90 | 91 | // add your dependency bellow 92 | testCompileOnly "com.android.support:support-v4:27.0.1" 93 | } 94 | ``` 95 | 96 | 3. Open the [dts-generator](dts-generator) folder in your terminal 97 | 4. Run the following command: 98 | 99 | ``` 100 | ./gradlew extractAllJars 101 | ``` 102 | 103 | 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`) 104 | 6. You can run the following command to check what are the dependencies between the packages: 105 | 106 | ``` 107 | ./gradlew dependencies --configuration testCompileOnly 108 | ``` 109 | 110 | 7. Run the dts-generator tool passing as **input** arguments the path to the output jars folder 111 | 112 | ## Androidx specifics 113 | 114 | To get all the jar files for androidx follow the steps above. You can find the **jar** files for androidx 1.0.0 in [the current repository](libs/androidx/1.0.0) 115 | As androidx 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: 116 | 117 | 1. Run the typings generator for androidx passing **android-17/android.jar** as a supper jar 118 | 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 119 | 3. Run `tsc` passing the generated typings file and there shouldn't be errors 120 | 4. Now start replacing the reference file with the files from other API level while the `tsc` execution completes with no error 121 | 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 122 | 123 | By repeating the steps above we've found that: 124 | 125 | - Androidx 17 typings(built with supper jar from android API 17) can be reused until android API 22 126 | - Androidx 23 typings(built with supper jar from android API 23) can be reused until android API 25 127 | - Androidx 26 typings(built with supper jar from android API 26) can be reused for API 26 and 27 128 | 129 | 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 130 | -------------------------------------------------------------------------------- /dts-generator/src/main/resources/generics.txt: -------------------------------------------------------------------------------- 1 | //android.accounts.AccountManagerCallback:1 2 | //android.accounts.AccountManagerFuture:1 3 | //android.animation.TypeEvaluator:1 4 | //android.app.LoaderManager.LoaderCallbacks:1 5 | //android.content.AsyncTaskLoader:1 6 | //android.content.ContentProvider.PipeDataWriter:1 7 | //android.content.Loader:1 8 | //android.content.Loader.OnLoadCanceledListener:1 9 | //android.content.Loader.OnLoadCompleteListener:1 10 | //android.database.Observable:1 11 | //android.os.AsyncTask:3 12 | //android.os.Parcelable.ClassLoaderCreator:1 13 | //android.os.Parcelable.Creator:1 14 | //android.os.RemoteCallbackList:1 15 | //android.test.ActivityInstrumentationTestCase:1 16 | //android.test.ActivityInstrumentationTestCase2:1 17 | //android.test.ActivityUnitTestCase:1 18 | //android.test.ApplicationTestCase:1 19 | //android.test.ProviderTestCase:1 20 | //android.test.ProviderTestCase2:1 21 | //android.test.ServiceTestCase:1 22 | //android.test.SingleLaunchActivityTestCase:1 23 | //android.util.LongSparseArray:1 24 | //android.util.LruCache:2 25 | //android.util.Pair:2 26 | //android.util.Property:2 27 | //android.util.SparseArray:1 28 | //android.webkit.ValueCallback:1 29 | //android.widget.AdapterView:1 30 | //android.widget.ArrayAdapter:1 31 | //com.android.internal.util.Predicate:1 32 | //java.lang.Class:1 33 | //java.lang.Comparable:1 34 | //java.lang.Enum:1 35 | //java.lang.InheritableThreadLocal:1 36 | //java.lang.Iterable:1 37 | //java.lang.ThreadLocal:1 38 | //java.lang.ref.PhantomReference:1 39 | //java.lang.ref.Reference:1 40 | //java.lang.ref.ReferenceQueue:1 41 | //java.lang.ref.SoftReference:1 42 | //java.lang.ref.WeakReference:1 43 | //java.lang.reflect.Constructor:1 44 | //java.lang.reflect.TypeVariable:1 45 | //java.security.PrivilegedAction:1 46 | //java.security.PrivilegedExceptionAction:1 47 | //java.util.AbstractCollection:1 48 | //java.util.AbstractList:1 49 | //java.util.AbstractMap:2 50 | //java.util.AbstractMap.SimpleEntry:2 51 | //java.util.AbstractMap.SimpleImmutableEntry:2 52 | //java.util.AbstractQueue:1 53 | //java.util.AbstractSequentialList:1 54 | //java.util.AbstractSet:1 55 | //java.util.ArrayDeque:1 56 | //java.util.ArrayList:1 57 | //java.util.Collection:1 58 | //java.util.Comparator:1 59 | //java.util.Deque:1 60 | //java.util.Dictionary:2 61 | //java.util.EnumMap:2 62 | //java.util.EnumSet:1 63 | //java.util.Enumeration:1 64 | //java.util.HashMap:2 65 | //java.util.HashSet:1 66 | //java.util.Hashtable:2 67 | //java.util.IdentityHashMap:2 68 | //java.util.Iterator:1 69 | //java.util.LinkedHashMap:2 70 | //java.util.LinkedHashSet:1 71 | //java.util.LinkedList:1 72 | //java.util.List:1 73 | //java.util.ListIterator:1 74 | //java.util.Map:2 75 | //java.util.Map.Entry:2 76 | //java.util.NavigableMap:2 77 | //java.util.NavigableSet:1 78 | //java.util.PriorityQueue:1 79 | //java.util.Queue:1 80 | //java.util.ServiceLoader:1 81 | //java.util.Set:1 82 | //java.util.SortedMap:2 83 | //java.util.SortedSet:1 84 | //java.util.Stack:1 85 | //java.util.TreeMap:2 86 | //java.util.TreeSet:1 87 | //java.util.Vector:1 88 | //java.util.WeakHashMap:2 89 | //java.util.concurrent.ArrayBlockingQueue:1 90 | //java.util.concurrent.BlockingDeque:1 91 | //java.util.concurrent.BlockingQueue:1 92 | //java.util.concurrent.Callable:1 93 | //java.util.concurrent.CompletionService:1 94 | //java.util.concurrent.ConcurrentHashMap:2 95 | //java.util.concurrent.ConcurrentLinkedQueue:1 96 | //java.util.concurrent.ConcurrentMap:2 97 | //java.util.concurrent.ConcurrentNavigableMap:2 98 | //java.util.concurrent.ConcurrentSkipListMap:2 99 | //java.util.concurrent.ConcurrentSkipListSet:1 100 | //java.util.concurrent.CopyOnWriteArrayList:1 101 | //java.util.concurrent.CopyOnWriteArraySet:1 102 | //java.util.concurrent.DelayQueue:1 103 | //java.util.concurrent.Exchanger:1 104 | //java.util.concurrent.ExecutorCompletionService:1 105 | //java.util.concurrent.Future:1 106 | //java.util.concurrent.FutureTask:1 107 | //java.util.concurrent.LinkedBlockingDeque:1 108 | //java.util.concurrent.LinkedBlockingQueue:1 109 | //java.util.concurrent.PriorityBlockingQueue:1 110 | //java.util.concurrent.RunnableFuture:1 111 | //java.util.concurrent.RunnableScheduledFuture:1 112 | //java.util.concurrent.ScheduledFuture:1 113 | //java.util.concurrent.SynchronousQueue:1 114 | //java.util.concurrent.atomic.AtomicIntegerFieldUpdater:1 115 | //java.util.concurrent.atomic.AtomicLongFieldUpdater:1 116 | //java.util.concurrent.atomic.AtomicMarkableReference:1 117 | //java.util.concurrent.atomic.AtomicReference:1 118 | //java.util.concurrent.atomic.AtomicReferenceArray:1 119 | //java.util.concurrent.atomic.AtomicReferenceFieldUpdater:2 120 | //java.util.concurrent.atomic.AtomicStampedReference:1 121 | //org.apache.http.client.ResponseHandler:1 122 | //android.support.design.widget.BaseTransientBottomBar:1 123 | //android.support.design.widget.BaseTransientBottomBar.BaseCallback:1 124 | //android.support.design.widget.BottomSheetBehavior:1 125 | //android.support.design.widget.CoordinatorLayout.Behavior:1 126 | //android.support.design.widget.DirectedAcyclicGraph:1 127 | //android.support.design.widget.HeaderBehavior:1 128 | //android.support.design.widget.SwipeDismissBehavior:1 129 | //android.support.design.widget.ViewOffsetBehavior:1 130 | //android.support.transition.PathProperty:1 131 | //android.support.v4.app.FragmentHostCallback:1 132 | //android.support.v4.app.LoaderManager.LoaderCallbacks:1 133 | //android.support.v4.content.AsyncTaskLoader:1 134 | //android.support.v4.content.Loader:1 135 | //android.support.v4.content.Loader.OnLoadCanceledListener:1 136 | //android.support.v4.content.Loader.OnLoadCompleteListener:1 137 | //android.support.v4.content.ModernAsyncTask:3 138 | //android.support.v4.content.ModernAsyncTask.AsyncTaskResult:1 139 | //android.support.v4.content.ModernAsyncTask.WorkerRunnable:2 140 | //android.support.v4.graphics.TypefaceCompatBaseImpl.StyleExtractor:1 141 | //android.support.v4.media.MediaBrowserCompatApi21.ConnectionCallbackProxy:1 142 | //android.support.v4.media.MediaBrowserCompatApi21.SubscriptionCallbackProxy:1 143 | //android.support.v4.media.MediaBrowserCompatApi23.ItemCallbackProxy:1 144 | //android.support.v4.media.MediaBrowserCompatApi26.SubscriptionCallbackProxy:1 145 | //android.support.v4.media.MediaBrowserServiceCompat.Result:1 146 | //android.support.v4.media.MediaBrowserServiceCompatApi21.ResultWrapper:1 147 | //android.support.v4.media.session.MediaControllerCompatApi21.CallbackProxy:1 148 | //android.support.v4.media.session.MediaSessionCompatApi21.CallbackProxy:1 149 | //android.support.v4.media.session.MediaSessionCompatApi23.CallbackProxy:1 150 | //android.support.v4.media.session.MediaSessionCompatApi24.CallbackProxy:1 151 | //android.support.v4.os.ParcelableCompat.ParcelableCompatCreatorHoneycombMR2:1 152 | //android.support.v4.os.ParcelableCompatCreatorCallbacks:1 153 | //android.support.v4.provider.SelfDestructiveThread.ReplyCallback:1 154 | //android.support.v4.util.ArrayMap:2 155 | //android.support.v4.util.ArraySet:1 156 | //android.support.v4.util.CircularArray:1 157 | //android.support.v4.util.LongSparseArray:1 158 | //android.support.v4.util.LruCache:2 159 | //android.support.v4.util.MapCollections:2 160 | //android.support.v4.util.MapCollections.ArrayIterator:1 161 | //android.support.v4.util.Pair:2 162 | //android.support.v4.util.Pools.Pool:1 163 | //android.support.v4.util.Pools.SimplePool:1 164 | //android.support.v4.util.Pools.SynchronizedPool:1 165 | //android.support.v4.util.SimpleArrayMap:2 166 | //android.support.v4.util.SparseArrayCompat:1 167 | //android.support.v4.widget.FocusStrategy.BoundsAdapter:1 168 | //android.support.v4.widget.FocusStrategy.CollectionAdapter:2 169 | //android.support.v4.widget.FocusStrategy.SequentialComparator:1 170 | //android.support.v7.util.AsyncListUtil:1 171 | //android.support.v7.util.AsyncListUtil.DataCallback:1 172 | //android.support.v7.util.MessageThreadUtil:1 173 | //android.support.v7.util.SortedList:1 174 | //android.support.v7.util.SortedList.BatchedCallback:1 175 | //android.support.v7.util.SortedList.Callback:1 176 | //android.support.v7.util.ThreadUtil:1 177 | //android.support.v7.util.ThreadUtil.BackgroundCallback:1 178 | //android.support.v7.util.ThreadUtil.MainThreadCallback:1 179 | //android.support.v7.util.TileList:1 180 | //android.support.v7.util.TileList.Tile:1 181 | //android.support.v7.view.menu.BaseMenuWrapper:1 182 | //android.support.v7.view.menu.BaseWrapper:1 183 | //android.support.v7.widget.PositionMap:1 184 | //android.support.v7.widget.RecyclerView.Adapter:1 185 | //android.support.v7.widget.util.SortedListAdapterCallback:1 186 | //com.telerik.android.common.CollectionChangeListener:1 187 | //com.telerik.android.common.CollectionChangedEvent:1 188 | //com.telerik.android.common.DataTuple:3 189 | //com.telerik.android.common.Function:2 190 | //com.telerik.android.common.Function2:3 191 | //com.telerik.android.common.Function2Async:3 192 | //com.telerik.android.common.ObservableCollection:1 193 | //com.telerik.android.common.Procedure:1 194 | //com.telerik.android.common.Procedure2:2 -------------------------------------------------------------------------------- /dts-generator/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Stop when "xargs" is not available. 209 | if ! command -v xargs >/dev/null 2>&1 210 | then 211 | die "xargs is not available" 212 | fi 213 | 214 | # Use "xargs" to parse quoted args. 215 | # 216 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 217 | # 218 | # In Bash we could simply go: 219 | # 220 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 221 | # set -- "${ARGS[@]}" "$@" 222 | # 223 | # but POSIX shell has neither arrays nor command substitution, so instead we 224 | # post-process each arg (as a line of input to sed) to backslash-escape any 225 | # character that might be a shell metacharacter, then use eval to reverse 226 | # that process (while maintaining the separation between arguments), and wrap 227 | # the whole thing up as a single "set" statement. 228 | # 229 | # This will of course break if any of these variables contains a newline or 230 | # an unmatched quote. 231 | # 232 | 233 | eval "set -- $( 234 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 235 | xargs -n1 | 236 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 237 | tr '\n' ' ' 238 | )" '"$@"' 239 | 240 | exec "$JAVACMD" "$@" 241 | -------------------------------------------------------------------------------- /dts-generator/src/main/java/com/telerik/dts/DtsApi.java: -------------------------------------------------------------------------------- 1 | package com.telerik.dts; 2 | 3 | import com.telerik.InputParameters; 4 | 5 | import org.apache.bcel.classfile.Attribute; 6 | import org.apache.bcel.classfile.Field; 7 | import org.apache.bcel.classfile.FieldOrMethod; 8 | import org.apache.bcel.classfile.JavaClass; 9 | import org.apache.bcel.classfile.LocalVariable; 10 | import org.apache.bcel.classfile.LocalVariableTable; 11 | import org.apache.bcel.classfile.Method; 12 | import org.apache.bcel.classfile.Signature; 13 | import org.apache.bcel.generic.ArrayType; 14 | import org.apache.bcel.generic.BasicType; 15 | import org.apache.bcel.generic.ObjectType; 16 | import org.apache.bcel.generic.ReferenceType; 17 | import org.apache.bcel.generic.Type; 18 | import org.apache.bcel.util.BCELComparator; 19 | 20 | import java.io.BufferedReader; 21 | import java.io.File; 22 | import java.io.InputStream; 23 | import java.io.InputStreamReader; 24 | import java.nio.charset.StandardCharsets; 25 | import java.nio.file.Files; 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.Collection; 29 | import java.util.HashMap; 30 | import java.util.HashSet; 31 | import java.util.Iterator; 32 | import java.util.LinkedList; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Queue; 36 | import java.util.Set; 37 | import java.util.regex.Matcher; 38 | import java.util.regex.Pattern; 39 | import java.util.stream.Collectors; 40 | import java.util.stream.Stream; 41 | 42 | import edu.umd.cs.findbugs.ba.generic.GenericObjectType; 43 | import edu.umd.cs.findbugs.ba.generic.GenericSignatureParser; 44 | import edu.umd.cs.findbugs.ba.generic.GenericUtilities; 45 | 46 | /** 47 | * Created by plamen5kov on 6/17/16. 48 | */ 49 | public class DtsApi { 50 | public static List> externalGenerics = new ArrayList<>(); 51 | public static List> generics = new ArrayList<>(); 52 | public static List imports = new ArrayList<>(); 53 | public static String JavaLangObject = "java.lang.Object"; 54 | 55 | private static Map globalAliases = new HashMap<>(); 56 | 57 | private Map extendsOverrides = new HashMap<>(); 58 | private Map superOverrides = new HashMap<>(); 59 | private Map typeOverrides = new HashMap<>(); 60 | 61 | private StringBuilder2 sbContent; 62 | private Set references; 63 | private JavaClass prevClass; 64 | private String currentFileClassname; 65 | private Set baseMethodNames; 66 | private List baseMethods; 67 | private Map mapNameMethod; 68 | private Map aliasedTypes; 69 | private String[] namespaceParts; 70 | private int indent = 0; 71 | private boolean allGenericImplements; 72 | private Pattern methodSignature = Pattern.compile("\\((?.*)\\)(?.*)"); 73 | private Pattern isWordPattern = Pattern.compile("^[\\w\\d]+$"); 74 | private Pattern isVoid = Pattern.compile("V(\\^.*\\;)?"); 75 | private int ignoreObfuscatedNameLength; 76 | private HashSet warnedMissing = new HashSet<>(); 77 | private Pattern jsFieldPattern = Pattern.compile("^[a-zA-Z$_][a-zA-Z0-9$_]*$"); 78 | 79 | private Set reservedJsKeywords = Set.of( 80 | "abstract", "arguments", "await", "boolean", 81 | "break", "byte", "case", "catch", 82 | "char", "class", "const", "continue", 83 | "debugger", "default", "delete", "do", 84 | "double", "else", "enum", "eval", 85 | "export", "extends", "false", "final", 86 | "finally", "float", "for", "function", 87 | "goto", "if", "implements", "import", 88 | "in", "instanceof", "int", "interface", 89 | "let", "long", "native", "new", 90 | "null", "package", "private", "protected", 91 | "public", "return", "short", "static", 92 | "super", "switch", "synchronized", "this", 93 | "throw", "throws", "transient", "true", 94 | "try", "typeof", "var", "void", 95 | "volatile", "while", "with", "yield" 96 | ); 97 | 98 | public DtsApi(boolean allGenericImplements, InputParameters inputParameters) { 99 | this.allGenericImplements = allGenericImplements; 100 | this.ignoreObfuscatedNameLength = inputParameters.getIgnoreObfuscatedNameLength(); 101 | this.indent = 0; 102 | 103 | overrideFieldComparator(); 104 | setOverrides(); 105 | setTypeOverrides(); 106 | setGlobalAliases(); 107 | 108 | this.aliasedTypes = new HashMap<>(); 109 | } 110 | 111 | public String generateDtsContent(List javaClasses) { 112 | this.prevClass = null; 113 | 114 | if ((javaClasses != null) && (javaClasses.size() > 0)) { 115 | references = new HashSet<>(); 116 | sbContent = new StringBuilder2(); 117 | 118 | // process class scope 119 | for (int i = 0; i < javaClasses.size(); i++) { 120 | Set methodsSet = new HashSet<>(); 121 | 122 | JavaClass currClass = javaClasses.get(i); 123 | currentFileClassname = currClass.getClassName(); 124 | 125 | 126 | String simpleClassName = getSimpleClassname(currClass); 127 | if (isObfuscated(simpleClassName)) { 128 | continue; 129 | } 130 | Signature signature = this.getSignature(currClass); 131 | TypeDefinition typeDefinition = null; 132 | if (signature != null) { 133 | typeDefinition = new TypeDefinition(signature.getSignature(), currentFileClassname); 134 | } 135 | 136 | if (currentFileClassname.startsWith("java.util.function") || 137 | currentFileClassname.startsWith("android.support.v4.media.routing.MediaRouterJellybeanMr1") || 138 | currentFileClassname.startsWith("android.support.v4.media.routing.MediaRouterJellybeanMr2") || 139 | currentFileClassname.contains(".debugger.") || 140 | currentFileClassname.endsWith("package-info") || 141 | currentFileClassname.endsWith("module-info") || 142 | currentFileClassname.endsWith("Kt")) { 143 | continue; 144 | } 145 | 146 | // check if processed class hijacks a namespace 147 | // TODO: optimize 148 | 149 | this.namespaceParts = currentFileClassname.split("\\."); 150 | if (isIgnoredNamespace()) { 151 | System.out.println(String.format("Found ignored namespace. %s", String.join(".", this.namespaceParts))); 152 | continue; 153 | } 154 | 155 | boolean isInterface = currClass.isInterface(); 156 | boolean isAbstract = currClass.isAbstract(); 157 | 158 | this.indent = closePackage(this.prevClass, currClass); 159 | this.indent = openPackage(this.prevClass, currClass); 160 | 161 | String tabs = getTabs(this.indent); 162 | 163 | String extendsLine = getExtendsLine(currClass, typeDefinition); 164 | 165 | if (simpleClassName.equals("AccessibilityDelegate")) { 166 | sbContent.appendln(tabs + "export class " + getFullClassNameConcatenated(currClass) + getTypeSuffix(currentFileClassname, typeDefinition, extendsLine) + extendsLine + " {"); 167 | } else { 168 | sbContent.appendln(tabs + "export" + (isAbstract && !isInterface ? " abstract " : " ") + "class " + simpleClassName + getTypeSuffix(currentFileClassname, typeDefinition, extendsLine) + extendsLine + " {"); 169 | } 170 | // process member scope 171 | 172 | mapNameMethod = new HashMap<>(); 173 | 174 | loadBaseMethods(currClass); //loaded in "baseMethodNames" and "baseMethods" 175 | 176 | addClassField(currClass); 177 | 178 | // process constructors for interfaces 179 | if (isInterface) { 180 | List allInterfaces = getAllInterfaces(currClass); 181 | 182 | List allInterfacesMethods = getAllInterfacesMethods(allInterfaces); 183 | Set allInterfaceFields = getAllInterfacesFields(allInterfaces); 184 | 185 | processInterfaceConstructor(currClass, typeDefinition, allInterfacesMethods); 186 | 187 | for (Method method : allInterfacesMethods) { 188 | processMethod(method, currClass, typeDefinition, methodsSet); 189 | } 190 | 191 | for (Field f : allInterfaceFields) { 192 | processField(f, currClass, typeDefinition); 193 | } 194 | } else { 195 | List members = getMembers(currClass, getAllInterfaces(currClass)); 196 | for (FieldOrMethod fieldOrMethod : members) { 197 | if (fieldOrMethod instanceof Field) { 198 | processField((Field) fieldOrMethod, currClass, typeDefinition); 199 | } else if (fieldOrMethod instanceof Method) { 200 | processMethod((Method) fieldOrMethod, currClass, typeDefinition, methodsSet); 201 | } else { 202 | throw new IllegalArgumentException("Argument is not method or field"); 203 | } 204 | } 205 | // process member scope end 206 | } 207 | 208 | if (!isInterface) { 209 | HashSet allInterfaces = new HashSet<>(getAllInterfaces(currClass)); 210 | 211 | List allClasses = getAllSuperClasses(currClass); 212 | 213 | // Include interfaces of extended classes 214 | for (JavaClass jclass : allClasses) { 215 | allInterfaces.addAll(getInterfaces(jclass)); 216 | } 217 | 218 | List allInterfacesMethods = getAllInterfacesMethods(allInterfaces); 219 | 220 | for (Method method : allInterfacesMethods) { 221 | processMethod(method, currClass, typeDefinition, methodsSet); 222 | } 223 | } 224 | 225 | writeMethods(methodsSet); 226 | 227 | sbContent.appendln(tabs + "}"); 228 | if (getSimpleClassname(currClass).equals("AccessibilityDelegate")) { 229 | String innerClassAlias = "export type " + getSimpleClassname(currClass) + " = " + getFullClassNameConcatenated(currClass); 230 | sbContent.appendln(tabs + innerClassAlias); 231 | } 232 | this.prevClass = currClass; 233 | } 234 | closePackage(prevClass, null); 235 | // process class scope end 236 | 237 | String[] refs = references.toArray(new String[references.size()]); 238 | Arrays.sort(refs); 239 | } 240 | 241 | String content = replaceIgnoredNamespaces(sbContent.toString()); 242 | 243 | return content; 244 | } 245 | 246 | private String replaceIgnoredNamespaces(String content) { 247 | String regexFormat = "(?%s(?:(?:\\.[a-zA-Z\\d]*)|<[a-zA-Z\\d\\.<>]*>)*)(?[^a-zA-Z\\d]+)"; 248 | // 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 249 | for (String ignoredNamespace : this.getIgnoredNamespaces()) { 250 | String regexString = String.format(regexFormat, ignoredNamespace.replace(".", "\\.")); 251 | content = content.replaceAll(regexString, "any$2"); 252 | regexString = String.format(regexFormat, getGlobalAliasedClassName(ignoredNamespace).replace(".", "\\.")); 253 | content = content.replaceAll(regexString, "any$2"); 254 | } 255 | 256 | // replace "extends any" with "extends java.lang.Object" 257 | content = content.replace(" extends any ", String.format(" extends %s ", DtsApi.JavaLangObject)); 258 | 259 | return content; 260 | } 261 | 262 | public static String serializeGenerics() { 263 | StringBuilder sb = new StringBuilder(); 264 | sb.append("//Generics information:\n"); 265 | for (Tuple generic : generics) { 266 | sb.append(String.format("//%s:%s\n", generic.x, generic.y)); 267 | } 268 | return sb.toString(); 269 | } 270 | 271 | public static void loadGenericsFromStream(InputStream stream) throws Exception { 272 | List doc = 273 | new BufferedReader(new InputStreamReader(stream, 274 | StandardCharsets.UTF_8)).lines().collect(Collectors.toList()); 275 | loadGenericsLines(doc); 276 | } 277 | 278 | public static void loadGenericsLines(List lines) throws Exception { 279 | for (String line : lines) { 280 | if (!line.equals("")) { 281 | while (line.startsWith("/")) { 282 | line = line.substring(1, line.length()); 283 | } 284 | String[] parts = line.split(":"); 285 | if (parts.length != 2) { 286 | throw new Exception(String.format("Invalid generic info(%s)", line)); 287 | } 288 | externalGenerics.add(new Tuple<>(parts[0], Integer.parseInt(parts[1]))); 289 | } 290 | } 291 | } 292 | 293 | public static void loadGenerics(File inputFile) throws Exception { 294 | System.out.println("loadGenerics from file: " + inputFile.getAbsolutePath()); 295 | try { 296 | List lines = Files.readAllLines(inputFile.toPath()); 297 | loadGenericsLines(lines); 298 | } catch (Exception e) { 299 | throw new Exception(String.format("%s in file %s", e.getMessage(), inputFile)); 300 | } 301 | } 302 | 303 | // Adds javalangObject types to all generics which are used without types 304 | public static String replaceGenericsInText(String content) { 305 | String any = "any"; 306 | String result = content; 307 | 308 | List> allGenerics = Stream.concat(generics.stream(), externalGenerics.stream()).collect(Collectors.toList()); 309 | 310 | for (Tuple generic : allGenerics) { 311 | result = replaceNonGenericUsage(result, generic.x, generic.y, any); 312 | String globalAliasedClassName = getGlobalAliasedClassName(generic.x); 313 | if (!generic.x.equals(globalAliasedClassName)) { 314 | result = replaceNonGenericUsage(result, globalAliasedClassName, generic.y, any); 315 | } 316 | } 317 | 318 | return result; 319 | } 320 | 321 | private static String replaceNonGenericUsage(String content, String className, Integer occurencies, String javalangObject) { 322 | String result = content; 323 | Pattern usedAsNonGenericPattern = Pattern.compile(className.replace(".", "\\.") + "(?[^a-zA-Z\\d^\\.^\\$^\\<])"); 324 | Matcher matcher = usedAsNonGenericPattern.matcher(result); 325 | 326 | if (!matcher.find()) 327 | return content; 328 | 329 | List arguments = new ArrayList<>(); 330 | for (int i = 0; i < occurencies; i++) { 331 | arguments.add(javalangObject); 332 | } 333 | String classSuffix = "<" + String.join(",", arguments) + ">"; 334 | 335 | System.out.println(String.format("Appending %s to occurrences of class %s without passed generic types", classSuffix, className)); 336 | 337 | String replaceString = String.format("%s%s$1", className, classSuffix); 338 | result = matcher.replaceAll(replaceString); 339 | return result; 340 | } 341 | 342 | private String getExtendsLine(JavaClass currClass, TypeDefinition typeDefinition) { 343 | String override = this.extendsOverrides.get(currClass.getClassName()); 344 | if (override != null) { 345 | System.out.println(String.format("Found extends override for class %s - %s", currClass.getClassName(), override)); 346 | return " extends " + override; 347 | } 348 | if (typeDefinition != null) { 349 | StringBuilder result = new StringBuilder(); 350 | ReferenceType parent = typeDefinition.getParent(); 351 | 352 | List interfaces = typeDefinition.getInterfaces(); 353 | if (parent != null) { 354 | result.append(" extends "); 355 | result.append(getTypeScriptTypeFromJavaType(parent, typeDefinition)); 356 | } 357 | if (interfaces.size() == 1 || (this.allGenericImplements && interfaces.size() > 0)) { 358 | result.append(" implements "); 359 | 360 | for (ReferenceType referenceType : interfaces) { 361 | String tsType = getTypeScriptTypeFromJavaType(referenceType, typeDefinition); 362 | if (!this.isPrimitiveTSType(tsType)) { 363 | result.append(tsType + ", "); 364 | } 365 | } 366 | result.deleteCharAt(result.lastIndexOf(",")); 367 | } 368 | return result.toString(); 369 | } else { 370 | JavaClass superClass = getSuperClass(currClass); 371 | List interfaces = getInterfaces(currClass); 372 | if (interfaces.size() == 1 && superClass == null && currClass.getSuperclassName().equals(DtsApi.JavaLangObject)) { 373 | superClass = interfaces.get(0); 374 | interfaces.clear(); 375 | } 376 | return getExtendsLine(superClass, interfaces); 377 | } 378 | } 379 | 380 | private String getExtendsLine(JavaClass superClass, List interfaces) { 381 | StringBuilder implementsSegmentSb = new StringBuilder(); 382 | String implementsSegment = ""; 383 | if (interfaces.size() > 0) { 384 | implementsSegmentSb.append(" implements "); 385 | 386 | for (JavaClass clazz : interfaces) { 387 | String implementedInterface = clazz.getClassName().replaceAll("\\$", "\\."); 388 | if (!typeBelongsInCurrentTopLevelNamespace(implementedInterface)) { 389 | implementedInterface = getAliasedClassName(implementedInterface); 390 | } 391 | 392 | implementsSegmentSb.append(implementedInterface + ", "); 393 | } 394 | 395 | implementsSegment = implementsSegmentSb.substring(0, implementsSegmentSb.lastIndexOf(",")); 396 | 397 | } 398 | 399 | if (superClass != null) { 400 | String extendedClass = superClass.getClassName().replaceAll("\\$", "\\."); 401 | 402 | if (!extendedClass.equals(DtsApi.JavaLangObject)) { 403 | // check for type override 404 | String override = this.typeOverrides.get(extendedClass); 405 | if (override != null) { 406 | System.out.println(String.format("Found type override for class %s - %s", extendedClass, override)); 407 | extendedClass = override; 408 | } 409 | } 410 | 411 | if (!typeBelongsInCurrentTopLevelNamespace(extendedClass)) { 412 | extendedClass = getAliasedClassName(extendedClass); 413 | } 414 | 415 | return " extends " + extendedClass + implementsSegment; 416 | } else { 417 | return implementsSegment; 418 | } 419 | } 420 | 421 | private String getAliasedClassName(String className) { 422 | return mangleRootClassname(className); 423 | } 424 | 425 | private boolean typeBelongsInCurrentTopLevelNamespace(String className) { 426 | return className.startsWith(this.namespaceParts[0] + "."); 427 | } 428 | 429 | private static String getGlobalAliasedClassName(String className) { 430 | String[] parts = className.split("\\."); 431 | String rootNamespace = parts[0]; 432 | if (globalAliases.containsKey(parts[0])) { 433 | String aliasedNamespace = globalAliases.get(rootNamespace); 434 | parts = Arrays.copyOfRange(parts, 1, parts.length); 435 | String result = aliasedNamespace; 436 | if (parts.length > 0) { 437 | result += "." + String.join(".", parts); 438 | } 439 | return result; 440 | } else { 441 | return className; 442 | } 443 | } 444 | 445 | private static void addImport(String importToAdd) { 446 | if (!imports.stream().anyMatch(x -> x.equals(importToAdd))) { 447 | imports.add(importToAdd); 448 | } 449 | } 450 | 451 | private String mangleRootClassname(String className) { 452 | String[] parts = className.split("\\."); 453 | String rootNamespace = parts[0]; 454 | if (globalAliases.containsKey(parts[0])) { 455 | String aliasedNamespace = DtsApi.globalAliases.get(rootNamespace); 456 | String aliasedType = aliasedTypes.get(rootNamespace); 457 | if (aliasedType == null) { 458 | aliasedTypes.put(rootNamespace, aliasedNamespace); 459 | addImport(String.format("import %s = %s;\n", aliasedNamespace, rootNamespace)); 460 | } 461 | 462 | parts = Arrays.copyOfRange(parts, 1, parts.length); 463 | String result = aliasedNamespace; 464 | if (parts.length > 0) { 465 | result += "." + String.join(".", parts); 466 | } 467 | return result; 468 | } 469 | return className; 470 | } 471 | 472 | private int closePackage(JavaClass prevClass, JavaClass currClass) { 473 | int indent = 0; 474 | 475 | if (prevClass == null) { 476 | return indent; 477 | } 478 | 479 | String prevClassName = prevClass.getClassName(); 480 | int prevDotCount = prevClassName.length() - prevClassName.replace(".", "").length(); 481 | int prevDollarCount = prevClassName.length() - prevClassName.replace("$", "").length(); 482 | int prevCount = prevDotCount + prevDollarCount; 483 | 484 | if (currClass == null) { 485 | indent = prevCount; 486 | while (indent > 0) { 487 | String tabs = getTabs(--indent); 488 | sbContent.appendln(tabs + "}"); 489 | } 490 | return indent; 491 | } 492 | 493 | String currClassName = currClass.getClassName(); 494 | int currDotCount = currClassName.length() - currClassName.replace(".", "").length(); 495 | int currDollarCount = currClassName.length() - currClassName.replace("$", "").length(); 496 | int currCount = currDotCount + currDollarCount; 497 | 498 | while (prevCount > currCount) { 499 | String tabs = getTabs(--prevCount); 500 | sbContent.appendln(tabs + "}"); 501 | } 502 | 503 | boolean isNested = isNested(currClass); 504 | 505 | if (!isNested) { 506 | throw new UnsupportedOperationException("TODO: implement"); 507 | // String prevClassName = prevClass.getClassName(); 508 | // int dotCount = prevClassName.length() - 509 | // prevClassName.replace(".", "").length(); 510 | // int dollarCount = prevClassName.length() - 511 | // prevClassName.replace("$", "").length(); 512 | // indent = dotCount + dollarCount; 513 | // 514 | // String[] prevParts = prevClassName.replace('$', 515 | // '.').split("\\."); 516 | // String[] currParts = currClass.getClassName().replace('$', 517 | // '.').split("\\."); 518 | // 519 | // int diffIdx = 0; 520 | // while ((diffIdx < prevParts.length) && (diffIdx < 521 | // currParts.length) && 522 | // prevParts[diffIdx].equals(currParts[diffIdx])) { 523 | // ++diffIdx; 524 | // } 525 | // 526 | // int count = prevParts.length - diffIdx - 1; 527 | // while (count-- > 0) { 528 | // String tabs = getTabs(--indent); 529 | // ps.println(tabs + "}"); 530 | // } 531 | } 532 | 533 | return indent; 534 | } 535 | 536 | private int openPackage(JavaClass prevClass, JavaClass currClass) { 537 | int indent = 0; 538 | 539 | String prevClassName = (prevClass != null) ? prevClass.getClassName() : ""; 540 | String[] prevParts = prevClassName.replace('$', '.').split("\\."); 541 | String[] currParts = currClass.getClassName().replace('$', '.').split("\\."); 542 | 543 | int diffIdx = 0; 544 | while ((diffIdx < prevParts.length) && (diffIdx < currParts.length) 545 | && prevParts[diffIdx].equals(currParts[diffIdx])) { 546 | ++diffIdx; 547 | } 548 | 549 | indent = diffIdx; 550 | for (int idx = diffIdx; idx < currParts.length - 1; idx++) { 551 | ++indent; 552 | String tabs = getTabs(idx); 553 | if (idx == 0) { 554 | sbContent.append(tabs + "declare "); 555 | } else { 556 | sbContent.append(tabs + "export "); 557 | } 558 | sbContent.appendln("module " + currParts[idx] + " {"); 559 | } 560 | 561 | if (isNested(currClass) && (prevParts.length < currParts.length)) { 562 | String tabs = getTabs(prevParts.length - 1); 563 | sbContent.appendln(tabs + "export module " + prevParts[prevParts.length - 1] + " {"); 564 | } 565 | 566 | return indent; 567 | } 568 | 569 | private void processInterfaceConstructor(JavaClass classInterface, TypeDefinition typeDefinition, List allInterfacesMethods) { 570 | String tabs = getTabs(this.indent + 1); 571 | 572 | generateInterfaceConstructorContent(classInterface, typeDefinition, tabs, allInterfacesMethods); 573 | } 574 | 575 | private void generateInterfaceConstructorContent(JavaClass classInterface, TypeDefinition typeDefinition, String tabs, List methods) { 576 | generateInterfaceConstructorCommentBlock(classInterface, tabs); 577 | 578 | sbContent.appendln(tabs + "public constructor(implementation: {"); 579 | 580 | for (Method m : methods) { 581 | sbContent.append(getTabs(this.indent + 2) + getMethodName(m) + getMethodParamSignature(classInterface, typeDefinition, m)); 582 | String bmSig = ""; 583 | if (!isConstructor(m)) { 584 | bmSig += ": " + getTypeScriptTypeFromJavaType(this.getReturnType(m), typeDefinition); 585 | } 586 | sbContent.appendln(bmSig + ";"); 587 | } 588 | 589 | sbContent.appendln(tabs + "});"); 590 | 591 | sbContent.appendln(tabs + "public constructor();"); 592 | } 593 | 594 | private void generateInterfaceConstructorCommentBlock(JavaClass classInterface, String tabs) { 595 | sbContent.appendln(tabs + "/**"); 596 | 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."); 597 | // sbContent.appendln(tabs + " * @param implementation - allows implementor to define their own logic for all public methods."); // <- causes too much noise 598 | sbContent.appendln(tabs + " */"); 599 | } 600 | 601 | private List getAllInterfaces(JavaClass classInterface) { 602 | ArrayList interfaces = new ArrayList<>(); 603 | 604 | Queue classQueue = new LinkedList<>(); 605 | classQueue.add(classInterface); 606 | 607 | while (!classQueue.isEmpty()) { 608 | JavaClass clazz = classQueue.poll(); 609 | 610 | interfaces.add(clazz); 611 | 612 | classQueue.addAll(getInterfaces(clazz)); 613 | } 614 | 615 | return interfaces; 616 | } 617 | 618 | private List getAllSuperClasses(JavaClass clazz) { 619 | ArrayList classes = new ArrayList<>(); 620 | 621 | Queue classQueue = new LinkedList<>(); 622 | classQueue.add(clazz); 623 | 624 | while (!classQueue.isEmpty()) { 625 | JavaClass currClazz = classQueue.poll(); 626 | 627 | if (currClazz.getClassName().equals(DtsApi.JavaLangObject)) { 628 | break; 629 | } 630 | 631 | classes.add(currClazz); 632 | 633 | JavaClass sClass = getSuperClass(currClazz); 634 | 635 | if (sClass != null) { 636 | classQueue.add(getSuperClass(currClazz)); 637 | } 638 | } 639 | 640 | return classes; 641 | } 642 | 643 | private List getInterfaces(JavaClass classInterface) { 644 | List interfaces = new ArrayList<>(); 645 | 646 | String[] interfaceNames = classInterface.getInterfaceNames(); 647 | for (String intface : interfaceNames) { 648 | JavaClass clazz1 = ClassRepo.findClass(intface); 649 | 650 | // Added guard to prevent NullPointerExceptions in case libs are not provided - the dev can choose to include it and rerun the generator 651 | if (clazz1 == null) { 652 | if (!warnedMissing.contains(intface)) { 653 | warnedMissing.add(intface); 654 | System.out.println("ignoring definitions in missing dependency: " + intface); 655 | } 656 | continue; 657 | } 658 | 659 | String className = clazz1.getClassName(); 660 | 661 | // TODO: Pete: Hardcoded until we figure out how to go around the 'type incompatible with Object' issue 662 | if (className.equals("java.util.Iterator") || 663 | className.equals("android.animation.TypeEvaluator") || 664 | className.equals("java.lang.Comparable") || 665 | className.startsWith("java.util.function")) { 666 | continue; 667 | } 668 | 669 | interfaces.add(clazz1); 670 | } 671 | 672 | return interfaces; 673 | } 674 | 675 | private List getAllInterfacesMethods(Collection interfaces) { 676 | ArrayList allInterfacesMethods = new ArrayList<>(); 677 | 678 | for (JavaClass clazz : interfaces) { 679 | Method[] intfaceMethods = clazz.getMethods(); 680 | allInterfacesMethods.addAll(Arrays.asList(intfaceMethods)); 681 | } 682 | 683 | return allInterfacesMethods; 684 | } 685 | 686 | private Set getAllInterfacesFields(List interfaces) { 687 | HashSet allInterfacesFields = new HashSet<>(); 688 | 689 | for (JavaClass clazz : interfaces) { 690 | allInterfacesFields.addAll(Arrays.asList(clazz.getFields())); 691 | } 692 | 693 | return allInterfacesFields; 694 | } 695 | 696 | //method related 697 | private void processMethod(Method method, JavaClass clazz, TypeDefinition typeDefinition, Set methodsSet) { 698 | String name = method.getName(); 699 | 700 | if (shouldIgnoreMember(name)) return; 701 | 702 | if (method.isSynthetic() || (!method.isPublic() && !method.isProtected())) { 703 | return; 704 | } 705 | 706 | // TODO: Pete: won't generate static initializers as invalid typescript properties 707 | if (clazz.isInterface() && name.equals("")) { 708 | return; 709 | } 710 | 711 | String tabs = getTabs(this.indent + 1); 712 | 713 | cacheMethodBySignature(method); //cached in "mapNameMethod" 714 | 715 | //generate base method content 716 | if (baseMethodNames.contains(name)) { 717 | for (Method baseMethod : baseMethods) { 718 | if (baseMethod.getName().equals(name)) { 719 | String sig = getMethodFullSignature(baseMethod); 720 | if (!mapNameMethod.containsKey(sig)) { 721 | mapNameMethod.put(sig, baseMethod); 722 | methodsSet.add(generateMethodContent(clazz, typeDefinition, tabs, baseMethod)); 723 | } 724 | } 725 | } 726 | } 727 | 728 | methodsSet.add(generateMethodContent(clazz, typeDefinition, tabs, method)); 729 | } 730 | 731 | private boolean methodIsDeprecated(Method method) { 732 | return Arrays.stream( 733 | method 734 | .getAttributes()) 735 | .anyMatch(x -> 736 | x.getClass() 737 | .isAssignableFrom(org.apache.bcel.classfile.Deprecated.class)); 738 | } 739 | 740 | private String generateMethodContent(JavaClass clazz, TypeDefinition typeDefinition, String tabs, Method method) { 741 | StringBuilder2 sbTemp = new StringBuilder2(); 742 | if (methodIsDeprecated(method)) { 743 | sbTemp.appendln(tabs + "/** @deprecated */"); 744 | } 745 | 746 | sbTemp.append(tabs + "public "); 747 | 748 | if (method.isStatic()) { 749 | sbTemp.append("static "); 750 | } 751 | 752 | sbTemp.append(getMethodName(method) + getMethodParamSignature(clazz, typeDefinition, method)); 753 | String bmSig = ""; 754 | if (!isConstructor(method)) { 755 | bmSig += ": " + getTypeScriptTypeFromJavaType(this.getReturnType(method), typeDefinition); 756 | } 757 | 758 | sbTemp.append(bmSig + ";"); 759 | 760 | return sbTemp.toString(); 761 | } 762 | 763 | private Signature getSignature(FieldOrMethod fieldOrMethod) { 764 | for (Attribute attribute : fieldOrMethod.getAttributes()) { 765 | if (attribute instanceof Signature) { 766 | return (Signature) attribute; 767 | } 768 | } 769 | return null; 770 | } 771 | 772 | private Signature getSignature(JavaClass clazz) { 773 | for (Attribute attribute : clazz.getAttributes()) { 774 | if (attribute instanceof Signature) { 775 | return (Signature) attribute; 776 | } 777 | } 778 | return null; 779 | } 780 | 781 | private Type[] getArgumentTypes(Method m) { 782 | Signature signature = this.getSignature(m); 783 | if (signature != null) { 784 | Matcher matcher = methodSignature.matcher(signature.getSignature()); 785 | if (matcher.matches()) { 786 | String argumentsSignature = matcher.group(1); 787 | if (argumentsSignature.equals("")) { 788 | return m.getArgumentTypes(); 789 | } 790 | try { 791 | List referenceTypes = DtsApi.getTypeParameters(argumentsSignature); 792 | Type[] types = new Type[referenceTypes.size()]; 793 | types = referenceTypes.toArray(types); 794 | return types; 795 | } catch (ClassCastException classCast) { 796 | return m.getArgumentTypes(); 797 | } 798 | } 799 | } 800 | return m.getArgumentTypes(); 801 | } 802 | 803 | private static List getTypeParameters(String signature) { 804 | GenericSignatureParser parser = new GenericSignatureParser("(" + signature + ")V"); 805 | List types = new ArrayList<>(); 806 | Iterator iter = parser.parameterSignatureIterator(); 807 | 808 | while (iter.hasNext()) { 809 | String parameterString = iter.next(); 810 | Type t = GenericUtilities.getType(parameterString); 811 | if (t == null) { 812 | return null; 813 | } 814 | 815 | types.add(t); 816 | } 817 | 818 | return types; 819 | } 820 | 821 | // gets the full field type including generic types 822 | private Type getFieldType(Field f) { 823 | Signature signature = this.getSignature(f); 824 | if (signature != null) { 825 | String typeSignature = signature.getSignature(); 826 | if (typeSignature.equals("")) { 827 | return f.getType(); 828 | } 829 | try { 830 | return GenericUtilities.getType(typeSignature); 831 | } catch (ClassCastException classCast) { 832 | return f.getType(); 833 | } 834 | } 835 | return f.getType(); 836 | } 837 | 838 | // gets the full method return type including generic types 839 | private Type getReturnType(Method m) { 840 | Signature signature = this.getSignature(m); 841 | if (signature != null) { 842 | Matcher matcher = methodSignature.matcher(signature.getSignature()); 843 | if (matcher.matches()) { 844 | String returnSignature = matcher.group(2); 845 | if (isVoid.matcher(returnSignature).matches()) { 846 | return m.getReturnType(); // returning void 847 | } 848 | return GenericUtilities.getType(returnSignature); 849 | } 850 | } 851 | return m.getReturnType(); 852 | } 853 | 854 | private void writeMethods(Set methodsSet) { 855 | for (String m : methodsSet) { 856 | sbContent.appendln(m); 857 | } 858 | } 859 | 860 | private void cacheMethodBySignature(Method m) { 861 | String methodName = getMethodFullSignature(m); 862 | if (!mapNameMethod.containsKey(methodName)) { 863 | mapNameMethod.put(methodName, m); 864 | } 865 | } 866 | 867 | private void loadBaseMethods(JavaClass clazz) { 868 | baseMethodNames = new HashSet(); 869 | baseMethods = new ArrayList(); 870 | 871 | JavaClass currClass = getSuperClass(clazz); 872 | 873 | if (currClass != null) { 874 | 875 | //get all base methods and method names 876 | while (true && currClass != null) { 877 | boolean isJavaLangObject = currClass.getClassName().equals(DtsApi.JavaLangObject); 878 | 879 | for (Method m : currClass.getMethods()) { 880 | if (!m.isSynthetic() && (m.isPublic() || m.isProtected())) { 881 | // don't write empty constructor typings for java objects 882 | if (isJavaLangObject && isConstructor(m)) { 883 | continue; 884 | } 885 | 886 | baseMethods.add(m); 887 | baseMethodNames.add(m.getName()); 888 | } 889 | } 890 | 891 | if (isJavaLangObject) { 892 | break; 893 | } 894 | 895 | String scn = currClass.getSuperclassName(); 896 | JavaClass baseClass = ClassRepo.findClass(scn); 897 | assert baseClass != null : "baseClass=" + currClass.getClassName() + " scn=" + scn; 898 | currClass = baseClass; 899 | } 900 | } 901 | } 902 | 903 | private JavaClass getSuperClass(JavaClass clazz) { 904 | if (clazz.getClassName().equals(DtsApi.JavaLangObject)) { 905 | return null; 906 | } 907 | 908 | String scn = clazz.getSuperclassName(); 909 | String override = this.superOverrides.get(clazz.getClassName()); 910 | if (override != null) { 911 | scn = override; 912 | } 913 | 914 | if (scn.equals("") || scn == null) { 915 | return null; 916 | } 917 | JavaClass currClass = ClassRepo.findClass(scn); 918 | return currClass; 919 | } 920 | 921 | private String getMethodFullSignature(Method m) { 922 | String sig = m.getName() + m.getSignature(); 923 | return sig; 924 | } 925 | 926 | private boolean isConstructor(Method m) { 927 | return m.getName().equals(""); 928 | } 929 | 930 | private String getMethodName(Method m) { 931 | String name = m.getName(); 932 | 933 | if (isConstructor(m)) { 934 | name = "constructor"; 935 | } 936 | 937 | if (!jsFieldPattern.matcher(name).matches()) { 938 | name = "\"" + name + "\""; 939 | } 940 | 941 | return name; 942 | } 943 | 944 | private String getMethodParamSignature(JavaClass clazz, TypeDefinition typeDefinition, Method m) { 945 | LocalVariableTable table = m.getLocalVariableTable(); 946 | LocalVariable[] variables = table != null ? table.getLocalVariableTable() : null; 947 | 948 | StringBuilder sb = new StringBuilder(); 949 | sb.append("("); 950 | int idx = 0; 951 | for (Type type : this.getArgumentTypes(m)) { 952 | if (idx > 0) { 953 | sb.append(", "); 954 | } 955 | 956 | int localVarIndex = m.isStatic() ? idx : idx + 1; // skip "this" variable name 957 | LocalVariable localVariable = variables != null && variables.length > localVarIndex 958 | ? variables[localVarIndex] 959 | : null; 960 | 961 | if (localVariable != null) { 962 | String name = localVariable.getName(); 963 | if (reservedJsKeywords.contains(name)) { 964 | System.out.println(String.format("Appending _ to reserved JS keyword %s", name)); 965 | sb.append(name + "_"); 966 | } else { 967 | sb.append(name); 968 | } 969 | } else { 970 | // interface declarations will fallback to paramN since they don't have names in the bytecode 971 | sb.append("param"); 972 | sb.append(idx); 973 | } 974 | idx++; 975 | sb.append(": "); 976 | 977 | String paramTypeName = getTypeScriptTypeFromJavaType(type, typeDefinition); 978 | 979 | // TODO: Pete: 980 | if (paramTypeName.startsWith("java.util.function")) { 981 | sb.append("any /* " + paramTypeName + "*/"); 982 | } else { 983 | addReference(type); 984 | sb.append(paramTypeName); 985 | } 986 | } 987 | sb.append(")"); 988 | String sig = sb.toString(); 989 | return sig; 990 | } 991 | 992 | //field related 993 | private void processField(Field f, JavaClass clazz, TypeDefinition typeDefinition) { 994 | String fieldName = f.getName(); 995 | 996 | if (shouldIgnoreMember(fieldName)) return; 997 | 998 | // 999 | // handle member names that conflict with an inner class. For example: 1000 | // 1001 | // class OuterClass { 1002 | // public static InnerClass: OuterClass.InnerClass; 1003 | // 1004 | // class InnerClass {} 1005 | // } 1006 | // 1007 | // the static field on the OuterClass will have a field type of OuterClass$InnerClass 1008 | // which we can check for and skip writing the static field to the definitions 1009 | // since typescript cannot handle this scenario well. 1010 | // 1011 | 1012 | // the name of the field eg. InnerClass 1013 | String name = f.getName(); 1014 | 1015 | // the type of the field eg. OuterClass$InnerClass 1016 | String fieldTypeString = this.getFieldType(f).toString(); 1017 | 1018 | // we check if the name matches OuterClass (which we are currently in) + "$" + InnerClass 1019 | if (fieldTypeString.equals(clazz.getClassName() + "$" + name)) { 1020 | return; 1021 | } 1022 | 1023 | String tabs = getTabs(this.indent + 1); 1024 | sbContent.append(tabs + "public "); 1025 | if (f.isStatic()) { 1026 | sbContent.append("static "); 1027 | } 1028 | 1029 | if (!jsFieldPattern.matcher(name).matches()) { 1030 | name = "\"" + name + "\""; 1031 | } 1032 | 1033 | sbContent.append(name + ": " + getTypeScriptTypeFromJavaType(this.getFieldType(f), typeDefinition)); 1034 | if (f.getConstantValue() != null) { 1035 | sbContent.appendln(" = " + f.getConstantValue() + ";"); 1036 | } else { 1037 | sbContent.appendln(";"); 1038 | 1039 | } 1040 | } 1041 | 1042 | private void addClassField(JavaClass clazz) { 1043 | String tabs = getTabs(this.indent + 1); 1044 | sbContent.append(String.format("%spublic static class: java.lang.Class<%s>;\n", tabs, clazz.getClassName().replace("$", "."))); 1045 | } 1046 | 1047 | private boolean isPrimitiveTSType(String tsType) { 1048 | switch (tsType) { 1049 | case "void": 1050 | case "string": 1051 | case "boolean": 1052 | case "number": 1053 | return true; 1054 | default: 1055 | return false; 1056 | } 1057 | } 1058 | 1059 | private String getTypeScriptTypeFromJavaType(Type type, TypeDefinition typeDefinition) { 1060 | String tsType; 1061 | String typeSig = type.getSignature(); 1062 | 1063 | switch (typeSig) { 1064 | case "V": 1065 | tsType = "void"; 1066 | break; 1067 | case "C": 1068 | tsType = "string"; 1069 | break; 1070 | case "Z": 1071 | tsType = "boolean"; 1072 | break; 1073 | case "B": 1074 | case "S": 1075 | case "I": 1076 | case "J": 1077 | case "F": 1078 | case "D": 1079 | tsType = "number"; 1080 | break; 1081 | case "Ljava/lang/CharSequence;": 1082 | case "Ljava/lang/String;": 1083 | tsType = "string"; 1084 | break; 1085 | default: 1086 | StringBuilder sb = new StringBuilder(); 1087 | convertToTypeScriptType(type, typeDefinition, sb); 1088 | tsType = sb.toString(); 1089 | 1090 | if (tsType.startsWith("java.util.function") || isPrivateGoogleApiClass(tsType)) { 1091 | tsType = "any /* " + tsType + "*/"; 1092 | } 1093 | } 1094 | 1095 | return tsType; 1096 | } 1097 | 1098 | private void convertToTypeScriptType(Type type, TypeDefinition typeDefinition, StringBuilder tsType) { 1099 | boolean isPrimitive = type instanceof BasicType; 1100 | boolean isArray = type instanceof ArrayType; 1101 | boolean isObjectType = type instanceof ObjectType; 1102 | boolean isGenericObjectType = type instanceof GenericObjectType; 1103 | 1104 | if (isPrimitive) { 1105 | if (type.equals(Type.BOOLEAN)) { 1106 | tsType.append("boolean"); 1107 | } else if (type.equals(Type.BYTE) || type.equals(Type.SHORT) 1108 | || type.equals(Type.INT) || type.equals(Type.LONG) 1109 | || type.equals(Type.FLOAT) || type.equals(Type.DOUBLE)) { 1110 | tsType.append("number"); 1111 | } else if (type.equals(Type.CHAR)) { 1112 | tsType.append("string"); 1113 | } else { 1114 | throw new RuntimeException("Unexpected type=" + type.getSignature()); 1115 | } 1116 | } else if (isArray) { 1117 | tsType.append("androidNative.Array<"); 1118 | Type elementType = ((ArrayType) type).getElementType(); 1119 | useAnyInsteadOfJavaLangObject(elementType, typeDefinition, tsType); 1120 | tsType.append(">"); 1121 | } else if (type.equals(Type.STRING)) { 1122 | tsType.append("string"); 1123 | } else if (isObjectType) { 1124 | if (isGenericObjectType) { 1125 | GenericObjectType genericObjectType = (GenericObjectType) type; 1126 | String genericVariable = genericObjectType.getVariable(); 1127 | if (genericVariable != null && isWordPattern.matcher(genericVariable).matches()) { 1128 | if (typeDefinition != null && typeDefinition.getGenericDefinitions() != null 1129 | && typeDefinition.getGenericDefinitions().stream() 1130 | .filter(definition -> definition.getLabel().equals(genericVariable)).count() > 0 1131 | && ((ObjectType) typeDefinition.getParent()).getClassName().equals(DtsApi.JavaLangObject)) { 1132 | tsType.append(genericObjectType.getVariable()); 1133 | addReference(type); 1134 | return; 1135 | } 1136 | } 1137 | } 1138 | ObjectType objType = (ObjectType) type; 1139 | String typeName = objType.getClassName(); 1140 | if (typeName.contains("$")) { 1141 | typeName = typeName.replaceAll("\\$", "\\."); 1142 | } 1143 | 1144 | if (this.typeOverrides.containsKey(typeName)) { 1145 | typeName = this.typeOverrides.get(typeName); 1146 | } 1147 | 1148 | if (!typeBelongsInCurrentTopLevelNamespace(typeName) && !typeName.startsWith("java.util.function.") && !isPrivateGoogleApiClass(typeName)) { 1149 | tsType.append(getAliasedClassName(typeName)); 1150 | } else { 1151 | tsType.append(typeName); 1152 | } 1153 | 1154 | if (type instanceof GenericObjectType) { 1155 | GenericObjectType genericType = (GenericObjectType) type; 1156 | if (genericType.getNumParameters() > 0) { 1157 | tsType.append("<"); 1158 | for (ReferenceType refType : genericType.getParameters()) { 1159 | useAnyInsteadOfJavaLangObject(refType, typeDefinition, tsType); 1160 | tsType.append(','); 1161 | } 1162 | tsType.deleteCharAt(tsType.lastIndexOf(",")); 1163 | tsType.append(">"); 1164 | } 1165 | } 1166 | 1167 | addReference(type); 1168 | } else { 1169 | throw new RuntimeException("Unhandled type=" + type.getSignature()); 1170 | } 1171 | } 1172 | 1173 | private void useAnyInsteadOfJavaLangObject(Type refType, TypeDefinition typeDefinition, StringBuilder tsType) { 1174 | // if (refType instanceof ObjectType) { 1175 | // ObjectType currentType = (ObjectType)refType; 1176 | // if (currentType.getClassName().equals(DtsApi.JavaLangObject)) { 1177 | // tsType.append("any"); 1178 | // return; 1179 | // } 1180 | // } 1181 | this.convertToTypeScriptType(refType, typeDefinition, tsType); 1182 | } 1183 | 1184 | private void addReference(Type type) { 1185 | boolean isObjectType = type instanceof ObjectType; 1186 | if (isObjectType) { 1187 | ObjectType objType = (ObjectType) type; 1188 | String typeName = objType.getClassName(); 1189 | if (!typeName.equals(currentFileClassname)) { 1190 | boolean isNested = typeName.contains("$"); 1191 | if (!isNested) { 1192 | references.add(typeName); 1193 | } 1194 | } 1195 | } 1196 | } 1197 | 1198 | private List getMembers(JavaClass javaClass, List interfaces) { 1199 | Set methodNames = new HashSet<>(); 1200 | ArrayList members = new ArrayList<>(); 1201 | 1202 | List allInterfacesMethods = getAllInterfacesMethods(interfaces); 1203 | List methods = new ArrayList<>(); 1204 | methods.addAll(Arrays.asList(javaClass.getMethods())); 1205 | methods.addAll(allInterfacesMethods); 1206 | 1207 | for (Method m : methods) { 1208 | if ((m.isPublic() || m.isProtected()) && !m.isSynthetic()) { 1209 | members.add(m); 1210 | methodNames.add(m.getName()); 1211 | } 1212 | } 1213 | for (Field f : javaClass.getFields()) { 1214 | if ((f.isPublic() || f.isProtected()) && !f.isSynthetic() && !methodNames.contains(f.getName())) { 1215 | members.add(f); 1216 | } 1217 | } 1218 | 1219 | return members; 1220 | } 1221 | 1222 | // HELPER METHODS 1223 | private boolean isNested(JavaClass javaClass) { 1224 | boolean isNested = javaClass.getClassName().contains("$"); 1225 | return isNested; 1226 | } 1227 | 1228 | private String getSimpleClassname(JavaClass javaClass) { 1229 | String[] parts = javaClass.getClassName().replace('$', '.') 1230 | .split("\\."); 1231 | return parts[parts.length - 1]; 1232 | } 1233 | 1234 | private String getFullClassNameConcatenated(JavaClass javaClass) { 1235 | String fullName = javaClass.getClassName().replaceAll("[$.]", ""); 1236 | return fullName; 1237 | } 1238 | 1239 | // gets the suffix like > 1240 | private String getTypeSuffix(String fullClassName, TypeDefinition typeDefinition, String extendsLine) { 1241 | if (typeDefinition == null) { 1242 | return ""; 1243 | } 1244 | List genericDefinitions = typeDefinition.getGenericDefinitions(); 1245 | if (genericDefinitions != null) { 1246 | List parts = new ArrayList<>(); 1247 | String genericClassName = fullClassName.replace("$", "."); 1248 | 1249 | // remove the current class name if it already exists 1250 | generics = generics.stream().filter(generic -> generic.x != genericClassName).collect(Collectors.toList()); 1251 | 1252 | generics.add(new Tuple<>(fullClassName.replace("$", "."), genericDefinitions.size())); 1253 | for (TypeDefinition.GenericDefinition definition : genericDefinitions) { 1254 | ObjectType genericObjectType = (ObjectType) definition.getType(); 1255 | String baseClassName = getAliasedClassName(genericObjectType.getClassName()); 1256 | String resultType = definition.getType().toString(); 1257 | String typeToExtend = resultType.replace(genericObjectType.getClassName(), baseClassName); 1258 | //parts.add(String.format("%s extends %s", definition.getLabel(), typeToExtend)); 1259 | parts.add(definition.getLabel()); 1260 | } 1261 | return "<" + String.join(", ", parts) + "> "; 1262 | } else { 1263 | return ""; 1264 | } 1265 | } 1266 | 1267 | private String getTabs(int count) { 1268 | String tabs = new String(new char[count]).replace("\0", "\t"); 1269 | return tabs; 1270 | } 1271 | 1272 | private boolean isPrivateGoogleApiMember(String memberName) { 1273 | return memberName.startsWith("zz"); 1274 | } 1275 | 1276 | private boolean isObfuscated(String memberName) { 1277 | if (this.ignoreObfuscatedNameLength > 0) { 1278 | 1279 | // basic test to remove obfuscated classes 1280 | return memberName.length() <= this.ignoreObfuscatedNameLength && !memberName.equals("R"); 1281 | } 1282 | return false; 1283 | } 1284 | 1285 | private boolean shouldIgnoreMember(String memberName) { 1286 | return isPrivateGoogleApiMember(memberName) || isObfuscated(memberName); 1287 | } 1288 | 1289 | private boolean isPrivateGoogleApiClass(String name) { 1290 | String[] classNameParts = name.replace('$', '.').split("\\."); 1291 | return classNameParts.length > 0 && classNameParts[classNameParts.length - 1].startsWith("zz"); 1292 | } 1293 | 1294 | private void overrideFieldComparator() { 1295 | BCELComparator cmp = Field.getComparator(); 1296 | 1297 | Field.setComparator(new BCELComparator<>() { 1298 | @Override 1299 | public boolean equals(Field o, Field o1) { 1300 | return o.getName().equals(o1.getName()); 1301 | } 1302 | 1303 | @Override 1304 | public int hashCode(Field o) { 1305 | return cmp.hashCode(o); 1306 | } 1307 | }); 1308 | } 1309 | 1310 | private void setOverrides() { 1311 | this.setTypeOverrides(); 1312 | this.setExtendsOverrides(); 1313 | this.setSuperOverrides(); 1314 | } 1315 | 1316 | private void setExtendsOverrides() { 1317 | // here we put extends overrides to avoid manual work to fix the generated .d.ts file 1318 | this.extendsOverrides.put("android.renderscript.ProgramFragmentFixedFunction$Builder", 1319 | "android.renderscript.Program.BaseProgramBuilder"); // android-17 1320 | this.extendsOverrides.put("android.renderscript.ProgramVertexFixedFunction$Builder", 1321 | "android.renderscript.ProgramVertex.Builder"); // android-17 1322 | this.extendsOverrides.put("android.support.v4.app.JobIntentService$JobServiceEngineImpl", 1323 | "android.support.v4.app.JobIntentService.CompatJobEngine"); // android-support 1324 | this.extendsOverrides.put("com.telerik.widget.autocomplete.RadAutoCompleteTextView$SavedState", 1325 | "com.telerik.widget.primitives.panels.RadScrollView.SavedState"); // nativescript-ui-autocomplete 1326 | 1327 | this.extendsOverrides.put("androidx.core.app.JobIntentService$JobServiceEngineImpl", 1328 | "androidx.core.app.JobIntentService.CompatJobEngine"); // androidx 1329 | 1330 | this.extendsOverrides.put("androidx.core.text.PrecomputedTextCompat", "android.text.Spannable"); // androidx 1331 | this.extendsOverrides.put("androidx.collection.ArrayMap", "java.util.Map"); // androidx 1332 | } 1333 | 1334 | private void setSuperOverrides() { 1335 | // here we put super overrides 1336 | this.superOverrides.put("android.support.v4.view.GestureDetectorCompat", "android.view.GestureDetector"); // android-support 1337 | } 1338 | 1339 | private void setTypeOverrides() { 1340 | // here we put type overrides if we want to change return types of java.lang.Object for instance to any 1341 | this.typeOverrides.put(DtsApi.JavaLangObject, "any"); 1342 | this.typeOverrides.put("java.lang.CharSequence", "string"); 1343 | this.typeOverrides.put("android.view.View.AccessibilityDelegate", "any"); 1344 | this.typeOverrides.put("android.view.PointerIcon", "any"); 1345 | this.typeOverrides.put("android.app.Person", "any"); 1346 | this.typeOverrides.put("android.app.RemoteAction", "any"); 1347 | this.typeOverrides.put("android.os.PersistableBundle", "any"); 1348 | this.typeOverrides.put("android.os.LocaleList", "any"); 1349 | this.typeOverrides.put("android.text.TextDirectionHeuristic", "any"); 1350 | this.typeOverrides.put("android.util.SizeF", "any"); 1351 | this.typeOverrides.put("android.util.Size", "any"); 1352 | } 1353 | 1354 | private void setGlobalAliases() { 1355 | // here we put extends overrides to avoid manual work to fix the generated .d.ts file 1356 | globalAliases.put("android", "globalAndroid"); 1357 | } 1358 | 1359 | private List getIgnoredNamespaces() { 1360 | // for some reason these namespaces are references but not existing, so we are replacing all types from these namespaces with "any" 1361 | List result = new ArrayList<>(); 1362 | 1363 | result.add("kotlin"); 1364 | result.add("org.jetbrains"); 1365 | result.add("org.intellij"); 1366 | 1367 | result.add("android.app.job"); 1368 | result.add("android.app.SharedElementCallback"); 1369 | result.add("android.arch"); 1370 | result.add("android.content.pm.ShortcutInfo"); 1371 | result.add("android.graphics.drawable.Icon"); 1372 | result.add("android.graphics.Outline"); 1373 | result.add("android.view.SearchEvent"); 1374 | result.add("android.view.KeyboardShortcutGroup"); 1375 | result.add("android.view.ViewStructure"); 1376 | result.add("android.view.textclassifier"); 1377 | result.add("android.telephony.mbms"); 1378 | result.add("android.text.PrecomputedText"); 1379 | result.add("android.media.browse"); 1380 | result.add("android.media.session"); 1381 | result.add("android.media.AudioAttributes"); 1382 | result.add("android.media.MediaMetadata"); 1383 | result.add("android.media.Rating"); 1384 | result.add("android.service.media"); 1385 | result.add("android.print"); 1386 | result.add("java.util.function"); 1387 | 1388 | result.add("com.tom_roush.pdfbox.pdmodel.common.function"); // com.tom_roush:pdfbox-android:1.8.10.0 1389 | 1390 | return result; 1391 | } 1392 | 1393 | private boolean isIgnoredNamespace() { 1394 | String[] namespaceOnlyParts = Arrays.copyOf(namespaceParts, namespaceParts.length - 1); 1395 | String namespace = String.join(".", namespaceOnlyParts); 1396 | for (String ignoredNamespace : getIgnoredNamespaces()) { 1397 | if (ignoredNamespace.equals(namespace) || namespace.startsWith(ignoredNamespace + ".")) { 1398 | return true; 1399 | } 1400 | } 1401 | return false; 1402 | } 1403 | } 1404 | --------------------------------------------------------------------------------