-
Notifications
You must be signed in to change notification settings - Fork 3
/
YamlTypeDefLoader.scala
223 lines (221 loc) · 9.21 KB
/
YamlTypeDefLoader.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package org.mojoz.metadata
package in
import scala.collection.immutable._
import scala.jdk.CollectionConverters._
import scala.util.Try
class YamlTypeDefLoader(yamlMd: Seq[YamlMd]) {
import YamlTableDefLoader._
val sources = yamlMd.filter(_.parsed.exists(_ contains "type")) // XXX for binary compatibility TODO remove
val typeDefs = {
val rawTypeDefs = sources flatMap { td =>
try loadYamlTypeDefs(td) catch {
case e: Exception => throw new RuntimeException(
s"Failed to load type definitions from ${td.filename}, line ${td.line}", e)
}
}
val nameToTableDef = {
val duplicateNames =
rawTypeDefs.map(_.name).groupBy(n => n).filter(_._2.size > 1).map(_._1)
if (duplicateNames.size > 0)
sys.error(
"Duplicate type definitions: " + duplicateNames.mkString(", "))
rawTypeDefs.map(t => (t.name, t)).toMap
}
// checkTypeDefs(rawTypeDefs)
rawTypeDefs
}
private def toString(src: Any, thisFail: String) = {
src match {
case s: java.lang.String => s
case x => sys.error(
" - unexpected definition class: " + x.getClass
+ "\nentry: " + x.toString)
}
}
private def toMinMax(s: String): (Option[Int], Option[Int]) = {
val parts = s.split("\\.\\.", 2)
def toIntOpt(s: String) = s match {
case "" | "*" => None
case i => Some(i).map(_.toInt) // TODO try, explain
}
if (s == "*")
(Some(0), None)
else {
val min = toIntOpt(parts(0))
val max = if (parts.size > 1) toIntOpt(parts(1)) else min
(min, max)
}
}
private lazy val NOT_JDBC_TYPE = 42 // XXX refactor, get rid of this
private lazy val ident = "[_\\p{IsLatin}][_\\p{IsLatin}0-9]*"
private lazy val PlainTypeDesc = s"^(\\d+|$ident|$ident +$ident)$$".r
private lazy val SizedTypeDesc = s"^(\\d+|$ident|$ident +$ident) +([*\\.\\d]+)$$".r
private lazy val FracTypeDesc = s"^(\\d+|$ident|$ident +$ident) +([*\\.\\d]+) +([*\\.\\d]+)$$".r
private def toJdbcLoadInfo(str: String) = {
val isJdbcType = !str.startsWith("(")
val s = str.stripPrefix("(").stripSuffix(")").trim
val sParts = s.split("->", 2)
val jdbcPart = Option(sParts(0)).map(_.trim).filter(_ != "").orNull
if (jdbcPart == null)
sys.error(
"Unexpected format for jdbc load info: " + s)
val targetPart =
if (sParts.size > 1) Option(sParts(1)).map(_.trim).filter(_ != "").orNull
else null
val targetPartParts = Option(targetPart).map(_.split(",\\s*")) getOrElse Array[String]()
val (jdbcNameOrCode, sizeInterval, fracInterval) = try jdbcPart match {
case FracTypeDesc (n, s, f) => (n, s, f)
case SizedTypeDesc(n, s) => (n, s, "")
case PlainTypeDesc(n) => (n, "", "")
case n => (n, "", "")
} catch {
case util.control.NonFatal(ex) =>
throw new RuntimeException(s"Failed to extract name and intervals from jdbcPart '$jdbcPart'", ex)
}
val jdbcCode =
if (isJdbcType)
Try(jdbcNameOrCode.toInt).toOption
.getOrElse(JdbcTableDefLoader.jdbcTypeNameToCode.get(jdbcNameOrCode)
.getOrElse(sys.error("Unexpected jdbc type name: " + jdbcNameOrCode)))
else NOT_JDBC_TYPE
val (minSize, maxSize) = toMinMax(sizeInterval)
val (minFrac, maxFrac) = toMinMax(fracInterval)
val targetLength: Option[Integer] =
if (targetPartParts.size == 1)
targetPartParts(0) match {
case "none" => None
case "size" => Some(null) // xxx Some(null) means copy from source
case fxSize => Some(Integer.valueOf(fxSize.toInt))
}
else None
val targetTotalDigits: Option[Integer] =
if (targetPartParts.size == 2)
targetPartParts(0) match {
case "none" => None
case "size" => Some(null) // xxx Some(null) means copy from source
case fxSize => Some(Integer.valueOf(fxSize.toInt))
}
else None
val targetFractionDigits: Option[Integer] =
if (targetPartParts.size == 2)
targetPartParts(1) match {
case "none" => None
case "frac" => Some(null) // xxx Some(null) means copy from source
case fxSize => Some(Integer.valueOf(fxSize.toInt))
}
else None
JdbcLoadInfo(jdbcNameOrCode, jdbcCode, minSize, maxSize, minFrac, maxFrac,
targetLength, targetTotalDigits, targetFractionDigits)
}
private def toYamlLoadInfo(s: String) = {
val sParts = s.split("->", 2)
val yamlPart = Option(sParts(0)).map(_.trim).filter(_ != "").orNull
if (yamlPart == null)
sys.error(
"Unexpected format for yaml load info: " + s)
val targetPart =
if (sParts.size > 1) Option(sParts(1)).map(_.trim).filter(_ != "").orNull
else null
val targetPartParts = Option(targetPart).map(_.split(",\\s*")) getOrElse Array[String]()
val yamlPartParts = yamlPart.split("\\s+", 3)
val yamlName = Option(yamlPartParts(0)).filter(_ != "null")
val sizeInterval = if (yamlPartParts.size > 1) yamlPartParts(1) else ""
val (minSize, maxSize) = toMinMax(sizeInterval)
val fracInterval = if (yamlPartParts.size > 2) yamlPartParts(2) else ""
val (minFrac, maxFrac) = toMinMax(fracInterval)
val targetLength: Option[Integer] =
if (targetPartParts.size == 1)
targetPartParts(0) match {
case "none" => None
case "size" => Some(null) // xxx Some(null) means copy from source
case fxSize => Some(Integer.valueOf(fxSize.toInt))
}
else None
val targetTotalDigits: Option[Integer] =
if (targetPartParts.size == 2)
targetPartParts(0) match {
case "none" => None
case "size" => Some(null) // xxx Some(null) means copy from source
case fxSize => Some(Integer.valueOf(fxSize.toInt))
}
else None
val targetFractionDigits: Option[Integer] =
if (targetPartParts.size == 2)
targetPartParts(1) match {
case "none" => None
case "frac" => Some(null) // xxx Some(null) means copy from source
case fxSize => Some(Integer.valueOf(fxSize.toInt))
}
else None
YamlLoadInfo(yamlName, minSize, maxSize, minFrac, maxFrac,
targetLength, targetTotalDigits, targetFractionDigits)
}
private def toSqlWriteInfo(s: String) = {
val sParts = s.split("->", 2)
val typePart = Option(sParts(0)).map(_.trim).filter(_ != "").orNull
if (typePart == null)
sys.error(
"Unexpected format for sql info: " + s)
val targetPattern =
if (sParts.size > 1) Option(sParts(1)).map(_.trim).filter(_ != "") getOrElse typePart
else typePart
val typePartParts = if (sParts.size == 1) Array(targetPattern) else typePart.split("\\s+", 3)
val sizeInterval = if (typePartParts.size > 1) typePartParts(1) else ""
val (minSize, maxSize) = toMinMax(sizeInterval)
val fracInterval = if (typePartParts.size > 2) typePartParts(2) else ""
val (minFrac, maxFrac) = toMinMax(fracInterval)
DdlWriteInfo(minSize, maxSize, minFrac, maxFrac, targetPattern)
}
private def loadYamlTypeDefs(md: YamlMd): Seq[TypeDef] = {
md.parsed.filter(_ contains "type").map { tdMap =>
val typeName = tdMap.get("type").map(_.toString)
.getOrElse(sys.error("Missing type name"))
val targetNames: Map[String, String] = TreeMap()(math.Ordering.String) ++
tdMap.filter(_._1 endsWith " name").map {
case (k, v) => (k.substring(0, k.length - "name".length - 1).trim, "" + v)
}
val jdbcLoad: Map[String, Seq[JdbcLoadInfo]] = TreeMap()(math.Ordering.String) ++
tdMap.filter(_._1 endsWith "jdbc").map {
case (k, v) =>
val jdbcLoadInfoSeq =
(v match {
case null => Nil
case a: java.util.ArrayList[_] => a.asScala.toList
case x => sys.error("Unexpected class: " + x.getClass)
})
.map(toString(_, s"Failed to load jdbc load definition for $k"))
.map(toJdbcLoadInfo)
(k, jdbcLoadInfoSeq)
}
val yamlLoad = tdMap.get("yaml")
.map {
case null => Nil
case a: java.util.ArrayList[_] => a.asScala.toList
case x => sys.error("Unexpected class: " + x.getClass)
}
.getOrElse(Nil)
.map(toString(_, "Failed to load yaml load definition"))
.map(toYamlLoadInfo)
val ddlWrite: Map[String, Seq[DdlWriteInfo]] = TreeMap()(math.Ordering.String) ++
tdMap.filter {
case (k, v) =>
k.endsWith("sql") || k.endsWith("cql")
}.map {
case (k, v) =>
val ddlWriteInfoSeq =
(v match {
case null => Nil
case a: java.util.ArrayList[_] => a.asScala.toList
case x => sys.error("Unexpected class: " + x.getClass)
})
.map(toString(_, s"Failed to load ddl write definition for $k"))
.map(toSqlWriteInfo)
(k, ddlWriteInfoSeq)
}
val defaults = null // TODO
val namingConventions: Seq[String] = Nil // TODO
val extras: Map[String, Any] = Map.empty // TODO val extras = tdMap -- TypeDefKeyStrings
TypeDef(typeName, targetNames, jdbcLoad, yamlLoad, ddlWrite, defaults, namingConventions, extras)
}
}
}