• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.googlejavaformat;
16 
17 import com.google.common.base.CharMatcher;
18 import com.google.common.collect.ImmutableSet;
19 import com.google.common.collect.Iterators;
20 import java.util.Iterator;
21 import java.util.NoSuchElementException;
22 
23 /** Platform-independent newline handling. */
24 public class Newlines {
25 
26   /** Returns the number of line breaks in the input. */
count(String input)27   public static int count(String input) {
28     return Iterators.size(lineOffsetIterator(input)) - 1;
29   }
30 
31   /** Returns the index of the first break in the input, or {@code -1}. */
firstBreak(String input)32   public static int firstBreak(String input) {
33     Iterator<Integer> it = lineOffsetIterator(input);
34     it.next();
35     return it.hasNext() ? it.next() : -1;
36   }
37 
38   private static final ImmutableSet<String> BREAKS = ImmutableSet.of("\r\n", "\n", "\r");
39 
40   /** Returns true if the entire input string is a recognized line break. */
isNewline(String input)41   public static boolean isNewline(String input) {
42     return BREAKS.contains(input);
43   }
44 
45   /** Returns the length of the newline sequence at the current offset, or {@code -1}. */
hasNewlineAt(String input, int idx)46   public static int hasNewlineAt(String input, int idx) {
47     for (String b : BREAKS) {
48       if (input.startsWith(b, idx)) {
49         return b.length();
50       }
51     }
52     return -1;
53   }
54 
55   /**
56    * Returns the terminating line break in the input, or {@code null} if the input does not end in a
57    * break.
58    */
getLineEnding(String input)59   public static String getLineEnding(String input) {
60     for (String b : BREAKS) {
61       if (input.endsWith(b)) {
62         return b;
63       }
64     }
65     return null;
66   }
67 
68   /**
69    * Returns the first line separator in the text, or {@code "\n"} if the text does not contain a
70    * single line separator.
71    */
guessLineSeparator(String text)72   public static String guessLineSeparator(String text) {
73     for (int i = 0; i < text.length(); i++) {
74       char c = text.charAt(i);
75       switch (c) {
76         case '\r':
77           if (i + 1 < text.length() && text.charAt(i + 1) == '\n') {
78             return "\r\n";
79           }
80           return "\r";
81         case '\n':
82           return "\n";
83         default:
84           break;
85       }
86     }
87     return "\n";
88   }
89 
90   /** Returns true if the input contains any line breaks. */
containsBreaks(String text)91   public static boolean containsBreaks(String text) {
92     return CharMatcher.anyOf("\n\r").matchesAnyOf(text);
93   }
94 
95   /** Returns an iterator over the start offsets of lines in the input. */
lineOffsetIterator(String input)96   public static Iterator<Integer> lineOffsetIterator(String input) {
97     return new LineOffsetIterator(input);
98   }
99 
100   /** Returns an iterator over lines in the input, including trailing whitespace. */
lineIterator(String input)101   public static Iterator<String> lineIterator(String input) {
102     return new LineIterator(input);
103   }
104 
105   private static class LineOffsetIterator implements Iterator<Integer> {
106 
107     private int curr = 0;
108     private int idx = 0;
109     private final String input;
110 
LineOffsetIterator(String input)111     private LineOffsetIterator(String input) {
112       this.input = input;
113     }
114 
115     @Override
hasNext()116     public boolean hasNext() {
117       return curr != -1;
118     }
119 
120     @Override
next()121     public Integer next() {
122       if (curr == -1) {
123         throw new NoSuchElementException();
124       }
125       int result = curr;
126       advance();
127       return result;
128     }
129 
advance()130     private void advance() {
131       for (; idx < input.length(); idx++) {
132         char c = input.charAt(idx);
133         switch (c) {
134           case '\r':
135             if (idx + 1 < input.length() && input.charAt(idx + 1) == '\n') {
136               idx++;
137             }
138             // falls through
139           case '\n':
140             idx++;
141             curr = idx;
142             return;
143           default:
144             break;
145         }
146       }
147       curr = -1;
148     }
149 
150     @Override
remove()151     public void remove() {
152       throw new UnsupportedOperationException("remove");
153     }
154   }
155 
156   private static class LineIterator implements Iterator<String> {
157 
158     int idx;
159     String curr;
160 
161     private final String input;
162     private final Iterator<Integer> indices;
163 
LineIterator(String input)164     private LineIterator(String input) {
165       this.input = input;
166       this.indices = lineOffsetIterator(input);
167       idx = indices.next(); // read leading 0
168     }
169 
advance()170     private void advance() {
171       int last = idx;
172       if (indices.hasNext()) {
173         idx = indices.next();
174       } else if (hasNext()) {
175         // no terminal line break
176         idx = input.length();
177       } else {
178         throw new NoSuchElementException();
179       }
180       curr = input.substring(last, idx);
181     }
182 
183     @Override
hasNext()184     public boolean hasNext() {
185       return idx < input.length();
186     }
187 
188     @Override
next()189     public String next() {
190       advance();
191       return curr;
192     }
193 
194     @Override
remove()195     public void remove() {
196       throw new UnsupportedOperationException("remove");
197     }
198   }
199 }
200