org.automagic.deps.doctor.editor.PomWriterImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.automagic.deps.doctor.editor.PomWriterImpl.java

Source

/*
 * Copyright 2015 Jose Carrizo
 * Licensed under the Apache License, Version 2.0 
 * See accompanying file LICENSE or get a copy at http://www.apache.org/licenses/LICENSE-2.0
 */
package org.automagic.deps.doctor.editor;

import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.trim;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
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.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.project.MavenProject;
import org.automagic.deps.doctor.TransitiveDependency;
import org.automagic.deps.doctor.Utils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.common.base.Optional;

public class PomWriterImpl implements PomWriter {

    private final Document document;
    private final MavenProject project;
    private final int indentationSize;
    private final boolean addComments;
    private final boolean indentWithTabs;
    private PomWriter parentWriter;

    public PomWriterImpl(MavenProject project, int indentationSize, boolean indentWithTabs, boolean addComments) {
        this.indentationSize = indentationSize;
        this.addComments = addComments;
        this.project = project;
        this.document = Utils.getDocument(project.getFile());
        this.indentWithTabs = indentWithTabs;
    }

    @Override
    public void writeFullDependency(TransitiveDependency dependency) {
        writeDependency("/project", dependency, 2, true);
    }

    @Override
    public void writeDependencyWithoutVersion(TransitiveDependency dependency) {
        writeDependency("/project", dependency, 2, false);
    }

    @Override
    public void setDependencyVersion(TransitiveDependency dependency) {
        setDependencyVersion("/project/dependencies", dependency);
    }

    @Override
    public void setVersionProperty(String propertyName, ArtifactVersion version) {
        Node node = Utils.getNode("/project/properties/" + propertyName, document).get();
        node.setTextContent(version.toString());

    }

    @Override
    public void addExplicitDependencyVersion(TransitiveDependency dependency) {

        Element versionElement = document.createElement("version");
        versionElement.setTextContent(dependency.getVersion().toString());

        Artifact artifact = dependency.getArtifact();
        Optional<Node> dependencyNode = Utils.getDependencyNode(artifact, document, "/project/dependencies");
        addIndentedNode(dependencyNode.get(), versionElement, 3);

    }

    @Override
    public void addDepsMamagement() {

        Node rootNode = Utils.getNode("/project", document).get();
        Element element = document.createElement("dependencyManagement");
        addDummyComment(element);
        addIndentedNode(rootNode, element, 1);
        rootNode = Utils.getNode("/project/dependencyManagement", document).get();
        clearDummyComment(rootNode);
    }

    @Override
    public void writeFullManagedDependency(TransitiveDependency dependency) {
        writeDependency("/project/dependencyManagement", dependency, 3, true);
    }

    @Override
    public void setManagedDependencyVersion(TransitiveDependency dependency) {
        setDependencyVersion("/project/dependencyManagement/dependencies", dependency);
    }

    @Override
    public byte[] saveChanges(boolean omitXmlDeclaration) {
        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            if (omitXmlDeclaration) {
                transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            }
            Result result = new StreamResult(output);
            Source input = new DOMSource(document);
            transformer.transform(input, result);
            return output.toByteArray();
        } catch (TransformerFactoryConfigurationError | TransformerException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void writeDependency(String parent, TransitiveDependency dependency, int indent, boolean full) {

        Optional<Node> node = Utils.getNode(parent + "/dependencies", document);

        Node dependenciesNode;
        if (node.isPresent()) {
            dependenciesNode = node.get();
        } else {
            Node parentNode = Utils.getNode(parent, document).get();
            dependenciesNode = document.createElement("dependencies");
            addDummyComment(dependenciesNode);
            addIndentedNode(parentNode, dependenciesNode, indent - 1);
            dependenciesNode = Utils.getNode(parent + "/dependencies", document).get();
            clearDummyComment(dependenciesNode);
        }

        Artifact artifact = dependency.getArtifact();
        String groupId = trim(artifact.getGroupId());
        String artifactId = trim(artifact.getArtifactId());
        String type = trim(artifact.getType());
        String classifier = trim(artifact.getClassifier());
        String version = dependency.getVersion().toString();

        Node dependencyNode = document.createElement("dependency");

        if (addComments) {
            String comment = getCommentText(dependency);
            dependencyNode.appendChild(document.createComment(comment));
        }

        Node groupIdNode = document.createElement("groupId");
        groupIdNode.setTextContent(groupId);
        dependencyNode.appendChild(groupIdNode);

        Node artifactIdNode = document.createElement("artifactId");
        artifactIdNode.setTextContent(artifactId);
        dependencyNode.appendChild(artifactIdNode);

        if (StringUtils.isNotBlank(type) && !"jar".equals(type)) {
            Node typeNode = document.createElement("type");
            typeNode.setTextContent(type);
            dependencyNode.appendChild(typeNode);
        }
        if (StringUtils.isNotBlank(classifier)) {
            Node classifierNode = document.createElement("classifier");
            classifierNode.setTextContent(classifier);
            dependencyNode.appendChild(classifierNode);
        }

        if (full) {
            Node versionNode = document.createElement("version");
            versionNode.setTextContent(version);
            dependencyNode.appendChild(versionNode);
        }

        addIndentedNode(dependenciesNode, dependencyNode, indent);
    }

    private void addIndentedNode(final Node parent, Node newChild, int indent) {

        Optional<Node> insertionPoint = getInsertionPoint(parent);

        List<Node> indentedList = indent(newChild, indent);
        Collections.reverse(indentedList);

        Node point = insertionPoint.get();
        for (Node node : indentedList) {
            if (insertionPoint.isPresent()) {
                parent.insertBefore(node, point);
                point = node;
            } else {
                parent.appendChild(node);
            }
        }
    }

    private List<Node> indent(Node node, int indent) {

        Node root = document.createElement("dummy");
        StringBuilder sb = new StringBuilder();
        Node wrapper = root;
        for (int i = 1; i < indent; i++) {
            Element element = document.createElement("dummy");
            wrapper.appendChild(element);
            wrapper = element;
            sb.append("/dummy");
        }
        wrapper.appendChild(node);

        Node prettyRoot = Utils.prettyFormat(root, indentationSize, indentWithTabs);

        if (sb.length() != 0) {
            String xpath = sb.substring(1);
            prettyRoot = Utils.getNode(xpath, prettyRoot).get();
        }

        final Optional<Node> point = getInsertionPoint(prettyRoot);
        final List<Node> result = new ArrayList<>();
        final NodeList childNodes = prettyRoot.getChildNodes();

        for (int i = 0; i < childNodes.getLength(); i++) {
            Node item = childNodes.item(i);
            if (point.isPresent() && point.get().equals(item)) {
                break;
            }
            result.add(item);
        }
        return result;
    }

    private Optional<Node> getInsertionPoint(Node node) {

        if (!node.hasChildNodes()) {
            return Optional.absent();
        }

        Node point = null;
        for (int i = node.getChildNodes().getLength() - 1; i >= 0; i--) {
            Node blankNode = node.getChildNodes().item(i);
            if (blankNode.getNodeType() == Node.TEXT_NODE) {
                String nodeValue = blankNode.getNodeValue();
                if (StringUtils.isBlank(nodeValue)) {
                    point = blankNode;
                }
            } else {
                break;
            }
        }

        if (point != null) {
            return Optional.of(point);
        }

        return Optional.absent();
    }

    @Override
    public PomWriter getParentWriter() {
        if (parentWriter == null) {
            parentWriter = new PomWriterImpl(project.getParent(), indentationSize, indentWithTabs, addComments);
        }
        return parentWriter;
    }

    private void addDummyComment(Node dependenciesNode) {
        dependenciesNode.appendChild(document.createComment("dummy"));
    }

    private void clearDummyComment(Node node) {
        NodeList childNodes = node.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node item = childNodes.item(i);
            if (Node.COMMENT_NODE == item.getNodeType()) {
                node.removeChild(item);
                break;
            }
            if (Node.TEXT_NODE == item.getNodeType()) {
                item.setNodeValue(EMPTY);
            }
        }
    }

    private void setDependencyVersion(String parent, TransitiveDependency dependency) {

        Optional<Node> dependencyNode = Utils.getDependencyNode(dependency.getArtifact(), document, parent);
        Node versionNode = Utils.getNode("./version", dependencyNode.get()).get();
        versionNode.setTextContent(dependency.getVersion().toString());

        Optional<Node> comment = Utils.getPluginComment(parent, dependency.getArtifact(), document);
        if (comment.isPresent()) {
            comment.get().setNodeValue(getCommentText(dependency));
        }
    }

    private String getCommentText(TransitiveDependency dependency) {
        return String.format(" %s trace=%s ", PomWriter.AUTO_COMMENT,
                dependency.getDependencyNode().toNodeString());
    }

    MavenProject getProject() {
        return project;
    }

}