Skip to content

Commit

Permalink
sfg-5 Move the general logic to BaseVisitor, sfg-4 Replace Fragment.b…
Browse files Browse the repository at this point in the history
…ind -> Fragment.initArguments
  • Loading branch information
udfvirus committed Oct 4, 2022
1 parent dfb6dd8 commit 260253c
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.screenfactorygeneratorsample.databinding.FragmentFirstBinding
import com.udfsoft.screenfactorygenerator.annotation.JParam
import com.udfsoft.screenfactorygenerator.annotation.JScreen

/**
* A simple [Fragment] subclass as the default destination in the navigation.
*/
@JScreen(generateScreenMethod = false)
class FirstFragment : Fragment() {

private var _binding: FragmentFirstBinding? = null
Expand All @@ -19,14 +22,20 @@ class FirstFragment : Fragment() {
// onDestroyView.
private val binding get() = _binding!!

@JParam
var test1: String = ""

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {

_binding = FragmentFirstBinding.inflate(inflater, container, false)
return binding.root

}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import com.udfsoft.screenfactorygenerator.processor.visitor.ActivityVisitor
import com.udfsoft.screenfactorygenerator.processor.visitor.FragmentVisitor
import com.udfsoft.screenfactorygenerator.utils.IoUtils.plusAssign
import com.udfsoft.screenfactorygenerator.utils.Utils.isChildForClass
import com.udfsoft.screenfactorygenerator.utils.Utils.replaceFirst

class JScreenProcessor(
private val codeGenerator: CodeGenerator,
Expand Down Expand Up @@ -80,9 +79,23 @@ class JScreenProcessor(
sourceFile += "package $packageName\n\n"

if (it.isChildForClass("Fragment")) {
it.accept(FragmentVisitor(sourceFile, className, logger), Unit)
it.accept(
FragmentVisitor(
sourceFile,
className,
logger,
screenManagerClassStringBuilder
), Unit
)
} else if (it.isChildForClass("Activity")) {
it.accept(ActivityVisitor(sourceFile, className, logger, screenManagerClassStringBuilder), Unit)
it.accept(
ActivityVisitor(
sourceFile,
className,
logger,
screenManagerClassStringBuilder
), Unit
)
}
sourceFile.close()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,130 +18,87 @@
package com.udfsoft.screenfactorygenerator.processor.visitor

import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.udfsoft.screenfactorygenerator.annotation.JParam
import com.udfsoft.screenfactorygenerator.annotation.JScreen
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSValueArgument
import com.udfsoft.screenfactorygenerator.processor.mapper.KSPropertyDeclarationToIntentPutExtraString
import com.udfsoft.screenfactorygenerator.processor.mapper.KsPropertyDeclarationToGetExtraString
import com.udfsoft.screenfactorygenerator.utils.IoUtils.plusAssign
import com.udfsoft.screenfactorygenerator.utils.Utils.findAnnotation
import com.udfsoft.screenfactorygenerator.utils.Utils.findArgument
import com.udfsoft.screenfactorygenerator.utils.Utils.getDeclaredPropertiesWithAnnotation
import com.udfsoft.screenfactorygenerator.utils.Utils.isChildForClass
import com.udfsoft.screenfactorygenerator.utils.Utils.replaceFirst
import java.io.OutputStream

class ActivityVisitor(
private val file: OutputStream,
private val className: String,
file: OutputStream,
className: String,
private val logger: KSPLogger,
private val screenManagerClassStringBuilder: StringBuilder
) : KSVisitorVoid() {
screenManagerClassStringBuilder: StringBuilder
) : BaseVisitor(file, className, logger, screenManagerClassStringBuilder) {

override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
if (classDeclaration.classKind != ClassKind.CLASS) {
logger.error("Only class can be annotated with @JScreen", classDeclaration)
return
}

val isActivity = classDeclaration.isChildForClass("Activity")

if (!isActivity) {
logger.error("Class must be inherited from Activity", classDeclaration)
return
}
override fun getParentClassString() = ACTIVITY_CLASS_NAME

val annotation: KSAnnotation = classDeclaration.findAnnotation(JScreen::class.simpleName!!)

val generateScreenMethodArgument = annotation.arguments.findArgument("generateScreenMethod")

val params =
classDeclaration.getDeclaredPropertiesWithAnnotation(JParam::class.simpleName!!)

if (generateScreenMethodArgument.value == true) {
file += "import com.github.terrakok.cicerone.androidx.ActivityScreen\n"
}
override fun getImportForGenericClass(generateScreenMethodArgument: KSValueArgument) =
buildString {
if (generateScreenMethodArgument.value == true) {
appendLine("import com.github.terrakok.cicerone.androidx.ActivityScreen")
}

file += "import android.content.Context\n"
file += "import android.content.Intent\n"
file += "import androidx.core.os.bundleOf\n"
file += "import androidx.fragment.app.Fragment\n"
file += "import com.udfsoft.screenfactorygenerator.BaseScreen\n\n"

file += "object ${className}Screen : BaseScreen {\n\n"

file += if (params.toList().isEmpty()) {
" fun newIntent(context: Context) = Intent(context, ${className}::class.java)\n\n"
} else {
val paramsForSetArguments =
params.map(KSPropertyDeclarationToIntentPutExtraString()::transform)
.joinToString("\n ")

val paramsInString = params.map {
"$it: ${it.type}"
}.joinToString()

" fun newIntent(context: Context, $paramsInString): Intent { \n" +
" val intent = Intent(context, ${className}::class.java)\n" +
" $paramsForSetArguments\n" +
" return intent\n" +
" }\n\n"
appendLine("import android.content.Context")
appendLine("import android.content.Intent")
appendLine("import androidx.core.os.bundleOf")
appendLine("import androidx.fragment.app.Fragment")
appendLine("import com.udfsoft.screenfactorygenerator.BaseScreen\n")
}

file += " fun ${className}.initArguments() {\n"

val paramsForGetArguments = params.map(KsPropertyDeclarationToGetExtraString()::transform)
.joinToString("\n")

file += "$paramsForGetArguments\n"
file += " }\n"

if (generateScreenMethodArgument.value == true) {
file += "\n"
file += if (params.toList().isEmpty()) {
" fun get${className}Screen() = ActivityScreen { newIntent(it) }\n"
override fun getClassBody(
params: Sequence<KSPropertyDeclaration>,
generateScreenMethodArgument: KSValueArgument,
className: String
): String = buildString {
appendLine(
if (params.toList().isEmpty()) {
" fun newIntent(context: Context) = Intent(context, ${className}::class.java)\n"
} else {

val paramsWithTypeInString = params.map {
"$it: ${it.type}"
}.joinToString()
val paramsForSetArguments =
params.map(KSPropertyDeclarationToIntentPutExtraString()::transform)
.joinToString("\n ")

val paramsInString = params.map {
"$it"
"$it: ${it.type}"
}.joinToString()

" fun get${className}Screen($paramsWithTypeInString) = ActivityScreen {\n" +
" newIntent(it, $paramsInString)\n" +
" }\n"
" fun newIntent(context: Context, $paramsInString): Intent { \n" +
" val intent = Intent(context, ${className}::class.java)\n" +
" $paramsForSetArguments\n" +
" return intent\n" +
" }\n"
}
}
)

file += "}\n"
appendLine(" fun ${className}.initArguments() {")

val packageName = classDeclaration.containingFile!!.packageName.asString()
val importIndex = screenManagerClassStringBuilder.indexOf("import")
screenManagerClassStringBuilder.insert(
importIndex,
"import ${packageName}.${className}Screen.initArguments\n"
)
screenManagerClassStringBuilder.insert(importIndex, "import ${packageName}.$className\n")
val paramsForGetArguments = params.map(KsPropertyDeclarationToGetExtraString()::transform)
.joinToString("\n")

if (screenManagerClassStringBuilder.contains("// initArguments(Activities)")) {
val body = buildString {
appendLine("\rwhen (activity) {")
append(" is $className -> activity.initArguments()")
append(" }")
}
screenManagerClassStringBuilder.replaceFirst("// initArguments(Activities)", body)
} else {
val body = buildString {
appendLine(" when (activity) {")
append(" is $className -> activity.initArguments()")
}
screenManagerClassStringBuilder.replaceFirst("when (activity) {", body)
appendLine(paramsForGetArguments)
appendLine(" }")

if (generateScreenMethodArgument.value == true) {
appendLine()
appendLine(
if (params.toList().isEmpty()) {
" fun get${className}Screen() = ActivityScreen { newIntent(it) }"
} else {

val paramsWithTypeInString = params.map {
"$it: ${it.type}"
}.joinToString()

val paramsInString = params.map {
"$it"
}.joinToString()

" fun get${className}Screen($paramsWithTypeInString) = ActivityScreen {\n" +
" newIntent(it, $paramsInString)\n" +
" }"
}
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2022 javavirys
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package com.udfsoft.screenfactorygenerator.processor.visitor

import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.*
import com.udfsoft.screenfactorygenerator.annotation.JParam
import com.udfsoft.screenfactorygenerator.annotation.JScreen
import com.udfsoft.screenfactorygenerator.utils.IoUtils.plusAssign
import com.udfsoft.screenfactorygenerator.utils.Utils.findAnnotation
import com.udfsoft.screenfactorygenerator.utils.Utils.findArgument
import com.udfsoft.screenfactorygenerator.utils.Utils.getDeclaredPropertiesWithAnnotation
import com.udfsoft.screenfactorygenerator.utils.Utils.isChildForClass
import com.udfsoft.screenfactorygenerator.utils.Utils.replaceFirst
import java.io.OutputStream

abstract class BaseVisitor(
private val file: OutputStream,
private val className: String,
private val logger: KSPLogger,
private val screenManagerClassStringBuilder: StringBuilder
) : KSVisitorVoid() {

override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
if (classDeclaration.classKind != ClassKind.CLASS) {
logger.error("Only class can be annotated with @JScreen", classDeclaration)
return
}

val isChild = classDeclaration.isChildForClass(getParentClassString())

if (!isChild) {
logger.error("Class must be inherited from ${getParentClassString()}", classDeclaration)
return
}

val annotation: KSAnnotation = classDeclaration.findAnnotation(JScreen::class.simpleName!!)

val generateScreenMethodArgument = annotation.arguments.findArgument("generateScreenMethod")

val params =
classDeclaration.getDeclaredPropertiesWithAnnotation(JParam::class.simpleName!!)

file += getImportForGenericClass(generateScreenMethodArgument)

file += "object ${className}Screen : BaseScreen {\n\n"
file += getClassBody(params, generateScreenMethodArgument, className)
file += "}\n"

val packageName = classDeclaration.containingFile!!.packageName.asString()
val importIndex = screenManagerClassStringBuilder.indexOf("import")
screenManagerClassStringBuilder.insert(
importIndex,
"import ${packageName}.${className}Screen.initArguments\n"
)
screenManagerClassStringBuilder.insert(importIndex, "import ${packageName}.$className\n")

if (getParentClassString() == ACTIVITY_CLASS_NAME) {
createScreenManagerBody("// initArguments(Activities)", name = "activity")
} else if (getParentClassString() == FRAGMENT_CLASS_NAME) {
createScreenManagerBody("// initArguments(Fragments)", name = "fragment")
}
}

private fun createScreenManagerBody(replaceString: String, name: String) {
if (screenManagerClassStringBuilder.contains(replaceString)) {
val body = buildString {
appendLine("when ($name) {")
appendLine(" is $className -> $name.initArguments()")
append(" }")
}
screenManagerClassStringBuilder.replaceFirst(replaceString, body)
} else {
val body = buildString {
appendLine("when ($name) {")
append(" is $className -> $name.initArguments()")
}
screenManagerClassStringBuilder.replaceFirst("when ($name) {", body)
}
}

abstract fun getParentClassString(): String

abstract fun getImportForGenericClass(generateScreenMethodArgument: KSValueArgument): String

abstract fun getClassBody(
params: Sequence<KSPropertyDeclaration>,
generateScreenMethodArgument: KSValueArgument,
className: String
): String

companion object {
const val FRAGMENT_CLASS_NAME = "Fragment"
const val ACTIVITY_CLASS_NAME = "Activity"
}
}
Loading

0 comments on commit 260253c

Please sign in to comment.