Skip to content

Commit 1559530

Browse files
authored
Revisit equals and hashcode on Column and ViewPortColumns (#1751)
* Revisit equals and hashcode on Column and ViewPortColumns * Revisit equals and hashcode on Column and ViewPortColumns * Revisit equals and hashcode on Column and ViewPortColumns * Revisit equals and hashcode on Column and ViewPortColumns * Revisit equals and hashcode on Column and ViewPortColumns
1 parent 1d3fae2 commit 1559530

File tree

9 files changed

+238
-71
lines changed

9 files changed

+238
-71
lines changed

vuu/src/main/scala/org/finos/vuu/api/TableDef.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ case class JoinTableDef(
253253

254254
val joinFieldColumns = joins.toArray.zip(startIndex to endIndex).map({ case (join, index) => {
255255
val baseColumn = join.table.columnForName(join.joinSpec.right)
256-
new JoinColumn(baseColumn.name, index, baseColumn.dataType, join.table, baseColumn)
256+
JoinColumn(baseColumn.name, index, baseColumn.dataType, join.table, baseColumn, isAlias = false)
257257
}
258258
})
259259

vuu/src/main/scala/org/finos/vuu/core/table/Column.scala

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package org.finos.vuu.core.table
22

33
import com.typesafe.scalalogging.StrictLogging
44
import org.finos.vuu.api.TableDef
5-
import org.finos.vuu.core.table.DefaultColumnNames.{CreatedTimeColumnName, LastUpdatedTimeColumnName, allDefaultColumns}
5+
import org.finos.vuu.core.table.DefaultColumnNames.allDefaultColumns
66
import org.finos.vuu.core.table.column.CalculatedColumnClause
77
import org.finos.vuu.util.schema.ExternalEntitySchema
88
import org.finos.vuu.util.types.{DefaultTypeConverters, TypeConverterContainerBuilder}
@@ -76,7 +76,7 @@ object Columns {
7676
}).toArray
7777

7878
def from(table: TableDef, names: Seq[String]): Array[Column] = {
79-
table.columns.filter(c => names.contains(c.name)).map(c => new JoinColumn(c.name, c.index, c.dataType, table, c))
79+
table.columns.filter(c => names.contains(c.name)).map(c => JoinColumn(c.name, c.index, c.dataType, table, c, isAlias = false))
8080
}
8181

8282
/**
@@ -89,7 +89,7 @@ object Columns {
8989

9090
def aliased(table: TableDef, aliases: (String, String)*): Array[Column] = {
9191
val aliased = aliases.map(tuple => tuple._1 -> tuple._2).toMap
92-
table.columns.filter(c => aliased.contains(c.name)) map (c => new AliasedJoinColumn(aliased(c.name), c.index, c.dataType, table, c).asInstanceOf[Column])
92+
table.columns.filter(c => aliased.contains(c.name)) map (c => JoinColumn(aliased(c.name), c.index, c.dataType, table, c, isAlias = true).asInstanceOf[Column])
9393
}
9494

9595
/**
@@ -100,7 +100,7 @@ object Columns {
100100

101101
val excluded = columnsToExclude.map(s => s -> 1).toMap
102102

103-
table.columns.filterNot(c => excluded.contains(c.name)).map(c => new JoinColumn(c.name, c.index, c.dataType, table, c))
103+
table.columns.filterNot(c => excluded.contains(c.name)).map(c => JoinColumn(c.name, c.index, c.dataType, table, c, isAlias = false))
104104
}
105105

106106
/**
@@ -124,10 +124,26 @@ trait Column {
124124

125125
def getDataFullyQualified(data: RowData): Any
126126

127-
override def hashCode(): Int = name.hashCode()
128127
}
129128

130-
object NoColumn extends Column {
129+
trait JoinColumn extends Column {
130+
def sourceTable: TableDef
131+
def sourceColumn: Column
132+
}
133+
134+
object JoinColumn {
135+
136+
def apply(name: String, index: Int, dataType: Class[_], sourceTable: TableDef, sourceColumn: Column, isAlias: Boolean): JoinColumn = {
137+
if (isAlias) {
138+
AliasedJoinColumn(name, index, dataType, sourceTable, sourceColumn)
139+
} else {
140+
SimpleJoinColumn(name, index, dataType, sourceTable, sourceColumn)
141+
}
142+
}
143+
144+
}
145+
146+
case class NoColumn() extends Column {
131147
override def name: String = "NoColumn"
132148

133149
override def index: Int = -1
@@ -137,6 +153,16 @@ object NoColumn extends Column {
137153
override def getData(data: RowData): Any = None
138154

139155
override def getDataFullyQualified(data: RowData): Any = None
156+
157+
override def hashCode(): Int = -1
158+
159+
override def equals(obj: scala.Any): Boolean = {
160+
obj match {
161+
case _ : NoColumn => true
162+
case _ => false
163+
}
164+
}
165+
140166
}
141167

142168
case class SimpleColumn(name: String, index: Int, dataType: Class[_]) extends Column {
@@ -145,35 +171,69 @@ case class SimpleColumn(name: String, index: Int, dataType: Class[_]) extends Co
145171
}
146172

147173
override def getDataFullyQualified(data: RowData): Any = getData(data)
174+
175+
private lazy val hash: Int = name.hashCode * dataType.hashCode()
176+
177+
override def hashCode(): Int = hash
178+
179+
override def equals(obj: scala.Any): Boolean = {
180+
obj match {
181+
case other: SimpleColumn =>
182+
this.name == other.name &&
183+
this.dataType == other.dataType
184+
case _ => false
185+
}
186+
}
187+
148188
}
149189

150-
class AliasedJoinColumn(name: String, index: Int, dataType: Class[_], sourceTable: TableDef, sourceColumn: Column) extends JoinColumn(name, index, dataType, sourceTable, sourceColumn) {
190+
private case class SimpleJoinColumn(name: String, index: Int, dataType: Class[_], sourceTable: TableDef, sourceColumn: Column) extends JoinColumn {
151191

152-
override def getData(data: RowData): Any = data.get(sourceColumn.name)
192+
override def toString: String = s"${sourceTable.name}.$sourceColumn@$name"
153193

154-
override def getDataFullyQualified(data: RowData): Any = data.get(sourceTable.fullyQuallifiedColumnName(sourceColumn.name))
194+
override def getData(data: RowData): Any = data.get(name)
195+
196+
override def getDataFullyQualified(data: RowData): Any = data.get(sourceTable.fullyQuallifiedColumnName(name))
197+
198+
private lazy val hash: Int = name.hashCode * dataType.hashCode() * sourceTable.name.hashCode * sourceColumn.name.hashCode
199+
200+
override def hashCode(): Int = hash
201+
202+
override def equals(obj: scala.Any): Boolean = {
203+
obj match {
204+
case other: SimpleJoinColumn =>
205+
this.sourceTable.name == other.sourceTable.name &&
206+
this.sourceColumn.name == other.sourceColumn.name &&
207+
this.name == other.name &&
208+
this.dataType == other.dataType
209+
case _ => false
210+
}
211+
}
155212
}
156213

157-
class JoinColumn(name: String, index: Int, dataType: Class[_], val sourceTable: TableDef, val sourceColumn: Column) extends SimpleColumn(name, index, dataType) {
214+
private case class AliasedJoinColumn(name: String, index: Int, dataType: Class[_], sourceTable: TableDef, sourceColumn: Column) extends JoinColumn {
158215

159-
private lazy val lazyToString = s"${sourceTable.name}.$sourceColumn@$name"
160-
private lazy val lazyHash = lazyToString.hashCode
216+
override def toString: String = s"${sourceTable.name}.$sourceColumn@alias($name)"
161217

162-
override def toString: String = lazyToString
218+
override def getData(data: RowData): Any = data.get(sourceColumn.name)
163219

164-
override def hashCode(): Int = lazyHash
220+
override def getDataFullyQualified(data: RowData): Any = data.get(sourceTable.fullyQuallifiedColumnName(sourceColumn.name))
165221

166-
override def getDataFullyQualified(data: RowData): Any = data.get(sourceTable.fullyQuallifiedColumnName(name))
222+
private lazy val hash: Int = name.hashCode * dataType.hashCode() * sourceTable.name.hashCode * sourceColumn.name.hashCode
167223

168-
override def getData(data: RowData): Any = data.get(name)
224+
override def hashCode(): Int = hash
169225

170226
override def equals(obj: scala.Any): Boolean = {
171227
obj match {
172-
case other: JoinColumn =>
173-
other.sourceColumn.name == this.sourceColumn.name && other.sourceTable.name == this.sourceTable.name
228+
case other: AliasedJoinColumn =>
229+
this.sourceTable.name == other.sourceTable.name &&
230+
this.sourceColumn.name == other.sourceColumn.name &&
231+
this.name == other.name &&
232+
this.dataType == other.dataType
174233
case _ => false
175234
}
176235
}
236+
177237
}
178238

179239
case class CalculatedColumn(name: String, clause: CalculatedColumnClause, index: Int, dataType: Class[_]) extends Column with StrictLogging {
@@ -184,4 +244,12 @@ case class CalculatedColumn(name: String, clause: CalculatedColumnClause, index:
184244
)
185245

186246
override def getDataFullyQualified(data: RowData): Any = getData(data)
247+
248+
override def hashCode(): Int = name.hashCode * dataType.hashCode()
249+
250+
override def equals(obj: Any): Boolean = obj match {
251+
case that: CalculatedColumn => that.name == name && that.dataType == this.dataType
252+
case _ => false
253+
}
254+
187255
}

vuu/src/main/scala/org/finos/vuu/core/table/ViewPortColumnCreator.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,14 @@ object ViewPortColumnCreator {
1717

1818
def create(table: DataTable, columns: List[String]): ViewPortColumns = {
1919

20-
//val staticColumns = columns.map( col => table.getTableDef.columnForName(col)).toList
21-
22-
val vpColumns = new ViewPortColumns(List())
20+
var vpColumns: ViewPortColumns = ViewPortColumns()
2321

2422
columns.foreach( column => {
2523
if (isCalculatedColumn(column)) {
2624
val (name, dataType, definition) = parseCalcColumn(column)
27-
vpColumns.addColumn(CalculatedColumnFactory.parse(vpColumns, name, dataType, definition))
25+
vpColumns = ViewPortColumns(CalculatedColumnFactory.parse(vpColumns, name, dataType, definition), vpColumns)
2826
} else {
29-
vpColumns.addColumn(table.getTableDef.columnForName(column))
27+
vpColumns = ViewPortColumns(table.getTableDef.columnForName(column), vpColumns)
3028
}
3129
})
3230

vuu/src/main/scala/org/finos/vuu/viewport/ViewPortColumns.scala

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,40 @@ package org.finos.vuu.viewport
22

33
import org.finos.vuu.core.table.{CalculatedColumn, Column, RowData, RowWithData, ViewPortColumnCreator}
44

5-
class ViewPortColumns(sourceColumns: List[Column]) {
6-
7-
@volatile private var columns: List[Column] = sourceColumns
5+
trait ViewPortColumns {
6+
def columnExists(name: String): Boolean
7+
def getColumns(): List[Column]
8+
def getColumnForName(name: String): Option[Column]
9+
def count(): Int
10+
def pullRow(key: String, row: RowData): RowData
11+
def pullRowAlwaysFilter(key: String, row: RowData): RowData
12+
}
813

9-
private def canEqual(a: Any): Boolean = a.isInstanceOf[ViewPortColumns]
14+
object ViewPortColumns {
1015

11-
override def equals(that: Any): Boolean =
12-
that match {
13-
case that: ViewPortColumns =>
14-
that.canEqual(this) &&
15-
this.columns.size == that.columns.size &&
16-
0 == this.columns.sortBy(c => c.name)
17-
.zip(that.columns.sortBy(c => c.name))
18-
.count(columnPair => columnPair._1.name != columnPair._2.name || columnPair._1.dataType != columnPair._2.dataType)
19-
case _ => false
20-
}
16+
def apply(): ViewPortColumns = ViewPortColumnsImpl(List())
2117

22-
override def hashCode(): Int = columns.sortBy(c => c.name).map(_.hashCode()).hashCode()
18+
def apply(columns: List[Column]): ViewPortColumns = {
19+
ViewPortColumnsImpl(columns)
20+
}
2321

24-
def addColumn(column: Column): Unit = {
25-
columns = columns ++ List(column)
22+
def apply(column: Column, viewPortColumns: ViewPortColumns): ViewPortColumns = {
23+
ViewPortColumnsImpl(viewPortColumns.getColumns() :+ column)
2624
}
2725

28-
def columnExists(name: String): Boolean = {
29-
columns.exists(_.name == name)
26+
}
27+
28+
private case class ViewPortColumnsImpl(sourceColumns: List[Column]) extends ViewPortColumns {
29+
30+
override def columnExists(name: String): Boolean = {
31+
getColumns().exists(_.name == name)
3032
}
3133

32-
def getColumns(): List[Column] = columns
34+
override def getColumns(): List[Column] = sourceColumns
3335

34-
def getColumnForName(name: String): Option[Column] = {
36+
override def getColumnForName(name: String): Option[Column] = {
3537
val evaluatedName = getEvaluatedName(name)
36-
columns.find(_.name == evaluatedName)
38+
getColumns().find(_.name == evaluatedName)
3739
}
3840

3941
private def getEvaluatedName(name: String): String = {
@@ -45,21 +47,29 @@ class ViewPortColumns(sourceColumns: List[Column]) {
4547
}
4648
}
4749

48-
def count(): Int = columns.size
49-
50-
private lazy val hasCalculatedColumn = columns.exists(c => c.isInstanceOf[CalculatedColumn])
50+
override def count(): Int = getColumns().size
5151

52-
def pullRow(key: String, row: RowData): RowData = {
52+
private lazy val hasCalculatedColumn = sourceColumns.exists(c => c.isInstanceOf[CalculatedColumn])
5353

54+
override def pullRow(key: String, row: RowData): RowData = {
5455
if (!hasCalculatedColumn) {
5556
row
5657
} else {
5758
this.pullRowAlwaysFilter(key, row)
5859
}
5960
}
6061

61-
def pullRowAlwaysFilter(key: String, row: RowData): RowData = {
62+
override def pullRowAlwaysFilter(key: String, row: RowData): RowData = {
6263
val rowData = this.getColumns().map(c => c.name -> row.get(c)).toMap
6364
RowWithData(key, rowData)
6465
}
66+
67+
private lazy val hash: Int = sourceColumns.hashCode()
68+
69+
override def hashCode(): Int = hash
70+
71+
override def equals(obj: Any): Boolean = obj match {
72+
case that: ViewPortColumnsImpl => that.sourceColumns == sourceColumns
73+
case _ => false
74+
}
6575
}

vuu/src/test/scala/org/finos/vuu/core/filter/FilterClauseTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,5 +211,5 @@ class FilterClauseTest extends AnyFeatureSpec with Matchers {
211211
}
212212

213213
private def givenARow(assetClass: String) = RowWithData("key", Map("assetClass" -> assetClass))
214-
private def givenVpColumns(names: List[String]) = new ViewPortColumns(names.map(SimpleColumn(_, -1, classOf[Any])))
214+
private def givenVpColumns(names: List[String]) = ViewPortColumns(names.map(SimpleColumn(_, -1, classOf[Any])))
215215
}

0 commit comments

Comments
 (0)