2b3b5047901ac67fefd8b392c26027b4

I would like to transform a set of files into a tree structure. The problem is that not all files making up the directory tree are in the set.
Here is an example:

The files:
A:\Folder1\Folder2\Folder3\File1.txt
A:\Folder1\File2.txt
B:\Folder4\Folder5\File3.txt
B:\Folder4\Folder5\File4.txt

The resulting tree should look like this:
A:\Folder1
-Folder2
--Folder3
---File1.txt
-File2.txt
B:\Folder4\Folder5
-File3.txt
-File4.txt

Technically, these are two trees, and I will need to be able to identify the root of each. The accompanying code shows the algorithm I use now. It works, but it is horribly slow. Any thoughts on how to improve its efficiency will be greatly appreciated.

Note: in the code, the object FileDescription can be thought of as a regular java.io.File. A FileDescription contains a file and a hashmap with properties. (For example, in the case of an audio file, 'volume = 10' or something along those lines.)
If I left anything unclear, I will gladly answer questions.

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
public PreviewTreeModel(Collection<FileDescription> files)
	{
                // contains all files in the tree
		this.files = new ArrayList<FileDescription>(files);
		
		// add missing parents
		for (FileDescription description : files)
		{

                        FileDescription parent = new FileDescription(description.getFile().getParentFile(), false);

                        // add missing parents
			File parentFile = parent.getFile();
			
			List<FileDescription> parents = new Vector<FileDescription>();
			while (parentFile != null)
			{
				FileDescription parentDescription = new FileDescription(parentFile, false);

				// connected to an existing file
				if (this.files.contains(parentDescription))
				{
					this.files.addAll(parents); // complete connection
					break;
				}
				
				parents.add(parentDescription);
				parentFile = parentFile.getParentFile();
			}
		}
		
		// find roots
		this.roots = this.findRoots(files);
		
		// make sure all roots are folders
		for (FileDescription root : this.roots)
			if (root.isFile())
			{
				this.roots.remove(root);
				this.roots.add(new FileDescription(root.getFile().getParentFile(), false));
			}
	}
	
	private Collection<FileDescription> findRoots(Collection<FileDescription> descriptions)
	{
		logger.trace("findRoots(" + descriptions + ")");
		
		Collection<FileDescription> files;
		Collection<FileDescription> roots = descriptions;
		
		do
		{
			files = new Vector<FileDescription>(roots);
			roots = new Vector<FileDescription>();

			for (FileDescription fileA : files)
				for (FileDescription fileB : files)
					if (!fileA.equals(fileB))
					{
						FileDescription ancestor = this.findAncestor(fileA, fileB);
						if (ancestor != null && !roots.contains(ancestor))
							roots.add(ancestor);
					}
		}
		
		while (!roots.isEmpty());
	
		return files;
	}
	
	private FileDescription findAncestor(FileDescription a, FileDescription b)
	{
		logger.trace("findAncestor(" + a + "," + b + ")");
		
		String[] partsA = a.getFile().getAbsolutePath().split("\\\\");
		String[] partsB = b.getFile().getAbsolutePath().split("\\\\");
		
		int minLength = Math.min(partsA.length, partsB.length);
		
		List<String> ancestorParts = new Vector<String>();
		for (int i = 0; i < minLength; i++)
			if (partsA[i].equals(partsB[i]))
				ancestorParts.add(partsA[i]);
			
			else
				break;
		
		String ancestor = "";
		for (String part : ancestorParts)
			ancestor += part + "\\";
		
		if (ancestor.isEmpty())
			return null;
		else
			return new FileDescription(new File(ancestor), false);
	}

Refactorings

No refactoring yet !

4bd262de5d82a235eeb64f71e4058198

spoon!

April 14, 2008, April 14, 2008 02:30, permalink

No rating. Login to rate!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import java.util.*;
import java.io.File;
import java.util.regex.Pattern;

// I don't know how your FileDescription class is defined
// but I just make a simplified example here
class FileDescription
{
    private File file;
    public FileDescription(String s) { file = new File(s); }
    public File getFile() { return file; }
}

// DirTree = anything in the filesystem, including file or directory
class DirTree
{
    // if it is a non-empty directory, contents will be non-null
    public Map<String,DirTree> contents;
    // if it is a file given in the input, then description will be non-null
    public FileDescription description;

    public void print() { print("", 0); }
    // recursively print directory tree
    public void print(String prefix, int depth) {
        for (int i = 0; i < depth; i++)
            System.out.print("-");
        System.out.println(prefix);

        if (contents != null)
            for (Map.Entry<String,DirTree> e : contents.entrySet()) {
                String name = e.getKey();
                DirTree subTree = e.getValue();

                subTree.print(prefix + "\\" + name, depth + 1);
            }
    }
}

public class PreviewTreeNode
{
    public static DirTree generateTree(Collection<FileDescription> files) {
        DirTree root = new DirTree();

        for (FileDescription fd : files) {
            String path = fd.getFile().getAbsolutePath();

            // keeps track of location in tree
            DirTree current = root;
            // iterate through each directory in path
            for (String part : path.split(Pattern.quote("\\"))) {
                if (current.contents == null)
                    current.contents = new HashMap<String,DirTree>();
                // add it if it is not already in the directory tree
                if (!current.contents.containsKey(part))
                    current.contents.put(part, new DirTree());
                // go in to the next deeper level
                current = current.contents.get(part);
            }

            // now current refers to the location of the file we want
            current.description = fd;
        }

        return root;
    }


    public static void main(String[] args) {
        Collection<FileDescription> files =
            Arrays.asList(new FileDescription("A:\\Folder1\\Folder2\\Folder3\\File1.txt"),
                          new FileDescription("A:\\Folder1\\File2.txt"),
                          new FileDescription("B:\\Folder4\\Folder5\\File3.txt"),
                          new FileDescription("B:\\Folder4\\Folder5\\File4.txt"));

        DirTree root = generateTree(files);

        root.print();
    }
}

Your refactoring





Format Copy from initial code

or Cancel