/**
* Takes a {@link DiffItem} and returns a list of qualifying child diff
* items (which includes the original diff item), optionally recursing
* fully. The server is queried for some paths, and the local disk is
* examined for items not in the repository.
*
* @param rootItem
* the item to examine for child changes (not null).
* @param workspace
* a workspace that can be used for server queries and working folder
* mapping queries (not null).
* @param recursive
* if true, both server and filesystem directories are fully
* recursed.
* @param version
* the version of the root item to generate items for. May be null.
* @param tempDirectory
* a temporary directory to be used for new {@link DiffItem}s (not
* null).
* @return a list of {@link DiffFolderItem}s.
*/
private List<DiffItem> generateChildDiffItems(
final DiffItem rootItem,
final Workspace workspace,
final boolean recursive,
final VersionSpec version,
final File tempDirectory) {
Check.notNull(rootItem, "rootItem"); //$NON-NLS-1$
Check.notNull(workspace, "workspace"); //$NON-NLS-1$
Check.notNull(tempDirectory, "tempDirectory"); //$NON-NLS-1$
final List<DiffItem> directoryList = new ArrayList<DiffItem>();
final boolean isWorkspaceVersionSpec = (version instanceof WorkspaceVersionSpec);
/*
* Query all the items from the server that match the given root item
* (at the given version spec). These items come back sorted so related
* items are contiguous for faster searching.
*/
Item[] itemArray = null;
if (version != null) {
itemArray = getFilteredServerItemList(rootItem.getServerPath(), version, false, recursive, false, true);
} else if (rootItem.isInRepository()) {
itemArray = getFilteredServerItemList(
rootItem.getServerPath(),
new WorkspaceVersionSpec(workspace),
false,
recursive,
false,
true);
}
/*
* Start with the root item.
*/
directoryList.add(rootItem);
/*
* If there are server items, iterate through them, and for each item
* keep walking in an inner loop to find items in the same directory.
* Then continue iterating after passing the related items.
*/
if (itemArray != null && itemArray.length > 0) {
/*
* Query the pending changes for edit changes and keep a map so we
* can look them up during iteration.
*/
final Map<String, PendingChange> serverPathToExistingAPendingChanges = new HashMap<String, PendingChange>();
if (isWorkspaceVersionSpec) {
final PendingSet pendingChangeSet = workspace.getPendingChanges(new String[] {
rootItem.getServerPath()
}, recursive ? RecursionType.FULL : RecursionType.NONE, false);
if (pendingChangeSet != null
&& pendingChangeSet.getPendingChanges() != null
&& pendingChangeSet.getPendingChanges().length > 0) {
for (int i = 0; i < pendingChangeSet.getPendingChanges().length; i++) {
final PendingChange change = pendingChangeSet.getPendingChanges()[i];
if (change != null && change.getChangeType().contains(ChangeType.EDIT)) {
serverPathToExistingAPendingChanges.put(change.getServerItem(), change);
}
}
}
}
/*
* Build the list of directory items (each item can have children).
*
* Iterate over the items from the starting index, and iterate over
* the remainder in a second loop inside to collapse contiguous
* items into one diff item (the items list is sorted).
*
* The variables in the outer loop start with "first" and the
* variables in the inner start with "second".
*/
String localPath = null;
String serverPath = null;
String path = null;
int index = 1;
while (index < itemArray.length) {
final Item firstItem = itemArray[index];
final String firstFolderName = ServerPath.getParent(firstItem.getServerItem());
final DiffFolderItem firstDiffFolderItem =
new DiffFolderItem(firstFolderName, null, null, rootItem, false, version);
/*
* If no version was given, see if the directory really exists
* on disk.
*/
boolean firstLocalFolderExists = true;
if (version == null) {
final PathTranslation firstTranslation = workspace.translateServerPathToLocalPath(firstFolderName);
if (firstTranslation != null && firstTranslation.isCloaked() == false) {
firstLocalFolderExists = new File(firstTranslation.getTranslatedPath()).exists();
} else {
firstLocalFolderExists = false;
}
}
/*
* If the server path is the root folder "$/", chop the length
* to just "$".
*/
int firstPathOffset = firstFolderName.length();
if (firstPathOffset == 2) {
firstPathOffset = 1;
}
/*
* Save the starting index and loop over the remaining items
* until we match one in a different directory.
*/
final int startingIndex = index;
while (index < itemArray.length) {
final Item secondItem = itemArray[index];
/*
* Assign the second item's server path to the outer loop's
* serverPath variable.
*/
serverPath = secondItem.getServerItem();
/*
* If the second item is not a child of the first item, or
* if it has a path separator after our first path's offset
* (which mean it's a whole directory underneath the first
* item), then we stop iterating because it can't be a
* direct child and needs its own diff item later.
*/
if (ServerPath.isChild(firstFolderName, serverPath) == false
|| serverPath.indexOf(ServerPath.PREFERRED_SEPARATOR_CHARACTER, firstPathOffset + 1) >= 0) {
break;
}
/*
* If the item is a folder and we were given a version to
* fetch, or it exists locally, process its children.
*/
if ((version != null || firstLocalFolderExists) && secondItem.getItemType() == ItemType.FOLDER) {
/*
* See if it's mapped. It may not be mapped
*/
final PathTranslation secondTranslation = workspace.translateServerPathToLocalPath(serverPath);
if (secondTranslation != null && secondTranslation.isCloaked() == false) {
/*
* Assign to the outer loop's local path.
*/
localPath = secondTranslation.getTranslatedPath();
}
/*
* If there was a version given, use a temp file for it,
* otherwise use the actual local file.
*/
if (version != null) {
path = constructTempFile(tempDirectory, serverPath);
} else {
path = localPath;
}
Check.notNull(path, "path"); //$NON-NLS-1$
try {
if (version != null || new File(path).exists()) {
final DiffFolderItem secondDiffFolderItem =
new DiffFolderItem(serverPath, localPath, path, rootItem, true, version);
/*
* If the first item has already been added to
* the list, add the second item to its
* children.
*/
final int indexOfFirstItem = directoryList.indexOf(firstDiffFolderItem);
if (indexOfFirstItem >= 0) {
final DiffFolderItem parent = (DiffFolderItem) directoryList.get(indexOfFirstItem);
parent.addDirectory(secondDiffFolderItem);
}
/*
* Add it to the end of the list so it will be
* processed on further iteration in the outer
* loop.
*/
if (recursive) {
directoryList.add(secondDiffFolderItem);
}
}
} catch (final Throwable t) {
log.error("Inner exception evaluating diff items", t); //$NON-NLS-1$
}
}
/*
* Move on to the next list item in the inner loop.
*/
index++;
}
/*
* The inner loop has stopped moving because it found a
* different directory, so process all the files between the
* startingIndex to the current index.
*/
if (version != null || firstLocalFolderExists) {
for (int i = startingIndex; i < index; i++) {
if (itemArray[i].getItemType() != ItemType.FOLDER) {
serverPath = itemArray[i].getServerItem();
final PathTranslation thirdTranslation =
workspace.translateServerPathToLocalPath(serverPath);
if (thirdTranslation != null && thirdTranslation.isCloaked() == false) {
localPath = thirdTranslation.getTranslatedPath();
}
if (version != null) {
path = constructTempFile(tempDirectory, serverPath);
} else {
path = localPath;
}
final DiffItem fileDiffItem =
new DiffItem(itemArray[i], localPath, path, rootItem, version);
if (serverPathToExistingAPendingChanges.containsKey((itemArray[i].getServerItem()))) {
fileDiffItem.setIsPendingChange(true);
}
if (isWorkspaceVersionSpec && localPath != null) {
final FileSystemAttributes localFileAttributes =
FileSystemUtils.getInstance().getAttributes(localPath);
if (localFileAttributes.exists() && localFileAttributes.isReadOnly() == false) {
fileDiffItem.setWritable(true);
}
}
if (version != null || new File(path).exists()) {
/*
* Find the parent to put this file into.
*/
final int indexOfFirstItem = directoryList.indexOf(firstDiffFolderItem);
if (indexOfFirstItem >= 0) {
((DiffFolderItem) directoryList.get(indexOfFirstItem)).addFile(fileDiffItem);
}
}
}
}
}
}
}
/*
* Walk down from the root item to find any filesystem items that don't
* have a corresponding server item.
*/
if (version == null) {
String[] onDiskSubdirectories = null;
/*
* If the root item is on disk, and exists, list the subdirectories
* in it.
*/
if (rootItem.getLocalPath() != null) {
final File rootFile = new File(rootItem.getLocalPath());
if (rootFile.isDirectory() && rootFile.exists()) {
/*
* List all the subdirectories.
*/
onDiskSubdirectories = rootFile.list(new DirectoriesOnlyFilter());
makePathsAbsolute(onDiskSubdirectories, rootFile);
/*
* Sort them.
*/
Arrays.sort(onDiskSubdirectories, LocalPath.TOP_DOWN_COMPARATOR);
}
}
if (onDiskSubdirectories != null) {
for (int i = 0; i < onDiskSubdirectories.length; i++) {
final String subDirectoryName = onDiskSubdirectories[i];
final DiffFolderItem subDirectoryDiffFolderItem =
new DiffFolderItem(null, subDirectoryName, subDirectoryName, rootItem, false, version);
((DiffFolderItem) rootItem).addDirectory(subDirectoryDiffFolderItem);
}
}
if (recursive) {
final List<DiffFolderItem> subdirectoryList = new ArrayList<DiffFolderItem>();
/*
* Iterate over all the directories we've added so far and
* expand them.
*/
for (int i = 0; i < directoryList.size(); i++) {
getSubDirectoryDiffItems((DiffFolderItem) directoryList.get(i), directoryList, subdirectoryList);
}
directoryList.addAll(subdirectoryList);
}
}
return directoryList;
}