Skip to content

Commit

Permalink
Make more experimental APIs public + overridable properties (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZacSweers authored Oct 22, 2021
1 parent 1009520 commit 64b3e2a
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ import javax.annotation.processing.ProcessingEnvironment
import javax.lang.model.element.Element
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier
import javax.lang.model.element.Modifier.PUBLIC
import javax.lang.model.element.NestingKind
import javax.lang.model.type.TypeMirror
import javax.lang.model.util.ElementFilter
import javax.lang.model.util.Elements
import javax.lang.model.util.Types
Expand Down Expand Up @@ -190,7 +188,7 @@ public class AutoValueKotlinExtension : AutoValueExtension() {
annotations = annotations,
isOverride = isAnOverride,
isRedacted = isRedacted,
visibility = if (PUBLIC in method.modifiers) KModifier.PUBLIC else KModifier.INTERNAL,
visibility = if (Modifier.PUBLIC in method.modifiers) KModifier.PUBLIC else KModifier.INTERNAL,
doc = method.parseDocs()
)
}
Expand All @@ -200,12 +198,7 @@ public class AutoValueKotlinExtension : AutoValueExtension() {
// If all the properties are redacted, redacted the class?
properties.values.all { it.isRedacted }

val isParcelable = elements
.getTypeElement("android.os.Parcelable")
?.asType()
?.let { parcelableClass ->
avClass.interfaces.any { it.isClassOfType(types, parcelableClass) }
} ?: false
val isParcelable = context.processingEnvironment().isParcelable(avClass)

val propertyMethods = context.properties().values.toSet()

Expand Down Expand Up @@ -409,7 +402,7 @@ public class AutoValueKotlinExtension : AutoValueExtension() {
packageName = context.packageName(),
doc = classDoc,
name = avClass.simpleName.toString(),
visibility = if (PUBLIC in avClass.modifiers) KModifier.PUBLIC else KModifier.INTERNAL,
visibility = if (Modifier.PUBLIC in avClass.modifiers) KModifier.PUBLIC else KModifier.INTERNAL,
isRedacted = isClassRedacted,
isParcelable = isParcelable,
superClass = superclass,
Expand All @@ -429,9 +422,6 @@ public class AutoValueKotlinExtension : AutoValueExtension() {

return null
}

private fun TypeMirror.isClassOfType(types: Types, other: TypeMirror?) =
types.isAssignable(this, other)
}

private fun AvkBuilder.Companion.from(
Expand Down Expand Up @@ -474,7 +464,7 @@ private fun AvkBuilder.Companion.from(
return AvkBuilder(
name = builderContext.builderType().simpleName.toString(),
doc = builderContext.builderType().parseDocs(),
visibility = if (PUBLIC in builderContext.builderType().modifiers) {
visibility = if (Modifier.PUBLIC in builderContext.builderType().modifiers) {
KModifier.PUBLIC
} else {
KModifier.INTERNAL
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/com/slack/auto/value/kotlin/KotlinClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ public data class KotlinClass(
if (!isRedacted && prop.isRedacted && redactedClassName != null) {
addAnnotation(redactedClassName)
}
if (prop.forcePropertyOverride) {
addModifiers(OVERRIDE)
}
prop.doc?.let { addKdoc(it) }
}
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public data class PropertyContext(
val type: TypeName,
val annotations: List<AnnotationSpec>,
val isOverride: Boolean,
/** Forces the property to be marked with the override modifier too. */
val forcePropertyOverride: Boolean = false,
val isRedacted: Boolean,
val visibility: KModifier,
val doc: String?
Expand Down
47 changes: 37 additions & 10 deletions src/main/kotlin/com/slack/auto/value/kotlin/utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("TooManyFunctions")
@file:OptIn(DelicateKotlinPoetApi::class)
package com.slack.auto.value.kotlin

Expand Down Expand Up @@ -42,13 +43,15 @@ import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.asTypeVariableName
import com.squareup.moshi.Json
import javax.annotation.processing.ProcessingEnvironment
import javax.lang.model.element.Element
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
import javax.lang.model.type.TypeMirror
import javax.lang.model.type.TypeVariable
import javax.lang.model.util.Types

internal val NONNULL_ANNOTATIONS = setOf(
"NonNull",
Expand All @@ -57,6 +60,7 @@ internal val NONNULL_ANNOTATIONS = setOf(
)

internal val PARCELIZE = ClassName("kotlinx.parcelize", "Parcelize")

internal val INTRINSIC_IMPORTS = setOf(
"import java.lang.String",
"import java.lang.CharSequence",
Expand All @@ -81,12 +85,14 @@ internal const val MAX_PARAMS = 7

internal val JSON_CN = Json::class.asClassName()

internal fun TypeMirror.asSafeTypeName(): TypeName {
@ExperimentalAvkApi
public fun TypeMirror.asSafeTypeName(): TypeName {
return asTypeName().copy(nullable = false).normalize()
}

@Suppress("ComplexMethod")
internal fun TypeName.normalize(): TypeName {
@ExperimentalAvkApi
public fun TypeName.normalize(): TypeName {
return when (this) {
is ClassName -> {
when (this) {
Expand All @@ -110,7 +116,8 @@ internal fun TypeName.normalize(): TypeName {
}
}

internal fun TypeElement.classAnnotations(): List<AnnotationSpec> {
@ExperimentalAvkApi
public fun TypeElement.classAnnotations(): List<AnnotationSpec> {
return annotationMirrors
.map {
@Suppress("DEPRECATION")
Expand All @@ -132,7 +139,8 @@ internal fun TypeElement.classAnnotations(): List<AnnotationSpec> {
}
}

internal fun TypeName.defaultPrimitiveValue(): CodeBlock =
@ExperimentalAvkApi
public fun TypeName.defaultPrimitiveValue(): CodeBlock =
when (this) {
BOOLEAN -> CodeBlock.of("false")
CHAR -> CodeBlock.of("0.toChar()")
Expand All @@ -148,15 +156,17 @@ internal fun TypeName.defaultPrimitiveValue(): CodeBlock =
else -> CodeBlock.of("null")
}

internal fun deprecatedAnnotation(message: String, replaceWith: String): AnnotationSpec {
@ExperimentalAvkApi
public fun deprecatedAnnotation(message: String, replaceWith: String): AnnotationSpec {
return AnnotationSpec.builder(Deprecated::class)
.addMember("message = %S", message)
.addMember("replaceWith = %T(%S)", ReplaceWith::class, replaceWith)
.build()
}

@Suppress("DEPRECATION", "SpreadOperator")
internal fun FunSpec.Companion.copyOf(method: ExecutableElement): FunSpec.Builder {
@ExperimentalAvkApi
public fun FunSpec.Companion.copyOf(method: ExecutableElement): FunSpec.Builder {
var modifiers: Set<Modifier> = method.modifiers

val methodName = method.simpleName.toString()
Expand Down Expand Up @@ -192,11 +202,13 @@ internal fun FunSpec.Companion.copyOf(method: ExecutableElement): FunSpec.Builde
return funBuilder
}

internal fun ParameterSpec.Companion.parametersWithNullabilityOf(method: ExecutableElement): List<ParameterSpec> =
@ExperimentalAvkApi
public fun ParameterSpec.Companion.parametersWithNullabilityOf(method: ExecutableElement): List<ParameterSpec> =
method.parameters.map(ParameterSpec.Companion::getWithNullability)

@Suppress("DEPRECATION")
internal fun ParameterSpec.Companion.getWithNullability(element: VariableElement): ParameterSpec {
@ExperimentalAvkApi
public fun ParameterSpec.Companion.getWithNullability(element: VariableElement): ParameterSpec {
val name = element.simpleName.toString()
val isNullable =
element.annotationMirrors.any { (it.annotationType.asElement() as TypeElement).simpleName.toString() == "Nullable" }
Expand All @@ -207,7 +219,8 @@ internal fun ParameterSpec.Companion.getWithNullability(element: VariableElement
}

/** Cleans up the generated doc and translates some html to equivalent markdown for Kotlin docs. */
internal fun cleanUpDoc(doc: String): String {
@ExperimentalAvkApi
public fun cleanUpDoc(doc: String): String {
// TODO not covered yet
// {@link TimeFormatter#getDateTimeString(SlackDateTime)}
return doc.replace("<em>", "*")
Expand Down Expand Up @@ -247,10 +260,24 @@ internal fun cleanUpDoc(doc: String): String {
.trim()
}

internal fun FunSpec.Builder.withDocsFrom(
@ExperimentalAvkApi
public fun FunSpec.Builder.withDocsFrom(
e: Element,
parseDocs: Element.() -> String?
): FunSpec.Builder {
val doc = e.parseDocs() ?: return this
return addKdoc(doc)
}

@ExperimentalAvkApi
public fun ProcessingEnvironment.isParcelable(element: TypeElement): Boolean {
return elementUtils
.getTypeElement("android.os.Parcelable")
?.asType()
?.let { parcelableClass ->
element.interfaces.any { it.isClassOfType(typeUtils, parcelableClass) }
} ?: false
}

private fun TypeMirror.isClassOfType(types: Types, other: TypeMirror?) =
types.isAssignable(this, other)

0 comments on commit 64b3e2a

Please sign in to comment.