GitHub PullRequestReviewCommentEvent at 2014-06-23 13:56:11
Repository: FileSieve
Actor: JeffBlodgett
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/comments/14075983","id":14075983,"diff_hunk":"@@ -0,0 +1,371 @@\n+package FileSieve.BusinessLogic.FileManagement;\n+\n+import org.junit.After;\n+import org.junit.Assume;\n+import org.junit.Before;\n+import org.junit.Test;\n+import org.junit.Assert;\n+\n+import java.beans.PropertyChangeEvent;\n+import java.beans.PropertyChangeListener;\n+import java.io.BufferedOutputStream;\n+import java.io.File;\n+import java.io.FileOutputStream;\n+import java.io.IOException;\n+import java.nio.file.DirectoryStream;\n+import java.nio.file.Files;\n+import java.nio.file.LinkOption;\n+import java.nio.file.Path;\n+import java.util.AbstractMap.SimpleImmutableEntry;\n+import java.util.concurrent.ExecutionException;\n+import java.util.concurrent.atomic.AtomicBoolean;\n+\n+/**\n+ * JUnit testing for the ConcurrentFileManager class\n+ */\n+public class ConcurrentFileManagerTest {\n+\n+    private final FileManager fileManager = FileManagerFactory.getFileManager();\n+    private final String userTempFolder = System.getProperty(\"java.io.tmpdir\");\n+    private final Path file = new File(userTempFolder + \"swingWorkerFileManagementTestFile.txt\").toPath();\n+    private final Path folder = new File(userTempFolder + \"swingWorkerFileManagementTestFolder\").toPath();\n+    private final Path subFolder = new File(folder + \"/subFolderForSwingWorkerFileManagementTests\").toPath();\n+    private final Path anotherFolder = new File(userTempFolder + \"swingWorkerFileManagementTestFolder2\").toPath();\n+    private static boolean deletePathnameTestsPassed = false;\n+\n+    @Before\n+    public void setup() {\n+        Assume.assumeTrue(\"temporary file and folder used for tests should not pre-exist\", !Files.exists(file) && !Files.exists(folder));\n+    }\n+\n+    @After\n+    public void cleanup() {\n+        try {\n+            Files.deleteIfExists(file);\n+\n+            if (deletePathnameTestsPassed) {\n+                fileManager.deletePathname(folder);\n+                fileManager.deletePathname(anotherFolder);\n+            }\n+        } catch (SecurityException | IOException e) {\n+            // Ignore exceptions\n+        }\n+    }\n+\n+    @Test\n+    public void testDeletePathname() {\n+        Path folderFile1 = new File(folder + \"/testFile1.txt\").toPath();\n+        Path folderFile2 = new File(folder + \"/testFile2.txt\").toPath();\n+        Path subFolderFile1 = new File(subFolder + \"/testDeletePathname1.txt\").toPath();\n+        Path subFolderFile2 = new File(subFolder + \"/testDeletePathname2.txt\").toPath();\n+\n+        try {\n+            Assert.assertFalse(\"Attempted deletion of non-existent file should return false\", fileManager.deletePathname(file));\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \" while attempting to delete non-existent file \\\"\" + file.toFile().getName() + \"\\\" in temp folder\");\n+        }\n+        try {\n+            Assert.assertFalse(\"Attempted deletion of non-existent folder should return false\", fileManager.deletePathname(folder));\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \" while attempting to delete non-existent folder \\\"\" + folder.toFile().getName() + \"\\\" in temp folder\");\n+        }\n+\n+        try {\n+            Files.createFile(file);\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \" while attempting to create \\\"\" + file.toFile().getName() + \"\\\" file in temp folder\");\n+        }\n+        try {\n+            Assert.assertTrue(fileManager.deletePathname(file));\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() +  \"while attempting to delete \\\"\" + file.toFile().getName() + \"\\\" file in temp folder\");\n+        }\n+\n+        try {\n+            Files.createDirectory(folder);\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \"while attempting to create \\\"\" + folder.toFile().getName() + \"\\\" folder in temp folder\");\n+        }\n+        try {\n+            Assert.assertTrue(fileManager.deletePathname(folder));\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \"while attempting to delete folder hierarchy in temp folder\");\n+        }\n+\n+        try {\n+            Files.createDirectory(folder);\n+            Files.createFile(folderFile1);\n+            Files.createFile(folderFile2);\n+            Files.createDirectory(subFolder);\n+            Files.createFile(subFolderFile1);\n+            Files.createFile(subFolderFile2);\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \"while attempting to create a folder hierarchy in the temp folder\");\n+        }\n+        try {\n+            Assert.assertTrue(fileManager.deletePathname(folder));\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \"while attempting to delete a folder hierarchy in the temp folder\");\n+        }\n+\n+        deletePathnameTestsPassed = true;\n+    }\n+\n+    @Test\n+    public void testOpenPathname() {\n+        try {\n+            Files.createFile(file);\n+        } catch (IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \"while attempting to create temporary file for use in testing \\\"openPathname\\\" method\");\n+        }\n+\n+        try {\n+            ((FileManager)fileManager).setDesktopOpenDisabled(true);   // Prevents file from being open in next statement\n+            fileManager.openPathname(file);\n+        } catch (UnsupportedOperationException e) {\n+            Assert.fail(\"current platform does not support the Desktop class, does not support the Desktop.Action.OPEN action, or is headless\");\n+        } catch (SecurityException e) {\n+            Assert.fail(\"insufficient access permissions\");\n+        } catch (IOException e) {\n+            Assert.fail(\"the specified file has no associated application or the associated application failed to be launched\");\n+        }\n+\n+        try {\n+            Files.deleteIfExists(file);\n+        } catch (IOException e) {\n+            Assert.fail(\"Unable to delete temporary file used for testing \\\"openPathname\\\" method\");\n+        }\n+    }\n+\n+    @Test\n+    public void testCopyPathname() {\n+        if (deletePathnameTestsPassed == false) Assert.fail(\"testings of pathnameCopyProviders method depends on deletePathname testing, one or assertions for which failed\");\n+\n+        // Prepare a temp folder with files and folders to copy\n+        try {\n+            Files.createDirectory(folder);          // creates folder named \"swingWorkerFileManagementTestFolder\" in user's temp folder\n+        } catch (SecurityException | IOException e) {\n+            Assert.fail(e.getClass().getSimpleName() + \"while attempting to create \\\"\" + folder.toFile().getName() + \"\\\" folder in temp folder\");\n+        }\n+        Path fileToCopy = new File(folder + \"/fileToCopy.dat\").toPath();\n+        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileToCopy.toFile()))) {\n+            int aByte = 0;\n+            for (long i = 0; i < 2000000L; i++) {\n+                bos.write(aByte);\n+            }\n+        } catch (IOException e) {\n+            Assert.fail(\"IOException while attempting to create temporary file #1 for use in testing file copy operations\");\n+        }\n+        try {\n+            Files.createDirectory(subFolder);   // Create an empty subfolder under the source folder\n+        } catch (IOException e) {\n+            Assert.fail(\"IOException while attempting to create temporary file #2 for use in testing file copy operations\");\n+        }\n+        Path anotherFileToCopy = new File(subFolder + \"/anotherFileToCopy.dat\").toPath();\n+        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(anotherFileToCopy.toFile()))) {\n+            int aByte = 0;\n+            for (long i = 0; i < 2000000L; i++) {\n+                bos.write(aByte);\n+            }\n+        } catch (IOException e) {\n+            Assert.fail(\"IOException while attempting to create temporary file #2 for use in testing file copy operations\");\n+        }\n+\n+        // ASSERTION SET 1: Copy a single file to a non-existing target folder (recursion enabled but isn't applicable in the case of a file as the sourcePathname)\n+        if (fileManager.copyPathname(fileToCopy, anotherFolder, true, false, false)) {\n+            try {\n+                // Use this test class' custom CopyJobCompletionListener for detecting when the SwingWorker is truly finished\n+                CopyJobCompletionListener jobCompletionListener = new CopyJobCompletionListener(fileManager, fileToCopy);\n+                fileManager.addPropertyChangeListener(fileToCopy.toString(), jobCompletionListener);\n+                jobCompletionListener.waitForJobCompletion();   // rethrows interrupted exception from SwingWorker internals\n+\n+                Assert.assertEquals(\"copying of a file to a new folder, created one pathname in target folder\", 1, getChildCount(anotherFolder));\n+\n+                Path fileNameToCheck = fileToCopy.getName(fileToCopy.getNameCount() - 1);\n+                Assert.assertTrue(\"copying of a file to a new folder, copy of file exists in targetPathname\", Files.isRegularFile(new File(anotherFolder.resolve(fileNameToCheck).toString()).toPath()));\n+            } catch (InterruptedException e) {\n+                Assert.fail(\"InterruptedException during BackgroundCopyWorker execution, copying of a file to a new folder\");\n+            } catch (ExecutionException e) {\n+                Assert.fail(e.getCause().getClass().getSimpleName() + \" during BackgroundCopyWorker execution, copying of a file to a new folder - \" + e.getCause().getMessage());\n+            }\n+            try {\n+                fileManager.deletePathname(anotherFolder);\n+            } catch (IOException e) {\n+                Assert.fail(\"IOException while deleting folder following assertion of non-recursive copying of a folder - \" + anotherFolder);\n+            }\n+        } else {\n+            Assert.fail(\"copying of a file to a new folder, could not start copy job\");\n+        }\n+\n+        // ASSERTION SET 2: Copy folder contents to a non-existing target folder with folder recursion disabled\n+        if (fileManager.copyPathname(folder, anotherFolder, false, false, false)) {\n+            try {\n+                CopyJobCompletionListener jobCompletionListener = new CopyJobCompletionListener(fileManager, folder);\n+                fileManager.addPropertyChangeListener(folder.toString(), jobCompletionListener);\n+                jobCompletionListener.waitForJobCompletion();\n+\n+                Assert.assertEquals(\"non recursive copying of a folder, created two pathnames in target folder\", 2, getChildCount(anotherFolder));\n+\n+                Path fileNameToCheck = fileToCopy.getName(fileToCopy.getNameCount() - 1);\n+                Assert.assertTrue(\"non-recursive copying of a folder, copied a file into the target folder\", Files.isRegularFile(anotherFolder.resolve(fileNameToCheck), LinkOption.NOFOLLOW_LINKS));\n+\n+                Path folderNameToCheck = subFolder.getName(subFolder.getNameCount() - 1);\n+                Assert.assertTrue(\"non-recursive copying of a folder, created an empty folder within target folder\", Files.isDirectory(anotherFolder.resolve(folderNameToCheck)));\n+            } catch (InterruptedException e) {\n+                Assert.fail(\"InterruptedException during BackgroundCopyWorker execution, non-recursive copying of a folder\");\n+            } catch (ExecutionException e) {\n+                Assert.fail(e.getCause().getClass().getSimpleName() + \" during BackgroundCopyWorker execution, non-recursive copying of a folder - \" + e.getCause().getMessage());\n+            }\n+            try {\n+                fileManager.deletePathname(anotherFolder);\n+            } catch (IOException e) {\n+                Assert.fail(\"IOException while deleting folder following assertion of non-recursive copying of a folder: \" + anotherFolder);\n+            }\n+        } else {\n+            Assert.fail(\"non-recursive copying of a folder, could not start copy job\");\n+        }\n+\n+        // ASSERTION SET 3: Copy a folder, with the contents of all subfolders, to a target folder (folder recursion enabled)\n+        if (fileManager.copyPathname(folder, anotherFolder, true, false, false)) {\n+            try {\n+                CopyJobCompletionListener jobCompletionListener = new CopyJobCompletionListener(fileManager, folder);\n+                fileManager.addPropertyChangeListener(folder.toString(), jobCompletionListener);\n+                jobCompletionListener.waitForJobCompletion();\n+\n+                Assert.assertEquals(\"recursive copying of a folder, created two pathnames in target folder\", 3, getChildCount(anotherFolder));\n+\n+                Path fileNameToCheck = fileToCopy.getName(fileToCopy.getNameCount() - 1);\n+                Assert.assertTrue(\"recursive copying of a folder, copied a file into the target folder\", Files.isRegularFile(anotherFolder.resolve(fileNameToCheck), LinkOption.NOFOLLOW_LINKS));\n+\n+                Path folderNameToCheck = subFolder.getName(subFolder.getNameCount() - 1);\n+                Path folderPathToCheck = anotherFolder.resolve(folderNameToCheck);\n+                Assert.assertTrue(\"recursive copying of a folder, created a folder within target folder\", Files.isDirectory(folderPathToCheck));\n+\n+                fileNameToCheck = anotherFileToCopy.getName(anotherFileToCopy.getNameCount() - 1);\n+                Assert.assertTrue(\"recursive copying of a folder, copied a file into a folder within the target folder\", Files.isRegularFile(folderPathToCheck.resolve(fileNameToCheck), LinkOption.NOFOLLOW_LINKS));\n+            } catch (InterruptedException e) {\n+                Assert.fail(\"InterruptedException during BackgroundCopyWorker execution, recursive copying of a folder\");\n+            } catch (ExecutionException e) {\n+                Assert.fail(e.getCause().getClass().getSimpleName() + \" during BackgroundCopyWorker execution, recursive copying of a folder - \" + e.getCause().getMessage());\n+            }\n+        } else {\n+            Assert.fail(\"recursive copying of a folder, could not start copy job\");\n+        }\n+\n+        Assert.assertEquals(\"tracked copy jobs have been been removed from class' internal map\", 0, ((ConcurrentFileManager)fileManager).copyJobs.size());\n+\n+        // Cleanup\n+        try {\n+            fileManager.deletePathname(folder);\n+            fileManager.deletePathname(anotherFolder);\n+        } catch (IOException e) {\n+            Assert.fail(\"IOException while attempting to delete temporary folder tree\");\n+        }\n+    }\n+\n+    /**\n+     * Helper method for counting the number of subfolders and files within a given pathname\n+     *\n+     * @param pathname  pathname (folder or file) to search\n+     * @return          number of files and subfolders found within the given pathname\n+     */\n+    private int getChildCount(Path pathname) {\n+        int count = 0;\n+\n+        try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(pathname);) {\n+            for (Path path : dirStream) {\n+                if (Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS)) {\n+                    count++;\n+                } else if (Files.isDirectory(path)) {\n+                    count += 1 + getChildCount(path);\n+                }\n+            }\n+        } catch (IOException e) {\n+            // Ignore exceptions\n+        }\n+\n+        return count;\n+    }\n+\n+    /**\n+     * Custom PropertyChangeLister for detecting when a copy job has been completed.\n+     */\n+    private static class CopyJobCompletionListener implements PropertyChangeListener {\n+\n+        private FileManager propertyChangeSupporter;\n+        private Path jobName = null;\n+        private final AtomicBoolean completionDetected = new AtomicBoolean(false);\n+        private final Object lock = new Object();\n+        private Throwable internalWorkerException = null;\n+\n+        /**\n+         * Constructor for CopyJobCompletionListener class\n+         *\n+         * @param propertyChangeSupporter   the FileManager with which the listener will be registered/unregistered\n+         */\n+        public CopyJobCompletionListener(FileManager propertyChangeSupporter, Path sourcePathBeingCopied) {\n+            if (propertyChangeSupporter == null) throw new IllegalArgumentException(\"null reference provided for PropertyChangeSupport object\");\n+            if (sourcePathBeingCopied == null) throw new IllegalArgumentException(\"null reference provided for PropertyChangeSupport object\");\n+\n+            this.propertyChangeSupporter = propertyChangeSupporter;\n+            setJob(sourcePathBeingCopied);\n+        }\n+\n+        /**\n+         *\n+         * @param sourcePathBeingCopied\n+         */\n+        private void setJob(Path sourcePathBeingCopied) {\n+                completionDetected.set(false);\n+                jobName = sourcePathBeingCopied;\n+                propertyChangeSupporter.addPropertyChangeListener(sourcePathBeingCopied.toString(), this);\n+        }\n+\n+        public void propertyChange(PropertyChangeEvent e) {\n+            String propertyName = e.getPropertyName();\n+\n+            if (propertyName.equals(jobName.toString())) {\n+                Object newValue = e.getNewValue();\n+                Object oldValue = e.getOldValue();\n+\n+                if ((newValue != null) && (newValue instanceof SimpleImmutableEntry) && (((SimpleImmutableEntry<String, Integer>)newValue).getKey().equals(\"totalCopyProgress\")) && (((SimpleImmutableEntry<String, Integer>)newValue).getValue() == 100)) {\n+                    completionDetected.set(true);\n+                    propertyChangeSupporter.removePropertyChangeListener(jobName.toString(), this);\n+","path":"src/test/java/FileSieve/BusinessLogic/FileManagement/ConcurrentFileManagerTest.java","position":334,"original_position":334,"commit_id":"4fd9e88dd7ba7f0794f848c78f5dfb068514f2b0","original_commit_id":"4fd9e88dd7ba7f0794f848c78f5dfb068514f2b0","user":{"login":"JeffBlodgett","id":7633920,"avatar_url":"https://avatars.githubusercontent.com/u/7633920?","gravatar_id":"bb4fddc5c51715b9601090db1072182f","url":"https://api.github.com/users/JeffBlodgett","html_url":"https://github.com/JeffBlodgett","followers_url":"https://api.github.com/users/JeffBlodgett/followers","following_url":"https://api.github.com/users/JeffBlodgett/following{/other_user}","gists_url":"https://api.github.com/users/JeffBlodgett/gists{/gist_id}","starred_url":"https://api.github.com/users/JeffBlodgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JeffBlodgett/subscriptions","organizations_url":"https://api.github.com/users/JeffBlodgett/orgs","repos_url":"https://api.github.com/users/JeffBlodgett/repos","events_url":"https://api.github.com/users/JeffBlodgett/events{/privacy}","received_events_url":"https://api.github.com/users/JeffBlodgett/received_events","type":"User","site_admin":false},"body":"This goes hand-in-hand with the “waitForJobCompletion()” method of that CopyJobCompletionListener class.  The “testCopyPathname” method uses it to wait until the current copy job is complete before continuing with certain assertions.  This “CopyJobCompletionListener” class is going to be re-worked and made a part of a type to be returned by the copyPathname method.  Will be working on that soon, after these initial revisions.\r\n\r\n \r\n\r\n- Jeff\r\n\r\n \r\n\r\nFrom: Spencer Marks [mailto:notifications@github.com] \r\nSent: Monday, June 23, 2014 7:44 AM\r\nTo: JeffBlodgett/FileSieve\r\nCc: Jeff Blodgett\r\nSubject: Re: [FileSieve] FileManagement package (#5)\r\n\r\n \r\n\r\nIn src/test/java/FileSieve/BusinessLogic/FileManagement/ConcurrentFileManagerTest.java:\r\n\r\n> +                completionDetected.set(false);\r\n> +                jobName = sourcePathBeingCopied;\r\n> +                propertyChangeSupporter.addPropertyChangeListener(sourcePathBeingCopied.toString(), this);\r\n> +        }\r\n> +\r\n> +        public void propertyChange(PropertyChangeEvent e) {\r\n> +            String propertyName = e.getPropertyName();\r\n> +\r\n> +            if (propertyName.equals(jobName.toString())) {\r\n> +                Object newValue = e.getNewValue();\r\n> +                Object oldValue = e.getOldValue();\r\n> +\r\n> +                if ((newValue != null) && (newValue instanceof SimpleImmutableEntry) && (((SimpleImmutableEntry<String, Integer>)newValue).getKey().equals(\"totalCopyProgress\")) && (((SimpleImmutableEntry<String, Integer>)newValue).getValue() == 100)) {\r\n> +                    completionDetected.set(true);\r\n> +                    propertyChangeSupporter.removePropertyChangeListener(jobName.toString(), this);\r\n> +\r\n\r\nPlease explain why you think you need synchronized here\r\n\r\n—\r\nReply to this email directly or view it on GitHub <https://github.com/JeffBlodgett/FileSieve/pull/5/files#r14070981> .  <https://github.com/notifications/beacon/7633920__eyJzY29wZSI6Ik5ld3NpZXM6QmVhY29uIiwiZXhwaXJlcyI6MTcxOTE0MzA2MywiZGF0YSI6eyJpZCI6MzQ2NTg1MTl9fQ==--75b83b249cd98e4cee8102fad3e8830f40c1da3d.gif> ","created_at":"2014-06-23T13:56:11Z","updated_at":"2014-06-23T13:56:11Z","html_url":"https://github.com/JeffBlodgett/FileSieve/pull/5#discussion_r14075983","pull_request_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/5","_links":{"self":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/comments/14075983"},"html":{"href":"https://github.com/JeffBlodgett/FileSieve/pull/5#discussion_r14075983"},"pull_request":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/5"}}},"pull_request":{"url":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/5","id":17139226,"html_url":"https://github.com/JeffBlodgett/FileSieve/pull/5","diff_url":"https://github.com/JeffBlodgett/FileSieve/pull/5.diff","patch_url":"https://github.com/JeffBlodgett/FileSieve/pull/5.patch","issue_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues/5","number":5,"state":"closed","title":"FileManagement package","user":{"login":"JeffBlodgett","id":7633920,"avatar_url":"https://avatars.githubusercontent.com/u/7633920?","gravatar_id":"bb4fddc5c51715b9601090db1072182f","url":"https://api.github.com/users/JeffBlodgett","html_url":"https://github.com/JeffBlodgett","followers_url":"https://api.github.com/users/JeffBlodgett/followers","following_url":"https://api.github.com/users/JeffBlodgett/following{/other_user}","gists_url":"https://api.github.com/users/JeffBlodgett/gists{/gist_id}","starred_url":"https://api.github.com/users/JeffBlodgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JeffBlodgett/subscriptions","organizations_url":"https://api.github.com/users/JeffBlodgett/orgs","repos_url":"https://api.github.com/users/JeffBlodgett/repos","events_url":"https://api.github.com/users/JeffBlodgett/events{/privacy}","received_events_url":"https://api.github.com/users/JeffBlodgett/received_events","type":"User","site_admin":false},"body":"Lots of work this past week on the FileManagement package.  Not yet finished, but getting close.  Note that JavaDoc comments are in need of tweaking and should not be trusted.","created_at":"2014-06-14T01:59:51Z","updated_at":"2014-06-23T13:56:11Z","closed_at":"2014-06-21T12:36:59Z","merged_at":"2014-06-21T12:36:59Z","merge_commit_sha":"a0b5975f1883eea2c3e1cb8ff4a0886c01905df3","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/5/commits","review_comments_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/5/comments","review_comment_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/comments/{number}","comments_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues/5/comments","statuses_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/statuses/4fd9e88dd7ba7f0794f848c78f5dfb068514f2b0","head":{"label":"JeffBlodgett:FileManagement","ref":"FileManagement","sha":"4fd9e88dd7ba7f0794f848c78f5dfb068514f2b0","user":{"login":"JeffBlodgett","id":7633920,"avatar_url":"https://avatars.githubusercontent.com/u/7633920?","gravatar_id":"bb4fddc5c51715b9601090db1072182f","url":"https://api.github.com/users/JeffBlodgett","html_url":"https://github.com/JeffBlodgett","followers_url":"https://api.github.com/users/JeffBlodgett/followers","following_url":"https://api.github.com/users/JeffBlodgett/following{/other_user}","gists_url":"https://api.github.com/users/JeffBlodgett/gists{/gist_id}","starred_url":"https://api.github.com/users/JeffBlodgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JeffBlodgett/subscriptions","organizations_url":"https://api.github.com/users/JeffBlodgett/orgs","repos_url":"https://api.github.com/users/JeffBlodgett/repos","events_url":"https://api.github.com/users/JeffBlodgett/events{/privacy}","received_events_url":"https://api.github.com/users/JeffBlodgett/received_events","type":"User","site_admin":false},"repo":{"id":20483689,"name":"FileSieve","full_name":"JeffBlodgett/FileSieve","owner":{"login":"JeffBlodgett","id":7633920,"avatar_url":"https://avatars.githubusercontent.com/u/7633920?","gravatar_id":"bb4fddc5c51715b9601090db1072182f","url":"https://api.github.com/users/JeffBlodgett","html_url":"https://github.com/JeffBlodgett","followers_url":"https://api.github.com/users/JeffBlodgett/followers","following_url":"https://api.github.com/users/JeffBlodgett/following{/other_user}","gists_url":"https://api.github.com/users/JeffBlodgett/gists{/gist_id}","starred_url":"https://api.github.com/users/JeffBlodgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JeffBlodgett/subscriptions","organizations_url":"https://api.github.com/users/JeffBlodgett/orgs","repos_url":"https://api.github.com/users/JeffBlodgett/repos","events_url":"https://api.github.com/users/JeffBlodgett/events{/privacy}","received_events_url":"https://api.github.com/users/JeffBlodgett/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/JeffBlodgett/FileSieve","description":"Identifies duplicate files, provides for deltion of selected duplicates, and provides file copy operations","fork":false,"url":"https://api.github.com/repos/JeffBlodgett/FileSieve","forks_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/forks","keys_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/keys{/key_id}","collaborators_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/teams","hooks_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/hooks","issue_events_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues/events{/number}","events_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/events","assignees_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/assignees{/user}","branches_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/branches{/branch}","tags_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/tags","blobs_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/refs{/sha}","trees_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/trees{/sha}","statuses_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/statuses/{sha}","languages_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/languages","stargazers_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/stargazers","contributors_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/contributors","subscribers_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/subscribers","subscription_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/subscription","commits_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/commits{/sha}","git_commits_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/commits{/sha}","comments_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/comments{/number}","issue_comment_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues/comments/{number}","contents_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/contents/{+path}","compare_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/compare/{base}...{head}","merges_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/merges","archive_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/downloads","issues_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues{/number}","pulls_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls{/number}","milestones_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/milestones{/number}","notifications_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/labels{/name}","releases_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/releases{/id}","created_at":"2014-06-04T13:01:30Z","updated_at":"2014-06-22T19:25:28Z","pushed_at":"2014-06-22T19:25:28Z","git_url":"git://github.com/JeffBlodgett/FileSieve.git","ssh_url":"git@github.com:JeffBlodgett/FileSieve.git","clone_url":"https://github.com/JeffBlodgett/FileSieve.git","svn_url":"https://github.com/JeffBlodgett/FileSieve","homepage":null,"size":2392,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"JeffBlodgett:master","ref":"master","sha":"71c3ca657162de01b08e119723d94f4b6845f2ee","user":{"login":"JeffBlodgett","id":7633920,"avatar_url":"https://avatars.githubusercontent.com/u/7633920?","gravatar_id":"bb4fddc5c51715b9601090db1072182f","url":"https://api.github.com/users/JeffBlodgett","html_url":"https://github.com/JeffBlodgett","followers_url":"https://api.github.com/users/JeffBlodgett/followers","following_url":"https://api.github.com/users/JeffBlodgett/following{/other_user}","gists_url":"https://api.github.com/users/JeffBlodgett/gists{/gist_id}","starred_url":"https://api.github.com/users/JeffBlodgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JeffBlodgett/subscriptions","organizations_url":"https://api.github.com/users/JeffBlodgett/orgs","repos_url":"https://api.github.com/users/JeffBlodgett/repos","events_url":"https://api.github.com/users/JeffBlodgett/events{/privacy}","received_events_url":"https://api.github.com/users/JeffBlodgett/received_events","type":"User","site_admin":false},"repo":{"id":20483689,"name":"FileSieve","full_name":"JeffBlodgett/FileSieve","owner":{"login":"JeffBlodgett","id":7633920,"avatar_url":"https://avatars.githubusercontent.com/u/7633920?","gravatar_id":"bb4fddc5c51715b9601090db1072182f","url":"https://api.github.com/users/JeffBlodgett","html_url":"https://github.com/JeffBlodgett","followers_url":"https://api.github.com/users/JeffBlodgett/followers","following_url":"https://api.github.com/users/JeffBlodgett/following{/other_user}","gists_url":"https://api.github.com/users/JeffBlodgett/gists{/gist_id}","starred_url":"https://api.github.com/users/JeffBlodgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JeffBlodgett/subscriptions","organizations_url":"https://api.github.com/users/JeffBlodgett/orgs","repos_url":"https://api.github.com/users/JeffBlodgett/repos","events_url":"https://api.github.com/users/JeffBlodgett/events{/privacy}","received_events_url":"https://api.github.com/users/JeffBlodgett/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/JeffBlodgett/FileSieve","description":"Identifies duplicate files, provides for deltion of selected duplicates, and provides file copy operations","fork":false,"url":"https://api.github.com/repos/JeffBlodgett/FileSieve","forks_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/forks","keys_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/keys{/key_id}","collaborators_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/teams","hooks_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/hooks","issue_events_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues/events{/number}","events_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/events","assignees_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/assignees{/user}","branches_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/branches{/branch}","tags_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/tags","blobs_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/refs{/sha}","trees_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/trees{/sha}","statuses_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/statuses/{sha}","languages_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/languages","stargazers_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/stargazers","contributors_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/contributors","subscribers_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/subscribers","subscription_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/subscription","commits_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/commits{/sha}","git_commits_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/git/commits{/sha}","comments_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/comments{/number}","issue_comment_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues/comments/{number}","contents_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/contents/{+path}","compare_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/compare/{base}...{head}","merges_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/merges","archive_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/downloads","issues_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues{/number}","pulls_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls{/number}","milestones_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/milestones{/number}","notifications_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/labels{/name}","releases_url":"https://api.github.com/repos/JeffBlodgett/FileSieve/releases{/id}","created_at":"2014-06-04T13:01:30Z","updated_at":"2014-06-22T19:25:28Z","pushed_at":"2014-06-22T19:25:28Z","git_url":"git://github.com/JeffBlodgett/FileSieve.git","ssh_url":"git@github.com:JeffBlodgett/FileSieve.git","clone_url":"https://github.com/JeffBlodgett/FileSieve.git","svn_url":"https://github.com/JeffBlodgett/FileSieve","homepage":null,"size":2392,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/5"},"html":{"href":"https://github.com/JeffBlodgett/FileSieve/pull/5"},"issue":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues/5"},"comments":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/issues/5/comments"},"review_comments":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/5/comments"},"review_comment":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/pulls/5/commits"},"statuses":{"href":"https://api.github.com/repos/JeffBlodgett/FileSieve/statuses/4fd9e88dd7ba7f0794f848c78f5dfb068514f2b0"}}}}