├── .github └── workflows │ ├── declarations.yml │ ├── gradle-plugin.yml │ ├── idea-plugin.yml │ └── package-lock.yml ├── .gitignore ├── .idea └── icon.png ├── .kotlin └── errors │ └── errors-1722683661652.log ├── LICENSE.md ├── README.md ├── assets └── online-documentation.gif ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── com │ └── github │ └── turansky │ └── yfiles │ ├── ClassId.kt │ ├── ClassRegistry.kt │ ├── CleanCode.kt │ ├── Codegen.kt │ ├── Collections.kt │ ├── Components.kt │ ├── Expressions.kt │ ├── Extensions.kt │ ├── FactoryMethod.kt │ ├── FileGenerator.kt │ ├── Generator.kt │ ├── Generics.kt │ ├── Handlers.kt │ ├── InvokeExtensions.kt │ ├── JsTypes.kt │ ├── JsonReader.kt │ ├── KDoc.kt │ ├── KotlinFileGenerator.kt │ ├── KotlinTypes.kt │ ├── Modifiers.kt │ ├── Resources.kt │ ├── Strings.kt │ ├── Summary.kt │ ├── TypeParser.kt │ ├── Wrappers.kt │ ├── YSettings.kt │ ├── YTypes.kt │ ├── correction │ ├── BindingHacks.kt │ ├── BusinessObjectHacks.kt │ ├── CanvasObjectDescriptorHacks.kt │ ├── CanvasObjectInstallerHacks.kt │ ├── ClassHacks.kt │ ├── ClipboardHelperHacks.kt │ ├── CloneableHacks.kt │ ├── CollectionHacks.kt │ ├── CommandHacks.kt │ ├── ComparableHacks.kt │ ├── ComparerHacks.kt │ ├── ConstructorHacks.kt │ ├── ContextLookupHacks.kt │ ├── ConvertersHacks.kt │ ├── CreationPropertyHacks.kt │ ├── CursorHacks.kt │ ├── DataHacks.kt │ ├── DataTagHacks.kt │ ├── Decorators.kt │ ├── DoubleCorrections.kt │ ├── DpKeyHacks.kt │ ├── DpdataHacks.kt │ ├── DragDropDataHacks.kt │ ├── EdgeDirectednessHacks.kt │ ├── ElementIdHacks.kt │ ├── EventDispatcherHacks.kt │ ├── EventHacks.kt │ ├── Exclusions.kt │ ├── ExtensionHacks.kt │ ├── FlagsHacks.kt │ ├── Hacks.kt │ ├── IdHacks.kt │ ├── IncrementalHintHacks.kt │ ├── IntCorrections.kt │ ├── IntersectionHacks.kt │ ├── IteratorSupport.kt │ ├── JKey.kt │ ├── JsonCorrections.kt │ ├── LabelModelParameterHacks.kt │ ├── LayoutDescriptorHacks.kt │ ├── LayoutStrictTypes.kt │ ├── ListCellHacks.kt │ ├── ListHacks.kt │ ├── MementoHacks.kt │ ├── MementoSupportHacks.kt │ ├── NodeTypeHacks.kt │ ├── NullabilityHacks.kt │ ├── NumberCorrections.kt │ ├── ObservableDelegates.kt │ ├── ObstacleDataHacks.kt │ ├── PartitionCellHacks.kt │ ├── ResourceHacks.kt │ ├── ResultHacks.kt │ ├── SerializarionHacks.kt │ ├── SingletonHacks.kt │ ├── SizeExtensions.kt │ ├── SnapLineProviderHacks.kt │ ├── Source.kt │ ├── StyleRendererHacks.kt │ ├── StyleTagHacks.kt │ ├── TagHacks.kt │ ├── TemplateLoadersHacks.kt │ ├── TemplatesHacks.kt │ ├── TimeSpanExtensions.kt │ ├── TooltipHacks.kt │ ├── VisualTemplateHacks.kt │ ├── YJson.kt │ ├── YListHacks.kt │ └── YndefinedHacks.kt │ ├── json │ ├── Delegates.kt │ └── Json.kt │ └── vsdx │ ├── FakeInterfaces.kt │ └── correction │ ├── DataHacks.kt │ ├── Hacks.kt │ ├── NumberCorrections.kt │ └── VsdxSource.kt ├── examples ├── configurable-properties │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── AbstractArrow.kt │ │ ├── CustomLabelModelParameter.kt │ │ ├── Delegates.kt │ │ └── MyPolylineEdgeStyleRenderer.kt ├── data-classes │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── DataClasses.kt ├── import-optimizer-application │ ├── build.gradle.kts │ ├── gradle.properties │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── Components.kt ├── import-optimizer-library │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── Graphs.kt └── simple-app │ ├── build.gradle.kts │ └── src │ └── jsMain │ └── kotlin │ ├── App.kt │ ├── Cast.kt │ ├── Comparer.kt │ ├── Converters.kt │ ├── Destructuring.kt │ ├── ExternalExtensions.kt │ ├── Flags.kt │ ├── Iterators.kt │ ├── Lookups.kt │ ├── Observable.kt │ ├── Resources.kt │ ├── Sequences.kt │ └── TimeSpan.kt ├── gradle-plugin-test ├── build.gradle.kts └── src │ ├── jsMain │ ├── kotlin │ │ ├── ArrowDelegate.kt │ │ ├── ComboClass.kt │ │ ├── CustomArrow.kt │ │ ├── CustomObject.kt │ │ ├── DataObject.kt │ │ ├── Object.kt │ │ └── SimpleVisibilityTestable.kt │ └── resources │ │ └── yfiles.js │ └── jsTest │ └── kotlin │ ├── BaseClassTest.kt │ ├── ConfigurablePropertyTest.kt │ └── CustomObjectTest.kt ├── gradle-plugin ├── LICENSE.md ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── release ├── settings.gradle.kts └── src │ └── main │ ├── kotlin │ └── com │ │ └── github │ │ └── turansky │ │ └── yfiles │ │ ├── compiler │ │ ├── backend │ │ │ └── common │ │ │ │ └── YFiles.kt │ │ └── diagnostic │ │ │ ├── YDiagnosticSuppressor.kt │ │ │ ├── YError.kt │ │ │ └── YMessagesExtension.kt │ │ └── gradle │ │ └── plugin │ │ ├── KotlinPluginArtifact.kt │ │ └── YFilesGradleSubplugin.kt │ └── resources │ └── META-INF │ └── services │ └── org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── idea-plugin-test ├── .gitignore ├── build.gradle.kts ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ └── main │ ├── kotlin │ ├── ColorTest.kt │ ├── CustomObject.kt │ ├── FillTest.kt │ └── MaterialPalette.kt │ └── resources │ ├── binding.svg │ └── index.html ├── idea-plugin ├── .gitignore ├── LICENSE.md ├── README.md ├── assets │ ├── class-interface-object.png │ ├── interface-combinations.png │ ├── template-binding-autocomplete.png │ └── template-mime-type-support.png ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── release ├── settings.gradle.kts └── src │ └── main │ ├── kotlin │ └── com │ │ └── github │ │ └── turansky │ │ └── yfiles │ │ └── ide │ │ ├── binding │ │ ├── Binding.kt │ │ ├── BindingAnnotator.kt │ │ ├── BindingDirective.kt │ │ ├── BindingHighlightingColors.kt │ │ ├── BindingParser.kt │ │ ├── BindingReferenceContributor.kt │ │ ├── BindingToken.kt │ │ ├── Bindings.kt │ │ ├── ContextProperty.kt │ │ ├── Properties.kt │ │ ├── PsiReferences.kt │ │ └── Strings.kt │ │ ├── color │ │ ├── ColorAnnotation.kt │ │ ├── ColorFormat.kt │ │ ├── HslColor.kt │ │ └── KotlinColorAnnotator.kt │ │ ├── documentation │ │ ├── BindingDocumentationProvider.kt │ │ └── Documentation.kt │ │ ├── highlighter │ │ └── markers │ │ │ ├── Icons.kt │ │ │ ├── LineMarkerOptions.kt │ │ │ └── YLineMarkerProvider.kt │ │ ├── inspections │ │ ├── InheritanceInspection.kt │ │ └── YDiagnosticSuppressor.kt │ │ ├── js │ │ └── YFiles.kt │ │ ├── psi │ │ ├── ClassDescriptors.kt │ │ ├── DefaultPsiFinder.kt │ │ ├── JavaPsiFinder.kt │ │ ├── JavaScriptPsiFinder.kt │ │ ├── KotlinPsiFinder.kt │ │ └── PsiFinder.kt │ │ └── template │ │ ├── TemplateErrorFilter.kt │ │ ├── TemplateFileType.kt │ │ └── TemplateLanguage.kt │ └── resources │ ├── META-INF │ ├── java.xml │ ├── javascript.xml │ ├── kotlin.xml │ ├── plugin.xml │ └── pluginIcon.svg │ ├── icons │ └── gutter │ │ ├── plugin.svg │ │ └── plugin_dark.svg │ └── inspectionDescriptions │ └── yfiles-kotlinjs-inheritance.html ├── package-lock.json ├── settings.gradle.kts ├── vsdx-kotlin ├── build.gradle.kts ├── gradle.properties └── settings.gradle.kts └── yfiles-kotlin ├── build.gradle.kts ├── gradle.properties └── settings.gradle.kts /.github/workflows/declarations.yml: -------------------------------------------------------------------------------- 1 | name: declarations 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Set up JDK 17 13 | uses: actions/setup-java@v3 14 | with: 15 | distribution: 'zulu' 16 | java-version: '17' 17 | - name: Build 18 | run: | 19 | ./gradlew build 20 | -------------------------------------------------------------------------------- /.github/workflows/gradle-plugin.yml: -------------------------------------------------------------------------------- 1 | name: gradle plugin 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Set up JDK 17 13 | uses: actions/setup-java@v3 14 | with: 15 | distribution: 'zulu' 16 | java-version: '17' 17 | - name: Build 18 | run: | 19 | cd gradle-plugin 20 | ./gradlew build 21 | -------------------------------------------------------------------------------- /.github/workflows/idea-plugin.yml: -------------------------------------------------------------------------------- 1 | name: idea plugin 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Set up JDK 17 13 | uses: actions/setup-java@v3 14 | with: 15 | distribution: 'zulu' 16 | java-version: '17' 17 | - name: Build 18 | run: | 19 | cd idea-plugin 20 | ./gradlew buildPlugin -Dorg.gradle.project.intellij.publish.token=unknown 21 | -------------------------------------------------------------------------------- /.github/workflows/package-lock.yml: -------------------------------------------------------------------------------- 1 | name: Package Lock 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | update: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Update 17 | uses: karakum-team/vitalik@v0.9 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | 4 | yfiles-kotlin/src/ 5 | vsdx-kotlin/src/ 6 | 7 | # IDEA ignore list 8 | .idea/* 9 | !.idea/icon.png 10 | *.iml 11 | 12 | webpack.config.d/ 13 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/.idea/icon.png -------------------------------------------------------------------------------- /assets/online-documentation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/assets/online-documentation.gif -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.kotlin.multiplatform) apply false 3 | alias(libs.plugins.kotlin.js.plain.objects) apply false 4 | } 5 | 6 | tasks.wrapper { 7 | gradleVersion = "8.9" 8 | } 9 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | tasks.compileKotlin { 10 | kotlinOptions.allWarningsAsErrors = true 11 | } 12 | 13 | dependencies { 14 | implementation("org.json:json:20200518") 15 | } 16 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/ClassId.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | typealias ClassId = String 4 | 5 | interface HasClassId { 6 | val classId: ClassId 7 | } 8 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/CleanCode.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | private val LINE_BREAK_3 = Regex("(\\n\\s?){3,}") 4 | private val LINE_BREAK_2 = Regex("(\\n\\s?){2,}}") 5 | 6 | private val YFILES_CLASS_DECLARATION = Regex("yfiles\\.([a-z]+)\\.([A-Za-z0-9]+)") 7 | private val DUPLICATED_LINK = Regex("(\\[[a-zA-Z0-9.]+])\\1") 8 | private val LONG_LINK = Regex("([^]])\\[(yfiles\\.[a-z]+)\\.([^]]+)]") 9 | 10 | internal fun String.clear(fqn: String): String { 11 | val packageName = fqn.substringBeforeLast(".") 12 | var content = replace("[${fqn}.", "[") 13 | .replace("$packageName.", "") 14 | .replace(LINE_BREAK_3, "\n\n") 15 | .replace(LINE_BREAK_2, "\n}") 16 | 17 | if (!content.endsWith("\n")) { 18 | content += "\n" 19 | } 20 | 21 | val importedClasses = content.getImportedClasses() 22 | 23 | if (importedClasses.isEmpty()) { 24 | return content 25 | } 26 | 27 | val imports = importedClasses 28 | .lines { "import $it" } 29 | 30 | for (className in importedClasses) { 31 | val name = className.substringAfterLast(".") 32 | content = content.replace(className, name) 33 | } 34 | 35 | content = content.cleanDoc() 36 | 37 | return "$imports\n$content" 38 | } 39 | 40 | private fun String.cleanDoc(): String { 41 | return replace(DUPLICATED_LINK, "$1") 42 | .replace(LONG_LINK, "$1[$3][$2.$3]") 43 | } 44 | 45 | private fun String.getImportedClasses(): List { 46 | val code = split("\n") 47 | .asSequence() 48 | .filterNot { it.startsWith("import yfiles.") } 49 | .filterNot { it.startsWith(" *") } 50 | .joinToString("\n") 51 | 52 | val additionalImports = mutableListOf() 53 | if ("js.yclass" in this) 54 | additionalImports += "yfiles.lang.yclass" 55 | 56 | if ("Array<" in this) 57 | additionalImports += "js.array.ReadonlyArray" 58 | 59 | return YFILES_CLASS_DECLARATION 60 | .findAll(code) 61 | .map { it.value } 62 | .plus(additionalImports) 63 | .distinct() 64 | .plus( 65 | STANDARD_IMPORTED_TYPES 66 | .asSequence() 67 | .filter { it in code } 68 | ) 69 | .sorted() 70 | .toList() 71 | } 72 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/Codegen.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | // language=kotlin 4 | internal val HIDDEN_METHOD_ANNOTATION = """ 5 | @Deprecated(message = "For code generation only", level = DeprecationLevel.HIDDEN) 6 | """.trimIndent() 7 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/Collections.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | internal fun Iterable.byComma(transform: ((T) -> CharSequence)? = null): String = 4 | joinToString(separator = ", ", transform = transform) 5 | 6 | internal fun Iterable.byCommaLine(transform: ((T) -> CharSequence)? = null): String = 7 | joinToString(separator = ",\n", transform = transform) 8 | 9 | internal fun Iterable.lines(transform: ((T) -> CharSequence)? = null): String = 10 | joinToString(separator = "\n", postfix = "\n", transform = transform) 11 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/Components.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | internal fun Class.getComponents(): String? = 4 | when (classId) { 5 | "yfiles.algorithms.Point2D", 6 | "yfiles.algorithms.Rectangle2D", 7 | 8 | "yfiles.algorithms.YDimension", 9 | "yfiles.algorithms.YPoint", 10 | "yfiles.algorithms.YOrientedRectangle", 11 | 12 | "yfiles.algorithms.YInsets", 13 | 14 | "yfiles.geometry.Point", 15 | "yfiles.geometry.MutablePoint", 16 | 17 | "yfiles.geometry.Size", 18 | "yfiles.geometry.MutableSize", 19 | 20 | "yfiles.geometry.Rect", 21 | "yfiles.geometry.OrientedRectangle", 22 | 23 | "yfiles.geometry.Insets", 24 | "yfiles.geometry.Tangent", 25 | 26 | "yfiles.view.Color", 27 | -> constructorComponents() 28 | 29 | "yfiles.algorithms.YVector", 30 | -> components("x", "y") 31 | 32 | else -> null 33 | } 34 | 35 | internal fun Interface.getComponentExtensions(): String? = 36 | if (classId == IENUMERABLE) { 37 | indexAccessComponentExtensions("at") 38 | } else { 39 | null 40 | } 41 | 42 | private fun Class.constructorComponents(): String = 43 | primaryConstructor!! 44 | .parameters 45 | .map { it.name } 46 | .let { components(*it.toTypedArray()) } 47 | 48 | private fun Interface.indexAccessComponentExtensions( 49 | getMethod: String, 50 | ): String = 51 | (1..5).joinToString("\n") { index -> 52 | "inline operator fun ${generics.wrapperDeclaration} $classId${generics.asAliasParameters()}.component$index(): T = $getMethod(${index - 1})!!" 53 | } 54 | 55 | private fun Class.components(vararg properties: String): String = 56 | properties.asSequence() 57 | .mapIndexed { index, property -> 58 | val type = memberProperties.first { it.name == property }.type 59 | """ 60 | /** 61 | * @return [$property] 62 | */ 63 | @JsName("__ygen_${property}_negy__") 64 | final operator fun component${index + 1}(): $type 65 | """.trimIndent() 66 | } 67 | .joinToString("\n") 68 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/Expressions.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | internal const val AS_DYNAMIC = "asDynamic()" 4 | 5 | internal const val EQ_DE = " = definedExternally" 6 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | import com.github.turansky.yfiles.correction.timeSpanExtensions 4 | 5 | internal fun Class.getExtensions(): String? = 6 | when (classId) { 7 | "yfiles.lang.TimeSpan", 8 | -> timeSpanExtensions(this) 9 | 10 | else 11 | -> null 12 | } 13 | 14 | internal fun Interface.getExtensions(): String? = 15 | when (classId) { 16 | // language=kotlin 17 | YOBJECT, 18 | -> """ 19 | inline fun T.getClass(): $YCLASS = 20 | $AS_DYNAMIC.getClass() 21 | """.trimIndent() 22 | 23 | // language=kotlin 24 | ILOOKUP, 25 | -> """ 26 | inline fun $ILOOKUP.lookup(): T? = 27 | lookup(T::class.js.yclass) 28 | 29 | inline fun $ILOOKUP.lookupValue(type: $YCLASS):T = 30 | requireNotNull(lookup(type)) { 31 | "Unable to lookup type ${'$'}type" 32 | } 33 | 34 | inline fun $ILOOKUP.lookupValue(): T = 35 | lookupValue(T::class.js.yclass) 36 | """.trimIndent() 37 | 38 | IMAPPER_REGISTRY, 39 | -> getMapperRegistryExtensions() 40 | 41 | else -> null 42 | } 43 | 44 | private fun getMapperRegistryExtensions(): String { 45 | return getMapperRegistryExtensions(GRAPH_DP_KEY, GRAPH, IGRAPH) 46 | } 47 | 48 | @Suppress("SameParameterValue") 49 | private fun getMapperRegistryExtensions( 50 | dpKeyType: String, 51 | keyType: String, 52 | modelKeyType: String = keyType, 53 | ): String { 54 | var keyClass = "$keyType.yclass" 55 | if (keyType != modelKeyType) 56 | keyClass += ".unsafeCast<$YCLASS<$modelKeyType>>()" 57 | 58 | val valueClass = "tag.valueType" 59 | 60 | // language=kotlin 61 | return """ 62 | inline fun IMapperRegistry.createConstantMapper( 63 | tag: $dpKeyType , 64 | constant: V? 65 | ):IMapper<$modelKeyType, V> = 66 | createConstantMapper($keyClass, $valueClass, tag, constant) 67 | 68 | inline fun IMapperRegistry.createDelegateMapper( 69 | tag: $dpKeyType , 70 | noinline getter: MapperDelegate<$modelKeyType, V> 71 | ):IMapper<$modelKeyType, V> = 72 | createDelegateMapper($keyClass, $valueClass, tag, getter) 73 | 74 | inline fun IMapperRegistry.createMapper(tag: $dpKeyType):Mapper<$modelKeyType, V> = 75 | createMapper($keyClass, $valueClass, tag) 76 | """.trimIndent() 77 | } 78 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/FactoryMethod.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | private val HAS_FACTORY_METHOD = setOf( 4 | "Matrix", 5 | "GeneralPath", 6 | 7 | "GridNodePlacer", 8 | 9 | "DashStyle", 10 | "GraphComponent", 11 | "Stroke", 12 | 13 | "DefaultLayoutGraph" 14 | ) 15 | 16 | internal fun Class.toFactoryMethodCode(): String? { 17 | if (!hasFactoryMethod()) { 18 | return null 19 | } 20 | 21 | return """ 22 | inline fun $name( 23 | block: $name.() -> Unit 24 | ): $name { 25 | return $name() 26 | .apply(block) 27 | } 28 | """.trimIndent() 29 | } 30 | 31 | private fun Class.hasFactoryMethod(): Boolean { 32 | return when { 33 | name in HAS_FACTORY_METHOD -> true 34 | !canHaveFactoryMethod() -> false 35 | primaryConstructor == null -> false 36 | secondaryConstructors.any { it.public } -> false 37 | else -> primaryConstructor.isFactoryMethodSource() 38 | } 39 | } 40 | 41 | private fun Class.canHaveFactoryMethod(): Boolean = 42 | when { 43 | abstract -> false 44 | generics.isNotEmpty() -> false 45 | extendedType() == null && memberProperties.none { it.public && it.mode.writable } -> false 46 | else -> true 47 | } 48 | 49 | private fun Constructor.isFactoryMethodSource(): Boolean = 50 | public && isDefault() 51 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/FileGenerator.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | internal interface FileGenerator { 4 | fun generate(context: GeneratorContext) 5 | } 6 | 7 | internal open class GeneratorData( 8 | val fqn: String, 9 | ) { 10 | private val names = fqn.split(".") 11 | private val packageNames = names.subList(0, names.size - 1) 12 | 13 | val name = names.last() 14 | val packageName = packageNames.joinToString(separator = ".") 15 | } 16 | 17 | internal class TypeGeneratorData( 18 | fqn: String, 19 | alias: String?, 20 | ) : GeneratorData(fqn) { 21 | val jsName = alias ?: name 22 | 23 | val fileId: String 24 | get() = if (primitive || isYObject) { 25 | "$packageName.$jsName" 26 | } else { 27 | fqn 28 | } 29 | 30 | val isYObject: Boolean 31 | get() = isYObjectClass(fqn) 32 | 33 | val isYBase: Boolean 34 | get() = fqn == YCLASS || fqn == YENUM 35 | 36 | val primitive: Boolean 37 | get() = isPrimitiveClass(fqn) 38 | 39 | val marker: Boolean 40 | get() = isMarkerClass(fqn) 41 | } 42 | 43 | internal fun es6GeneratorData( 44 | declaration: Type, 45 | ) = TypeGeneratorData( 46 | fqn = declaration.classId, 47 | alias = declaration.es6name 48 | ) 49 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/Generics.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | internal interface ITypeParameter { 4 | val name: String 5 | 6 | fun toCode(): String 7 | } 8 | 9 | internal fun Generics(parameters: List): Generics = 10 | if (parameters.isNotEmpty()) { 11 | SimpleGenerics(parameters) 12 | } else { 13 | EmptyGenerics 14 | } 15 | 16 | internal sealed class Generics { 17 | protected abstract val parameters: List 18 | 19 | abstract val declaration: String 20 | abstract val wrapperDeclaration: String 21 | 22 | abstract fun asParameters(): String 23 | abstract fun asAliasParameters(): String 24 | 25 | abstract val placeholder: String 26 | 27 | abstract fun isEmpty(): Boolean 28 | fun isNotEmpty(): Boolean = !isEmpty() 29 | 30 | operator fun plus(other: Generics): Generics = 31 | Generics(parameters + other.parameters) 32 | } 33 | 34 | private object EmptyGenerics : Generics() { 35 | override val parameters: List = emptyList() 36 | 37 | override val declaration: String = "" 38 | override val wrapperDeclaration: String = "" 39 | override fun asParameters(): String = "" 40 | override fun asAliasParameters(): String = "" 41 | override val placeholder: String = "" 42 | 43 | override fun isEmpty(): Boolean = true 44 | } 45 | 46 | private class SimpleGenerics(override val parameters: List) : Generics() { 47 | override val declaration: String 48 | get() = toString { it.toCode() } 49 | 50 | override val wrapperDeclaration: String 51 | get() = toString { it.toCode().clearName() } 52 | 53 | override fun asParameters(): String = 54 | toString { it.name } 55 | 56 | override fun asAliasParameters(): String = 57 | toString { it.name.clearName() } 58 | 59 | override val placeholder: String 60 | get() = toString { "*" } 61 | 62 | override fun isEmpty(): Boolean = false 63 | 64 | private fun toString(transform: (ITypeParameter) -> String): String = 65 | "<${parameters.byComma { transform(it) }}>" 66 | } 67 | 68 | private fun String.clearName(): String = 69 | removePrefix("in ").removePrefix("out ") 70 | 71 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/InvokeExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | private const val T = "T" 4 | 5 | private val INVOKE_TARGETS = setOf( 6 | "GraphComponent", 7 | 8 | "BendDecorator", 9 | "EdgeDecorator", 10 | "GraphDecorator", 11 | "LabelDecorator", 12 | "NodeDecorator", 13 | "PortDecorator", 14 | "StripeDecorator", 15 | "StripeLabelDecorator", 16 | "TableDecorator", 17 | 18 | "Visual" 19 | ) 20 | 21 | internal fun invokeExtension( 22 | className: String, 23 | generics: Generics, 24 | final: Boolean = false, 25 | ): String? { 26 | if (className !in INVOKE_TARGETS) { 27 | return null 28 | } 29 | 30 | val type = className + generics.asParameters() 31 | 32 | lateinit var receiverType: String 33 | lateinit var typeGenerics: Generics 34 | 35 | if (final) { 36 | receiverType = type 37 | typeGenerics = generics 38 | } else { 39 | receiverType = T 40 | typeGenerics = Generics(listOf(CustomTypeParameter(T, type))) + generics 41 | } 42 | 43 | return """ 44 | inline operator fun ${typeGenerics.declaration} $receiverType.invoke( 45 | block: $receiverType.() -> Unit 46 | ): $receiverType = apply(block) 47 | """.trimIndent() 48 | } 49 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/JsTypes.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | const val JS_VOID = "void" 4 | 5 | const val JS_ANY = "any" 6 | const val JS_OBJECT = "Object" 7 | const val JS_BOOLEAN = "boolean" 8 | const val JS_STRING = "string" 9 | const val JS_NUMBER = "number" 10 | 11 | val JS_INT = INT 12 | val JS_DOUBLE = DOUBLE 13 | 14 | const val JS_CLASS = "JsClass" 15 | 16 | const val JS_BLOB = "Blob" 17 | 18 | const val JS_ELEMENT = "Element" 19 | const val JS_SVG_ELEMENT = "SVGElement" 20 | const val JS_SVG_DEFS_ELEMENT = "SVGDefsElement" 21 | const val JS_SVG_SVG_ELEMENT = "SVGSVGElement" 22 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/KDoc.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | const val LIST_MARKER = "-" 4 | const val LINE_DELIMITER = "\n" 5 | const val MULTILINE_INDENT = " " 6 | 7 | // TODO: remove after fix 8 | // https://youtrack.jetbrains.com/issue/KT-32815 9 | private fun String.applyAnchorWorkaround(): String = 10 | if ("#" in this) { 11 | val index = indexOf("#") + 1 12 | substring(0, index) + substring(index).replace("#", "%23") 13 | } else { 14 | this 15 | } 16 | 17 | // TODO: use Markdown after fix 18 | // https://youtrack.jetbrains.com/issue/KT-32640 19 | fun link(text: String, href: String): String = 20 | """$text""" 21 | 22 | fun listItem(text: String): String = 23 | "$LIST_MARKER $text" 24 | 25 | fun constructor(): String = 26 | "@constructor" 27 | 28 | fun constructor(summary: String): String = 29 | "@constructor $summary" 30 | 31 | fun property(name: String): String = 32 | "@property $name" 33 | 34 | fun receiver(summary: String): List = 35 | "@receiver $summary" 36 | .asMultiline() 37 | 38 | fun param(name: String, summary: String): List = 39 | "@param [$name] $summary" 40 | .asMultiline() 41 | 42 | fun ret(summary: String): List = 43 | "@return $summary" 44 | .asMultiline() 45 | 46 | fun throws(summary: String): String = 47 | "@throws $summary" 48 | 49 | fun see(link: String): String = 50 | "@see $link" 51 | 52 | private fun String.asMultiline(): List = 53 | split(LINE_DELIMITER) 54 | // TODO: check if required 55 | .filter { it.isNotEmpty() } 56 | .mapIndexed { index, line -> 57 | if (index == 0) { 58 | line 59 | } else { 60 | MULTILINE_INDENT + line 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/KotlinTypes.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | internal val UNIT = Unit::class.simpleName!! 4 | internal val ANY = Any::class.simpleName!! 5 | 6 | internal val STRING: String = String::class.simpleName!! 7 | internal val INT: String = Int::class.simpleName!! 8 | internal val DOUBLE: String = Double::class.simpleName!! 9 | internal val BOOLEAN: String = Boolean::class.simpleName!! 10 | 11 | internal const val PROMISE = "js.promise.Promise" 12 | internal const val PROMISE_RESULT = "js.promise.PromiseResult" 13 | internal const val READ_ONLY_PROPERTY = "kotlin.properties.ReadOnlyProperty" 14 | internal const val READ_WRITE_PROPERTY = "kotlin.properties.ReadWriteProperty" 15 | internal const val KCLASS = "kotlin.reflect.KClass" 16 | internal const val KPROPERTY = "kotlin.reflect.KProperty" 17 | 18 | internal const val BLOB = "web.blob.Blob" 19 | 20 | internal const val ELEMENT = "web.dom.Element" 21 | internal const val HTML_ELEMENT = "web.html.HTMLElement" 22 | internal const val SVG_ELEMENT = "web.svg.SVGElement" 23 | internal const val SVG_SVG_ELEMENT = "web.svg.SVGSVGElement" 24 | 25 | internal const val WEBGL2_RENDERING_CONTEXT = "web.gl.WebGL2RenderingContext" 26 | 27 | internal fun getKotlinType(type: String): String? = 28 | STANDARD_TYPE_MAP[type] 29 | 30 | private val STANDARD_TYPE_MAP = mapOf( 31 | JS_VOID to "js.core.Void", 32 | " unknown" to "*", 33 | 34 | JS_ANY to ANY, 35 | JS_OBJECT to ANY, 36 | JS_BOOLEAN to "Boolean", 37 | JS_STRING to STRING, 38 | JS_NUMBER to "Number", 39 | "Date" to "kotlin.js.Date", 40 | "Function" to "() -> $UNIT", 41 | 42 | "Record" to "js.objects.ReadonlyRecord", 43 | 44 | "Event" to "web.events.Event", 45 | "KeyboardEvent" to "web.uievents.KeyboardEvent", 46 | 47 | "Document" to "web.dom.Document", 48 | "Node" to "web.dom.Node", 49 | JS_ELEMENT to ELEMENT, 50 | "HTMLElement" to HTML_ELEMENT, 51 | "HTMLInputElement" to "web.html.HTMLInputElement", 52 | "HTMLDivElement" to "web.html.HTMLDivElement", 53 | 54 | "ImageData" to "web.images.ImageData", 55 | "CanvasRenderingContext2D" to "web.canvas.CanvasRenderingContext2D", 56 | 57 | JS_SVG_ELEMENT to SVG_ELEMENT, 58 | JS_SVG_DEFS_ELEMENT to "web.svg.SVGDefsElement", 59 | "SVGGElement" to "web.svg.SVGGElement", 60 | "SVGImageElement" to "web.svg.SVGImageElement", 61 | "SVGPathElement" to "web.svg.SVGPathElement", 62 | "SVGTextElement" to "web.svg.SVGTextElement", 63 | JS_SVG_SVG_ELEMENT to SVG_SVG_ELEMENT, 64 | 65 | "WebGLProgram" to "web.gl.WebGLProgram", 66 | "WebGLRenderingContext" to "web.gl.WebGLRenderingContext", 67 | "WebGL2RenderingContext" to WEBGL2_RENDERING_CONTEXT, 68 | 69 | JS_BLOB to BLOB, 70 | 71 | "Promise" to PROMISE, 72 | "PromiseResult" to PROMISE_RESULT, 73 | JS_CLASS to "kotlin.js.JsClass" 74 | ) 75 | 76 | val STANDARD_IMPORTED_TYPES = STANDARD_TYPE_MAP 77 | .values 78 | .asSequence() 79 | .filter { "." in it } 80 | .plus(READ_ONLY_PROPERTY) 81 | .plus(READ_WRITE_PROPERTY) 82 | .plus(KCLASS) 83 | .plus(KPROPERTY) 84 | .toSet() 85 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/Strings.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | internal fun String.between( 4 | start: String, 5 | end: String, 6 | firstEnd: Boolean = false, 7 | ): String { 8 | val startIndex = indexOf(start) 9 | require(startIndex != -1) 10 | { "String '$this' doesn't contain '$start'" } 11 | 12 | val endIndex = if (firstEnd) { 13 | indexOf(end) 14 | } else { 15 | lastIndexOf(end) 16 | } 17 | require(endIndex != -1) 18 | { "String '$this' doesn't contain '$end'" } 19 | 20 | return substring(startIndex + start.length, endIndex) 21 | } 22 | 23 | fun till(str: String, end: String): String { 24 | val endIndex = str.indexOf(end) 25 | require(endIndex != -1) 26 | { "String '$str' doesn't contain '$end'" } 27 | 28 | return str.substring(0, endIndex) 29 | } 30 | 31 | @Suppress("NOTHING_TO_INLINE") 32 | inline fun exp(condition: Boolean, str: String): String { 33 | return if (condition) str else "" 34 | } 35 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/YSettings.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles 2 | 3 | const val YOBJECT = "yfiles.lang.YObject" 4 | 5 | private const val YOBJECT_CLASS = "yfiles.lang.Object" 6 | 7 | private val PRIMITIVE_CLASSES = setOf( 8 | "yfiles.lang.Boolean", 9 | "yfiles.lang.Number", 10 | "yfiles.lang.String" 11 | ) 12 | 13 | private val MARKER_CLASSES = setOf( 14 | YOBJECT_CLASS, 15 | "yfiles.lang.EventArgs", 16 | "yfiles.lang.Attribute", 17 | GRAPH_OBJECT 18 | ) 19 | 20 | fun isYObjectClass(className: String): Boolean = 21 | className == YOBJECT_CLASS 22 | 23 | fun isPrimitiveClass(className: String): Boolean = 24 | className in PRIMITIVE_CLASSES 25 | 26 | fun isMarkerClass(className: String): Boolean = 27 | className in MARKER_CLASSES 28 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/BindingHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_ANY 5 | 6 | private val BINDING_LIKE = "yfiles.binding.BindingLike" 7 | 8 | internal fun generateBindingUtils(context: GeneratorContext) { 9 | // language=kotlin 10 | context[BINDING_LIKE] = 11 | """ 12 | external interface BindingLike 13 | 14 | inline fun BindingLike(source:Any):BindingLike = 15 | source.unsafeCast<$BINDING_LIKE>() 16 | """.trimIndent() 17 | } 18 | 19 | internal fun applyBindingHacks(source: Source) { 20 | source.types( 21 | "GraphBuilder", 22 | "TreeBuilder" 23 | ).flatMap(PROPERTIES) 24 | .filter { it[NAME].endsWith("Binding") } 25 | .filter { it[TYPE] == JS_ANY } 26 | .forEach { it[TYPE] = BINDING_LIKE } 27 | } 28 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/BusinessObjectHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_ANY 5 | import com.github.turansky.yfiles.JS_OBJECT 6 | 7 | private const val BUSINESS_OBJECT = "yfiles.binding.BusinessObject" 8 | 9 | internal fun generateBusinessObjectUtils(context: GeneratorContext) { 10 | // language=kotlin 11 | context[BUSINESS_OBJECT] = "typealias BusinessObject = Any" 12 | } 13 | 14 | internal fun applyBusinessObjectHacks(source: Source) { 15 | source.types() 16 | .filter { it[ID].let { it.startsWith("yfiles.binding.") && it.endsWith("Builder") } } 17 | .forEach { 18 | it.flatMap(PROPERTIES) 19 | .filter { it[NAME].endsWith("Source") } 20 | .filter { it[TYPE] == JS_ANY } 21 | .forEach { it[TYPE] = BUSINESS_OBJECT } 22 | 23 | it.flatMap(METHODS) 24 | .optFlatMap(PARAMETERS) 25 | .filter { it[NAME].run { endsWith("Object") || endsWith("Data") } } 26 | .filter { it[TYPE] == JS_OBJECT } 27 | .forEach { it[TYPE] = BUSINESS_OBJECT } 28 | 29 | it.flatMap(METHODS) 30 | .filter { it[NAME] == "getBusinessObject" } 31 | .forEach { it[RETURNS][TYPE] = BUSINESS_OBJECT } 32 | 33 | it.flatMap(EVENTS) 34 | .eventListeners() 35 | .map { it.firstParameter } 36 | .filter { "GraphBuilderItemEventArgs" in it[SIGNATURE] } 37 | .forEach { it.replaceInSignature(",$JS_OBJECT>>", ",$BUSINESS_OBJECT>>") } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/CanvasObjectInstallerHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.* 4 | import org.json.JSONObject 5 | 6 | internal fun applyCanvasObjectInstallerHacks(source: Source) { 7 | source.type("ICanvasObjectInstaller") 8 | .method("create") 9 | .get(RETURNS) 10 | .addGeneric("T") 11 | 12 | source.types() 13 | .filter { it[ID].run { startsWith("yfiles.view.") && endsWith("Installer") } } 14 | .filter { it[GROUP] == "interface" } 15 | .onEach { 16 | it.setSingleTypeParameter( 17 | name = if (it[ID] == ICANVAS_OBJECT_INSTALLER) "in T" else "T", 18 | bound = IMODEL_ITEM 19 | ) 20 | } 21 | .onEach { it.fixUserObjectType("T") } 22 | .filter { it.has(IMPLEMENTS) } 23 | .onEach { require(it[IMPLEMENTS].single() == ICANVAS_OBJECT_INSTALLER) } 24 | .forEach { it[IMPLEMENTS] = arrayOf("$ICANVAS_OBJECT_INSTALLER") } 25 | 26 | source.types() 27 | .filter { it[ID].run { startsWith("yfiles.graph.") && endsWith("Decorator") } } 28 | .optFlatMap(PROPERTIES) 29 | .filter { it[TYPE].endsWith("Installer>") } 30 | .forEach { 31 | it[TYPE] = it[TYPE].run { 32 | val typeParameter = this.between("<", ",") 33 | replace(">", "<$typeParameter>>") 34 | } 35 | } 36 | 37 | source.types() 38 | .filter { it[ID].run { startsWith("yfiles.view.") && endsWith("Installer") } } 39 | .filter { it[GROUP] == "class" } 40 | .forEach { 41 | val name = it[NAME] 42 | val typeParameter = when { 43 | name.startsWith("Node") -> INODE 44 | name.startsWith("Edge") -> IEDGE 45 | name.startsWith("Port") -> IPORT 46 | name.startsWith("Label") -> ILABEL 47 | 48 | else -> { 49 | it.setSingleTypeParameter(bound = IMODEL_ITEM) 50 | "T" 51 | } 52 | } 53 | 54 | if (it.has(IMPLEMENTS)) { 55 | it[IMPLEMENTS] = it[IMPLEMENTS].map { "$it<$typeParameter>" } 56 | } 57 | 58 | it.fixUserObjectType(typeParameter) 59 | } 60 | 61 | source.types() 62 | .filter { it[ID].run { startsWith("yfiles.view.") && endsWith("Manager") } } 63 | .optFlatMap(METHODS) 64 | .filter { it[NAME] == "getInstaller" } 65 | .forEach { it[RETURNS].addGeneric(it.firstParameter[TYPE]) } 66 | } 67 | 68 | private val ITEM_NAMES = setOf( 69 | "item", 70 | "userObject" 71 | ) 72 | 73 | private fun JSONObject.fixUserObjectType(type: String) { 74 | optFlatMap(METHODS) 75 | .optFlatMap(PARAMETERS) 76 | .filter { it[NAME] in ITEM_NAMES } 77 | .onEach { it.changeNullability(false) } 78 | .forEach { it[TYPE] = type } 79 | } 80 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ClipboardHelperHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.ICLIPBOARD_HELPER 4 | import com.github.turansky.yfiles.IMODEL_ITEM 5 | import com.github.turansky.yfiles.JS_ANY 6 | 7 | private const val T = "T" 8 | private const val D = "D" 9 | 10 | internal fun applyClipboardHelperHacks(source: Source) { 11 | source.type("IClipboardHelper") { 12 | set( 13 | TYPE_PARAMETERS, 14 | arrayOf( 15 | typeParameter("in $T", IMODEL_ITEM), 16 | typeParameter(D) 17 | ) 18 | ) 19 | 20 | flatMap(METHODS) 21 | .flatMap(PARAMETERS) 22 | .forEach { 23 | when (it[NAME]) { 24 | "item" -> it[TYPE] = T 25 | "userData" -> { 26 | it[TYPE] = D 27 | it.changeNullability(false) 28 | } 29 | } 30 | } 31 | 32 | flatMap(METHODS) 33 | .filter { it.has(RETURNS) } 34 | .filter { it[RETURNS][TYPE] == JS_ANY } 35 | .forEach { 36 | it[RETURNS][TYPE] = D 37 | it.changeNullability(false) 38 | } 39 | } 40 | 41 | fixDecoratorProperties(source, ICLIPBOARD_HELPER, true) 42 | 43 | source.type("GraphClipboard") 44 | .method("getClipboardHelper") 45 | .apply { 46 | setSingleTypeParameter(bound = IMODEL_ITEM) 47 | firstParameter[TYPE] = "T" 48 | get(RETURNS).addGeneric("T,*") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/CloneableHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.ICLONEABLE 4 | import org.json.JSONObject 5 | 6 | internal fun applyCloneableHacks(source: Source) { 7 | fixClass(source) 8 | 9 | fixImplementedType(source) 10 | } 11 | 12 | private fun fixClass(source: Source) { 13 | source.type("ICloneable").apply { 14 | setSingleTypeParameter(name = "out T", bound = "$ICLONEABLE") 15 | method("clone") 16 | .get(RETURNS) 17 | .set(TYPE, "T") 18 | } 19 | } 20 | 21 | private fun fixImplementedType(source: Source) { 22 | source.types() 23 | .filter { it.has(IMPLEMENTS) } 24 | .forEach { type -> 25 | type[IMPLEMENTS].apply { 26 | val index = indexOf(ICLONEABLE) 27 | if (index != -1) { 28 | if (type.hasCloneableSuperType(source)) { 29 | remove(index) 30 | } else { 31 | val typeId = type[ID] 32 | put(index, "$ICLONEABLE<$typeId>") 33 | } 34 | } 35 | } 36 | } 37 | 38 | source.types() 39 | .filter { it.has(METHODS) } 40 | .filterNot { it[ID] == ICLONEABLE } 41 | .forEach { type -> 42 | type.flatMap(METHODS) 43 | .filter { it[NAME] == "clone" } 44 | .filterNot { it.has(PARAMETERS) } 45 | .map { it[RETURNS] } 46 | .forEach { 47 | val returnType = type[ID] + 48 | if (type.has(TYPE_PARAMETERS)) { 49 | "<" + type[TYPE_PARAMETERS].getJSONObject(0)[NAME] + ">" 50 | } else "" 51 | 52 | it[TYPE] = returnType 53 | } 54 | } 55 | } 56 | 57 | private fun JSONObject.hasCloneableSuperType(source: Source): Boolean { 58 | if (get(ID) == "yfiles.tree.DefaultNodePlacer") { 59 | return true 60 | } 61 | 62 | if (!has(IMPLEMENTS)) { 63 | return false 64 | } 65 | 66 | return get(IMPLEMENTS) 67 | .asSequence() 68 | .map { it as String } 69 | .map { it.toClassName() } 70 | .map { source.type(it) } 71 | .filter { it.has(IMPLEMENTS) } 72 | .map { it[IMPLEMENTS] } 73 | .flatMap { it.asSequence() } 74 | .map { it as String } 75 | .any { it.startsWith(ICLONEABLE) } 76 | } 77 | 78 | private fun String.toClassName(): String = 79 | when (this) { 80 | "yfiles.hierarchic.INodePlacer" -> "IHierarchicLayoutNodePlacer" 81 | "yfiles.tree.INodePlacer" -> "ITreeLayoutNodePlacer" 82 | else -> substringAfterLast(".") 83 | } 84 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/CollectionHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.ICOLLECTION 4 | import com.github.turansky.yfiles.JS_ANY 5 | import com.github.turansky.yfiles.JS_OBJECT 6 | import org.json.JSONObject 7 | 8 | private fun collection(generic: String): String = 9 | "$ICOLLECTION<$generic>" 10 | 11 | private val DEFAULT_COLLECTIONS = setOf( 12 | collection(JS_ANY), 13 | collection(JS_OBJECT) 14 | ) 15 | 16 | internal fun applyCollectionHacks(source: Source) { 17 | source.types( 18 | "PathRequest", 19 | "IEdgeData" 20 | ).flatMap { 21 | sequenceOf( 22 | it.property("sourcePortCandidates"), 23 | it.property("targetPortCandidates") 24 | ) 25 | }.forEach { it.fixTypeGeneric("yfiles.layout.PortCandidate") } 26 | 27 | source.type("PartitionGrid") 28 | .flatMap(METHODS) 29 | .single { it[ID] == "PartitionGrid-method-createCellSpanId(yfiles.collections.ICollection,yfiles.collections.ICollection)" } 30 | .apply { 31 | firstParameter.fixTypeGeneric("yfiles.layout.RowDescriptor") 32 | secondParameter.fixTypeGeneric("yfiles.layout.ColumnDescriptor") 33 | } 34 | } 35 | 36 | private fun JSONObject.fixTypeGeneric(generic: String) { 37 | require(get(TYPE) in DEFAULT_COLLECTIONS) 38 | 39 | set(TYPE, collection(generic)) 40 | } 41 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ComparableHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.ICOMPARABLE 4 | import org.json.JSONObject 5 | 6 | internal fun applyComparableHacks(source: Source) { 7 | source.type("IComparable") { 8 | setSingleTypeParameter(bound = "IComparable") 9 | configureCompareTo("T") 10 | } 11 | 12 | source.types( 13 | "ColumnDescriptor", 14 | "PartitionCellIdEntry", 15 | "RowDescriptor", 16 | "SnapResult", 17 | "SwimlaneDescriptor", 18 | "TimeSpan", 19 | "YDimension", 20 | "YPoint" 21 | ) 22 | .forEach { 23 | val id = it[ID] 24 | 25 | val implements = it[IMPLEMENTS] 26 | implements.put( 27 | implements.indexOf(ICOMPARABLE), 28 | "$ICOMPARABLE<$id>" 29 | ) 30 | 31 | it.configureCompareTo(id) 32 | } 33 | 34 | source.types( 35 | "IComparer", 36 | "DefaultOutEdgeComparer", 37 | "NodeOrderComparer", 38 | "NodeWeightComparer" 39 | ).flatMap(METHODS) 40 | .filter { it[NAME] == "compare" } 41 | .flatMap(PARAMETERS) 42 | .forEach { it.changeNullability(false) } 43 | } 44 | 45 | private fun JSONObject.configureCompareTo(type: String) { 46 | flatMap(METHODS) 47 | .filter { it[NAME] == "compareTo" } 48 | .singleOrNull() 49 | ?.apply { 50 | firstParameter.apply { 51 | set(NAME, "other") 52 | set(TYPE, type) 53 | changeNullability(false) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ConvertersHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.* 4 | 5 | private const val CONVERTERS = "yfiles.styles.Converters" 6 | 7 | internal fun generateConvertersUtils(context: GeneratorContext) { 8 | // language=kotlin 9 | context[CONVERTERS] = 10 | """ 11 | @JsName("Object") 12 | external class Converters 13 | private constructor() 14 | 15 | inline operator fun Converters.invoke( 16 | block: Converters.() -> Unit 17 | ): Converters = 18 | apply(block) 19 | 20 | fun Converters.put( 21 | name: String, 22 | converter: (value: V) -> R 23 | ): Converters { 24 | $AS_DYNAMIC[name] = converter 25 | return this 26 | } 27 | 28 | fun Converters.put( 29 | name: String, 30 | converter: (value: V, parameter: P) -> R 31 | ): Converters { 32 | $AS_DYNAMIC[name] = converter 33 | return this 34 | } 35 | """.trimIndent() 36 | } 37 | 38 | internal fun applyConvertersHacks(source: Source) { 39 | val likeObjectTypes = setOf( 40 | JS_OBJECT, 41 | JS_ANY 42 | ) 43 | 44 | source.type(TEMPLATES_NAME) 45 | .flatMap(CONSTANTS) 46 | .first { it[NAME] == "CONVERTERS" } 47 | .also { check(it[TYPE] in likeObjectTypes) } 48 | .set(TYPE, CONVERTERS) 49 | } 50 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/CreationPropertyHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.* 4 | import com.github.turansky.yfiles.json.get 5 | import com.github.turansky.yfiles.json.strictRemove 6 | 7 | private const val CREATION_PROPERTY_KEY = "yfiles.graphml.CreationPropertyKey" 8 | 9 | private fun propertyKey(typeParameter: String) = 10 | "$CREATION_PROPERTY_KEY<$typeParameter>" 11 | 12 | internal fun generateCreationPropertyUtils(context: GeneratorContext) { 13 | // language=kotlin 14 | context[CREATION_PROPERTY_KEY] = """ 15 | @JsName("String") 16 | external class CreationPropertyKey 17 | private constructor() 18 | 19 | inline fun CreationPropertyKey(source: String): CreationPropertyKey = 20 | source.unsafeCast>() 21 | """.trimIndent() 22 | } 23 | 24 | internal fun applyCreationPropertyHacks(source: Source) { 25 | val typeMap = mapOf( 26 | "BENDS" to "$IENUMERABLE<*>", 27 | "IS_GROUP_NODE" to JS_BOOLEAN, 28 | "LABELS" to "$IENUMERABLE<*>", 29 | "LAYOUT" to "yfiles.geometry.Rect", 30 | "PORT_LOCATION_MODEL_PARAMETER" to "yfiles.graph.IPortLocationModelParameter", 31 | "STYLE" to JS_ANY, 32 | "TAG" to TAG 33 | ) 34 | 35 | source.type("CreationProperties").apply { 36 | strictRemove(IMPLEMENTS) 37 | 38 | get(PROPERTIES)["entries"].also { 39 | it.replaceInType("<$JS_ANY,", "<${propertyKey("*")},") 40 | } 41 | 42 | flatMap(METHODS) 43 | .filter { it.has(PARAMETERS) } 44 | .forEach { 45 | val typeParameter = when (it[NAME]) { 46 | "removeValue" -> "*" 47 | else -> { 48 | it.setSingleTypeParameter(bound = JS_OBJECT) 49 | "T" 50 | } 51 | } 52 | 53 | if (it[NAME] == "get") { 54 | it[RETURNS][TYPE] = "T" 55 | } 56 | 57 | it.flatMap(PARAMETERS) 58 | .forEach { 59 | it[TYPE] = when (it[NAME]) { 60 | "key" -> propertyKey(typeParameter) 61 | "value" -> typeParameter 62 | else -> TODO() 63 | } 64 | } 65 | } 66 | 67 | flatMap(CONSTANTS) 68 | .forEach { it[TYPE] = propertyKey(typeMap.getValue(it[NAME])) } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/DataTagHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | 5 | internal const val DATA_TAG = "yfiles.graph.DataTag" 6 | 7 | internal fun generateDataTagUtils(context: GeneratorContext) { 8 | // language=kotlin 9 | context[DATA_TAG] = 10 | """ 11 | external interface DataTag 12 | 13 | inline fun DataTag(source:String):DataTag = 14 | source.unsafeCast>() 15 | """.trimIndent() 16 | } 17 | 18 | internal fun applyDataTagHacks(source: Source) { 19 | val tagNames = setOf( 20 | "tag", 21 | "registryTag" 22 | ) 23 | 24 | val typeParameterMap = mapOf( 25 | "createMapper" to "TData", 26 | "addRegistryInputMapper" to "TData", 27 | "addRegistryOutputMapper" to "TValue" 28 | ) 29 | 30 | source.type("GraphMLIOHandler") 31 | .flatMap(METHODS) 32 | .forEach { method -> 33 | method.optFlatMap(PARAMETERS) 34 | .filter { it[NAME] in tagNames } 35 | .forEach { 36 | val typeParameter = when { 37 | method.has(TYPE_PARAMETERS) -> typeParameterMap.getValue(method[NAME]) 38 | else -> "*" 39 | } 40 | 41 | it[TYPE] = "$DATA_TAG<*, $typeParameter>" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/Decorators.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.between 4 | 5 | internal fun fixDecoratorProperties( 6 | source: Source, 7 | type: String, 8 | addExtraTypeParameter: Boolean = false, 9 | ) { 10 | source.types() 11 | .filter { it[ID].run { startsWith("yfiles.graph.") && endsWith("Decorator") } } 12 | .optFlatMap(PROPERTIES) 13 | .filter { it[TYPE].endsWith("$type>") } 14 | .forEach { 15 | var typeParameters = it[TYPE].between("<", ",") 16 | if (addExtraTypeParameter) { 17 | typeParameters += ",*" 18 | } 19 | it.replaceInType(">", "<$typeParameters>>") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/DragDropDataHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | 5 | private const val DRAG_DROP_DATA = "yfiles.view.DragDropData" 6 | 7 | internal fun generateDragDropData(context: GeneratorContext) { 8 | // language=kotlin 9 | context[DRAG_DROP_DATA] = "external interface DragDropData" 10 | } 11 | 12 | internal fun applyDragDropDataHacks(source: Source) { 13 | source.type("DragDropItem") { 14 | (flatMap(CONSTRUCTORS) + flatMap(METHODS)) 15 | .flatMap(PARAMETERS) 16 | .filter { it[NAME] == "data" } 17 | .plus(method("getData")[RETURNS]) 18 | .forEach { it[TYPE] = DRAG_DROP_DATA } 19 | } 20 | 21 | source.type("DropInputMode") 22 | .property("dropData")[TYPE] = DRAG_DROP_DATA 23 | 24 | source.functionSignature("yfiles.input.DropCreationCallback") 25 | .parameter("dropData")[TYPE] = DRAG_DROP_DATA 26 | 27 | source.type("DragSource") 28 | .property("item")[TYPE] = "DragDropItem" 29 | } 30 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/EdgeDirectednessHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_NUMBER 5 | 6 | internal const val EDGE_DIRECTEDNESS = "yfiles.algorithms.EdgeDirectedness" 7 | 8 | internal fun generateEdgeDirectednessUtils(context: GeneratorContext) { 9 | context[EDGE_DIRECTEDNESS] = """ 10 | @JsName("Number") 11 | external class EdgeDirectedness 12 | private constructor() 13 | 14 | inline fun EdgeDirectedness(value: Double): $EDGE_DIRECTEDNESS = 15 | value.unsafeCast<$EDGE_DIRECTEDNESS>() 16 | 17 | object EdgeDirectednesses { 18 | inline val SOURCE_TO_TARGET: $EDGE_DIRECTEDNESS 19 | get() = EdgeDirectedness(1.0) 20 | inline val TARGET_TO_SOURCE: $EDGE_DIRECTEDNESS 21 | get() = EdgeDirectedness(-1.0) 22 | inline val UNDIRECTED: $EDGE_DIRECTEDNESS 23 | get() = EdgeDirectedness(0.0) 24 | } 25 | """.trimIndent() 26 | } 27 | 28 | internal fun applyEdgeDirectednessHacks(source: Source) { 29 | source.types() 30 | .optFlatMap(PROPERTIES) 31 | .filter { it[NAME] == "edgeDirectedness" } 32 | .forEach { it.replaceInType(",$JS_NUMBER>", ",$EDGE_DIRECTEDNESS>") } 33 | 34 | source.types() 35 | .optFlatMap(CONSTANTS) 36 | .filter { it[NAME] == "EDGE_DIRECTEDNESS_DP_KEY" } 37 | .forEach { it.replaceInType("<$JS_NUMBER>", "<$EDGE_DIRECTEDNESS>") } 38 | } 39 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ElementIdHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_STRING 5 | 6 | private const val ELEMENT_ID = "yfiles.graphml.ElementId" 7 | 8 | internal fun generateElementIdUtils(context: GeneratorContext) { 9 | // language=kotlin 10 | context[ELEMENT_ID] = """ 11 | @JsName("String") 12 | external class ElementId 13 | private constructor() 14 | 15 | inline fun ElementId(source:String):ElementId = 16 | source.unsafeCast() 17 | """.trimIndent() 18 | } 19 | 20 | internal fun applyElementIdHacks(source: Source) { 21 | source.types() 22 | .filter { it[ID].startsWith("yfiles.graphml") } 23 | .forEach { 24 | it.optFlatMap(PROPERTIES) 25 | .filter { it[NAME].let { it == "id" || it.endsWith("Id") } } 26 | .filter { it[TYPE] == JS_STRING } 27 | .forEach { it[TYPE] = ELEMENT_ID } 28 | 29 | it.optFlatMap(METHODS) 30 | .filter { it.has(RETURNS) } 31 | .filter { it[NAME].endsWith("Id") } 32 | .filter { it[RETURNS][TYPE] == JS_STRING } 33 | .forEach { it[RETURNS][TYPE] = ELEMENT_ID } 34 | 35 | it.optFlatMap(METHODS) 36 | .plus(it.optFlatMap(CONSTRUCTORS)) 37 | .optFlatMap(PARAMETERS) 38 | .filter { it[NAME].let { it == "id" || it.endsWith("Id") } } 39 | .filter { it[TYPE] == JS_STRING } 40 | .forEach { it[TYPE] = ELEMENT_ID } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/EventDispatcherHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.IEVENT_DISPATCHER 5 | import com.github.turansky.yfiles.JS_ANY 6 | import com.github.turansky.yfiles.JS_OBJECT 7 | import org.json.JSONObject 8 | 9 | internal fun generateEventDispatcherUtils(context: GeneratorContext) { 10 | // language=kotlin 11 | context[IEVENT_DISPATCHER] = "external interface IEventDispatcher" 12 | } 13 | 14 | internal fun applyEventDispatcherHacks(source: Source) { 15 | source.types() 16 | .filter { it.has(EVENTS) || it[NAME] == "ItemModelManager" } 17 | .filterNot { it.hasParentDispatcher(source) } 18 | .forEach { 19 | if (it.has(IMPLEMENTS)) { 20 | it[IMPLEMENTS].put(IEVENT_DISPATCHER) 21 | } else { 22 | it[IMPLEMENTS] = arrayOf(IEVENT_DISPATCHER) 23 | } 24 | } 25 | 26 | val parameterNames = setOf( 27 | "sender", 28 | "source" 29 | ) 30 | 31 | val likeObjectTypes = setOf( 32 | "this", 33 | JS_OBJECT, 34 | JS_ANY 35 | ) 36 | 37 | source.types() 38 | .optFlatMap(METHODS) 39 | .optFlatMap(PARAMETERS) 40 | .filter { it[NAME] in parameterNames } 41 | .filter { it[TYPE] in likeObjectTypes } 42 | .forEach { it[TYPE] = IEVENT_DISPATCHER } 43 | 44 | source.functionSignatures.apply { 45 | keys().asSequence() 46 | .map { getJSONObject(it) } 47 | .optFlatMap(PARAMETERS) 48 | .filter { it[NAME] in parameterNames } 49 | .filter { it[TYPE] in likeObjectTypes } 50 | .forEach { it[TYPE] = IEVENT_DISPATCHER } 51 | } 52 | } 53 | 54 | private fun JSONObject.hasParentDispatcher( 55 | source: Source, 56 | ): Boolean { 57 | if (has(EXTENDS)) { 58 | val extends = getType(get(EXTENDS), source) 59 | if (extends.has(EVENTS) || extends.hasParentDispatcher(source)) { 60 | return true 61 | } 62 | } 63 | 64 | val implements = opt(IMPLEMENTS) 65 | ?: return false 66 | 67 | if (IEVENT_DISPATCHER in implements) { 68 | return true 69 | } 70 | 71 | return implements.asSequence() 72 | .map { it as String } 73 | .filterNot { it == IEVENT_DISPATCHER } 74 | .map { getType(it, source) } 75 | .any { it.has(EVENTS) || it.hasParentDispatcher(source) } 76 | } 77 | 78 | private fun getType( 79 | classId: String, 80 | source: Source, 81 | ): JSONObject { 82 | val className = classId 83 | .substringBefore("<") 84 | .substringAfterLast(".") 85 | 86 | return source.type(className) 87 | } 88 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/EventHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import org.json.JSONObject 4 | 5 | internal fun applyEventHacks(source: Source) { 6 | source.types() 7 | .flatMap { it.optFlatMap(CONSTRUCTORS) + it.optFlatMap(METHODS) } 8 | .plus(source.functionSignatures.run { keySet().map { getJSONObject(it) } }) 9 | .optFlatMap(PARAMETERS) 10 | .forEach { 11 | val name = it.getNewName() 12 | ?: return@forEach 13 | 14 | it[NAME] = name 15 | } 16 | 17 | fixMouseEventArgsConstructor(source) 18 | } 19 | 20 | fun JSONObject.getNewName(): String? = 21 | when (get(NAME)) { 22 | "evt" -> "event" 23 | "args" -> if (get(TYPE).endsWith("Args")) { 24 | "event" 25 | } else { 26 | null 27 | } 28 | 29 | "src", "eventSource" -> "source" 30 | 31 | else -> null 32 | } 33 | 34 | private fun fixMouseEventArgsConstructor(source: Source) { 35 | val nameFixMap = mapOf( 36 | "mouseWheelDelta" to "wheelDelta", 37 | "mouseWheelDeltaX" to "wheelDeltaX", 38 | "scrollType" to "deltaMode" 39 | ) 40 | 41 | source.type("MouseEventArgs") 42 | .flatMap(CONSTRUCTORS) 43 | .flatMap(PARAMETERS) 44 | .filter { nameFixMap.containsKey(it[NAME]) } 45 | .forEach { it[NAME] = nameFixMap.getValue(it[NAME]) } 46 | } 47 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/Exclusions.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.json.removeAllObjects 4 | import org.json.JSONObject 5 | 6 | private val EXCLUDED_TYPES = setOf( 7 | "yfiles.lang.Abstract", 8 | "yfiles.lang.Struct", 9 | 10 | "yfiles.lang.AttributeDefinition", 11 | 12 | "yfiles.lang.EnumDefinition", 13 | 14 | "yfiles.lang.Interface", 15 | "yfiles.lang.InterfaceDefinition", 16 | 17 | "yfiles.lang.ClassDefinition", 18 | "yfiles.lang.PropertyInfo", 19 | 20 | "yfiles.lang.delegate", 21 | "yfiles.lang.Exception", 22 | "yfiles.lang.Trait", 23 | 24 | "yfiles.styles.BevelNodeStyle", 25 | "yfiles.styles.BevelNodeStyleRenderer", 26 | "yfiles.styles.PanelNodeStyle", 27 | "yfiles.styles.PanelNodeStyleRenderer", 28 | "yfiles.styles.ShinyPlateNodeStyle", 29 | "yfiles.styles.ShinyPlateNodeStyleRenderer", 30 | ) 31 | 32 | internal fun excludeUnusedTypes(api: JSONObject) { 33 | api[TYPES].removeAllObjects { 34 | it[ID] in EXCLUDED_TYPES 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ExtensionHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.ABSTRACT 4 | 5 | internal fun applyExtensionHacks(source: Source) { 6 | source.type("IFoldingView") 7 | .property("manager") 8 | .get(MODIFIERS) 9 | .put(ABSTRACT) 10 | } 11 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/FlagsHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.YFLAGS 5 | 6 | internal fun generateFlagsUtils(context: GeneratorContext) { 7 | // language=kotlin 8 | context[YFLAGS] = 9 | """ 10 | external interface YFlags> 11 | 12 | inline val YFlags<*>.value:Int 13 | get() = unsafeCast() 14 | 15 | inline infix fun > T.or(other: T): T = 16 | (value or other.value).unsafeCast() 17 | 18 | inline operator fun > T.contains(other: T): Boolean = 19 | (value and other.value) == other.value 20 | """.trimIndent() 21 | } 22 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/IdHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.* 4 | import com.github.turansky.yfiles.json.get 5 | import org.json.JSONObject 6 | 7 | internal fun generateIdUtils(context: GeneratorContext) { 8 | // language=kotlin 9 | context[YID] = """ 10 | external interface Id: $YOBJECT 11 | 12 | inline fun Id(source:Any):Id = 13 | source.unsafeCast() 14 | """.trimIndent() 15 | } 16 | 17 | private val ID_DP_KEYS = setOf( 18 | edgeDpKey(JS_ANY), 19 | nodeDpKey(JS_ANY), 20 | 21 | "$IEDGE_LABEL_LAYOUT_DP_KEY<$JS_ANY>", 22 | "$INODE_LABEL_LAYOUT_DP_KEY<$JS_ANY>" 23 | ) 24 | 25 | internal fun applyIdHacks(source: Source) { 26 | source.types() 27 | .flatMap { it.optFlatMap(CONSTANTS) } 28 | .filter { it[TYPE] in ID_DP_KEYS } 29 | .filter { it[NAME].let { it.endsWith("_ID_DP_KEY") || it == "CUSTOM_GROUPS_DP_KEY" } } 30 | .forEach { 31 | val newType = it[TYPE] 32 | .replace("<$JS_ANY>", "<$YID>") 33 | 34 | it[TYPE] = newType 35 | } 36 | 37 | val likeObjectTypes = setOf( 38 | JS_OBJECT, 39 | JS_ANY 40 | ) 41 | 42 | typedItems(source) 43 | .filter { looksLikeId(it[NAME]) } 44 | .filter { it[TYPE] in likeObjectTypes } 45 | .forEach { it[TYPE] = YID } 46 | 47 | typedItems(source) 48 | .filter { it[NAME].let { it.endsWith("Ids") || it == "customGroups" } } 49 | .forEach { 50 | val newType = it[TYPE] 51 | .replace(",$JS_ANY>", ",$YID>") 52 | 53 | it[TYPE] = newType 54 | } 55 | 56 | source.type("IEdgeData")[PROPERTIES].also { properties -> 57 | sequenceOf( 58 | "group", 59 | 60 | "sourceGroup", 61 | "targetGroup", 62 | 63 | "sourcePortGroup", 64 | "targetPortGroup" 65 | ).forEach { properties[it][TYPE] = YID } 66 | } 67 | 68 | source.types("GraphClipboard", "IClipboardIdProvider") 69 | .map { it.method("getId") } 70 | .forEach { it[RETURNS][TYPE] = YID } 71 | 72 | source.type("BusRouterBusDescriptor") 73 | .flatMap(CONSTRUCTORS) 74 | .flatMap(PARAMETERS) 75 | .forEach { 76 | val name = it[NAME] 77 | if (name.endsWith("ID")) { 78 | it[NAME] = name.replace("ID", "Id") 79 | } 80 | } 81 | 82 | source.type("ChannelRoutingTool") 83 | .flatMap(METHODS) 84 | .optFlatMap(PARAMETERS) 85 | .filter { it[NAME] == "key" } 86 | .filter { it[TYPE] in likeObjectTypes } 87 | .forEach { it[TYPE] = YID } 88 | } 89 | 90 | private fun looksLikeId(name: String): Boolean = 91 | name == "id" || name.endsWith("Id") || name.endsWith("ID") 92 | 93 | private fun typedItems(source: Source): Sequence = 94 | source.types() 95 | .flatMap { 96 | (it.optFlatMap(CONSTRUCTORS) + it.optFlatMap(METHODS)) 97 | .optFlatMap(PARAMETERS) 98 | .plus(it.optFlatMap(PROPERTIES)) 99 | } 100 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/IncrementalHintHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_ANY 5 | 6 | internal const val INCREMENTAL_HINT = "yfiles.hierarchic.IncrementalHint" 7 | 8 | internal fun generateIncrementalHint(context: GeneratorContext) { 9 | // language=kotlin 10 | context[INCREMENTAL_HINT] = """ 11 | @JsName("Object") 12 | external class IncrementalHint 13 | private constructor() 14 | """.trimIndent() 15 | } 16 | 17 | internal fun applyIncrementalHintHacks(source: Source) { 18 | source.type("IIncrementalHintsFactory") 19 | .flatMap(METHODS) 20 | .forEach { it[RETURNS][TYPE] = INCREMENTAL_HINT } 21 | 22 | source.type("IncrementalHintItemMapping").also { 23 | it[EXTENDS] = it[EXTENDS].replace(",$JS_ANY,", ",$INCREMENTAL_HINT,") 24 | } 25 | 26 | source.types("HierarchicLayout", "HierarchicLayoutCore") 27 | .map { it.constant("INCREMENTAL_HINTS_DP_KEY") } 28 | .forEach { it.replaceInType("<$JS_ANY>", "<$INCREMENTAL_HINT>") } 29 | 30 | source.type("INodeData") 31 | .property("incrementalHint")[TYPE] = INCREMENTAL_HINT 32 | } 33 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/IntersectionHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.json.get 4 | 5 | private const val IPLANE_OBJECT = "yfiles.algorithms.IPlaneObject" 6 | 7 | internal fun applyIntersectionHacks(source: Source) { 8 | source.type("IntersectionAlgorithm")[METHODS]["intersect"].apply { 9 | setSingleTypeParameter(bound = IPLANE_OBJECT) 10 | 11 | flatMap(PARAMETERS) 12 | .forEach { it.addGeneric("T") } 13 | } 14 | 15 | source.type("IIntersectionHandler") { 16 | setSingleTypeParameter(bound = IPLANE_OBJECT) 17 | 18 | method("checkIntersection") 19 | .flatMap(PARAMETERS) 20 | .forEach { it[TYPE] = "T" } 21 | 22 | method("create") 23 | .get(RETURNS) 24 | .addGeneric("T") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/IteratorSupport.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.ContentMode.ITERATOR 4 | import com.github.turansky.yfiles.GeneratorContext 5 | import com.github.turansky.yfiles.ICURSOR 6 | import com.github.turansky.yfiles.IENUMERABLE 7 | import com.github.turansky.yfiles.YOBJECT 8 | 9 | internal fun addIteratorSupport(context: GeneratorContext) { 10 | // language=kotlin 11 | context[IENUMERABLE, ITERATOR] = """ 12 | operator fun IEnumerable.iterator(): Iterator = 13 | EnumeratorIterator(getEnumerator()) 14 | 15 | fun IEnumerable.asSequence(): Sequence = 16 | iterator().asSequence() 17 | 18 | private class EnumeratorIterator( 19 | private val source: IEnumerator 20 | ) : Iterator { 21 | 22 | private var validCurrent = source.moveNext() 23 | 24 | override fun hasNext(): Boolean = 25 | validCurrent 26 | 27 | override fun next(): T { 28 | val result = source.current 29 | validCurrent = source.moveNext() 30 | return result 31 | } 32 | } 33 | """.trimIndent() 34 | 35 | // language=kotlin 36 | context[ICURSOR, ITERATOR] = """ 37 | operator fun ICursor.iterator(): Iterator = 38 | CursorIterator(this) 39 | 40 | fun ICursor.asSequence(): Sequence = 41 | iterator().asSequence() 42 | 43 | private class CursorIterator( 44 | private val source: ICursor 45 | ) : Iterator { 46 | 47 | override fun hasNext(): Boolean = 48 | source.ok 49 | 50 | override fun next(): T { 51 | val result = source.current 52 | source.next() 53 | return result 54 | } 55 | } 56 | """.trimIndent() 57 | } 58 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/LabelModelParameterHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.ILABEL_MODEL_PARAMETER 4 | 5 | internal fun applyLabelModelParameterHacks(source: Source) { 6 | source.types( 7 | "ILabelLayout", 8 | "LabelLayoutBase", 9 | 10 | "LabelCandidate" 11 | ).forEach { it.property("modelParameter")[TYPE] = ILABEL_MODEL_PARAMETER } 12 | 13 | source.types( 14 | "LabelCandidate", 15 | 16 | "EdgeLabelCandidate", 17 | "ExtendedEdgeLabelCandidate", 18 | 19 | "NodeLabelCandidate", 20 | "ExtendedNodeLabelCandidate", 21 | 22 | "LayoutGraphUtilities" 23 | ).flatMap { it.optFlatMap(CONSTRUCTORS) + it.optFlatMap(METHODS) } 24 | .optFlatMap(PARAMETERS) 25 | .filter { it[NAME] == "param" } 26 | .forEach { it[TYPE] = ILABEL_MODEL_PARAMETER } 27 | 28 | source.types( 29 | "IEdgeLabelLayoutModel", 30 | "DiscreteEdgeLabelLayoutModel", 31 | "FreeEdgeLabelLayoutModel", 32 | "SliderEdgeLabelLayoutModel", 33 | 34 | "INodeLabelLayoutModel", 35 | "DiscreteNodeLabelLayoutModel", 36 | "FreeNodeLabelLayoutModel" 37 | ).onEach { it.property("defaultParameter")[TYPE] = ILABEL_MODEL_PARAMETER } 38 | .onEach { it.method("createModelParameter")[RETURNS][TYPE] = ILABEL_MODEL_PARAMETER } 39 | .flatMap(METHODS) 40 | .flatMap(PARAMETERS) 41 | .filter { it[NAME] == "parameter" } 42 | .forEach { it[TYPE] = ILABEL_MODEL_PARAMETER } 43 | 44 | source.type("DiscreteEdgeLabelLayoutModel") 45 | .method("createPositionParameter") 46 | .get(RETURNS)[TYPE] = ILABEL_MODEL_PARAMETER 47 | } 48 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/LayoutDescriptorHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | 5 | internal const val LAYOUT_DESCRIPTOR = "yfiles.layout.LayoutDescriptor" 6 | 7 | internal fun generateLayoutDescriptorUtils(context: GeneratorContext) { 8 | // language=kotlin 9 | context[LAYOUT_DESCRIPTOR] = 10 | """ 11 | external interface LayoutDescriptor 12 | """.trimIndent() 13 | } 14 | 15 | internal fun applyLayoutDescriptorHacks(source: Source) { 16 | source.type("LayoutExecutorAsync").apply { 17 | flatMap(CONSTRUCTORS) 18 | .flatMap(PARAMETERS) 19 | .plus(flatMap(PROPERTIES)) 20 | .filter { it[NAME] == "layoutDescriptor" } 21 | .forEach { it[TYPE] = LAYOUT_DESCRIPTOR } 22 | } 23 | 24 | source.functionSignature("yfiles.layout.WorkerHandler") 25 | .parameter("descriptor") 26 | .set(TYPE, LAYOUT_DESCRIPTOR) 27 | } 28 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/LayoutStrictTypes.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.IENUMERABLE 4 | import com.github.turansky.yfiles.JS_ANY 5 | import com.github.turansky.yfiles.JS_OBJECT 6 | import com.github.turansky.yfiles.YOBJECT 7 | import com.github.turansky.yfiles.json.removeAllObjects 8 | 9 | internal fun applyLayoutStrictTypes(source: Source) { 10 | source.types("CopiedLayoutGraph", "LayoutGraphAdapter") 11 | .flatMap(METHODS) 12 | .flatMap { it.optFlatMap(PARAMETERS) + it.returnsSequence() } 13 | .filter { it[TYPE] == JS_OBJECT } 14 | .forEach { it[TYPE] = YOBJECT } 15 | 16 | source.type("LayoutGraphAdapter") 17 | .let { type -> sequenceOf("nodeObjects", "edgeObjects").map { type.method(it) } } 18 | .map { it[RETURNS] } 19 | .onEach { check(it[TYPE] == "$IENUMERABLE<$JS_ANY>") } 20 | .forEach { it[TYPE] = "$IENUMERABLE<$YOBJECT>" } 21 | 22 | source.type("LayoutGraphUtilities")[METHODS].removeAllObjects { 23 | it[ID] == "LayoutGraphUtilities-method-getBoundingBox(yfiles.layout.LayoutGraph,yfiles.algorithms.Node)" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ListCellHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.EDGE 4 | import com.github.turansky.yfiles.YOBJECT 5 | import org.json.JSONObject 6 | 7 | private const val LIST_CELL = "yfiles.algorithms.ListCell" 8 | 9 | internal fun applyListCellHacks(source: Source) { 10 | source.type("ListCell") { 11 | setSingleTypeParameter(bound = YOBJECT) 12 | 13 | property("info")[TYPE] = "T" 14 | 15 | flatMap(METHODS) 16 | .forEach { it[RETURNS].addGeneric("T") } 17 | } 18 | 19 | source.type("YList") 20 | .getTypeHolders() 21 | .filter { it[TYPE] == LIST_CELL } 22 | .forEach { it.addGeneric("T") } 23 | 24 | source.type("BorderLine") 25 | .flatMap(METHODS) 26 | .filter { it[NAME] == "getValueAt" } 27 | .flatMap(PARAMETERS) 28 | .single { it[TYPE] == LIST_CELL } 29 | .addGeneric("BorderLineSegment") 30 | 31 | source.type("INodeData") 32 | .property("firstSameLayerEdgeCell") 33 | .addGeneric(EDGE) 34 | } 35 | 36 | private fun JSONObject.getTypeHolders(): Sequence = 37 | flatMap(METHODS) 38 | .flatMap { it.optFlatMap(PARAMETERS) + it.returnsSequence() } 39 | .plus(flatMap(PROPERTIES)) 40 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/MementoHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_ANY 5 | import com.github.turansky.yfiles.json.get 6 | 7 | private const val HIERARCHIC_MEMENTOS = "yfiles.hierarchic.Mementos" 8 | private const val TREE_MEMENTOS = "yfiles.tree.Mementos" 9 | 10 | private const val LAYER_CONSTRAINTS_MEMENTO = "yfiles.hierarchic.LayerConstraintsMemento" 11 | private const val SEQUENCE_CONSTRAINTS_MEMENTO = "yfiles.hierarchic.SequenceConstraintsMemento" 12 | 13 | private const val COMPACT_STRATEGY_MEMENTO = "yfiles.tree.CompactStrategyMemento" 14 | 15 | internal fun generateMementoUtils(context: GeneratorContext) { 16 | // language=kotlin 17 | context[HIERARCHIC_MEMENTOS] = """ 18 | @JsName("Object") 19 | external class LayerConstraintsMemento 20 | private constructor() 21 | 22 | @JsName("Object") 23 | external class SequenceConstraintsMemento 24 | private constructor() 25 | """.trimIndent() 26 | 27 | // language=kotlin 28 | context[TREE_MEMENTOS] = """ 29 | @JsName("Object") 30 | external class CompactStrategyMemento 31 | private constructor() 32 | """.trimIndent() 33 | } 34 | 35 | internal fun applyMementoHacks(source: Source) { 36 | source.type("ILayerConstraintFactory") 37 | .property("memento")[TYPE] = LAYER_CONSTRAINTS_MEMENTO 38 | 39 | source.type("ISequenceConstraintFactory") 40 | .property("memento")[TYPE] = SEQUENCE_CONSTRAINTS_MEMENTO 41 | 42 | source.type("HierarchicLayout") 43 | .get(CONSTANTS).apply { 44 | sequenceOf( 45 | "LAYER_CONSTRAINTS_MEMENTO_DP_KEY" to LAYER_CONSTRAINTS_MEMENTO, 46 | "SEQUENCE_CONSTRAINTS_MEMENTO_DP_KEY" to SEQUENCE_CONSTRAINTS_MEMENTO 47 | ).forEach { (name, typeParameter) -> 48 | get(name).also { 49 | require(it[TYPE] == graphDpKey(JS_ANY)) 50 | it[TYPE] = graphDpKey(typeParameter) 51 | } 52 | } 53 | } 54 | 55 | source.type("TreeLayoutData") 56 | .property("compactNodePlacerStrategyMementos") 57 | .also { it.replaceInType(",$JS_ANY>", ",$COMPACT_STRATEGY_MEMENTO>") } 58 | 59 | source.type("CompactNodePlacer") 60 | .constant("STRATEGY_MEMENTO_DP_KEY") 61 | .also { 62 | require(it[TYPE] == nodeDpKey(JS_ANY)) 63 | it[TYPE] = nodeDpKey(COMPACT_STRATEGY_MEMENTO) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/MementoSupportHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.IMEMENTO_SUPPORT 4 | 5 | private const val T = "T" 6 | private const val S = "S" 7 | 8 | internal fun applyMementoSupportHacks(source: Source) { 9 | source.type("IMementoSupport") { 10 | setTypeParameters("in $T", S) 11 | 12 | flatMap(METHODS) 13 | .flatMap(PARAMETERS) 14 | .forEach { 15 | val name = it[NAME] 16 | it[TYPE] = when { 17 | name == "subject" -> T 18 | name.startsWith("state") -> S 19 | else -> TODO() 20 | } 21 | } 22 | 23 | method("getState")[RETURNS][TYPE] = S 24 | } 25 | 26 | fixDecoratorProperties(source, IMEMENTO_SUPPORT, true) 27 | 28 | source.functionSignature("yfiles.graph.MementoSupportProvider") 29 | .get(RETURNS) 30 | .addGeneric("T,*") 31 | } 32 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/NodeTypeHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_ANY 5 | 6 | internal const val INODE_TYPE = "yfiles.layout.INodeType" 7 | 8 | internal fun generateNodeTypeUtils(context: GeneratorContext) { 9 | // language=kotlin 10 | context[INODE_TYPE] = 11 | """ 12 | external interface INodeType 13 | 14 | inline fun INodeType(source:Any):INodeType = 15 | source.unsafeCast() 16 | """.trimIndent() 17 | } 18 | 19 | internal fun applyNodeTypeHacks(source: Source) { 20 | source.types() 21 | .optFlatMap(PROPERTIES) 22 | .filter { it[NAME] in "nodeTypes" } 23 | .forEach { it.replaceInType(",$JS_ANY>", ",$INODE_TYPE>") } 24 | } 25 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ObstacleDataHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | 5 | private const val OBSTACLE_DATA = "yfiles.router.ObstacleData" 6 | 7 | internal fun generateObstacleData(context: GeneratorContext) { 8 | // language=kotlin 9 | context[OBSTACLE_DATA] = "external interface ObstacleData" 10 | } 11 | 12 | internal fun applyObstacleDataHacks(source: Source) { 13 | source.type("Obstacle").apply { 14 | flatMap(CONSTRUCTORS) 15 | .flatMap(PARAMETERS) 16 | .filter { it[NAME] == "data" } 17 | .plus(property("data")) 18 | .forEach { it[TYPE] = OBSTACLE_DATA } 19 | } 20 | 21 | source.type("GraphPartition") 22 | .flatMap(METHODS) 23 | .filter { it.has(RETURNS) } 24 | .filter { it[RETURNS][TYPE] == "yfiles.router.Obstacle" } 25 | .flatMap(PARAMETERS) 26 | .filter { it[NAME] == "data" } 27 | .forEach { it[TYPE] = OBSTACLE_DATA } 28 | } 29 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/PartitionCellHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.* 4 | 5 | internal fun generatePartitionCellUtils(context: GeneratorContext) { 6 | // language=kotlin 7 | context["yfiles.router.PartitionCellKey"] = """ 8 | external interface PartitionCellKey 9 | 10 | inline fun PartitionCellKey(source:Any):PartitionCellKey = 11 | source.unsafeCast>() 12 | """.trimIndent() 13 | } 14 | 15 | private val KEY_TYPE_MAP = mapOf( 16 | "EDGE_LABEL_CROSSING_COST_FACTORS_KEY" to "$ILIST<$JS_DOUBLE>", 17 | "EDGE_LABEL_LAYOUTS_KEY" to "$ILIST<$IEDGE_LABEL_LAYOUT>", 18 | "NODES_IN_NODE_TO_EDGE_DISTANCE_KEY" to "$ILIST<$NODE>", 19 | "NODES_KEY" to "$ILIST<$NODE>", 20 | "NODE_LABEL_CROSSING_COST_FACTORS_KEY" to "$ILIST<$JS_DOUBLE>", 21 | "NODE_LABEL_LAYOUTS_KEY" to "$ILIST<$INODE_LABEL_LAYOUT>", 22 | "PARTITION_GRID_CELL_ID_KEY" to "yfiles.layout.PartitionCellId", 23 | "PARTITION_GRID_COLUMN_INDEX_KEY" to JS_INT, 24 | "PARTITION_GRID_ROW_INDEX_KEY" to JS_INT 25 | ) 26 | 27 | internal fun applyPartitionCellHacks(source: Source) { 28 | val methodNames = setOf( 29 | "getData", 30 | "putData", 31 | "removeData" 32 | ) 33 | 34 | source.type("PartitionCell") 35 | .flatMap(METHODS) 36 | .filter { it[NAME] in methodNames } 37 | .onEach { it.setSingleTypeParameter(bound = JS_OBJECT) } 38 | .onEach { it[RETURNS][TYPE] = "T" } 39 | .onEach { it.changeNullability(true) } 40 | .flatMap(PARAMETERS) 41 | .forEach { 42 | it[TYPE] = when (val name = it[NAME]) { 43 | "key" -> "$PARTITION_CELL_KEY" 44 | "data" -> "T" 45 | else -> throw IllegalArgumentException("Unable to calculate type by name '$name'") 46 | } 47 | } 48 | 49 | source.type("PartitionCellKeys") 50 | .flatMap(CONSTANTS) 51 | .forEach { 52 | val keyType = KEY_TYPE_MAP.getValue(it[NAME]) 53 | it[TYPE] = "$PARTITION_CELL_KEY<$keyType>" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ResourceHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.IVISUAL_TEMPLATE 5 | import com.github.turansky.yfiles.JS_STRING 6 | 7 | private const val RESOURCE_KEY = "yfiles.view.ResourceKey" 8 | private const val RESOURCE_MAP = "yfiles.view.ResourceMap" 9 | 10 | private fun resourceKey(typeParameter: String) = 11 | "$RESOURCE_KEY<$typeParameter>" 12 | 13 | internal fun generateResourceUtils(context: GeneratorContext) { 14 | // language=kotlin 15 | context["yfiles.view.Resources"] = """ 16 | @JsName("String") 17 | external class ResourceKey 18 | private constructor() 19 | 20 | external interface ResourceMap { 21 | operator fun set( 22 | key: ResourceKey, 23 | value: T 24 | ) 25 | } 26 | """.trimIndent() 27 | } 28 | 29 | internal fun applyResourceHacks(source: Source) { 30 | source.types().forEach { 31 | val className = it[NAME] 32 | it.optFlatMap(CONSTANTS) 33 | .filter { it[TYPE] == JS_STRING } 34 | .filter { it[NAME].endsWith("_KEY") } 35 | .forEach { it[TYPE] = getType(className, it[NAME]) } 36 | } 37 | 38 | source.type("CanvasComponent") 39 | .property("resources")[TYPE] = RESOURCE_MAP 40 | 41 | val DEFAULT_PORT_CANDIDATE_DESCRIPTOR = "DefaultPortCandidateDescriptor" 42 | source.type(DEFAULT_PORT_CANDIDATE_DESCRIPTOR) 43 | .method("setTemplate").apply { 44 | val typeParameter = getVisualTemplateParameter(DEFAULT_PORT_CANDIDATE_DESCRIPTOR) 45 | firstParameter[TYPE] = resourceKey("$IVISUAL_TEMPLATE<$typeParameter>") 46 | secondParameter.addGeneric(typeParameter) 47 | } 48 | 49 | 50 | val RECTANGLE_INDICATOR_INSTALLER = "RectangleIndicatorInstaller" 51 | source.type(RECTANGLE_INDICATOR_INSTALLER) 52 | .flatMap(CONSTRUCTORS) 53 | .optFlatMap(PARAMETERS) 54 | .filter { it[NAME] == "resourceKey" } 55 | .single { it[TYPE] == JS_STRING } 56 | .also { it[TYPE] = resourceKey("$IVISUAL_TEMPLATE<${getVisualTemplateParameter(RECTANGLE_INDICATOR_INSTALLER)}>") } 57 | 58 | val ORIENTED_RECTANGLE_INDICATOR_INSTALLER = "OrientedRectangleIndicatorInstaller" 59 | source.type(ORIENTED_RECTANGLE_INDICATOR_INSTALLER) 60 | .flatMap(CONSTRUCTORS) 61 | .optFlatMap(PARAMETERS) 62 | .filter { it[NAME] == "templateKey" } 63 | .single { it[TYPE] == JS_STRING } 64 | .also { it[TYPE] = resourceKey("$IVISUAL_TEMPLATE<${getVisualTemplateParameter(ORIENTED_RECTANGLE_INDICATOR_INSTALLER)}>") } 65 | } 66 | 67 | private fun getType( 68 | className: String, 69 | name: String, 70 | ): String { 71 | val typeParameter = when { 72 | name == "TEMPLATE_KEY" -> JS_STRING 73 | name.endsWith("_FILL_KEY") -> "yfiles.view.Fill" 74 | name.endsWith("STROKE_KEY") -> "yfiles.view.Stroke" 75 | else -> "$IVISUAL_TEMPLATE<${getVisualTemplateParameter(className)}>" 76 | } 77 | 78 | return resourceKey(typeParameter) 79 | } 80 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/ResultHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.* 4 | import com.github.turansky.yfiles.json.jArray 5 | import com.github.turansky.yfiles.json.jObject 6 | 7 | internal fun applyResultHacks(source: Source) { 8 | source.types() 9 | .filter { it[GROUP] == "class" } 10 | .filter { FINAL in it[MODIFIERS] } 11 | .filterNot { ENUM_LIKE in it[MODIFIERS] } 12 | .filterNot { it.has(EXTENDS) } 13 | .filterNot { it.has(CONSTRUCTORS) } 14 | .filter { it.has(PROPERTIES) } 15 | .filter { it.flatMap(PROPERTIES).all { it[MODIFIERS].let { RO in it && STATIC !in it } } } 16 | .filter { it.optFlatMap(METHODS).all { STATIC !in it[MODIFIERS] } } 17 | .forEach { 18 | it[CONSTRUCTORS] = jArray( 19 | jObject( 20 | MODIFIERS to arrayOf(PRIVATE) 21 | ) 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/SerializarionHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.* 4 | 5 | private const val SERIALIZATION_PROPERTY_KEY = "yfiles.graphml.SerializationPropertyKey" 6 | 7 | private fun propertyKey(typeParameter: String) = 8 | "$SERIALIZATION_PROPERTY_KEY<$typeParameter>" 9 | 10 | internal fun generateSerializationUtils(context: GeneratorContext) { 11 | // language=kotlin 12 | context[SERIALIZATION_PROPERTY_KEY] = """ 13 | @JsName("String") 14 | external class SerializationPropertyKey 15 | private constructor() 16 | """.trimIndent() 17 | } 18 | 19 | internal fun applySerializationHacks(source: Source) { 20 | source.types() 21 | .filter { it[ID].startsWith("yfiles.graphml.") } 22 | .optFlatMap(METHODS) 23 | .filter { it.has(PARAMETERS) } 24 | .filter { it.firstParameter.let { it[NAME] == "key" && it[TYPE] == JS_STRING } } 25 | .forEach { 26 | val size = it[PARAMETERS].length() 27 | if (size == 1 && !it.has(RETURNS)) { 28 | it.firstParameter[TYPE] = propertyKey("*") 29 | } else { 30 | it.setSingleTypeParameter(bound = JS_OBJECT) 31 | it.firstParameter[TYPE] = propertyKey("T") 32 | if (size == 2) { 33 | it.secondParameter[TYPE] = "T" 34 | } 35 | if (it.has(RETURNS)) { 36 | it[RETURNS][TYPE] = "T" 37 | } 38 | } 39 | } 40 | 41 | val typeMap = mapOf( 42 | "BASE_URI" to JS_STRING, 43 | "CACHE_EXTERNAL_REFERENCES" to JS_BOOLEAN, 44 | "CURRENT_KEY_SCOPE" to "yfiles.graphml.KeyScope", 45 | "DISABLE_GEOMETRY" to GRAPH_ITEM_TYPES, 46 | "DISABLE_GRAPH_SETTINGS" to JS_BOOLEAN, 47 | "DISABLE_ITEMS" to GRAPH_ITEM_TYPES, 48 | "DISABLE_STRIPE_LABELS" to STRIPE_TYPES, 49 | "DISABLE_STRIPE_STYLES" to STRIPE_TYPES, 50 | "DISABLE_STRIPE_USER_TAGS" to STRIPE_TYPES, 51 | "DISABLE_STYLES" to GRAPH_ITEM_TYPES, 52 | "DISABLE_USER_TAGS" to GRAPH_ITEM_TYPES, 53 | "IGNORE_PROPERTY_CASE" to JS_BOOLEAN, 54 | "IGNORE_XAML_DESERIALIZATION_ERRORS" to JS_BOOLEAN, 55 | "INDENT_OUTPUT" to JS_BOOLEAN, 56 | "PARSE_LABEL_SIZE" to JS_BOOLEAN, 57 | "REPRESENTED_EDGE" to IEDGE, 58 | "REWRITE_RELATIVE_RESOURCE_URIS" to JS_BOOLEAN, 59 | "UNDEFINED_HANDLING" to "yfiles.graphml.UndefinedHandling", 60 | "WRITE_EDGE_STYLE_DEFAULT" to JS_BOOLEAN, 61 | "WRITE_LABEL_SIZE_PREDICATE" to "yfiles.lang.Predicate<$ILABEL>", 62 | "WRITE_NODE_STYLE_DEFAULT" to JS_BOOLEAN, 63 | "WRITE_PORT_STYLE_DEFAULT" to JS_BOOLEAN, 64 | "WRITE_STRIPE_DEFAULTS" to JS_BOOLEAN 65 | ) 66 | 67 | source.type("SerializationProperties") 68 | .flatMap(CONSTANTS) 69 | .forEach { it[TYPE] = propertyKey(typeMap.getValue(it[NAME])) } 70 | } 71 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/SingletonHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.FINAL 4 | import com.github.turansky.yfiles.PRIVATE 5 | import com.github.turansky.yfiles.PROTECTED 6 | import com.github.turansky.yfiles.json.jArray 7 | import com.github.turansky.yfiles.json.jObject 8 | 9 | internal fun applySingletonHacks(source: Source) { 10 | source.types() 11 | .filter { it[GROUP] == "class" } 12 | .filterNot { it.has(CONSTRUCTORS) } 13 | .filter { it.optFlatMap(CONSTANTS).any { it[NAME] == "INSTANCE" } } 14 | .forEach { 15 | val modifier = if (it[MODIFIERS].contains(FINAL)) PRIVATE else PROTECTED 16 | it[CONSTRUCTORS] = jArray( 17 | jObject( 18 | NAME to it[NAME], 19 | MODIFIERS to arrayOf(modifier) 20 | ) 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/SizeExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GENERATED 4 | import com.github.turansky.yfiles.JS_BOOLEAN 5 | import com.github.turansky.yfiles.JS_INT 6 | import com.github.turansky.yfiles.RO 7 | import com.github.turansky.yfiles.json.jObject 8 | import org.json.JSONObject 9 | 10 | internal fun addSizeExtensions(source: Source) { 11 | source.type("IEnumerable") 12 | .addGeneratedProperty("lastIndex", JS_INT, "size - 1") 13 | 14 | source.types() 15 | .filter { it.optFlatMap(PROPERTIES).any { it[NAME] == "empty" } } 16 | .forEach { it.addGeneratedProperty("notEmpty", JS_BOOLEAN, "!empty") } 17 | 18 | source.types() 19 | .filter { it.optFlatMap(PROPERTIES).any { it[NAME] == "isEmpty" } } 20 | .forEach { it.addGeneratedProperty("isNotEmpty", JS_BOOLEAN, "!isEmpty") } 21 | } 22 | 23 | private fun JSONObject.addGeneratedProperty( 24 | name: String, 25 | type: String, 26 | body: String, 27 | ) { 28 | val property = jObject( 29 | NAME to name, 30 | TYPE to type, 31 | MODIFIERS to listOf(RO, GENERATED), 32 | BODY to body 33 | ) 34 | 35 | get(PROPERTIES).put(property) 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/SnapLineProviderHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.IEDGE 4 | import com.github.turansky.yfiles.IMODEL_ITEM 5 | import com.github.turansky.yfiles.INODE 6 | import org.json.JSONObject 7 | 8 | private const val ISNAP_LINE_PROVIDER = "yfiles.input.ISnapLineProvider" 9 | 10 | internal fun applySnapLineProviderHacks(source: Source) { 11 | source.type("ISnapLineProvider").apply { 12 | setSingleTypeParameter("in T", IMODEL_ITEM) 13 | 14 | fixItemType("T") 15 | 16 | method("create") 17 | .get(RETURNS) 18 | .addGeneric("T") 19 | } 20 | 21 | sequenceOf( 22 | "NodeSnapLineProvider" to INODE, 23 | "EdgeSnapLineProvider" to IEDGE 24 | ).forEach { (className, typeParameter) -> 25 | source.type(className).apply { 26 | get(IMPLEMENTS).apply { 27 | put(indexOf(ISNAP_LINE_PROVIDER), "$ISNAP_LINE_PROVIDER<$typeParameter>") 28 | } 29 | 30 | fixItemType(typeParameter) 31 | } 32 | } 33 | 34 | fixDecoratorProperties(source, ISNAP_LINE_PROVIDER) 35 | 36 | source.type("CreateEdgeInputMode") 37 | .method("getDummyEdgeSnapLines") 38 | .parameter("provider") 39 | .addGeneric(IEDGE) 40 | } 41 | 42 | fun JSONObject.fixItemType(type: String) { 43 | flatMap(METHODS) 44 | .flatMap(PARAMETERS) 45 | .filter { it[NAME] == "item" } 46 | .filter { it[TYPE] == IMODEL_ITEM } 47 | .forEach { it[TYPE] = type } 48 | } 49 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/Source.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import org.json.JSONObject 4 | 5 | internal class Source(api: JSONObject) : SourceBase(api) 6 | 7 | internal abstract class SourceBase(private val api: JSONObject) { 8 | val functionSignatures: JSONObject 9 | get() = api[FUNCTION_SIGNATURES] 10 | 11 | fun functionSignature(name: String): JSONObject = 12 | functionSignatures.getJSONObject(name) 13 | 14 | private val types: List = api.flatMap(TYPES).toList() 15 | 16 | private val typeMap = types.associateBy { it.uid }.toMutableMap() 17 | 18 | fun types(): Sequence = 19 | types.asSequence() 20 | 21 | fun type(className: String): JSONObject = 22 | typeMap.getValue(className) 23 | 24 | fun type(className: String, action: JSONObject.() -> Unit): JSONObject = 25 | typeMap.getValue(className).apply(action) 26 | 27 | fun types(vararg classNames: String): Sequence = 28 | classNames.asSequence() 29 | .map { type(it) } 30 | 31 | fun allMethods(vararg methodNames: String): Sequence = 32 | types.asSequence() 33 | .optFlatMap(METHODS) 34 | .filter { it[NAME] in methodNames } 35 | 36 | private val JSONObject.uid: String 37 | get() = opt(ES6_NAME) ?: get(NAME) 38 | 39 | fun add(type: JSONObject) { 40 | api[TYPES].put(type) 41 | typeMap[type[NAME]] = type 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/StyleRendererHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | private const val RENDERER = "renderer" 4 | 5 | internal fun applyStyleRendererHacks(source: Source) { 6 | source.types() 7 | .filter { isStyleLikeId(it[ID]) } 8 | .forEach { type -> 9 | val renderer = type.optFlatMap(PROPERTIES) 10 | .firstOrNull { it[NAME] == RENDERER } 11 | ?: return@forEach 12 | 13 | if (type[NAME].startsWith("Void")) { 14 | renderer[TYPE] = "${type[ID]}Renderer" 15 | return@forEach 16 | } 17 | 18 | type.optFlatMap(CONSTRUCTORS) 19 | .optFlatMap(PARAMETERS) 20 | .filter { it[NAME] == RENDERER } 21 | .map { it[TYPE] } 22 | .singleOrNull() 23 | ?.takeIf { isInterfaceLikeId(it) } 24 | ?.let { renderer[TYPE] = it } 25 | } 26 | } 27 | 28 | private fun isStyleLikeId(id: String): Boolean = 29 | id.startsWith("yfiles.styles.") && 30 | (id.endsWith("Style") || id.endsWith("StyleBase")) 31 | 32 | private fun isInterfaceLikeId(id: String): Boolean { 33 | val prefix = id.substringAfterLast(".").substring(0, 2) 34 | return prefix != prefix.uppercase() 35 | } 36 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/StyleTagHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_ANY 5 | import com.github.turansky.yfiles.JS_OBJECT 6 | 7 | private const val STYLE_TAG = "yfiles.styles.StyleTag" 8 | 9 | internal fun generateStyleTagUtils(context: GeneratorContext) { 10 | // language=kotlin 11 | context[STYLE_TAG] = """ 12 | external interface StyleTag 13 | 14 | inline fun StyleTag(source:Any):StyleTag = 15 | source.unsafeCast() 16 | """.trimIndent() 17 | } 18 | 19 | internal fun applyStyleTagHacks(source: Source) { 20 | val likeObjectTypes = setOf( 21 | JS_OBJECT, 22 | JS_ANY 23 | ) 24 | 25 | source.types() 26 | .optFlatMap(PROPERTIES) 27 | .filter { it[NAME] == "styleTag" } 28 | .filter { it[TYPE] in likeObjectTypes } 29 | .forEach { it[TYPE] = STYLE_TAG } 30 | } 31 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/TagHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.JS_ANY 5 | import com.github.turansky.yfiles.JS_OBJECT 6 | import org.json.JSONObject 7 | 8 | internal const val TAG = "yfiles.graph.Tag" 9 | 10 | internal fun generateTagUtils(context: GeneratorContext) { 11 | // language=kotlin 12 | context[TAG] = 13 | """ 14 | external interface Tag 15 | 16 | inline fun Tag(source:Any):Tag = 17 | source.unsafeCast() 18 | """.trimIndent() 19 | } 20 | 21 | internal fun applyTagHacks(source: Source) { 22 | val likeObjectTypes = setOf( 23 | JS_OBJECT, 24 | JS_ANY 25 | ) 26 | 27 | typedItems(source) 28 | .filter { looksLikeTag(it[NAME]) } 29 | .filter { it[TYPE] in likeObjectTypes } 30 | .forEach { it[TYPE] = TAG } 31 | 32 | source.type("GraphCopier") 33 | .flatMap(METHODS) 34 | .filter { it[NAME].run { startsWith("copy") && endsWith("Tag") } } 35 | .forEach { it[RETURNS][TYPE] = TAG } 36 | 37 | source.types("GraphWrapperBase") 38 | .flatMap(METHODS) 39 | .filter { it[NAME].endsWith("TagChanged") } 40 | .map { it.firstParameter } 41 | .forEach { it.replaceInType(",$JS_OBJECT>", ",$TAG>") } 42 | 43 | source.types() 44 | .optFlatMap(EVENTS) 45 | .filter { "TagChanged" in it[NAME] } 46 | .eventListeners() 47 | .map { it.firstParameter } 48 | .forEach { it.replaceInSignature(",$JS_OBJECT>>", ",$TAG>>") } 49 | 50 | source.type("LayoutGraphAdapter") 51 | .constant("ORIGINAL_TAG_DP_KEY").also { 52 | it.replaceInType("<$JS_ANY>", "<$TAG>") 53 | } 54 | } 55 | 56 | private fun looksLikeTag(name: String): Boolean = 57 | name == "tag" || (name.endsWith("Tag") && name != "styleTag") 58 | 59 | private fun typedItems(source: Source): Sequence = 60 | source.types() 61 | .filterNot { it[NAME] == "GraphMLIOHandler" } 62 | .flatMap { 63 | (it.optFlatMap(CONSTRUCTORS) + it.optFlatMap(METHODS)) 64 | .optFlatMap(PARAMETERS) 65 | .plus(it.optFlatMap(PROPERTIES)) 66 | } 67 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/TemplateLoadersHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.json.jObject 4 | import com.github.turansky.yfiles.json.removeAllObjects 5 | import org.json.JSONObject 6 | 7 | private const val TEMPLATE_LOADERS = "yfiles.styles.TemplateLoaders" 8 | private val TEMPLATE_LOADERS_NAME = TEMPLATE_LOADERS.substringAfterLast(".") 9 | internal const val TEMPLATE_LOADERS_ALIAS = "TemplateNodeStyleBase" 10 | 11 | private val COPIED_NAMES = setOf( 12 | "loadAllTemplates" 13 | ) 14 | 15 | internal fun applyTemplateLoadersHacks(source: Source) { 16 | source.add(createTemplates(source.type(TEMPLATE_LOADERS_ALIAS))) 17 | 18 | source.types() 19 | .filter { it[ID].startsWith("yfiles.styles.") } 20 | .filter { it[NAME].startsWith("Template") } 21 | .filter { it[NAME].endsWith("StyleBase") } 22 | .forEach { it.removeCommonItems() } 23 | } 24 | 25 | private fun JSONObject.removeCommonItems() { 26 | get(METHODS).removeAllObjects { 27 | it[NAME] in COPIED_NAMES 28 | } 29 | } 30 | 31 | private fun createTemplates(sourceType: JSONObject): JSONObject { 32 | return jObject( 33 | ID to TEMPLATE_LOADERS, 34 | NAME to TEMPLATE_LOADERS_NAME, 35 | ES6_NAME to TEMPLATE_LOADERS_ALIAS, 36 | GROUP to "class" 37 | ).also { type -> 38 | type[METHODS] = sourceType.flatMap(METHODS) 39 | .filter { it[NAME] in COPIED_NAMES } 40 | .toList() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/TemplatesHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.INTERNAL 4 | import com.github.turansky.yfiles.JS_OBJECT 5 | import com.github.turansky.yfiles.json.jObject 6 | import com.github.turansky.yfiles.json.removeAllObjects 7 | import org.json.JSONObject 8 | 9 | private const val TEMPLATES = "yfiles.styles.Templates" 10 | internal val TEMPLATES_NAME = TEMPLATES.substringAfterLast(".") 11 | private const val TEMPLATES_ALIAS = "StringTemplateNodeStyle" 12 | 13 | private val COPIED_KEYS = setOf( 14 | CONSTANTS, 15 | PROPERTIES, 16 | METHODS 17 | ) 18 | 19 | private val COPIED_NAMES = setOf( 20 | "CONVERTERS", 21 | "trusted", 22 | "makeObservable" 23 | ) 24 | 25 | internal fun applyTemplatesHacks(source: Source) { 26 | source.add(createTemplates(source.type(TEMPLATES_ALIAS))) 27 | 28 | source.types() 29 | .filter { it[ID].startsWith("yfiles.styles.") } 30 | .filter { it[NAME].contains("Template") } 31 | .filter { it[NAME].endsWith("Style") } 32 | .forEach { it.removeCommonItems() } 33 | } 34 | 35 | private fun JSONObject.removeCommonItems() { 36 | COPIED_KEYS 37 | .filter { has(it) } 38 | .forEach { key -> 39 | get(key).removeAllObjects { 40 | it[NAME] in COPIED_NAMES 41 | } 42 | } 43 | } 44 | 45 | private fun createTemplates(sourceType: JSONObject): JSONObject { 46 | val type = jObject( 47 | ID to TEMPLATES, 48 | NAME to TEMPLATES_NAME, 49 | ES6_NAME to TEMPLATES_ALIAS, 50 | GROUP to "class" 51 | ) 52 | 53 | COPIED_KEYS.forEach { key -> 54 | type[key] = sourceType.flatMap(key) 55 | .filter { it[NAME] in COPIED_NAMES } 56 | .toList() 57 | } 58 | 59 | type.method("makeObservable").apply { 60 | get(MODIFIERS).put(INTERNAL) 61 | 62 | setSingleTypeParameter(bound = JS_OBJECT) 63 | firstParameter[TYPE] = "T" 64 | get(RETURNS)[TYPE] = "T" 65 | } 66 | 67 | return type 68 | } 69 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/TimeSpanExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.Class 4 | 5 | private val MULTIPLIERS = listOf( 6 | 24, 60, 60, 1000 7 | ) 8 | 9 | internal fun timeSpanExtensions(timeSpanClass: Class): String { 10 | val parameters = timeSpanClass.secondaryConstructors 11 | .maxByOrNull { it.parameters.size }!! 12 | .parameters 13 | 14 | return parameters 15 | .zipWithNext { current, next -> 16 | val multiplier = MULTIPLIERS[parameters.indexOf(current)] 17 | """ 18 | inline val Int.${current.name}: TimeSpan 19 | get() = (this * $multiplier).${next.name} 20 | """.trimIndent() 21 | } 22 | .joinToString(separator = "\n\n", postfix = "\n\n") + 23 | """ 24 | inline val Int.${parameters.last().name}: TimeSpan 25 | get() = TimeSpan(this) 26 | """.trimIndent() 27 | } 28 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/TooltipHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | import com.github.turansky.yfiles.HTML_ELEMENT 5 | 6 | private const val TOOL_TIP_CONTENT = "yfiles.view.ToolTipContent" 7 | 8 | internal fun generateTooltipUtils(context: GeneratorContext) { 9 | // language=kotlin 10 | context[TOOL_TIP_CONTENT] = """ 11 | external interface ToolTipContent 12 | 13 | inline fun ToolTipContent(source:$HTML_ELEMENT):$TOOL_TIP_CONTENT = 14 | source.unsafeCast<$TOOL_TIP_CONTENT>() 15 | 16 | inline fun ToolTipContent(source:String):$TOOL_TIP_CONTENT = 17 | source.unsafeCast<$TOOL_TIP_CONTENT>() 18 | """.trimIndent() 19 | } 20 | 21 | internal fun applyTooltipHacks(source: Source) { 22 | source.type("ToolTip") { 23 | property("content")[TYPE] = TOOL_TIP_CONTENT 24 | 25 | flatMap(METHODS) 26 | .optFlatMap(PARAMETERS) 27 | .filter { it[NAME].endsWith("Content") } 28 | .forEach { it[TYPE] = TOOL_TIP_CONTENT } 29 | } 30 | 31 | source.type("ToolTipQueryEventArgs") 32 | .property("toolTip")[TYPE] = TOOL_TIP_CONTENT 33 | 34 | source.type("MouseHoverInputMode") { 35 | method("getToolTipContent")[RETURNS][TYPE] = TOOL_TIP_CONTENT 36 | 37 | flatMap(METHODS) 38 | .optFlatMap(PARAMETERS) 39 | .filter { it[NAME] == "content" } 40 | .forEach { it[TYPE] = TOOL_TIP_CONTENT } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/VisualTemplateHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.IBEND 4 | import com.github.turansky.yfiles.IVISUAL_TEMPLATE 5 | import com.github.turansky.yfiles.JS_VOID 6 | import com.github.turansky.yfiles.STATIC 7 | import com.github.turansky.yfiles.json.get 8 | 9 | internal fun applyVisualTemplateHacks(source: Source) { 10 | source.type("IVisualTemplate") { 11 | setSingleTypeParameter("in T") 12 | 13 | flatMap(METHODS) 14 | .filter { STATIC !in it[MODIFIERS] } 15 | .map { it[PARAMETERS]["dataObject"] } 16 | .onEach { it.changeNullability(false) } 17 | .forEach { it[TYPE] = "T" } 18 | } 19 | 20 | source.types().forEach { 21 | val className = it[NAME] 22 | it.optFlatMap(PROPERTIES) 23 | .filter { it[TYPE] == IVISUAL_TEMPLATE } 24 | .forEach { it[TYPE] = "$IVISUAL_TEMPLATE<${getVisualTemplateParameter(className)}>" } 25 | 26 | it.optFlatMap(METHODS) 27 | .filter { it.has(RETURNS) } 28 | .map { it[RETURNS] } 29 | .filter { it[TYPE] == IVISUAL_TEMPLATE } 30 | .forEach { it[TYPE] = "$IVISUAL_TEMPLATE<${getVisualTemplateParameter(className)}>" } 31 | } 32 | } 33 | 34 | internal fun getVisualTemplateParameter(className: String): String = 35 | when (className) { 36 | "DefaultPortCandidateDescriptor" -> "$TAG?" 37 | "DefaultStripeInputVisualizationHelper" -> "yfiles.graph.IStripe" 38 | "HandleInputMode" -> "yfiles.input.IHandle" 39 | 40 | "EdgeDecorationInstaller", 41 | "EdgeFocusIndicatorInstaller", 42 | "EdgeHighlightIndicatorInstaller", 43 | "EdgeSelectionIndicatorInstaller", 44 | -> IBEND 45 | 46 | "LabelPositionHandler", 47 | 48 | "LassoSelectionInputMode", 49 | "MarqueeSelectionInputMode", 50 | "OverviewInputMode", 51 | 52 | "OrientedRectangleIndicatorInstaller", 53 | "RectangleIndicatorInstaller", 54 | 55 | "Theme", 56 | -> JS_VOID 57 | 58 | else -> throw IllegalArgumentException("Unable to calculate type parameter for class '$className'") 59 | } 60 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/correction/YndefinedHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | 5 | internal fun generateYndefined(context: GeneratorContext) { 6 | // language=kotlin 7 | context["yfiles.lang.yndefined"] = 8 | """ 9 | inline fun yndefined(): T = 10 | null.unsafeCast() 11 | """.trimIndent() 12 | } 13 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/json/Json.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.json 2 | 3 | import com.github.turansky.yfiles.correction.JKey 4 | import com.github.turansky.yfiles.correction.NAME 5 | import com.github.turansky.yfiles.correction.get 6 | import com.github.turansky.yfiles.correction.remove 7 | import org.json.JSONArray 8 | import org.json.JSONObject 9 | 10 | 11 | internal fun jArray(vararg items: JSONObject): JSONArray { 12 | return JSONArray(items.toList()) 13 | } 14 | 15 | internal fun jObject(vararg items: Pair): JSONObject { 16 | return JSONObject( 17 | items.associate { (key, value) -> key.name to value } 18 | ) 19 | } 20 | 21 | internal fun JSONArray.first(predicate: (JSONObject) -> Boolean): JSONObject = 22 | (0 until this.length()) 23 | .map(this::getJSONObject) 24 | .first(predicate) 25 | 26 | internal operator fun JSONArray.get(name: String): JSONObject = 27 | first { it[NAME] == name } 28 | 29 | internal fun JSONArray.objects(predicate: (JSONObject) -> Boolean): Iterable { 30 | return (0 until this.length()) 31 | .map(this::getJSONObject) 32 | .filter(predicate) 33 | } 34 | 35 | internal fun JSONArray.removeAllObjects(predicate: (JSONObject) -> Boolean) { 36 | removeAll { 37 | predicate(it as JSONObject) 38 | } 39 | } 40 | 41 | internal fun JSONObject.strictRemove(key: JKey) { 42 | requireNotNull(remove(key)) 43 | } 44 | 45 | internal fun JSONObject.strictRemove(name: String) { 46 | requireNotNull(remove(name)) 47 | } 48 | 49 | internal fun JSONArray.strictRemove(index: Int) { 50 | requireNotNull(remove(index)) 51 | } 52 | 53 | internal fun JSONArray.removeItem(item: JSONObject) { 54 | requireNotNull(remove(indexOf(item))) 55 | } 56 | 57 | internal fun JSONArray.removeItem(item: String) { 58 | requireNotNull(remove(indexOf(item))) 59 | } 60 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/vsdx/FakeInterfaces.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.vsdx 2 | 3 | import com.github.turansky.yfiles.IENUMERABLE 4 | import com.github.turansky.yfiles.ILIST_ENUMERABLE 5 | import com.github.turansky.yfiles.Interface 6 | import com.github.turansky.yfiles.correction.ID 7 | import com.github.turansky.yfiles.correction.METHODS 8 | import com.github.turansky.yfiles.correction.NAME 9 | import com.github.turansky.yfiles.json.jObject 10 | 11 | internal fun fakeVsdxInterfaces(): List { 12 | return listOf( 13 | fakeVsdxInterface( 14 | id = IENUMERABLE, 15 | methodNames = setOf("getEnumerator") 16 | ), 17 | fakeVsdxInterface( 18 | id = ILIST_ENUMERABLE, 19 | methodNames = setOf("getEnumerator", "get") 20 | ) 21 | ) 22 | } 23 | 24 | private fun fakeVsdxInterface(id: String, methodNames: Set): Interface { 25 | return Interface( 26 | jObject( 27 | ID to id, 28 | NAME to id, 29 | METHODS to methodNames.map { 30 | jObject( 31 | NAME to it 32 | ) 33 | } 34 | ) 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/vsdx/correction/DataHacks.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.vsdx.correction 2 | 3 | import com.github.turansky.yfiles.GeneratorContext 4 | 5 | internal const val MASTER_STATE = "MasterState" 6 | internal const val IMAGE_DATA_RESPONSE = "ImageDataResponse" 7 | 8 | internal fun createVsdxDataClasses(context: GeneratorContext) { 9 | // language=kotlin 10 | context["yfiles.vsdx.$MASTER_STATE"] = """ 11 | external interface $MASTER_STATE { 12 | val master: Master 13 | val fillStyle: StyleSheet 14 | val lineStyle: StyleSheet 15 | val textStyle: StyleSheet 16 | } 17 | """.trimIndent() 18 | 19 | // language=kotlin 20 | context["yfiles.vsdx.$IMAGE_DATA_RESPONSE"] = """ 21 | external interface $IMAGE_DATA_RESPONSE { 22 | val data: String 23 | val format: String 24 | } 25 | """.trimIndent() 26 | } 27 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/github/turansky/yfiles/vsdx/correction/VsdxSource.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.vsdx.correction 2 | 3 | import com.github.turansky.yfiles.correction.SourceBase 4 | import org.json.JSONObject 5 | 6 | internal class VsdxSource(api: JSONObject) : SourceBase(api) 7 | -------------------------------------------------------------------------------- /examples/configurable-properties/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(kfc.plugins.library) 3 | alias(libs.plugins.yfiles) 4 | } 5 | 6 | dependencies { 7 | jsMainImplementation(project(":yfiles-kotlin")) 8 | } 9 | -------------------------------------------------------------------------------- /examples/configurable-properties/src/jsMain/kotlin/AbstractArrow.kt: -------------------------------------------------------------------------------- 1 | import yfiles.styles.IArrow 2 | 3 | abstract class AbstractArrow : IArrow { 4 | override val cropLength = 13.0 5 | override val length = 42.0 6 | } 7 | 8 | abstract class ZArrow { 9 | val cropLength = -13.0 10 | val length = -42.0 11 | } 12 | -------------------------------------------------------------------------------- /examples/configurable-properties/src/jsMain/kotlin/CustomLabelModelParameter.kt: -------------------------------------------------------------------------------- 1 | import yfiles.graph.FreeEdgeLabelModel 2 | import yfiles.graph.ILabel 3 | import yfiles.graph.ILabelModel 4 | import yfiles.graph.ILabelModelParameter 5 | 6 | class CustomLabelModelParameter : ILabelModelParameter { 7 | override val model: ILabelModel 8 | get() = FreeEdgeLabelModel.INSTANCE 9 | 10 | override fun supports(label: ILabel): Boolean = true 11 | override fun clone(): ILabelModelParameter = this 12 | } 13 | -------------------------------------------------------------------------------- /examples/configurable-properties/src/jsMain/kotlin/Delegates.kt: -------------------------------------------------------------------------------- 1 | import yfiles.graph.IFoldingView 2 | 3 | @JsExport 4 | @ExperimentalJsExport 5 | class FoldingViewDelegate(source: IFoldingView) : IFoldingView by source 6 | -------------------------------------------------------------------------------- /examples/configurable-properties/src/jsMain/kotlin/MyPolylineEdgeStyleRenderer.kt: -------------------------------------------------------------------------------- 1 | import yfiles.styles.PolylineEdgeStyleRenderer 2 | 3 | abstract class MyPolylineEdgeStyleRenderer : PolylineEdgeStyleRenderer() { 4 | override val addBridges: Boolean 5 | get() = false 6 | } 7 | -------------------------------------------------------------------------------- /examples/data-classes/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(kfc.plugins.library) 3 | alias(libs.plugins.yfiles) 4 | } 5 | 6 | dependencies { 7 | jsMainImplementation(project(":yfiles-kotlin")) 8 | } 9 | -------------------------------------------------------------------------------- /examples/data-classes/src/jsMain/kotlin/DataClasses.kt: -------------------------------------------------------------------------------- 1 | import yfiles.lang.IComparable 2 | import yfiles.lang.YObject 3 | 4 | data class Person( 5 | val firstName: String, 6 | val lastName: String, 7 | ) : YObject 8 | 9 | data class YDate( 10 | val milliseconds: Int, 11 | ) : IComparable { 12 | override fun compareTo(other: YDate): Int { 13 | return milliseconds.compareTo(other.milliseconds) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/import-optimizer-application/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(kfc.plugins.library) 3 | alias(libs.plugins.yfiles) 4 | } 5 | 6 | dependencies { 7 | jsMainImplementation(project(":yfiles-kotlin")) 8 | jsMainImplementation(project(":examples:import-optimizer-library")) 9 | } 10 | -------------------------------------------------------------------------------- /examples/import-optimizer-application/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.js.generate.executable.default=false 2 | -------------------------------------------------------------------------------- /examples/import-optimizer-application/src/jsMain/kotlin/Components.kt: -------------------------------------------------------------------------------- 1 | import yfiles.algorithms.nodeDpKey 2 | import yfiles.input.IHitTestable 3 | import yfiles.styles.IArrow 4 | import yfiles.view.GraphComponent 5 | import yfiles.view.IVisualCreator 6 | 7 | @JsExport 8 | @ExperimentalJsExport 9 | fun createComponent(): GraphComponent = 10 | GraphComponent { 11 | graph = createGraph() 12 | } 13 | 14 | @JsExport 15 | @ExperimentalJsExport 16 | object Keys { 17 | val STRING_DATA_KEY by nodeDpKey() 18 | 19 | val INT_DATA_KEY by nodeDpKey() 20 | } 21 | 22 | abstract class AbstractArrow : IArrow, IVisualCreator, IHitTestable 23 | -------------------------------------------------------------------------------- /examples/import-optimizer-library/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(kfc.plugins.library) 3 | alias(libs.plugins.yfiles) 4 | } 5 | 6 | dependencies { 7 | jsMainImplementation(project(":yfiles-kotlin")) 8 | } 9 | -------------------------------------------------------------------------------- /examples/import-optimizer-library/src/jsMain/kotlin/Graphs.kt: -------------------------------------------------------------------------------- 1 | import yfiles.graph.DefaultGraph 2 | import yfiles.graph.IGraph 3 | 4 | fun createGraph(): IGraph = 5 | DefaultGraph { 6 | val n1 = createNode() 7 | val n2 = createNode() 8 | 9 | createEdge(n1, n2) 10 | } 11 | -------------------------------------------------------------------------------- /examples/simple-app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(kfc.plugins.library) 3 | alias(libs.plugins.yfiles) 4 | } 5 | 6 | dependencies { 7 | jsMainImplementation(kotlinWrappers.browser) 8 | 9 | jsMainImplementation(project(":yfiles-kotlin")) 10 | } 11 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/App.kt: -------------------------------------------------------------------------------- 1 | import web.html.HTMLDivElement 2 | import yfiles.graph.DefaultGraph 3 | import yfiles.graph.invoke 4 | import yfiles.hierarchic.HierarchicLayout 5 | import yfiles.input.GraphViewerInputMode 6 | import yfiles.layout.LayoutOrientation 7 | import yfiles.view.GraphComponent 8 | 9 | fun create(): HTMLDivElement = 10 | GraphComponent().run { 11 | inputMode = GraphViewerInputMode() 12 | 13 | div.style.apply { 14 | width = "100%" 15 | height = "100%" 16 | backgroundColor = "#CCCCCC" 17 | } 18 | 19 | val layout = HierarchicLayout { 20 | layoutOrientation = LayoutOrientation.LEFT_TO_RIGHT 21 | automaticEdgeGrouping = true 22 | gridSpacing = 20.0 23 | } 24 | 25 | graph = DefaultGraph { 26 | val node1 = createNode() 27 | val node2 = createNode() 28 | createEdge(node1, node2) 29 | 30 | applyLayout(layout) 31 | } 32 | 33 | graph.decorator { 34 | nodeDecorator { 35 | selectionDecorator.hideImplementation() 36 | focusIndicatorDecorator.hideImplementation() 37 | highlightDecorator.hideImplementation() 38 | } 39 | } 40 | 41 | fitGraphBounds() 42 | 43 | div 44 | } 45 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Cast.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UNUSED_VARIABLE") 2 | 3 | import yfiles.graph.DefaultGraph 4 | import yfiles.graph.IGraph 5 | import yfiles.graph.IModelItem 6 | import yfiles.graph.INode 7 | import yfiles.lang.yAs 8 | import yfiles.lang.yIs 9 | import yfiles.lang.yOpt 10 | 11 | fun cast() { 12 | val g: IGraph = DefaultGraph() 13 | val n1: IModelItem = g.createNode() 14 | val n2: INode = g.createNode() 15 | 16 | val isNode: Boolean = n1 yIs INode 17 | val optNode: INode? = n1 yOpt INode 18 | val asNode: INode = n1 yAs INode 19 | } -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Comparer.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UNUSED_VARIABLE") 2 | 3 | import yfiles.algorithms.Comparers.createComparableComparer 4 | import yfiles.algorithms.Graph 5 | import yfiles.layout.DefaultLayoutGraph 6 | import yfiles.layout.TabularLayout 7 | import yfiles.tree.NodeOrderComparer 8 | 9 | fun comparer() { 10 | val g: Graph = DefaultLayoutGraph() 11 | g.sortNodes(NodeOrderComparer()) 12 | g.sortNodes(createComparableComparer()) 13 | 14 | val l = TabularLayout() 15 | l.nodeComparer = NodeOrderComparer() 16 | l.nodeComparer = createComparableComparer() 17 | } -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Converters.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UNUSED_VARIABLE") 2 | 3 | import yfiles.styles.Templates 4 | import yfiles.styles.invoke 5 | import yfiles.styles.put 6 | 7 | fun converters() { 8 | Templates.CONVERTERS { 9 | put("visibility", ::visibility) 10 | put("color", ::color) 11 | } 12 | } 13 | 14 | fun visibility(visible: Boolean): String { 15 | return if (visible) "visible" else "none" 16 | } 17 | 18 | fun color(width: Double, defaultColor: String): String = 19 | when { 20 | width > 10 -> "white" 21 | width > 20 -> "grey" 22 | else -> defaultColor 23 | } 24 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Destructuring.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UNUSED_VARIABLE") 2 | 3 | import yfiles.algorithms.YPoint 4 | import yfiles.algorithms.YPoint.Companion.plus 5 | import yfiles.collections.* 6 | 7 | fun destructuring() { 8 | val p1 = YPoint(4.0, 8.0) 9 | val p2 = YPoint(15.0, 16.0) 10 | val p3 = YPoint(23.0, 42.0) 11 | 12 | val (x, y) = p1 + p2 + p3 13 | 14 | val l: IList = List() 15 | val (l1, l2, l3) = l 16 | 17 | val e: IEnumerable = l 18 | val (e1, e2, e3) = e 19 | } 20 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/ExternalExtensions.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UNUSED_VARIABLE") 2 | 3 | import yfiles.algorithms.Graph 4 | import yfiles.algorithms.GraphChecker.isAcyclic 5 | import yfiles.algorithms.GraphChecker.isCyclic 6 | import yfiles.algorithms.Trees.isForest 7 | import yfiles.layout.DefaultLayoutGraph 8 | 9 | fun externalExtensions() { 10 | val graph: Graph = DefaultLayoutGraph() 11 | // JS: GraphChecker.isCyclic(graph) 12 | graph.isCyclic() 13 | // JS: GraphChecker.isAcyclic(graph) 14 | graph.isAcyclic() 15 | // JS: Trees.isForest(graph) 16 | graph.isForest() 17 | } 18 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Flags.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | import yfiles.graph.GraphItemTypes.Companion.EDGE 4 | import yfiles.graph.GraphItemTypes.Companion.LABEL 5 | import yfiles.graph.GraphItemTypes.Companion.NODE 6 | import yfiles.input.GraphViewerInputMode 7 | import yfiles.lang.contains 8 | import yfiles.lang.or 9 | 10 | fun flags() { 11 | val mode = GraphViewerInputMode { 12 | clickableItems = NODE or EDGE or LABEL 13 | } 14 | 15 | println(NODE in mode.clickableItems) 16 | println(EDGE in mode.clickableItems) 17 | } 18 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Iterators.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | import yfiles.algorithms.iterator 4 | import yfiles.collections.iterator 5 | import yfiles.graph.DefaultGraph 6 | import yfiles.graph.IGraph 7 | import yfiles.hierarchic.HierarchicLayout 8 | import yfiles.layout.DefaultLayoutGraph 9 | import yfiles.layout.LayoutGraph 10 | 11 | fun enumerableIterator() { 12 | val graph: IGraph = DefaultGraph { 13 | createNode() 14 | createNode() 15 | createNode() 16 | 17 | applyLayout(HierarchicLayout()) 18 | } 19 | 20 | for (node in graph.nodes) { 21 | println("Node layout: ${node.layout}") 22 | } 23 | } 24 | 25 | fun cursorIterator() { 26 | val graph: LayoutGraph = DefaultLayoutGraph { 27 | createNode() 28 | createNode() 29 | createNode() 30 | } 31 | 32 | HierarchicLayout() 33 | .applyLayout(graph) 34 | 35 | for (node in graph.getNodeCursor()) { 36 | println("Node index: ${node.index}") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Lookups.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress( 2 | "EXTERNAL_INTERFACE_AS_REIFIED_TYPE_ARGUMENT", 3 | "unused", 4 | "UNUSED_VARIABLE", 5 | ) 6 | 7 | import yfiles.graph.DefaultGraph 8 | import yfiles.graph.IGraph 9 | import yfiles.graph.lookup 10 | import yfiles.graph.lookupValue 11 | import yfiles.input.IHitTestable 12 | import yfiles.lang.TimeSpan 13 | 14 | fun lookups() { 15 | val graph: IGraph = DefaultGraph() 16 | val node = graph.createNode() 17 | 18 | // for classes 19 | val t13: TimeSpan? = node.lookup() // reified lookup type 20 | val t14 = node.lookup() // 'TimeSpan?' 21 | 22 | val t23: TimeSpan = node.lookupValue() // reified lookup type 23 | val t24 = node.lookupValue() // 'TimeSpan' 24 | 25 | // for interfaces 26 | val h13: IHitTestable? = node.lookup() // reified lookup type 27 | val h14 = node.lookup() // 'IHitTestable?' 28 | 29 | val h23: IHitTestable = node.lookupValue() // reified lookup type 30 | val h24 = node.lookupValue() // 'IHitTestable' 31 | } 32 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Observable.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UNUSED_VARIABLE") 2 | 3 | import yfiles.graph.observable 4 | 5 | class Point3D { 6 | var x: Double by observable(0.0) 7 | var y: Double by observable(0.0) 8 | var z: Double by observable(0.0) 9 | } 10 | 11 | class User { 12 | var firstName: String by observable("Frodo") 13 | var lastName: String by observable("Baggins") 14 | var age: Int by observable(42) 15 | } 16 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Resources.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | import yfiles.lang.ResourceKeys.COPY 4 | import yfiles.lang.ResourceKeys.COPY_KEY 5 | import yfiles.lang.Resources.invariant 6 | import yfiles.lang.get 7 | 8 | fun resources() { 9 | println(invariant[COPY]) // Copy 10 | println(invariant[COPY_KEY]) // Action+C;Ctrl+Ins 11 | } 12 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/Sequences.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | import yfiles.algorithms.asSequence 4 | import yfiles.collections.asSequence 5 | import yfiles.graph.DefaultGraph 6 | import yfiles.graph.IGraph 7 | import yfiles.hierarchic.HierarchicLayout 8 | import yfiles.layout.DefaultLayoutGraph 9 | import yfiles.layout.LayoutGraph 10 | 11 | fun enumerableSequence() { 12 | val graph: IGraph = DefaultGraph { 13 | createNode() 14 | createNode() 15 | createNode() 16 | 17 | applyLayout(HierarchicLayout()) 18 | } 19 | 20 | graph.nodes.asSequence() 21 | .forEach { println("Node layout: ${it.layout}") } 22 | } 23 | 24 | fun cursorSequence() { 25 | val graph: LayoutGraph = DefaultLayoutGraph { 26 | createNode() 27 | createNode() 28 | createNode() 29 | } 30 | 31 | HierarchicLayout() 32 | .applyLayout(graph) 33 | 34 | graph.getNodeCursor() 35 | .asSequence() 36 | .filter { it.index % 2 == 0 } 37 | .forEach { println("Node index: ${it.index}") } 38 | } 39 | -------------------------------------------------------------------------------- /examples/simple-app/src/jsMain/kotlin/TimeSpan.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UNUSED_VARIABLE") 2 | 3 | import yfiles.lang.* 4 | 5 | fun timeSpan() { 6 | val c: TimeSpan = 2.hours 7 | val o: TimeSpan = 0.minutes 8 | val d: TimeSpan = 1.seconds 9 | val e: TimeSpan = 3.milliseconds 10 | } 11 | -------------------------------------------------------------------------------- /gradle-plugin-test/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(kfc.plugins.library) 3 | alias(libs.plugins.yfiles) 4 | } 5 | 6 | dependencies { 7 | jsMainImplementation(kotlinWrappers.browser) 8 | jsMainImplementation(project(":yfiles-kotlin")) 9 | 10 | jsTestImplementation(kotlin("test-js")) 11 | } 12 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsMain/kotlin/ArrowDelegate.kt: -------------------------------------------------------------------------------- 1 | import yfiles.styles.IArrow 2 | 3 | class ArrowDelegate(source: IArrow) : IArrow by source 4 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsMain/kotlin/ComboClass.kt: -------------------------------------------------------------------------------- 1 | import yfiles.view.IBoundsProvider 2 | import yfiles.view.IVisibilityTestable 3 | 4 | abstract class ComboClass : IVisibilityTestable, IBoundsProvider 5 | 6 | abstract class SuperComboClass : ComboClass() { 7 | val puper = true 8 | } -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsMain/kotlin/CustomArrow.kt: -------------------------------------------------------------------------------- 1 | import yfiles.styles.IArrow 2 | 3 | abstract class CustomArrow : IArrow { 4 | override val cropLength = 13.0 5 | override val length = 42.0 6 | } 7 | 8 | abstract class ZArrow { 9 | val cropLength: Double 10 | get() = -13.0 11 | 12 | val length: Double 13 | get() = -42.0 14 | } 15 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsMain/kotlin/CustomObject.kt: -------------------------------------------------------------------------------- 1 | import web.prompts.alert 2 | import yfiles.lang.IClassMetadata 3 | import yfiles.lang.YObject 4 | import yfiles.lang.classMetadata 5 | 6 | class CustomObject : YObject { 7 | fun hallo() { 8 | alert("Hallo from CustomObject!") 9 | } 10 | 11 | companion object : IClassMetadata by classMetadata() 12 | } 13 | 14 | class OtherCustomObject : YObject { 15 | fun hallo() { 16 | alert("Hallo from OtherCustomObject!") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsMain/kotlin/DataObject.kt: -------------------------------------------------------------------------------- 1 | import yfiles.lang.YObject 2 | 3 | class DataObject( 4 | val key: String, 5 | val name: String 6 | ) : YObject 7 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsMain/kotlin/Object.kt: -------------------------------------------------------------------------------- 1 | external class Object { 2 | companion object { 3 | fun getOwnPropertyDescriptor(obj: Any, prop: String): PropertyDescriptor 4 | } 5 | } 6 | 7 | external interface PropertyDescriptor { 8 | val configurable: Boolean 9 | } 10 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsMain/kotlin/SimpleVisibilityTestable.kt: -------------------------------------------------------------------------------- 1 | import yfiles.geometry.Rect 2 | import yfiles.view.ICanvasContext 3 | import yfiles.view.IVisibilityTestable 4 | 5 | abstract class IVisibilityTestableBase : IVisibilityTestable 6 | 7 | class SimpleVisibilityTestable : IVisibilityTestableBase() { 8 | override fun isVisible( 9 | context: ICanvasContext, 10 | rectangle: Rect 11 | ): Boolean { 12 | return true 13 | } 14 | } -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsMain/resources/yfiles.js: -------------------------------------------------------------------------------- 1 | function YObject () {} 2 | 3 | function Class () {} 4 | 5 | Class.fixType = function (type, name) { 6 | type.className = name 7 | type.prototype.className = name 8 | } 9 | 10 | function BaseClass (...types) { 11 | const generic = types 12 | .map(type => type.name) 13 | .join('-') 14 | const className = `BaseClass[${generic}]` 15 | 16 | const YClass = function () {} 17 | Object.defineProperty(YClass.prototype, 'className', { get: () => className }) 18 | return YClass 19 | } 20 | 21 | function IArrow () {} 22 | 23 | function IVisibilityTestable () {} 24 | 25 | function IBoundsProvider () {} 26 | 27 | export { 28 | YObject, 29 | Class, 30 | BaseClass, 31 | IArrow, 32 | IVisibilityTestable, 33 | IBoundsProvider 34 | } 35 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsTest/kotlin/BaseClassTest.kt: -------------------------------------------------------------------------------- 1 | import kotlin.test.Ignore 2 | import kotlin.test.Test 3 | import kotlin.test.assertEquals 4 | 5 | @Ignore 6 | class BaseClassTest { 7 | @Test 8 | fun comboClassName() { 9 | val jsClass = ComboClass::class.js 10 | 11 | assertEquals( 12 | "BaseClass[IVisibilityTestable-IBoundsProvider]", 13 | jsClass.asDynamic().prototype.className 14 | ) 15 | } 16 | 17 | @Test 18 | fun superComboClassName() { 19 | val jsClass = SuperComboClass::class.js 20 | 21 | assertEquals( 22 | "BaseClass[IVisibilityTestable-IBoundsProvider]", 23 | jsClass.asDynamic().prototype.className 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsTest/kotlin/ConfigurablePropertyTest.kt: -------------------------------------------------------------------------------- 1 | import Object.Companion.getOwnPropertyDescriptor 2 | import kotlin.test.Test 3 | import kotlin.test.assertTrue 4 | 5 | class ConfigurablePropertyTest { 6 | @Test 7 | fun arrowProperties() { 8 | val jsClassPrototype: Any = CustomArrow::class.js.asDynamic().prototype 9 | 10 | assertTrue { 11 | getOwnPropertyDescriptor(jsClassPrototype, "cropLength").configurable 12 | } 13 | 14 | assertTrue { 15 | getOwnPropertyDescriptor(jsClassPrototype, "length").configurable 16 | } 17 | } 18 | 19 | @Test 20 | fun arrowDelegateProperties() { 21 | val jsClassPrototype: Any = ArrowDelegate::class.js.asDynamic().prototype 22 | 23 | assertTrue { 24 | getOwnPropertyDescriptor(jsClassPrototype, "cropLength").configurable 25 | } 26 | 27 | assertTrue { 28 | getOwnPropertyDescriptor(jsClassPrototype, "length").configurable 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /gradle-plugin-test/src/jsTest/kotlin/CustomObjectTest.kt: -------------------------------------------------------------------------------- 1 | import kotlin.test.Ignore 2 | import kotlin.test.Test 3 | import kotlin.test.assertEquals 4 | 5 | @Ignore 6 | class CustomObjectTest { 7 | @Test 8 | fun testClassName() { 9 | val o = CustomObject() 10 | assertEquals("CustomObject", o.asDynamic().className) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gradle-plugin/README.md: -------------------------------------------------------------------------------- 1 | [![CI Status](https://github.com/turansky/yfiles-kotlin/workflows/gradle%20plugin/badge.svg)](https://github.com/turansky/yfiles-kotlin/actions) 2 | [![Gradle Plugin Portal](https://img.shields.io/maven-metadata/v/https/plugins.gradle.org/m2/com/github/turansky/yfiles/com.github.turansky.yfiles.gradle.plugin/maven-metadata.xml.svg?label=plugin&logo=gradle)](https://plugins.gradle.org/plugin/com.github.turansky.yfiles) 3 | [![Kotlin](https://img.shields.io/badge/kotlin-1.7.21-blue.svg?logo=kotlin)](http://kotlinlang.org) 4 | 5 | # yFiles Gradle Plugin for Kotlin/JS 6 | 7 | ## Goal 8 | - Safe [interface implementing](http://docs.yworks.com/yfileshtml/#/dguide/framework_basic_interfaces#framework_implementing_interfaces) 9 | - OOB `YObject` support 10 | - Optimize yFiles imports 11 | - while [ES modules](https://youtrack.jetbrains.com/issue/KT-8373) not supported 12 | 13 | ## Interface(s) implementing 14 | 15 | #### Example 16 | ```Kotlin 17 | // Generated prototype (JS): 18 | // MyVisualTemplate.prototype = Object.create(BaseClass(IVisualTemplate).prototype) 19 | class MyVisualTemplate: IVisualTemplate { 20 | /* body */ 21 | } 22 | 23 | // Generated prototype (JS): 24 | // MyArrow.prototype = Object.create(BaseClass(IArrow, IVisualCreator, IBoundsProvider).prototype) 25 | class MyArrow: IArrow, IVisualCreator, IBoundsProvider { 26 | /* body */ 27 | } 28 | ``` 29 | 30 | #### Requirements 31 | - Only yFiles interfaces implemented 32 | - No extended class 33 | 34 | ## Custom `YObject` 35 | 36 | #### Example 37 | ```Kotlin 38 | // Generated JS: 39 | // Class.fixType(SelectionProvider) 40 | class SelectionProvider : YObject { 41 | fun isSelected(item: IModelItem): Boolean = /* ... */ 42 | } 43 | 44 | // Generated JS: 45 | // Class.fixType(HighlightProvider) 46 | class HighlightProvider : YObject { 47 | fun isHighlighted(item: IModelItem): Boolean = /* ... */ 48 | } 49 | ``` 50 | 51 | #### Requirements 52 | - No extended interfaces 53 | 54 | ## Class property 55 | ```Kotlin 56 | class MyObject : YObject { 57 | companion object : IClassMetadata by classMetadata() 58 | } 59 | 60 | // JS: MyObject.$class 61 | MyObject.yclass 62 | ``` 63 | -------------------------------------------------------------------------------- /gradle-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | 4 | id("com.gradle.plugin-publish") version "1.3.1" 5 | id("io.github.turansky.kfc.plugin-publish") version "13.7.0" 6 | 7 | kotlin("jvm") version "2.1.10" 8 | } 9 | 10 | dependencies { 11 | compileOnly(kotlin("gradle-plugin")) 12 | compileOnly(kotlin("compiler-embeddable")) 13 | } 14 | 15 | val REPO_URL = "https://github.com/turansky/yfiles-kotlin" 16 | 17 | gradlePlugin { 18 | website.set(REPO_URL) 19 | vcsUrl.set(REPO_URL) 20 | 21 | plugins { 22 | create("yfiles") { 23 | id = "com.github.turansky.yfiles" 24 | displayName = "yFiles Kotlin/JS plugin" 25 | description = "yFiles class framework helper for Kotlin/JS" 26 | implementationClass = "com.github.turansky.yfiles.gradle.plugin.YFilesGradleSubplugin" 27 | tags.set( 28 | listOf( 29 | "yfiles", 30 | "kotlin", 31 | "kotlin-js", 32 | "javascript" 33 | ) 34 | ) 35 | } 36 | } 37 | } 38 | 39 | tasks.wrapper { 40 | gradleVersion = "8.9" 41 | } 42 | -------------------------------------------------------------------------------- /gradle-plugin/gradle.properties: -------------------------------------------------------------------------------- 1 | group=com.github.turansky.yfiles 2 | version=6.21.1-SNAPSHOT 3 | kfc.pom.name=yFiles Kotlin Gradle Plugin 4 | kfc.pom.description=yFiles Kotlin Gradle Plugin 5 | kfc.pom.inception.year=2019 6 | kfc.pom.url=https\://github.com/turansky/yfiles-kotlin 7 | -------------------------------------------------------------------------------- /gradle-plugin/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/gradle-plugin/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle-plugin/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradle-plugin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | ./gradlew clean 7 | ./gradlew build 8 | ./gradlew preparePublish 9 | ./gradlew publishPlugins 10 | ./gradlew prepareDevelopment 11 | -------------------------------------------------------------------------------- /gradle-plugin/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "gradle-plugin" 2 | 3 | dependencyResolutionManagement { 4 | repositories { 5 | mavenCentral() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /gradle-plugin/src/main/kotlin/com/github/turansky/yfiles/compiler/backend/common/YFiles.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.compiler.backend.common 2 | 3 | import org.jetbrains.kotlin.descriptors.ClassDescriptor 4 | import org.jetbrains.kotlin.name.FqName 5 | import org.jetbrains.kotlin.name.Name.identifier 6 | import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe 7 | import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces 8 | 9 | private val YFILES_PACKAGE_ID = identifier("yfiles") 10 | private val YFILES_PACKAGE = FqName(YFILES_PACKAGE_ID.identifier) 11 | internal val LANG_PACKAGE = YFILES_PACKAGE.child(identifier("lang")) 12 | internal val YOBJECT = LANG_PACKAGE.child(identifier("YObject")) 13 | internal val YENUM = LANG_PACKAGE.child(identifier("YEnum")) 14 | 15 | internal val YCLASS_NAME = identifier("YClass") 16 | internal val BASE_CLASS_NAME = identifier("BaseClass") 17 | 18 | internal val FqName.isYFiles: Boolean 19 | get() = startsWith(YFILES_PACKAGE_ID) 20 | 21 | internal val ClassDescriptor.locatedInYFilesPackage: Boolean 22 | get() = fqNameSafe.isYFiles 23 | 24 | private val ClassDescriptor.isYObject: Boolean 25 | get() = isExternal && fqNameSafe == YOBJECT 26 | 27 | internal val ClassDescriptor.isYEnum: Boolean 28 | get() = isExternal && fqNameSafe == YENUM 29 | 30 | internal fun ClassDescriptor.isYFilesInterface(): Boolean = 31 | isExternal and (isYObject or implementsYObject) 32 | 33 | internal val ClassDescriptor.implementsYObjectDirectly: Boolean 34 | get() = getSuperInterfaces() 35 | .any { it.isYObject } 36 | 37 | private val ClassDescriptor.implementsYObject: Boolean 38 | get() { 39 | if (implementsYObjectDirectly) { 40 | return true 41 | } 42 | 43 | return getSuperInterfaces() 44 | .any { it.implementsYObject } 45 | } 46 | 47 | internal val ClassDescriptor.implementsYFilesInterface: Boolean 48 | get() = getSuperInterfaces() 49 | .any { it.isYFilesInterface() } 50 | -------------------------------------------------------------------------------- /gradle-plugin/src/main/kotlin/com/github/turansky/yfiles/compiler/diagnostic/YError.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.compiler.diagnostic 2 | 3 | import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0 4 | import org.jetbrains.kotlin.diagnostics.Errors 5 | import org.jetbrains.kotlin.diagnostics.Severity.ERROR 6 | import org.jetbrains.kotlin.psi.KtClassOrObject 7 | import org.jetbrains.kotlin.psi.KtElement 8 | import kotlin.reflect.KClass 9 | 10 | private fun errorDiagnosticFactory(): DiagnosticFactory0 = 11 | DiagnosticFactory0.create(ERROR) 12 | 13 | private fun initialize(klass: KClass<*>) { 14 | Errors.Initializer.initializeFactoryNames(klass.java) 15 | } 16 | 17 | internal object BaseClassErrors { 18 | @JvmField 19 | val INTERFACE_IMPLEMENTING_NOT_SUPPORTED: DiagnosticFactory0 = 20 | errorDiagnosticFactory() 21 | 22 | @JvmField 23 | val SUPER_CLASS_NOT_SUPPORTED: DiagnosticFactory0 = 24 | errorDiagnosticFactory() 25 | 26 | @JvmField 27 | val INTERFACE_MIXING_NOT_SUPPORTED: DiagnosticFactory0 = 28 | errorDiagnosticFactory() 29 | 30 | @JvmField 31 | val INLINE_CLASS_NOT_SUPPORTED: DiagnosticFactory0 = 32 | errorDiagnosticFactory() 33 | 34 | init { 35 | initialize(BaseClassErrors::class) 36 | } 37 | } 38 | 39 | internal object YObjectErrors { 40 | @JvmField 41 | val INTERFACE_IMPLEMENTING_NOT_SUPPORTED: DiagnosticFactory0 = 42 | errorDiagnosticFactory() 43 | 44 | init { 45 | initialize(YObjectErrors::class) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /gradle-plugin/src/main/kotlin/com/github/turansky/yfiles/compiler/diagnostic/YMessagesExtension.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.compiler.diagnostic 2 | 3 | import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages 4 | import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap 5 | 6 | private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { 7 | DiagnosticFactoryToRendererMap("yfiles").apply { 8 | put( 9 | BaseClassErrors.INTERFACE_IMPLEMENTING_NOT_SUPPORTED, 10 | "yFiles interface implementing supported only for ordinary classes" 11 | ) 12 | put( 13 | BaseClassErrors.SUPER_CLASS_NOT_SUPPORTED, 14 | "yFiles interface implementing not supported for classes with super class" 15 | ) 16 | put( 17 | BaseClassErrors.INTERFACE_MIXING_NOT_SUPPORTED, 18 | "yFiles interfaces could't be mixed with non-yFiles interfaces" 19 | ) 20 | put( 21 | BaseClassErrors.INLINE_CLASS_NOT_SUPPORTED, 22 | "yFiles interface implementing not supported for inline classes" 23 | ) 24 | 25 | put( 26 | YObjectErrors.INTERFACE_IMPLEMENTING_NOT_SUPPORTED, 27 | "Interface implementing not supported for direct `yfiles.lang.YObject` inheritors" 28 | ) 29 | } 30 | } 31 | 32 | internal object YMessagesExtension : DefaultErrorMessages.Extension { 33 | override fun getMap(): DiagnosticFactoryToRendererMap = 34 | DIAGNOSTIC_FACTORY_TO_RENDERER 35 | } 36 | -------------------------------------------------------------------------------- /gradle-plugin/src/main/kotlin/com/github/turansky/yfiles/gradle/plugin/KotlinPluginArtifact.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.gradle.plugin 2 | 3 | import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact 4 | 5 | internal val KOTLIN_PLUGIN_ARTIFACT: SubpluginArtifact 6 | get() = SubpluginArtifact( 7 | groupId = "com.github.turansky.yfiles", 8 | artifactId = "gradle-plugin", 9 | version = "6.21.1-SNAPSHOT" 10 | ) 11 | -------------------------------------------------------------------------------- /gradle-plugin/src/main/kotlin/com/github/turansky/yfiles/gradle/plugin/YFilesGradleSubplugin.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.gradle.plugin 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.provider.Provider 5 | import org.jetbrains.kotlin.gradle.plugin.* 6 | 7 | private val YFILES_COMPILER_PLUGIN_ID = "com.github.turansky.yfiles" 8 | 9 | class YFilesGradleSubplugin : KotlinCompilerPluginSupportPlugin { 10 | override fun apply(target: Project): Unit = with(target) { 11 | // add compiler support 12 | } 13 | 14 | override fun isApplicable( 15 | kotlinCompilation: KotlinCompilation<*>, 16 | ): Boolean = 17 | kotlinCompilation.target.platformType == KotlinPlatformType.js 18 | 19 | override fun applyToCompilation( 20 | kotlinCompilation: KotlinCompilation<*>, 21 | ): Provider> = 22 | kotlinCompilation.target.project 23 | .provider { emptyList() } 24 | 25 | override fun getCompilerPluginId(): String = 26 | YFILES_COMPILER_PLUGIN_ID 27 | 28 | override fun getPluginArtifact(): SubpluginArtifact = 29 | KOTLIN_PLUGIN_ARTIFACT 30 | } 31 | -------------------------------------------------------------------------------- /gradle-plugin/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/gradle-plugin/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xms2g -Xmx4g 2 | org.gradle.parallel=true 3 | 4 | kotlin.version=2.1.10 5 | kfc.version=13.7.0 6 | kotlin-wrappers.version=2025.2.8 7 | 8 | yfiles.api.url=https://docs.yworks.com/yfileshtml/assets/api.0ca73b0b.js 9 | yfiles.devguide.url=https://docs.yworks.com/yfileshtml/assets/dguide.3fcdb1e9.js 10 | # vsdx.api.url=https://docs.yworks.com/vsdx-html/assets/api.6cb6e578.js 11 | vsdx.api.url=https://docs.yworks.com/vsdx-html/assets/api.daa52818.js 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /idea-plugin-test/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | 4 | # IDEA ignore list 5 | .idea/ 6 | *.iml 7 | -------------------------------------------------------------------------------- /idea-plugin-test/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("multiplatform") version "2.1.10" apply false 3 | kotlin("plugin.js-plain-objects") version "2.1.10" apply false 4 | id("io.github.turansky.kfc.library") version "13.7.0" 5 | id("com.github.turansky.yfiles") version "6.20.0" 6 | } 7 | 8 | dependencies { 9 | jsMainImplementation("com.yworks.yfiles:yfiles-kotlin:26.0.4-SNAPSHOT") 10 | } 11 | 12 | tasks.wrapper { 13 | gradleVersion = "8.9" 14 | } 15 | -------------------------------------------------------------------------------- /idea-plugin-test/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/idea-plugin-test/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /idea-plugin-test/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /idea-plugin-test/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | mavenLocal() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /idea-plugin-test/src/main/kotlin/ColorTest.kt: -------------------------------------------------------------------------------- 1 | import yfiles.view.Color 2 | import yfiles.view.Color.Companion.BLUE 3 | import yfiles.view.Color.Companion.LIME 4 | import yfiles.view.Color.Companion.RED 5 | 6 | class Color2(val source: String) 7 | 8 | fun colorTest() { 9 | val color_01_1 = Color("red") 10 | val color_01_2 = Color("lime") 11 | val color_01_3 = Color("blue") 12 | 13 | val color_11_1 = Color("#F00") 14 | val color_11_2 = Color("#0F0") 15 | val color_11_3 = Color("#00F") 16 | 17 | val color_12_1 = Color("#FF0000") 18 | val color_12_2 = Color("#00FF00") 19 | val color_12_3 = Color("#0000FF") 20 | 21 | val color_21_1 = Color("rgb(255, 0, 0)") 22 | val color_21_2 = Color("rgb(0, 255, 0)") 23 | val color_21_3 = Color("rgb(0, 0, 255)") 24 | 25 | val color_31_1 = Color("hsl(0, 100%, 50%)") 26 | val color_31_2 = Color("hsl(120, 100%, 50%)") 27 | val color_31_3 = Color("hsl(240, 100%, 50%)") 28 | 29 | val color_71_1 = Color.RED 30 | val color_71_2 = Color.LIME 31 | val color_71_3 = Color.BLUE 32 | 33 | val color_72_1 = RED 34 | val color_72_2 = LIME 35 | val color_72_3 = BLUE 36 | } 37 | 38 | // language=CSS 39 | val COLOR_CSS = """ 40 | #red { 41 | background-color: red 42 | border-color: hsl(0, 100%, 50%) 43 | } 44 | 45 | #green { 46 | background-color: lime 47 | border-color: hsl(120, 100%, 50%) 48 | } 49 | 50 | #blue { 51 | background-color: blue 52 | border-color: hsl(240, 100%, 50%) 53 | } 54 | """.trimIndent() 55 | -------------------------------------------------------------------------------- /idea-plugin-test/src/main/kotlin/CustomObject.kt: -------------------------------------------------------------------------------- 1 | import yfiles.lang.YObject 2 | import yfiles.styles.IArrow 3 | import kotlinx.browser.window 4 | import yfiles.lang.IClassMetadata 5 | import yfiles.lang.classMetadata 6 | 7 | /** 8 | * [yfiles.styles.ITemplateStyleBindingContext.zoom] 9 | */ 10 | 11 | class CustomObject : YObject { 12 | fun hallo() { 13 | window.alert("Hallo from CustomObject!") 14 | } 15 | 16 | companion object : IClassMetadata by classMetadata() 17 | } 18 | 19 | class OtherCustomObject : YObject { 20 | fun hallo() { 21 | window.alert("Hallo from OtherCustomObject!") 22 | } 23 | } 24 | 25 | abstract class AbstractArrow : IArrow {} 26 | -------------------------------------------------------------------------------- /idea-plugin-test/src/main/kotlin/FillTest.kt: -------------------------------------------------------------------------------- 1 | import yfiles.view.Color.Companion.BLUE 2 | import yfiles.view.Color.Companion.LIME 3 | import yfiles.view.Color.Companion.RED 4 | import yfiles.view.Fill 5 | 6 | class Fill2(val source: String) 7 | 8 | fun fillTest() { 9 | val fill_01_1 = Fill("red") 10 | val fill_01_2 = Fill("lime") 11 | val fill_01_3 = Fill("blue") 12 | 13 | val fill_11_1 = Fill("#F00") 14 | val fill_11_2 = Fill("#0F0") 15 | val fill_11_3 = Fill("#00F") 16 | 17 | val fill_12_1 = Fill("#FF0000") 18 | val fill_12_2 = Fill("#00FF00") 19 | val fill_12_3 = Fill("#0000FF") 20 | 21 | val fill_21_1 = Fill("rgb(255, 0, 0)") 22 | val fill_21_2 = Fill("rgb(0, 255, 0)") 23 | val fill_21_3 = Fill("rgb(0, 0, 255)") 24 | 25 | val fill_31_1 = Fill("hsl(0, 100%, 50%)") 26 | val fill_31_2 = Fill("hsl(120, 100%, 50%)") 27 | val fill_31_3 = Fill("hsl(240, 100%, 50%)") 28 | 29 | val fill_71_1 = Fill.RED 30 | val fill_71_2 = Fill.LIME 31 | val fill_71_3 = Fill.BLUE 32 | 33 | val fill_72_1 = RED 34 | val fill_72_2 = LIME 35 | val fill_72_3 = BLUE 36 | } 37 | 38 | // language=CSS 39 | val FILL_CSS = """ 40 | #red { 41 | background-color: red 42 | border-color: hsl(0, 100%, 50%) 43 | } 44 | 45 | #green { 46 | background-color: lime 47 | border-color: hsl(120, 100%, 50%) 48 | } 49 | 50 | #blue { 51 | background-color: blue 52 | border-color: hsl(240, 100%, 50%) 53 | } 54 | """.trimIndent() 55 | -------------------------------------------------------------------------------- /idea-plugin-test/src/main/kotlin/MaterialPalette.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | import yfiles.view.Color 4 | 5 | object PrimaryColor { 6 | val RED: Color = Color("#F44336") 7 | val PINK: Color = Color("#E91E63") 8 | val PURPLE: Color = Color("#9C27B0") 9 | val DEEP_PURPLE: Color = Color("#673AB7") 10 | val INDIGO: Color = Color("#3F51B5") 11 | val BLUE: Color = Color("#2196F3") 12 | val LIGHT_BLUE: Color = Color("#03A9F4") 13 | val CYAN: Color = Color("#00BCD4") 14 | val TEAL: Color = Color("#009688") 15 | val GREEN: Color = Color("#4CAF50") 16 | val LIGHT_GREEN: Color = Color("#8BC34A") 17 | val LIME: Color = Color("#CDDC39") 18 | val YELLOW: Color = Color("#FFEB3B") 19 | val AMBER: Color = Color("#FFC107") 20 | val ORANGE: Color = Color("#FF9800") 21 | val DEEP_ORANGE: Color = Color("#FF5722") 22 | val BROWN: Color = Color("#795548") 23 | val GREY: Color = Color("#9E9E9E") 24 | val BLUE_GREY: Color = Color("#607D8B") 25 | } 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /idea-plugin-test/src/main/resources/binding.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 25 | 26 | 33 | 34 | 39 | 40 | -------------------------------------------------------------------------------- /idea-plugin-test/src/main/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SVG Templates 6 | 7 | 8 | 16 | 17 | 18 | 26 | 27 | 43 | 44 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /idea-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | 4 | # IDEA ignore list 5 | .idea/ 6 | *.iml 7 | -------------------------------------------------------------------------------- /idea-plugin/README.md: -------------------------------------------------------------------------------- 1 | [![CI Status](https://github.com/turansky/yfiles-kotlin/workflows/idea%20plugin/badge.svg)](https://github.com/turansky/yfiles-kotlin/actions) 2 | [![IntelliJ IDEA Plugin](https://img.shields.io/jetbrains/plugin/v/13384-yfiles?label=plugin&logo=intellij-idea)](https://plugins.jetbrains.com/plugin/13384-yfiles/) 3 | [![IntelliJ IDEA Plugin](https://img.shields.io/jetbrains/plugin/d/13384-yfiles?logo=intellij-idea)](https://plugins.jetbrains.com/plugin/13384-yfiles/) 4 | [![Kotlin](https://img.shields.io/badge/kotlin-2.1.10-blue.svg?logo=kotlin)](http://kotlinlang.org) 5 | 6 | # yFiles IDEA plugin 7 | + Check [inheritance rules](../gradle-plugin) on the fly 8 | + Highlight binding syntax 9 | 10 | ## Examples 11 | ![Example #1](assets/template-binding-autocomplete.png) 12 | ![Example #2](assets/template-mime-type-support.png) 13 | ![Example #3](assets/class-interface-object.png) 14 | ![Example #4](assets/interface-combinations.png) 15 | -------------------------------------------------------------------------------- /idea-plugin/assets/class-interface-object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/idea-plugin/assets/class-interface-object.png -------------------------------------------------------------------------------- /idea-plugin/assets/interface-combinations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/idea-plugin/assets/interface-combinations.png -------------------------------------------------------------------------------- /idea-plugin/assets/template-binding-autocomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/idea-plugin/assets/template-binding-autocomplete.png -------------------------------------------------------------------------------- /idea-plugin/assets/template-mime-type-support.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/idea-plugin/assets/template-mime-type-support.png -------------------------------------------------------------------------------- /idea-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "2.1.10" 3 | id("org.jetbrains.intellij") version "1.17.3" 4 | id("io.github.turansky.kfc.version") version "13.7.0" 5 | } 6 | 7 | repositories { 8 | mavenCentral() 9 | } 10 | 11 | intellij { 12 | pluginName = "yfiles" 13 | 14 | type = "IU" 15 | version = "2024.1" 16 | 17 | plugins = listOf( 18 | "java", 19 | "org.jetbrains.kotlin", 20 | "JavaScript" 21 | ) 22 | } 23 | 24 | tasks { 25 | compileKotlin { 26 | kotlinOptions.jvmTarget = "17" 27 | } 28 | 29 | runIde { 30 | jvmArgs( 31 | "-Xms1g", 32 | "-Xmx4g" 33 | ) 34 | } 35 | 36 | patchPluginXml { 37 | sinceBuild = "241.*" 38 | untilBuild = "242.*" 39 | } 40 | 41 | publishPlugin { 42 | token = project.property("intellij.publish.token") as String 43 | } 44 | 45 | buildSearchableOptions { 46 | enabled = false 47 | } 48 | 49 | wrapper { 50 | gradleVersion = "8.9" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /idea-plugin/gradle.properties: -------------------------------------------------------------------------------- 1 | group=com.github.turansky.yfiles 2 | version=1.32.1-SNAPSHOT 3 | org.gradle.jvmargs=-Xms2g -Xmx4g 4 | kotlin.stdlib.default.dependency=false 5 | -------------------------------------------------------------------------------- /idea-plugin/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turansky/yfiles-kotlin/876c8657999f0b994c6fc59e2478dee0a40be3e1/idea-plugin/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /idea-plugin/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /idea-plugin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | ./gradlew setReleaseVersion 7 | ./gradlew buildPlugin 8 | ./gradlew verifyPlugin 9 | ./gradlew publishPlugin 10 | ./gradlew setNextSnapshotVersion 11 | -------------------------------------------------------------------------------- /idea-plugin/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "idea-plugin" 2 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/Binding.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | import com.github.turansky.yfiles.ide.binding.BindingDirective.* 4 | 5 | private const val TAG: String = "yfiles.graph.Tag" 6 | 7 | internal sealed class Binding { 8 | abstract val parentName: String 9 | abstract val name: String? 10 | abstract val converter: String? 11 | abstract val parameter: String? 12 | 13 | abstract val parentReference: String 14 | 15 | fun toCode(): String { 16 | val target = join(parentName, ".", name) 17 | val converter = converter ?: return target 18 | val parameter = parameter ?: return "$converter($target)" 19 | return """$converter($target, "$parameter")""" 20 | } 21 | } 22 | 23 | internal data class TagBinding( 24 | override val name: String?, 25 | override val converter: String?, 26 | override val parameter: String?, 27 | ) : Binding() { 28 | override val parentName: String = "tag" 29 | override val parentReference: String = TAG 30 | } 31 | 32 | internal data class TemplateBinding( 33 | override val name: String?, 34 | override val converter: String?, 35 | override val parameter: String?, 36 | ) : Binding() { 37 | override val parentName: String = "context" 38 | override val parentReference: String = ContextProperty.findParentClass(name) 39 | } 40 | 41 | internal fun String.toBinding(): Binding? { 42 | val code = trimBraces() ?: return null 43 | val blocks = code.split(",") 44 | if (blocks.size !in 1..3) return null 45 | 46 | val dataMap = mutableMapOf() 47 | for (block in blocks) { 48 | val (directive, value) = BindingParser.find(block) ?: continue 49 | 50 | if (dataMap.containsKey(directive)) return null 51 | 52 | dataMap[directive] = value 53 | } 54 | 55 | return when { 56 | dataMap.containsKey(BINDING) 57 | -> TagBinding( 58 | name = dataMap[BINDING], 59 | converter = dataMap[CONVERTER], 60 | parameter = dataMap[PARAMETER] 61 | ) 62 | 63 | dataMap.containsKey(TEMPLATE_BINDING) 64 | -> TemplateBinding( 65 | name = dataMap[TEMPLATE_BINDING], 66 | converter = dataMap[CONVERTER], 67 | parameter = dataMap[PARAMETER] 68 | ) 69 | 70 | else -> null 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/BindingAnnotator.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | import com.github.turansky.yfiles.ide.binding.BindingToken.ERROR 4 | import com.intellij.lang.annotation.AnnotationHolder 5 | import com.intellij.lang.annotation.Annotator 6 | import com.intellij.lang.annotation.HighlightSeverity 7 | import com.intellij.openapi.util.TextRange 8 | import com.intellij.psi.PsiElement 9 | import com.intellij.psi.xml.XmlAttributeValue 10 | 11 | internal class BindingAnnotator : Annotator { 12 | override fun annotate( 13 | element: PsiElement, 14 | holder: AnnotationHolder, 15 | ) { 16 | if (element !is XmlAttributeValue) return 17 | if (!element.bindingEnabled) return 18 | 19 | val value = element.value 20 | value.toBinding() ?: return 21 | 22 | val offset = element.valueTextRange.startOffset 23 | BindingParser.parse(value).forEach { (token, range) -> 24 | holder.info(token, range.shiftRight(offset)) 25 | } 26 | } 27 | } 28 | 29 | private fun AnnotationHolder.info( 30 | token: BindingToken, 31 | range: TextRange, 32 | ) { 33 | val severity = when (token) { 34 | ERROR -> HighlightSeverity.ERROR 35 | else -> HighlightSeverity.INFORMATION 36 | } 37 | 38 | newSilentAnnotation(severity) 39 | .textAttributes(BindingHighlightingColors[token]) 40 | .range(range) 41 | .create() 42 | } 43 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/BindingDirective.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | internal enum class BindingDirective( 4 | val key: String, 5 | ) { 6 | BINDING("Binding"), 7 | TEMPLATE_BINDING("TemplateBinding"), 8 | CONVERTER("Converter"), 9 | PARAMETER("Parameter"); 10 | 11 | override fun toString(): String = key 12 | 13 | companion object { 14 | private val map = values().associateBy { it.key } 15 | 16 | fun find(key: String): BindingDirective = map.getValue(key) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/BindingHighlightingColors.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | import com.intellij.openapi.editor.XmlHighlighterColors 4 | import com.intellij.openapi.editor.colors.CodeInsightColors 5 | import com.intellij.openapi.editor.colors.TextAttributesKey 6 | import com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey 7 | 8 | internal object BindingHighlightingColors { 9 | private val LANGUAGE_INJECTION = createTextAttributesKey("BINDING_LANGUAGE_INJECTION", XmlHighlighterColors.XML_INJECTED_LANGUAGE_FRAGMENT) 10 | private val BRACE = createTextAttributesKey("BINDING_BRACE", XmlHighlighterColors.XML_ATTRIBUTE_NAME) 11 | private val KEYWORD = createTextAttributesKey("BINDING_KEYWORD", XmlHighlighterColors.XML_NS_PREFIX) 12 | private val ASSIGN = createTextAttributesKey("BINDING_ASSIGN", KEYWORD) 13 | 14 | private val ARGUMENT = createTextAttributesKey("BINDING_ARGUMENT", XmlHighlighterColors.XML_ATTRIBUTE_NAME) 15 | private val VALUE = createTextAttributesKey("BINDING_VALUE", XmlHighlighterColors.XML_ATTRIBUTE_VALUE) 16 | private val COMMA = createTextAttributesKey("BINDING_COMMA", ARGUMENT) 17 | 18 | private val ERROR = createTextAttributesKey("ERROR", CodeInsightColors.WRONG_REFERENCES_ATTRIBUTES) 19 | 20 | private val MAP = mapOf( 21 | BindingToken.LANGUAGE_INJECTION to LANGUAGE_INJECTION, 22 | BindingToken.BRACE to BRACE, 23 | BindingToken.COMMA to COMMA, 24 | 25 | BindingToken.KEYWORD to KEYWORD, 26 | BindingToken.ASSIGN to ASSIGN, 27 | BindingToken.ARGUMENT to ARGUMENT, 28 | BindingToken.VALUE to VALUE, 29 | BindingToken.ERROR to ERROR, 30 | ) 31 | 32 | operator fun get(token: BindingToken): TextAttributesKey = 33 | MAP.getValue(token) 34 | } 35 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/BindingReferenceContributor.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | import com.github.turansky.yfiles.ide.binding.BindingDirective.* 4 | import com.intellij.patterns.PlatformPatterns 5 | import com.intellij.psi.* 6 | import com.intellij.psi.xml.XmlAttributeValue 7 | import com.intellij.util.ProcessingContext 8 | 9 | internal class BindingReferenceContributor : PsiReferenceContributor() { 10 | override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { 11 | registrar.registerReferenceProvider( 12 | PlatformPatterns.psiElement(XmlAttributeValue::class.java), 13 | BindingReferenceProvider(), 14 | PsiReferenceRegistrar.HIGHER_PRIORITY 15 | ) 16 | } 17 | } 18 | 19 | private class BindingReferenceProvider : PsiReferenceProvider() { 20 | override fun getReferencesByElement( 21 | element: PsiElement, 22 | context: ProcessingContext, 23 | ): Array { 24 | element as XmlAttributeValue 25 | 26 | if (!element.bindingEnabled) 27 | return PsiReference.EMPTY_ARRAY 28 | 29 | val binding = element.value.toBinding() 30 | ?: return PsiReference.EMPTY_ARRAY 31 | 32 | val value = element.value 33 | val valueOffset = element.valueTextRange.startOffset - element.textRange.startOffset 34 | 35 | val result = mutableListOf() 36 | BindingParser.parse(value).forEach { (token, range, directive) -> 37 | when (token) { 38 | BindingToken.KEYWORD -> when (directive) { 39 | BINDING, 40 | TEMPLATE_BINDING, 41 | -> result += ClassReference( 42 | element = element, 43 | rangeInElement = range.shiftRight(valueOffset), 44 | className = binding.parentReference 45 | ) 46 | 47 | CONVERTER, 48 | PARAMETER, 49 | -> result += PropertyReference( 50 | element = element, 51 | rangeInElement = range.shiftRight(valueOffset), 52 | property = Properties.TEMPLATE_CONVERTERS 53 | ) 54 | 55 | else -> Unit 56 | } 57 | 58 | BindingToken.ARGUMENT -> when (directive) { 59 | TEMPLATE_BINDING, 60 | -> result += ContextPropertyReference( 61 | element = element, 62 | rangeInElement = range.shiftRight(valueOffset), 63 | propertyName = binding.name!! 64 | ) 65 | 66 | else -> Unit 67 | } 68 | 69 | else -> Unit 70 | } 71 | } 72 | 73 | return result.toTypedArray() 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/BindingToken.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | internal enum class BindingToken { 4 | LANGUAGE_INJECTION, 5 | BRACE, 6 | COMMA, 7 | 8 | KEYWORD, 9 | ASSIGN, 10 | ARGUMENT, 11 | VALUE, 12 | 13 | ERROR 14 | } 15 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/Bindings.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | import com.github.turansky.yfiles.ide.template.TemplateFileType 4 | import com.intellij.psi.PsiFile 5 | import com.intellij.psi.xml.XmlAttributeValue 6 | 7 | internal val XmlAttributeValue.bindingEnabled: Boolean 8 | get() = containingFile.bindingEnabled 9 | 10 | private val PsiFile.bindingEnabled: Boolean 11 | get() = fileType.defaultExtension 12 | .equals(TemplateFileType.defaultExtension, ignoreCase = true) 13 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/ContextProperty.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement 4 | import com.intellij.codeInsight.lookup.LookupElementBuilder 5 | import com.intellij.icons.AllIcons 6 | 7 | private const val CONTEXT: String = "yfiles.styles.ITemplateStyleBindingContext" 8 | private const val LABEL_CONTEXT: String = "yfiles.styles.ILabelTemplateStyleBindingContext" 9 | 10 | internal val CONTEXT_CLASSES: List = listOf( 11 | CONTEXT, 12 | LABEL_CONTEXT 13 | ) 14 | 15 | internal val CONTEXT_PROPERTY_VARIANTS: Array by lazy { 16 | ContextProperty.values() 17 | .map { it.toVariant() } 18 | .toTypedArray() 19 | } 20 | 21 | internal enum class ContextProperty( 22 | override val className: String = CONTEXT, 23 | ) : IProperty { 24 | bounds, 25 | canvasComponent, 26 | height, 27 | item, 28 | itemFocused, 29 | itemHighlighted, 30 | itemSelected, 31 | styleTag, 32 | width, 33 | zoom, 34 | 35 | isFlipped(LABEL_CONTEXT), 36 | isUpsideDown(LABEL_CONTEXT), 37 | labelText(LABEL_CONTEXT); 38 | 39 | companion object { 40 | private val MAP = values() 41 | .associateBy { it.name } 42 | 43 | private val COMPLEX_MAP = listOf( 44 | bounds, 45 | canvasComponent, 46 | item, 47 | styleTag 48 | ).associateBy { it.name } 49 | 50 | internal fun findParentClass(propertyName: String?): String = 51 | MAP[propertyName]?.className ?: CONTEXT 52 | 53 | internal fun find(name: String): ContextProperty? = 54 | MAP[name] 55 | 56 | internal fun findComplex(name: String): ContextProperty? = 57 | COMPLEX_MAP[name] 58 | } 59 | } 60 | 61 | private fun ContextProperty.toVariant(): LookupElement = 62 | LookupElementBuilder.create(name) 63 | .withIcon(AllIcons.Nodes.Field) 64 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/Properties.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | internal object Properties { 4 | val TEMPLATE_CONVERTERS: IProperty = SimpleProperty("yfiles.styles.Templates", "CONVERTERS") 5 | } 6 | 7 | internal interface IProperty { 8 | val className: String 9 | val name: String 10 | } 11 | 12 | private class SimpleProperty( 13 | override val className: String, 14 | override val name: String, 15 | ) : IProperty 16 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/PsiReferences.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | import com.github.turansky.yfiles.ide.psi.DefaultPsiFinder 4 | import com.intellij.openapi.util.TextRange 5 | import com.intellij.psi.PsiElement 6 | import com.intellij.psi.PsiReference 7 | import com.intellij.psi.PsiReferenceBase 8 | import com.intellij.psi.xml.XmlAttributeValue 9 | 10 | internal class ClassReference( 11 | element: XmlAttributeValue, 12 | rangeInElement: TextRange, 13 | private val className: String, 14 | ) : PsiReferenceBase(element, rangeInElement, true) { 15 | override fun resolve(): PsiElement? = 16 | DefaultPsiFinder.findClass(element, className) 17 | } 18 | 19 | internal open class PropertyReference( 20 | element: XmlAttributeValue, 21 | rangeInElement: TextRange, 22 | private val property: IProperty, 23 | ) : PsiReferenceBase(element, rangeInElement, true) { 24 | override fun resolve(): PsiElement? = 25 | DefaultPsiFinder.findProperty(element, property.className, property.name) 26 | } 27 | 28 | internal fun ContextPropertyReference( 29 | element: XmlAttributeValue, 30 | rangeInElement: TextRange, 31 | propertyName: String, 32 | ): PsiReference { 33 | val property = when { 34 | "." !in propertyName -> null 35 | ".." in propertyName -> null 36 | propertyName.endsWith(".") -> null 37 | else -> ContextProperty.findComplex(propertyName.substringBefore(".")) 38 | } 39 | 40 | if (property != null) { 41 | return ContextPropertyReference( 42 | element = element, 43 | rangeInElement = TextRange.from(rangeInElement.startOffset, property.name.length), 44 | property = property 45 | ) 46 | } 47 | 48 | return ContextPropertyReference( 49 | element = element, 50 | rangeInElement = rangeInElement, 51 | property = ContextProperty.find(propertyName) 52 | ) 53 | } 54 | 55 | private class ContextPropertyReference( 56 | element: XmlAttributeValue, 57 | rangeInElement: TextRange, 58 | private val property: IProperty?, 59 | ) : PsiReferenceBase(element, rangeInElement, property != null) { 60 | override fun resolve(): PsiElement? = 61 | if (property != null) { 62 | DefaultPsiFinder.findProperty(element, property.className, property.name) 63 | } else { 64 | null 65 | } 66 | 67 | override fun getVariants(): Array = 68 | DefaultPsiFinder.findPropertyVariants(element, CONTEXT_CLASSES) 69 | ?: CONTEXT_PROPERTY_VARIANTS 70 | } 71 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/binding/Strings.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.binding 2 | 3 | internal fun join( 4 | first: String, 5 | delimiter: String, 6 | second: String?, 7 | ): String = 8 | if (second != null) { 9 | "$first$delimiter$second" 10 | } else { 11 | first 12 | } 13 | 14 | 15 | internal fun String.trimBraces(): String? = 16 | when { 17 | !startsWith("{") -> null 18 | !endsWith("}") -> null 19 | else -> removePrefix("{").removeSuffix("}").trim().takeIf { it.isNotEmpty() } 20 | } 21 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/color/ColorAnnotation.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.color 2 | 3 | import com.intellij.lang.annotation.AnnotationHolder 4 | import com.intellij.lang.annotation.HighlightSeverity 5 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors 6 | import com.intellij.openapi.editor.markup.GutterIconRenderer 7 | import com.intellij.openapi.util.TextRange 8 | import com.intellij.xml.util.ColorIconCache 9 | import javax.swing.Icon 10 | 11 | internal fun AnnotationHolder.createColorAnnotation( 12 | colorText: String, 13 | format: ColorFormat, 14 | range: TextRange, 15 | ) { 16 | newSilentAnnotation(HighlightSeverity.INFORMATION) 17 | .textAttributes(DefaultLanguageHighlighterColors.VALID_STRING_ESCAPE) 18 | .gutterIconRenderer(ColorIconRenderer(colorText, format)) 19 | .range(range) 20 | .create() 21 | } 22 | 23 | private class ColorIconRenderer( 24 | private val colorText: String, 25 | private val format: ColorFormat, 26 | ) : GutterIconRenderer() { 27 | override fun getIcon(): Icon = 28 | ColorIconCache.getIconCache() 29 | .getIcon(format.parse(colorText), ICON_SIZE) 30 | 31 | override fun equals(other: Any?): Boolean { 32 | if (this === other) return true 33 | if (javaClass != other?.javaClass) return false 34 | 35 | other as ColorIconRenderer 36 | 37 | if (colorText != other.colorText) return false 38 | if (format != other.format) return false 39 | 40 | return true 41 | } 42 | 43 | override fun hashCode(): Int = 44 | 31 * colorText.hashCode() + format.hashCode() 45 | 46 | companion object { 47 | private const val ICON_SIZE = 8 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/color/ColorFormat.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.color 2 | 3 | import com.intellij.xml.util.ColorMap 4 | import java.awt.Color 5 | 6 | enum class ColorFormat { 7 | NAMED { 8 | override fun matches(color: String): Boolean = 9 | ColorMap.getHexCodeForColorName(color) != null 10 | 11 | override fun parse(color: String): Color { 12 | val code = ColorMap.getHexCodeForColorName(color)!! 13 | return ColorMap.getColor(code)!! 14 | } 15 | }, 16 | 17 | SHORT_HEX { 18 | private val PATTERN = Regex("#[A-Fa-f0-9]{3}") 19 | private val LENGTH = 4 20 | 21 | override fun matches(color: String): Boolean = 22 | color.length == LENGTH && PATTERN.matches(color) 23 | 24 | override fun parse(color: String): Color { 25 | val code = color.substring(1).toInt(16) 26 | return Color( 27 | (code shr 8 and 15) * 17, 28 | (code shr 4 and 15) * 17, 29 | (code and 15) * 17 30 | ) 31 | } 32 | }, 33 | 34 | HEX { 35 | private val PATTERN = Regex("#[A-Fa-f0-9]{6}") 36 | private val LENGTH = 7 37 | 38 | override fun matches(color: String): Boolean = 39 | color.length == LENGTH && PATTERN.matches(color) 40 | 41 | override fun parse(color: String): Color = 42 | ColorMap.getColor(color) 43 | }, 44 | 45 | RGB { 46 | private val PATTERN = Regex("rgb\\((\\d{1,3}), (\\d{1,3}), (\\d{1,3})\\)") 47 | private val LENGTH_RANGE = 12..18 48 | 49 | override fun matches(color: String): Boolean = 50 | color.length in LENGTH_RANGE && PATTERN.matches(color) 51 | 52 | override fun parse(color: String): Color { 53 | val (r, g, b) = PATTERN.find(color)!! 54 | .groupValues 55 | .asSequence() 56 | .drop(1) 57 | .map { it.toInt() } 58 | .map { it and 255 } 59 | .toList() 60 | 61 | return Color(r, g, b) 62 | } 63 | }, 64 | 65 | HSL { 66 | private val PATTERN = Regex("hsl\\((\\d{1,3}), (\\d{1,3})%, (\\d{1,3})%\\)") 67 | private val LENGTH_RANGE = 14..20 68 | 69 | override fun matches(color: String): Boolean = 70 | color.length in LENGTH_RANGE && PATTERN.matches(color) 71 | 72 | override fun parse(color: String): Color { 73 | val (h, s, l) = PATTERN.find(color)!! 74 | .groupValues 75 | .asSequence() 76 | .drop(1) 77 | .map { it.toFloat() } 78 | .toList() 79 | 80 | return getHSLColor( 81 | h = h, 82 | s = minOf(s, 100f), 83 | l = minOf(l, 100f) 84 | ) 85 | } 86 | }; 87 | 88 | abstract fun matches(color: String): Boolean 89 | abstract fun parse(color: String): Color 90 | } 91 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/color/HslColor.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.color 2 | 3 | import java.awt.Color 4 | 5 | fun getHSLColor( 6 | h: Float, 7 | s: Float, 8 | l: Float, 9 | a: Float = 1f, 10 | ): Color { 11 | check(s in 0f..100f) 12 | check(l in 0f..100f) 13 | check(a in 0f..1f) 14 | 15 | val hue = h % 360f / 360f 16 | val sp = s / 100f 17 | val lp = l / 100f 18 | 19 | val q = if (lp < 0.5) lp * (1 + sp) else lp + sp - sp * lp 20 | val p = 2 * lp - q 21 | 22 | return Color( 23 | toRGB(p, q, hue + 1f / 3f).coerceIn(0f, 1f), 24 | toRGB(p, q, hue).coerceIn(0f, 1f), 25 | toRGB(p, q, hue - 1f / 3f).coerceIn(0f, 1f), 26 | a 27 | ) 28 | } 29 | 30 | private fun toRGB( 31 | p: Float, 32 | q: Float, 33 | h: Float, 34 | ): Float { 35 | var hue = h 36 | if (hue < 0) hue += 1f 37 | if (hue > 1) hue -= 1f 38 | 39 | return when { 40 | 6 * hue < 1 41 | -> p + (q - p) * 6 * hue 42 | 43 | 2 * hue < 1 44 | -> q 45 | 46 | 3 * hue < 2 47 | -> p + (q - p) * 6 * (2f / 3f - hue) 48 | 49 | else -> p 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/color/KotlinColorAnnotator.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.color 2 | 3 | import com.intellij.lang.annotation.AnnotationHolder 4 | import com.intellij.lang.annotation.Annotator 5 | import com.intellij.psi.PsiElement 6 | import org.jetbrains.kotlin.name.FqName 7 | import org.jetbrains.kotlin.psi.* 8 | 9 | private val COLOR_CLASS_NAMES = setOf( 10 | FqName("yfiles.view.Color"), 11 | FqName("yfiles.view.Fill"), 12 | FqName("yfiles.view.Stroke"), 13 | ) 14 | 15 | private val COLOR_METHOD_NAMES = setOf( 16 | "Color", 17 | "Fill", 18 | ) 19 | 20 | internal class KotlinColorAnnotator : Annotator { 21 | override fun annotate( 22 | element: PsiElement, 23 | holder: AnnotationHolder, 24 | ) { 25 | when (element) { 26 | is KtLiteralStringTemplateEntry, 27 | -> annotate(element, holder) 28 | 29 | is KtProperty, 30 | -> annotate(element, holder) 31 | } 32 | } 33 | 34 | private fun annotate( 35 | element: KtLiteralStringTemplateEntry, 36 | holder: AnnotationHolder, 37 | ) { 38 | val argument = element.parent.parent as? KtValueArgument ?: return 39 | val expression = argument.parent.parent as? KtCallExpression ?: return 40 | if (expression.calleeExpression?.text !in COLOR_METHOD_NAMES) return 41 | 42 | val text = element.text ?: return 43 | val format = ColorFormat.values() 44 | .firstOrNull { it.matches(text) } 45 | ?: return 46 | 47 | holder.createColorAnnotation(text, format, element.textRange) 48 | } 49 | 50 | private fun annotate( 51 | element: KtProperty, 52 | holder: AnnotationHolder, 53 | ) { 54 | val name = element.name ?: return 55 | val range = element.nameIdentifier?.textRange ?: return 56 | 57 | if (element.isVar) return 58 | if (!element.isMember) return 59 | 60 | val companion = element.parent?.parent as? KtObjectDeclaration ?: return 61 | if (!companion.isCompanion()) return 62 | 63 | val klass = companion.parent?.parent as? KtClass ?: return 64 | if (klass.fqName !in COLOR_CLASS_NAMES) return 65 | 66 | val color = name.replace("_", "").lowercase() 67 | if (!ColorFormat.NAMED.matches(color)) return 68 | 69 | holder.createColorAnnotation(color, ColorFormat.NAMED, range) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/documentation/BindingDocumentationProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.documentation 2 | 3 | import com.github.turansky.yfiles.ide.binding.bindingEnabled 4 | import com.github.turansky.yfiles.ide.binding.toBinding 5 | import com.intellij.lang.documentation.AbstractDocumentationProvider 6 | import com.intellij.psi.PsiElement 7 | import com.intellij.psi.xml.XmlAttributeValue 8 | 9 | internal class BindingDocumentationProvider : AbstractDocumentationProvider() { 10 | override fun generateDoc( 11 | element: PsiElement, 12 | originalElement: PsiElement?, 13 | ): String? { 14 | val attributeValue = originalElement?.context as? XmlAttributeValue ?: return null 15 | if (!attributeValue.bindingEnabled) return null 16 | 17 | return attributeValue.value.toBinding()?.let(::documentation) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/documentation/Documentation.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.documentation 2 | 3 | import com.github.turansky.yfiles.ide.binding.Binding 4 | import com.github.turansky.yfiles.ide.binding.TagBinding 5 | import com.github.turansky.yfiles.ide.binding.TemplateBinding 6 | import com.intellij.codeInsight.documentation.DocumentationManagerUtil.createHyperlink 7 | import com.intellij.lang.documentation.DocumentationMarkup.* 8 | 9 | private const val SVG_TEMPLATES_URL: String = "https://docs.yworks.com/yfileshtml/#/dguide/custom-styles_template-styles" 10 | private const val TEMPLATE_BINDING_URL: String = "$SVG_TEMPLATES_URL%23_template_binding" 11 | 12 | internal fun documentation(binding: Binding): String = 13 | buildString { 14 | renderBindingBlock(binding) 15 | renderConverterBlock(binding) 16 | 17 | renderReturnsBlock(binding.toCode()) 18 | renderSeeAlsoBlock() 19 | } 20 | 21 | private fun StringBuilder.renderBindingBlock(binding: Binding) { 22 | renderSection("Binding") { 23 | append("
")
24 |         reference(binding.parentReference, binding.parentName)
25 |         binding.name?.also { name ->
26 |             when (binding) {
27 |                 is TagBinding -> append(".$name")
28 |                 is TemplateBinding -> {
29 |                     append(".")
30 |                     reference("${binding.parentReference}.$name", name)
31 |                 }
32 |             }
33 |         }
34 |         append("
") 35 | } 36 | } 37 | 38 | private fun StringBuilder.renderConverterBlock(binding: Binding) { 39 | val converter = binding.converter ?: return 40 | 41 | renderSection("Converter") { 42 | reference(converter) 43 | } 44 | } 45 | 46 | private fun StringBuilder.renderReturnsBlock(code: String) { 47 | renderSection("Returns") { 48 | append("
")
49 |         append(code)
50 |         append("
") 51 | } 52 | } 53 | 54 | private fun StringBuilder.renderSeeAlsoBlock() { 55 | renderSection("See Also") { 56 | link("SVG Templates", SVG_TEMPLATES_URL) 57 | append(", ") 58 | link("Template Binding", TEMPLATE_BINDING_URL) 59 | } 60 | } 61 | 62 | private fun StringBuilder.renderSection( 63 | title: String, 64 | content: StringBuilder.() -> Unit, 65 | ) { 66 | append(SECTION_HEADER_START, title, ":", SECTION_SEPARATOR) 67 | content() 68 | append(SECTION_END) 69 | } 70 | 71 | private fun StringBuilder.reference( 72 | reference: String, 73 | title: String = reference, 74 | ) { 75 | createHyperlink(this, reference, title, true) 76 | } 77 | 78 | private fun StringBuilder.link( 79 | title: String, 80 | href: String, 81 | ) { 82 | append("""$title""") 83 | } 84 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/highlighter/markers/Icons.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.highlighter.markers 2 | 3 | import com.intellij.openapi.util.IconLoader 4 | import javax.swing.Icon 5 | 6 | internal object Icons { 7 | object Gutter { 8 | val BASE_CLASS: Icon = IconLoader.getIcon("/icons/gutter/plugin.svg", Gutter::class.java) 9 | val CLASS_FIX_TYPE: Icon = IconLoader.getIcon("/icons/gutter/plugin.svg", Gutter::class.java) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/highlighter/markers/LineMarkerOptions.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.highlighter.markers 2 | 3 | import com.intellij.codeInsight.daemon.GutterIconDescriptor 4 | 5 | internal object LineMarkerOptions { 6 | val baseClassOption = GutterIconDescriptor.Option( 7 | "yfiles.base.class", 8 | "BaseClass inside", 9 | Icons.Gutter.BASE_CLASS 10 | ) 11 | 12 | val classFixTypeOption = GutterIconDescriptor.Option( 13 | "yfiles.class.fix.type", 14 | "Type fix activated", 15 | Icons.Gutter.CLASS_FIX_TYPE 16 | ) 17 | 18 | val allOptions = arrayOf( 19 | baseClassOption, 20 | classFixTypeOption 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/js/YFiles.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.js 2 | 3 | import org.jetbrains.kotlin.descriptors.ClassDescriptor 4 | import org.jetbrains.kotlin.descriptors.ClassKind 5 | import org.jetbrains.kotlin.name.FqName 6 | import org.jetbrains.kotlin.name.Name 7 | import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe 8 | import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny 9 | import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces 10 | 11 | private val YFILES_PACKAGE_ID = Name.identifier("yfiles") 12 | private val YOBJECT = FqName("yfiles.lang.YObject") 13 | private val YENUM = FqName("yfiles.lang.YEnum") 14 | 15 | internal val FqName.isYFiles: Boolean 16 | get() = startsWith(YFILES_PACKAGE_ID) 17 | 18 | internal val ClassDescriptor.locatedInYFilesPackage: Boolean 19 | get() = fqNameSafe.isYFiles 20 | 21 | private val ClassDescriptor.isYObject: Boolean 22 | get() = isExternal && fqNameSafe == YOBJECT 23 | 24 | internal val ClassDescriptor.isYEnum: Boolean 25 | get() = isExternal && fqNameSafe == YENUM 26 | 27 | internal fun ClassDescriptor.isYFilesInterface(): Boolean = 28 | isExternal and (isYObject or implementsYObject) 29 | 30 | internal val ClassDescriptor.implementsYObjectDirectly: Boolean 31 | get() = getSuperInterfaces() 32 | .any { it.isYObject } 33 | 34 | private val ClassDescriptor.implementsYObject: Boolean 35 | get() { 36 | if (implementsYObjectDirectly) { 37 | return true 38 | } 39 | 40 | return getSuperInterfaces() 41 | .any { it.implementsYObject } 42 | } 43 | 44 | internal val ClassDescriptor.implementsYFilesInterface: Boolean 45 | get() = getSuperInterfaces() 46 | .any { it.isYFilesInterface() } 47 | 48 | internal val ClassDescriptor.baseClassUsed: Boolean 49 | get() = checkClass { 50 | it.all { it.isYFilesInterface() && !it.isYObject } 51 | } 52 | 53 | internal val ClassDescriptor.classFixTypeUsed: Boolean 54 | get() = checkClass { 55 | it.singleOrNull()?.isYObject ?: false 56 | } 57 | 58 | private fun ClassDescriptor.checkClass( 59 | check: (List) -> Boolean, 60 | ): Boolean = 61 | when { 62 | isExternal -> false 63 | kind != ClassKind.CLASS -> false 64 | getSuperClassNotAny() != null -> false 65 | 66 | else -> getSuperInterfaces() 67 | .let { it.isNotEmpty() && check(it) } 68 | } 69 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/psi/ClassDescriptors.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.psi 2 | 3 | import org.jetbrains.kotlin.descriptors.ClassDescriptor 4 | import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny 5 | import org.jetbrains.kotlin.psi.KtClassOrObject 6 | import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode 7 | 8 | internal val KtClassOrObject.descriptor: ClassDescriptor? 9 | get() = resolveToDescriptorIfAny(BodyResolveMode.FULL) 10 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/psi/DefaultPsiFinder.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.psi 2 | 3 | import com.intellij.psi.PsiElement 4 | 5 | internal object DefaultPsiFinder : PsiFinder { 6 | private fun find(transform: (PsiFinder) -> T?): T? = 7 | PsiFinder.EP_NAME.extensionList.asSequence() 8 | .mapNotNull(transform) 9 | .firstOrNull() 10 | 11 | override fun findClass( 12 | context: PsiElement, 13 | className: String, 14 | ): PsiElement? = 15 | find { it.findClass(context, className) } 16 | 17 | override fun findProperty( 18 | context: PsiElement, 19 | className: String, 20 | propertyName: String, 21 | ): PsiElement? = 22 | find { it.findProperty(context, className, propertyName) } 23 | 24 | override fun findPropertyVariants( 25 | context: PsiElement, 26 | classNames: List, 27 | ): Array? = 28 | find { it.findPropertyVariants(context, classNames) } 29 | } 30 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/psi/JavaPsiFinder.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.psi 2 | 3 | import com.intellij.psi.JavaPsiFacade 4 | import com.intellij.psi.PsiClass 5 | import com.intellij.psi.PsiElement 6 | 7 | class JavaPsiFinder : PsiFinder { 8 | override fun findClass( 9 | context: PsiElement, 10 | className: String, 11 | ): PsiClass? = 12 | JavaPsiFacade.getInstance(context.project) 13 | .findClass("com.yworks.$className", context.resolveScope) 14 | 15 | override fun findProperty( 16 | context: PsiElement, 17 | className: String, 18 | propertyName: String, 19 | ): PsiElement? = 20 | findClass(context, className) 21 | ?.findMethodsByName(propertyName.toMethodName(), true) 22 | ?.firstOrNull() 23 | 24 | private fun String.toMethodName(): String = 25 | when { 26 | startsWith("is") -> this 27 | endsWith("ed") -> "is" + replaceFirstChar { it.uppercase() } 28 | else -> "get" + replaceFirstChar { it.uppercase() } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/psi/JavaScriptPsiFinder.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.psi 2 | 3 | import com.intellij.lang.javascript.index.JavaScriptIndex 4 | import com.intellij.lang.javascript.psi.ecmal4.JSClass 5 | import com.intellij.psi.PsiElement 6 | 7 | class JavaScriptPsiFinder : PsiFinder { 8 | override fun findClass( 9 | context: PsiElement, 10 | className: String, 11 | ): JSClass? = 12 | JavaScriptIndex.getInstance(context.project) 13 | .getClassByName(className.substringAfterLast("."), true) 14 | .filterIsInstance() 15 | .firstOrNull { !it.isInterface } 16 | 17 | override fun findProperty( 18 | context: PsiElement, 19 | className: String, 20 | propertyName: String, 21 | ): PsiElement? = 22 | findClass(context, className) 23 | ?.findFieldByName(propertyName) 24 | } 25 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/psi/KotlinPsiFinder.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.psi 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement 4 | import com.intellij.codeInsight.lookup.LookupElementBuilder 5 | import com.intellij.icons.AllIcons 6 | import com.intellij.psi.PsiElement 7 | import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex 8 | import org.jetbrains.kotlin.psi.KtClassOrObject 9 | import org.jetbrains.kotlin.psi.KtProperty 10 | import org.jetbrains.kotlin.psi.psiUtil.findPropertyByName 11 | 12 | class KotlinPsiFinder : PsiFinder { 13 | override fun findClass( 14 | context: PsiElement, 15 | className: String, 16 | ): KtClassOrObject? = 17 | KotlinFullClassNameIndex 18 | .get(className, context.project, context.resolveScope) 19 | .firstOrNull() 20 | 21 | override fun findProperty( 22 | context: PsiElement, 23 | className: String, 24 | propertyName: String, 25 | ): PsiElement? = 26 | findClass(context, className) 27 | ?.findPropertyByName(propertyName) 28 | 29 | override fun findPropertyVariants( 30 | context: PsiElement, 31 | classNames: List, 32 | ): Array? { 33 | val classes = classNames 34 | .mapNotNull { findClass(context, it) } 35 | .takeIf { it.isNotEmpty() } 36 | ?: return null 37 | 38 | return classes 39 | .asSequence() 40 | .flatMap { it.declarations } 41 | .filterIsInstance() 42 | .map { it.toVariant() } 43 | .toList() 44 | .toTypedArray() 45 | } 46 | 47 | private fun KtProperty.toVariant(): LookupElement = 48 | LookupElementBuilder.create(this) 49 | .withIcon(AllIcons.Nodes.PropertyRead) 50 | } 51 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/psi/PsiFinder.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.psi 2 | 3 | import com.intellij.openapi.extensions.ExtensionPointName 4 | import com.intellij.psi.PsiElement 5 | 6 | 7 | interface PsiFinder { 8 | fun findClass( 9 | context: PsiElement, 10 | className: String, 11 | ): PsiElement? 12 | 13 | fun findProperty( 14 | context: PsiElement, 15 | className: String, 16 | propertyName: String, 17 | ): PsiElement? 18 | 19 | fun findPropertyVariants( 20 | context: PsiElement, 21 | classNames: List, 22 | ): Array? = null 23 | 24 | companion object { 25 | val EP_NAME: ExtensionPointName = ExtensionPointName.create("com.github.turansky.yfiles.psiFinder") 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/template/TemplateErrorFilter.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.template 2 | 3 | import com.intellij.codeInsight.highlighting.HighlightErrorFilter 4 | import com.intellij.psi.PsiErrorElement 5 | import com.intellij.psi.xml.XmlTag 6 | import com.intellij.xml.psi.XmlPsiBundle 7 | 8 | class TemplateErrorFilter : HighlightErrorFilter() { 9 | private val suppressedErrorDescription = XmlPsiBundle.message("xml.parsing.multiple.root.tags") 10 | 11 | override fun shouldHighlightErrorElement(element: PsiErrorElement): Boolean = 12 | !shouldSuppress(element) 13 | 14 | private fun shouldSuppress(element: PsiErrorElement): Boolean { 15 | val context = element.context as? XmlTag 16 | ?: return false 17 | 18 | return context.language == TemplateLanguage 19 | && element.errorDescription == suppressedErrorDescription 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/template/TemplateFileType.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.template 2 | 3 | import com.intellij.ide.highlighter.XmlLikeFileType 4 | import com.intellij.openapi.fileTypes.UIBasedFileType 5 | import javax.swing.Icon 6 | 7 | object TemplateFileType : XmlLikeFileType(TemplateLanguage), UIBasedFileType { 8 | override fun getName(): String = language.id 9 | override fun getDescription(): String = language.id 10 | override fun getDefaultExtension(): String = "svg" 11 | override fun getIcon(): Icon? = null 12 | } 13 | -------------------------------------------------------------------------------- /idea-plugin/src/main/kotlin/com/github/turansky/yfiles/ide/template/TemplateLanguage.kt: -------------------------------------------------------------------------------- 1 | package com.github.turansky.yfiles.ide.template 2 | 3 | import com.intellij.lang.xml.XMLLanguage 4 | import com.intellij.lang.xml.XMLParserDefinition 5 | import com.intellij.psi.FileViewProvider 6 | import com.intellij.psi.PsiFile 7 | import com.intellij.psi.impl.source.xml.XmlFileImpl 8 | import com.intellij.psi.tree.IFileElementType 9 | 10 | internal object TemplateLanguage : XMLLanguage( 11 | INSTANCE, 12 | "yfiles-template", 13 | "text/yfiles-template" 14 | ) 15 | 16 | class TemplateParserDefinition : XMLParserDefinition() { 17 | override fun getFileNodeType(): IFileElementType = 18 | TEMPLATE_FILE 19 | 20 | override fun createFile(viewProvider: FileViewProvider): PsiFile = 21 | XmlFileImpl(viewProvider, TEMPLATE_FILE) 22 | 23 | private companion object { 24 | val TEMPLATE_FILE = IFileElementType(TemplateLanguage) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /idea-plugin/src/main/resources/META-INF/java.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /idea-plugin/src/main/resources/META-INF/javascript.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /idea-plugin/src/main/resources/META-INF/kotlin.xml: -------------------------------------------------------------------------------- 1 | 2 | org.jetbrains.kotlin 3 | 4 | 5 | 9 | 10 | 19 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /idea-plugin/src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.github.turansky.yfiles 3 | yFiles 4 | Victor Turansky 5 | 6 | 7 | 8 | Kotlin/JS
10 | • Check yFiles inheritance rules on the fly
11 | • Companion of yFiles Gradle plugin

12 | 13 | SVG Templates
14 | • Binding syntax highlight
15 | • Template MIME type support

16 | 17 | Links
18 | • yFiles Kotlin/JS declarations
19 | • yFiles Class Framework
20 | ]]> 21 |
22 | 23 | com.intellij.modules.lang 24 | com.intellij.modules.java 25 | org.jetbrains.kotlin 26 | JavaScript 27 | 28 | 29 | 30 | 36 | 37 | 41 | 42 | 45 | 46 | 47 | 51 | 52 | 56 | 57 | 61 | 62 | 63 | 64 | 68 | 69 |
70 | -------------------------------------------------------------------------------- /idea-plugin/src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /idea-plugin/src/main/resources/icons/gutter/plugin.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /idea-plugin/src/main/resources/icons/gutter/plugin_dark.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /idea-plugin/src/main/resources/inspectionDescriptions/yfiles-kotlinjs-inheritance.html: -------------------------------------------------------------------------------- 1 | Checks yFiles inheritance rules. 2 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "yfiles-kotlin" 2 | 3 | dependencyResolutionManagement { 4 | repositories { 5 | mavenCentral() 6 | } 7 | 8 | versionCatalogs { 9 | create("libs") { 10 | val kotlinVersion = extra["kotlin.version"] as String 11 | plugin("kotlin-multiplatform", "org.jetbrains.kotlin.multiplatform").version(kotlinVersion) 12 | plugin("kotlin-js-plain-objects", "org.jetbrains.kotlin.plugin.js-plain-objects").version(kotlinVersion) 13 | 14 | val yfilesVersion = "--predefined--" 15 | plugin("yfiles", "com.github.turansky.yfiles").version(yfilesVersion) 16 | 17 | plugin("download", "de.undercouch.download").version("5.5.0") 18 | } 19 | 20 | create("kfc") { 21 | val kfcVersion = extra["kfc.version"] as String 22 | plugin("library", "io.github.turansky.kfc.library").version(kfcVersion) 23 | } 24 | 25 | create("kotlinWrappers") { 26 | val wrappersVersion = extra["kotlin-wrappers.version"] as String 27 | from("org.jetbrains.kotlin-wrappers:kotlin-wrappers-catalog:$wrappersVersion") 28 | } 29 | } 30 | } 31 | 32 | include("yfiles-kotlin") 33 | include("vsdx-kotlin") 34 | 35 | includeBuild("gradle-plugin") 36 | include("gradle-plugin-test") 37 | 38 | include("examples:simple-app") 39 | include("examples:data-classes") 40 | include("examples:configurable-properties") 41 | 42 | include("examples:import-optimizer-library") 43 | include("examples:import-optimizer-application") 44 | -------------------------------------------------------------------------------- /vsdx-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.turansky.yfiles.generateVsdxKotlinDeclarations 2 | import de.undercouch.gradle.tasks.download.Download 3 | 4 | plugins { 5 | alias(kfc.plugins.library) 6 | alias(libs.plugins.yfiles) 7 | 8 | alias(libs.plugins.download) 9 | } 10 | 11 | dependencies { 12 | jsMainImplementation(kotlinWrappers.browser) 13 | 14 | jsMainImplementation(project(":yfiles-kotlin")) 15 | } 16 | 17 | val kotlinSourceDir: File 18 | get() = kotlin 19 | .sourceSets 20 | .get("jsMain") 21 | .kotlin 22 | .sourceDirectories 23 | .first() 24 | 25 | tasks { 26 | clean { 27 | delete("src") 28 | } 29 | 30 | val apiDescriptorFile = File(buildDir, "api.js") 31 | 32 | val downloadApiDescriptor by registering(Download::class) { 33 | src(project.property("vsdx.api.url")) 34 | dest(apiDescriptorFile) 35 | overwrite(true) 36 | } 37 | 38 | val generateDeclarations by registering { 39 | doLast { 40 | val sourceDir = kotlinSourceDir 41 | .also { delete(it) } 42 | 43 | generateVsdxKotlinDeclarations(apiDescriptorFile, sourceDir) 44 | } 45 | 46 | dependsOn(downloadApiDescriptor) 47 | } 48 | 49 | named("compileKotlinJs") { 50 | dependsOn(generateDeclarations) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vsdx-kotlin/gradle.properties: -------------------------------------------------------------------------------- 1 | group=com.yworks.yfiles 2 | version=2.3.2-SNAPSHOT 3 | -------------------------------------------------------------------------------- /vsdx-kotlin/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "vsdx-kotlin" -------------------------------------------------------------------------------- /yfiles-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.turansky.yfiles.generateKotlinDeclarations 2 | import de.undercouch.gradle.tasks.download.Download 3 | 4 | plugins { 5 | alias(kfc.plugins.library) 6 | alias(libs.plugins.yfiles) 7 | 8 | alias(libs.plugins.download) 9 | } 10 | 11 | dependencies { 12 | jsMainImplementation(kotlinWrappers.browser) 13 | } 14 | 15 | val kotlinSourceDir: File 16 | get() = kotlin 17 | .sourceSets 18 | .get("jsMain") 19 | .kotlin 20 | .sourceDirectories 21 | .first() 22 | 23 | tasks { 24 | clean { 25 | delete("src") 26 | } 27 | 28 | val apiDescriptorFile = File(buildDir, "api.js") 29 | val devguideDescriptorFile = File(buildDir, "devguide.js") 30 | 31 | val downloadApiDescriptor by registering(Download::class) { 32 | src(project.property("yfiles.api.url")) 33 | dest(apiDescriptorFile) 34 | overwrite(true) 35 | } 36 | 37 | val downloadDevguideDescriptor by registering(Download::class) { 38 | src(project.property("yfiles.devguide.url")) 39 | dest(devguideDescriptorFile) 40 | overwrite(true) 41 | } 42 | 43 | val generateDeclarations by registering { 44 | doLast { 45 | val sourceDir = kotlinSourceDir 46 | .also { delete(it) } 47 | 48 | generateKotlinDeclarations( 49 | apiFile = apiDescriptorFile, 50 | devguideFile = devguideDescriptorFile, 51 | sourceDir = sourceDir 52 | ) 53 | } 54 | 55 | dependsOn(downloadApiDescriptor) 56 | dependsOn(downloadDevguideDescriptor) 57 | } 58 | 59 | named("compileKotlinJs") { 60 | dependsOn(generateDeclarations) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /yfiles-kotlin/gradle.properties: -------------------------------------------------------------------------------- 1 | group=com.yworks.yfiles 2 | version=26.0.4-SNAPSHOT 3 | -------------------------------------------------------------------------------- /yfiles-kotlin/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "yfiles-kotlin" --------------------------------------------------------------------------------