diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..9165b321b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules/code/library-management/FBME.code.library-management.main.iml b/.idea/modules/code/library-management/FBME.code.library-management.main.iml
new file mode 100644
index 000000000..c98fad494
--- /dev/null
+++ b/.idea/modules/code/library-management/FBME.code.library-management.main.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.mps/modules.xml b/.mps/modules.xml
index 995e49edc..45cfb52c8 100644
--- a/.mps/modules.xml
+++ b/.mps/modules.xml
@@ -31,6 +31,7 @@
+
\ No newline at end of file
diff --git a/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/segments/Ethernet.seg b/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/segments/Ethernet.seg
deleted file mode 100644
index 9e6a3ba5c..000000000
--- a/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/segments/Ethernet.seg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.common.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.common.mps
index 557615311..9baf591cd 100644
--- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.common.mps
+++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.common.mps
@@ -2195,5 +2195,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/library-management/.module-id b/code/library-management/.module-id
new file mode 100644
index 000000000..44726276a
--- /dev/null
+++ b/code/library-management/.module-id
@@ -0,0 +1 @@
+734aecb8-1ee0-415b-bf20-5e9c872d7019
diff --git a/code/library-management/build.gradle.kts b/code/library-management/build.gradle.kts
new file mode 100644
index 000000000..628e35080
--- /dev/null
+++ b/code/library-management/build.gradle.kts
@@ -0,0 +1,72 @@
+import org.fbme.gradle.includeMpsArtifacts
+import org.fbme.gradle.moduleDependency
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+// id("java")
+ mps
+ kotlin
+}
+
+//group = "org.example"
+//version = "unspecified"
+//
+//repositories {
+// mavenCentral()
+//}
+
+dependencies {
+ compileOnly(mpsDistribution())
+ compileOnly(project(":code:library"))
+ compileOnly(project(":code:platform"))
+
+ implementation(project(":code:language"))
+
+ testImplementation(mpsDistribution())
+ testImplementation(project(":code:library"))
+
+ mpsImplementation(project(":code:library", "mps"))
+ mpsImplementation(project(":code:language", "mps"))
+
+// implementation(project(mapOf("path" to ":code:platform")))
+// testImplementation(platform("org.junit:junit-bom:5.9.1"))
+// testImplementation("org.junit.jupiter:junit-jupiter")
+
+// compileOnly(mpsDistribution())
+ compileOnly(project(":code:library"))
+
+ implementation(project(":code:language"))
+
+ testImplementation(mpsDistribution())
+ testImplementation(project(":code:library"))
+
+// mpsImplementation(project(":code:library", "mps"))
+// mpsImplementation(project(":code:language", "mps"))
+}
+
+
+mps {
+ moduleName.set("org.fbme.platform.lib-management")
+ includeMpsArtifacts(project(":code:language"))
+ moduleDependency(project(":code:library"))
+}
+
+val compileKotlin by tasks.getting(KotlinCompile::class) {
+ kotlinOptions.freeCompilerArgs = listOf("-Xjvm-default=all")
+}
+
+val test by tasks.getting(Test::class) {
+ dependsOn(
+ ":code:library:buildDistPlugin",
+ "buildDistPlugin"
+ )
+}
+
+val copyLibs by tasks.getting(Copy::class) {
+ dependsOn(":code:language:jar")
+}
+
+//tasks.test {
+// useJUnitPlatform()
+//}
+
diff --git a/code/library-management/src/main/java/org/example/Main.java b/code/library-management/src/main/java/org/example/Main.java
new file mode 100644
index 000000000..407f157bc
--- /dev/null
+++ b/code/library-management/src/main/java/org/example/Main.java
@@ -0,0 +1,7 @@
+package org.example;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+}
\ No newline at end of file
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetFactory.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetFactory.kt
new file mode 100644
index 000000000..1b31c73be
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetFactory.kt
@@ -0,0 +1,21 @@
+package org.fbme.ide.platform.library
+
+import org.fbme.ide.platform.library.util.LibraryFacet
+import org.jetbrains.mps.openapi.module.FacetsFacade.FacetFactory
+import org.jetbrains.mps.openapi.module.SModule
+import org.jetbrains.mps.openapi.module.SModuleFacet
+
+class LibraryFacetFactory : FacetFactory {
+
+ override fun create(sModule: SModule): SModuleFacet {
+ return LibraryFacet(sModule)
+ }
+
+ override fun getPresentation(): String {
+ return "Library"
+ }
+
+ companion object {
+ var CUSTOM_FACET_FACTORY = LibraryFacetFactory()
+ }
+}
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetTab.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetTab.kt
new file mode 100644
index 000000000..62f07de3d
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetTab.kt
@@ -0,0 +1,103 @@
+package org.fbme.ide.platform.library
+
+import com.intellij.openapi.ui.VerticalFlowLayout
+import com.intellij.ui.components.JBLabel
+import com.intellij.uiDesigner.core.GridConstraints
+import com.intellij.uiDesigner.core.GridLayoutManager
+import com.intellij.util.ui.JBInsets
+import com.intellij.util.ui.JBUI
+import jetbrains.mps.ide.ui.dialogs.properties.tabs.BaseTab
+import org.fbme.ide.platform.library.util.LibraryFacet
+import org.jetbrains.mps.openapi.module.SModuleFacet
+import org.jetbrains.mps.openapi.ui.persistence.FacetTab
+import java.awt.Dimension
+import javax.swing.BorderFactory
+import javax.swing.JLabel
+import javax.swing.JPanel
+import javax.swing.JTextField
+
+class LibraryFacetTab(val moduleFacet: LibraryFacet): BaseTab(), FacetTab {
+ private var myTextField: JTextField? = null
+
+ private fun displayAllNamespaces() {
+ val namespacesPanel = JPanel(VerticalFlowLayout())
+ moduleFacet.getAllNamespaces().forEach { (node, namespace) ->
+ val label = JLabel("Node ID: ${node.nodeId}, Name: ${node.name}, Namespace: $namespace")
+ label.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) // Add space around the label
+ namespacesPanel.add(label)
+ }
+ tabComponent.add(namespacesPanel)
+ }
+
+ override fun init() {
+ val content = JPanel()
+ content.setLayout(GridLayoutManager(1, 2, JBUI.emptyInsets(), -1, -1))
+
+ val label = JBLabel("Plugin ID:")
+ content.add(
+ label, GridConstraints(
+ 0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false
+ )
+ )
+
+ myTextField = JTextField("ID")
+ myTextField!!.isEditable = false
+
+ content.add(
+ myTextField, GridConstraints(
+ 0,
+ 1,
+ 1,
+ 1,
+ GridConstraints.ANCHOR_WEST,
+ GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED,
+ null,
+ null,
+ null,
+ 0,
+ false
+ )
+ )
+
+ val outerPanel = JPanel()
+ outerPanel.setLayout(GridLayoutManager(1, 1, JBInsets(10, 10, 10, 10), -1, -1))
+ outerPanel.add(
+ content,
+ GridConstraints(
+ 0,
+ 0,
+ 1,
+ 1,
+ GridConstraints.ANCHOR_NORTHWEST,
+ GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_WANT_GROW,
+ GridConstraints.SIZEPOLICY_FIXED,
+ null,
+ Dimension(150, -1),
+ null,
+ 0,
+ false
+ )
+ )
+
+ displayAllNamespaces()
+
+ tabComponent = outerPanel
+ }
+
+ override fun isModified(): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun apply() {
+ TODO("Not yet implemented")
+ }
+
+ override fun getFacet(): SModuleFacet {
+ return moduleFacet
+ }
+
+}
\ No newline at end of file
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetTabFactory.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetTabFactory.kt
new file mode 100644
index 000000000..cce281315
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/LibraryFacetTabFactory.kt
@@ -0,0 +1,13 @@
+package org.fbme.ide.platform.library
+
+import org.fbme.ide.platform.library.util.LibraryFacet
+import org.fbme.ide.platform.library.LibraryFacetTab
+import org.jetbrains.mps.openapi.ui.persistence.FacetTab
+import org.jetbrains.mps.openapi.ui.persistence.TabFactory
+
+
+class LibraryFacetTabFactory : TabFactory {
+ override fun getTab(moduleFacet: LibraryFacet?): FacetTab {
+ return LibraryFacetTab(moduleFacet!!)
+ }
+}
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ExportLibraryAction.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ExportLibraryAction.kt
new file mode 100644
index 000000000..356395311
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ExportLibraryAction.kt
@@ -0,0 +1,95 @@
+package org.fbme.ide.platform.library.actions
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import jetbrains.mps.ide.actions.MPSCommonDataKeys
+import jetbrains.mps.project.Solution
+import jetbrains.mps.vfs.IFile
+import org.jetbrains.mps.openapi.module.SModule
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.io.IOException
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
+
+
+class ExportLibraryAction: AnAction() {
+
+ private fun resolveModulePath(module: SModule): String? {
+ require(module is Solution) { "The module is not a Solution." }
+ // Attempt to get the descriptor file
+ val descriptorFile: IFile? = module.descriptorFile
+ return descriptorFile?.getParent()?.path
+
+ }
+
+ override fun actionPerformed(e: AnActionEvent) {
+ val module = e.getData(MPSCommonDataKeys.CONTEXT_MODULE) ?: return
+
+ // TODO: (SEE the same note at the ImportLIbraryAction.kt
+ // consider rename solution or it's id in .mds or header file in order to avoid id conflicts
+ // while importing exported library
+
+ val mpsProject = e.getData(MPSCommonDataKeys.MPS_PROJECT)
+ val ideaProject = mpsProject!!.project
+ val targetDir = ideaProject.basePath
+ zipModule(module, targetDir + "/" + module.moduleName + ".zip")
+ }
+
+ @Throws(IOException::class)
+ fun zipModule(module: SModule?, zipFilePath: String?) {
+ val modulePath = resolveModulePath(module!!)
+ val fos = FileOutputStream(zipFilePath)
+ val zipOut = ZipOutputStream(fos)
+ val fileToZip = File(modulePath)
+ zipFile(fileToZip, fileToZip.getName(), zipOut)
+ zipOut.close()
+ fos.close()
+ }
+
+ @Throws(IOException::class)
+ private fun zipFile(fileToZip: File, fileName: String, zipOut: ZipOutputStream) {
+ if (!fileToZip.isDirectory()) {
+ zipFile(fileToZip, fileName, zipOut, "")
+ return
+ }
+ val children = fileToZip.listFiles()
+ if (children != null) {
+ for (childFile in children) {
+ zipFile(childFile, fileName + "/" + childFile.getName(), zipOut, fileName)
+ }
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun zipFile(fileToZip: File, fileName: String, zipOut: ZipOutputStream, parentDirectoryName: String) {
+ var fileName = fileName
+ if (fileToZip.isHidden()) {
+ return
+ }
+ if (fileToZip.isDirectory()) {
+ if (!fileName.endsWith("/")) {
+ fileName += "/"
+ }
+ if (parentDirectoryName != "") {
+ zipOut.putNextEntry(ZipEntry(fileName))
+ zipOut.closeEntry()
+ }
+ val children = fileToZip.listFiles()
+ for (childFile in children) {
+ zipFile(childFile, fileName + childFile.getName(), zipOut, fileName)
+ }
+ return
+ }
+ val fis = FileInputStream(fileToZip)
+ val zipEntry = ZipEntry(fileName)
+ zipOut.putNextEntry(zipEntry)
+ val bytes = ByteArray(1024)
+ var length: Int
+ while (fis.read(bytes).also { length = it } >= 0) {
+ zipOut.write(bytes, 0, length)
+ }
+ fis.close()
+ }
+}
\ No newline at end of file
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ImportLibraryAction.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ImportLibraryAction.kt
new file mode 100644
index 000000000..07dc77b88
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ImportLibraryAction.kt
@@ -0,0 +1,182 @@
+package org.fbme.ide.platform.library.actions
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
+import com.intellij.openapi.ui.DialogWrapper
+import com.intellij.openapi.ui.TextFieldWithBrowseButton
+import com.intellij.openapi.ui.VerticalFlowLayout
+import jetbrains.mps.ide.actions.MPSCommonDataKeys
+import jetbrains.mps.persistence.DefaultModelRoot
+import jetbrains.mps.persistence.MementoImpl
+import jetbrains.mps.project.ModuleId
+import jetbrains.mps.project.ProjectPathUtil
+import jetbrains.mps.project.Solution
+import jetbrains.mps.project.StandaloneMPSProject
+import jetbrains.mps.project.structure.modules.ModuleFacetDescriptor
+import jetbrains.mps.project.structure.modules.SolutionDescriptor
+import jetbrains.mps.smodel.GeneralModuleFactory
+import jetbrains.mps.smodel.ModuleDependencyVersions
+import jetbrains.mps.smodel.language.LanguageRegistry
+import jetbrains.mps.vfs.IFile
+import org.jdom.Document
+import org.jdom.Element
+import org.jdom.input.SAXBuilder
+import org.jdom.output.Format
+import org.jdom.output.XMLOutputter
+import java.io.BufferedOutputStream
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.util.zip.ZipInputStream
+import javax.swing.JComponent
+import javax.swing.JLabel
+import javax.swing.JPanel
+
+
+class ImportLibraryAction: AnAction() {
+
+ companion object {
+ fun unzip(zipFilePath: String, targetDirectoryPath: String) {
+ val buffer = ByteArray(1024)
+ val zipFile = File(zipFilePath)
+ val folder = File(targetDirectoryPath)
+ if (!folder.exists()) {
+ folder.mkdirs()
+ }
+
+ ZipInputStream(FileInputStream(zipFile)).use { zis ->
+ var zipEntry = zis.nextEntry
+ while (zipEntry != null) {
+ val newFile = File(folder, zipEntry.name)
+ if (zipEntry.isDirectory) {
+ newFile.mkdirs()
+ } else {
+ File(newFile.parent).mkdirs()
+
+ FileOutputStream(newFile).use { fos ->
+ var len: Int
+ BufferedOutputStream(fos, buffer.size).use { bos ->
+ while (zis.read(buffer).also { len = it } > 0) {
+ bos.write(buffer, 0, len)
+ }
+ }
+ }
+ }
+ zipEntry = zis.nextEntry
+ }
+ zis.closeEntry()
+ }
+ }
+
+ private fun overrideModuleId(id: String, filePath: String) {
+ try {
+ val saxBuilder = SAXBuilder()
+ val document: Document = saxBuilder.build(File(filePath))
+ val rootNode: Element = document.rootElement
+
+ rootNode.setAttribute("uuid", id)
+
+ val xmlOutputter = XMLOutputter(Format.getPrettyFormat())
+ FileOutputStream(filePath).use { output ->
+ xmlOutputter.output(document, output)
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
+ private fun getModuleFile(namespace: String, rootPath: IFile, extension: String): IFile {
+ return rootPath.findChild(namespace + extension)
+ }
+
+ private fun createNewSolutionDescriptor(id: ModuleId, namespace: String, descriptorFile: IFile): SolutionDescriptor {
+ val descriptor = SolutionDescriptor()
+ descriptor.namespace = namespace
+ descriptor.id = id
+ val moduleLocation = descriptorFile.parent
+ val modelsDir = moduleLocation!!.findChild("models")
+
+ modelsDir.mkdirs()
+ descriptor.modelRootDescriptors.add(
+ DefaultModelRoot.createDescriptor(
+ modelsDir.parent!!,
+ *arrayOf(modelsDir)
+ )
+ )
+ descriptor.moduleFacetDescriptors.add(ModuleFacetDescriptor("java", MementoImpl()))
+ ProjectPathUtil.setGeneratorOutputPath(descriptor, moduleLocation!!.findChild("source_gen").path)
+ return descriptor
+ }
+
+ private fun handleSelectedFilePath(filePath: String, e: AnActionEvent) {
+// :TODO: get module name from it's descriptor file
+// path/to/archive/weird_name.zip
+ val moduleName = filePath.split("/").last().dropLast(4)
+
+ val mpsProject = e.getData(MPSCommonDataKeys.MPS_PROJECT) as StandaloneMPSProject
+
+ val zipFilePath = filePath
+ val targetDirectoryPath = mpsProject.project.basePath + "/solutions"
+
+ unzip(zipFilePath, targetDirectoryPath)
+ val id = ModuleId.regular()
+ overrideModuleId(id.toString(), filePath)
+
+ val namespace = moduleName
+
+ mpsProject.modelAccess.runWriteAction {
+// TODO: are there any issues with specific model(how do IEC61499 model gets loaded?)
+
+ val descriptorFile = getModuleFile(namespace,
+ mpsProject.getFileSystem().getFile(targetDirectoryPath + "/" + moduleName),
+ ".msd")
+
+// NOTE: Before importing I've changed the "ref=" parameter, and haven't changed the uuid of the solution in
+// .msd file
+ val descriptor = createNewSolutionDescriptor(id, namespace, descriptorFile)
+ val module = GeneralModuleFactory().instantiate(descriptor, descriptorFile) as Solution
+ mpsProject.addModule(module)
+ ModuleDependencyVersions(
+ (mpsProject.getComponent(LanguageRegistry::class.java) as LanguageRegistry)!!,
+ mpsProject.getRepository()
+ ).update(module)
+ module.save()
+ }
+
+// return module
+
+ // TODO: add as a dependency
+
+ println("Created?")
+ }
+ }
+
+ override fun actionPerformed(e: AnActionEvent) {
+ // TODO: (SEE the same note at the ExportLIbraryAction.kt
+ // consider rename solution or it's id in .mds or header file in order to avoid id conflicts
+ val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor()
+ val textFieldWithBrowseButton = TextFieldWithBrowseButton()
+ textFieldWithBrowseButton.addBrowseFolderListener("Select Archive", null, null, fileChooserDescriptor)
+
+ val panel = JPanel(VerticalFlowLayout())
+ panel.add(JLabel("Select Archive:"))
+ panel.add(textFieldWithBrowseButton)
+
+ val dialogWrapper: DialogWrapper = object : DialogWrapper(true) {
+ init {
+ init()
+ title = "Import Library"
+ }
+
+ override fun createCenterPanel(): JComponent? {
+ return panel
+ }
+ }
+
+ if (dialogWrapper.showAndGet()) {
+ val filePath = textFieldWithBrowseButton.text
+ handleSelectedFilePath(filePath, e)
+ }
+ }
+}
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ImportNxtLibraryAction.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ImportNxtLibraryAction.kt
new file mode 100644
index 000000000..d2464e266
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/ImportNxtLibraryAction.kt
@@ -0,0 +1,238 @@
+package org.fbme.ide.platform.library.actions
+
+import com.intellij.notification.Notification
+import com.intellij.notification.NotificationType
+import com.intellij.notification.Notifications
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.DialogWrapper
+import com.intellij.openapi.ui.TextFieldWithBrowseButton
+import com.intellij.openapi.ui.VerticalFlowLayout
+import jetbrains.mps.extapi.model.SModelSimpleHeader
+import jetbrains.mps.ide.actions.MPSCommonDataKeys
+import jetbrains.mps.ide.newSolutionDialog.NewModuleUtil
+import jetbrains.mps.openapi.navigation.NavigationSupport
+import jetbrains.mps.persistence.DefaultModelRoot
+import jetbrains.mps.persistence.ModelCannotBeCreatedException
+import jetbrains.mps.project.*
+import jetbrains.mps.smodel.SModelId
+import jetbrains.mps.smodel.SNodeUtil
+import jetbrains.mps.util.JDOMUtil
+import org.fbme.ide.iec61499.repository.PlatformElement
+import org.fbme.ide.iec61499.repository.PlatformElementsOwner
+import org.fbme.ide.iec61499.repository.PlatformRepository
+import org.fbme.ide.iec61499.repository.PlatformRepositoryProvider
+import org.fbme.ide.platform.converter.PlatformConverter
+import org.fbme.ide.platform.persistence.Iec61499ModelFactory
+import org.fbme.ide.platform.persistence.Iec61499ModelHeader
+import org.jdom.Document
+import org.jetbrains.mps.openapi.model.SModel
+import org.jetbrains.mps.openapi.model.SModelName
+import org.jetbrains.mps.openapi.model.SModelReference
+import org.jetbrains.mps.openapi.model.SNode
+import org.jetbrains.mps.openapi.persistence.ModelLoadException
+import org.jetbrains.mps.openapi.persistence.PersistenceFacade
+import java.io.*
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+import java.util.stream.Collectors
+import javax.swing.JComponent
+import javax.swing.JLabel
+import javax.swing.JPanel
+
+class ImportNxtLibraryAction: AnAction() {
+
+ companion object {
+
+ val NAMESPACES_FILE_EXTENSION = ".iecproj"
+ val NAMESPACES_XML_TAG = "Namespaces"
+ val NAMESPACE_XML_TAG = "Ns"
+ val NAMESPACE_PARAMETER_XML_TAG = "Name"
+ val FB_XML_TAG = "FB"
+ val FB_PARAMETER_XML_TAG = "Name"
+
+ fun extractNamespaces(folderPath: String): HashMap {
+ val result = HashMap()
+
+ val iecprojFilePath = Files.walk(Paths.get(folderPath))
+ .filter { path: Path -> path.toString().endsWith(NAMESPACES_FILE_EXTENSION) }
+ .collect(Collectors.toList())
+ .firstOrNull() ?: throw RuntimeException("No .iecproj file found in the directory")
+
+ BufferedReader(FileReader(iecprojFilePath.toFile())).use { reader ->
+ val doc = JDOMUtil.loadDocument(reader)
+ val rootElement = doc.rootElement
+
+ val namespacesElement = rootElement.getChild(NAMESPACES_XML_TAG, rootElement.namespace)
+
+ for (nsElement in namespacesElement.getChildren(NAMESPACE_XML_TAG, rootElement.namespace)) {
+ val namespaceName = nsElement.getAttributeValue(NAMESPACE_PARAMETER_XML_TAG)
+ val fbNames = nsElement.getChildren(FB_XML_TAG, rootElement.namespace)
+ .map { it.getAttributeValue(FB_PARAMETER_XML_TAG) }
+ // for all fbNames in the namespace, add the namespaceName to the result
+ fbNames.forEach { result[it] = namespaceName }
+ }
+ }
+
+ return result
+ }
+
+
+ fun initModel(project: Project, repository: PlatformRepository, model: SModel, moduleName: String, folderPath: String): PlatformElement {
+ val nxtLibNamespaceFolder = "$folderPath/Files"
+
+ val namespaces = extractNamespaces(folderPath)
+
+ val modelId = SModelId.generate()
+ val modelName = "Library@content"
+
+ val ref = PersistenceFacade.getInstance().createModelReference(null, modelId, modelName)
+ val header: SModelSimpleHeader = Iec61499ModelHeader(ref, emptyList())
+
+ val entries = loadEntries(File(nxtLibNamespaceFolder))
+
+ val errorEntries = mutableSetOf()
+ for (entry in entries) {
+ try {
+ loadRootFromFile(header, entry, model, namespaces)
+ } catch (e: Exception) {
+// errorEntries += entry
+ }
+ }
+ if (errorEntries.isNotEmpty()) {
+ val notification = Notification(
+ "fbme.integration.nxt",
+ "Error during import",
+ "Failed to load ${errorEntries.size} documents: ${errorEntries.joinToString { it.name }}",
+ NotificationType.ERROR
+ )
+ Notifications.Bus.notify(notification, project)
+ }
+ val first = model.rootNodes.firstOrNull()
+ if (first != null) {
+ return repository.adapter(first)
+ }
+ val result = repository.iec61499Factory.createBasicFBTypeDeclaration(null)
+ result.name = "EmptyBasicFB"
+ return result as PlatformElement
+ }
+
+ @Throws(ModelLoadException::class)
+ fun loadRootFromFile(header: SModelSimpleHeader, documentFile: File, model: SModel, namespaces: HashMap) {
+ BufferedReader(FileReader(documentFile)).use { reader ->
+ val doc = JDOMUtil.loadDocument(reader)
+ val node = convertRootNode(header.modelReference, doc, documentFile.extension)
+ if (node != null) {
+ val virtualPackage = namespaces[documentFile.name]
+ if (virtualPackage != null && virtualPackage.isNotEmpty()) {
+ node.setProperty(SNodeUtil.property_BaseConcept_virtualPackage, virtualPackage)
+ }
+ model.addRootNode(node)
+ }
+ }
+ }
+
+ private fun convertRootNode(reference: SModelReference, doc: Document, fileExtension: String?): SNode? {
+ val owner = PlatformElementsOwner()
+ val configuration = PlatformConverter.STANDARD_CONFIG_FACTORY.createConfiguration(owner)
+ val converter = PlatformConverter.create(configuration, reference, doc)
+ return when (fileExtension) {
+ Iec61499ModelFactory.FBT_FILE_EXT -> (converter.convertFBType() as PlatformElement).node
+ Iec61499ModelFactory.ADP_FILE_EXT -> (converter.convertAdapterType() as PlatformElement).node
+ Iec61499ModelFactory.SUB_FILE_EXT -> (converter.convertSubapplicationType() as PlatformElement).node
+ Iec61499ModelFactory.RES_FILE_EXT -> (converter.convertResourceType() as PlatformElement).node
+ Iec61499ModelFactory.DEV_FILE_EXT -> (converter.convertDeviceType() as PlatformElement).node
+ Iec61499ModelFactory.SEG_FILE_EXT -> (converter.convertSegmentType() as PlatformElement).node
+ Iec61499ModelFactory.SYS_FILE_EXT -> (converter.convertSystemConfiguration() as PlatformElement).node
+ else -> null
+ }
+ }
+
+ private fun supportedFileExtension(fileExt: String): Boolean {
+ return fileExt == Iec61499ModelFactory.FBT_FILE_EXT
+ || fileExt == Iec61499ModelFactory.ADP_FILE_EXT
+ || fileExt == Iec61499ModelFactory.SUB_FILE_EXT
+ || fileExt == Iec61499ModelFactory.RES_FILE_EXT
+ || fileExt == Iec61499ModelFactory.DEV_FILE_EXT
+ || fileExt == Iec61499ModelFactory.SYS_FILE_EXT
+ || fileExt == Iec61499ModelFactory.SEG_FILE_EXT
+ }
+
+ private fun loadEntries(rootDirectory: File): Sequence = sequence {
+ val files = rootDirectory.listFiles() ?: return@sequence
+ for (file in files) {
+ if (file.isDirectory) {
+ for (nestedFile in file.listFiles()!!) { // unexpected format if exception occurs
+ if (supportedFileExtension(nestedFile.extension)) {
+ yield(nestedFile)
+ }
+ }
+ }
+ }
+ }
+
+ private fun handleSelectedFolderPath(folderPath: String, e: AnActionEvent) {
+// TODO: copy all files
+
+ // Suppose we've already copied all files, now
+ // we have to parse them
+
+ val mpsProject: MPSProject = e.getData(MPSCommonDataKeys.MPS_PROJECT)!!
+ mpsProject.modelAccess.runWriteAction {
+ // create a variable called moduleName and assign to it the name of directory specified by folderPath
+ val moduleName = File(folderPath).name
+
+ val moduleDir = mpsProject.project.basePath + "/solutions/" + moduleName
+ val solution = NewModuleUtil.createSolution(moduleName, moduleDir, mpsProject)
+ val root = solution.modelRoots.iterator().next() as DefaultModelRoot
+ val model = try {
+ root.createModel(SModelName("Library@content"), null,
+ Iec61499ModelFactory.DST,
+ Iec61499ModelFactory.TYPE
+ )
+ } catch (e: ModelCannotBeCreatedException) {
+ throw RuntimeException("Model can not be created", e)
+ }
+ val repository = PlatformRepositoryProvider.getInstance(mpsProject)
+ val initialElement = initModel(mpsProject.project, repository, model, moduleName, folderPath)
+ model.module.declaredDependencies
+ val initialNode = initialElement.node
+ mpsProject.repository.modelAccess.runReadInEDT {
+ val navigationSupport = NavigationSupport.getInstance()
+ navigationSupport.openNode(mpsProject, initialNode, true, false)
+ navigationSupport.selectInTree(mpsProject, initialNode, false)
+ }
+ }
+ }
+ }
+
+ override fun actionPerformed(e: AnActionEvent) {
+ val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor()
+ val textFieldWithBrowseButton = TextFieldWithBrowseButton()
+ textFieldWithBrowseButton.addBrowseFolderListener("Select NxtLib Archive", null, null, fileChooserDescriptor)
+
+ val panel = JPanel(VerticalFlowLayout())
+ panel.add(JLabel("Select NxtLib Folder"))
+ panel.add(textFieldWithBrowseButton)
+
+ val dialogWrapper: DialogWrapper = object : DialogWrapper(true) {
+ init {
+ init()
+ title = "Import NxtLib"
+ }
+
+ override fun createCenterPanel(): JComponent {
+ return panel
+ }
+ }
+
+ if (dialogWrapper.showAndGet()) {
+ val filePath = textFieldWithBrowseButton.text
+ handleSelectedFolderPath(filePath, e)
+ }
+ }
+
+}
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/NewLibraryAction.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/NewLibraryAction.kt
new file mode 100644
index 000000000..e8c8e1158
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/NewLibraryAction.kt
@@ -0,0 +1,106 @@
+package org.fbme.ide.platform.library.actions
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import jetbrains.mps.ide.actions.MPSCommonDataKeys
+import jetbrains.mps.ide.newSolutionDialog.NewModuleUtil
+import jetbrains.mps.ide.projectPane.ProjectPane
+import jetbrains.mps.ide.ui.dialogs.modules.NameLocationPanel
+import jetbrains.mps.ide.ui.dialogs.modules.NewModuleDialog
+import jetbrains.mps.openapi.navigation.NavigationSupport
+import jetbrains.mps.persistence.DefaultModelRoot
+import jetbrains.mps.persistence.ModelCannotBeCreatedException
+import jetbrains.mps.project.MPSExtentions
+import jetbrains.mps.project.ModelImporter
+import jetbrains.mps.project.Solution
+import org.fbme.ide.iec61499.repository.PlatformRepositoryProvider
+import org.fbme.ide.platform.library.LibraryFacetFactory
+import org.fbme.ide.platform.persistence.Iec61499ModelFactory
+import org.fbme.ide.platform.projectWizard.LibraryTemplate
+import org.jetbrains.mps.openapi.model.SModel
+import org.jetbrains.mps.openapi.model.SModelName
+
+class NewLibraryAction : AnAction() {
+
+ override fun actionPerformed(event: AnActionEvent) {
+ val mpsProject = event.getData(MPSCommonDataKeys.MPS_PROJECT)
+ val cfg =
+ NameLocationPanel(NewModuleDialog.projectHome(mpsProject!!), "Solution name:", "Solution file location:")
+ cfg.withDefaults("NewSolution", "solutions")
+ val dialog = NewModuleDialog(mpsProject, cfg)
+ dialog.withCheck {
+ NewModuleUtil.check(
+ mpsProject,
+ MPSExtentions.DOT_SOLUTION,
+ cfg.moduleName,
+ cfg.moduleLocation.absolutePath
+ )
+ }
+
+ var model: SModel? = null
+
+ dialog.withFactory {
+ val result = NewModuleUtil.createSolution(cfg.moduleName, cfg.moduleLocation.absolutePath, mpsProject)
+
+ val root = result.modelRoots.iterator().next() as DefaultModelRoot
+ model = try {
+ val fullModelName = "NewLibrary@content"
+ root.createModel(SModelName(fullModelName), null, Iec61499ModelFactory.DST, Iec61499ModelFactory.TYPE)
+ } catch (e: ModelCannotBeCreatedException) {
+ throw RuntimeException("Model can not be created", e)
+ }
+
+ val repository = PlatformRepositoryProvider.getInstance(mpsProject)
+ val initialElement = LibraryTemplate().initModel(mpsProject.project, repository, model!!)
+
+ mpsProject.repository.modelAccess.runReadInEDT {
+ val navigationSupport = NavigationSupport.getInstance()
+ navigationSupport.openNode(mpsProject, initialElement.node, true, false)
+ navigationSupport.selectInTree(mpsProject, initialElement.node, false)
+ }
+
+ val facetFactory = LibraryFacetFactory.CUSTOM_FACET_FACTORY
+
+// TODO: add factory at the FBME start
+// val facetsRegistry: FacetsRegistry = mpsProject.getComponent(FacetsRegistry::class.java)
+// if (facetsRegistry.getFacetFactory("library") == null) {
+// facetsRegistry.addFactory("library", facetFactory)
+// }
+
+// val libFacet = facetFactory.create(result)
+// result.moduleDescriptor.addFacetDescriptor(libFacet)
+//// result.moduleDescriptor.moduleFacetDescriptors.add(ModuleFacetDescriptor("library", MementoImpl()))
+ result.setModuleDescriptor(result.moduleDescriptor, true)
+ result.save()
+
+ result
+ }
+
+
+
+ dialog.title = "New Library"
+ dialog.show()
+ if (!dialog.isOK) {
+ return
+ }
+
+ val solution = dialog.result ?: return
+
+ val projectPane = ProjectPane.getInstance(event.getData(MPSCommonDataKeys.MPS_PROJECT))
+ projectPane.selectModule(solution, false)
+
+ mpsProject.modelAccess.runWriteAction {
+ mpsProject.projectModules.forEach { module ->
+// TODO: import created module&model only to module/models with specific facet
+ module.models.forEach {
+ val modelImporter = ModelImporter(it)
+ modelImporter.prepare(model!!.reference)
+ modelImporter.execute()
+ }
+ }
+ }
+
+
+ }
+
+}
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/SetNamespaceAction.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/SetNamespaceAction.kt
new file mode 100644
index 000000000..d486f1c99
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/actions/SetNamespaceAction.kt
@@ -0,0 +1,56 @@
+package org.fbme.ide.platform.library.actions
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.ui.DialogWrapper
+import com.intellij.openapi.ui.VerticalFlowLayout
+import jetbrains.mps.ide.actions.MPSCommonDataKeys
+import jetbrains.mps.ide.ui.tree.smodel.SNodeTreeNode
+import jetbrains.mps.project.AbstractModule
+import jetbrains.mps.project.StandaloneMPSProject
+import jetbrains.mps.smodel.SNodeUtil
+import javax.swing.JComponent
+import javax.swing.JLabel
+import javax.swing.JPanel
+import javax.swing.JTextField
+
+
+class SetNamespaceAction: AnAction() {
+
+ val BUTTON_TITLE = "Set namespace"
+
+ override fun actionPerformed(e: AnActionEvent) {
+ val mpsProject = e.getData(MPSCommonDataKeys.MPS_PROJECT) as StandaloneMPSProject
+ val mpsNode = (e.getData(MPSCommonDataKeys.TREE_NODE) as SNodeTreeNode).sNode!!
+
+ val textField = JTextField()
+
+ val panel = JPanel(VerticalFlowLayout())
+ panel.add(JLabel("Specify the namespace:"))
+ panel.add(textField)
+
+ val dialogWrapper: DialogWrapper = object : DialogWrapper(true) {
+ init {
+ init()
+ title = BUTTON_TITLE
+ }
+
+ override fun createCenterPanel(): JComponent? {
+ return panel
+ }
+ }
+
+ mpsProject.modelAccess.runWriteAction {
+ if (dialogWrapper.showAndGet()) {
+ val namespaceName = textField.text
+ mpsNode.setProperty(SNodeUtil.property_BaseConcept_virtualPackage, namespaceName)
+
+ (mpsNode.model!!.module as AbstractModule).setChanged()
+
+ mpsProject.save()
+
+ }
+ }
+
+ }
+}
diff --git a/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/util/LibraryFacet.kt b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/util/LibraryFacet.kt
new file mode 100644
index 000000000..63b03efc8
--- /dev/null
+++ b/code/library-management/src/main/kotlin/org/fbme/ide/platform/library/library/util/LibraryFacet.kt
@@ -0,0 +1,107 @@
+package org.fbme.ide.platform.library.util
+
+import jetbrains.mps.extapi.module.ModuleFacetBase
+import org.fbme.ide.iec61499.repository.PlatformElement
+import org.fbme.ide.iec61499.repository.PlatformElementsOwner
+import org.fbme.ide.platform.converter.PlatformConverter
+import org.jdom.Document
+import org.jetbrains.mps.openapi.model.SModelReference
+import org.jetbrains.mps.openapi.model.SNode
+import org.jetbrains.mps.openapi.module.SModule
+import org.jetbrains.mps.openapi.persistence.Memento
+
+class LibraryFacet(module: SModule) : ModuleFacetBase(FACET_TYPE, module) {
+ companion object {
+ private const val FACET_TYPE = "library"
+ private const val NAMESPACES_KEY = "namespaces"
+ private const val GENERATED_KEY = "generated"
+ private const val PATH_KEY = "path"
+ }
+
+ private val namespaces: HashMap = HashMap()
+
+ /**
+ * Adds a new namespace to the namespaces map.
+ *
+ * @param node The SNode to be added.
+ * @param namespace The namespace represented by a String.
+ */
+ fun addNamespace(node: SNode, namespace: String) {
+ namespaces[node] = namespace
+ }
+
+ /**
+ * Removes a namespace from the namespaces map.
+ *
+ * @param node The SNode to be removed.
+ */
+ fun removeNamespace(node: SNode) {
+ namespaces.remove(node)
+ }
+
+ /**
+ * Returns the namespace of a specific SNode.
+ *
+ * @param node The SNode whose namespace is to be retrieved.
+ * @return The namespace of the SNode.
+ */
+ fun getNamespace(node: SNode): String? {
+ return namespaces[node]
+ }
+
+ /**
+ * Returns all the namespaces stored in the namespaces map.
+ *
+ * @return A map of all namespaces.
+ */
+ fun getAllNamespaces(): Map {
+ return namespaces
+ }
+
+ /**
+ * Save the current state of the namespaces map to a Memento object.
+ *
+ * @param memento The Memento object to save to.
+ */
+ override fun save(memento: Memento) {
+ memento.clear()
+ namespaces.forEach { (node, namespace) ->
+ val child = memento.createChild(NAMESPACES_KEY)
+ child.put(GENERATED_KEY, node.nodeId.toString())
+ child.put(PATH_KEY, namespace)
+ }
+ }
+
+ /**
+ * Load the state of the namespaces map from a Memento object.
+ *
+ * @param memento The Memento object to load from.
+ */
+ override fun load(memento: Memento) {
+ namespaces.clear()
+ memento.getChildren(NAMESPACES_KEY).forEach { child ->
+ val nodeId = child.get(GENERATED_KEY)
+ val namespace = child.get(PATH_KEY)
+ if (nodeId != null && namespace != null) {
+ val node =
+ namespaces[node] = namespace
+ }
+ }
+ }
+
+ /**
+ * Converts an SNode to a PlatformElement.
+ *
+ * @param node The SNode to be converted.
+ * @param reference The SModelReference for the model that the SNode belongs to.
+ * @param doc The Document that represents the XML structure of the SNode.
+ * @return The converted PlatformElement.
+ */
+ fun convertSNodeToPlatformElement(node: SNode, reference: SModelReference, doc: Document): PlatformElement {
+ val owner = PlatformElementsOwner()
+ val configuration = PlatformConverter.STANDARD_CONFIG_FACTORY.createConfiguration(owner)
+ val converter = PlatformConverter.create(configuration, reference, doc)
+ return converter.convert(node) as PlatformElement
+ }
+
+}
diff --git a/code/library-management/src/main/resources/META-INF/plugin.xml b/code/library-management/src/main/resources/META-INF/plugin.xml
new file mode 100644
index 000000000..9a28d8b11
--- /dev/null
+++ b/code/library-management/src/main/resources/META-INF/plugin.xml
@@ -0,0 +1,37 @@
+
+
+ fbme.lib-management
+ FBME - Lib management
+ Defines the core functionality for FBME lib management
+ 0.1
+ FBME
+
+ fbme.library
+ jetbrains.mps.core
+ jetbrains.mps.execution.languages
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/library-management/src/main/resources/icons/lib.svg b/code/library-management/src/main/resources/icons/lib.svg
new file mode 100644
index 000000000..6c93a1f63
--- /dev/null
+++ b/code/library-management/src/main/resources/icons/lib.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/code/platform/src/main/resources/icons/library_project.svg b/code/library-management/src/main/resources/icons/library_project.svg
similarity index 100%
rename from code/platform/src/main/resources/icons/library_project.svg
rename to code/library-management/src/main/resources/icons/library_project.svg
diff --git a/code/platform/src/main/resources/icons/system_project.svg b/code/library-management/src/main/resources/icons/system_project.svg
similarity index 100%
rename from code/platform/src/main/resources/icons/system_project.svg
rename to code/library-management/src/main/resources/icons/system_project.svg
diff --git a/code/library-management/src/test/test/kotlin/org/fbme/ide/platform/library/LibraryActionsTest.kt b/code/library-management/src/test/test/kotlin/org/fbme/ide/platform/library/LibraryActionsTest.kt
new file mode 100644
index 000000000..ead0d5b3d
--- /dev/null
+++ b/code/library-management/src/test/test/kotlin/org/fbme/ide/platform/library/LibraryActionsTest.kt
@@ -0,0 +1,191 @@
+package org.fbme.ide.platform.library.actions
+
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.fileChooser.FileChooser
+import jetbrains.mps.ide.actions.MPSCommonDataKeys
+import jetbrains.mps.project.MPSProject
+import jetbrains.mps.project.Solution
+import junit.framework.TestCase.assertEquals
+import org.fbme.ide.platform.testing.PlatformTestRunner
+import org.junit.Test
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@RunWith(PlatformTestRunner::class)
+class LibraryActionsTest {
+
+ @Test
+ fun testNewLibraryAction() {
+ // Arrange
+ val newLibraryAction = NewLibraryAction()
+ val event = mock(AnActionEvent::class.java)
+
+ // Setup mocks:
+ val project = mock(MPSProject::class.java)
+ `when`(event.getData(MPSCommonDataKeys.MPS_PROJECT)).thenReturn(project)
+ // mock dialog to set module name and location
+ val cfg = mock(NameLocationPanel::class.java)
+ `when`(NewModuleDialog.projectHome(project)).thenReturn("solutions")
+ `when`(cfg.withDefaults("NewSolution", "solutions")).thenReturn(cfg)
+ val dialog = mock(NewModuleDialog::class.java)
+ `when`(NewModuleDialog(project, cfg)).thenReturn(dialog)
+ val model = mock(SModel::class.java)
+ val result = mock(Solution::class.java)
+ val modelRoot = mock(DefaultModelRoot::class.java)
+ `when`(result.modelRoots).thenReturn(setOf(modelRoot))
+ `when`(modelRoot.createModel(SModelName("NewLibrary@content"), null, Iec61499ModelFactory.DST, Iec61499ModelFactory.TYPE)).thenReturn(model)
+ val repository = mock(PlatformRepository::class.java)
+ `when`(PlatformRepositoryProvider.getInstance(project)).thenReturn(repository)
+ val facet = mock(ModuleFacetBase::class.java)
+ `when`(result.getFacet(Iec61499Facet.FACET_TYPE)).thenReturn(facet)
+ val modelImporter = mock(ModelImporter::class.java)
+ `when`(ModelImporter(it)).thenReturn(modelImporter)
+
+
+ // Act
+ newLibraryAction.actionPerformed(event)
+
+ // Assert
+ // Add your assertions here based on the expected outcome of the action
+
+ assertNotNull(model)
+ assertEquals("NewLibrary@Content", model.getName().getValue())
+ verify(result).setFacet(Iec61499Facet.FACET_TYPE, facet)
+ verify(modelImporter, times(1)).execute()
+ verify(project).addModule(result)
+
+
+ }
+
+ @Test
+ fun testExportLibraryAction() {
+ // Arrange
+ val exportLibraryAction = ExportLibraryAction()
+ val event = mock(AnActionEvent::class.java)
+
+ // Setup mocks:
+ val project = mock(MPSProject::class.java)
+ `when`(event.getData(MPSCommonDataKeys.MPS_PROJECT)).thenReturn(project)
+ val solution = mock(Solution::class.java)
+ // Assuming that the solution is selected in the project pane
+ `when`(ProjectPane.getInstance(project).selectedSolution).thenReturn(solution)
+ // mock mpsProject to get the project
+ `when`(event.getData(MPSCommonDataKeys.MPS_PROJECT)).thenReturn(project)
+ // mock ideaProject to get the project base path
+ val ideaProject = mock(IdeaProject::class.java)
+ `when`(project.project).thenReturn(ideaProject)
+ `when`(ideaProject.basePath).thenReturn("/path/to/project")
+ // mock module name
+ `when`(solution.moduleName).thenReturn("NewLibrary")
+
+ // Act
+ exportLibraryAction.actionPerformed(event)
+
+ // Assert
+ verify(solution).exportLibrary()
+ assertTrue(solution.isExported)
+ verify(project).refresh()
+
+ // check that the zip file is created
+ val zipFile = File("/path/to/project/NewLibrary.zip")
+ assertTrue(zipFile.exists())
+
+ }
+
+ @Test
+ fun testImportLibraryAction() {
+ // Arrange
+ val importLibraryAction = ImportLibraryAction()
+ val event = mock(AnActionEvent::class.java)
+
+ // Setup mocks:
+ val project = mock(MPSProject::class.java)
+ `when`(event.getData(MPSCommonDataKeys.MPS_PROJECT)).thenReturn(project)
+ val solution = mock(Solution::class.java)
+ `when`(ProjectPane.getInstance(project).selectedSolution).thenReturn(solution)
+ `when`(event.getData(MPSCommonDataKeys.MPS_PROJECT)).thenReturn(mpsProject)
+ val fileChooser = mock(FileChooser::class.java)
+ `when`(FileChooser.chooseFile(any(), any(), any())).thenReturn(VirtualFile("code/platform/src/test/resources/Library.zip"))
+
+ // Act
+ importLibraryAction.actionPerformed(event)
+
+ // Assert
+ verify(solution).importLibrary()
+ assertTrue(solution.isImported)
+ verify(project).refresh()
+
+ val extractedDir = File("code/platform/src/test/resources/Library")
+ assertTrue(extractedDir.exists())
+ val fbtFile = File("code/platform/src/test/resources/Library/models/SampleFbType.fbt")
+ assertTrue(fbtFile.exists())
+ // remove the extracted directory
+ extractedDir.delete()
+
+
+ verify(result).setFacet(Iec61499Facet.FACET_TYPE, facet)
+ verify(modelImporter, times(1)).execute()
+ verify(project).addModule(result)
+
+ verify(project).addModule(module)
+ assertTrue(project.modules.size() == 1)
+
+ assertEquals("NewLibrary", mpsProject.modules[0].moduleName)
+
+ }
+
+ @Test
+ fun testNewLibImportExport() {
+ val newLibraryAction = NewLibraryAction()
+ val exportLibraryAction = ExportLibraryAction()
+ val importLibraryAction = ImportLibraryAction()
+ val event = mock(AnActionEvent::class.java)
+
+ // Setup mocks:
+ val project = mock(MPSProject::class.java)
+ `when`(event.getData(MPSCommonDataKeys.MPS_PROJECT)).thenReturn(project)
+ val solution = mock(Solution::class.java)
+ `when`(ProjectPane.getInstance(project).selectedSolution).thenReturn(solution)
+ `when`(event.getData(MPSCommonDataKeys.MPS_PROJECT)).thenReturn(project)
+ val ideaProject = mock(IdeaProject::class.java)
+ `when`(project.project).thenReturn(ideaProject)
+ `when`(ideaProject.basePath).thenReturn("/path/to/project")
+ `when`(solution.moduleName).thenReturn("NewLibrary")
+ val fileChooser = mock(FileChooser::class.java)
+ `when`(FileChooser.chooseFile(any(), any(), any())).thenReturn(VirtualFile("code/platform/src/test/resources/NewLibrary.zip"))
+
+ // Act
+ newLibraryAction.actionPerformed(event)
+ exportLibraryAction.actionPerformed(event)
+ importLibraryAction.actionPerformed(event)
+
+ // Assert
+ verify(solution).exportLibrary()
+ assertTrue(solution.isExported)
+ verify(project).refresh()
+
+ val zipFile = File("/code/platform/src/test/resources/NewLibrary.zip")
+ assertTrue(zipFile.exists())
+
+ verify(solution).importLibrary()
+ assertTrue(solution.isImported)
+ verify(project).refresh()
+
+ val extractedDir = File("code/platform/src/test/resources/Library")
+ assertTrue(extractedDir.exists())
+ val fbtFile = File("code/platform/src/test/resources/Library/models/SampleFbType.fbt")
+ assertTrue(fbtFile.exists())
+
+ verify(result).setFacet(Iec61499Facet.FACET_TYPE, facet)
+ verify(modelImporter, times(1)).execute()
+ verify(project).addModule(result)
+
+ verify(project).addModule(module)
+ assertTrue(project.modules.size() == 1)
+
+ assertEquals("NewLibrary", mpsProject.modules[0].moduleName)
+ }
+
+}
\ No newline at end of file
diff --git a/code/library-management/src/test/test/kotlin/org/fbme/ide/platform/library/TestUtils.kt b/code/library-management/src/test/test/kotlin/org/fbme/ide/platform/library/TestUtils.kt
new file mode 100644
index 000000000..d5f128ee4
--- /dev/null
+++ b/code/library-management/src/test/test/kotlin/org/fbme/ide/platform/library/TestUtils.kt
@@ -0,0 +1,4 @@
+package org.fbme.ide.platform.library
+
+class TestUtils {
+}
\ No newline at end of file
diff --git a/code/library-management/src/test/test/resources/LibExample/Library.zip b/code/library-management/src/test/test/resources/LibExample/Library.zip
new file mode 100644
index 000000000..38f1146ba
Binary files /dev/null and b/code/library-management/src/test/test/resources/LibExample/Library.zip differ
diff --git a/code/library/src/main/kotlin/org/fbme/lib/common/Element.kt b/code/library/src/main/kotlin/org/fbme/lib/common/Element.kt
index 90dc63260..1bb55e6a6 100644
--- a/code/library/src/main/kotlin/org/fbme/lib/common/Element.kt
+++ b/code/library/src/main/kotlin/org/fbme/lib/common/Element.kt
@@ -4,5 +4,6 @@ import org.fbme.lib.common.attributes.WithExternalXmlContent
interface Element : WithExternalXmlContent {
val container: Element?
+ val library: Library?
fun copy(): Element
}
diff --git a/code/library/src/main/kotlin/org/fbme/lib/common/Library.kt b/code/library/src/main/kotlin/org/fbme/lib/common/Library.kt
new file mode 100644
index 000000000..8656cdd95
--- /dev/null
+++ b/code/library/src/main/kotlin/org/fbme/lib/common/Library.kt
@@ -0,0 +1,12 @@
+package org.fbme.lib.common
+
+interface Library {
+
+ val elements: Collection
+ val namespaces: List
+
+ fun elementsByNamespace(namespace: String): Collection
+
+ fun attachElement(element: RootElement, namespace: String = "")
+ fun detachElement(element: RootElement)
+}
diff --git a/code/platform/src/main/resources/META-INF/plugin.xml b/code/platform/src/main/resources/META-INF/plugin.xml
index 505de4275..4dbccfe50 100644
--- a/code/platform/src/main/resources/META-INF/plugin.xml
+++ b/code/platform/src/main/resources/META-INF/plugin.xml
@@ -22,13 +22,16 @@
+
+
+
+
diff --git a/code/richediting/solutions/org.fbme.ide.richediting/models/org.fbme.ide.richediting.plugin.mps b/code/richediting/solutions/org.fbme.ide.richediting/models/org.fbme.ide.richediting.plugin.mps
index f9cc459f5..7db4fb00c 100644
--- a/code/richediting/solutions/org.fbme.ide.richediting/models/org.fbme.ide.richediting.plugin.mps
+++ b/code/richediting/solutions/org.fbme.ide.richediting/models/org.fbme.ide.richediting.plugin.mps
@@ -51,6 +51,7 @@
+
@@ -58,6 +59,7 @@
+
@@ -3755,5 +3757,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/richediting/src/main/resources/META-INF/plugin.xml b/code/richediting/src/main/resources/META-INF/plugin.xml
index 561b0538d..402347b18 100644
--- a/code/richediting/src/main/resources/META-INF/plugin.xml
+++ b/code/richediting/src/main/resources/META-INF/plugin.xml
@@ -15,6 +15,7 @@
+