Package com.arconsis.android.datarobot.manifest

Source Code of com.arconsis.android.datarobot.manifest.AndroidManifestProcessor$ManifestBuilder

/*
* Copyright (C) 2014 The Datarobot Authors
*
* 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.arconsis.android.datarobot.manifest;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.arconsis.android.datarobot.processor.ContentProviderData;

/**
* Allows parsing and changing data of the AndroidManifest.xml
*
* @author Alexander Frank
* @author Falk Appel
*/
public class AndroidManifestProcessor {

  private final File manifestFile;

  public AndroidManifestProcessor(final File manifestFile) {
    this.manifestFile = manifestFile;
  }

  public AndroidManifest parse() throws Exception {
    InputStream manifestStream = new FileInputStream(manifestFile);

    try {
      Document document = createDocument(manifestStream);
      Element root = document.getDocumentElement();
      String usedPackage = root.getAttribute("package");

      return new AndroidManifest(usedPackage, findExistingContentProvider(root));
    } finally {
      manifestStream.close();
    }
  }

  private List<ContentProviderData> findExistingContentProvider(final Element root) {
    NodeList nodesUnderApplication = getApplicationNodeFromRoot(root).getChildNodes();

    List<ContentProviderData> data = new ArrayList<ContentProviderData>(nodesUnderApplication.getLength());

    for (int i = 0; i < nodesUnderApplication.getLength(); i++) {
      Node node = nodesUnderApplication.item(i);
      if (node.getNodeName().equals("provider")) {
        NamedNodeMap attributes = node.getAttributes();
        String providerName = attributes.getNamedItem("android:name").getTextContent();
        String authority = attributes.getNamedItem("android:authorities").getTextContent();
        Node exportedAttribute = attributes.getNamedItem("android:exported");
        boolean exported = false;
        if (exportedAttribute != null) {
          exported = Boolean.parseBoolean(exportedAttribute.getTextContent());
        }
        data.add(new ContentProviderData(providerName, authority, exported));
      }
    }
    return data;
  }

  private static Document createDocument(final InputStream manifestStream) throws Exception {
    DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    Document document = documentBuilder.parse(manifestStream);
    return document;
  }

  private static Node getApplicationNodeFromRoot(final Element root) {
    NodeList applicationTag = root.getElementsByTagName("application");
    if (applicationTag.getLength() != 1) {
      throw new IllegalStateException("Wrong number of application tags found within AndroidManifest.xml. There are " + applicationTag.getLength()
          + " tags");
    }
    return applicationTag.item(0);
  }

  public ManifestBuilder change() throws Exception {
    return new ManifestBuilder(manifestFile, parse());
  }

  public static final class ManifestBuilder {

    private final File manifestFile;
    private final LinkedList<ContentProviderData> addedProviders;
    private final AndroidManifest androidManifest;

    private ManifestBuilder(final File manifestFile, final AndroidManifest androidManifest) {
      this.manifestFile = manifestFile;
      this.androidManifest = androidManifest;
      addedProviders = new LinkedList<ContentProviderData>();
    }

    public ManifestBuilder addProviderIfNotExists(final ContentProviderData contentProviderData) {
      addedProviders.add(contentProviderData);
      return this;
    }

    public void commit() throws Exception {
      Document document = createDocument(new FileInputStream(manifestFile));
      Element root = document.getDocumentElement();
      Node application = getApplicationNodeFromRoot(root);
      boolean addedSomething = addProviderTagsIfNeeded(document, application);
      boolean removedSomething = removeUnusedProviders(application);
      saveToAndroidManifest(document, addedSomething || removedSomething);
    }

    private boolean addProviderTagsIfNeeded(final Document document, final Node application) {
      boolean changesSomething = false;
      for (ContentProviderData added : addedProviders) {
        if (notContainedInProviderList(added, androidManifest.getContentProviders())) {
          Element newProvider = document.createElement("provider");

          newProvider.setAttribute("android:name", added.getCanonicalName());
          newProvider.setAttribute("android:authorities", added.getAuthority());
          newProvider.setAttribute("android:exported", Boolean.toString(added.isExported()));
          newProvider.setAttribute("arconsis:generated", "true");
          application.appendChild(newProvider);

          document.getDocumentElement().setAttribute("xmlns:arconsis", "http://arconsis.com");
          changesSomething = true;
        }
      }
      return changesSomething;
    }

    private boolean removeUnusedProviders(final Node application) {
      boolean changedSomething = false;
      for (ContentProviderData inManifest : androidManifest.getContentProviders()) {
        if (notContainedInProviderList(inManifest, addedProviders)) {
          changedSomething |= removeSingleProvider(inManifest, application);
        }
      }
      return changedSomething;
    }

    private boolean removeSingleProvider(final ContentProviderData inManifest, final Node application) {
      NodeList applicationChildren = application.getChildNodes();
      for (int i = 0; i < applicationChildren.getLength(); i++) {
        Node child = applicationChildren.item(i);
        if (child.getNodeName().equals("provider") && isGenerated(child)) {
          NamedNodeMap attributes = child.getAttributes();
          Node name = attributes.getNamedItem("android:name");
          Node authority = attributes.getNamedItem("android:authorities");
          if (authority.getTextContent().equals(inManifest.getAuthority()) && namesAreEqual(name.getTextContent(), inManifest.getCanonicalName())) {
            application.removeChild(child);
            return true;
          }
        }
      }
      return false;
    }

    private boolean notContainedInProviderList(final ContentProviderData added, final List<ContentProviderData> providers) {
      for (ContentProviderData inManifest : providers) {
        if (inManifest.getAuthority().equals(added.getAuthority()) && namesAreEqual(inManifest.getCanonicalName(), added.getCanonicalName())) {
          return false;
        }
      }
      return true;
    }

    private boolean namesAreEqual(final String providerName, final String otherProviderName) {
      String comparableManifestName = packagedProviderName(providerName);
      String comparableProviderName = packagedProviderName(otherProviderName);
      return comparableManifestName.equals(comparableProviderName);
    }

    private String packagedProviderName(final String name) {
      String packagedName;
      if (name.startsWith(".")) {
        packagedName = androidManifest.getPackageName() + name;
      } else {
        packagedName = name;
      }
      return packagedName;
    }

    private boolean isGenerated(final Node child) {
      NamedNodeMap attributes = child.getAttributes();
      Node generated = attributes.getNamedItem("arconsis:generated");
      if (generated == null) {
        return false;
      }
      return Boolean.parseBoolean(generated.getTextContent());
    }

    private void saveToAndroidManifest(final Document document, final boolean changedSomething) throws TransformerFactoryConfigurationError,
    TransformerConfigurationException, FileNotFoundException, TransformerException {
      if (changedSomething) {

        DOMSource source = new DOMSource(document);

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StreamResult result = new StreamResult(new FileOutputStream(manifestFile));
        transformer.transform(source, result);
      }
    }
  }

}
TOP

Related Classes of com.arconsis.android.datarobot.manifest.AndroidManifestProcessor$ManifestBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.