/*
* 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.google.devtools.j2objc.gen;
import com.google.common.annotations.VisibleForTesting;
import com.google.devtools.j2objc.util.UnicodeUtils;
import java.util.regex.Pattern;
import javax.lang.model.type.TypeKind;
/**
* Utility methods for generating correct Objective-C literals.
*/
public class LiteralGenerator {
private static final String EXPONENTIAL_FLOATING_POINT_REGEX =
"[+-]?\\d*\\.?\\d*[eE][+-]?\\d+";
private static final String FLOATING_POINT_SUFFIX_REGEX = ".*[fFdD]";
private static final String HEX_LITERAL_REGEX = "0[xX].*";
private static final Pattern TRIGRAPH_REGEX = Pattern.compile("@\".*\\?\\?[=/'()!<>-].*\"");
public static String generateStringLiteral(String value) {
if (!UnicodeUtils.hasValidCppCharacters(value)) {
return buildStringFromChars(value);
}
String s = "@\"" + UnicodeUtils.escapeStringLiteral(value) + "\"";
if (TRIGRAPH_REGEX.matcher(s).matches()) {
// Split string between the two '?' chars in the trigraph, so compiler
// will concatenate the string without interpreting the trigraph.
String[] substrings = s.split("\\?\\?");
StringBuilder buffer = new StringBuilder(substrings[0]);
for (int i = 1; i < substrings.length; i++) {
buffer.append("?\" \"?");
buffer.append(substrings[i]);
}
return buffer.toString();
} else {
return s;
}
}
@VisibleForTesting
static String buildStringFromChars(String s) {
int length = s.length();
StringBuilder buffer = new StringBuilder();
buffer.append(
"[NSString stringWithCharacters:(jchar[]) { ");
int i = 0;
while (i < length) {
char c = s.charAt(i);
buffer.append("(int) 0x");
buffer.append(Integer.toHexString(c));
if (++i < length) {
buffer.append(", ");
}
}
buffer.append(" } length:");
String lengthString = Integer.toString(length);
buffer.append(lengthString);
buffer.append(']');
return buffer.toString();
}
public static String fixNumberToken(String token, TypeKind kind) {
token = token.replace("_", ""); // Remove any embedded underscores.
assert kind.isPrimitive();
switch (kind) {
case DOUBLE:
return fixDoubleToken(token);
case FLOAT:
return fixFloatToken(token);
case LONG:
return fixLongToken(token);
case INT:
return fixIntToken(token);
default:
return token;
}
}
public static String fixDoubleToken(String token) {
// Convert floating point literals to C format. No checking is
// necessary, since the format was verified by the parser.
if (token.matches(FLOATING_POINT_SUFFIX_REGEX)) {
token = token.substring(0, token.length() - 1); // strip suffix
}
if (token.matches(HEX_LITERAL_REGEX)) {
token = Double.toString(Double.parseDouble(token));
} else if (!token.matches(EXPONENTIAL_FLOATING_POINT_REGEX)) {
if (token.indexOf('.') == -1) {
token += ".0"; // C requires a fractional part, except in exponential form.
}
}
return token;
}
public static String fixFloatToken(String token) {
return fixDoubleToken(token) + 'f';
}
public static String fixLongToken(String token) {
if (token.equals("0x8000000000000000L") || token.equals("-9223372036854775808L")) {
// Convert min long literal to an expression
token = "-0x7fffffffffffffffLL - 1";
} else {
// Convert Java long literals to jlong for Obj-C
if (token.startsWith("0x")) {
token = "(jlong) " + token; // Ensure constant is treated as signed.
}
int pos = token.length() - 1;
int numLs = 0;
while (pos > 0 && token.charAt(pos) == 'L') {
numLs++;
pos--;
}
if (numLs == 1) {
token += 'L';
}
}
return token;
}
public static String fixIntToken(String token) {
if (token.equals("0x80000000") || token.equals("-2147483648")) {
// Convert min int literal to an expression
token = "-0x7fffffff - 1";
} else if (token.startsWith("0x")) {
token = "(jint) " + token; // Ensure constant is treated as signed.
}
return token;
}
public static String generate(Object value) {
if (value instanceof Boolean) {
return ((Boolean) value).booleanValue() ? "true" : "false";
} else if (value instanceof Character) {
return UnicodeUtils.escapeCharLiteral(((Character) value).charValue());
} else if (value instanceof Number) {
return generate((Number) value);
} else {
return value.toString();
}
}
public static String generate(Number value) {
if (value instanceof Long) {
return generate((Long) value);
} else if (value instanceof Integer) {
return generate((Integer) value);
} else if (value instanceof Float) {
return generate((Float) value);
} else if (value instanceof Double) {
return generate((Double) value);
} else {
return value.toString();
}
}
public static String generate(Long value) {
if (value.longValue() == Long.MIN_VALUE) {
return "((jlong) 0x8000000000000000LL)";
} else {
return value.toString() + "LL";
}
}
public static String generate(Integer value) {
if (value.intValue() == Integer.MIN_VALUE) {
return "((jint) 0x80000000)";
} else {
return value.toString();
}
}
public static String generate(Float value) {
float f = value.floatValue();
if (Float.isNaN(f)) {
return "NAN";
} else if (f == Float.POSITIVE_INFINITY) {
return "INFINITY";
} else if (f == Float.NEGATIVE_INFINITY) {
// FP representations are symmetrical.
return "-INFINITY";
} else if (f == Float.MAX_VALUE) {
return "__FLT_MAX__";
} else if (f == Float.MIN_NORMAL) {
return "__FLT_MIN__";
} else {
return value.toString() + "f";
}
}
public static String generate(Double value) {
double d = ((Double) value).doubleValue();
if (Double.isNaN(d)) {
return "NAN";
} else if (d == Double.POSITIVE_INFINITY) {
return "INFINITY";
} else if (d == Double.NEGATIVE_INFINITY) {
// FP representations are symmetrical.
return "-INFINITY";
} else if (d == Double.MAX_VALUE) {
return "__DBL_MAX__";
} else if (d == Double.MIN_NORMAL) {
return "__DBL_MIN__";
} else {
return value.toString();
}
}
}