Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -6783,11 +6783,14 @@ public final class org/jetbrains/kotlinx/dataframe/schema/CompareResult : java/l
public static final field Equals Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;
public static final field IsDerived Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;
public static final field IsSuper Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;
public static final field Matches Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;
public static final field None Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;
public final fun combine (Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;)Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public final fun isEqual ()Z
public final fun isSuperOrEqual ()Z
public final fun isSuperOrMatches ()Z
public final fun matches ()Z
public static fun valueOf (Ljava/lang/String;)Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;
public static fun values ()[Lorg/jetbrains/kotlinx/dataframe/schema/CompareResult;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import org.jetbrains.kotlinx.dataframe.schema.DataFrameSchema
private fun renderNullability(nullable: Boolean) = if (nullable) "?" else ""

internal fun Iterable<Marker>.filterRequiredForSchema(schema: DataFrameSchema) =
filter { it.isOpen && it.schema.compare(schema, ComparisonMode.STRICT_FOR_NESTED_SCHEMAS).isSuperOrEqual() }
filter { it.isOpen && it.schema.compare(schema, ComparisonMode.STRICT_FOR_NESTED_SCHEMAS).isSuperOrMatches() }

internal val charsToQuote = """[ `(){}\[\].<>'"/|\\!?@:;%^&*#$-]""".toRegex()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ internal class ReplCodeGeneratorImpl : ReplCodeGenerator {
if (currentMarker.isOpen) {
val columnSchema = currentMarker.schema
// for mutable properties we do strong typing only at the first processing, after that we allow its type to be more general than actual dataframe type
if (wasProcessedBefore || columnSchema == targetSchema) {
if (wasProcessedBefore || columnSchema.compare(targetSchema).matches()) {
// property scheme is valid for current dataframe, but we should also check that all compatible open markers are implemented by it
val requiredBaseMarkers = registeredMarkers.values.filterRequiredForSchema(columnSchema)
if (requiredBaseMarkers.any() && requiredBaseMarkers.all { currentMarker.implements(it) }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ internal class SchemaProcessorImpl(
override val generatedMarkers = mutableListOf<Marker>()

private fun DataFrameSchema.getAllSuperMarkers() =
registeredMarkers
.filter { it.isOpen && it.schema.compare(this, ComparisonMode.STRICT_FOR_NESTED_SCHEMAS).isSuperOrEqual() }
registeredMarkers.filter {
it.isOpen && it.schema.compare(this, ComparisonMode.STRICT_FOR_NESTED_SCHEMAS).isSuperOrMatches()
}

private fun List<Marker>.onlyLeafs(): List<Marker> {
val skip = flatMap { it.allSuperMarkers.keys }.toSet()
Expand Down Expand Up @@ -81,8 +82,6 @@ internal class SchemaProcessorImpl(
columnSchema.nullable,
renderAsList = true,
)

else -> throw NotImplementedError()
}

return schema.columns.asIterable().sortedBy { it.key }.flatMapIndexed { index, column ->
Expand Down Expand Up @@ -159,7 +158,7 @@ internal class SchemaProcessorImpl(
val markerName: String
val required = schema.getRequiredMarkers()
val existingMarker = registeredMarkers.firstOrNull {
(!isOpen || it.isOpen) && it.schema == schema && it.implementsAll(required)
(!isOpen || it.isOpen) && it.schema.compare(schema).matches() && it.implementsAll(required)
}
if (existingMarker != null) {
return existingMarker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@ package org.jetbrains.kotlinx.dataframe.impl.schema
import org.jetbrains.kotlinx.dataframe.impl.renderType
import org.jetbrains.kotlinx.dataframe.schema.ColumnSchema
import org.jetbrains.kotlinx.dataframe.schema.CompareResult
import org.jetbrains.kotlinx.dataframe.schema.CompareResult.Equals
import org.jetbrains.kotlinx.dataframe.schema.CompareResult.IsDerived
import org.jetbrains.kotlinx.dataframe.schema.CompareResult.IsSuper
import org.jetbrains.kotlinx.dataframe.schema.CompareResult.Matches
import org.jetbrains.kotlinx.dataframe.schema.CompareResult.None
import org.jetbrains.kotlinx.dataframe.schema.ComparisonMode
import org.jetbrains.kotlinx.dataframe.schema.ComparisonMode.STRICT
import org.jetbrains.kotlinx.dataframe.schema.ComparisonMode.STRICT_FOR_NESTED_SCHEMAS
import org.jetbrains.kotlinx.dataframe.schema.DataFrameSchema
import org.jetbrains.kotlinx.dataframe.schema.plus
import kotlin.collections.forEach

public class DataFrameSchemaImpl(override val columns: Map<String, ColumnSchema>) : DataFrameSchema {

override fun compare(other: DataFrameSchema, comparisonMode: ComparisonMode): CompareResult {
require(other is DataFrameSchemaImpl)
if (this === other) return Equals
if (this === other) return Matches

var result: CompareResult = Equals
var result: CompareResult = Matches

// check for each column in this schema if there is a column with the same name in the other schema
// - if so, check those schemas for equality, taking comparisonMode into account
Expand Down Expand Up @@ -54,11 +53,47 @@ public class DataFrameSchemaImpl(override val columns: Map<String, ColumnSchema>
return result
}

override fun equals(other: Any?): Boolean = other is DataFrameSchema && this.compare(other).isEqual()
/**
* Returns `true` if, and only if,
* [this schema][this] has the same columns **in the same order** as the [other schema][other].
* The types must also match exactly.
*
* Use [compare][DataFrameSchema.compare] it the order does not matter and
* for other comparison options.
*
* @see [DataFrameSchema.compare]
* @see [CompareResult.matches]
*/
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DataFrameSchema) return false
if (this.compare(other) != Matches) return false
if (columns.keys.toList() != other.columns.keys.toList()) return false

for ((name, col) in columns) {
val other = other.columns[name]!!
when (col) {
is ColumnSchema.Group -> {
other as ColumnSchema.Group // safe to cast because of compare
if (col.schema != other.schema) return false
}

is ColumnSchema.Frame -> {
other as ColumnSchema.Frame // safe to cast because of compare
if (col.schema != other.schema) return false
}

// already checked by compare
is ColumnSchema.Value -> Unit
}
}

return true
}

override fun toString(): String = render()

override fun hashCode(): Int = columns.hashCode()
override fun hashCode(): Int = columns.toList().hashCode()
}

internal fun DataFrameSchemaImpl.render(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public sealed class ColumnSchema {

public fun compare(other: Value, comparisonMode: ComparisonMode = LENIENT): CompareResult =
when {
type == other.type -> CompareResult.Equals
type == other.type -> CompareResult.Matches
comparisonMode == STRICT -> CompareResult.None
type.isSubtypeOf(other.type) -> CompareResult.IsDerived
type.isSupertypeOf(other.type) -> CompareResult.IsSuper
Expand Down Expand Up @@ -80,7 +80,7 @@ public sealed class ColumnSchema {
) + CompareResult.compareNullability(thisIsNullable = nullable, otherIsNullable = other.nullable)
}

/** Checks equality just on kind, type, or schema. */
/** Checks equality by kind, type, or schema. TODO was matching, check if == works. */
override fun equals(other: Any?): Boolean {
val otherType = other as? ColumnSchema ?: return false
if (otherType.kind != kind) return false
Expand All @@ -94,7 +94,7 @@ public sealed class ColumnSchema {

public fun compare(other: ColumnSchema, comparisonMode: ComparisonMode = LENIENT): CompareResult {
if (kind != other.kind) return CompareResult.None
if (this === other) return CompareResult.Equals
if (this === other) return CompareResult.Matches
return when (this) {
is Value -> compare(other as Value, comparisonMode)
is Group -> compare(other as Group, comparisonMode)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,93 @@
package org.jetbrains.kotlinx.dataframe.schema

import org.jetbrains.kotlinx.dataframe.util.COMPARE_RESULT_EQUALS

public enum class CompareResult {

// TODO can be reintroduced at 1.1 to support "equals exactly" as CompareResult
@Deprecated(
message = COMPARE_RESULT_EQUALS,
replaceWith = ReplaceWith("Matches", "org.jetbrains.kotlinx.dataframe.schema.CompareResult.Matches"),
level = DeprecationLevel.ERROR,
)
Equals,

/**
* If the other schema has columns this has not,
* or their columns have a more specific type than in this schema,
* this is considered "super".
*/
IsSuper,

/**
* If this schema has columns the other has not,
* or their columns have a less specific type than in this schema,
* this is considered "derived".
*/
IsDerived,

/** The two schemas are incomparable. */
None,

/**
* Both schemas contain exactly the same columns, column groups, and frame columns,
* though their order might still be different.
*/
Matches,
;

public fun isSuperOrEqual(): Boolean = this == Equals || this == IsSuper
/**
* True if
* - both schemas contain exactly the same columns, column groups, and frame columns
* - or the other schema has columns this has not
* - or their columns have a more specific type than in this schema
*
* The column order might still be different.
*/
public fun isSuperOrMatches(): Boolean = this == Matches || this == IsSuper

@Deprecated(
message = COMPARE_RESULT_EQUALS,
replaceWith = ReplaceWith("isSuperOrMatches()"),
level = DeprecationLevel.ERROR,
)
public fun isSuperOrEqual(): Boolean = isSuperOrMatches()

/**
* True if both schemas contain exactly the same columns, column groups, and frame columns,
* though their order might still be different.
*/
public fun matches(): Boolean = this == Matches

@Deprecated(
message = COMPARE_RESULT_EQUALS,
replaceWith = ReplaceWith("matches()"),
level = DeprecationLevel.ERROR,
)
public fun isEqual(): Boolean = this.matches()

public fun isEqual(): Boolean = this == Equals
/**
* Temporary helper method to avoid breaking changes.
*/
@Deprecated(
message = COMPARE_RESULT_EQUALS,
level = DeprecationLevel.WARNING,
)
private fun isDeprecatedEquals(): Boolean = this != IsSuper && this != IsDerived && this != None && this != Matches

public fun combine(other: CompareResult): CompareResult =
when (this) {
Equals -> other
Matches -> other
None -> None
IsDerived -> if (other == Equals || other == IsDerived) this else None
IsSuper -> if (other == Equals || other == IsSuper) this else None
IsDerived -> if (other == Matches || other == IsDerived || other.isDeprecatedEquals()) this else None
IsSuper -> if (other == Matches || other == IsSuper || other.isDeprecatedEquals()) this else None
else -> other
}

public companion object {
public fun compareNullability(thisIsNullable: Boolean, otherIsNullable: Boolean): CompareResult =
when {
thisIsNullable == otherIsNullable -> Equals
thisIsNullable == otherIsNullable -> Matches
thisIsNullable -> IsSuper
else -> IsDerived
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ public interface DataFrameSchema {
public val columns: Map<String, ColumnSchema>

/**
* Compares this schema with [other] schema.
*
* @param comparisonMode The [mode][ComparisonMode] to compare the schema's by.
* By default, generated markers for leafs aren't used as supertypes: `@DataSchema(isOpen = false)`
* Setting [comparisonMode] to [ComparisonMode.STRICT_FOR_NESTED_SCHEMAS] takes this into account
* for internal codegen logic.
*
* @return a [CompareResult] that indicates whether this schema compared to [other] is
* [matching][CompareResult.Matches] (neglecting order),
* [derived][CompareResult.IsDerived], [superset][CompareResult.IsSuper], or [incomparable][CompareResult.None].
*/
public fun compare(other: DataFrameSchema, comparisonMode: ComparisonMode = ComparisonMode.LENIENT): CompareResult
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ internal const val DISPLAY_CONFIGURATION = "This constructor is only here for bi

internal const val DISPLAY_CONFIGURATION_COPY = "This function is only here for binary compatibility. $MESSAGE_1_0"

internal const val COMPARE_RESULT_EQUALS =
"'Equals' is deprecated in favor of 'Matches' to clarify column order is irrelevant. $MESSAGE_1_0"

// endregion

// region WARNING in 1.0, ERROR in 1.1
Expand Down
Loading