package com.redhat.ceylon.model.loader;
import com.redhat.ceylon.model.loader.model.FieldValue;
import com.redhat.ceylon.model.loader.model.JavaBeanValue;
import com.redhat.ceylon.model.loader.model.OutputElement;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Value;
public class NamingBase {
public static final String OLD_MODULE_DESCRIPTOR_CLASS_NAME = "module_";
public static final String MODULE_DESCRIPTOR_CLASS_NAME = "$module_";
public static final String PACKAGE_DESCRIPTOR_CLASS_NAME = "$package_";
/**
* A synthetic name, or part of a synthetic name
*/
interface Affix {
}
/**
* An internally used identifier (not used as a prefix or suffix).
* Should start and end with a {@code $} and contain no {@code $}
*/
public enum Unfix implements Affix {
ref,
set_,
get_,
value,
$name$,
$annotationSequence$,
$array$,
$call$,
$callvariadic$,
$calltyped$,
$element$,
$evaluate$,
$get$,
$getArray$,
$getFirst$,
$getLength$,
$getType$,
$getIterables$,
$index$,
$initException$,
$instance$,
$invoke$,
$lookup$,
$refine$,
$return$,
$sb$,
$spreadVarargs$,
$TypeDescriptor$,
$apply$, apply,
$serialize$,
deconstructor,
$references$,
$set$,
reference,
instance, $isMember$
}
/**
* Enumerates suffixes used in synthetic names.
*
* Should start and end with a {@code $} and contain no {@code $}
*/
public enum Suffix implements Affix {
$delegation$,
$aliased$,
$annotation$,
$annotations$,
$arg$,
$args$,
$argthis$,
$callable$,
$canonical$,
$element$,
$exhausted$,
$getter$,
$impl, // special case, since it's used in type names
$iterable$,
$iteration$,
$iterator$,
$new$,
$param$,
$priv$,
$qual$,
$reified$,
$sb$,
$setter$,
$specifier$,
$this$,
$variadic$,
}
/**
* Enumerates prefixes used in synthetic names.
*
* Should start and end with a {@code $} and contain no {@code $}
*/
public enum Prefix implements Affix {
$next$,
$arg$,
$ceylontmp$,
$default$,
$init$,
$iterator$,
$reified$,
$superarg$,
$pattern$,
$instance$
}
public static String suffixName(Suffix suffix, String s) {
return s + suffix.toString();
}
public static String getAliasInstantiatorMethodName(Class model) {
String name = suffixName(Suffix.$aliased$, model.getName());
if (!model.isShared()) {
name = suffixName(Suffix.$priv$, name);
}
return name;
}
/**
* Removes any leading $ from the given string.
*/
public static String stripLeadingDollar(String str){
return (str.charAt(0) == '$') ? str.substring(1) : str;
}
public static String capitalize(String str){
return new StringBuilder().appendCodePoint(Character.toUpperCase(str.codePointAt(0))).append(str.substring(Character.isSurrogate(str.charAt(0)) ? 2 : 1)).toString();
}
public static String getDisambigAnnoCtorName(Interface iface, OutputElement target) {
return getJavaBeanName(iface.getName())+"__"+target;
}
/**
* Turns:
* - UrlDecoder -> urlDecoder
* - URLDecoder -> urlDecoder
* - urlDecoder -> urlDecoder
* - URL -> url
*/
public static String getJavaBeanName(String name) {
// See https://github.com/ceylon/ceylon-compiler/issues/340
// make it lowercase until the first non-uppercase
int[] newName = new int[name.codePointCount(0, name.length())];
// fill the code point array; String has no getCodePointArray()
for(int charIndex=0,codePointIndex=0;charIndex<name.length();){
int c = name.codePointAt(charIndex);
newName[codePointIndex++] = c;
charIndex += Character.charCount(c);
}
for(int i=0;i<newName.length;i++){
int codepoint = newName[i];
if(Character.isLowerCase(codepoint) || codepoint == '_'){
// if we had more than one upper-case, we leave the last uppercase: getURLDecoder -> urlDecoder
if(i > 1){
newName[i-1] = Character.toUpperCase(newName[i-1]);
}
break;
}
newName[i] = Character.toLowerCase(codepoint);
}
return new String(newName, 0, newName.length);
}
/**
* Turns:
* - urlDecoder -> URLDecoder
* - url -> URL
* Warning: also turns:
* - uRLDecoder -> URLDecoder (which is then decoded to urlDecoder instead of the original, so we need to guard)
*/
public static String getReverseJavaBeanName(String name){
// turns urlDecoder -> URLDecoder
for(int i=0; i<name.length(); i+= Character.isSurrogate(name.charAt(i)) ? 2 : 1){
if(Character.isUpperCase(name.codePointAt(i))){
// make everything before the upper case into upper case
return name.substring(0, i).toUpperCase() + name.substring(i);
}
}
// we did not find a single upper case, just make it all upper case
return name.toUpperCase();
}
public static String getGetterName(Declaration decl) {
return getGetterName(decl, false);
}
public static String getGetterName(Declaration decl, boolean indirect) {
// always use the refined decl
decl = decl.getRefinedDeclaration();
if (decl instanceof FieldValue){
return ((FieldValue)decl).getRealName();
}
if (decl instanceof JavaBeanValue) {
return ((JavaBeanValue)decl).getGetterName();
}
boolean enumeratedConstructor = (decl instanceof Value
&& ((Value)decl).getType() != null
&& ((Value)decl).getType().getDeclaration() instanceof com.redhat.ceylon.model.typechecker.model.Constructor);
if (ModelUtil.withinClassOrInterface(decl)
&& (!ModelUtil.isLocalToInitializer(decl) || enumeratedConstructor)
&& !indirect) {
if(enumeratedConstructor) {
return getGetterName(((Class)decl.getContainer()).getName() + "$" + decl.getName());
}
return getErasedGetterName(decl);
} else if (decl instanceof TypedDeclaration && JvmBackendUtil.isBoxedVariable((TypedDeclaration)decl)) {
return name(Unfix.ref);
} else {
return name(Unfix.get_);
}
}
public static String getSetterName(Declaration decl){
// use the refined decl except when the current declaration is variable and the refined isn't
Declaration refinedDecl = decl.getRefinedDeclaration();
if (JvmBackendUtil.isValue(decl) && JvmBackendUtil.isValue(refinedDecl)) {
Value v = (Value)decl;
Value rv = (Value)refinedDecl;
if (!v.isVariable() || rv.isVariable()) {
decl = refinedDecl;
}
} else {
decl = refinedDecl;
}
if (decl instanceof FieldValue){
return ((FieldValue)decl).getRealName();
}
if (decl instanceof JavaBeanValue
// only if the declaration actually has a setter name, if it's a non-variable
// one it will not. This is also used for late setters...
&& ((JavaBeanValue)decl).getSetterName() != null) {
return ((JavaBeanValue)decl).getSetterName();
} else if (ModelUtil.withinClassOrInterface(decl) && !ModelUtil.isLocalToInitializer(decl)) {
String setterName = getSetterName(decl.getName());
if (decl.isMember() && !decl.isShared()) {
setterName = suffixName(Suffix.$priv$, setterName);
}
return setterName;
} else if (decl instanceof TypedDeclaration && JvmBackendUtil.isBoxedVariable((TypedDeclaration)decl)) {
return name(Unfix.ref);
} else {
return name(Unfix.set_);
}
}
private static String getErasedGetterName(Declaration decl) {
String property = decl.getName();
// ERASURE
if (!(decl instanceof Value) || ((Value)decl).isShared()) {
if ("hash".equals(property)) {
return "hashCode";
} else if ("string".equals(property)) {
return "toString";
}
}
String getterName = getGetterName(property);
if (decl.isMember() && !decl.isShared()) {
getterName = suffixName(Suffix.$priv$, getterName);
}
return getterName;
}
/**
* @deprecated Use of this method outside this package is
* <strong>strongly</strong> discouraged.
* Its public modifier will be removed at a future date.
*/
public static String getGetterName(String property) {
return "get"+capitalize(stripLeadingDollar(property));
}
/**
* @deprecated Use of this method outside this package is
* <strong>strongly</strong> discouraged.
* Its public modifier will be removed at a future date.
*/
public static String getSetterName(String property){
return "set"+capitalize(stripLeadingDollar(property));
}
public static String name(Unfix unfix) {
return unfix.toString();
}
}