All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.dnault.xmlpatch.BatchPatcher Maven / Gradle / Ivy

/*
 * Copyright 2015 David Nault and contributors.
 *
 * 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.github.dnault.xmlpatch;

import static com.github.dnault.xmlpatch.internal.XmlHelper.getChildren;
import static java.util.Arrays.asList;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.github.dnault.xmlpatch.batch.PatchAssembler;
import com.github.dnault.xmlpatch.internal.IoHelper;
import com.github.dnault.xmlpatch.internal.Log;
import com.github.dnault.xmlpatch.internal.XmlHelper;
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

public class BatchPatcher {

    private static OptionSet parseOptions(OptionParser parser, String... args) throws IOException {
        try {
            return parser.parse(args);
        } catch (OptionException e) {
            System.err.println(e.getMessage());
            System.err.println();
            parser.printHelpOn(System.err);
            System.exit(1);
            throw new Error();
        }
    }

    public static void main(String... args) throws Exception {
        patch(args);


        //patch("--patch", "/tmp/patch.xml", "--srcdir", "/tmp/srcdir", "--destdir", "/tmp/patchdest");
    }

    public static void patch(String... args) throws Exception {
        OptionParser parser = new OptionParser();

        OptionSpec helpOption = parser.acceptsAll(asList("help", "?"), "display this help message");

        OptionSpec patchOption = parser.accepts("patch", "patch file to apply").withRequiredArg().ofType(File.class).required();

        OptionSpec srcdirOption = parser.accepts("srcdir", "for multi-file patches, specifies the base dir for the files to be patched")
                .withRequiredArg().ofType(File.class).required();

        OptionSpec destdirOption = parser.accepts("destdir", "for multi-file patches, specifies the output directory. (default: apply the patch in-place)")
                .withRequiredArg().ofType(File.class);

        OptionSet options = parseOptions(parser, args);

        if (options.has(helpOption)) {
            parser.printHelpOn(System.out);
            System.exit(0);
        }

        File patchFile = patchOption.value(options);
        assertFileExists(patchFile, "patch");

        File srcdir = srcdirOption.value(options);
        assertDirectoryExists(srcdir, "srcdir");

        File destdir = options.has(destdirOption) ? destdirOption.value(options) : srcdir;
        if (destdir.exists() && !destdir.isDirectory()) {
            throw new IllegalArgumentException("destdir is not a directory: " + destdir.getAbsolutePath());
        }

        File tempdir = IoHelper.createTempDir();

        try {
            Set relativePathsOfPatchedFiles = patch(
                    assemblePatchDocument(patchFile), srcdir, destdir, tempdir);

            Log.info("writing patched files to destdir: " + destdir.getAbsolutePath());
            for (String path : relativePathsOfPatchedFiles) {
                File tempFile = new File(tempdir, path);
                File destFile = new File(destdir, path);
                IoHelper.makeParentDirectory(destFile);
                IoHelper.move(tempFile, destFile);
            }

            IoHelper.deleteDirectory(tempdir);

        } catch (PatchException e) {
            Log.error(e.getMessage());
            System.exit(1);
        }
    }

    public static void assertFileExists(File f, String argname) throws FileNotFoundException {
        if (!f.exists()) {
            throw new FileNotFoundException(argname + " file not found: " + f.getAbsolutePath());
        }
        if (!f.isFile()) {
            throw new IllegalArgumentException(argname + " is not a file: " + f.getAbsolutePath());
        }
    }

    public static void assertDirectoryExists(File f, String argname) throws FileNotFoundException {
        if (!f.exists()) {
            throw new FileNotFoundException(argname + " directory not found: " + f.getAbsolutePath());
        }
        if (!f.isDirectory()) {
            throw new IllegalArgumentException(argname + " is not a directory: " + f.getAbsolutePath());
        }
    }

    private static List assemblePatchDocument(File patchFile) throws Exception {
        return new PatchAssembler().assemble(patchFile).getDiffs();
    }

    private static Set patch(List diffs, File srcdir, File destdir, File tempdir) throws Exception {

        Set outputFilePaths = new HashSet<>();

        for (Element diff : diffs) {

            if (diff.getAttribute("file") == null) {
                throw new IllegalArgumentException("diff element missing 'file' attribute");
            }

            String srcfilePath = diff.getAttributeValue("file");

            File fileToPatch = new File(srcfilePath);
            if (fileToPatch.isAbsolute()) {
                throw new IllegalArgumentException("not a relative path: " + srcfilePath);
            }

            outputFilePaths.add(srcfilePath);

            File alreadyInTempDir = new File(tempdir, srcfilePath);
            fileToPatch = alreadyInTempDir.exists() ? alreadyInTempDir : new File(srcdir, srcfilePath);

            Log.info("patching " + srcfilePath + " [from " + fileToPatch.getAbsolutePath() + "]");

            diff.removeAttribute("file");
            Format format = Format.getRawFormat();
            format.setOmitDeclaration(true);
            XMLOutputter outputter = new XMLOutputter(format);
            String s = outputter.outputString(diff);

            InputStream diffStream = new ByteArrayInputStream(s.getBytes("UTF-8"));
            InputStream inputStream = new FileInputStream(fileToPatch);

            File outputFile = File.createTempFile("xmlpatch", ".xml");
            OutputStream outputStream = new FileOutputStream(outputFile);

            Patcher.patch(inputStream, diffStream, outputStream);

            inputStream.close();
            outputStream.close();

            File outputTemp = new File(tempdir, srcfilePath);
            File tempParentDir = outputTemp.getParentFile();

            if (!tempParentDir.exists() && !tempParentDir.mkdirs()) {
                throw new IOException("failed to create directory: " + tempParentDir.getAbsolutePath());
            }

            IoHelper.move(outputFile, outputTemp);
        }
        return outputFilePaths;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy