/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.internal; import static com.github.anba.es6draft.parser.Characters.isWhitespaceOrLineTerminator; import java.util.Arrays; /** * Operations on strings */ public final class Strings { private Strings() { } /** * Returns the string representation of a single code point. * * @param codePoint * the code point * @return the result string */ public static String fromCodePoint(int codePoint) { if (Character.isBmpCodePoint(codePoint)) { return String.valueOf((char) codePoint); } return String.valueOf(Character.toChars(codePoint)); } /** * Removes leading whitespace. * * @param s * the string * @return the string with leading whitespace removed */ public static String trimLeft(String s) { for (int start = 0, end = s.length(); start < end; ++start) { char c = s.charAt(start); if (!isWhitespaceOrLineTerminator(c)) { return s.substring(start, end); } } return ""; } /** * Removes trailing whitespace. * * @param s * the string * @return the string with trailing whitespace removed */ public static String trimRight(String s) { for (int end = s.length(); end > 0; --end) { char c = s.charAt(end - 1); if (!isWhitespaceOrLineTerminator(c)) { return s.substring(0, end); } } return ""; } /** * Removes leading and trailing whitespace. * * @param s * the string * @return the string with leading and trailing whitespace removed */ public static String trim(String s) { int start = 0, end = s.length(); for (; start < end; ++start) { char c = s.charAt(start); if (!isWhitespaceOrLineTerminator(c)) { break; } } for (; end > start; --end) { char c = s.charAt(end - 1); if (!isWhitespaceOrLineTerminator(c)) { break; } } assert start <= end; if (start == end) { return ""; } return s.substring(start, end); } /** * Concatenates the input strings, adjacent strings are separated by the given separator char. * * @param separator * the separator character * @param strings * the input strings * @return the concatenated string */ public static String concatWith(char separator, String... strings) { if (strings.length == 0) { return ""; } else if (strings.length == 1) { return strings[0]; } else { StringBuilder sb = new StringBuilder(); for (String string : strings) { sb.append(string).append(separator); } sb.setLength(sb.length() - 1); return sb.toString(); } } /** * Concatenates two strings. * * @param s1 * the first string * @param s2 * the second string * @return the concatenated string */ public static String concat(String s1, String s2) { char[] ca = new char[s1.length() + s2.length()]; s1.getChars(0, s1.length(), ca, 0); s2.getChars(0, s2.length(), ca, s1.length()); return new String(ca); } /** * Repeat {@code c} {@code n}-times. * * @param c * the character to repeat * @param n * the repetition count * @return the result string */ public static String repeat(char c, int n) { assert n >= 0; char[] value = new char[n]; Arrays.fill(value, c); return new String(value); } private static final char[] HEXDIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Quotes the input string. * * @param s * the string * @return the quoted string */ public static String quote(String s) { for (int i = 0, length = s.length(); i < length; ++i) { char c = s.charAt(i); switch (c) { case '"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': break; default: if (c < ' ' || c > 0xff) { break; } continue; } return quote(s, i); } return new StringBuilder(s.length() + 2).append('"').append(s).append('"').toString(); } private static String quote(String s, int start) { StringBuilder sb = new StringBuilder(s.length() + 5); sb.append('"').append(s.substring(0, start)); for (int i = start, length = s.length(); i < length; ++i) { char c = s.charAt(i); switch (c) { case '"': case '\\': sb.append('\\').append(c); break; case '\b': sb.append('\\').append('b'); break; case '\f': sb.append('\\').append('f'); break; case '\n': sb.append('\\').append('n'); break; case '\r': sb.append('\\').append('r'); break; case '\t': sb.append('\\').append('t'); break; default: if (c < ' ' || c > 0xff) { sb.append('\\').append('u')// .append(HEXDIGITS[(c >> 12) & 0xf])// .append(HEXDIGITS[(c >> 8) & 0xf])// .append(HEXDIGITS[(c >> 4) & 0xf])// .append(HEXDIGITS[(c >> 0) & 0xf]); } else { sb.append(c); } } } sb.append('"'); return sb.toString(); } /** * If {@code s} is an argument index less than {@code 2}<span><sup>{@code 31}</sup></span> * {@code -1} ({@code 0x7FFFFFF}), its integer value is returned. Otherwise {@code -1} is * returned. * * @param s * the property key * @return the integer index or {@code -1} */ public static int toArgumentIndex(String s) { // "2147483647".length == 10 return (int) toIndex(s, 10, 0x7FFF_FFFFL); } /** * If {@code s} is a string index less than {@code 2}<span><sup>{@code 31}</sup></span> * {@code -1} ({@code 0x7FFFFFF}), its integer value is returned. Otherwise {@code -1} is * returned. * * @param s * the property key * @return the integer index or {@code -1} */ public static int toStringIndex(String s) { // "2147483647".length == 10 return (int) toIndex(s, 10, 0x7FFF_FFFFL); } /** * If {@code s} is an integer indexed property key less than {@code 2}<span><sup>{@code 32} * </sup></span>{@code -1} ({@code 0xFFFFFFF}), its integer value is returned. Otherwise * {@code -1} is returned. * * @param s * the property key * @return the array index or {@code -1} */ public static long toArrayIndex(String s) { // "4294967295".length == 10 return toIndex(s, 10, 0xFFFF_FFFFL); } /** * If {@code s} is an integer indexed property key less than {@code 2}<span><sup>{@code 53} * </sup></span>{@code -1}, its integer value is returned. Otherwise {@code -1} is returned. * * @param propertyKey * the property key * @return the integer index or {@code -1} */ static long toIndex(String propertyKey) { // "9007199254740991".length == 16 return toIndex(propertyKey, 16, 0x1F_FFFF_FFFF_FFFFL); } private static long toIndex(String s, int maxLength, long limit) { int length = s.length(); if (length < 1 || length > maxLength) { // empty string or definitely greater than maximum value return -1; } if (s.charAt(0) == '0') { return length == 1 ? 0 : -1; } long index = 0L; for (int i = 0; i < length; ++i) { char c = s.charAt(i); if (!(c >= '0' && c <= '9')) { return -1; } index = index * 10 + (c - '0'); } return index < limit ? index : -1; } }