| /* |
| * Copyright 2012-present Facebook, Inc. |
| * |
| * 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.facebook.buck.util; |
| |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Throwables; |
| import com.google.common.collect.Lists; |
| import com.google.common.io.Files; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.InputSource; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import javax.xml.namespace.NamespaceContext; |
| import javax.xml.xpath.XPath; |
| import javax.xml.xpath.XPathConstants; |
| import javax.xml.xpath.XPathExpression; |
| import javax.xml.xpath.XPathExpressionException; |
| import javax.xml.xpath.XPathFactory; |
| |
| public class DefaultAndroidManifestReader implements AndroidManifestReader{ |
| |
| /** |
| * XPath expression to retrieve the names of activities with an intent-filter that gets them |
| * to show up in the launcher. |
| */ |
| private static final String XPATH_LAUNCHER_ACTIVITIES = |
| "/manifest/application/*[self::activity or self::activity-alias]"+ |
| " [intent-filter[action/@android:name='android.intent.action.MAIN' and " + |
| " category/@android:name='android.intent.category.LAUNCHER']]" + |
| " /@android:name"; |
| |
| /** |
| * XPath expression to get the package. |
| * For a manifest as {@code <manifest package="com.facebook.katana" />}, this results in |
| * {@code com.facebook.katana}. |
| */ |
| private static final String XPATH_PACKAGE = "/manifest/@package"; |
| |
| private final XPathExpression packageExpression; |
| private final XPathExpression launchableActivitiesExpression; |
| private final Document doc; |
| |
| private DefaultAndroidManifestReader(InputSource src) throws IOException { |
| try { |
| // Parse the XML. |
| doc = XmlDomParser.parse(src, true); |
| |
| // Compile the XPath expressions. |
| XPath xPath = XPathFactory.newInstance().newXPath(); |
| xPath.setNamespaceContext(androidNamespaceContext); |
| launchableActivitiesExpression = xPath.compile(XPATH_LAUNCHER_ACTIVITIES); |
| packageExpression = xPath.compile(XPATH_PACKAGE); |
| } catch (XPathExpressionException e) { |
| throw Throwables.propagate(e); |
| } |
| } |
| |
| @Override |
| public List<String> getLauncherActivities() { |
| try { |
| NodeList nodes; |
| nodes = (NodeList) launchableActivitiesExpression.evaluate(doc, XPathConstants.NODESET); |
| |
| List<String> activities = Lists.newArrayList(); |
| for (int i = 0; i < nodes.getLength(); i++) { |
| activities.add(nodes.item(i).getTextContent()); |
| } |
| return activities; |
| |
| } catch (XPathExpressionException e) { |
| throw Throwables.propagate(e); |
| } |
| } |
| |
| @Override |
| public String getPackage() { |
| try { |
| return (String) packageExpression.evaluate(doc, XPathConstants.STRING); |
| } catch (XPathExpressionException e) { |
| throw Throwables.propagate(e); |
| } |
| } |
| |
| /** |
| * This allows querying the AndroidManifest for e.g. attributes like android:name using XPath |
| */ |
| private static NamespaceContext androidNamespaceContext = new NamespaceContext() { |
| @Override |
| public Iterator<String> getPrefixes(String namespaceURI) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public String getPrefix(String namespaceURI) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public String getNamespaceURI(String prefix) { |
| if (prefix.equals("android")) { |
| return "http://schemas.android.com/apk/res/android"; |
| } else { |
| throw new IllegalArgumentException(); |
| } |
| } |
| }; |
| |
| /** |
| * Parses an XML given via its path and returns an {@link AndroidManifestReader} for it. |
| * @param path path to an AndroidManifest.xml file |
| * @return an {@code AndroidManifestReader} for {@code path} |
| * @throws IOException |
| */ |
| public static AndroidManifestReader forPath(String path) throws IOException { |
| File androidManifestXml = new File(path); |
| Reader reader = Files.newReader(androidManifestXml, Charsets.UTF_8); |
| return forReader(reader); |
| } |
| |
| /** |
| * Parses an XML given as a string and returns an {@link AndroidManifestReader} for it. |
| * @param xmlString a string representation of an XML document |
| * @return an {@code AndroidManifestReader} for the XML document |
| * @throws IOException |
| */ |
| public static AndroidManifestReader forString(String xmlString) throws IOException { |
| return forReader(new StringReader(xmlString)); |
| } |
| |
| private static AndroidManifestReader forReader(Reader reader) throws IOException { |
| return new DefaultAndroidManifestReader(new InputSource(reader)); |
| } |
| |
| } |