/**
* Copyright (c) 2002-2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*/
package org.eclipse.emf.codegen.ecore.genmodel.impl;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
/**
* Utility class for converting primitive values, strings, and classes to
* literals that could appear in code.
*/
public class Literals
{
// Suppress default constructor for non-instantiability.
private Literals()
{
super();
}
/**
* Convenience dispatch method. If the <code>object</code> is an instance
* of <code>Boolean</code>, <code>Byte</code>, <code>Short</code>,
* <code>Integer</code>, <code>Long</code>, <code>Float</code>,
* <code>Double</code>, <code>Character</code>, <code>String</code>,
* <code>BigDecimal</code>, <code>BigInteger</code>, <code>Date</code>,
* <code>Class</code>, or <code>byte[]</code>, the appropriate conversion
* method is called, with the unboxed primitive, or the typed object as an
* argument. Class names are never imported; the qualified name is used.
*/
public static String toLiteral(Object o)
{
return toLiteral(o, null);
}
/**
* Convenience dispatch method. If the <code>object</code> is an instance
* of <code>Boolean</code>, <code>Byte</code>, <code>Short</code>,
* <code>Integer</code>, <code>Long</code>, <code>Float</code>,
* <code>Double</code>, <code>Character</code>, <code>String</code>,
* <code>BigDecimal</code>, <code>BigInteger</code>, <code>Date</code>,
* <code>Class</code>, or <code>byte[]</code>, the appropriate conversion
* method is called, with the unboxed primitive, or the typed object as an
* argument.
* <p>The specified {@link org.eclipse.emf.codegen.ecore.genmodel.GenModel},
* if non-null, is used when necessary to import class names.
*/
public static String toLiteral(Object o, GenModel genModel)
{
return toLiteral(o, false, genModel);
}
/**
* Convenience dispatch method. If the <code>object</code> is an instance
* of <code>Boolean</code>, <code>Byte</code>, <code>Short</code>,
* <code>Integer</code>, <code>Long</code>, <code>Float</code>,
* <code>Double</code>, <code>Character</code>, <code>String</code>,
* <code>BigDecimal</code>, <code>BigInteger</code>, <code>Date</code>,
* <code>Class</code>, or <code>byte[]</code>, the appropriate conversion
* method is called, with the unwrapped primitive, or the typed object as an
* argument.
* <p>The specified {@link org.eclipse.emf.codegen.ecore.genmodel.GenModel},
* if non-null, is used when necessary to import class names.
* <p>If the object is a primitive type, <boolean>boxPrimitive</boolean>
* indicates whether to include boxing code in the literal expression.
* Otherwise, this argument is ignored.
*/
public static String toLiteral(Object o, boolean boxPrimitive, GenModel genModel)
{
if (o instanceof Boolean)
{
return toBooleanLiteral((Boolean)o, boxPrimitive, genModel);
}
if (o instanceof Byte)
{
return toByteLiteral((Byte)o, boxPrimitive, genModel);
}
if (o instanceof Short)
{
String result = toShortLiteral((Short)o, genModel);
return boxPrimitive ? box(result, "Short", "short", genModel) : result;
}
if (o instanceof Integer)
{
String result = toIntLiteral((Integer)o, genModel);
return boxPrimitive ? box(result, "Integer", null, genModel) : result;
}
if (o instanceof Long)
{
String result = toLongLiteral((Long)o, genModel);
return boxPrimitive ? box(result, "Long", null, genModel) : result;
}
if (o instanceof Float)
{
String result = toFloatLiteral((Float)o, genModel);
return boxPrimitive ? box(result, "Float", null, genModel) : result;
}
if (o instanceof Double)
{
String result = toDoubleLiteral((Double)o, genModel);
return boxPrimitive ? box(result, "Double", null, genModel) : result;
}
if (o instanceof Character)
{
String result = toCharLiteral((Character)o, genModel);
return boxPrimitive ? box(result, "Character", null, genModel) : result;
}
if (o instanceof String)
{
return toStringLiteral((String)o, genModel);
}
if (o instanceof BigDecimal)
{
return toBigDecimalLiteral((BigDecimal)o, genModel);
}
if (o instanceof BigInteger)
{
return toBigIntegerLiteral((BigInteger)o, genModel);
}
if (o instanceof Date)
{
return toDateLiteral((Date)o, genModel);
}
if (o instanceof Class<?>)
{
return toClassLiteral((Class<?>)o, genModel);
}
if (o instanceof byte[])
{
return toByteArrayLiteral((byte[])o, genModel);
}
return null;
}
/**
* Returns the literal expression for the given <code>boolean</code> value,
* with optional boxing.
*/
private static String toBooleanLiteral(boolean b, boolean box, GenModel genModel)
{
if (box)
{
return importName("java.lang.Boolean", genModel) + (b ? ".TRUE" : ".FALSE");
}
return b ? "true" : "false";
}
/**
* Returns the literal expression for the given <code>boolean</code> value.
*/
public static String toBooleanLiteral(boolean b, GenModel genModel)
{
return toBooleanLiteral(b, false, genModel);
}
/**
* Returns the hexadecimal literal expression for the given <code>byte</code>
* value, with optional boxing.
*/
private static String toByteLiteral(byte b, boolean box, GenModel genModel)
{
String result = toByteLiteral(b, genModel);
if (box)
{
String wrapperClass = importName("java.lang.Byte", genModel);
StringBuilder boxed = new StringBuilder(16 + wrapperClass.length());
boxed.append("new ");
boxed.append(wrapperClass);
boxed.append('(');
if (b >= 0)
{
boxed.append("(byte)");
}
boxed.append(result);
boxed.append(')');
result = boxed.toString();
}
return result;
}
/**
* Returns the hexadecimal literal expression for the given <code>byte</code>
* value.
*/
public static String toByteLiteral(byte b, GenModel genModel)
{
String hex = Integer.toHexString(b);
switch (hex.length())
{
case 1: return "0x0" + hex;
case 2: return "0x" + hex;
case 8: return "(byte)0x" + hex.substring(hex.length() - 2);
}
throw new IllegalArgumentException("A byte cannot convert to a " + hex.length() + " digit hex string (Java platform error in Integer.toHexString())");
}
/**
* Returns the boxed form of the literal: new wrapperClass((castType)literal)
* The wrapperClass is imported, and, if the castType is null, no cast is
* included.
*/
private static String box(String literal, String wrapperClass, String castType, GenModel genModel)
{
wrapperClass = importName(wrapperClass, genModel);
StringBuilder boxed = new StringBuilder(6 + literal.length() + wrapperClass.length() + (castType != null ? castType.length() + 2 : 0));
boxed.append("new ");
boxed.append(wrapperClass);
boxed.append('(');
if (castType != null)
{
boxed.append('(');
boxed.append(castType);
boxed.append(')');
}
boxed.append(literal);
boxed.append(')');
return boxed.toString();
}
/**
* Returns the decimal literal expression for the given <code>short</code>
* value.
*/
public static String toShortLiteral(short s, GenModel genModel)
{
return Short.toString(s);
}
/**
* Returns the decimal literal expression for the given <code>int</code>
* value.
*/
public static String toIntLiteral(int i, GenModel genModel)
{
return Integer.toString(i);
}
/**
* Returns the decimal literal expression for the given <code>long</code>
* value.
*/
public static String toLongLiteral(long l, GenModel genModel)
{
return Long.toString(l) + "L";
}
/**
* Returns a literal expression for the given <code>float</code> value.
* This literal may be in simple form or exponential notation, or it may
* be one of the special values <code>java.lang.Float.NaN</code>,
* <code>java.lang.Float.POSITIVE_INFINITY</code>, or
* <code>java.lang.Float.NEGATIVE_INFINITY</code>.
*/
public static String toFloatLiteral(float f, GenModel genModel)
{
if (Float.isNaN(f)) return importName("java.lang.Float", genModel) + ".NaN";
if (Float.isInfinite(f)) return f > 0 ?
importName("java.lang.Float", genModel) + ".POSITIVE_INFINITY" :
importName("java.lang.Float", genModel) + ".NEGATIVE_INFINITY";
return Float.toString(f) + "F";
}
/**
* Returns a literal expression for the given <code>double</code> value.
* This literal may be in simple form or exponential notation, or it may
* be one of the special values <code>java.lang.Double.NaN</code>,
* <code>java.lang.Double.POSITIVE_INFINITY</code>, or
* <code>java.lang.Double.NEGATIVE_INFINITY</code>.
*/
public static String toDoubleLiteral(double d, GenModel genModel)
{
if (Double.isNaN(d)) return importName("java.lang.Double", genModel) + ".NaN";
if (Double.isInfinite(d)) return d > 0 ?
importName("java.lang.Double", genModel) + ".POSITIVE_INFINITY" :
importName("java.lang.Double", genModel) + ".NEGATIVE_INFINITY";
return Double.toString(d);
}
private static String importName(String name, GenModel genModel)
{
return genModel != null && genModel.getImportManager() != null ? genModel.getImportedName(name) : name;
}
/**
* Returns a literal expression for the given <code>char</code> value.
* This literal will be in its escaped form if it is backspace,
* horizontal tab, newline, form feed, carriage return, double quote,
* single quote, or backslash. If it is within the common printable
* range of space (32) to <code>~</code> (126), it will simply be the
* character literal. Otherwise, it will be in the escaped Unicode
* encoding form.
*/
public static String toCharLiteral(char c, GenModel genModel)
{
StringBuilder result = new StringBuilder(8);
result.append('\'');
result.append(escapeChar(c, false));
result.append('\'');
return result.toString();
}
/**
* Returns a literal expression for the given <code>char</code> value.
* This literal will be in its escaped form if it is backspace,
* horizontal tab, newline, form feed, carriage return, double quote,
* single quote, or backslash. Otherwise, it will simply be the character
* literal. This result may not fall within the common printable range.
* @since 2.5
*/
public static String toUnsafeCharLiteral(char c, GenModel genModel)
{
StringBuilder result = new StringBuilder(8);
result.append('\'');
result.append(escapeChar(c, true));
result.append('\'');
return result.toString();
}
/**
* Returns a literal expression for the given <code>String</code>. Each
* of its characters will appear in the same form as if it was the
* argument to {@link #toCharLiteral}.
*/
public static String toStringLiteral(String s, GenModel genModel)
{
return toStringLiteral(s, genModel, false);
}
/**
* Returns a literal expression for the given <code>String</code>. Each
* of its characters will appear in the same form as if it was the
* argument to {@link #toUnsafeCharLiteral}.
* @since 2.5
*/
public static String toUnsafeStringLiteral(String s, GenModel genModel)
{
return toStringLiteral(s, genModel, true);
}
private static String toStringLiteral(String s, GenModel genModel, boolean unsafe)
{
if (s == null) return "null";
int len = s.length();
StringBuilder result = new StringBuilder(len + 16);
result.append('\"');
for (int i = 0; i < len; i++)
{
result.append(escapeChar(s.charAt(i), unsafe));
}
result.append('\"');
return result.toString();
}
private static String escapeChar(char c, boolean unsafe)
{
if (c == '\b') return "\\b";
if (c == '\t') return "\\t";
if (c == '\n') return "\\n";
if (c == '\f') return "\\f";
if (c == '\r') return "\\r";
if (c == '\"') return "\\\"";
if (c == '\'') return "\\\'";
if (c == '\\') return "\\\\";
if (unsafe || (c >= 32 && c < 127)) return String.valueOf(c);
// escaped unicode form
String num = Integer.toHexString(c);
switch(num.length()) {
case 1: return "\\u000" + num;
case 2: return "\\u00" + num;
case 3: return "\\u0" + num;
}
return "\\u" + num;
}
/**
* Returns a literal expression for the given <code>BigDecimal</code> value.
*/
public static String toBigDecimalLiteral(BigDecimal bigDecimal, GenModel genModel)
{
if (bigDecimal == null) return "null";
return "new " + importName("java.math.BigDecimal", genModel) + "(\"" + bigDecimal.toString() + "\")";
}
/**
* Returns a literal expression for the given <code>BigInteger</code> value.
*/
public static String toBigIntegerLiteral(BigInteger bigInteger, GenModel genModel)
{
if (bigInteger == null) return "null";
return "new " + importName("java.math.BigInteger", genModel) + "(\"" + bigInteger.toString() + "\")";
}
/**
* Returns a literal expression for the given <code>Date</code> value.
*/
public static String toDateLiteral(Date date, GenModel genModel)
{
String timeLiteral = toLongLiteral(date.getTime(), genModel);
return "new " + importName("java.util.Date", genModel) + "(" + timeLiteral + ")";
}
/**
* Returns a literal expression for the given <code>Class</code> value.
*/
public static String toClassLiteral(Class<?> c, GenModel genModel)
{
if (c == null) return "null";
String name = c.getName();
// See java.lang.Class.getName() javadoc for explanation of array encoding.
int arrayDepth = 0;
while (name.charAt(arrayDepth) == '[')
{
++arrayDepth;
}
if (arrayDepth > 0)
{
if (name.charAt(arrayDepth) == 'B') name = "byte";
else if (name.charAt(arrayDepth) == 'C') name = "char";
else if (name.charAt(arrayDepth) == 'D') name = "double";
else if (name.charAt(arrayDepth) == 'F') name = "float";
else if (name.charAt(arrayDepth) == 'I') name = "int";
else if (name.charAt(arrayDepth) == 'J') name = "long";
else if (name.charAt(arrayDepth) == 'S') name = "short";
else if (name.charAt(arrayDepth) == 'Z') name = "boolean";
else if (name.charAt(arrayDepth) == 'L') name = importName(name.substring(arrayDepth + 1, name.length() - 1), genModel);
else throw new IllegalArgumentException("Invalid class name: " + name);
}
else if (!c.isPrimitive())
{
name = importName(name, genModel);
}
StringBuilder result = new StringBuilder(name.length() + 2 * arrayDepth + 8);
result.append(name);
for (int i = 0; i < arrayDepth; i++)
{
result.append('[');
result.append(']');
}
result.append(".class");
return result.toString();
}
/**
* Returns a literal expression for the given <code>byte[]</code> value.
*/
public static String toByteArrayLiteral(byte[] bytes, GenModel genModel)
{
if (bytes == null) return "null";
if (bytes.length == 0) return "{}";
StringBuilder result = new StringBuilder(2 + bytes.length * 7);
result.append("{ ");
for (int i = 0, last = bytes.length - 1; i <= last; i++)
{
result.append(toByteLiteral(bytes[i], genModel));
if (i < last)
{
result.append(", ");
}
}
result.append(" }");
return result.toString();
}
}