aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/android/tools/internal/artifacts/PomHandler.java
blob: 372380df6d6bce92dd3f177e766f8be394de3510 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.internal.artifacts;

import com.google.common.io.Closeables;
import org.gradle.api.artifacts.ModuleIdentifier;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * Class able to parse a POM file, and provide some information from it:
 * - Is the artifact relocated (and what's the relocation artifact)
 * - parent POM
 * - packaging
 */
class PomHandler {

    private final File pomFile;
    private Document document = null;

    private final static class FakeModuleVersionIdentifier implements ModuleVersionIdentifier {

        private final String group;
        private final String name;
        private final String version;

        FakeModuleVersionIdentifier(String group, String name, String version) {

            this.group = group;
            this.name = name;
            this.version = version;
        }

        @Override
        public String getVersion() {
            return version;
        }

        @Override
        public String getGroup() {
            return group;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public ModuleIdentifier getModule() {
            throw new UnsupportedOperationException();
        }

        @Override
        public String toString() {
            return getGroup() + ":" + getName() + ":" + getVersion();
        }
    }

    PomHandler(File pomFile) {
        this.pomFile = pomFile;
    }

    ModuleVersionIdentifier getRelocation() throws IOException {
        Document document = getDocument();
        Node rootNode = document.getDocumentElement();

        Node node = findNode(rootNode, "distributionManagement");
        if (node == null) {
            return null;
        }

        node = findNode(node, "relocation");
        if (node == null) {
            return null;
        }

        // ok there is a relocation. Let's find out the current artifact address
        ModuleVersionIdentifier original = readArtifactAddress(rootNode);

        // now read the relocation info
        ModuleVersionIdentifier relocation = readArtifactAddress(node);

        // merge them in case only part of the address is changed
        return new FakeModuleVersionIdentifier(
                relocation.getGroup() != null ? relocation.getGroup() : original.getGroup(),
                relocation.getName() != null ? relocation.getName() : original.getName(),
                relocation.getVersion() != null ? relocation.getVersion() : original.getVersion());
    }

    private ModuleVersionIdentifier readArtifactAddress(Node parentNode) {
        String group = null;
        String name = null;
        String version = null;

        Node node = findNode(parentNode, "groupId");
        if (node != null) {
            group = getTextNode(node);
        }

        node = findNode(parentNode, "artifactId");
        if (node != null) {
            name = getTextNode(node);
        }

        node = findNode(parentNode, "version");
        if (node != null) {
            version = getTextNode(node);
        }

        return new FakeModuleVersionIdentifier(group, name, version);
    }

    ModuleVersionIdentifier getParentPom() throws IOException {
        Document document = getDocument();
        Node rootNode = document.getDocumentElement();

        Node node = findNode(rootNode, "parent");
        if (node == null) {
            return null;
        }

        // there is a parent. Look for the rest of the nodes.
        final Node groupId = findNode(node, "groupId");
        final Node artifactId = findNode(node, "artifactId");
        final Node version = findNode(node, "version");

        if (groupId == null || artifactId == null || version == null) {
            return null;
        }

        return new FakeModuleVersionIdentifier(
                getTextNode(groupId),
                getTextNode(artifactId),
                getTextNode(version));
    }

    public String getPackaging() throws IOException {
        Document document = getDocument();

        Node rootNode = document.getDocumentElement();

        Node node = findNode(rootNode, "packaging");
        if (node == null) {
            return null;
        }

        return getTextNode(node);
    }

    private Document getDocument() throws IOException {
        if (document == null) {
            document = parseDocument(pomFile);
        }

        return document;
    }

    private Node findNode(Node rootNode, String name) {
        NodeList nodes = rootNode.getChildNodes();

        for (int i = 0, n = nodes.getLength(); i < n; i++) {
            Node node = nodes.item(i);

            if (node.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }

            if (name.equals(node.getLocalName())) {
                return node;
            }
        }

        return null;
    }

    private String getTextNode(Node rootNode) {
        NodeList nodes = rootNode.getChildNodes();

        for (int i = 0, n = nodes.getLength(); i < n; i++) {
            Node node = nodes.item(i);

            if (node.getNodeType() != Node.TEXT_NODE) {
                continue;
            }

            return node.getNodeValue();
        }

        return null;
    }

    /**
     * Loads the DOM for a given file and returns a {@link org.w3c.dom.Document} object.
     * @param file the file to parse
     * @return a Document object.
     * @throws java.io.IOException
     */
    static Document parseDocument(File file) throws IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file));
        InputSource is = new InputSource(stream);
        factory.setNamespaceAware(true);
        factory.setValidating(false);
        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            return builder.parse(is);
        } catch (ParserConfigurationException e) {
            throw new IOException(e);
        } catch (SAXException e) {
            throw new IOException(e);
        } finally {
            Closeables.close(stream, true /* swallowIOException */);
        }
    }
}