google-java-format-master/core/src/main/java/com/google/googlejavaformat/Newlines.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;

import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import java.util.Iterator;
import java.util.NoSuchElementException;

/** Platform-independent newline handling. */
public class Newlines {

  /** Returns the number of line breaks in the input. */
  public static int count(String input) {
    return Iterators.size(lineOffsetIterator(input)) - 1;
  }

  /** Returns the index of the first break in the input, or {@code -1}. */
  public static int firstBreak(String input) {
    Iterator<Integer> it = lineOffsetIterator(input);
    it.next();
    return it.hasNext() ? it.next() : -1;
  }

  private static final ImmutableSet<String> BREAKS = ImmutableSet.of("\r\n", "\n", "\r");

  /** Returns true if the entire input string is a recognized line break. */
  public static boolean isNewline(String input) {
    return BREAKS.contains(input);
  }

  /** Returns the length of the newline sequence at the current offset, or {@code -1}. */
  public static int hasNewlineAt(String input, int idx) {
    for (String b : BREAKS) {
      if (input.startsWith(b, idx)) {
        return b.length();
      }
    }
    return -1;
  }

  /**
   * Returns the terminating line break in the input, or {@code null} if the input does not end in a
   * break.
   */
  public static String getLineEnding(String input) {
    for (String b : BREAKS) {
      if (input.endsWith(b)) {
        return b;
      }
    }
    return null;
  }

  /**
   * Returns the first line separator in the text, or {@code "\n"} if the text does not contain a
   * single line separator.
   */
  public static String guessLineSeparator(String text) {
    for (int i = 0; i < text.length(); i++) {
      char c = text.charAt(i);
      switch (c) {
        case '\r':
          if (i + 1 < text.length() && text.charAt(i + 1) == '\n') {
            return "\r\n";
          }
          return "\r";
        case '\n':
          return "\n";
        default:
          break;
      }
    }
    return "\n";
  }

  /** Returns true if the input contains any line breaks. */
  public static boolean containsBreaks(String text) {
    return CharMatcher.anyOf("\n\r").matchesAnyOf(text);
  }

  /** Returns an iterator over the start offsets of lines in the input. */
  public static Iterator<Integer> lineOffsetIterator(String input) {
    return new LineOffsetIterator(input);
  }

  /** Returns an iterator over lines in the input, including trailing whitespace. */
  public static Iterator<String> lineIterator(String input) {
    return new LineIterator(input);
  }

  private static class LineOffsetIterator implements Iterator<Integer> {

    private int curr = 0;
    private int idx = 0;
    private final String input;

    private LineOffsetIterator(String input) {
      this.input = input;
    }

    @Override
    public boolean hasNext() {
      return curr != -1;
    }

    @Override
    public Integer next() {
      if (curr == -1) {
        throw new NoSuchElementException();
      }
      int result = curr;
      advance();
      return result;
    }

    private void advance() {
      for (; idx < input.length(); idx++) {
        char c = input.charAt(idx);
        switch (c) {
          case '\r':
            if (idx + 1 < input.length() && input.charAt(idx + 1) == '\n') {
              idx++;
            }
            // falls through
          case '\n':
            idx++;
            curr = idx;
            return;
          default:
            break;
        }
      }
      curr = -1;
    }

    @Override
    public void remove() {
      throw new UnsupportedOperationException("remove");
    }
  }

  private static class LineIterator implements Iterator<String> {

    int idx;
    String curr;

    private final String input;
    private final Iterator<Integer> indices;

    private LineIterator(String input) {
      this.input = input;
      this.indices = lineOffsetIterator(input);
      idx = indices.next(); // read leading 0
    }

    private void advance() {
      int last = idx;
      if (indices.hasNext()) {
        idx = indices.next();
      } else if (hasNext()) {
        // no terminal line break
        idx = input.length();
      } else {
        throw new NoSuchElementException();
      }
      curr = input.substring(last, idx);
    }

    @Override
    public boolean hasNext() {
      return idx < input.length();
    }

    @Override
    public String next() {
      advance();
      return curr;
    }

    @Override
    public void remove() {
      throw new UnsupportedOperationException("remove");
    }
  }
}
