1import org.gradle.api.DefaultTask
2import org.gradle.api.tasks.Input
3import org.gradle.api.tasks.OutputFile
4import org.gradle.api.tasks.TaskAction
5import org.w3c.dom.Document
6import org.w3c.dom.Element
7import org.w3c.dom.Node
8import org.w3c.dom.NodeList
9
10import javax.xml.parsers.DocumentBuilder
11import javax.xml.parsers.DocumentBuilderFactory
12import javax.xml.transform.TransformerFactory
13import javax.xml.transform.dom.DOMSource
14import javax.xml.transform.stream.StreamResult
15
16import static org.gradle.api.internal.lambdas.SerializableLambdas.spec;
17
18/**
19 * Gradle task to update sources link in sdk
20 */
21class SdkSourceUpdaterTask extends DefaultTask  {
22
23    private static final JDK_TABLE_PATH = "out/gradle/AndroidStudio/config/options/jdk.table.xml"
24    private static final JAVA_CORE_PATH = "frameworks/base/core/java"
25    private static final JAVA_GRAPHICS_PATH = "frameworks/base/graphics/java"
26
27    @Input
28    String androidRoot
29
30    public SdkSourceUpdaterTask() {
31        setOnlyIf("Sdk file is missing", spec(task -> new File(androidRoot, JDK_TABLE_PATH).exists()))
32        outputs.upToDateWhen {
33            String sdkDefLines = new File(androidRoot, JDK_TABLE_PATH).text
34            String corePath = new File(androidRoot, JAVA_CORE_PATH).getCanonicalPath()
35            String graphicsPath = new File(androidRoot, JAVA_GRAPHICS_PATH).getCanonicalPath()
36            return sdkDefLines.contains(corePath) && sdkDefLines.contains(graphicsPath)
37        }
38    }
39
40    @OutputFile
41    public File getOutputFile() {
42        return new File(androidRoot, JDK_TABLE_PATH)
43    }
44
45    @TaskAction
46    void execute() throws Exception {
47        File sdkDef = new File(androidRoot, JDK_TABLE_PATH)
48        if (!sdkDef.exists()) {
49            throw new IllegalStateException("Sdk config file not found at " + sdkDef);
50        }
51
52        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
53        DocumentBuilder db = dbf.newDocumentBuilder();
54        Document doc = db.parse(sdkDef);
55
56        NodeList list = doc.getElementsByTagName("jdk");
57        for (int i = 0; i < list.getLength(); i++) {
58            Node node = list.item(i);
59            if (node.getNodeType() == Node.ELEMENT_NODE) {
60                Element element = (Element) node;
61                Element homePath = findFirstElement(element, "homePath");
62                if (homePath == null) {
63                    continue;
64                }
65
66                String pathValue = homePath.getAttribute("value");
67                if (pathValue == null || pathValue.isBlank()) {
68                    continue;
69                }
70
71
72                if (!pathValue.contains("out/gradle/MockSdk")) {
73                    continue;
74                }
75
76                // Found the right SDK
77                Element sourcePath = findFirstElement(element, "sourcePath");
78                if (sourcePath == null) {
79                    // TODO: Add source path
80                    continue;
81                }
82
83                while (sourcePath.hasChildNodes())
84                    sourcePath.removeChild(sourcePath.getFirstChild());
85
86                // Create root
87                Element el = createRoot(doc, "type", "composite");
88                sourcePath.appendChild(el);
89
90                // Create paths
91                el.appendChild(createRoot(doc, "type", "simple", "url", "file://" + new File(androidRoot, JAVA_CORE_PATH).getCanonicalPath()));
92                el.appendChild(createRoot(doc, "type", "simple", "url", "file://" + new File(androidRoot, JAVA_GRAPHICS_PATH).getCanonicalPath()));
93            }
94        }
95
96        // Write the xml
97        TransformerFactory.newInstance().newTransformer()
98                .transform(new DOMSource(doc), new StreamResult(sdkDef))
99
100        System.out.println("======================================")
101        System.out.println("======================================")
102        System.out.println("       Android sources linked")
103        System.out.println("Restart IDE for changes to take effect")
104        System.out.println("======================================")
105        System.out.println("======================================")
106    }
107
108    private Element createRoot(Document doc, String... attrs) {
109        Element el = doc.createElement("root");
110        for (int i = 0; i < attrs.length; i += 2) {
111            el.setAttribute(attrs[i], attrs[i + 1]);
112        }
113        return el;
114    }
115
116    private Element findFirstElement(Element node, String tag) {
117        NodeList paths = node.getElementsByTagName(tag);
118        if (paths.getLength() < 1) {
119            return null;
120        }
121        Node n = paths.item(0);
122        if (n.getNodeType() != Node.ELEMENT_NODE) {
123            return null;
124        }
125        return (Element) n;
126    }
127}