0c8f2ed061efad70046444084a31eea5a1a2e7dd
[idea/community.git] / platform / util-rt / src / com / intellij / openapi / util / text / StringUtilRt.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.openapi.util.text;
17
18 import org.jetbrains.annotations.Contract;
19 import org.jetbrains.annotations.NonNls;
20 import org.jetbrains.annotations.NotNull;
21 import org.jetbrains.annotations.Nullable;
22
23 /**
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.
26  *
27  * @since 12.0
28  */
29 @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"})
30 public class StringUtilRt {
31   public static final String EMPTY_STRING = "";
32
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);
36   }
37
38   @NotNull
39   @Contract(pure = true)
40   public static CharSequence toUpperCase(@NotNull CharSequence s) {
41     StringBuilder answer = null;
42
43     for (int i = 0; i < s.length(); i++) {
44       char c = s.charAt(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));
49       }
50
51       if (answer != null) {
52         answer.append(upcased);
53       }
54     }
55
56     return answer == null ? s : answer;
57   }
58
59   @Contract(pure = true)
60   public static char toUpperCase(char a) {
61     if (a < 'a') {
62       return a;
63     }
64     if (a <= 'z') {
65       return (char)(a + ('A' - 'a'));
66     }
67     return Character.toUpperCase(a);
68   }
69
70   @Contract(pure = true)
71   public static char toLowerCase(char a) {
72     if (a < 'A' || a >= 'a' && a <= 'z') {
73       return a;
74     }
75
76     if (a <= 'Z') {
77       return (char)(a + ('a' - 'A'));
78     }
79
80     return Character.toLowerCase(a);
81   }
82
83   /**
84    * Converts line separators to <code>"\n"</code>
85    */
86   @NotNull
87   @Contract(pure = true)
88   public static String convertLineSeparators(@NotNull String text) {
89     return convertLineSeparators(text, false);
90   }
91
92   @NotNull
93   @Contract(pure = true)
94   public static String convertLineSeparators(@NotNull String text, boolean keepCarriageReturn) {
95     return convertLineSeparators(text, "\n", null, keepCarriageReturn);
96   }
97
98   @NotNull
99   @Contract(pure = true)
100   public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator) {
101     return convertLineSeparators(text, newSeparator, null);
102   }
103
104   @NotNull
105   @Contract(pure = true)
106   public static CharSequence convertLineSeparators(@NotNull CharSequence text, @NotNull String newSeparator) {
107     return unifyLineSeparators(text, newSeparator, null, false);
108   }
109
110   @NotNull
111   public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator, @Nullable int[] offsetsToKeep) {
112     return convertLineSeparators(text, newSeparator, offsetsToKeep, false);
113   }
114
115   @NotNull
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();
121   }
122
123   @NotNull
124   @Contract(pure = true)
125   public static CharSequence unifyLineSeparators(@NotNull CharSequence text) {
126     return unifyLineSeparators(text, "\n", null, false);
127   }
128
129   @NotNull
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);
139       if (c == '\n') {
140         if (!newSeparatorIsSlashN) {
141           if (buffer == null) {
142             buffer = new StringBuilder(text.length());
143             buffer.append(text, 0, intactLength);
144           }
145           buffer.append(newSeparator);
146           shiftOffsets(offsetsToKeep, buffer.length(), 1, newSeparator.length());
147         }
148         else if (buffer == null) {
149           intactLength++;
150         }
151         else {
152           buffer.append(c);
153         }
154       }
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) {
159             intactLength++;
160           }
161           else {
162             buffer.append(c);
163           }
164           continue;
165         }
166         if (buffer == null) {
167           buffer = new StringBuilder(text.length());
168           buffer.append(text, 0, intactLength);
169         }
170         buffer.append(newSeparator);
171         if (followedByLineFeed) {
172           //noinspection AssignmentToForLoopParameter
173           i++;
174           shiftOffsets(offsetsToKeep, buffer.length(), 2, newSeparator.length());
175         }
176         else {
177           shiftOffsets(offsetsToKeep, buffer.length(), 1, newSeparator.length());
178         }
179       }
180       else {
181         if (buffer == null) {
182           intactLength++;
183         }
184         else {
185           buffer.append(c);
186         }
187       }
188     }
189     return buffer == null ? text : buffer;
190   }
191
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) {
199         offsets[i] += shift;
200       }
201     }
202   }
203
204   @Contract(pure = true)
205   public static int parseInt(@Nullable String string, final int defaultValue) {
206     if (string == null) {
207       return defaultValue;
208     }
209
210     try {
211       return Integer.parseInt(string);
212     }
213     catch (Exception e) {
214       return defaultValue;
215     }
216   }
217
218   @Contract(pure = true)
219   public static double parseDouble(final String string, final double defaultValue) {
220     try {
221       return Double.parseDouble(string);
222     }
223     catch (Exception e) {
224       return defaultValue;
225     }
226   }
227
228   @Contract(pure = true)
229   public static boolean parseBoolean(final String string, final boolean defaultValue) {
230     try {
231       return Boolean.parseBoolean(string);
232     }
233     catch (Exception e) {
234       return defaultValue;
235     }
236   }
237
238   @NotNull
239   @Contract(pure = true)
240   public static String getShortName(@NotNull Class aClass) {
241     return getShortName(aClass.getName());
242   }
243
244   @NotNull
245   @Contract(pure = true)
246   public static String getShortName(@NotNull String fqName) {
247     return getShortName(fqName, '.');
248   }
249
250   @NotNull
251   @Contract(pure = true)
252   public static String getShortName(@NotNull String fqName, char separator) {
253     int lastPointIdx = fqName.lastIndexOf(separator);
254     if (lastPointIdx >= 0) {
255       return fqName.substring(lastPointIdx + 1);
256     }
257     return fqName;
258   }
259
260   @Contract(pure = true)
261   public static boolean endsWithChar(@Nullable CharSequence s, char suffix) {
262     return s != null && s.length() != 0 && s.charAt(s.length() - 1) == suffix;
263   }
264
265   @Contract(pure = true)
266   public static boolean startsWithIgnoreCase(@NonNls @NotNull String str, @NonNls @NotNull String prefix) {
267     final int stringLength = str.length();
268     final int prefixLength = prefix.length();
269     return stringLength >= prefixLength && str.regionMatches(true, 0, prefix, 0, prefixLength);
270   }
271
272   @Contract(pure = true)
273   public static boolean endsWithIgnoreCase(@NonNls @NotNull CharSequence text, @NonNls @NotNull CharSequence suffix) {
274     int l1 = text.length();
275     int l2 = suffix.length();
276     if (l1 < l2) return false;
277
278     for (int i = l1 - 1; i >= l1 - l2; i--) {
279       if (!charsEqualIgnoreCase(text.charAt(i), suffix.charAt(i + l2 - l1))) {
280         return false;
281       }
282     }
283
284     return true;
285   }
286
287   /**
288    * Allows to retrieve index of last occurrence of the given symbols at <code>[start; end)</code> sub-sequence of the given text.
289    *
290    * @param s     target text
291    * @param c     target symbol which last occurrence we want to check
292    * @param start start offset of the target text (inclusive)
293    * @param end   end offset of the target text (exclusive)
294    * @return index of the last occurrence of the given symbol at the target sub-sequence of the given text if any;
295    * <code>-1</code> otherwise
296    */
297   @Contract(pure = true)
298   public static int lastIndexOf(@NotNull CharSequence s, char c, int start, int end) {
299     start = Math.max(start, 0);
300     for (int i = Math.min(end, s.length()) - 1; i >= start; i--) {
301       if (s.charAt(i) == c) return i;
302     }
303     return -1;
304   }
305
306
307 }