/* * Copyright 2000-2016 JetBrains s.r.o. * * 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.intellij.openapi.util.text; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Stripped-down version of {@code com.intellij.openapi.util.text.StringUtil}. * Intended to use by external (out-of-IDE-process) runners and helpers so it should not contain any library dependencies. * * @since 12.0 */ @SuppressWarnings("UtilityClassWithoutPrivateConstructor") public class StringUtilRt { @Contract(pure = true) public static boolean charsEqualIgnoreCase(char a, char b) { return a == b || toUpperCase(a) == toUpperCase(b) || toLowerCase(a) == toLowerCase(b); } @NotNull @Contract(pure = true) public static CharSequence toUpperCase(@NotNull CharSequence s) { StringBuilder answer = null; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); char upCased = toUpperCase(c); if (answer == null && upCased != c) { answer = new StringBuilder(s.length()); answer.append(s.subSequence(0, i)); } if (answer != null) { answer.append(upCased); } } return answer == null ? s : answer; } @Contract(pure = true) public static char toUpperCase(char a) { if (a < 'a') { return a; } if (a <= 'z') { return (char)(a + ('A' - 'a')); } return Character.toUpperCase(a); } @Contract(pure = true) public static char toLowerCase(char a) { if (a < 'A' || a >= 'a' && a <= 'z') { return a; } if (a <= 'Z') { return (char)(a + ('a' - 'A')); } return Character.toLowerCase(a); } /** * Converts line separators to {@code "\n"} */ @NotNull @Contract(pure = true) public static String convertLineSeparators(@NotNull String text) { return convertLineSeparators(text, false); } @NotNull @Contract(pure = true) public static String convertLineSeparators(@NotNull String text, boolean keepCarriageReturn) { return convertLineSeparators(text, "\n", null, keepCarriageReturn); } @NotNull @Contract(pure = true) public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator) { return convertLineSeparators(text, newSeparator, null); } @NotNull @Contract(pure = true) public static CharSequence convertLineSeparators(@NotNull CharSequence text, @NotNull String newSeparator) { return unifyLineSeparators(text, newSeparator, null, false); } @NotNull public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator, @Nullable int[] offsetsToKeep) { return convertLineSeparators(text, newSeparator, offsetsToKeep, false); } @NotNull public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator, @Nullable int[] offsetsToKeep, boolean keepCarriageReturn) { return unifyLineSeparators(text, newSeparator, offsetsToKeep, keepCarriageReturn).toString(); } @NotNull private static CharSequence unifyLineSeparators(@NotNull CharSequence text, @NotNull String newSeparator, @Nullable int[] offsetsToKeep, boolean keepCarriageReturn) { StringBuilder buffer = null; int intactLength = 0; final boolean newSeparatorIsSlashN = "\n".equals(newSeparator); for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (c == '\n') { if (!newSeparatorIsSlashN) { if (buffer == null) { buffer = new StringBuilder(text.length()); buffer.append(text, 0, intactLength); } buffer.append(newSeparator); shiftOffsets(offsetsToKeep, buffer.length(), 1, newSeparator.length()); } else if (buffer == null) { intactLength++; } else { buffer.append(c); } } else if (c == '\r') { boolean followedByLineFeed = i < text.length() - 1 && text.charAt(i + 1) == '\n'; if (!followedByLineFeed && keepCarriageReturn) { if (buffer == null) { intactLength++; } else { buffer.append(c); } continue; } if (buffer == null) { buffer = new StringBuilder(text.length()); buffer.append(text, 0, intactLength); } buffer.append(newSeparator); if (followedByLineFeed) { //noinspection AssignmentToForLoopParameter i++; shiftOffsets(offsetsToKeep, buffer.length(), 2, newSeparator.length()); } else { shiftOffsets(offsetsToKeep, buffer.length(), 1, newSeparator.length()); } } else { if (buffer == null) { intactLength++; } else { buffer.append(c); } } } return buffer == null ? text : buffer; } private static void shiftOffsets(int[] offsets, int changeOffset, int oldLength, int newLength) { if (offsets == null) return; int shift = newLength - oldLength; if (shift == 0) return; for (int i = 0; i < offsets.length; i++) { int offset = offsets[i]; if (offset >= changeOffset + oldLength) { offsets[i] += shift; } } } @Contract(pure = true) public static int parseInt(@Nullable String string, final int defaultValue) { if (string == null) { return defaultValue; } try { return Integer.parseInt(string); } catch (Exception e) { return defaultValue; } } @Contract(pure = true) public static long parseLong(@Nullable String string, long defaultValue) { if (string == null) { return defaultValue; } try { return Long.parseLong(string); } catch (Exception e) { return defaultValue; } } @Contract(pure = true) public static double parseDouble(final String string, final double defaultValue) { try { return Double.parseDouble(string); } catch (Exception e) { return defaultValue; } } @Contract(pure = true) public static boolean parseBoolean(final String string, final boolean defaultValue) { try { return Boolean.parseBoolean(string); } catch (Exception e) { return defaultValue; } } @Contract(pure = true) static <E extends Enum<E>> E parseEnum(@NotNull String string, E defaultValue, @NotNull Class<E> clazz) { try { return Enum.valueOf(clazz, string); } catch (Exception e) { return defaultValue; } } @NotNull @Contract(pure = true) public static String getShortName(@NotNull Class aClass) { return getShortName(aClass.getName()); } @NotNull @Contract(pure = true) public static String getShortName(@NotNull String fqName) { return getShortName(fqName, '.'); } @NotNull @Contract(pure = true) public static String getShortName(@NotNull String fqName, char separator) { int lastPointIdx = fqName.lastIndexOf(separator); if (lastPointIdx >= 0) { return fqName.substring(lastPointIdx + 1); } return fqName; } @Contract(pure = true) public static boolean endsWithChar(@Nullable CharSequence s, char suffix) { return s != null && s.length() != 0 && s.charAt(s.length() - 1) == suffix; } @Contract(pure = true) public static boolean startsWithIgnoreCase(@NonNls @NotNull String str, @NonNls @NotNull String prefix) { final int stringLength = str.length(); final int prefixLength = prefix.length(); return stringLength >= prefixLength && str.regionMatches(true, 0, prefix, 0, prefixLength); } @Contract(pure = true) public static boolean endsWithIgnoreCase(@NonNls @NotNull CharSequence text, @NonNls @NotNull CharSequence suffix) { int l1 = text.length(); int l2 = suffix.length(); if (l1 < l2) return false; for (int i = l1 - 1; i >= l1 - l2; i--) { if (!charsEqualIgnoreCase(text.charAt(i), suffix.charAt(i + l2 - l1))) { return false; } } return true; } /** * Allows to retrieve index of last occurrence of the given symbols at {@code [start; end)} sub-sequence of the given text. * * @param s target text * @param c target symbol which last occurrence we want to check * @param start start offset of the target text (inclusive) * @param end end offset of the target text (exclusive) * @return index of the last occurrence of the given symbol at the target sub-sequence of the given text if any; * {@code -1} otherwise */ @Contract(pure = true) public static int lastIndexOf(@NotNull CharSequence s, char c, int start, int end) { start = Math.max(start, 0); for (int i = Math.min(end, s.length()) - 1; i >= start; i--) { if (s.charAt(i) == c) return i; } return -1; } }