com.google.googlejavaformat.java.GoogleJavaFormatter.java Source code

Java tutorial

Introduction

Here is the source code for com.google.googlejavaformat.java.GoogleJavaFormatter.java

Source

/*
 * Copyright 2017 Google 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.google.googlejavaformat.java;

import com.google.common.base.Preconditions;
import com.google.common.collect.Range;
import com.google.googlejavaformat.java.SnippetFormatter.SnippetKind;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;

/** Runs the Google Java formatter on the given code. */
public class GoogleJavaFormatter extends CodeFormatter {

    private static final int INDENTATION_SIZE = 2;

    @Override
    public TextEdit format(int kind, String source, int offset, int length, int indentationLevel,
            String lineSeparator) {
        IRegion[] regions = new IRegion[] { new Region(offset, length) };
        return formatInternal(kind, source, regions, indentationLevel);
    }

    @Override
    public TextEdit format(int kind, String source, IRegion[] regions, int indentationLevel, String lineSeparator) {
        return formatInternal(kind, source, regions, indentationLevel);
    }

    @Override
    public String createIndentationString(int indentationLevel) {
        Preconditions.checkArgument(indentationLevel >= 0, "Indentation level cannot be less than zero. Given: %s",
                indentationLevel);
        int spaces = indentationLevel * INDENTATION_SIZE;
        StringBuilder buf = new StringBuilder(spaces);
        for (int i = 0; i < spaces; i++) {
            buf.append(' ');
        }
        return buf.toString();
    }

    /** Runs the Google Java formatter on the given source, with only the given ranges specified. */
    private TextEdit formatInternal(int kind, String source, IRegion[] regions, int initialIndent) {
        try {
            boolean includeComments = (kind & CodeFormatter.F_INCLUDE_COMMENTS) == CodeFormatter.F_INCLUDE_COMMENTS;
            kind &= ~CodeFormatter.F_INCLUDE_COMMENTS;
            SnippetKind snippetKind;
            switch (kind) {
            case ASTParser.K_EXPRESSION:
                snippetKind = SnippetKind.EXPRESSION;
                break;
            case ASTParser.K_STATEMENTS:
                snippetKind = SnippetKind.STATEMENTS;
                break;
            case ASTParser.K_CLASS_BODY_DECLARATIONS:
                snippetKind = SnippetKind.CLASS_BODY_DECLARATIONS;
                break;
            case ASTParser.K_COMPILATION_UNIT:
                snippetKind = SnippetKind.COMPILATION_UNIT;
                break;
            default:
                throw new IllegalArgumentException(String.format("Unknown snippet kind: %d", kind));
            }
            List<Replacement> replacements = new SnippetFormatter().format(snippetKind, source,
                    rangesFromRegions(regions), initialIndent, includeComments);
            if (idempotent(source, regions, replacements)) {
                // Do not create edits if there's no diff.
                return null;
            }
            // Convert replacements to text edits.
            return editFromReplacements(replacements);
        } catch (IllegalArgumentException | FormatterException exception) {
            // Do not format on errors.
            return null;
        }
    }

    private List<Range<Integer>> rangesFromRegions(IRegion[] regions) {
        List<Range<Integer>> ranges = new ArrayList<>();
        for (IRegion region : regions) {
            ranges.add(Range.closedOpen(region.getOffset(), region.getOffset() + region.getLength()));
        }
        return ranges;
    }

    /** @return {@code true} if input and output texts are equal, else {@code false}. */
    private boolean idempotent(String source, IRegion[] regions, List<Replacement> replacements) {
        // This implementation only checks for single replacement.
        if (replacements.size() == 1) {
            Replacement replacement = replacements.get(0);
            String output = replacement.getReplacementString();
            // Entire source case: input = output, nothing changed.
            if (output.equals(source)) {
                return true;
            }
            // Single region and single replacement case: if they are equal, nothing changed.
            if (regions.length == 1) {
                Range<Integer> range = replacement.getReplaceRange();
                String snippet = source.substring(range.lowerEndpoint(), range.upperEndpoint());
                if (output.equals(snippet)) {
                    return true;
                }
            }
        }
        return false;
    }

    private TextEdit editFromReplacements(List<Replacement> replacements) {
        // Split the replacements that cross line boundaries.
        TextEdit edit = new MultiTextEdit();
        for (Replacement replacement : replacements) {
            Range<Integer> replaceRange = replacement.getReplaceRange();
            edit.addChild(new ReplaceEdit(replaceRange.lowerEndpoint(),
                    replaceRange.upperEndpoint() - replaceRange.lowerEndpoint(),
                    replacement.getReplacementString()));
        }
        return edit;
    }
}