Skip to content

Commit d4dfe29

Browse files
authored
#142: Add the ability to list class constructor parameter names (#144)
* extended `.gitignore` for VS Code files
1 parent 57c7606 commit d4dfe29

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ syntax: glob
3535

3636
# vs code
3737
.vscode
38+
.metals/
39+
.bloop/
40+
metals.sbt
3841

3942
# building
4043
target
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2022 ABSA Group Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package za.co.absa.db.fadb.utils
18+
19+
import za.co.absa.db.fadb.naming.NamingConvention
20+
import za.co.absa.db.fadb.naming.implementations.SnakeCaseNaming
21+
22+
import java.lang
23+
import scala.reflect.runtime.universe._
24+
25+
object ClassFieldNamesExtractor {
26+
27+
private def doExtract[T: TypeTag](namingConvention: NamingConvention): Seq[String] = {
28+
val tpe = typeOf[T]
29+
if (!tpe.typeSymbol.isClass) {
30+
throw new IllegalArgumentException(s"${tpe.typeSymbol} is not a case class nor a class")
31+
}
32+
val cl = tpe.typeSymbol.asClass
33+
if (cl.isPrimitive) {
34+
throw new IllegalArgumentException(s"${tpe.typeSymbol} is a primitive type, extraction is not supported")
35+
}
36+
if (cl.isTrait) {
37+
throw new IllegalArgumentException(s"${tpe.typeSymbol} is a trait, extraction is not supported")
38+
}
39+
tpe
40+
.decl(termNames.CONSTRUCTOR)
41+
.asMethod
42+
.paramLists
43+
.flatten
44+
.map(_.name.decodedName.toString)
45+
.map(namingConvention.stringPerConvention)
46+
}
47+
48+
/**
49+
* Extracts constructor field names from case class or regular class, and converts them according to naming convention.
50+
* @param namingConvention - the naming convention to use when converting the constructor parameters names into field name
51+
* @tparam T - type to investigate and extract field names from
52+
* @return - list of field names
53+
*/
54+
def extract[T: TypeTag](
55+
implicit namingConvention: NamingConvention = SnakeCaseNaming.Implicits.namingConvention
56+
): Seq[String] = {
57+
doExtract[T](namingConvention)
58+
}
59+
60+
def extract[T: TypeTag](namingConvention: NamingConvention): Seq[String] = {
61+
doExtract[T](namingConvention)
62+
}
63+
64+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2025 ABSA Group Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package za.co.absa.db.fadb.utils
18+
19+
import org.scalatest.funsuite.AnyFunSuiteLike
20+
import za.co.absa.db.fadb.naming.LettersCase
21+
import za.co.absa.db.fadb.naming.implementations.AsIsNaming
22+
import za.co.absa.db.fadb.utils.ClassFieldNamesExtractorUnitTests._
23+
24+
class ClassFieldNamesExtractorUnitTests extends AnyFunSuiteLike {
25+
test("Extract from case class returns its fields") {
26+
val expected = Seq(
27+
"int_field",
28+
"string_field"
29+
)
30+
val fieldNames = ClassFieldNamesExtractor.extract[TestCaseClass]
31+
assert(fieldNames == expected)
32+
}
33+
34+
test("Extract from class constructor fields returns its constructor fields, explicit naming convention") {
35+
val expected = Seq(
36+
"XFIELD",
37+
"YFIELD"
38+
)
39+
val fieldNames = ClassFieldNamesExtractor.extract[TestClass](new AsIsNaming(LettersCase.UpperCase))
40+
assert(fieldNames == expected)
41+
}
42+
43+
test("Extract from class constructor fields returns its constructor fields, implicit naming convention") {
44+
implicit val namingConvention: AsIsNaming = new AsIsNaming(LettersCase.LowerCase)
45+
val expected = Seq(
46+
"xfield",
47+
"yfield"
48+
)
49+
val fieldNames = ClassFieldNamesExtractor.extract[TestClass]
50+
assert(fieldNames == expected)
51+
}
52+
53+
test("Extract from trait fails") {
54+
intercept[IllegalArgumentException] {
55+
ClassFieldNamesExtractor.extract[TestTrait]
56+
}
57+
}
58+
59+
test("Extract fails on simple type") {
60+
intercept[IllegalArgumentException] {
61+
ClassFieldNamesExtractor.extract[Boolean]
62+
}
63+
}
64+
65+
}
66+
67+
object ClassFieldNamesExtractorUnitTests {
68+
case class TestCaseClass(intField: Int, stringField: String) {
69+
def stringFunction: String = intField.toString + stringField
70+
}
71+
72+
class TestClass(val xField: Int, val yField: String) {
73+
val w: String = xField.toString + yField
74+
75+
def z: String = xField.toString + yField
76+
}
77+
78+
trait TestTrait {
79+
def foo: String
80+
}
81+
82+
}

project/Dependencies.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import sbt.*
1919
object Dependencies {
2020

2121
private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq(
22+
"org.scala-lang" % "scala-reflect" % scalaVersion,
2223
"org.typelevel" %% "cats-core" % "2.9.0",
2324
"org.typelevel" %% "cats-effect" % "3.5.0",
2425
"org.scalatest" %% "scalatest" % "3.1.0" % Test,

0 commit comments

Comments
 (0)