LANG-1480 getAbbreviatedName refactored to create appropriate length … (#446)
* LANG-1480 getAbbreviatedName refactored to create appropriate length short class names * LANG-1480 code fixed for special extreme case ".." abbreviated to 1 length should result ".." it was throwing exception. Tests are added * import changed to avoid wild cards apache master merged into current branch * Mutable object import was moved to it's original place * some accidental formatting reverted * some accidental formatting reverted * some accidental formatting reverted * some accidental formatting reverted * some accidental formatting reverted * some accidental formatting reverted * some accidental formatting reverted * added another test case * LANG-1480 fixing JavaDoc documentation as per requested by garydgregory * LANG-1480 shortcut implemented, argument renamed, more tests * LANG-1480 checkstyle update * LANG-1492 tests methods modified to be public * LANG-1480 imports rearranged * LANG-1480 imports rearranged * LANG-1480 imports rearranged * imports were added that were accidentally removed during merging master
This commit is contained in:
parent
a32c188c32
commit
bedae6950d
|
@ -428,17 +428,17 @@ public class ClassUtils {
|
|||
* <p>Gets the abbreviated name of a {@code Class}.</p>
|
||||
*
|
||||
* @param cls the class to get the abbreviated name for, may be {@code null}
|
||||
* @param len the desired length of the abbreviated name
|
||||
* @param lengthHint the desired length of the abbreviated name
|
||||
* @return the abbreviated name or an empty string
|
||||
* @throws IllegalArgumentException if len <= 0
|
||||
* @see #getAbbreviatedName(String, int)
|
||||
* @since 3.4
|
||||
*/
|
||||
public static String getAbbreviatedName(final Class<?> cls, final int len) {
|
||||
public static String getAbbreviatedName(final Class<?> cls, final int lengthHint) {
|
||||
if (cls == null) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return getAbbreviatedName(cls.getName(), len);
|
||||
return getAbbreviatedName(cls.getName(), lengthHint);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -448,9 +448,20 @@ public class ClassUtils {
|
|||
*
|
||||
* <p>The abbreviation algorithm will shorten the class name, usually without
|
||||
* significant loss of meaning.</p>
|
||||
*
|
||||
* <p>The abbreviated class name will always include the complete package hierarchy.
|
||||
* If enough space is available, rightmost sub-packages will be displayed in full
|
||||
* length.</p>
|
||||
* length. The abbreviated package names will be shortened to a single character.</p>
|
||||
* <p>Only package names are shortened, the class simple name remains untouched. (See examples.)</p>
|
||||
* <p>The result will be longer than the desired length only if all the package names
|
||||
* shortened to a single character plus the class simple name with the separating dots
|
||||
* together are longer than the desired length. In other words, when the class name
|
||||
* cannot be shortened to the desired length.</p>
|
||||
* <p>If the class name can be shortened then
|
||||
* the final length will be at most {@code lengthHint} characters.</p>
|
||||
* <p>If the {@code lengthHint} is zero or negative then the method
|
||||
* throws exception. If you want to achieve the shortest possible version then
|
||||
* use {@code 1} as a {@code lengthHint}.</p>
|
||||
*
|
||||
* <table>
|
||||
* <caption>Examples</caption>
|
||||
|
@ -459,48 +470,85 @@ public class ClassUtils {
|
|||
* <tr><td>"java.lang.String"</td><td> 5</td><td>"j.l.String"</td></tr>
|
||||
* <tr><td>"java.lang.String"</td><td>15</td><td>"j.lang.String"</td></tr>
|
||||
* <tr><td>"java.lang.String"</td><td>30</td><td>"java.lang.String"</td></tr>
|
||||
* <tr><td>"org.apache.commons.lang3.ClassUtils"</td><td>18</td><td>"o.a.c.l.ClassUtils"</td></tr>
|
||||
* </table>
|
||||
* @param className the className to get the abbreviated name for, may be {@code null}
|
||||
* @param len the desired length of the abbreviated name
|
||||
* @return the abbreviated name or an empty string
|
||||
* @throws IllegalArgumentException if len <= 0
|
||||
*
|
||||
* @param className the className to get the abbreviated name for, may be {@code null}
|
||||
* @param lengthHint the desired length of the abbreviated name
|
||||
* @return the abbreviated name or an empty string if the specified
|
||||
* class name is {@code null} or empty string. The abbreviated name may be
|
||||
* longer than the desired length if it cannot be abbreviated to the desired length.
|
||||
* @throws IllegalArgumentException if {@code len <= 0}
|
||||
* @since 3.4
|
||||
*/
|
||||
public static String getAbbreviatedName(final String className, final int len) {
|
||||
if (len <= 0) {
|
||||
throw new IllegalArgumentException("len must be > 0");
|
||||
}
|
||||
if (className == null) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
int availableSpace = len;
|
||||
final int packageLevels = StringUtils.countMatches(className, '.');
|
||||
final String[] output = new String[packageLevels + 1];
|
||||
int endIndex = className.length() - 1;
|
||||
for (int level = packageLevels; level >= 0; level--) {
|
||||
final int startIndex = className.lastIndexOf('.', endIndex);
|
||||
final String part = className.substring(startIndex + 1, endIndex + 1);
|
||||
availableSpace -= part.length();
|
||||
if (level > 0) {
|
||||
// all elements except top level require an additional char space
|
||||
availableSpace--;
|
||||
public static String getAbbreviatedName(final String className, final int lengthHint) {
|
||||
if (lengthHint <= 0) {
|
||||
throw new IllegalArgumentException("len must be > 0");
|
||||
}
|
||||
if (level == packageLevels) {
|
||||
// ClassName is always complete
|
||||
output[level] = part;
|
||||
} else {
|
||||
if (availableSpace > 0) {
|
||||
output[level] = part;
|
||||
} else {
|
||||
// if no space is left still the first char is used
|
||||
output[level] = part.substring(0, 1);
|
||||
}
|
||||
if (className == null) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
endIndex = startIndex - 1;
|
||||
}
|
||||
if (className.length() <= lengthHint) {
|
||||
return className;
|
||||
}
|
||||
final char[] abbreviated = className.toCharArray();
|
||||
int target = 0;
|
||||
int source = 0;
|
||||
while (source < abbreviated.length) {
|
||||
// copy the next part
|
||||
int runAheadTarget = target;
|
||||
while (source < abbreviated.length && abbreviated[source] != '.') {
|
||||
abbreviated[runAheadTarget++] = abbreviated[source++];
|
||||
}
|
||||
|
||||
return StringUtils.join(output, '.');
|
||||
++target;
|
||||
if (useFull(runAheadTarget, source, abbreviated.length, lengthHint)
|
||||
|| target > runAheadTarget) {
|
||||
target = runAheadTarget;
|
||||
}
|
||||
|
||||
// copy the '.' unless it was the last part
|
||||
if (source < abbreviated.length) {
|
||||
abbreviated[target++] = abbreviated[source++];
|
||||
}
|
||||
}
|
||||
return new String(abbreviated, 0, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Decides if the part that was just copied to its destination
|
||||
* location in the work array can be kept as it was copied or must be
|
||||
* abbreviated. It must be kept when the part is the last one, which
|
||||
* is the simple name of the class. In this case the {@code source}
|
||||
* index, from where the characters are copied points one position
|
||||
* after the last character, a.k.a. {@code source ==
|
||||
* originalLength}</p>
|
||||
*
|
||||
* <p>If the part is not the last one then it can be kept
|
||||
* unabridged if the number of the characters copied so far plus
|
||||
* the character that are to be copied is less than or equal to the
|
||||
* desired length.</p>
|
||||
*
|
||||
* @param runAheadTarget the target index (where the characters were
|
||||
* copied to) pointing after the last character
|
||||
* copied when the current part was copied
|
||||
* @param source the source index (where the characters were
|
||||
* copied from) pointing after the last
|
||||
* character copied when the current part was
|
||||
* copied
|
||||
* @param originalLength the original length of the class full name,
|
||||
* which is abbreviated
|
||||
* @param desiredLength the desired length of the abbreviated class
|
||||
* name
|
||||
* @return {@code true} if it can be kept in its original length
|
||||
* {@code false} if the current part has to be abbreviated and
|
||||
*/
|
||||
private static boolean useFull(final int runAheadTarget,
|
||||
final int source,
|
||||
final int originalLength,
|
||||
final int desiredLength) {
|
||||
return source >= originalLength ||
|
||||
runAheadTarget + originalLength - source <= desiredLength;
|
||||
}
|
||||
|
||||
// Superclasses/Superinterfaces
|
||||
|
|
|
@ -20,6 +20,8 @@ import org.apache.commons.lang3.ClassUtils.Interfaces;
|
|||
import org.apache.commons.lang3.reflect.testbed.GenericConsumer;
|
||||
import org.apache.commons.lang3.reflect.testbed.GenericParent;
|
||||
import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
@ -160,17 +162,33 @@ public class ClassUtilsTest {
|
|||
assertEquals("", ClassUtils.getAbbreviatedName((Class<?>) null, 1));
|
||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName(String.class, 1));
|
||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName(String.class, 5));
|
||||
assertEquals("o.a.c.l.ClassUtils", ClassUtils.getAbbreviatedName(ClassUtils.class, 18));
|
||||
assertEquals("j.lang.String", ClassUtils.getAbbreviatedName(String.class, 13));
|
||||
assertEquals("j.lang.String", ClassUtils.getAbbreviatedName(String.class, 15));
|
||||
assertEquals("java.lang.String", ClassUtils.getAbbreviatedName(String.class, 20));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that in case the required length is larger than the name and thus there is no need for any shortening
|
||||
* then the returned string object is the same as the one passed as argument. Note, however, that this is
|
||||
* tested as an internal implementation detail, but it is not a guaranteed feature of the implementation.
|
||||
*/
|
||||
@Test
|
||||
@DisplayName("When the length hint is longer than the actual length then the same String object is returned")
|
||||
public void test_getAbbreviatedName_TooLongHint(){
|
||||
final String className = "java.lang.String";
|
||||
Assertions.assertSame(className, ClassUtils.getAbbreviatedName(className, className.length()+1));
|
||||
Assertions.assertSame(className, ClassUtils.getAbbreviatedName(className, className.length()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("When the desired length is negative then exception is thrown")
|
||||
public void test_getAbbreviatedName_Class_NegativeLen() {
|
||||
assertThrows(IllegalArgumentException.class, () -> ClassUtils.getAbbreviatedName(String.class, -10));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("When the desired length is zero then exception is thrown")
|
||||
public void test_getAbbreviatedName_Class_ZeroLen() {
|
||||
assertThrows(IllegalArgumentException.class, () -> ClassUtils.getAbbreviatedName(String.class, 0));
|
||||
}
|
||||
|
@ -178,8 +196,25 @@ public class ClassUtilsTest {
|
|||
@Test
|
||||
public void test_getAbbreviatedName_String() {
|
||||
assertEquals("", ClassUtils.getAbbreviatedName((String) null, 1));
|
||||
assertEquals("", ClassUtils.getAbbreviatedName("", 1));
|
||||
assertEquals("WithoutPackage", ClassUtils.getAbbreviatedName("WithoutPackage", 1));
|
||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName("java.lang.String", 1));
|
||||
assertEquals("o.a.c.l.ClassUtils", ClassUtils.getAbbreviatedName("org.apache.commons.lang3.ClassUtils", 18));
|
||||
assertEquals("org.apache.commons.lang3.ClassUtils",
|
||||
ClassUtils.getAbbreviatedName("org.apache.commons.lang3.ClassUtils",
|
||||
"org.apache.commons.lang3.ClassUtils".length()));
|
||||
assertEquals("o.a.c.l.ClassUtils", ClassUtils.getAbbreviatedName("o.a.c.l.ClassUtils", 18));
|
||||
assertEquals("o..c.l.ClassUtils", ClassUtils.getAbbreviatedName("o..c.l.ClassUtils", 18));
|
||||
assertEquals(".", ClassUtils.getAbbreviatedName(".", 18));
|
||||
assertEquals(".", ClassUtils.getAbbreviatedName(".", 1));
|
||||
assertEquals("..", ClassUtils.getAbbreviatedName("..", 1));
|
||||
assertEquals("...", ClassUtils.getAbbreviatedName("...", 2));
|
||||
assertEquals("...", ClassUtils.getAbbreviatedName("...", 3));
|
||||
assertEquals("java.lang.String", ClassUtils.getAbbreviatedName("java.lang.String", Integer.MAX_VALUE));
|
||||
assertEquals("j.lang.String", ClassUtils.getAbbreviatedName("java.lang.String", "j.lang.String".length()));
|
||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName("java.lang.String", "j.lang.String".length() - 1));
|
||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName("java.lang.String", "j.l.String".length()));
|
||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName("java.lang.String", "j.l.String".length() - 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue