in api/applib/src/main/java/org/apache/causeway/applib/Identifier.java [47:309]
public record Identifier(
LogicalType logicalType,
String memberLogicalName,
Can<String> memberParameterClassNames,
Type type,
/**
* Optional. Used for <i>Action Parameters</i>, otherwise {@code -1}.
*/
int parameterIndex)
implements
Comparable<Identifier>,
HasLogicalType,
HasTranslationContext,
Serializable {
/**
* What type of feature this identifies.
*/
public static enum Type {
/**
* A <i>Value-type</i> or <i>Domain Object</i>.
*/
CLASS,
/**
* <i>Action</i> either declared or mixed in.
*/
ACTION,
/**
* <i>Action Parameter</i>, also has a non-negative {@link #parameterIndex}.
*/
ACTION_PARAMETER,
/**
* <i>One to One Association (Property)</i> either declared or mixed in.
*/
PROPERTY,
/**
* <i>One to Many Association (Collection)</i> either declared or mixed in.
*/
COLLECTION
;
public boolean isClass() { return this == CLASS; }
public boolean isAction() { return this == ACTION; }
public boolean isActionParameter() { return this == ACTION_PARAMETER; }
public boolean isProperty() { return this == PROPERTY;}
public boolean isCollection() { return this == COLLECTION;}
public boolean isPropertyOrCollection() { return this == PROPERTY || this == COLLECTION;}
}
// -- FACTORY METHODS
public static Identifier classIdentifier(final LogicalType typeIdentifier) {
return new Identifier(typeIdentifier, "", Can.empty(), Type.CLASS);
}
public static Identifier propertyIdentifier(
final LogicalType typeIdentifier,
final String propertyName) {
return new Identifier(typeIdentifier, propertyName, Can.empty(),
Type.PROPERTY);
}
public static Identifier collectionIdentifier(
final LogicalType typeIdentifier,
final String collectionName) {
return new Identifier(typeIdentifier, collectionName, Can.empty(),
Type.COLLECTION);
}
/** for reporting orphaned methods */
public static Identifier methodIdentifier(
final LogicalType typeIdentifier,
final ResolvedMethod method) {
return actionIdentifier(typeIdentifier, _Reflect.methodToShortString(method.method()), method.paramTypes());
}
public static Identifier actionIdentifier(
final LogicalType typeIdentifier,
final String actionName,
final Class<?>... parameterClasses) {
return actionIdentifier(typeIdentifier, actionName, classNamesOf(parameterClasses));
}
public static Identifier actionIdentifier(
final LogicalType typeIdentifier,
final String actionName,
final Can<String> parameterClassNames) {
return new Identifier(typeIdentifier, actionName, parameterClassNames, Type.ACTION);
}
// --
/**
* Fully qualified Identity String. (class-name + member-logical-name + param-class-names)
*/
public String getFullIdentityString() {
return _Strings.isEmpty(memberLogicalName)
? className()
: className() + "#" + getMemberNameAndParameterClassNamesIdentityString();
}
/**
* Member Identity String (class omitted), including parameters if any.
*/
public String getMemberNameAndParameterClassNamesIdentityString() { return memberLogicalName + (type.isAction()
? "(" + memberParameterClassNames.stream().collect(Collectors.joining(",")) + ")"
: ""); }
/**
* Context to be used for i18n translation.
* @see TranslationService
*/
@Override
public TranslationContext getTranslationContext() {
return TranslationContext.named(
className() + "#" + memberLogicalName + (type.isAction() ? "()" : ""));
}
// -- CONSTRUCTION
private Identifier(
final LogicalType logicalType,
final String memberLogicalName,
final Can<String> memberParameterClassNames,
final Type type) {
this(logicalType, memberLogicalName, memberParameterClassNames, type, -1);
}
// -- WITHERS
public Identifier withParameterIndex(final int parameterIndex) {
return new Identifier(
logicalType, memberLogicalName, memberParameterClassNames, Type.ACTION_PARAMETER, parameterIndex);
}
// -- LOGICAL ID
public String getLogicalIdentityString(final @NonNull String delimiter) {
return logicalTypeName()
+ delimiter
+ getMemberNameAndParameterClassNamesIdentityString();
}
// -- NATURAL NAMES
public String getClassNaturalName() {
var className = className();
var isolatedName = className.substring(className.lastIndexOf('.') + 1);
return naturalName(isolatedName);
}
public String getMemberNaturalName() {
return naturalName(memberLogicalName);
}
public Can<String> getMemberParameterClassNaturalNames() {
return naturalNames(memberParameterClassNames);
}
// -- OBJECT CONTRACT
@Override
public int compareTo(final Identifier other) {
return toString().compareTo(other.toString());
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Identifier) {
return isEqualTo((Identifier) obj);
}
return false;
}
public boolean isEqualTo(final Identifier other) {
return Objects.equals(this.className(), other.className())
&& Objects.equals(this.memberLogicalName, other.memberLogicalName)
&& this.memberParameterClassNames.equals(other.memberParameterClassNames)
&& this.parameterIndex == other.parameterIndex;
}
@Override
public int hashCode() {
return getFullIdentityString().hashCode();
}
@Override
public String toString() {
return parameterIndex>=0
? String.format("%s[%d]", getFullIdentityString(), parameterIndex)
: getFullIdentityString();
}
// -- HELPER
private static Can<String> classNamesOf(final Class<?>[] parameterClasses) {
return Can.ofArray(parameterClasses)
.map(Class::getName);
}
private static final char SPACE = ' ';
/*
* Returns a word spaced version of the specified name, so there are spaces
* between the words, where each word starts with a capital letter. E.g.,
* "NextAvailableDate" is returned as "Next Available Date".
*/
private static String naturalName(final String name) {
final int length = name.length();
if (length <= 1) {
return name.toUpperCase();// ensure first character is upper case
}
final StringBuffer naturalName = new StringBuffer(length);
char previousCharacter;
char character = Character.toUpperCase(name.charAt(0));// ensure first
// character is
// upper case
naturalName.append(character);
char nextCharacter = name.charAt(1);
for (int pos = 2; pos < length; pos++) {
previousCharacter = character;
character = nextCharacter;
nextCharacter = name.charAt(pos);
if (previousCharacter != SPACE) {
if (Character.isUpperCase(character)
&& !Character.isUpperCase(previousCharacter)) {
naturalName.append(SPACE);
}
if (Character.isUpperCase(character)
&& Character.isLowerCase(nextCharacter)
&& Character.isUpperCase(previousCharacter)) {
naturalName.append(SPACE);
}
if (Character.isDigit(character)
&& !Character.isDigit(previousCharacter)) {
naturalName.append(SPACE);
}
}
naturalName.append(character);
}
naturalName.append(nextCharacter);
return naturalName.toString();
}
private static Can<String> naturalNames(final Can<String> names) {
return names.map(Identifier::naturalName);
}
// -- DEPRECATIONS
@Deprecated public String getMemberLogicalName() { return memberLogicalName; }
@Deprecated public int getParameterIndex() { return parameterIndex; }
@Deprecated public Can<String> getMemberParameterClassNames() { return memberParameterClassNames; }
@Deprecated public Type getType() { return type; }
}