Improve tests & validate FSEntry names #4
@@ -10,16 +10,22 @@ class FSCreator {
 | 
			
		||||
    /**
 | 
			
		||||
     * Create entry, leaving existing folders' contents, but overwriting existing files.
 | 
			
		||||
     * @throws CyclicFolderException Cyclic folders cannot be created.
 | 
			
		||||
     * @throws DuplicateEntryNameException A folder or sub-folder contains entries with duplicate names.
 | 
			
		||||
     */
 | 
			
		||||
    @Throws(CyclicFolderException::class)
 | 
			
		||||
    @Throws(CyclicFolderException::class, DuplicateEntryNameException::class)
 | 
			
		||||
    fun create(
 | 
			
		||||
        entryToCreate: FSEntry,
 | 
			
		||||
        destination: String,
 | 
			
		||||
    ) {
 | 
			
		||||
        // No point in running anything if we know the input is invalid.
 | 
			
		||||
        if (entryToCreate is FSFolder && entryToCreate.isCyclic()) {
 | 
			
		||||
        if (entryToCreate is FSFolder) {
 | 
			
		||||
            if (entryToCreate.isCyclic()) {
 | 
			
		||||
                throw CyclicFolderException()
 | 
			
		||||
            }
 | 
			
		||||
            if (entryToCreate.deepHasDuplicateNames()) {
 | 
			
		||||
                throw DuplicateEntryNameException()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val queue =
 | 
			
		||||
            ArrayDeque(
 | 
			
		||||
@@ -47,3 +53,4 @@ class FSCreator {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CyclicFolderException : Exception("Cyclic FSFolders are not supported")
 | 
			
		||||
class DuplicateEntryNameException : Exception("Folder contains entries with duplicate names")
 | 
			
		||||
@@ -34,6 +34,26 @@ class FSFolder(name: String, val entries: List<FSEntry>) : FSEntry(name) {
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a folder contains multiple entries with the same name.
 | 
			
		||||
     */
 | 
			
		||||
    fun hasDuplicateNames(): Boolean {
 | 
			
		||||
        val seen = HashSet<String>()
 | 
			
		||||
        return entries.any { !seen.add(it.name) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun deepHasDuplicateNames(): Boolean {
 | 
			
		||||
        val queue = ArrayDeque(listOf(this))
 | 
			
		||||
        while (queue.isNotEmpty()) {
 | 
			
		||||
            val entry = queue.removeFirst()
 | 
			
		||||
            if (entry.hasDuplicateNames()) {
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            queue.addAll(entry.entries.mapNotNull { it as? FSFolder })
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class InvalidEntryNameException(name: String) : Exception("Invalid FSEntry name: '$name'")
 | 
			
		||||
		Reference in New Issue
	
	Block a user