package com.intellij.lang.javascript.flex.flashbuilder;
import com.intellij.flex.FlexCommonUtils;
import com.intellij.lang.javascript.flex.projectStructure.model.AirPackagingOptions;
import com.intellij.lang.javascript.flex.projectStructure.model.ModifiableAirPackagingOptions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.roots.ModuleRootModel;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.util.PathUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class FilesToPackageUtil {
private static final Logger LOG = Logger.getInstance(FilesToPackageUtil.class.getName());
static void setupFilesToPackage(final ModifiableAirPackagingOptions packagingOptions,
final Collection<String> pathsExcludedFromPackaging,
final ModuleRootModel rootModel) {
final List<AirPackagingOptions.FilePathAndPathInPackage> filesToPackage = new ArrayList<>();
for (VirtualFile srcRoot : rootModel.getSourceRoots()) {
final FolderNode rootNode = new FolderNode(null, srcRoot.getPath(), ".");
initNodes(srcRoot, rootNode, pathsExcludedFromPackaging);
appendFilesToPackage(filesToPackage, rootNode);
}
packagingOptions.setFilesToPackage(filesToPackage);
}
private static void initNodes(final VirtualFile rootFolder,
final FolderNode rootFolderNode,
final Collection<String> pathsExcludedFromPackaging) {
final Map<VirtualFile, Node> map = new THashMap<>();
map.put(rootFolder, rootFolderNode);
VfsUtilCore.visitChildrenRecursively(rootFolder, new VirtualFileVisitor() {
@NotNull
public Result visitFileEx(@NotNull final VirtualFile file) {
if (file.equals(rootFolder)) return CONTINUE;
final VirtualFile parentFile = file.getParent();
final Node parentNode = map.get(parentFile);
LOG.assertTrue(parentNode instanceof FolderNode, file.getPath());
if (pathsExcludedFromPackaging.contains(((FolderNode)parentNode).getChildRelativePath(file.getName())) ||
file.getPath().endsWith("-app.xml") ||
!canBeAddedToPackage(file)) {
((FolderNode)parentNode).setHasExcludedChildren();
return SKIP_CHILDREN;
}
else {
if (file.isDirectory()) {
final FolderNode childFolderNode = ((FolderNode)parentNode).addChildFolderNode(file.getPath());
map.put(file, childFolderNode);
}
else {
((FolderNode)parentNode).addChildFileNode(file.getPath());
}
return CONTINUE;
}
}
});
}
private static void appendFilesToPackage(final List<AirPackagingOptions.FilePathAndPathInPackage> filesToPackage,
final FolderNode node) {
if (node.hasExcludedChildren()) {
for (Node childNode : node.getChildNodes()) {
if (childNode instanceof FolderNode) {
appendFilesToPackage(filesToPackage, (FolderNode)childNode);
}
else {
filesToPackage.add(new AirPackagingOptions.FilePathAndPathInPackage(childNode.path, childNode.relativePath));
}
}
}
else {
filesToPackage.add(new AirPackagingOptions.FilePathAndPathInPackage(node.path, node.relativePath));
}
}
private static boolean canBeAddedToPackage(final VirtualFile file) {
if (FileTypeManager.getInstance().isFileIgnored(file.getName())) {
return false;
}
if (!file.isDirectory()) {
if (FlexCommonUtils.isSourceFile(file.getName()) || FileUtilRt.getExtension(file.getName()).equalsIgnoreCase("properties")) {
return false;
}
}
return true;
}
private static class Node {
final @Nullable FolderNode parentNode;
final String path;
final String relativePath;
protected Node(@Nullable final FolderNode parentNode, final String path, final String relativePath) {
this.parentNode = parentNode;
this.path = path;
this.relativePath = relativePath;
}
}
private static class FolderNode extends Node {
private final Collection<Node> childNodes = new ArrayList<>();
private boolean hasExcludedChildren;
protected FolderNode(@Nullable final FolderNode parent, final String path, final String relativePath) {
super(parent, path, relativePath);
}
String getChildRelativePath(final String childName) {
return relativePath.equals(".") ? childName : relativePath + "/" + childName;
}
void setHasExcludedChildren() {
if (hasExcludedChildren) return;
FolderNode currentNode = this;
do {
currentNode.hasExcludedChildren = true;
currentNode = currentNode.parentNode;
}
while (currentNode != null);
}
private boolean hasExcludedChildren() {
return hasExcludedChildren;
}
FolderNode addChildFolderNode(final String childFolderPath) {
final int lastSlashIndex = childFolderPath.lastIndexOf("/");
LOG.assertTrue(lastSlashIndex > 0 && path.equals(childFolderPath.substring(0, lastSlashIndex)), path + ", " + childFolderPath);
final FolderNode childFolderNode = new FolderNode(this, childFolderPath, getChildRelativePath(PathUtil.getFileName(childFolderPath)));
childNodes.add(childFolderNode);
return childFolderNode;
}
Node addChildFileNode(final String childFilePath) {
final int lastSlashIndex = childFilePath.lastIndexOf("/");
LOG.assertTrue(lastSlashIndex > 0 && path.equals(childFilePath.substring(0, lastSlashIndex)), path + ", " + childFilePath);
final Node childFileNode = new Node(this, childFilePath, getChildRelativePath(PathUtil.getFileName(childFilePath)));
childNodes.add(childFileNode);
return childFileNode;
}
Collection<Node> getChildNodes() {
return childNodes;
}
}
}