2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.openapi.util.text;
18 import org.jetbrains.annotations.Contract;
19 import org.jetbrains.annotations.NonNls;
20 import org.jetbrains.annotations.NotNull;
21 import org.jetbrains.annotations.Nullable;
24 * Stripped-down version of {@code com.intellij.openapi.util.text.StringUtil}.
25 * Intended to use by external (out-of-IDE-process) runners and helpers so it should not contain any library dependencies.
29 @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"})
30 public class StringUtilRt {
31 public static final String EMPTY_STRING = "";
33 @Contract(pure = true)
34 public static boolean charsEqualIgnoreCase(char a, char b) {
35 return a == b || toUpperCase(a) == toUpperCase(b) || toLowerCase(a) == toLowerCase(b);
39 @Contract(pure = true)
40 public static CharSequence toUpperCase(@NotNull CharSequence s) {
41 StringBuilder answer = null;
43 for (int i = 0; i < s.length(); i++) {
45 char upcased = toUpperCase(c);
46 if (answer == null && upcased != c) {
47 answer = new StringBuilder(s.length());
48 answer.append(s.subSequence(0, i));
52 answer.append(upcased);
56 return answer == null ? s : answer;
59 @Contract(pure = true)
60 public static char toUpperCase(char a) {
65 return (char)(a + ('A' - 'a'));
67 return Character.toUpperCase(a);
70 @Contract(pure = true)
71 public static char toLowerCase(char a) {
72 if (a < 'A' || a >= 'a' && a <= 'z') {
77 return (char)(a + ('a' - 'A'));
80 return Character.toLowerCase(a);
84 * Converts line separators to <code>"\n"</code>
87 @Contract(pure = true)
88 public static String convertLineSeparators(@NotNull String text) {
89 return convertLineSeparators(text, false);
93 @Contract(pure = true)
94 public static String convertLineSeparators(@NotNull String text, boolean keepCarriageReturn) {
95 return convertLineSeparators(text, "\n", null, keepCarriageReturn);
99 @Contract(pure = true)
100 public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator) {
101 return convertLineSeparators(text, newSeparator, null);
105 @Contract(pure = true)
106 public static CharSequence convertLineSeparators(@NotNull CharSequence text, @NotNull String newSeparator) {
107 return unifyLineSeparators(text, newSeparator, null, false);
111 public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator, @Nullable int[] offsetsToKeep) {
112 return convertLineSeparators(text, newSeparator, offsetsToKeep, false);
116 public static String convertLineSeparators(@NotNull String text,
117 @NotNull String newSeparator,
118 @Nullable int[] offsetsToKeep,
119 boolean keepCarriageReturn) {
120 return unifyLineSeparators(text, newSeparator, offsetsToKeep, keepCarriageReturn).toString();
124 @Contract(pure = true)
125 public static CharSequence unifyLineSeparators(@NotNull CharSequence text) {
126 return unifyLineSeparators(text, "\n", null, false);
130 public static CharSequence unifyLineSeparators(@NotNull CharSequence text,
131 @NotNull String newSeparator,
132 @Nullable int[] offsetsToKeep,
133 boolean keepCarriageReturn) {
134 StringBuilder buffer = null;
135 int intactLength = 0;
136 final boolean newSeparatorIsSlashN = "\n".equals(newSeparator);
137 for (int i = 0; i < text.length(); i++) {
138 char c = text.charAt(i);
140 if (!newSeparatorIsSlashN) {
141 if (buffer == null) {
142 buffer = new StringBuilder(text.length());
143 buffer.append(text, 0, intactLength);
145 buffer.append(newSeparator);
146 shiftOffsets(offsetsToKeep, buffer.length(), 1, newSeparator.length());
148 else if (buffer == null) {
155 else if (c == '\r') {
156 boolean followedByLineFeed = i < text.length() - 1 && text.charAt(i + 1) == '\n';
157 if (!followedByLineFeed && keepCarriageReturn) {
158 if (buffer == null) {
166 if (buffer == null) {
167 buffer = new StringBuilder(text.length());
168 buffer.append(text, 0, intactLength);
170 buffer.append(newSeparator);
171 if (followedByLineFeed) {
172 //noinspection AssignmentToForLoopParameter
174 shiftOffsets(offsetsToKeep, buffer.length(), 2, newSeparator.length());
177 shiftOffsets(offsetsToKeep, buffer.length(), 1, newSeparator.length());
181 if (buffer == null) {
189 return buffer == null ? text : buffer;
192 private static void shiftOffsets(int[] offsets, int changeOffset, int oldLength, int newLength) {
193 if (offsets == null) return;
194 int shift = newLength - oldLength;
195 if (shift == 0) return;
196 for (int i = 0; i < offsets.length; i++) {
197 int offset = offsets[i];
198 if (offset >= changeOffset + oldLength) {
204 @Contract(pure = true)
205 public static int parseInt(@Nullable String string, final int defaultValue) {
206 if (string == null) {
211 return Integer.parseInt(string);
213 catch (Exception e) {
218 @Contract(pure = true)
219 public static long parseLong(@Nullable String string, long defaultValue) {
220 if (string == null) {
224 return Long.parseLong(string);
226 catch (Exception e) {
231 @Contract(pure = true)
232 public static double parseDouble(final String string, final double defaultValue) {
234 return Double.parseDouble(string);
236 catch (Exception e) {
241 @Contract(pure = true)
242 public static boolean parseBoolean(final String string, final boolean defaultValue) {
244 return Boolean.parseBoolean(string);
246 catch (Exception e) {
252 @Contract(pure = true)
253 public static String getShortName(@NotNull Class aClass) {
254 return getShortName(aClass.getName());
258 @Contract(pure = true)
259 public static String getShortName(@NotNull String fqName) {
260 return getShortName(fqName, '.');
264 @Contract(pure = true)
265 public static String getShortName(@NotNull String fqName, char separator) {
266 int lastPointIdx = fqName.lastIndexOf(separator);
267 if (lastPointIdx >= 0) {
268 return fqName.substring(lastPointIdx + 1);
273 @Contract(pure = true)
274 public static boolean endsWithChar(@Nullable CharSequence s, char suffix) {
275 return s != null && s.length() != 0 && s.charAt(s.length() - 1) == suffix;
278 @Contract(pure = true)
279 public static boolean startsWithIgnoreCase(@NonNls @NotNull String str, @NonNls @NotNull String prefix) {
280 final int stringLength = str.length();
281 final int prefixLength = prefix.length();
282 return stringLength >= prefixLength && str.regionMatches(true, 0, prefix, 0, prefixLength);
285 @Contract(pure = true)
286 public static boolean endsWithIgnoreCase(@NonNls @NotNull CharSequence text, @NonNls @NotNull CharSequence suffix) {
287 int l1 = text.length();
288 int l2 = suffix.length();
289 if (l1 < l2) return false;
291 for (int i = l1 - 1; i >= l1 - l2; i--) {
292 if (!charsEqualIgnoreCase(text.charAt(i), suffix.charAt(i + l2 - l1))) {
301 * Allows to retrieve index of last occurrence of the given symbols at <code>[start; end)</code> sub-sequence of the given text.
303 * @param s target text
304 * @param c target symbol which last occurrence we want to check
305 * @param start start offset of the target text (inclusive)
306 * @param end end offset of the target text (exclusive)
307 * @return index of the last occurrence of the given symbol at the target sub-sequence of the given text if any;
308 * <code>-1</code> otherwise
310 @Contract(pure = true)
311 public static int lastIndexOf(@NotNull CharSequence s, char c, int start, int end) {
312 start = Math.max(start, 0);
313 for (int i = Math.min(end, s.length()) - 1; i >= start; i--) {
314 if (s.charAt(i) == c) return i;