From d2339f881b7aa890fcde66c7478ee282e42bee41 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Mon, 15 Feb 2016 21:40:14 -0800 Subject: [PATCH 01/23] Generalize PathItem so that TreeItem subclasses can be used without needing to directly implement them and duplicate code. --- .../org/fxmisc/livedirs/DirectoryModel.java | 5 +- .../java/org/fxmisc/livedirs/LiveDirs.java | 23 ++- .../java/org/fxmisc/livedirs/LiveDirsIO.java | 4 +- .../org/fxmisc/livedirs/LiveDirsModel.java | 47 ++--- .../java/org/fxmisc/livedirs/PathItem.java | 165 ++++++++++-------- 5 files changed, 139 insertions(+), 105 deletions(-) diff --git a/src/main/java/org/fxmisc/livedirs/DirectoryModel.java b/src/main/java/org/fxmisc/livedirs/DirectoryModel.java index 9f73edd..bef06a7 100644 --- a/src/main/java/org/fxmisc/livedirs/DirectoryModel.java +++ b/src/main/java/org/fxmisc/livedirs/DirectoryModel.java @@ -14,9 +14,10 @@ /** * Observable model of multiple directory trees. + * @param type for {@link TreeItem#getValue()} * @param type of initiator of changes to the model. */ -public interface DirectoryModel { +public interface DirectoryModel { /** * Factory to create graphics for {@link TreeItem}s in a @@ -108,7 +109,7 @@ public UpdateType getType() { * As a consequence, the returned TreeItem shall be used with * {@link TreeView#showRootProperty()} set to {@code false}. */ - TreeItem getRoot(); + TreeItem getRoot(); /** * Indicates whether this directory model contains the given path. diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/src/main/java/org/fxmisc/livedirs/LiveDirs.java index fbbc313..bbca6ce 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirs.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirs.java @@ -13,8 +13,10 @@ import java.util.List; import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; +import java.util.function.Function; import javafx.application.Platform; +import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import org.reactfx.EventSource; @@ -33,45 +35,50 @@ * *

The directory model can be used directly as a model for {@link TreeView}. * + * @param type for {@link TreeItem#getValue()} * @param type of the initiator of I/O actions. */ -public class LiveDirs { +public class LiveDirs { private final EventSource localErrors = new EventSource<>(); private final EventStream errors; private final Executor clientThreadExecutor; private final DirWatcher dirWatcher; - private final LiveDirsModel model; + private final LiveDirsModel model; private final LiveDirsIO io; private final I externalInitiator; /** * Creates a LiveDirs instance to be used from the JavaFX application * thread. + * @param projector converts the ({@link T}) {@link TreeItem#getValue()} into a {@link Path} object + * @param injector converts a given {@link Path} object into {@link T}. The reverse of {@code projector} * @param externalInitiator object to represent an initiator of an external * file-system change. * @throws IOException */ - public LiveDirs(I externalInitiator) throws IOException { - this(externalInitiator, Platform::runLater); + public LiveDirs(I externalInitiator, Function projector, Function injector) throws IOException { + this(externalInitiator, projector, injector, Platform::runLater); } /** * Creates a LiveDirs instance to be used from a designated thread. + * @param projector converts the ({@link T}) {@link TreeItem#getValue()} into a {@link Path} object + * @param injector converts a given {@link Path} object into {@link T}. The reverse of {@code projector} * @param externalInitiator object to represent an initiator of an external * file-system change. * @param clientThreadExecutor executor to execute actions on the caller * thread. Used to publish updates and errors on the caller thread. * @throws IOException */ - public LiveDirs(I externalInitiator, Executor clientThreadExecutor) throws IOException { + public LiveDirs(I externalInitiator, Function projector, Function injector, Executor clientThreadExecutor) throws IOException { this.externalInitiator = externalInitiator; this.clientThreadExecutor = clientThreadExecutor; this.dirWatcher = new DirWatcher(clientThreadExecutor); - this.model = new LiveDirsModel<>(externalInitiator); + this.model = new LiveDirsModel<>(externalInitiator, projector, injector); this.io = new LiveDirsIO<>(dirWatcher, model, clientThreadExecutor); - this.dirWatcher.signalledKeys().subscribe(this::processKey); + this.dirWatcher.signalledKeys().subscribe(key -> processKey(key)); this.errors = EventStreams.merge(dirWatcher.errors(), model.errors(), localErrors); } @@ -83,7 +90,7 @@ public LiveDirs(I externalInitiator, Executor clientThreadExecutor) throws IOExc /** * Observable directory model. */ - public DirectoryModel model() { return model; } + public DirectoryModel model() { return model; } /** * Asynchronous I/O facility. All I/O operations performed by this facility diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java b/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java index 95b4eb2..3c04625 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java @@ -8,10 +8,10 @@ class LiveDirsIO implements InitiatorTrackingIOFacility { private final DirWatcher dirWatcher; - private final LiveDirsModel model; + private final LiveDirsModel model; private final Executor clientThreadExecutor; - public LiveDirsIO(DirWatcher dirWatcher, LiveDirsModel model, Executor clientThreadExecutor) { + public LiveDirsIO(DirWatcher dirWatcher, LiveDirsModel model, Executor clientThreadExecutor) { this.dirWatcher = dirWatcher; this.model = model; this.clientThreadExecutor = clientThreadExecutor; diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java b/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java index 17cd86a..8b38214 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java @@ -4,6 +4,7 @@ import java.nio.file.attribute.FileTime; import java.util.Arrays; import java.util.List; +import java.util.function.Function; import java.util.stream.Stream; import javafx.scene.control.TreeItem; @@ -11,20 +12,24 @@ import org.reactfx.EventSource; import org.reactfx.EventStream; -class LiveDirsModel implements DirectoryModel { +class LiveDirsModel implements DirectoryModel { - private final TreeItem root = new TreeItem<>(); + private final TreeItem root = new TreeItem<>(); private final EventSource> creations = new EventSource<>(); private final EventSource> deletions = new EventSource<>(); private final EventSource> modifications = new EventSource<>(); private final EventSource errors = new EventSource<>(); private final Reporter reporter; private final I defaultInitiator; + private final Function projector; + private final Function injector; private GraphicFactory graphicFactory = DEFAULT_GRAPHIC_FACTORY; - public LiveDirsModel(I defaultInitiator) { + public LiveDirsModel(I defaultInitiator, Function projector, Function injector) { this.defaultInitiator = defaultInitiator; + this.projector = projector; + this.injector = injector; this.reporter = new Reporter() { @Override public void reportCreation(Path baseDir, Path relPath, I initiator) { @@ -48,7 +53,7 @@ public void reportError(Throwable error) { }; } - @Override public TreeItem getRoot() { return root; } + @Override public TreeItem getRoot() { return root; } @Override public EventStream> creations() { return creations; } @Override public EventStream> deletions() { return deletions; } @Override public EventStream> modifications() { return modifications; } @@ -63,42 +68,43 @@ public void setGraphicFactory(GraphicFactory factory) { @Override public boolean contains(Path path) { return topLevelAncestorStream(path).anyMatch(root -> - root.contains(root.getValue().relativize(path))); + root.contains(root.getPath().relativize(path))); } public boolean containsPrefixOf(Path path) { return root.getChildren().stream() - .anyMatch(item -> path.startsWith(item.getValue())); + .map(child -> (PathItem) child) + .anyMatch(item -> path.startsWith(item.getPath())); } void addTopLevelDirectory(Path dir) { - root.getChildren().add(new TopLevelDirItem<>(dir, graphicFactory, reporter)); + root.getChildren().add(new TopLevelDirItem<>(injector.apply(dir), graphicFactory, projector, injector, reporter)); } void updateModificationTime(Path path, FileTime lastModified, I initiator) { - for(TopLevelDirItem root: getTopLevelAncestorsNonEmpty(path)) { - Path relPath = root.getValue().relativize(path); + for(TopLevelDirItem root: getTopLevelAncestorsNonEmpty(path)) { + Path relPath = root.getPath().relativize(path); root.updateModificationTime(relPath, lastModified, initiator); } } void addDirectory(Path path, I initiator) { topLevelAncestorStream(path).forEach(root -> { - Path relPath = root.getValue().relativize(path); + Path relPath = root.getPath().relativize(path); root.addDirectory(relPath, initiator); }); } void addFile(Path path, I initiator, FileTime lastModified) { topLevelAncestorStream(path).forEach(root -> { - Path relPath = root.getValue().relativize(path); + Path relPath = root.getPath().relativize(path); root.addFile(relPath, lastModified, initiator); }); } void delete(Path path, I initiator) { - for(TopLevelDirItem root: getTopLevelAncestorsNonEmpty(path)) { - Path relPath = root.getValue().relativize(path); + for(TopLevelDirItem root: getTopLevelAncestorsNonEmpty(path)) { + Path relPath = root.getPath().relativize(path); root.remove(relPath, initiator); } } @@ -109,19 +115,20 @@ void sync(PathNode tree) { .forEach(root -> root.sync(tree, defaultInitiator)); } - private Stream> topLevelAncestorStream(Path path) { + private Stream> topLevelAncestorStream(Path path) { return root.getChildren().stream() - .filter(item -> path.startsWith(item.getValue())) - .map(item -> (TopLevelDirItem) item); + .map(child -> (PathItem) child) + .filter(item -> path.startsWith(item.getPath())) + .map(item -> (TopLevelDirItem) item); } - private List> getTopLevelAncestors(Path path) { + private List> getTopLevelAncestors(Path path) { return Arrays.asList(topLevelAncestorStream(path) - .>toArray(TopLevelDirItem[]::new)); + .>toArray(TopLevelDirItem[]::new)); } - private List> getTopLevelAncestorsNonEmpty(Path path) { - List> roots = getTopLevelAncestors(path); + private List> getTopLevelAncestorsNonEmpty(Path path) { + List> roots = getTopLevelAncestors(path); assert !roots.isEmpty() : "path resolved against a dir that was reported to be in the model does not have a top-level ancestor in the model"; return roots; } diff --git a/src/main/java/org/fxmisc/livedirs/PathItem.java b/src/main/java/org/fxmisc/livedirs/PathItem.java index d698701..e16ff7d 100644 --- a/src/main/java/org/fxmisc/livedirs/PathItem.java +++ b/src/main/java/org/fxmisc/livedirs/PathItem.java @@ -7,6 +7,7 @@ import java.util.HashSet; import java.util.NoSuchElementException; import java.util.Set; +import java.util.function.Function; import javafx.collections.ObservableList; import javafx.scene.Node; @@ -14,9 +15,15 @@ import org.fxmisc.livedirs.DirectoryModel.GraphicFactory; -abstract class PathItem extends TreeItem { - protected PathItem(Path path, Node graphic) { +abstract class PathItem extends TreeItem { + + private final Function projector; + protected final Function getProjector() { return projector; } + public final Path getPath() { return projector.apply(getValue()); } + + protected PathItem(T path, Node graphic, Function projector) { super(path, graphic); + this.projector = projector; } @Override @@ -26,26 +33,27 @@ public final boolean isLeaf() { public abstract boolean isDirectory(); - public FileItem asFileItem() { return (FileItem) this; } - public DirItem asDirItem() { return (DirItem) this; } + public FileItem asFileItem() { return (FileItem) this; } + public DirItem asDirItem() { return (DirItem) this; } - public PathItem getRelChild(Path relPath) { + public PathItem getRelChild(Path relPath) { assert relPath.getNameCount() == 1; - Path childValue = getValue().resolve(relPath); - for(TreeItem ch: getChildren()) { - if(ch.getValue().equals(childValue)) { - return (PathItem) ch; + Path childValue = projector.apply(getValue()).resolve(relPath); + for(TreeItem ch: getChildren()) { + PathItem pathCh = (PathItem) ch; + if(pathCh.getPath().equals(childValue)) { + return pathCh; } } return null; } - protected PathItem resolve(Path relPath) { + protected PathItem resolve(Path relPath) { int len = relPath.getNameCount(); if(len == 0) { return this; } else { - PathItem child = getRelChild(relPath.getName(0)); + PathItem child = getRelChild(relPath.getName(0)); if(child == null) { return null; } else if(len == 1) { @@ -57,15 +65,15 @@ protected PathItem resolve(Path relPath) { } } -class FileItem extends PathItem { - public static FileItem create(Path path, FileTime lastModified, GraphicFactory graphicFactory) { - return new FileItem(path, lastModified, graphicFactory.createGraphic(path, false)); +class FileItem extends PathItem { + public static FileItem create(T path, FileTime lastModified, GraphicFactory graphicFactory, Function projector) { + return new FileItem<>(path, lastModified, graphicFactory.createGraphic(projector.apply(path), false), projector); } private FileTime lastModified; - private FileItem(Path path, FileTime lastModified, Node graphic) { - super(path, graphic); + private FileItem(T path, FileTime lastModified, Node graphic, Function projector) { + super(path, graphic, projector); this.lastModified = lastModified; } @@ -84,13 +92,19 @@ public boolean updateModificationTime(FileTime lastModified) { } } -class DirItem extends PathItem { - public static DirItem create(Path path, GraphicFactory graphicFactory) { - return new DirItem(path, graphicFactory.createGraphic(path, true)); +class DirItem extends PathItem { + + private final Function injector; + protected final Function getInjector() { return injector; } + public final T inject(Path path) { return injector.apply(path); } + + public static DirItem create(T path, GraphicFactory graphicFactory, Function projector, Function injector) { + return new DirItem<>(path, graphicFactory.createGraphic(projector.apply(path), true), projector, injector); } - protected DirItem(Path path, Node graphic) { - super(path, graphic); + protected DirItem(T path, Node graphic, Function projector, Function injector) { + super(path, graphic, projector); + this.injector = injector; } @Override @@ -98,29 +112,33 @@ public final boolean isDirectory() { return true; } - public FileItem addChildFile(Path fileName, FileTime lastModified, GraphicFactory graphicFactory) { + public FileItem addChildFile(Path fileName, FileTime lastModified, GraphicFactory graphicFactory) { assert fileName.getNameCount() == 1; int i = getFileInsertionIndex(fileName.toString()); - FileItem child = FileItem.create(getValue().resolve(fileName), lastModified, graphicFactory); + + @SuppressWarnings("unchecked") + FileItem child = FileItem.create(inject(getPath().resolve(fileName)), lastModified, graphicFactory, getProjector()); getChildren().add(i, child); return child; } - public DirItem addChildDir(Path dirName, GraphicFactory graphicFactory) { + public DirItem addChildDir(Path dirName, GraphicFactory graphicFactory) { assert dirName.getNameCount() == 1; int i = getDirInsertionIndex(dirName.toString()); - DirItem child = DirItem.create(getValue().resolve(dirName), graphicFactory); + + @SuppressWarnings("unchecked") + DirItem child = DirItem.create(inject(getPath().resolve(dirName)), graphicFactory, getProjector(), getInjector()); getChildren().add(i, child); return child; } private int getFileInsertionIndex(String fileName) { - ObservableList> children = getChildren(); + ObservableList> children = getChildren(); int n = children.size(); for(int i = 0; i < n; ++i) { - PathItem child = (PathItem) children.get(i); + PathItem child = (PathItem) children.get(i); if(!child.isDirectory()) { - String childName = child.getValue().getFileName().toString(); + String childName = child.getPath().getFileName().toString(); if(childName.compareToIgnoreCase(fileName) > 0) { return i; } @@ -130,12 +148,12 @@ private int getFileInsertionIndex(String fileName) { } private int getDirInsertionIndex(String dirName) { - ObservableList> children = getChildren(); + ObservableList> children = getChildren(); int n = children.size(); for(int i = 0; i < n; ++i) { - PathItem child = (PathItem) children.get(i); + PathItem child = (PathItem) children.get(i); if(child.isDirectory()) { - String childName = child.getValue().getFileName().toString(); + String childName = child.getPath().getFileName().toString(); if(childName.compareToIgnoreCase(dirName) > 0) { return i; } @@ -147,17 +165,17 @@ private int getDirInsertionIndex(String dirName) { } } -class ParentChild { - private final DirItem parent; - private final PathItem child; +class ParentChild { + private final DirItem parent; + private final PathItem child; - public ParentChild(DirItem parent, PathItem child) { + public ParentChild(DirItem parent, PathItem child) { this.parent = parent; this.child = child; } - public DirItem getParent() { return parent; } - public PathItem getChild() { return child; } + public DirItem getParent() { return parent; } + public PathItem getChild() { return child; } } interface Reporter { @@ -167,33 +185,33 @@ interface Reporter { void reportError(Throwable error); } -class TopLevelDirItem extends DirItem { +class TopLevelDirItem extends DirItem { private final GraphicFactory graphicFactory; private final Reporter reporter; - TopLevelDirItem(Path path, GraphicFactory graphicFactory, Reporter reporter) { - super(path, graphicFactory.createGraphic(path, true)); + TopLevelDirItem(T path, GraphicFactory graphicFactory, Function projector, Function injector, Reporter reporter) { + super(path, graphicFactory.createGraphic(projector.apply(path), true), projector, injector); this.graphicFactory = graphicFactory; this.reporter = reporter; } - private ParentChild resolveInParent(Path relPath) { + private ParentChild resolveInParent(Path relPath) { int len = relPath.getNameCount(); if(len == 0) { - return new ParentChild(null, this); + return new ParentChild<>(null, this); } else if(len == 1) { - if(getValue().resolve(relPath).equals(getValue())) { - return new ParentChild(null, this); + if(getPath().resolve(relPath).equals(getValue())) { + return new ParentChild<>(null, this); } else { - return new ParentChild(this, getRelChild(relPath.getName(0))); + return new ParentChild<>(this, getRelChild(relPath.getName(0))); } } else { - PathItem parent = resolve(relPath.subpath(0, len - 1)); + PathItem parent = resolve(relPath.subpath(0, len - 1)); if(parent == null || !parent.isDirectory()) { - return new ParentChild(null, null); + return new ParentChild<>(null, null); } else { - PathItem child = parent.getRelChild(relPath.getFileName()); - return new ParentChild(parent.asDirItem(), child); + PathItem child = parent.getRelChild(relPath.getFileName()); + return new ParentChild<>(parent.asDirItem(), child); } } } @@ -201,7 +219,7 @@ private ParentChild resolveInParent(Path relPath) { private void updateFile(Path relPath, FileTime lastModified, I initiator) { PathItem item = resolve(relPath); if(item == null || item.isDirectory()) { - sync(PathNode.file(getValue().resolve(relPath), lastModified), initiator); + sync(PathNode.file(getPath().resolve(relPath), lastModified), initiator); } } @@ -220,16 +238,16 @@ public void updateModificationTime(Path relPath, FileTime lastModified, I initia public void addDirectory(Path relPath, I initiator) { PathItem item = resolve(relPath); if(item == null || !item.isDirectory()) { - sync(PathNode.directory(getValue().resolve(relPath), Collections.emptyList()), initiator); + sync(PathNode.directory(getPath().resolve(relPath), Collections.emptyList()), initiator); } } public void sync(PathNode tree, I initiator) { Path path = tree.getPath(); - Path relPath = getValue().relativize(path); - ParentChild pc = resolveInParent(relPath); - DirItem parent = pc.getParent(); - PathItem item = pc.getChild(); + Path relPath = getPath().relativize(path); + ParentChild pc = resolveInParent(relPath); + DirItem parent = pc.getParent(); + PathItem item = pc.getChild(); if(parent != null) { syncChild(parent, relPath.getFileName(), tree, initiator); } else if(item == null) { // neither path nor its parent present in model @@ -244,18 +262,19 @@ public void sync(PathNode tree, I initiator) { } } - private void syncContent(DirItem dir, PathNode tree, I initiator) { + private void syncContent(DirItem dir, PathNode tree, I initiator) { Set desiredChildren = new HashSet<>(); for(PathNode ch: tree.getChildren()) { desiredChildren.add(ch.getPath()); } - ArrayList> actualChildren = new ArrayList<>(dir.getChildren()); + ArrayList> actualChildren = new ArrayList<>(dir.getChildren()); // remove undesired children - for(TreeItem ch: actualChildren) { - if(!desiredChildren.contains(ch.getValue())) { - removeNode(ch, null); + for(TreeItem ch: actualChildren) { + PathItem pathCh = (PathItem) ch; + if(!desiredChildren.contains(pathCh.getPath())) { + removeNode(pathCh, null); } } @@ -265,48 +284,48 @@ private void syncContent(DirItem dir, PathNode tree, I initiator) { } } - private void syncChild(DirItem parent, Path childName, PathNode tree, I initiator) { - PathItem child = parent.getRelChild(childName); + private void syncChild(DirItem parent, Path childName, PathNode tree, I initiator) { + PathItem child = parent.getRelChild(childName); if(child != null && child.isDirectory() != tree.isDirectory()) { removeNode(child, null); } if(child == null) { if(tree.isDirectory()) { - DirItem dirChild = parent.addChildDir(childName, graphicFactory); - reporter.reportCreation(getValue(), getValue().relativize(dirChild.getValue()), initiator); + DirItem dirChild = parent.addChildDir(childName, graphicFactory); + reporter.reportCreation(getPath(), getPath().relativize(dirChild.getPath()), initiator); syncContent(dirChild, tree, initiator); } else { - FileItem fileChild = parent.addChildFile(childName, tree.getLastModified(), graphicFactory); - reporter.reportCreation(getValue(), getValue().relativize(fileChild.getValue()), initiator); + FileItem fileChild = parent.addChildFile(childName, tree.getLastModified(), graphicFactory); + reporter.reportCreation(getPath(), getPath().relativize(fileChild.getPath()), initiator); } } else { if(child.isDirectory()) { syncContent(child.asDirItem(), tree, initiator); } else { if(child.asFileItem().updateModificationTime(tree.getLastModified())) { - reporter.reportModification(getValue(), getValue().relativize(child.getValue()), initiator); + reporter.reportModification(getPath(), getPath().relativize(child.getPath()), initiator); } } } } public void remove(Path relPath, I initiator) { - PathItem item = resolve(relPath); + PathItem item = resolve(relPath); if(item != null) { removeNode(item, initiator); } } - private void removeNode(TreeItem node, I initiator) { + private void removeNode(PathItem node, I initiator) { signalDeletionRecursively(node, initiator); node.getParent().getChildren().remove(node); } - private void signalDeletionRecursively(TreeItem node, I initiator) { - for(TreeItem child: node.getChildren()) { - signalDeletionRecursively(child, initiator); + private void signalDeletionRecursively(PathItem node, I initiator) { + for(TreeItem child: node.getChildren()) { + signalDeletionRecursively((PathItem) child, initiator); } - reporter.reportDeletion(getValue(), getValue().relativize(node.getValue()), initiator); + reporter.reportDeletion(getPath(), getPath().relativize(node.getPath()), initiator); } private void raise(Throwable t) { From 4f2dbdd562a9c5983688c988b3057cf53027b94a Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Mon, 15 Feb 2016 21:56:18 -0800 Subject: [PATCH 02/23] Implemented NormalLiveDirs, the new LiveDirs instance that uses the regular TreeItem class for its root and children. --- .../java/org/fxmisc/livedirs/LiveDirs.java | 13 ----- .../org/fxmisc/livedirs/NormalLiveDirs.java | 54 +++++++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/src/main/java/org/fxmisc/livedirs/LiveDirs.java index bbca6ce..5e87f0b 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirs.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirs.java @@ -48,19 +48,6 @@ public class LiveDirs { private final LiveDirsIO io; private final I externalInitiator; - /** - * Creates a LiveDirs instance to be used from the JavaFX application - * thread. - * @param projector converts the ({@link T}) {@link TreeItem#getValue()} into a {@link Path} object - * @param injector converts a given {@link Path} object into {@link T}. The reverse of {@code projector} - * @param externalInitiator object to represent an initiator of an external - * file-system change. - * @throws IOException - */ - public LiveDirs(I externalInitiator, Function projector, Function injector) throws IOException { - this(externalInitiator, projector, injector, Platform::runLater); - } - /** * Creates a LiveDirs instance to be used from a designated thread. * @param projector converts the ({@link T}) {@link TreeItem#getValue()} into a {@link Path} object diff --git a/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java b/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java new file mode 100644 index 0000000..7a5565b --- /dev/null +++ b/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java @@ -0,0 +1,54 @@ +package org.fxmisc.livedirs; + + +import javafx.application.Platform; +import javafx.scene.control.TreeItem; +import org.reactfx.EventStreams; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.Executor; +import java.util.function.Function; + +/** + * NormalLiveDirs is the standard {@link LiveDirs} used in most + * {@link javafx.scene.control.TreeView} cases. It uses {@link TreeItem} (not + * one of its subclasses) for its {@link LiveDirsModel}'s root and children. + * + *

To initialize a {@link javafx.scene.control.TreeView} correctly, use the following code:

+ *
+ *     {@code
+ *     NormalLiveDirs dirs = // creation code;
+ *     TreeView view = new TreeView(dirs.model().getRoot());
+ *     view.setShowRoot(false);
+ *     }
+ * 
+ * @param type of the initiator of I/O actions. + */ +public class NormalLiveDirs extends LiveDirs { + + /** + * Creates a NormalLiveDirs instance to be used from the JavaFX application + * thread. + * + * @param externalInitiator object to represent an initiator of an external + * file-system change. + * @throws IOException + */ + public NormalLiveDirs(I externalInitiator) throws IOException { + this(externalInitiator, Platform::runLater); + } + + /** + * Creates a NormalLiveDirs instance to be used from a designated thread. + * + * @param externalInitiator object to represent an initiator of an external + * file-system change. + * @param clientThreadExecutor executor to execute actions on the caller + * thread. Used to publish updates and errors on the caller thread. + * @throws IOException + */ + public NormalLiveDirs(I externalInitiator, Executor clientThreadExecutor) throws IOException { + super(externalInitiator, Function.identity(), Function.identity(), clientThreadExecutor); + } +} From 016c961945e910cfdff0bdacfd50fc227df5ac16 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Mon, 15 Feb 2016 21:57:56 -0800 Subject: [PATCH 03/23] Fix minor code bugs - Now that "T" generic is used in class, update `wrap()`'s generic to "U" - Return lambda back to method reference (this was accidentally and erroneously reverted in previous commit) --- src/main/java/org/fxmisc/livedirs/LiveDirs.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/src/main/java/org/fxmisc/livedirs/LiveDirs.java index 5e87f0b..a6a74e8 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirs.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirs.java @@ -65,7 +65,7 @@ public LiveDirs(I externalInitiator, Function projector, Function(externalInitiator, projector, injector); this.io = new LiveDirsIO<>(dirWatcher, model, clientThreadExecutor); - this.dirWatcher.signalledKeys().subscribe(key -> processKey(key)); + this.dirWatcher.signalledKeys().subscribe(this::processKey); this.errors = EventStreams.merge(dirWatcher.errors(), model.errors(), localErrors); } @@ -220,7 +220,7 @@ private void refreshOrLogError(Path path) { }); } - private CompletionStage wrap(CompletionStage stage) { + private CompletionStage wrap(CompletionStage stage) { return new CompletionStageWithDefaultExecutor<>(stage, clientThreadExecutor); } } \ No newline at end of file From 9d29f3495b11dcd8c3ec8f7c25b4502f3d5f233d Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Tue, 16 Feb 2016 15:08:10 -0800 Subject: [PATCH 04/23] Switched DirectoryModel generics: Initiator before TreeItem#getValue Type --- .../org/fxmisc/livedirs/DirectoryModel.java | 4 ++-- .../java/org/fxmisc/livedirs/LiveDirs.java | 8 ++++---- .../java/org/fxmisc/livedirs/LiveDirsIO.java | 4 ++-- .../org/fxmisc/livedirs/LiveDirsModel.java | 18 +++++++++--------- .../org/fxmisc/livedirs/NormalLiveDirs.java | 2 +- .../java/org/fxmisc/livedirs/PathItem.java | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/fxmisc/livedirs/DirectoryModel.java b/src/main/java/org/fxmisc/livedirs/DirectoryModel.java index bef06a7..4e42baa 100644 --- a/src/main/java/org/fxmisc/livedirs/DirectoryModel.java +++ b/src/main/java/org/fxmisc/livedirs/DirectoryModel.java @@ -14,10 +14,10 @@ /** * Observable model of multiple directory trees. - * @param type for {@link TreeItem#getValue()} * @param type of initiator of changes to the model. + * @param type for {@link TreeItem#getValue()} */ -public interface DirectoryModel { +public interface DirectoryModel { /** * Factory to create graphics for {@link TreeItem}s in a diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/src/main/java/org/fxmisc/livedirs/LiveDirs.java index a6a74e8..2dd1d03 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirs.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirs.java @@ -35,16 +35,16 @@ * *

The directory model can be used directly as a model for {@link TreeView}. * - * @param type for {@link TreeItem#getValue()} * @param type of the initiator of I/O actions. + * @param type for {@link TreeItem#getValue()} */ -public class LiveDirs { +public class LiveDirs { private final EventSource localErrors = new EventSource<>(); private final EventStream errors; private final Executor clientThreadExecutor; private final DirWatcher dirWatcher; - private final LiveDirsModel model; + private final LiveDirsModel model; private final LiveDirsIO io; private final I externalInitiator; @@ -77,7 +77,7 @@ public LiveDirs(I externalInitiator, Function projector, Function model() { return model; } + public DirectoryModel model() { return model; } /** * Asynchronous I/O facility. All I/O operations performed by this facility diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java b/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java index 3c04625..260694b 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java @@ -8,10 +8,10 @@ class LiveDirsIO implements InitiatorTrackingIOFacility { private final DirWatcher dirWatcher; - private final LiveDirsModel model; + private final LiveDirsModel model; private final Executor clientThreadExecutor; - public LiveDirsIO(DirWatcher dirWatcher, LiveDirsModel model, Executor clientThreadExecutor) { + public LiveDirsIO(DirWatcher dirWatcher, LiveDirsModel model, Executor clientThreadExecutor) { this.dirWatcher = dirWatcher; this.model = model; this.clientThreadExecutor = clientThreadExecutor; diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java b/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java index 8b38214..ae81d45 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java @@ -12,7 +12,7 @@ import org.reactfx.EventSource; import org.reactfx.EventStream; -class LiveDirsModel implements DirectoryModel { +class LiveDirsModel implements DirectoryModel { private final TreeItem root = new TreeItem<>(); private final EventSource> creations = new EventSource<>(); @@ -82,7 +82,7 @@ void addTopLevelDirectory(Path dir) { } void updateModificationTime(Path path, FileTime lastModified, I initiator) { - for(TopLevelDirItem root: getTopLevelAncestorsNonEmpty(path)) { + for(TopLevelDirItem root: getTopLevelAncestorsNonEmpty(path)) { Path relPath = root.getPath().relativize(path); root.updateModificationTime(relPath, lastModified, initiator); } @@ -103,7 +103,7 @@ void addFile(Path path, I initiator, FileTime lastModified) { } void delete(Path path, I initiator) { - for(TopLevelDirItem root: getTopLevelAncestorsNonEmpty(path)) { + for(TopLevelDirItem root: getTopLevelAncestorsNonEmpty(path)) { Path relPath = root.getPath().relativize(path); root.remove(relPath, initiator); } @@ -115,20 +115,20 @@ void sync(PathNode tree) { .forEach(root -> root.sync(tree, defaultInitiator)); } - private Stream> topLevelAncestorStream(Path path) { + private Stream> topLevelAncestorStream(Path path) { return root.getChildren().stream() .map(child -> (PathItem) child) .filter(item -> path.startsWith(item.getPath())) - .map(item -> (TopLevelDirItem) item); + .map(item -> (TopLevelDirItem) item); } - private List> getTopLevelAncestors(Path path) { + private List> getTopLevelAncestors(Path path) { return Arrays.asList(topLevelAncestorStream(path) - .>toArray(TopLevelDirItem[]::new)); + .>toArray(TopLevelDirItem[]::new)); } - private List> getTopLevelAncestorsNonEmpty(Path path) { - List> roots = getTopLevelAncestors(path); + private List> getTopLevelAncestorsNonEmpty(Path path) { + List> roots = getTopLevelAncestors(path); assert !roots.isEmpty() : "path resolved against a dir that was reported to be in the model does not have a top-level ancestor in the model"; return roots; } diff --git a/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java b/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java index 7a5565b..d7c37b9 100644 --- a/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java +++ b/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java @@ -25,7 +25,7 @@ * * @param type of the initiator of I/O actions. */ -public class NormalLiveDirs extends LiveDirs { +public class NormalLiveDirs extends LiveDirs { /** * Creates a NormalLiveDirs instance to be used from the JavaFX application diff --git a/src/main/java/org/fxmisc/livedirs/PathItem.java b/src/main/java/org/fxmisc/livedirs/PathItem.java index e16ff7d..5654465 100644 --- a/src/main/java/org/fxmisc/livedirs/PathItem.java +++ b/src/main/java/org/fxmisc/livedirs/PathItem.java @@ -185,7 +185,7 @@ interface Reporter { void reportError(Throwable error); } -class TopLevelDirItem extends DirItem { +class TopLevelDirItem extends DirItem { private final GraphicFactory graphicFactory; private final Reporter reporter; From bef6ac0c815ff71ebd51ecbea69dc5cbe9653b24 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Thu, 18 Feb 2016 17:31:55 -0800 Subject: [PATCH 05/23] Implemented CheckBoxTreeItem-like display for `LiveDirs.model().getRoot()` --- .../org/fxmisc/livedirs/CheckBoxContent.java | 23 ++++ .../fxmisc/livedirs/CheckBoxContentImpl.java | 24 ++++ .../org/fxmisc/livedirs/CheckBoxTreeCell.java | 119 ++++++++++++++++++ .../java/org/fxmisc/livedirs/LiveDirs.java | 72 +++++++++++ .../fxmisc/livedirs/TreeCellFactories.java | 26 ++++ 5 files changed, 264 insertions(+) create mode 100644 src/main/java/org/fxmisc/livedirs/CheckBoxContent.java create mode 100644 src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java create mode 100644 src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java create mode 100644 src/main/java/org/fxmisc/livedirs/TreeCellFactories.java diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java b/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java new file mode 100644 index 0000000..80942a8 --- /dev/null +++ b/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java @@ -0,0 +1,23 @@ +package org.fxmisc.livedirs; + +import org.reactfx.value.Var; + +import java.nio.file.Path; + +public interface CheckBoxContent { + + /** The State of the {@link javafx.scene.control.CheckBox} */ + enum State { + UNCHECKED, + UNDEFINED, + CHECKED + } + + State getState(); + void setState(State value); + Var stateProperty(); + + Path getPath(); + void setPath(Path p); + +} diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java b/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java new file mode 100644 index 0000000..6228b0d --- /dev/null +++ b/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java @@ -0,0 +1,24 @@ +package org.fxmisc.livedirs; + +import org.reactfx.value.Var; + +import java.nio.file.Path; + +/** + * A basic implementation of {@link CheckBoxContent} + */ +public class CheckBoxContentImpl implements CheckBoxContent { + + private final Var state = Var.newSimpleVar(State.UNCHECKED); + public final State getState() { return state.getValue(); } + public final void setState(State value) { state.setValue(value); } + public final Var stateProperty() { return state; } + + private Path path; + public final Path getPath() { return path; } + public final void setPath(Path p) { path = p; } + + public CheckBoxContentImpl(Path p) { + path = p; + } +} diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java b/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java new file mode 100644 index 0000000..36564b4 --- /dev/null +++ b/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java @@ -0,0 +1,119 @@ +package org.fxmisc.livedirs; + +import javafx.beans.InvalidationListener; +import javafx.scene.Node; +import javafx.scene.control.CheckBox; +import javafx.scene.control.TreeCell; +import javafx.scene.control.TreeItem; +import org.reactfx.Subscription; +import org.reactfx.value.Var; + +import java.util.function.Function; + +public class CheckBoxTreeCell extends TreeCell { + + private static final Function DEFAULT_CONVERTER = (content) -> content.getPath().toString(); + + private final CheckBox checkBox = new CheckBox(); + private final Function stringConverter; + private Var state; + private Var select; + private Subscription intermediateState; + + private final InvalidationListener stateInvalidations = (obs) -> { + TreeItem treeItem = getTreeItem(); + if (treeItem != null) { + final TreeItem parentItem = treeItem.getParent(); + + // do upward call first + if (parentItem != null) { + CheckBoxContent value = parentItem.getValue(); + + if (value != null) { + CheckBoxContent.State[] childrenStates = parentItem.getChildren() + .stream().map(v -> v.getValue().getState()) + .distinct() + .toArray(CheckBoxContent.State[]::new); + + /* + Due to `distinct()`, + if length > 1, + then children were 2+ of the 3 CheckBoxContent.State enum values + Thus, set to UNDEFINED + else + then children were all UNCHECKED or CHECKED. + Thus, set the current value to that State + */ + value.setState(childrenStates.length > 1 + ? CheckBoxContent.State.UNDEFINED + : childrenStates[0] + ); + } + } + + // then do downward call + CheckBoxContent.State state = treeItem.getValue().getState(); + if (state != null) { + if (state.equals(CheckBoxContent.State.CHECKED)) { + treeItem.getChildren().forEach(v -> v.getValue().setState(CheckBoxContent.State.CHECKED)); + } else if (state.equals(CheckBoxContent.State.UNCHECKED)) { + treeItem.getChildren().forEach(v -> v.getValue().setState(CheckBoxContent.State.UNCHECKED)); + } + // else state == UNDEFINED, so do nothing + } + } + }; + + public CheckBoxTreeCell() { + this(DEFAULT_CONVERTER); + } + + public CheckBoxTreeCell(Function stringConverter) { + super(); + this.stringConverter = stringConverter; + } + + @Override + protected void updateItem(C item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setText(null); + checkBox.setGraphic(null); + setGraphic(null); + } else { + // update the text + setText(stringConverter.apply(getItem())); + + // update the graphic + TreeItem treeItem = getTreeItem(); + Node graphic = treeItem.getGraphic(); + checkBox.setGraphic( graphic != null ? graphic : null); + setGraphic(checkBox); + + // unbind properties + if (state != null) { + checkBox.selectedProperty().unbindBidirectional(select); + intermediateState.unsubscribe(); + state.removeListener(stateInvalidations); + } + + // rebind properties + state = treeItem.getValue().stateProperty(); + select = state.mapBidirectional( + s -> s == CheckBoxContent.State.CHECKED, + val -> val ? CheckBoxContent.State.CHECKED : CheckBoxContent.State.UNCHECKED + ); + checkBox.selectedProperty().bindBidirectional(select); + + // using checkBox.intermediateProperty().bind(state.map(s -> s == UNDEFINED)); results in a + // RunTimeException: a bounded property cannot be set + // So, get around it by feeding state values into it. + intermediateState = state.values() + .map(s -> s == CheckBoxContent.State.UNDEFINED) + .feedTo(checkBox.indeterminateProperty()); + + state.addListener(stateInvalidations); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/src/main/java/org/fxmisc/livedirs/LiveDirs.java index 2dd1d03..3c08dd6 100644 --- a/src/main/java/org/fxmisc/livedirs/LiveDirs.java +++ b/src/main/java/org/fxmisc/livedirs/LiveDirs.java @@ -40,6 +40,78 @@ */ public class LiveDirs { + /* ****************** * + * Static Factories * + * ****************** */ + + /** + * Creates a LiveDirs instance to be used from a designated thread. + * + * @param externalInitiator object to represent an initiator of an external + * file-system change. + * @param clientThreadExecutor executor to execute actions on the caller + * thread. Used to publish updates and errors on the caller thread. + * @throws IOException + */ + public static LiveDirs getNormalInstance(I externalInitiator, Executor clientThreadExecutor) throws IOException { + return new LiveDirs<>(externalInitiator, Function.identity(), Function.identity(), clientThreadExecutor); + } + + /** + * Same as {@link #getNormalInstance(Object, Executor)} but is for use on the JavaFX application + * thread. + * + * @param externalInitiator object to represent an initiator of an external + * file-system change. + * @throws IOException + */ + public static LiveDirs getNormalInstance(I externalInitiator) throws IOException { + return getNormalInstance(externalInitiator, Platform::runLater); + } + + /** + * Creates a LiveDirs instance to be used from a designated thread. This type of instance + * can be used to display a {@link TreeItem}s as though they were + * {@link javafx.scene.control.CheckBoxTreeItem}s. + * + *

To do so, use the following code: + *

+     *     {@code
+     *     LiveDirs dirs = LiveDirs.getCheckBoxInstance( args );
+     *     TreeView view = new TreeView<>(dirs.model().getRoot());
+     *     view.setCellFactory(TreeCellFactories.checkBoxFactory());
+     *     view.setShowRoot(false);
+     *     }
+     * 
+ * + * @param externalInitiator object to represent an initiator of an external + * file-system change. + * @param clientThreadExecutor executor to execute actions on the caller + * thread. Used to publish updates and errors on the caller thread. + * @throws IOException + */ + public static LiveDirs getCheckBoxInstance( + I externalInitiator, Function treeItemConstructor, Executor clientThreadExecutor) throws IOException { + return new LiveDirs<>(externalInitiator, CheckBoxContent::getPath, treeItemConstructor, clientThreadExecutor); + } + + /** + * Same as {@link #getCheckBoxInstance(Object, Function, Executor)} but uses + * {@link CheckBoxContentImpl} to implement the {@link CheckBoxContent} interface. + */ + public static LiveDirs getCheckBoxInstance( + I externalInitiator, Executor clientThreadExecutor) throws IOException { + return getCheckBoxInstance(externalInitiator, CheckBoxContentImpl::new, clientThreadExecutor); + } + + /** + * Same as {@link #getCheckBoxInstance(Object, Executor)} but the instance will be used on the + * JavaFX Application thread + */ + public static LiveDirs getCheckBoxInstance(I externalInitiator) throws IOException { + return getCheckBoxInstance(externalInitiator, Platform::runLater); + } + private final EventSource localErrors = new EventSource<>(); private final EventStream errors; private final Executor clientThreadExecutor; diff --git a/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java b/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java new file mode 100644 index 0000000..3b2819c --- /dev/null +++ b/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java @@ -0,0 +1,26 @@ +package org.fxmisc.livedirs; + +import javafx.scene.control.TreeCell; +import javafx.scene.control.TreeView; +import javafx.util.Callback; + +import java.util.function.Function; + +/** + * A class that provides cell factories to help display a {@link PathItem} + * as though it extended a subclass of {@link javafx.scene.control.TreeItem} + * or to provide additional functionality not present in the normal cell factory. + */ +public class TreeCellFactories { + + public static Callback, TreeCell> checkBoxFactory() { + return (view) -> new CheckBoxTreeCell(); + } + + public static Callback, TreeCell> checkBoxFactory( + Function stringConverter + ) { + return (view) -> new CheckBoxTreeCell(stringConverter); + } + +} From 20c2e861f97878241717bb3d7cdf850c73e05499 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Thu, 18 Feb 2016 17:48:27 -0800 Subject: [PATCH 06/23] Update cases of `CheckBoxContent` to generic --- src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java b/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java index 36564b4..9165879 100644 --- a/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java +++ b/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java @@ -12,10 +12,8 @@ public class CheckBoxTreeCell extends TreeCell { - private static final Function DEFAULT_CONVERTER = (content) -> content.getPath().toString(); - private final CheckBox checkBox = new CheckBox(); - private final Function stringConverter; + private final Function stringConverter; private Var state; private Var select; private Subscription intermediateState; @@ -65,10 +63,10 @@ public class CheckBoxTreeCell extends TreeCell { }; public CheckBoxTreeCell() { - this(DEFAULT_CONVERTER); + this((content) -> content.getPath().toString()); } - public CheckBoxTreeCell(Function stringConverter) { + public CheckBoxTreeCell(Function stringConverter) { super(); this.stringConverter = stringConverter; } From 1391fb80b921489eca4908e4ca105a4eda665268 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Thu, 18 Feb 2016 18:07:45 -0800 Subject: [PATCH 07/23] Fixed two bugs relating to the checkbox's state: - Checking/Unchecking a directory wouldn't update all of its children recursively to the same state - Checking/Unchecking a directory would sometimes make its parent's state UNDEFINED when it should be CHECKED or UNCHECKED --- .../org/fxmisc/livedirs/CheckBoxContent.java | 4 +++ .../fxmisc/livedirs/CheckBoxContentImpl.java | 5 ++++ .../org/fxmisc/livedirs/CheckBoxTreeCell.java | 28 +++++++++++++------ .../java/org/fxmisc/livedirs/Recursive.java | 13 +++++++++ 4 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/fxmisc/livedirs/Recursive.java diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java b/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java index 80942a8..fd6ac2b 100644 --- a/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java +++ b/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java @@ -20,4 +20,8 @@ enum State { Path getPath(); void setPath(Path p); + void lock(); + void unlock(); + boolean isLocked(); + } diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java b/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java index 6228b0d..af1fe3a 100644 --- a/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java +++ b/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java @@ -18,6 +18,11 @@ public class CheckBoxContentImpl implements CheckBoxContent { public final Path getPath() { return path; } public final void setPath(Path p) { path = p; } + private boolean locked = false; + public final boolean isLocked() { return locked; } + public final void lock() { locked = true; } + public final void unlock() { locked = false; } + public CheckBoxContentImpl(Path p) { path = p; } diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java b/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java index 9165879..dcc6085 100644 --- a/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java +++ b/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java @@ -8,10 +8,19 @@ import org.reactfx.Subscription; import org.reactfx.value.Var; +import java.util.function.BiConsumer; import java.util.function.Function; public class CheckBoxTreeCell extends TreeCell { + private final Recursive, CheckBoxContent.State>> UPDATE_DOWNWARDS = new Recursive<>(); + { + UPDATE_DOWNWARDS.f = (item, state) -> { + item.getValue().setState(state); + item.getChildren().forEach(child -> UPDATE_DOWNWARDS.f.accept(child, state)); + }; + } + private final CheckBox checkBox = new CheckBox(); private final Function stringConverter; private Var state; @@ -27,7 +36,7 @@ public class CheckBoxTreeCell extends TreeCell { if (parentItem != null) { CheckBoxContent value = parentItem.getValue(); - if (value != null) { + if (value != null && !value.isLocked()) { CheckBoxContent.State[] childrenStates = parentItem.getChildren() .stream().map(v -> v.getValue().getState()) .distinct() @@ -50,15 +59,16 @@ public class CheckBoxTreeCell extends TreeCell { } // then do downward call - CheckBoxContent.State state = treeItem.getValue().getState(); - if (state != null) { - if (state.equals(CheckBoxContent.State.CHECKED)) { - treeItem.getChildren().forEach(v -> v.getValue().setState(CheckBoxContent.State.CHECKED)); - } else if (state.equals(CheckBoxContent.State.UNCHECKED)) { - treeItem.getChildren().forEach(v -> v.getValue().setState(CheckBoxContent.State.UNCHECKED)); - } - // else state == UNDEFINED, so do nothing + C itemVal = treeItem.getValue(); + // when children's invalidation listeners are called, skip this item's update as it + // was the one the initiated the call. + itemVal.lock(); + CheckBoxContent.State state = itemVal.getState(); + if (state != CheckBoxContent.State.UNDEFINED) { + treeItem.getChildren().forEach(child -> UPDATE_DOWNWARDS.f.accept(child, state)); } + // once finished, unlock so updates via one of its children will propogate through the tree + itemVal.unlock(); } }; diff --git a/src/main/java/org/fxmisc/livedirs/Recursive.java b/src/main/java/org/fxmisc/livedirs/Recursive.java new file mode 100644 index 0000000..725b53c --- /dev/null +++ b/src/main/java/org/fxmisc/livedirs/Recursive.java @@ -0,0 +1,13 @@ +package org.fxmisc.livedirs; + +/** + * Allows recursive {@link FunctionalInterface} calls. + * + *

See https://stackoverflow.com/questions/19429667/implement-recursive-lambda-function-using-java-8

+ * + * @param the {@link FunctionalInterface} to call recursively. + */ +public class Recursive { + + public I f; +} From 48c0ae9b33a1c7783d562badc682ed4374037a19 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 13:45:28 -0800 Subject: [PATCH 08/23] Partial commit: set up structure needed to separate CheckBoxTreeCell-related files (since it's GUI related) from core library and putting them into their own 'demo' module --- livedirsfx-demo/build.gradle | 14 ++++++++++++++ livedirsfx/build.gradle | 13 +++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 livedirsfx-demo/build.gradle create mode 100644 livedirsfx/build.gradle diff --git a/livedirsfx-demo/build.gradle b/livedirsfx-demo/build.gradle new file mode 100644 index 0000000..1f7713a --- /dev/null +++ b/livedirsfx-demo/build.gradle @@ -0,0 +1,14 @@ +group 'org.fxmisc.livedirs' +version '1.0.0-SNAPSHOT' + +apply plugin: 'java' + +sourceCompatibility = 1.5 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.11' +} diff --git a/livedirsfx/build.gradle b/livedirsfx/build.gradle new file mode 100644 index 0000000..9b94814 --- /dev/null +++ b/livedirsfx/build.gradle @@ -0,0 +1,13 @@ +version 'unspecified' + +apply plugin: 'java' + +sourceCompatibility = 1.5 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.11' +} From 58357c01e35149bb3bb8587d85547d0b2670e1a6 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 13:47:19 -0800 Subject: [PATCH 09/23] Partial commit: Moved `src` folder to `livedirsfx` module and copied its previous `build.gradle` content to that module's `build.gradle` file. --- livedirsfx/build.gradle | 102 +++++++++++++++++- .../org/fxmisc/livedirs/CheckBoxContent.java | 0 .../fxmisc/livedirs/CheckBoxContentImpl.java | 0 .../org/fxmisc/livedirs/CheckBoxTreeCell.java | 0 .../CompletionStageWithDefaultExecutor.java | 0 .../java/org/fxmisc/livedirs/DirWatcher.java | 0 .../org/fxmisc/livedirs/DirectoryModel.java | 0 .../java/org/fxmisc/livedirs/IOFacility.java | 0 .../livedirs/InitiatorTrackingIOFacility.java | 0 .../java/org/fxmisc/livedirs/LiveDirs.java | 0 .../java/org/fxmisc/livedirs/LiveDirsIO.java | 0 .../org/fxmisc/livedirs/LiveDirsModel.java | 0 .../org/fxmisc/livedirs/NormalLiveDirs.java | 0 .../java/org/fxmisc/livedirs/PathItem.java | 0 .../java/org/fxmisc/livedirs/Recursive.java | 0 .../fxmisc/livedirs/TreeCellFactories.java | 0 .../resources/org/fxmisc/livedirs/file-16.png | Bin .../org/fxmisc/livedirs/folder-16.png | Bin 18 files changed, 98 insertions(+), 4 deletions(-) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/CheckBoxContent.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/CompletionStageWithDefaultExecutor.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/DirWatcher.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/DirectoryModel.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/IOFacility.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/InitiatorTrackingIOFacility.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/LiveDirs.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/LiveDirsIO.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/LiveDirsModel.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/NormalLiveDirs.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/PathItem.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/Recursive.java (100%) rename {src => livedirsfx/src}/main/java/org/fxmisc/livedirs/TreeCellFactories.java (100%) rename {src => livedirsfx/src}/main/resources/org/fxmisc/livedirs/file-16.png (100%) rename {src => livedirsfx/src}/main/resources/org/fxmisc/livedirs/folder-16.png (100%) diff --git a/livedirsfx/build.gradle b/livedirsfx/build.gradle index 9b94814..18c1755 100644 --- a/livedirsfx/build.gradle +++ b/livedirsfx/build.gradle @@ -1,13 +1,107 @@ -version 'unspecified' +version = '1.0.0-SNAPSHOT' apply plugin: 'java' - -sourceCompatibility = 1.5 +apply plugin: 'eclipse' +apply plugin: 'maven' +apply plugin: 'signing' repositories { mavenCentral() + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots/' + } } +sourceCompatibility = '1.8' +targetCompatibility = '1.8' + +group = 'org.fxmisc.livedirs' + dependencies { - testCompile group: 'junit', name: 'junit', version: '4.11' + compile group: 'org.reactfx', name: 'reactfx', version: '2.0-M4u1' +} + +javadoc { + // ignore missing Javadoc comments or tags + options.addStringOption('Xdoclint:all,-missing', '-quiet') +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from 'build/docs/javadoc' } + +task sourcesJar(type: Jar) { + from sourceSets.main.allSource + classifier = 'sources' +} + +artifacts { + archives jar + + archives javadocJar + archives sourcesJar +} + +signing { + sign configurations.archives +} + +signArchives.onlyIf { + project.hasProperty('signing.keyId') && project.hasProperty('signing.password') && project.hasProperty('signing.secretKeyRingFile') +} + +def doUploadArchives = project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword') + +if(doUploadArchives) { + uploadArchives { + repositories.mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: sonatypeUsername, password: sonatypePassword) + } + + snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots') { + authentication(userName: sonatypeUsername, password: sonatypePassword) + } + + pom.project { + name 'LiveDirsFX' + packaging 'jar' + description 'Directory tree model for JavaFX that watches the filesystem for changes.' + url 'http://www.fxmisc.org/livedirs/' + + scm { + url 'scm:git@github.com:TomasMikula/LiveDirsFX.git' + connection 'scm:git@github.com:TomasMikula/LiveDirsFX.git' + developerConnection 'scm:git@github.com:TomasMikula/LiveDirsFX.git' + } + + licenses { + license { + name 'The BSD 2-Clause License' + url 'http://opensource.org/licenses/BSD-2-Clause' + distribution 'repo' + } + } + + developers { + developer { + name 'Tomas Mikula' + } + } + } + } + } +} + +uploadArchives.onlyIf { doUploadArchives } + +task fatJar(type: Jar, dependsOn: classes) { + appendix = 'fat' + from sourceSets.main.output + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } +} + +assemble.dependsOn fatJar diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/CheckBoxContent.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java diff --git a/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java diff --git a/src/main/java/org/fxmisc/livedirs/CompletionStageWithDefaultExecutor.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/CompletionStageWithDefaultExecutor.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/CompletionStageWithDefaultExecutor.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/CompletionStageWithDefaultExecutor.java diff --git a/src/main/java/org/fxmisc/livedirs/DirWatcher.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/DirWatcher.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/DirWatcher.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/DirWatcher.java diff --git a/src/main/java/org/fxmisc/livedirs/DirectoryModel.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/DirectoryModel.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/DirectoryModel.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/DirectoryModel.java diff --git a/src/main/java/org/fxmisc/livedirs/IOFacility.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/IOFacility.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/IOFacility.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/IOFacility.java diff --git a/src/main/java/org/fxmisc/livedirs/InitiatorTrackingIOFacility.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/InitiatorTrackingIOFacility.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/InitiatorTrackingIOFacility.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/InitiatorTrackingIOFacility.java diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/LiveDirs.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/LiveDirsIO.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirsIO.java diff --git a/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/LiveDirsModel.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java diff --git a/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java diff --git a/src/main/java/org/fxmisc/livedirs/PathItem.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/PathItem.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java diff --git a/src/main/java/org/fxmisc/livedirs/Recursive.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/Recursive.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/Recursive.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/Recursive.java diff --git a/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java similarity index 100% rename from src/main/java/org/fxmisc/livedirs/TreeCellFactories.java rename to livedirsfx/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java diff --git a/src/main/resources/org/fxmisc/livedirs/file-16.png b/livedirsfx/src/main/resources/org/fxmisc/livedirs/file-16.png similarity index 100% rename from src/main/resources/org/fxmisc/livedirs/file-16.png rename to livedirsfx/src/main/resources/org/fxmisc/livedirs/file-16.png diff --git a/src/main/resources/org/fxmisc/livedirs/folder-16.png b/livedirsfx/src/main/resources/org/fxmisc/livedirs/folder-16.png similarity index 100% rename from src/main/resources/org/fxmisc/livedirs/folder-16.png rename to livedirsfx/src/main/resources/org/fxmisc/livedirs/folder-16.png From fd61bb0d907ab24cf7aaad1e7a31dda4a3c7c072 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 13:49:50 -0800 Subject: [PATCH 10/23] Partial Commit: Update `build.gradle` files to build respective projects. --- build.gradle | 111 ++++------------------------------- livedirsfx-demo/build.gradle | 17 +++--- livedirsfx/build.gradle | 14 ----- 3 files changed, 19 insertions(+), 123 deletions(-) diff --git a/build.gradle b/build.gradle index 716adca..b6cb6db 100644 --- a/build.gradle +++ b/build.gradle @@ -1,107 +1,18 @@ -version = '1.0.0-SNAPSHOT' +subprojects { + apply plugin: 'java' + apply plugin: 'eclipse' -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'maven' -apply plugin: 'signing' + version = '1.0.0-SNAPSHOT' -repositories { - mavenCentral() - maven { - url 'https://oss.sonatype.org/content/repositories/snapshots/' - } -} - -sourceCompatibility = '1.8' -targetCompatibility = '1.8' - -group = 'org.fxmisc.livedirs' - -dependencies { - compile group: 'org.reactfx', name: 'reactfx', version: '2.0-M4u1' -} - -javadoc { - // ignore missing Javadoc comments or tags - options.addStringOption('Xdoclint:all,-missing', '-quiet') -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from 'build/docs/javadoc' -} - -task sourcesJar(type: Jar) { - from sourceSets.main.allSource - classifier = 'sources' -} - -artifacts { - archives jar - - archives javadocJar - archives sourcesJar -} - -signing { - sign configurations.archives -} - -signArchives.onlyIf { - project.hasProperty('signing.keyId') && project.hasProperty('signing.password') && project.hasProperty('signing.secretKeyRingFile') -} - -def doUploadArchives = project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword') - -if(doUploadArchives) { - uploadArchives { - repositories.mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { - authentication(userName: sonatypeUsername, password: sonatypePassword) - } - - snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots') { - authentication(userName: sonatypeUsername, password: sonatypePassword) - } - - pom.project { - name 'LiveDirsFX' - packaging 'jar' - description 'Directory tree model for JavaFX that watches the filesystem for changes.' - url 'http://www.fxmisc.org/livedirs/' - - scm { - url 'scm:git@github.com:TomasMikula/LiveDirsFX.git' - connection 'scm:git@github.com:TomasMikula/LiveDirsFX.git' - developerConnection 'scm:git@github.com:TomasMikula/LiveDirsFX.git' - } - - licenses { - license { - name 'The BSD 2-Clause License' - url 'http://opensource.org/licenses/BSD-2-Clause' - distribution 'repo' - } - } - - developers { - developer { - name 'Tomas Mikula' - } - } - } + repositories { + mavenCentral() + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots/' } } -} -uploadArchives.onlyIf { doUploadArchives } + sourceCompatibility = '1.8' + targetCompatibility = '1.8' -task fatJar(type: Jar, dependsOn: classes) { - appendix = 'fat' - from sourceSets.main.output - from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + compileJava.options.deprecation = true } - -assemble.dependsOn fatJar diff --git a/livedirsfx-demo/build.gradle b/livedirsfx-demo/build.gradle index 1f7713a..ddccccf 100644 --- a/livedirsfx-demo/build.gradle +++ b/livedirsfx-demo/build.gradle @@ -1,14 +1,13 @@ group 'org.fxmisc.livedirs' -version '1.0.0-SNAPSHOT' -apply plugin: 'java' - -sourceCompatibility = 1.5 - -repositories { - mavenCentral() +dependencies { + compile project(":livedirsfx") } -dependencies { - testCompile group: 'junit', name: 'junit', version: '4.11' +task fatJar(type: Jar, dependsOn: classes) { + appendix = 'fat' + from sourceSets.main.output + from { configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } } } + +assemble.dependsOn fatJar diff --git a/livedirsfx/build.gradle b/livedirsfx/build.gradle index 18c1755..5a6515a 100644 --- a/livedirsfx/build.gradle +++ b/livedirsfx/build.gradle @@ -1,20 +1,6 @@ -version = '1.0.0-SNAPSHOT' - -apply plugin: 'java' -apply plugin: 'eclipse' apply plugin: 'maven' apply plugin: 'signing' -repositories { - mavenCentral() - maven { - url 'https://oss.sonatype.org/content/repositories/snapshots/' - } -} - -sourceCompatibility = '1.8' -targetCompatibility = '1.8' - group = 'org.fxmisc.livedirs' dependencies { From 49e3bacae5e674e6655796f34f7f7f77c0f97dfb Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 13:50:21 -0800 Subject: [PATCH 11/23] Partial Commit: Update `settings.gradle` files to include both projects. --- settings.gradle | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 settings.gradle diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..38820d2 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include 'livedirsfx', 'livedirsfx-demo' + From 78de587eb40682199760b9f1099245388c3bd22d Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 14:01:21 -0800 Subject: [PATCH 12/23] Partial Commit: Removed factory methods --- .../java/org/fxmisc/livedirs/LiveDirs.java | 73 ------------------- 1 file changed, 73 deletions(-) diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java index 3c08dd6..c122087 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java +++ b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java @@ -15,7 +15,6 @@ import java.util.concurrent.Executor; import java.util.function.Function; -import javafx.application.Platform; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; @@ -40,78 +39,6 @@ */ public class LiveDirs { - /* ****************** * - * Static Factories * - * ****************** */ - - /** - * Creates a LiveDirs instance to be used from a designated thread. - * - * @param externalInitiator object to represent an initiator of an external - * file-system change. - * @param clientThreadExecutor executor to execute actions on the caller - * thread. Used to publish updates and errors on the caller thread. - * @throws IOException - */ - public static LiveDirs getNormalInstance(I externalInitiator, Executor clientThreadExecutor) throws IOException { - return new LiveDirs<>(externalInitiator, Function.identity(), Function.identity(), clientThreadExecutor); - } - - /** - * Same as {@link #getNormalInstance(Object, Executor)} but is for use on the JavaFX application - * thread. - * - * @param externalInitiator object to represent an initiator of an external - * file-system change. - * @throws IOException - */ - public static LiveDirs getNormalInstance(I externalInitiator) throws IOException { - return getNormalInstance(externalInitiator, Platform::runLater); - } - - /** - * Creates a LiveDirs instance to be used from a designated thread. This type of instance - * can be used to display a {@link TreeItem}s as though they were - * {@link javafx.scene.control.CheckBoxTreeItem}s. - * - *

To do so, use the following code: - *

-     *     {@code
-     *     LiveDirs dirs = LiveDirs.getCheckBoxInstance( args );
-     *     TreeView view = new TreeView<>(dirs.model().getRoot());
-     *     view.setCellFactory(TreeCellFactories.checkBoxFactory());
-     *     view.setShowRoot(false);
-     *     }
-     * 
- * - * @param externalInitiator object to represent an initiator of an external - * file-system change. - * @param clientThreadExecutor executor to execute actions on the caller - * thread. Used to publish updates and errors on the caller thread. - * @throws IOException - */ - public static LiveDirs getCheckBoxInstance( - I externalInitiator, Function treeItemConstructor, Executor clientThreadExecutor) throws IOException { - return new LiveDirs<>(externalInitiator, CheckBoxContent::getPath, treeItemConstructor, clientThreadExecutor); - } - - /** - * Same as {@link #getCheckBoxInstance(Object, Function, Executor)} but uses - * {@link CheckBoxContentImpl} to implement the {@link CheckBoxContent} interface. - */ - public static LiveDirs getCheckBoxInstance( - I externalInitiator, Executor clientThreadExecutor) throws IOException { - return getCheckBoxInstance(externalInitiator, CheckBoxContentImpl::new, clientThreadExecutor); - } - - /** - * Same as {@link #getCheckBoxInstance(Object, Executor)} but the instance will be used on the - * JavaFX Application thread - */ - public static LiveDirs getCheckBoxInstance(I externalInitiator) throws IOException { - return getCheckBoxInstance(externalInitiator, Platform::runLater); - } - private final EventSource localErrors = new EventSource<>(); private final EventStream errors; private final Executor clientThreadExecutor; From 5953f154a1d8ce83fbbc90576defff41889e8409 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 14:01:53 -0800 Subject: [PATCH 13/23] Partial Commit: Moved CheckBox related contents to demo package --- livedirsfx-demo/build.gradle | 1 + .../fxmisc/livedirs/demo/checkbox}/CheckBoxContent.java | 2 +- .../livedirs/demo/checkbox}/CheckBoxContentImpl.java | 2 +- .../fxmisc/livedirs/demo/checkbox}/CheckBoxTreeCell.java | 2 +- .../org/fxmisc/livedirs/demo/checkbox}/Recursive.java | 2 +- .../fxmisc/livedirs/demo/checkbox}/TreeCellFactories.java | 8 ++++---- 6 files changed, 9 insertions(+), 8 deletions(-) rename {livedirsfx/src/main/java/org/fxmisc/livedirs => livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox}/CheckBoxContent.java (91%) rename {livedirsfx/src/main/java/org/fxmisc/livedirs => livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox}/CheckBoxContentImpl.java (95%) rename {livedirsfx/src/main/java/org/fxmisc/livedirs => livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox}/CheckBoxTreeCell.java (99%) rename {livedirsfx/src/main/java/org/fxmisc/livedirs => livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox}/Recursive.java (87%) rename {livedirsfx/src/main/java/org/fxmisc/livedirs => livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox}/TreeCellFactories.java (78%) diff --git a/livedirsfx-demo/build.gradle b/livedirsfx-demo/build.gradle index ddccccf..057c6c1 100644 --- a/livedirsfx-demo/build.gradle +++ b/livedirsfx-demo/build.gradle @@ -2,6 +2,7 @@ group 'org.fxmisc.livedirs' dependencies { compile project(":livedirsfx") + compile group: 'org.reactfx', name: 'reactfx', version: '2.0-M4u1' } task fatJar(type: Jar, dependsOn: classes) { diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContent.java similarity index 91% rename from livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java rename to livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContent.java index fd6ac2b..b973f07 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContent.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContent.java @@ -1,4 +1,4 @@ -package org.fxmisc.livedirs; +package org.fxmisc.livedirs.demo.checkbox; import org.reactfx.value.Var; diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContentImpl.java similarity index 95% rename from livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java rename to livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContentImpl.java index af1fe3a..429da7d 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxContentImpl.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContentImpl.java @@ -1,4 +1,4 @@ -package org.fxmisc.livedirs; +package org.fxmisc.livedirs.demo.checkbox; import org.reactfx.value.Var; diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxTreeCell.java similarity index 99% rename from livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java rename to livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxTreeCell.java index dcc6085..fdae963 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/CheckBoxTreeCell.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxTreeCell.java @@ -1,4 +1,4 @@ -package org.fxmisc.livedirs; +package org.fxmisc.livedirs.demo.checkbox; import javafx.beans.InvalidationListener; import javafx.scene.Node; diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/Recursive.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/Recursive.java similarity index 87% rename from livedirsfx/src/main/java/org/fxmisc/livedirs/Recursive.java rename to livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/Recursive.java index 725b53c..00cc5d0 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/Recursive.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/Recursive.java @@ -1,4 +1,4 @@ -package org.fxmisc.livedirs; +package org.fxmisc.livedirs.demo.checkbox; /** * Allows recursive {@link FunctionalInterface} calls. diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/TreeCellFactories.java similarity index 78% rename from livedirsfx/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java rename to livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/TreeCellFactories.java index 3b2819c..55fd1bd 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/TreeCellFactories.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/TreeCellFactories.java @@ -1,4 +1,4 @@ -package org.fxmisc.livedirs; +package org.fxmisc.livedirs.demo.checkbox; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeView; @@ -7,7 +7,7 @@ import java.util.function.Function; /** - * A class that provides cell factories to help display a {@link PathItem} + * A class that provides cell factories to help display a {@link org.fxmisc.livedirs.PathItem} * as though it extended a subclass of {@link javafx.scene.control.TreeItem} * or to provide additional functionality not present in the normal cell factory. */ @@ -18,9 +18,9 @@ public static Callback, TreeCell> che } public static Callback, TreeCell> checkBoxFactory( - Function stringConverter + Function stringConverter ) { - return (view) -> new CheckBoxTreeCell(stringConverter); + return (view) -> new CheckBoxTreeCell<>(stringConverter); } } From a2425eaf8130461111339e73269af8dcda84cabd Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 14:19:03 -0800 Subject: [PATCH 14/23] Finish commit: Create a CheckBoxLiveDirs demo --- .../fxmisc/livedirs/demo/ChangeSource.java | 7 +++ .../livedirs/demo/CheckBoxLiveDirs.java | 43 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/ChangeSource.java create mode 100644 livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/ChangeSource.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/ChangeSource.java new file mode 100644 index 0000000..55eea5c --- /dev/null +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/ChangeSource.java @@ -0,0 +1,7 @@ +package org.fxmisc.livedirs.demo; + + +public enum ChangeSource { + INTERNAL, + EXTERNAL +} diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java new file mode 100644 index 0000000..1895d75 --- /dev/null +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java @@ -0,0 +1,43 @@ +package org.fxmisc.livedirs.demo; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.Scene; +import javafx.scene.control.TreeView; +import javafx.stage.Stage; +import org.fxmisc.livedirs.LiveDirs; +import org.fxmisc.livedirs.demo.checkbox.CheckBoxContentImpl; +import org.fxmisc.livedirs.demo.checkbox.TreeCellFactories; + +import java.io.IOException; +import java.nio.file.Paths; + +public class CheckBoxLiveDirs extends Application { + + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) { + TreeView view = new TreeView<>(); + view.setShowRoot(false); + view.setCellFactory(TreeCellFactories.checkBoxFactory()); + + try { + // create a LiveDirs instance for use on the JavaFX Application Thread + // and make it display its items as though they were CheckBoxTreeItems + LiveDirs dirs = new LiveDirs<>(ChangeSource.EXTERNAL, + CheckBoxContentImpl::getPath, CheckBoxContentImpl::new, Platform::runLater); + + // set directory to watch + dirs.addTopLevelDirectory(Paths.get(System.getProperty("user.home"), "Documents").toAbsolutePath()); + view.setRoot(dirs.model().getRoot()); + } catch (IOException e) { + e.printStackTrace(); + } + + primaryStage.setScene(new Scene(view, 500, 500)); + primaryStage.show(); + } +} From 3ec63a98a0b738f080a4357078db237c1e71b4a3 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 14:19:21 -0800 Subject: [PATCH 15/23] Add a task to execute CheckBoxLiveDirs demo --- livedirsfx-demo/build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/livedirsfx-demo/build.gradle b/livedirsfx-demo/build.gradle index 057c6c1..b8f5d9b 100644 --- a/livedirsfx-demo/build.gradle +++ b/livedirsfx-demo/build.gradle @@ -12,3 +12,10 @@ task fatJar(type: Jar, dependsOn: classes) { } assemble.dependsOn fatJar + +task CheckBoxLiveDirs(type: JavaExec, dependsOn: classes) { + main = 'org.fxmisc.livedirs.demo.CheckBoxLiveDirs' + classpath = files(sourceSets.main.output, configurations.runtime) + description = 'Demonstrates a working LiveDirs instance that displays its items as though' + + 'they are CheckBoxTreeItems' +} \ No newline at end of file From 0e2f195fd595e05206ce684528f5fc981e2cdb46 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 14:19:59 -0800 Subject: [PATCH 16/23] Move NormalLiveDirs' constructors into LiveDirs as static factory methods --- .../java/org/fxmisc/livedirs/LiveDirs.java | 26 +++++++++ .../org/fxmisc/livedirs/NormalLiveDirs.java | 54 ------------------- 2 files changed, 26 insertions(+), 54 deletions(-) delete mode 100644 livedirsfx/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java index c122087..bf24eaa 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java +++ b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java @@ -15,6 +15,7 @@ import java.util.concurrent.Executor; import java.util.function.Function; +import javafx.application.Platform; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; @@ -39,6 +40,31 @@ */ public class LiveDirs { + /** + * Creates a LiveDirs instance to be used from the JavaFX application + * thread. + * + * @param externalInitiator object to represent an initiator of an external + * file-system change. + * @throws IOException + */ + public static LiveDirs getNormalInstance(I externalInitiator) throws IOException { + return getNormalInstance(externalInitiator, Platform::runLater); + } + + /** + * Creates a LiveDirs instance to be used from a designated thread. + * + * @param externalInitiator object to represent an initiator of an external + * file-system change. + * @param clientThreadExecutor executor to execute actions on the caller + * thread. Used to publish updates and errors on the caller thread. + * @throws IOException + */ + public static LiveDirs getNormalInstance(I externalInitiator, Executor clientThreadExecutor) throws IOException { + return new LiveDirs<>(externalInitiator, Function.identity(), Function.identity(), clientThreadExecutor); + } + private final EventSource localErrors = new EventSource<>(); private final EventStream errors; private final Executor clientThreadExecutor; diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java deleted file mode 100644 index d7c37b9..0000000 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/NormalLiveDirs.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.fxmisc.livedirs; - - -import javafx.application.Platform; -import javafx.scene.control.TreeItem; -import org.reactfx.EventStreams; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.concurrent.Executor; -import java.util.function.Function; - -/** - * NormalLiveDirs is the standard {@link LiveDirs} used in most - * {@link javafx.scene.control.TreeView} cases. It uses {@link TreeItem} (not - * one of its subclasses) for its {@link LiveDirsModel}'s root and children. - * - *

To initialize a {@link javafx.scene.control.TreeView} correctly, use the following code:

- *
- *     {@code
- *     NormalLiveDirs dirs = // creation code;
- *     TreeView view = new TreeView(dirs.model().getRoot());
- *     view.setShowRoot(false);
- *     }
- * 
- * @param type of the initiator of I/O actions. - */ -public class NormalLiveDirs extends LiveDirs { - - /** - * Creates a NormalLiveDirs instance to be used from the JavaFX application - * thread. - * - * @param externalInitiator object to represent an initiator of an external - * file-system change. - * @throws IOException - */ - public NormalLiveDirs(I externalInitiator) throws IOException { - this(externalInitiator, Platform::runLater); - } - - /** - * Creates a NormalLiveDirs instance to be used from a designated thread. - * - * @param externalInitiator object to represent an initiator of an external - * file-system change. - * @param clientThreadExecutor executor to execute actions on the caller - * thread. Used to publish updates and errors on the caller thread. - * @throws IOException - */ - public NormalLiveDirs(I externalInitiator, Executor clientThreadExecutor) throws IOException { - super(externalInitiator, Function.identity(), Function.identity(), clientThreadExecutor); - } -} From 0d80fd28d0ef1f0479d93685bd22286e8030cb71 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 14:23:07 -0800 Subject: [PATCH 17/23] Added demo for LiveDirs instances that don't display items as though they were CheckBoxTreeItems --- .../fxmisc/livedirs/demo/NormalLiveDirs.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java new file mode 100644 index 0000000..908c422 --- /dev/null +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java @@ -0,0 +1,38 @@ +package org.fxmisc.livedirs.demo; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.TreeView; +import javafx.stage.Stage; +import org.fxmisc.livedirs.LiveDirs; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class NormalLiveDirs extends Application { + + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) { + TreeView view = new TreeView<>(); + view.setShowRoot(false); + + try { + // create a LiveDirs instance for use on the JavaFX Application Thread + LiveDirs dirs = LiveDirs.getNormalInstance(ChangeSource.EXTERNAL); + + // set directory to watch + dirs.addTopLevelDirectory(Paths.get(System.getProperty("user.home"), "Documents").toAbsolutePath()); + view.setRoot(dirs.model().getRoot()); + } catch (IOException e) { + e.printStackTrace(); + } + + primaryStage.setScene(new Scene(view, 500, 500)); + primaryStage.show(); + } +} From f3ce1d9a8195ebfcccc62052ac46f8067924f06c Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 14:23:36 -0800 Subject: [PATCH 18/23] Added gradle task for NormalLiveDirs demo --- livedirsfx-demo/build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/livedirsfx-demo/build.gradle b/livedirsfx-demo/build.gradle index b8f5d9b..fb23a20 100644 --- a/livedirsfx-demo/build.gradle +++ b/livedirsfx-demo/build.gradle @@ -18,4 +18,10 @@ task CheckBoxLiveDirs(type: JavaExec, dependsOn: classes) { classpath = files(sourceSets.main.output, configurations.runtime) description = 'Demonstrates a working LiveDirs instance that displays its items as though' + 'they are CheckBoxTreeItems' +} + +task NormalLiveDirs(type: JavaExec, dependsOn: classes) { + main = 'org.fxmisc.livedirs.demo.NormalLiveDirs' + classpath = files(sourceSets.main.output, configurations.runtime) + description = 'Demonstrates a working LiveDirs instance that displays its items normally' } \ No newline at end of file From e60c4c31a6e81e2a97f6f71b774cd02b8495815c Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 14:27:06 -0800 Subject: [PATCH 19/23] Fixed bug in demos: after closing, DirWatcher's threads wouldn't terminate. --- .../main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java | 3 +++ .../src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java index 1895d75..64aeb61 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java @@ -33,6 +33,9 @@ public void start(Stage primaryStage) { // set directory to watch dirs.addTopLevelDirectory(Paths.get(System.getProperty("user.home"), "Documents").toAbsolutePath()); view.setRoot(dirs.model().getRoot()); + + // stop DirWatcher's thread + primaryStage.setOnCloseRequest(val -> dirs.dispose()); } catch (IOException e) { e.printStackTrace(); } diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java index 908c422..f2cf0c2 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java @@ -28,6 +28,9 @@ public void start(Stage primaryStage) { // set directory to watch dirs.addTopLevelDirectory(Paths.get(System.getProperty("user.home"), "Documents").toAbsolutePath()); view.setRoot(dirs.model().getRoot()); + + // stop DirWatcher's thread + primaryStage.setOnCloseRequest(val -> dirs.dispose()); } catch (IOException e) { e.printStackTrace(); } From 7be7a36ad6c1686b2ef2b4530a138c24ef24c359 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 15:19:06 -0800 Subject: [PATCH 20/23] Rename `getNormalInstance` to shorter `getInstance` --- .../main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java | 2 +- livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java index f2cf0c2..9a8ec7c 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java @@ -23,7 +23,7 @@ public void start(Stage primaryStage) { try { // create a LiveDirs instance for use on the JavaFX Application Thread - LiveDirs dirs = LiveDirs.getNormalInstance(ChangeSource.EXTERNAL); + LiveDirs dirs = LiveDirs.getInstance(ChangeSource.EXTERNAL); // set directory to watch dirs.addTopLevelDirectory(Paths.get(System.getProperty("user.home"), "Documents").toAbsolutePath()); diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java index bf24eaa..e6d626a 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java +++ b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirs.java @@ -48,8 +48,8 @@ public class LiveDirs { * file-system change. * @throws IOException */ - public static LiveDirs getNormalInstance(I externalInitiator) throws IOException { - return getNormalInstance(externalInitiator, Platform::runLater); + public static LiveDirs getInstance(I externalInitiator) throws IOException { + return getInstance(externalInitiator, Platform::runLater); } /** @@ -61,7 +61,7 @@ public static LiveDirs getNormalInstance(I externalInitiator) throw * thread. Used to publish updates and errors on the caller thread. * @throws IOException */ - public static LiveDirs getNormalInstance(I externalInitiator, Executor clientThreadExecutor) throws IOException { + public static LiveDirs getInstance(I externalInitiator, Executor clientThreadExecutor) throws IOException { return new LiveDirs<>(externalInitiator, Function.identity(), Function.identity(), clientThreadExecutor); } From 1d6c51dd89e1ef4ebf38e752807e98de6ed60088 Mon Sep 17 00:00:00 2001 From: Tomas Mikula Date: Fri, 19 Feb 2016 18:42:34 -0500 Subject: [PATCH 21/23] Eliminate some type casts and warnings. --- .../org/fxmisc/livedirs/LiveDirsModel.java | 6 ++--- .../java/org/fxmisc/livedirs/PathItem.java | 23 ++++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java index ae81d45..dcbfd88 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java +++ b/livedirsfx/src/main/java/org/fxmisc/livedirs/LiveDirsModel.java @@ -73,8 +73,7 @@ public boolean contains(Path path) { public boolean containsPrefixOf(Path path) { return root.getChildren().stream() - .map(child -> (PathItem) child) - .anyMatch(item -> path.startsWith(item.getPath())); + .anyMatch(item -> path.startsWith(projector.apply(item.getValue()))); } void addTopLevelDirectory(Path dir) { @@ -117,8 +116,7 @@ void sync(PathNode tree) { private Stream> topLevelAncestorStream(Path path) { return root.getChildren().stream() - .map(child -> (PathItem) child) - .filter(item -> path.startsWith(item.getPath())) + .filter(item -> path.startsWith(projector.apply(item.getValue()))) .map(item -> (TopLevelDirItem) item); } diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java index 5654465..a88dd5a 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java +++ b/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java @@ -66,7 +66,7 @@ protected PathItem resolve(Path relPath) { } class FileItem extends PathItem { - public static FileItem create(T path, FileTime lastModified, GraphicFactory graphicFactory, Function projector) { + public static FileItem create(T path, FileTime lastModified, GraphicFactory graphicFactory, Function projector) { return new FileItem<>(path, lastModified, graphicFactory.createGraphic(projector.apply(path), false), projector); } @@ -98,7 +98,7 @@ class DirItem extends PathItem { protected final Function getInjector() { return injector; } public final T inject(Path path) { return injector.apply(path); } - public static DirItem create(T path, GraphicFactory graphicFactory, Function projector, Function injector) { + public static DirItem create(T path, GraphicFactory graphicFactory, Function projector, Function injector) { return new DirItem<>(path, graphicFactory.createGraphic(projector.apply(path), true), projector, injector); } @@ -116,7 +116,6 @@ public FileItem addChildFile(Path fileName, FileTime lastModified, GraphicFac assert fileName.getNameCount() == 1; int i = getFileInsertionIndex(fileName.toString()); - @SuppressWarnings("unchecked") FileItem child = FileItem.create(inject(getPath().resolve(fileName)), lastModified, graphicFactory, getProjector()); getChildren().add(i, child); return child; @@ -126,7 +125,6 @@ public DirItem addChildDir(Path dirName, GraphicFactory graphicFactory) { assert dirName.getNameCount() == 1; int i = getDirInsertionIndex(dirName.toString()); - @SuppressWarnings("unchecked") DirItem child = DirItem.create(inject(getPath().resolve(dirName)), graphicFactory, getProjector(), getInjector()); getChildren().add(i, child); return child; @@ -217,7 +215,7 @@ private ParentChild resolveInParent(Path relPath) { } private void updateFile(Path relPath, FileTime lastModified, I initiator) { - PathItem item = resolve(relPath); + PathItem item = resolve(relPath); if(item == null || item.isDirectory()) { sync(PathNode.file(getPath().resolve(relPath), lastModified), initiator); } @@ -236,7 +234,7 @@ public void updateModificationTime(Path relPath, FileTime lastModified, I initia } public void addDirectory(Path relPath, I initiator) { - PathItem item = resolve(relPath); + PathItem item = resolve(relPath); if(item == null || !item.isDirectory()) { sync(PathNode.directory(getPath().resolve(relPath), Collections.emptyList()), initiator); } @@ -272,9 +270,8 @@ private void syncContent(DirItem dir, PathNode tree, I initiator) { // remove undesired children for(TreeItem ch: actualChildren) { - PathItem pathCh = (PathItem) ch; - if(!desiredChildren.contains(pathCh.getPath())) { - removeNode(pathCh, null); + if(!desiredChildren.contains(getProjector().apply(ch.getValue()))) { + removeNode(ch, null); } } @@ -316,16 +313,16 @@ public void remove(Path relPath, I initiator) { } } - private void removeNode(PathItem node, I initiator) { + private void removeNode(TreeItem node, I initiator) { signalDeletionRecursively(node, initiator); node.getParent().getChildren().remove(node); } - private void signalDeletionRecursively(PathItem node, I initiator) { + private void signalDeletionRecursively(TreeItem node, I initiator) { for(TreeItem child: node.getChildren()) { - signalDeletionRecursively((PathItem) child, initiator); + signalDeletionRecursively(child, initiator); } - reporter.reportDeletion(getPath(), getPath().relativize(node.getPath()), initiator); + reporter.reportDeletion(getPath(), getPath().relativize(getProjector().apply(node.getValue())), initiator); } private void raise(Throwable t) { From 1f732836804b4cf17381a9a4b918c7e96723f093 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Fri, 19 Feb 2016 15:49:34 -0800 Subject: [PATCH 22/23] Minor edit that makes code more readable --- livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java b/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java index 5654465..45371ab 100644 --- a/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java +++ b/livedirsfx/src/main/java/org/fxmisc/livedirs/PathItem.java @@ -38,7 +38,7 @@ public final boolean isLeaf() { public PathItem getRelChild(Path relPath) { assert relPath.getNameCount() == 1; - Path childValue = projector.apply(getValue()).resolve(relPath); + Path childValue = getPath().resolve(relPath); for(TreeItem ch: getChildren()) { PathItem pathCh = (PathItem) ch; if(pathCh.getPath().equals(childValue)) { From 8e0f6f04969926ea8b5f618d172af33c8fd8bb27 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Thu, 10 Mar 2016 12:16:01 -0800 Subject: [PATCH 23/23] Dedicate demos to public domain to get around BSD license restrictions --- .../main/java/org/fxmisc/livedirs/demo/ChangeSource.java | 6 ++++++ .../java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java | 6 ++++++ .../main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java | 6 ++++++ .../org/fxmisc/livedirs/demo/checkbox/CheckBoxContent.java | 6 ++++++ .../fxmisc/livedirs/demo/checkbox/CheckBoxContentImpl.java | 6 ++++++ .../org/fxmisc/livedirs/demo/checkbox/CheckBoxTreeCell.java | 6 ++++++ .../java/org/fxmisc/livedirs/demo/checkbox/Recursive.java | 6 ++++++ .../fxmisc/livedirs/demo/checkbox/TreeCellFactories.java | 6 ++++++ 8 files changed, 48 insertions(+) diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/ChangeSource.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/ChangeSource.java index 55eea5c..b50e807 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/ChangeSource.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/ChangeSource.java @@ -1,3 +1,9 @@ +/** + * Created 2016 by Jordan Martinez + * + * The author dedicates this to the public domain + */ + package org.fxmisc.livedirs.demo; diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java index 64aeb61..4c1feae 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/CheckBoxLiveDirs.java @@ -1,3 +1,9 @@ +/** + * Created 2016 by Jordan Martinez + * + * The author dedicates this to the public domain + */ + package org.fxmisc.livedirs.demo; import javafx.application.Application; diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java index 9a8ec7c..5e57c82 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/NormalLiveDirs.java @@ -1,3 +1,9 @@ +/** + * Created 2016 by Jordan Martinez + * + * The author dedicates this to the public domain + */ + package org.fxmisc.livedirs.demo; import javafx.application.Application; diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContent.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContent.java index b973f07..21e031b 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContent.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContent.java @@ -1,3 +1,9 @@ +/** + * Created 2016 by Jordan Martinez + * + * The author dedicates this to the public domain + */ + package org.fxmisc.livedirs.demo.checkbox; import org.reactfx.value.Var; diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContentImpl.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContentImpl.java index 429da7d..b4ffee9 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContentImpl.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxContentImpl.java @@ -1,3 +1,9 @@ +/** + * Created 2016 by Jordan Martinez + * + * The author dedicates this to the public domain + */ + package org.fxmisc.livedirs.demo.checkbox; import org.reactfx.value.Var; diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxTreeCell.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxTreeCell.java index fdae963..2ea64b0 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxTreeCell.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/CheckBoxTreeCell.java @@ -1,3 +1,9 @@ +/** + * Created 2016 by Jordan Martinez + * + * The author dedicates this to the public domain + */ + package org.fxmisc.livedirs.demo.checkbox; import javafx.beans.InvalidationListener; diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/Recursive.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/Recursive.java index 00cc5d0..adc53f4 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/Recursive.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/Recursive.java @@ -1,3 +1,9 @@ +/** + * Created 2016 by Jordan Martinez + * + * The author dedicates this to the public domain + */ + package org.fxmisc.livedirs.demo.checkbox; /** diff --git a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/TreeCellFactories.java b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/TreeCellFactories.java index 55fd1bd..9c576ec 100644 --- a/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/TreeCellFactories.java +++ b/livedirsfx-demo/src/main/java/org/fxmisc/livedirs/demo/checkbox/TreeCellFactories.java @@ -1,3 +1,9 @@ +/** + * Created 2016 by Jordan Martinez + * + * The author dedicates this to the public domain + */ + package org.fxmisc.livedirs.demo.checkbox; import javafx.scene.control.TreeCell;