google-java-format-master/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java
/*
 * 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.google.googlejavaformat.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.google.googlejavaformat.Input.Tok;
import com.google.googlejavaformat.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 java.util.Map.Entry;
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<Range<Integer>> 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<Integer> tokenRanges = javaInput.characterRangesToTokenRanges(characterRanges);
    Iterator<? extends Token> it = javaInput.getTokens().iterator();
    TreeRangeMap<Integer, String> 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<Token> modifierTokens = new ArrayList<>();
      List<Modifier> 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));
          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<? extends Tok> 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<Integer, String> replacementMap) throws FormatterException {
    // process in descending order so the replacement ranges aren't perturbed if any replacements
    // differ in size from the input
    Map<Range<Integer>, String> ranges = replacementMap.asDescendingMapOfRanges();
    if (ranges.isEmpty()) {
      return javaInput;
    }
    StringBuilder sb = new StringBuilder(javaInput.getText());
    for (Entry<Range<Integer>, String> entry : ranges.entrySet()) {
      Range<Integer> range = entry.getKey();
      sb.replace(range.lowerEndpoint(), range.upperEndpoint(), entry.getValue());
    }
    return new JavaInput(sb.toString());
  }
}
