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>
|
* <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 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
|
* @return the abbreviated name or an empty string
|
||||||
* @throws IllegalArgumentException if len <= 0
|
* @throws IllegalArgumentException if len <= 0
|
||||||
* @see #getAbbreviatedName(String, int)
|
* @see #getAbbreviatedName(String, int)
|
||||||
* @since 3.4
|
* @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) {
|
if (cls == null) {
|
||||||
return StringUtils.EMPTY;
|
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
|
* <p>The abbreviation algorithm will shorten the class name, usually without
|
||||||
* significant loss of meaning.</p>
|
* significant loss of meaning.</p>
|
||||||
|
*
|
||||||
* <p>The abbreviated class name will always include the complete package hierarchy.
|
* <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
|
* 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>
|
* <table>
|
||||||
* <caption>Examples</caption>
|
* <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> 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>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>"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>
|
* </table>
|
||||||
|
*
|
||||||
* @param className the className to get the abbreviated name for, may be {@code null}
|
* @param className the className 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
|
* @return the abbreviated name or an empty string if the specified
|
||||||
* @throws IllegalArgumentException if len <= 0
|
* 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
|
* @since 3.4
|
||||||
*/
|
*/
|
||||||
public static String getAbbreviatedName(final String className, final int len) {
|
public static String getAbbreviatedName(final String className, final int lengthHint) {
|
||||||
if (len <= 0) {
|
if (lengthHint <= 0) {
|
||||||
throw new IllegalArgumentException("len must be > 0");
|
throw new IllegalArgumentException("len must be > 0");
|
||||||
}
|
}
|
||||||
if (className == null) {
|
if (className == null) {
|
||||||
return StringUtils.EMPTY;
|
return StringUtils.EMPTY;
|
||||||
}
|
}
|
||||||
|
if (className.length() <= lengthHint) {
|
||||||
int availableSpace = len;
|
return className;
|
||||||
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--;
|
|
||||||
}
|
}
|
||||||
if (level == packageLevels) {
|
final char[] abbreviated = className.toCharArray();
|
||||||
// ClassName is always complete
|
int target = 0;
|
||||||
output[level] = part;
|
int source = 0;
|
||||||
} else {
|
while (source < abbreviated.length) {
|
||||||
if (availableSpace > 0) {
|
// copy the next part
|
||||||
output[level] = part;
|
int runAheadTarget = target;
|
||||||
} else {
|
while (source < abbreviated.length && abbreviated[source] != '.') {
|
||||||
// if no space is left still the first char is used
|
abbreviated[runAheadTarget++] = abbreviated[source++];
|
||||||
output[level] = part.substring(0, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
endIndex = startIndex - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// 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.GenericConsumer;
|
||||||
import org.apache.commons.lang3.reflect.testbed.GenericParent;
|
import org.apache.commons.lang3.reflect.testbed.GenericParent;
|
||||||
import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild;
|
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 org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
@ -160,17 +162,33 @@ public class ClassUtilsTest {
|
||||||
assertEquals("", ClassUtils.getAbbreviatedName((Class<?>) null, 1));
|
assertEquals("", ClassUtils.getAbbreviatedName((Class<?>) null, 1));
|
||||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName(String.class, 1));
|
assertEquals("j.l.String", ClassUtils.getAbbreviatedName(String.class, 1));
|
||||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName(String.class, 5));
|
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, 13));
|
||||||
assertEquals("j.lang.String", ClassUtils.getAbbreviatedName(String.class, 15));
|
assertEquals("j.lang.String", ClassUtils.getAbbreviatedName(String.class, 15));
|
||||||
assertEquals("java.lang.String", ClassUtils.getAbbreviatedName(String.class, 20));
|
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
|
@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() {
|
public void test_getAbbreviatedName_Class_NegativeLen() {
|
||||||
assertThrows(IllegalArgumentException.class, () -> ClassUtils.getAbbreviatedName(String.class, -10));
|
assertThrows(IllegalArgumentException.class, () -> ClassUtils.getAbbreviatedName(String.class, -10));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@DisplayName("When the desired length is zero then exception is thrown")
|
||||||
public void test_getAbbreviatedName_Class_ZeroLen() {
|
public void test_getAbbreviatedName_Class_ZeroLen() {
|
||||||
assertThrows(IllegalArgumentException.class, () -> ClassUtils.getAbbreviatedName(String.class, 0));
|
assertThrows(IllegalArgumentException.class, () -> ClassUtils.getAbbreviatedName(String.class, 0));
|
||||||
}
|
}
|
||||||
|
@ -178,8 +196,25 @@ public class ClassUtilsTest {
|
||||||
@Test
|
@Test
|
||||||
public void test_getAbbreviatedName_String() {
|
public void test_getAbbreviatedName_String() {
|
||||||
assertEquals("", ClassUtils.getAbbreviatedName((String) null, 1));
|
assertEquals("", ClassUtils.getAbbreviatedName((String) null, 1));
|
||||||
|
assertEquals("", ClassUtils.getAbbreviatedName("", 1));
|
||||||
assertEquals("WithoutPackage", ClassUtils.getAbbreviatedName("WithoutPackage", 1));
|
assertEquals("WithoutPackage", ClassUtils.getAbbreviatedName("WithoutPackage", 1));
|
||||||
assertEquals("j.l.String", ClassUtils.getAbbreviatedName("java.lang.String", 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
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue