package org.codefx.mvn.jdeps.rules;
import com.google.common.collect.ImmutableList;
import java.util.Iterator;
import static java.lang.Integer.max;
import static java.lang.String.format;
import static java.lang.String.join;
import static java.util.Objects.requireNonNull;
/**
* Splits a type's or package's name into a an iteration of successivley more general names.
* <p>
* Depending on the {@link PackageInclusion} specified during construction, this might include only the package
* containing the type or all "super packages".
* <p>
* E.g.: "java.lang.String" to { "java.lang.String", "java.lang", "java" } or to { "java.lang.String", "java.lang" }.
*/
final class TypeNameHierarchy implements Iterable<String> {
private final ImmutableList<String> hierarchy;
private TypeNameHierarchy(ImmutableList<String> hierarchy) {
this.hierarchy =
requireNonNull(hierarchy, "The argument 'hierarchy' must not be null.");
for (int i = 1; i < hierarchy.size(); i++) {
String shorterPrefix = hierarchy.get(i) + ".";
String longerPrefix = hierarchy.get(i - 1);
if (!longerPrefix.startsWith(shorterPrefix))
throw new IllegalArgumentException();
}
}
/**
* @param fullName
* the fully qualified name of the type or package for which the hierarchy will be created
* @param packageInclusion
* determines which packages to include in the resulting hierarchy; for {@link PackageInclusion#FLAT FLAT}
* this will only be the package containing the type; for
* {@link PackageInclusion#HIERARCHICAL HIERARCHICAL} it will be all "super packages"
*
* @return a type name hierarchy for the specified name including the specified packages
*/
public static TypeNameHierarchy forFullyQualifiedName(String fullName, PackageInclusion packageInclusion) {
requireNonNull(fullName, "The argument 'fullName' must not be null.");
requireNonNull(packageInclusion, "The argument 'packageInclusion' must not be null.");
String[] nameParts = fullName.split("\\.");
int indexOfTopmostNamePart = indexOfTopmostNamePart(nameParts, packageInclusion);
String[] hierarchy = new String[nameParts.length - indexOfTopmostNamePart];
String partialName = "";
for (int i = 0; i < nameParts.length; i++) {
// append next name part
if (i == 0)
partialName = nameParts[0];
else
partialName += "." + nameParts[i];
// add name to hierarchy if contains topmost package
if (i >= indexOfTopmostNamePart) {
// the index is counted from the back because we create the less specific names first
// but want the more specific names to appear at the front of the array
int indexInHierarchy = nameParts.length - 1 - i;
hierarchy[indexInHierarchy] = partialName;
}
}
return new TypeNameHierarchy(ImmutableList.copyOf(hierarchy));
}
private static int indexOfTopmostNamePart(String[] nameParts, PackageInclusion packageInclusion) {
switch (packageInclusion) {
case FLAT:
return indexOfContainingPackage(nameParts);
case HIERARCHICAL:
return 0;
default:
throw new IllegalArgumentException(format("Unknown inclusion '%s'.", packageInclusion));
}
}
private static int indexOfContainingPackage(String[] nameParts) {
for (int i = 0; i < nameParts.length; i++) {
char firstLetter = getFirstLetter(nameParts, i);
boolean foundTypeName = Character.isUpperCase(firstLetter);
if (foundTypeName)
return max(i - 1, 0);
}
return 0;
}
private static char getFirstLetter(String[] nameParts, int index) {
String namePart = nameParts[index];
if (namePart.isEmpty())
throw new IllegalArgumentException(
format("The name %scontained an empty name.", join(".", nameParts)));
return namePart.charAt(0);
}
@Override
public Iterator<String> iterator() {
return hierarchy.iterator();
}
}