diff --git a/generators/generators.iml b/generators/generators.iml
index d590b297bfd..4584b2ac1c2 100644
--- a/generators/generators.iml
+++ b/generators/generators.iml
@@ -40,5 +40,6 @@
+
\ No newline at end of file
diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
index f4664f80b37..20d07fca238 100755
--- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
+++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
@@ -136,6 +136,7 @@ import org.jetbrains.kotlin.js.test.semantics.*
import org.jetbrains.kotlin.jvm.compiler.*
import org.jetbrains.kotlin.jvm.runtime.AbstractJvm8RuntimeDescriptorLoaderTest
import org.jetbrains.kotlin.jvm.runtime.AbstractJvmRuntimeDescriptorLoaderTest
+import org.jetbrains.kotlin.kapt3.test.AbstractJCTreeConverterTest
import org.jetbrains.kotlin.kdoc.AbstractKDocLexerTest
import org.jetbrains.kotlin.lang.resolve.android.test.AbstractAndroidBoxTest
import org.jetbrains.kotlin.lang.resolve.android.test.AbstractAndroidBytecodeShapeTest
@@ -1137,6 +1138,12 @@ fun main(args: Array) {
}
}
+ testGroup("plugins/kapt3/test", "plugins/kapt3/testData") {
+ testClass {
+ model("converter")
+ }
+ }
+
testGroup("plugins/android-extensions/android-extensions-idea/tests", "plugins/android-extensions/android-extensions-idea/testData") {
testClass() {
model("android/completion", recursive = false, extension = null)
diff --git a/plugins/kapt3/kapt3.iml b/plugins/kapt3/kapt3.iml
index 00a9aed3a04..08584ed92ed 100644
--- a/plugins/kapt3/kapt3.iml
+++ b/plugins/kapt3/kapt3.iml
@@ -15,5 +15,6 @@
+
\ No newline at end of file
diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/JCTreeConverter.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/JCTreeConverter.kt
new file mode 100644
index 00000000000..98f72c0f3d5
--- /dev/null
+++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/JCTreeConverter.kt
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * 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 org.jetbrains.kotlin.kapt3
+
+import com.sun.tools.javac.tree.JCTree
+import com.sun.tools.javac.tree.JCTree.*
+import com.sun.tools.javac.tree.TreeMaker
+import com.sun.tools.javac.util.Context
+import com.sun.tools.javac.util.Name
+import com.sun.tools.javac.util.Names
+import com.sun.tools.javac.util.SharedNameTable
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
+import org.jetbrains.org.objectweb.asm.Opcodes
+import org.jetbrains.org.objectweb.asm.Type
+import org.jetbrains.org.objectweb.asm.tree.*
+import com.sun.tools.javac.util.List as JavacList
+
+class JCTreeConverter(context: Context, val classes: List, val origins: Map) {
+ private companion object {
+ private val BLACKLISTED_ANNOTATATIONS = listOf(
+ "java.lang.Deprecated", "kotlin.Deprecated", // Deprecated annotations
+ "java.lang.annotation.", // Java annotations
+ "org.jetbrains.annotations.", // Nullable/NotNull, ReadOnly, Mutable
+ "kotlin.jvm.", "kotlin.Metadata" // Kotlin annotations from runtime
+ )
+ }
+
+ private val treeMaker = TreeMaker.instance(context)
+ private val nameTable = SharedNameTable(Names.instance(context))
+
+ private var done = false
+
+ fun convert(): JavacList {
+ if (done) error(JCTreeConverter::class.java.simpleName + " can convert classes only once")
+ done = true
+ return mapValues(classes) { convertTopLevelClass(it) }
+ }
+
+ private fun convertTopLevelClass(clazz: ClassNode): JCCompilationUnit? {
+ val origin = origins[clazz] ?: return null
+ val ktFile = origin.element?.containingFile as? KtFile ?: return null
+ val descriptor = origin.descriptor as? ClassDescriptor ?: return null
+
+ // Nested classes will be processed during the outer classes conversion
+ if (descriptor.containingDeclaration is ClassDescriptor) return null
+
+ val classDeclaration = convert(clazz)
+
+ val packageAnnotations = JavacList.nil()
+ val packageName = ktFile.packageFqName.asString()
+ val packageClause = if (packageName.isEmpty()) null else convertFqName(packageName)
+
+ val imports = JavacList.nil()
+ val classes = JavacList.of(classDeclaration)
+
+ return treeMaker.TopLevel(packageAnnotations, packageClause, imports + classes)
+ }
+
+ /**
+ * Returns false for the inner classe or if the origin for the class was not found.
+ */
+ private fun convert(clazz: ClassNode): JCClassDecl? {
+ if (isSynthetic(clazz.access)) return null
+
+ val descriptor = origins[clazz]?.descriptor as? ClassDescriptor ?: return null
+ val modifiers = convertModifiers(clazz.access, clazz.visibleAnnotations, clazz.invisibleAnnotations)
+ val simpleName = name(descriptor.name.asString())
+ val typeParams = JavacList.nil()
+ val extending = if (clazz.superName == "java/lang/Object") null else convertFqName(clazz.superName)
+ val implementing = mapValues(clazz.interfaces) { convertFqName(it) }
+
+ val fields = mapValues(clazz.fields) { convertField(it) }
+ val methods = mapValues(clazz.methods) { convertMethod(it, simpleName) }
+ val nestedClasses = mapValues(clazz.innerClasses) { innerClass ->
+ if (innerClass.outerName != clazz.name) return@mapValues null
+ val innerClassNode = classes.firstOrNull { it.name == innerClass.name } ?: return@mapValues null
+ convert(innerClassNode)
+ }
+
+ return treeMaker.ClassDef(modifiers, simpleName, typeParams, extending, implementing, fields + methods + nestedClasses)
+ }
+
+ private fun convertField(field: FieldNode): JCVariableDecl? {
+ if (isSynthetic(field.access)) return null
+
+ val modifiers = convertModifiers(field.access, field.visibleAnnotations, field.invisibleAnnotations)
+ val name = name(field.name)
+ val type = convertFqName(Type.getType(field.desc).className)
+ val value = field.value
+ val initializer = when (value) {
+ is Byte, is Boolean, is Char, is Short, is Int, is Long, is Float, is Double, is String -> treeMaker.Literal(value)
+ else -> null
+ }
+ return treeMaker.VarDef(modifiers, name, type, initializer)
+ }
+
+ private fun convertMethod(method: MethodNode, containingClassSimpleName: Name): JCMethodDecl? {
+ if (isSynthetic(method.access)) return null
+
+ val modifiers = convertModifiers(method.access, method.visibleAnnotations, method.invisibleAnnotations)
+ val name = if (method.name == "") containingClassSimpleName else name(method.name)
+ val typeParameters = JavacList.nil()
+ val receiverParameter = null
+
+ val returnType = Type.getReturnType(method.desc)
+ val returnTypeExpr = convertFqName(returnType.className)
+
+ val parametersInfo = method.getParametersInfo()
+ val parameters = mapValues(parametersInfo) { info ->
+ val modifiers = convertModifiers(info.access, info.visibleAnnotations, info.invisibleAnnotations)
+ val name = name(info.name)
+ val type = convertFqName(info.type)
+ treeMaker.VarDef(modifiers, name, type, null)
+ }
+
+ val thrown = mapValues(method.exceptions) { convertFqName(it) }
+
+ val defaultValue = method.annotationDefault?.let { convertLiteralExpression(it) }
+
+ val body = if (defaultValue != null) {
+ null
+ } else {
+ val returnStatement = treeMaker.Return(convertLiteralExpression(getDefaultValue(returnType)))
+ treeMaker.Block(0, JavacList.of(returnStatement))
+ }
+
+ return treeMaker.MethodDef(modifiers, name, returnTypeExpr,
+ typeParameters, receiverParameter, parameters, thrown, body, defaultValue)
+ }
+
+ private fun convertModifiers(
+ access: Int,
+ visibleAnnotations: List?,
+ invisibleAnnotations: List?
+ ): JCModifiers {
+ var annotations = visibleAnnotations?.fold(JavacList.nil()) { list, anno ->
+ convertAnnotation(anno)?.let { list.prepend(it) } ?: list
+ } ?: JavacList.nil()
+ annotations = invisibleAnnotations?.fold(annotations) { list, anno ->
+ convertAnnotation(anno)?.let { list.prepend(it) } ?: list
+ } ?: annotations
+
+ return treeMaker.Modifiers(access.toLong(), annotations)
+ }
+
+ private fun convertAnnotation(annotation: AnnotationNode): JCAnnotation? {
+ val fqName = Type.getType(annotation.desc).className
+ if (BLACKLISTED_ANNOTATATIONS.any { fqName.startsWith(it) }) return null
+
+ val name = convertFqName(fqName)
+ val values = mapPairedValues(annotation.values) { key, value ->
+ treeMaker.Assign(convertSimpleName(key), convertLiteralExpression(value))
+ }
+ return treeMaker.Annotation(name, values)
+ }
+
+ private fun convertLiteralExpression(value: Any?): JCExpression {
+ return when (value) {
+ null -> convertSimpleName("null")
+ is Byte, is Boolean, is Char, is Short, is Int, is Long, is Float, is Double, is String -> treeMaker.Literal(value)
+
+ is ByteArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is BooleanArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is CharArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is ShortArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is IntArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is LongArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is FloatArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is DoubleArray -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is Array<*> -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value.asIterable()) { convertLiteralExpression(it) })
+ is List<*> -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value) { convertLiteralExpression(it) })
+
+ is Type -> treeMaker.Select(convertFqName(value.className), name("class"))
+ is AnnotationNode -> convertAnnotation(value) ?: error("Annotation is filtered out")
+ else -> throw IllegalArgumentException("Illegal literal expression value: $value (${value.javaClass.canonicalName})")
+ }
+ }
+
+ private fun getDefaultValue(type: Type): Any? = when (type) {
+ Type.BYTE_TYPE -> 0.toByte()
+ Type.BOOLEAN_TYPE -> false
+ Type.CHAR_TYPE -> '\u0000'
+ Type.SHORT_TYPE -> 0.toShort()
+ Type.INT_TYPE -> 0
+ Type.LONG_TYPE -> 0L
+ Type.FLOAT_TYPE -> 0.0F
+ Type.DOUBLE_TYPE -> 0.0
+ else -> null
+ }
+
+ private fun convertFqName(internalOrFqName: String): JCExpression {
+ val path = internalOrFqName.replace('/', '.').split('.')
+ assert(path.isNotEmpty())
+ if (path.size == 1) return convertSimpleName(path.single())
+
+ var expr = treeMaker.Select(convertSimpleName(path[0]), name(path[1]))
+ for (index in 2..path.lastIndex) {
+ expr = treeMaker.Select(expr, name(path[index]))
+ }
+ return expr
+ }
+
+ private fun convertSimpleName(name: String): JCIdent = treeMaker.Ident(name(name))
+
+ private fun name(name: String) = nameTable.fromString(name)
+}
+
+private class ParameterInfo(
+ val access: Int,
+ val name: String,
+ val type: String,
+ val visibleAnnotations: List?,
+ val invisibleAnnotations: List?)
+
+private fun MethodNode.getParametersInfo(): List {
+ val localVariables = this.localVariables ?: emptyList()
+ val parameters = this.parameters ?: emptyList()
+ val isStatic = (access and Opcodes.ACC_STATIC) > 0
+ val parameterTypes = Type.getArgumentTypes(desc)
+ return parameterTypes.mapIndexed { index, type ->
+ val name = parameters.getOrNull(index)?.name ?: localVariables.getOrNull(index + (if (isStatic) 0 else 1))?.name ?: "p$index"
+ val visibleAnnotations = visibleParameterAnnotations?.get(index)
+ val invisibleAnnotations = invisibleParameterAnnotations?.get(index)
+ ParameterInfo(0, name, type.className, visibleAnnotations, invisibleAnnotations)
+ }
+}
+
+private inline fun mapValues(values: Iterable?, f: (T) -> R?): JavacList {
+ if (values == null) return JavacList.nil()
+
+ var result = JavacList.nil()
+ for (item in values) {
+ f(item)?.let { result = result.prepend(it) }
+ }
+ return result.reverse()
+}
+
+private inline fun mapPairedValues(valuePairs: List?, f: (String, Any) -> T?): JavacList {
+ if (valuePairs == null || valuePairs.isEmpty()) return JavacList.nil()
+
+ val size = valuePairs.size
+ var result = JavacList.nil()
+ assert(size % 2 == 0)
+ var index = 0
+ while (index < size) {
+ val key = valuePairs[index] as String
+ val value = valuePairs[index + 1]
+ f(key, value)?.let { result = result.prepend(it) }
+ index += 2
+ }
+ return result.reverse()
+}
+
+private operator fun JavacList.plus(other: JavacList): JavacList {
+ return this.appendList(other)
+}
+
+private fun isSynthetic(access: Int) = (access and Opcodes.ACC_SYNTHETIC) > 0
\ No newline at end of file
diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3AnalysisCompletedHandlerExtension.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3AnalysisCompletedHandlerExtension.kt
index 931d82b6aca..2f314a11877 100644
--- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3AnalysisCompletedHandlerExtension.kt
+++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3AnalysisCompletedHandlerExtension.kt
@@ -77,7 +77,7 @@ class Kapt3AnalysisCompletedHandlerExtension(
}
}
-private class Kapt3BuilderFactory : ClassBuilderFactory {
+class Kapt3BuilderFactory : ClassBuilderFactory {
val compiledClasses = mutableListOf()
val origins = mutableMapOf()
diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/KaptRunner.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/KaptRunner.kt
index 78e79788f71..3169ae82f19 100644
--- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/KaptRunner.kt
+++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/KaptRunner.kt
@@ -51,7 +51,7 @@ class KaptError : RuntimeException {
}
class KaptRunner {
- private val context = Context()
+ val context = Context()
private val options: Options
init {
diff --git a/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/AbstractJCTreeConverterTest.kt b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/AbstractJCTreeConverterTest.kt
new file mode 100644
index 00000000000..a1906e56dd8
--- /dev/null
+++ b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/AbstractJCTreeConverterTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * 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 org.jetbrains.kotlin.kapt3.test
+
+import org.jetbrains.kotlin.codegen.CodegenTestCase
+import org.jetbrains.kotlin.codegen.CodegenTestUtil
+import org.jetbrains.kotlin.kapt3.JCTreeConverter
+import org.jetbrains.kotlin.kapt3.Kapt3BuilderFactory
+import org.jetbrains.kotlin.kapt3.KaptRunner
+import org.jetbrains.kotlin.test.ConfigurationKind
+import org.jetbrains.kotlin.test.KotlinTestUtils
+import java.io.File
+
+abstract class AbstractJCTreeConverterTest : CodegenTestCase() {
+ override fun doMultiFileTest(wholeFile: File, files: List, javaFilesDir: File?) {
+ val javaSources = javaFilesDir?.let { arrayOf(it) } ?: emptyArray()
+
+ createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *javaSources)
+ loadMultiFiles(files)
+
+ val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".txt")
+ val classBuilderFactory = Kapt3BuilderFactory()
+ CodegenTestUtil.generateFiles(myEnvironment, myFiles, classBuilderFactory)
+
+ val kaptRunner = KaptRunner()
+ val converter = JCTreeConverter(kaptRunner.context, classBuilderFactory.compiledClasses, classBuilderFactory.origins)
+ val javaFiles = converter.convert()
+
+ KotlinTestUtils.assertEqualsToFile(txtFile, javaFiles.joinToString("\n\n////////////////////\n\n"))
+ }
+}
+
diff --git a/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/JCTreeConverterTestGenerated.java b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/JCTreeConverterTestGenerated.java
new file mode 100644
index 00000000000..81baee8323c
--- /dev/null
+++ b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/JCTreeConverterTestGenerated.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * 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 org.jetbrains.kotlin.kapt3.test;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
+import org.jetbrains.kotlin.test.KotlinTestUtils;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
+@SuppressWarnings("all")
+@TestMetadata("plugins/kapt3/testData/converter")
+@TestDataPath("$PROJECT_ROOT")
+@RunWith(JUnit3RunnerWithInners.class)
+public class JCTreeConverterTestGenerated extends AbstractJCTreeConverterTest {
+ public void testAllFilesPresentInConverter() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/kapt3/testData/converter"), Pattern.compile("^(.+)\\.kt$"), true);
+ }
+
+ @TestMetadata("dataClass.kt")
+ public void testDataClass() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/dataClass.kt");
+ doTest(fileName);
+ }
+}
diff --git a/plugins/kapt3/test/KaptRunnerTest.kt b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/KaptRunnerTest.kt
similarity index 86%
rename from plugins/kapt3/test/KaptRunnerTest.kt
rename to plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/KaptRunnerTest.kt
index c90c9e9ce78..3acd688842c 100644
--- a/plugins/kapt3/test/KaptRunnerTest.kt
+++ b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/KaptRunnerTest.kt
@@ -1,6 +1,23 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * 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 org.jetbrains.kotlin.kapt3.test
+
import org.jetbrains.kotlin.kapt3.KaptError
import org.jetbrains.kotlin.kapt3.KaptRunner
-import org.junit.Assert
import org.junit.Assert.*
import org.junit.Test
import java.io.File
diff --git a/plugins/kapt3/testData/converter/dataClass.kt b/plugins/kapt3/testData/converter/dataClass.kt
new file mode 100644
index 00000000000..6466e746d38
--- /dev/null
+++ b/plugins/kapt3/testData/converter/dataClass.kt
@@ -0,0 +1 @@
+data class User(val firstName: String, val secondName: String, val age: Int)
\ No newline at end of file
diff --git a/plugins/kapt3/testData/converter/dataClass.txt b/plugins/kapt3/testData/converter/dataClass.txt
new file mode 100644
index 00000000000..9f5b59bd126
--- /dev/null
+++ b/plugins/kapt3/testData/converter/dataClass.txt
@@ -0,0 +1,49 @@
+public final synchronized class User {
+ private final java.lang.String firstName;
+ private final java.lang.String secondName;
+ private final int age;
+
+ public final java.lang.String getFirstName() {
+ return null;
+ }
+
+ public final java.lang.String getSecondName() {
+ return null;
+ }
+
+ public final int getAge() {
+ return 0;
+ }
+
+ public void User(java.lang.String firstName, java.lang.String secondName, int age) {
+ return null;
+ }
+
+ public final java.lang.String component1() {
+ return null;
+ }
+
+ public final java.lang.String component2() {
+ return null;
+ }
+
+ public final int component3() {
+ return 0;
+ }
+
+ public final User copy(java.lang.String firstName, java.lang.String secondName, int age) {
+ return null;
+ }
+
+ public java.lang.String toString() {
+ return null;
+ }
+
+ public int hashCode() {
+ return 0;
+ }
+
+ public boolean equals(java.lang.Object p0) {
+ return false;
+ }
+}
\ No newline at end of file