PatchOptions.java

/*
 * Copyright (c) 2020, Stein Eldar Johnsen
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 net.morimekta.strings.diff;

import net.morimekta.strings.chr.Color;

import java.util.Optional;

import static java.util.Objects.requireNonNull;

/**
 * A class with options for displaying patch strings.
 */
public class PatchOptions {
    /** Number of context lines to show before a change. */
    public final int    before;
    /** Number of context lines to show after a change. */
    public final int    after;
    /** String to prepend before the patch header line ({@code @@ ... @@}). */
    public final String beforePatch;
    /** String to append after the patch header line ({@code @@ ... @@}). */
    public final String afterPatch;
    /** String to prepend before the comment on a patch header line. */
    public final String beforeComment;
    /** String to append after the comment on a patch header line. */
    public final String afterComment;
    /** String to prepend before an equal (unchanged) line. */
    public final String beforeEq;
    /** String to append after an equal (unchanged) line. */
    public final String afterEq;
    /** String to prepend before an added line. */
    public final String beforeAdd;
    /** String to prepend before a deleted line. */
    public final String beforeDel;
    /** String to append after a changed (added or deleted) line. */
    public final String afterChange;

    /**
     * Create patch options with the given values.
     *
     * @param before        Number of context lines before a change.
     * @param after         Number of context lines after a change.
     * @param beforePatch   String to prepend before patch header.
     * @param afterPatch    String to append after patch header.
     * @param beforeComment String to prepend before comment.
     * @param afterComment  String to append after comment.
     * @param beforeEq      String to prepend before equal lines.
     * @param afterEq       String to append after equal lines.
     * @param beforeAdd     String to prepend before added lines.
     * @param beforeDel     String to prepend before deleted lines.
     * @param afterChange   String to append after changed lines.
     */
    public PatchOptions(int before,
                        int after,
                        String beforePatch,
                        String afterPatch,
                        String beforeComment,
                        String afterComment,
                        String beforeEq,
                        String afterEq,
                        String beforeAdd,
                        String beforeDel,
                        String afterChange) {
        if (before < 0) throw new IllegalArgumentException("before < 0: " + before);
        if (after < 0) throw new IllegalArgumentException("after < 0: " + after);
        this.before = before;
        this.after = after;
        this.beforePatch = requireNonNull(beforePatch, "beforePatch == null");
        this.afterPatch = requireNonNull(afterPatch, "afterPatch == null");
        this.beforeComment = requireNonNull(beforeComment, "beforeComment == null");
        this.afterComment = requireNonNull(afterComment, "afterComment == null");
        this.beforeEq = requireNonNull(beforeEq, "beforeEq == null");
        this.afterEq = requireNonNull(afterEq, "afterEq == null");
        this.beforeAdd = requireNonNull(beforeAdd, "beforeAdd == null");
        this.beforeDel = requireNonNull(beforeDel, "beforeDel == null");
        this.afterChange = requireNonNull(afterChange, "afterChange == null");
    }

    /**
     * Create a new builder for patch options.
     *
     * @return A new builder instance.
     */
    public static Builder newBuilder() {
        return new Builder();
    }

    /**
     * Builder for {@link PatchOptions}.
     */
    public static class Builder {
        private int    before        = 0;
        private int    after         = 0;
        private String beforePatch   = "";
        private String afterPatch    = "";
        private String beforeComment = "";
        private String afterComment  = "";
        private String beforeEq      = "";
        private String afterEq       = "";
        private String beforeAdd     = "";
        private String beforeDel     = "";
        private String afterChange   = "";

        /**
         * Create a new builder with default (empty/zero) values.
         */
        public Builder() {}

        /**
         * Set all options to their minimal defaults (no context, no decoration).
         *
         * @return This builder.
         */
        public Builder withDefaultMinimal() {
            return withBefore(0)
                    .withAfter(0)
                    .withBeforePatch("")
                    .withAfterPatch("")
                    .withBeforeComment("")
                    .withAfterComment("")
                    .withBeforeEq("")
                    .withAfterEq("")
                    .withBeforeAdd("")
                    .withBeforeDel("")
                    .withAfterChange("");
        }

        /**
         * Set all options to pretty-print defaults with ANSI color codes
         * and 3 lines of context.
         *
         * @return This builder.
         */
        public Builder withDefaultPretty() {
            return withBefore(3)
                    .withAfter(3)
                    .withBeforePatch(new Color(Color.CYAN, Color.DIM).toString())
                    .withAfterPatch(Color.CLEAR.toString())
                    .withBeforeComment(Color.DIM.toString())
                    .withAfterComment(Color.CLEAR.toString())
                    .withBeforeEq("")
                    .withAfterEq("")
                    .withBeforeAdd(Color.GREEN.toString())
                    .withBeforeDel(Color.RED.toString())
                    .withAfterChange(Color.CLEAR.toString());
        }

        /**
         * Set number of context lines to show before a change.
         *
         * @param before Number of lines before.
         * @return This builder.
         */
        public Builder withBefore(int before) {
            if (before < 0) throw new IllegalArgumentException("before < 0: " + before);
            this.before = before;
            return this;
        }

        /**
         * Set number of context lines to show after a change.
         *
         * @param after Number of lines after.
         * @return This builder.
         */
        public Builder withAfter(int after) {
            if (after < 0) throw new IllegalArgumentException("after < 0: " + after);
            this.after = after;
            return this;
        }

        /**
         * Set string to prepend before the patch header line.
         *
         * @param beforePatch The prefix string, or null for empty.
         * @return This builder.
         */
        public Builder withBeforePatch(String beforePatch) {
            this.beforePatch = Optional.ofNullable(beforePatch).orElse("");
            return this;
        }

        /**
         * Set string to append after the patch header line.
         *
         * @param afterPatch The suffix string, or null for empty.
         * @return This builder.
         */
        public Builder withAfterPatch(String afterPatch) {
            this.afterPatch = Optional.ofNullable(afterPatch).orElse("");
            return this;
        }

        /**
         * Set string to prepend before the comment on a patch header line.
         *
         * @param beforeComment The prefix string, or null for empty.
         * @return This builder.
         */
        public Builder withBeforeComment(String beforeComment) {
            this.beforeComment = Optional.ofNullable(beforeComment).orElse("");
            return this;
        }

        /**
         * Set string to append after the comment on a patch header line.
         *
         * @param afterComment The suffix string, or null for empty.
         * @return This builder.
         */
        public Builder withAfterComment(String afterComment) {
            this.afterComment = Optional.ofNullable(afterComment).orElse("");
            return this;
        }

        /**
         * Set string to prepend before an equal (unchanged) line.
         *
         * @param beforeEq The prefix string, or null for empty.
         * @return This builder.
         */
        public Builder withBeforeEq(String beforeEq) {
            this.beforeEq = Optional.ofNullable(beforeEq).orElse("");
            return this;
        }

        /**
         * Set string to append after an equal (unchanged) line.
         *
         * @param afterEq The suffix string, or null for empty.
         * @return This builder.
         */
        public Builder withAfterEq(String afterEq) {
            this.afterEq = Optional.ofNullable(afterEq).orElse("");
            return this;
        }

        /**
         * Set string to prepend before an added line.
         *
         * @param beforeAdd The prefix string, or null for empty.
         * @return This builder.
         */
        public Builder withBeforeAdd(String beforeAdd) {
            this.beforeAdd = Optional.ofNullable(beforeAdd).orElse("");
            return this;
        }

        /**
         * Set string to prepend before a deleted line.
         *
         * @param beforeDel The prefix string, or null for empty.
         * @return This builder.
         */
        public Builder withBeforeDel(String beforeDel) {
            this.beforeDel = Optional.ofNullable(beforeDel).orElse("");
            return this;
        }

        /**
         * Set string to append after a changed (added or deleted) line.
         *
         * @param afterChange The suffix string, or null for empty.
         * @return This builder.
         */
        public Builder withAfterChange(String afterChange) {
            this.afterChange = Optional.ofNullable(afterChange).orElse("");
            return this;
        }

        /**
         * Build the patch options.
         *
         * @return The new {@link PatchOptions} instance.
         */
        public PatchOptions build() {
            return new PatchOptions(before,
                                    after,
                                    beforePatch,
                                    afterPatch,
                                    beforeComment,
                                    afterComment,
                                    beforeEq,
                                    afterEq,
                                    beforeAdd,
                                    beforeDel,
                                    afterChange);
        }
    }
}