diff --git a/editor/build.gradle b/editor/build.gradle index 2fd7c7a6..30b141a6 100644 --- a/editor/build.gradle +++ b/editor/build.gradle @@ -59,6 +59,18 @@ task provideGameEngine(type: Copy) { task provideApi(type: Copy) { from project(':api').file('src/main/java') into file('build/resources/main/api') + + doLast { + def apiDir = file('build/resources/main/api') + def apiIndex = file('build/resources/main/api.idx') + def buffer = new StringBuilder() + + fileTree(apiDir).matching { include "**/*.java" }.each { + buffer.append(apiDir.relativePath(it)).append("\n") + } + + apiIndex.write(buffer.toString()) + } } processResources { diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/api/APIProvider.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/api/APIProvider.kt new file mode 100644 index 00000000..aef91d74 --- /dev/null +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/api/APIProvider.kt @@ -0,0 +1,21 @@ +package com.bartlomiejpluta.base.editor.code.api + +import com.bartlomiejpluta.base.editor.file.model.FileNode +import com.bartlomiejpluta.base.editor.file.model.ResourceFileNode +import org.springframework.beans.factory.annotation.Value +import org.springframework.core.io.Resource +import org.springframework.stereotype.Component +import java.io.BufferedReader + +@Component +class APIProvider { + + @Value("classpath:api.idx") + private lateinit var apiIndex: Resource + + val apiNode: FileNode by lazy { loadNode() } + + private fun loadNode() = ResourceFileNode.root("api").apply { + apiIndex.inputStream.bufferedReader().use(BufferedReader::readLines).forEach(this::createNode) + } +} \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/compiler/JaninoCompiler.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/compiler/JaninoCompiler.kt index d2caf39d..8d7a7977 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/compiler/JaninoCompiler.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/compiler/JaninoCompiler.kt @@ -1,15 +1,16 @@ package com.bartlomiejpluta.base.editor.code.build.compiler +import com.bartlomiejpluta.base.editor.code.api.APIProvider import com.bartlomiejpluta.base.editor.code.build.exception.BuildException -import com.bartlomiejpluta.base.editor.code.build.model.ClasspathResource +import com.bartlomiejpluta.base.editor.code.build.model.FileNodeResourceAdapter import com.bartlomiejpluta.base.editor.common.logs.enumeration.Severity import com.bartlomiejpluta.base.editor.event.AppendBuildLogsEvent import com.bartlomiejpluta.base.editor.file.model.FileSystemNode +import com.bartlomiejpluta.base.editor.file.model.FileType import org.codehaus.commons.compiler.CompileException -import org.codehaus.commons.compiler.util.resource.FileResource import org.codehaus.commons.compiler.util.resource.Resource import org.codehaus.janino.CompilerFactory -import org.springframework.beans.factory.annotation.Value +import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component import tornadofx.FX.Companion.eventbus import java.io.File @@ -18,8 +19,8 @@ import java.io.File class JaninoCompiler : Compiler { private val compilerFactory = CompilerFactory() - @Value("classpath:api/**/*.java") - private lateinit var apiFiles: Array + @Autowired + private lateinit var apiProvider: APIProvider override fun compile(sourceDirectory: FileSystemNode, targetDirectory: File) = try { tryToCompile(sourceDirectory, targetDirectory) @@ -56,17 +57,11 @@ class JaninoCompiler : Compiler { } private fun prepareCompilationUnits(sourceDirectory: FileSystemNode): Array { - val sources = sourceDirectory - .allChildren - .map(FileSystemNode::file) - .filter(File::isFile) - .map(::FileResource) - .toTypedArray() - - val api = apiFiles - .map(::ClasspathResource) + val sources = sourceDirectory.allChildren + val api = apiProvider.apiNode.allChildren + return (sources + api) + .filter { it.type == FileType.FILE } + .map(::FileNodeResourceAdapter) .toTypedArray() - - return sources + api } } diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/model/FileNodeResourceAdapter.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/model/FileNodeResourceAdapter.kt new file mode 100644 index 00000000..6e032c22 --- /dev/null +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/model/FileNodeResourceAdapter.kt @@ -0,0 +1,10 @@ +package com.bartlomiejpluta.base.editor.code.build.model + +import com.bartlomiejpluta.base.editor.file.model.FileNode +import org.codehaus.commons.compiler.util.resource.Resource + +class FileNodeResourceAdapter(private val fileNode: FileNode) : Resource { + override fun open() = fileNode.inputStream() + override fun getFileName() = fileNode.absolutePath + override fun lastModified() = fileNode.lastModified +} \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/FileNode.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/FileNode.kt index 836e7017..5acb55fc 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/FileNode.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/FileNode.kt @@ -1,5 +1,6 @@ package com.bartlomiejpluta.base.editor.file.model +import javafx.beans.value.ObservableLongValue import javafx.beans.value.ObservableValue import java.io.InputStream import java.io.OutputStream @@ -19,7 +20,11 @@ interface FileNode { val parent: FileNode? val children: Iterable - val allChildren: Iterable + val allChildren: List + get() = children + children.flatMap { it.allChildren } + + val lastModifiedProperty: ObservableLongValue + val lastModified: Long fun delete() fun rename(name: String) @@ -29,7 +34,7 @@ interface FileNode { fun inputStream(): InputStream fun outputStream(): OutputStream - fun writeBytes(array: ByteArray) - fun writeText(text: String, charset: Charset = Charsets.UTF_8) = writeBytes(text.toByteArray(charset)) fun readText(charset: Charset = Charsets.UTF_8) = inputStream().reader(charset).readText() + fun writeText(text: String, charset: Charset = Charsets.UTF_8) = writeBytes(text.toByteArray(charset)) + fun writeBytes(array: ByteArray) = outputStream().use { it.write(array) } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/FileSystemNode.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/FileSystemNode.kt index 71e48591..52bc62fd 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/FileSystemNode.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/FileSystemNode.kt @@ -1,12 +1,12 @@ package com.bartlomiejpluta.base.editor.file.model +import javafx.beans.binding.Bindings.createLongBinding import javafx.beans.binding.Bindings.createStringBinding import tornadofx.getValue import tornadofx.observableListOf import tornadofx.setValue import tornadofx.toProperty import java.io.File -import java.io.FileOutputStream import java.nio.file.Path class FileSystemNode(file: File, override val parent: FileSystemNode? = null) : FileNode { @@ -30,8 +30,9 @@ class FileSystemNode(file: File, override val parent: FileSystemNode? = null) : } override val children = observableListOf() - override val allChildren: List - get() = children + children.flatMap { it.allChildren } + + override val lastModifiedProperty = createLongBinding({ fileProperty.value.lastModified() }, fileProperty) + override val lastModified by lastModifiedProperty init { refreshChildren() @@ -78,7 +79,7 @@ class FileSystemNode(file: File, override val parent: FileSystemNode? = null) : else -> file.createNewFile() } - when (val child = findChild(file)) { + when (val child = parent.children.firstOrNull { it.name == file.name }) { null -> FileSystemNode(file, parent).also { parent.children += it } else -> child } @@ -89,11 +90,5 @@ class FileSystemNode(file: File, override val parent: FileSystemNode? = null) : override fun outputStream() = file.outputStream() - override fun writeBytes(array: ByteArray): Unit = FileOutputStream(file).use { it.write(array) } - - private fun findChild(file: File): FileSystemNode? { - return children.firstOrNull { it.file.name == file.name } - } - - fun findByFile(file: File) = allChildren.firstOrNull { it.file.equals(file) } + fun findByFile(file: File) = allChildren.firstOrNull { it.absolutePath == file.absolutePath } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/ResourceFileNode.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/ResourceFileNode.kt new file mode 100644 index 00000000..e73e176b --- /dev/null +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/file/model/ResourceFileNode.kt @@ -0,0 +1,61 @@ +package com.bartlomiejpluta.base.editor.file.model + +import javafx.beans.property.StringProperty +import org.springframework.core.io.ClassPathResource +import org.springframework.core.io.Resource +import tornadofx.getValue +import tornadofx.observableListOf +import tornadofx.toProperty +import java.nio.file.Path + +class ResourceFileNode private constructor( + private val resource: Resource? = null, + override val parent: ResourceFileNode? = null, + override val type: FileType = FileType.FILE, + name: String? = null +) : FileNode { + + override val nameProperty = (resource?.filename ?: name ?: "").toProperty() + override val name by nameProperty + + override val extensionProperty = (resource?.filename?.substringAfter(".") ?: "").toProperty() + override val extension by extensionProperty + + override val absolutePathProperty: StringProperty = + ((parent?.absolutePath ?: "") + "/${nameProperty.value}").toProperty() + override val absolutePath by absolutePathProperty + + override val children = observableListOf() + + override val lastModifiedProperty = (resource?.lastModified() ?: 0L).toProperty() + override val lastModified by lastModifiedProperty + + override fun createNode(path: String): ResourceFileNode { + val segments = Path.of(path.replace("..", ".")) + + return segments.foldIndexed(this) { index, parent, segment -> + when (val child = parent.children.firstOrNull { it.name == segment.toString() }) { + null -> when (index < segments.count() - 1) { + true -> directory(segment.toString(), parent) + false -> resource(ClassPathResource("$absolutePath/$path"), parent) + }.also { parent.children += it } + else -> child + } + } + } + + override fun inputStream() = resource + ?.inputStream + ?: throw IllegalStateException("Attempt to open input stream of resource directory") + + override fun delete() = throw UnsupportedOperationException() + override fun rename(name: String) = throw UnsupportedOperationException() + override fun refresh() = throw UnsupportedOperationException() + override fun outputStream() = throw UnsupportedOperationException() + + companion object { + fun resource(resource: Resource, parent: ResourceFileNode) = ResourceFileNode(resource, parent) + fun directory(name: String, parent: ResourceFileNode) = ResourceFileNode(null, parent, FileType.DIRECTORY, name) + fun root(name: String) = ResourceFileNode(null, null, FileType.DIRECTORY, name) + } +} \ No newline at end of file