public final Iterator<Path> filesIterator() {
		return new Iterator<Path>() {

			private DirTree currentTree = walkTo(DirTree.this.base);
			private Iterator<String> dirIter = this.currentTree.directories
					.keySet().iterator();
			private Iterator<String> fileIter = this.currentTree.files.keySet()
					.iterator();
			private final ArrayDeque<Iterator<String>> dirIterStack = new ArrayDeque<>();
			private final ArrayDeque<Iterator<String>> fileIterStack = new ArrayDeque<>();

			@Override
			public boolean hasNext() {
				while (true) {
					if (this.fileIter.hasNext()) {
						return true;
					}
					if (this.dirIter.hasNext()) {
						final String nextDir = this.dirIter.next();
						if (this.currentTree.directories.get(nextDir) != null) {
							this.dirIterStack.add(this.dirIter);
							this.fileIterStack.add(this.fileIter);
							this.currentTree = this.currentTree.directories
									.get(nextDir);
							this.dirIter = this.currentTree.directories
									.keySet().iterator();
							this.fileIter = this.currentTree.files.keySet()
									.iterator();
						}
						continue;
					}
					if (this.dirIterStack.isEmpty()) {
						return false;
					}
					// pop
					this.currentTree = this.currentTree.parent;
					this.fileIter = this.fileIterStack.removeLast();
					this.dirIter = this.dirIterStack.removeLast();
				}
			}

			@Override
			public Path next() {
				final String next = this.fileIter.next();
				return this.currentTree.buildPath().resolve(next);
			}

			@Override
			public void remove() {
				throw new UnsupportedOperationException();
			}

		};
	}