package org.netbeans.gradle.project.java.model;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.jtrim.utils.ExceptionHelper;
import org.netbeans.gradle.model.java.JavaSourceGroup;
import org.netbeans.gradle.model.java.JavaSourceGroupName;
import org.netbeans.gradle.model.java.JavaSourceSet;
import org.netbeans.gradle.model.util.CollectionUtils;
import org.netbeans.gradle.project.NbStrings;
import org.netbeans.gradle.project.util.ExcludeIncludeRules;
import org.netbeans.gradle.project.util.StringUtils;
public final class NamedSourceRoot {
private final JavaSourceGroupID groupID;
private final String displayName;
private final File root;
private final ExcludeIncludeRules includeRules;
public NamedSourceRoot(
JavaSourceGroupID groupID,
String displayName,
File root,
ExcludeIncludeRules includeRules) {
ExceptionHelper.checkNotNullArgument(groupID, "groupID");
ExceptionHelper.checkNotNullArgument(displayName, "displayName");
ExceptionHelper.checkNotNullArgument(root, "root");
ExceptionHelper.checkNotNullArgument(includeRules, "includeRules");
this.groupID = groupID;
this.displayName = displayName;
this.root = root;
this.includeRules = includeRules;
}
public JavaSourceGroupID getGroupID() {
return groupID;
}
public String getDisplayName() {
return displayName;
}
public File getRoot() {
return root;
}
public ExcludeIncludeRules getIncludeRules() {
return includeRules;
}
private static String displayNameOfSourceSet(String sourceSetName) {
if (sourceSetName.isEmpty()) {
return "?";
}
else {
return StringUtils.capitalizeFirstCharacter(sourceSetName);
}
}
private static int countNonResource(Collection<JavaSourceGroup> sourceGroups) {
int result = 0;
for (JavaSourceGroup sourceGroup: sourceGroups) {
if (sourceGroup.getGroupName() != JavaSourceGroupName.RESOURCES) {
result++;
}
}
return result;
}
public static String getSourceGroupDisplayName(JavaSourceGroup sourceGroup) {
JavaSourceGroupName groupName = sourceGroup.getGroupName();
if (groupName == JavaSourceGroupName.RESOURCES) {
return NbStrings.getResourcesPackageCaption();
}
return StringUtils.capitalizeFirstCharacter(groupName.toString().toLowerCase(Locale.ROOT));
}
public static List<NamedSourceRoot> getAllSourceRoots(NbJavaModule module) {
List<NamedSourceRoot> result = new ArrayList<>();
for (JavaSourceSet sourceSet: module.getSources()) {
String sourceSetName = sourceSet.getName();
String displaySourceSetName = displayNameOfSourceSet(sourceSetName);
String mainName;
switch (sourceSetName) {
case JavaSourceSet.NAME_MAIN:
mainName = NbStrings.getSrcPackageCaption();
break;
case JavaSourceSet.NAME_TEST:
mainName = NbStrings.getTestPackageCaption();
break;
default:
mainName = null;
break;
}
Collection<JavaSourceGroup> sourceGroups = sourceSet.getSourceGroups();
int nonResourceCount = countNonResource(sourceGroups);
for (JavaSourceGroup sourceGroup: sourceSet.getSourceGroups()) {
JavaSourceGroupName groupName = sourceGroup.getGroupName();
JavaSourceGroupID groupID = new JavaSourceGroupID(sourceSetName, groupName);
Set<File> sourceRoots = sourceGroup.getSourceRoots();
String groupNamePrefix;
if (groupName == JavaSourceGroupName.RESOURCES) {
groupNamePrefix = NbStrings.getResourcesPackageCaption() + " [" + displaySourceSetName + "]";
}
else if (nonResourceCount == 1) {
groupNamePrefix = mainName != null
? mainName
: NbStrings.getOtherPackageCaption(displaySourceSetName);
}
else {
String groupDisplayName = getSourceGroupDisplayName(sourceGroup);
groupNamePrefix = mainName != null
? mainName + " [" + groupDisplayName + "]"
: NbStrings.getOtherPackageCaption(displaySourceSetName + "/" + groupDisplayName);
}
ExcludeIncludeRules includeRules = ExcludeIncludeRules.create(sourceGroup);
if (sourceRoots.size() == 1) {
result.add(new NamedSourceRoot(
groupID,
groupNamePrefix,
sourceRoots.iterator().next(),
includeRules));
}
else {
for (NamedFile root: nameSourceRoots(sourceRoots)) {
String rootName = NbStrings.getMultiRootSourceGroups(groupNamePrefix, root.getName());
result.add(new NamedSourceRoot(
groupID,
rootName,
root.getPath(),
includeRules));
}
}
}
}
return sortNamedSourceRoots(result);
}
private static List<NamedSourceRoot> sortNamedSourceRoots(Collection<NamedSourceRoot> namedRoots) {
NamedSourceRoot[] orderedRoots = namedRoots.toArray(new NamedSourceRoot[namedRoots.size()]);
Arrays.sort(orderedRoots, new Comparator<NamedSourceRoot>() {
@Override
public int compare(NamedSourceRoot root1, NamedSourceRoot root2) {
JavaSourceGroupID groupID1 = root1.getGroupID();
JavaSourceGroupID groupID2 = root2.getGroupID();
String sourceSetName1 = groupID1.getSourceSetName();
String sourceSetName2 = groupID2.getSourceSetName();
if (sourceSetName1.equals(sourceSetName2)) {
return compareGroups(groupID1.getGroupName(), groupID2.getGroupName());
}
return compareSourceSetNames(sourceSetName1, sourceSetName2);
}
});
return Arrays.asList(orderedRoots);
}
private static int getOrderOfGroup(JavaSourceGroupName groupName) {
// If we have Groovy or Scala sources, Groovy and Scala source roots
// are preferred over Java because joint compilation is more convenient.
switch (groupName) {
case GROOVY:
return 0;
case SCALA:
return 1;
case JAVA:
return 2;
case OTHER:
return 3;
case RESOURCES:
return 4;
default:
throw new AssertionError(groupName.name());
}
}
private static int compareGroups(JavaSourceGroupName name1, JavaSourceGroupName name2) {
int order1 = getOrderOfGroup(name1);
int order2 = getOrderOfGroup(name2);
return order1 - order2;
}
public static int compareSourceSetNames(String name1, String name2) {
if (JavaSourceSet.NAME_MAIN.equals(name1)) {
return -1;
}
if (JavaSourceSet.NAME_MAIN.equals(name2)) {
return 1;
}
if (JavaSourceSet.NAME_TEST.equals(name1)) {
return -1;
}
if (JavaSourceSet.NAME_TEST.equals(name2)) {
return 1;
}
return StringUtils.STR_CMP.compare(name1, name2);
}
private static <K, V> void addToMap(Map<K, List<V>> map, K key, V value) {
List<V> valueList = map.get(key);
if (valueList == null) {
valueList = new ArrayList<>();
map.put(key, valueList);
}
valueList.add(value);
}
public static List<NamedFile> nameSourceRoots(Collection<File> files) {
// The common case
if (files.size() == 1) {
File file = files.iterator().next();
return Collections.singletonList(new NamedFile(file, file.getName()));
}
Map<String, List<FileWithBase>> nameToFile = CollectionUtils.newHashMap(files.size());
int fileIndex = 0;
for (File file: files) {
String name = file.getName();
File parent = file.getParentFile();
addToMap(nameToFile, name, new FileWithBase(fileIndex, parent, file));
fileIndex++;
}
boolean didSomething;
do {
didSomething = false;
List<Map.Entry<String, List<FileWithBase>>> currentEntries
= new ArrayList<>(nameToFile.entrySet());
for (Map.Entry<String, List<FileWithBase>> entry: currentEntries) {
String entryName = entry.getKey();
List<FileWithBase> entryFiles = entry.getValue();
int renameableCount = 0;
for (FileWithBase file: entryFiles) {
if (file.base != null) renameableCount++;
}
if (renameableCount > 1) {
nameToFile.remove(entryName);
for (FileWithBase file: entryFiles) {
if (file.base != null) {
String newName = file.base.getName() + '/' + entryName;
File newParent = file.base.getParentFile();
addToMap(nameToFile,
newName,
new FileWithBase(file.index, newParent, file.file));
}
else {
addToMap(nameToFile, entryName, file);
}
}
didSomething = true;
}
}
} while (didSomething);
NamedFile[] result = new NamedFile[fileIndex];
for (Map.Entry<String, List<FileWithBase>> entry: nameToFile.entrySet()) {
String entryName = entry.getKey();
for (FileWithBase file: entry.getValue()) {
result[file.index] = new NamedFile(file.file, entryName);
}
}
return Arrays.asList(result);
}
@Override
public int hashCode() {
int hash = 7;
hash = 23 * hash + groupID.hashCode();
hash = 23 * hash + displayName.hashCode();
hash = 23 * hash + root.hashCode();
hash = 23 * hash + includeRules.hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (getClass() != obj.getClass()) return false;
final NamedSourceRoot other = (NamedSourceRoot)obj;
if (!groupID.equals(other.groupID)) {
return false;
}
if (!displayName.equals(other.displayName)) {
return false;
}
if (!includeRules.equals(other.includeRules)) {
return false;
}
return root.equals(other.root);
}
private static final class FileWithBase {
public final int index;
public final File base;
public final File file;
public FileWithBase(int index, File base, File file) {
assert file != null;
this.index = index;
this.base = base;
this.file = file;
}
}
}