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

com.palantir.javaformat.java.ModifierOrderer Maven / Gradle / Ivy

There is a newer version: 2.50.0
Show newest version
/*
 * Copyright 2016 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.palantir.javaformat.java;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeMap;
import com.palantir.javaformat.Input.Tok;
import com.palantir.javaformat.Input.Token;
import com.sun.tools.javac.parser.Tokens.TokenKind;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.Modifier;

/** Fixes sequences of modifiers to be in JLS order. */
final class ModifierOrderer {

    /** Reorders all modifiers in the given text to be in JLS order. */
    static JavaInput reorderModifiers(String text) throws FormatterException {
        return reorderModifiers(new JavaInput(text), ImmutableList.of(Range.closedOpen(0, text.length())));
    }

    /** Reorders all modifiers in the given text and within the given character ranges to be in JLS order. */
    static JavaInput reorderModifiers(JavaInput javaInput, Collection> characterRanges)
            throws FormatterException {
        if (javaInput.getTokens().isEmpty()) {
            // There weren't any tokens, possible because of a lexing error.
            // Errors about invalid input will be reported later after parsing.
            return javaInput;
        }
        RangeSet tokenRanges = javaInput.characterRangesToTokenRanges(characterRanges);
        Iterator it = javaInput.getTokens().iterator();
        TreeRangeMap replacements = TreeRangeMap.create();
        while (it.hasNext()) {
            Token token = it.next();
            if (!tokenRanges.contains(token.getTok().getIndex())) {
                continue;
            }
            Modifier mod = asModifier(token);
            if (mod == null) {
                continue;
            }

            List modifierTokens = new ArrayList<>();
            List mods = new ArrayList<>();

            int begin = token.getTok().getPosition();
            mods.add(mod);
            modifierTokens.add(token);

            int end = -1;
            while (it.hasNext()) {
                token = it.next();
                mod = asModifier(token);
                if (mod == null) {
                    break;
                }
                mods.add(mod);
                modifierTokens.add(token);
                end = token.getTok().getPosition() + token.getTok().length();
            }

            if (!Ordering.natural().isOrdered(mods)) {
                Collections.sort(mods);
                StringBuilder replacement = new StringBuilder();
                for (int i = 0; i < mods.size(); i++) {
                    if (i > 0) {
                        addTrivia(replacement, modifierTokens.get(i).getToksBefore());
                    }
                    replacement.append(mods.get(i).toString());
                    if (i < (modifierTokens.size() - 1)) {
                        addTrivia(replacement, modifierTokens.get(i).getToksAfter());
                    }
                }
                replacements.put(Range.closedOpen(begin, end), replacement.toString());
            }
        }
        return applyReplacements(javaInput, replacements);
    }

    private static void addTrivia(StringBuilder replacement, ImmutableList toks) {
        for (Tok tok : toks) {
            replacement.append(tok.getText());
        }
    }

    /**
     * Returns the given token as a {@link javax.lang.model.element.Modifier}, or {@code null} if it is not a modifier.
     */
    private static Modifier asModifier(Token token) {
        TokenKind kind = ((JavaInput.Tok) token.getTok()).kind();
        if (kind != null) {
            switch (kind) {
                case PUBLIC:
                    return Modifier.PUBLIC;
                case PROTECTED:
                    return Modifier.PROTECTED;
                case PRIVATE:
                    return Modifier.PRIVATE;
                case ABSTRACT:
                    return Modifier.ABSTRACT;
                case STATIC:
                    return Modifier.STATIC;
                case DEFAULT:
                    return Modifier.DEFAULT;
                case FINAL:
                    return Modifier.FINAL;
                case TRANSIENT:
                    return Modifier.TRANSIENT;
                case VOLATILE:
                    return Modifier.VOLATILE;
                case SYNCHRONIZED:
                    return Modifier.SYNCHRONIZED;
                case NATIVE:
                    return Modifier.NATIVE;
                case STRICTFP:
                    return Modifier.STRICTFP;
                default: // fall out
            }
        }
        switch (token.getTok().getText()) {
            case "non-sealed":
                return Modifier.valueOf("NON_SEALED");
            case "sealed":
                return Modifier.valueOf("SEALED");
            default:
                return null;
        }
    }

    /** Applies replacements to the given string. */
    private static JavaInput applyReplacements(JavaInput javaInput, TreeRangeMap replacementMap)
            throws FormatterException {
        // process in descending order so the replacement ranges aren't perturbed if any replacements
        // differ in size from the input
        Map, String> ranges = replacementMap.asDescendingMapOfRanges();
        if (ranges.isEmpty()) {
            return javaInput;
        }
        StringBuilder sb = new StringBuilder(javaInput.getText());
        for (Map.Entry, String> entry : ranges.entrySet()) {
            Range range = entry.getKey();
            sb.replace(range.lowerEndpoint(), range.upperEndpoint(), entry.getValue());
        }
        return new JavaInput(sb.toString());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy