Skip to content

Commit

Permalink
+ new extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
Artem committed Mar 6, 2021
1 parent 1cc3f08 commit cae68b4
Show file tree
Hide file tree
Showing 13 changed files with 711 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package ru.wearemad.mad_base.collection

fun <T> MutableList<T>.copyListAndReplaceItem(
predicate: (T) -> Boolean,
duplicator: (T) -> T,
action: (T) -> Unit = {}
): MutableList<T> {
val newList = this.toMutableList()
val itemPosition = newList.indexOfFirst(predicate)
if (itemPosition != -1) {
val copiedItem = duplicator(newList[itemPosition])
action(copiedItem)
newList.removeAt(itemPosition)
newList.add(itemPosition, copiedItem)
}
return newList
}

fun <T> MutableList<T>.replaceItem(
predicate: (T) -> Boolean,
duplicator: (T) -> T,
action: (T) -> Unit = {}
) {
val itemPosition = indexOfFirst(predicate)
if (itemPosition != -1) {
val copiedItem = duplicator(get(itemPosition))
action(copiedItem)
removeAt(itemPosition)
add(itemPosition, copiedItem)
}
}

fun <T> MutableList<T>.replaceItemByPosition(
itemPosition: Int,
duplicator: (T) -> T
) {
val copiedItem = duplicator(get(itemPosition))
removeAt(itemPosition)
add(itemPosition, copiedItem)
}

fun <T> MutableList<T>.replaceItemFromOneListToAnother(
source: List<T>,
targetPredicate: (T) -> Boolean,
sourcePredicate: (T) -> Boolean = targetPredicate
) {
val targetPosition = indexOfFirst(targetPredicate)
val sourcePosition = source.indexOfFirst(sourcePredicate)
if (targetPosition != -1 && sourcePosition != -1) {
removeAt(targetPosition)
add(targetPosition, source[sourcePosition])
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ru.wearemad.mad_base.data_wrapper

/**
* Extension-функции для [DataWrapperInterface]
*/

fun <T> Collection<DataWrapperInterface<T>>.getData(): Collection<T> =
this.map { it.data }

fun <T> List<DataWrapperInterface<T>>.getData(): List<T> =
this.map { it.data }

fun <T> Set<DataWrapperInterface<T>>.getData(): Set<T> =
this.map { it.data }.toSet()

/**
* Найти объект(ы) в коллекции по filterPredicate
* и изменить в соответствии с applyConsumer
*/
fun <T, E : DataWrapperInterface<T>> filterAndApply(
collection: Collection<E>,
filterPredicate: (T) -> Boolean,
applyConsumer: (E) -> Unit
) {
collection
.filter { filterPredicate(it.data) }
.forEach { applyConsumer(it) }
}

inline fun <reified T> List<*>.isListOfType(): Boolean {
return all { it is T }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package ru.wearemad.mad_base.data_wrapper

interface DataWrapperInterface<T : Any?> {

var data: T
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ru.wearemad.mad_base.data_wrapper.checkable

import ru.wearemad.mad_base.data_wrapper.DataWrapperInterface

/**
* Интерфейс сущности, если объект может быть выделяемым
*/
interface CheckableDataInterface {

var isChecked: Boolean

fun toggleChecked() {
isChecked = !isChecked
}
}

/**
* Поддерживает множество выделений, через расширение коллекций
* Если необходимо одиночное выделение -> смотри [SelectableData]
*/
data class CheckableData<T : Any?>(
override var data: T,
override var isChecked: Boolean = false
) : DataWrapperInterface<T>,
CheckableDataInterface
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package ru.wearemad.mad_base.data_wrapper.checkable

import ru.wearemad.mad_base.data_wrapper.DataWrapperInterface
import ru.wearemad.mad_base.data_wrapper.filterAndApply

/**
* Extension-функции для коллекции, использующая [CheckableData]
* у [CheckableData] может быть несколько выделенных элементов
*
* Если необходимо одиночное выделение -> смотри [SelectableData]
*/

/**
* Изменить выделение для <T> на противоположный,
* удовлетворающее предикату
*
* @param (T) -> Boolean
*/
fun <T : Any?, E> Collection<E>.toggleChecked(predicate: (T) -> Boolean)
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
filterAndApply(this, { predicate(it) }, { it.toggleChecked() })
}

/**
* Поставить выделение для <T>, удовлетворающее предикату
*
* @param (T) -> Boolean
*/
fun <T : Any?, E> Collection<E>.setCheck(predicate: (T) -> Boolean)
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
filterAndApply(this, { predicate(it) }, { it.isChecked = true })
}

/**
* Убрать выделение для <T>, удовлетворающее предикату
*
* @param (T) -> Boolean
*/
fun <T : Any?, E> Collection<E>.setUncheck(predicate: (T) -> Boolean)
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
filterAndApply(this, { predicate(it) }, { it.isChecked = false })
}

/**
* Изменить выделение для <T> на противоположный
* @param T
*/
fun <T : Any?, E> Collection<E>.toggleChecked(value: T)
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
toggleChecked(predicate = { it == value })
}

/**
* Поставить выделение для <T>
* @param T
*/
fun <T : Any?, E> Collection<E>.setCheck(value: T)
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
setCheck(predicate = { it == value })
}

/**
* Убрать выделение для <T>
*/
fun <T : Any?, E> Collection<E>.setUncheck(value: T)
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
setUncheck(predicate = { it == value })
}

/**
* @return коллекции с выделенными
*/
fun <T : Any?, E> Collection<E>.getChecked(): Collection<T>
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
if (!isAnyChecked()) {
throw IllegalStateException("нет выделенных элементов")
}
return this.filter { it.isChecked }.map { it.data }
}

/**
* @return коллекции с выделенными. Если не найдено, то null
*/
fun <T : Any?, E> Collection<E>.getCheckedNullable(): Collection<T>?
where E : DataWrapperInterface<T>, E : CheckableDataInterface =
this.filter { it.isChecked }.map { it.data }

/**
* Выделить все
*/
fun <T : Any?, E> Collection<E>.setCheckedAll()
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
this.forEach {
it.apply {
this.isChecked = true
}
}
}

/**
* Убрать выделение у всех
*/
fun <T : Any?, E> Collection<E>.setUncheckedAll()
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
this.forEach {
it.apply {
this.isChecked = false
}
}
}

/**
* Поставить выделение для первого
*/
fun <T : Any?, E> Collection<E>.setCheckedFirst()
where E : DataWrapperInterface<T>, E : CheckableDataInterface {
this.first().apply { this.isChecked = true }
}

/**
* @return есть ли хотя бы один выделенный объект?
*/
fun <T : Any?, E> Collection<E>.isAnyChecked(): Boolean
where E : DataWrapperInterface<T>, E : CheckableDataInterface =
this.find { it.isChecked } != null
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ru.wearemad.mad_base.data_wrapper.selectable

import ru.wearemad.mad_base.data_wrapper.DataWrapperInterface

interface SelectableDataInterface {

var isSelected: Boolean

fun toggleSelected() {
isSelected = !isSelected
}
}

data class SelectableData<T : Any?>(
override var data: T,
override var isSelected: Boolean = false
) : DataWrapperInterface<T>,
SelectableDataInterface
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package ru.wearemad.mad_base.data_wrapper.selectable

import ru.wearemad.mad_base.data_wrapper.DataWrapperInterface

/**
* Extension-функции для коллекции, использующая [SelectableData]
* у [SelectableData] может быть выделен только один элемент
*
* Если необходимо множественное выделение -> смотри [CheckableData]
*/

/**
* Поставить выделение для <T>, используя предикат
*/
fun <T : Any?, E> Collection<E>.setSelected(predicate: (T) -> Boolean)
where E : DataWrapperInterface<T>, E : SelectableDataInterface {
setDeselected()
findSingleAndApply(this, { predicate(it) }, { it.isSelected = true })
}

/**
* Поставить выделение для <T>
*/
fun <T : Any?, E> Collection<E>.setSelected(value: T)
where E : DataWrapperInterface<T>, E : SelectableDataInterface {
setSelected(predicate = { it == value })
}

/**
* @return возвращает выделенный объект <T>.
*/
fun <T : Any?, E> Collection<E>.getSelectedData(): T
where E : DataWrapperInterface<T>, E : SelectableDataInterface {
return getSelectedDataNullable()
?: throw IllegalStateException("ни одного выделенного элемента")
}

/**
* @return возвращает выделенный объект <T>. Если ни один не выделен, то null
*/
fun <T : Any?, E> Collection<E>.getSelectedDataNullable(): T?
where E : DataWrapperInterface<T>, E : SelectableDataInterface {
val foundedItems = this.filter { it.isSelected }
if (foundedItems.size > 1) throw IllegalStateException("было найдено больше одного элемента")

return if (foundedItems.isEmpty()) {
null
} else {
foundedItems.first().data
}
}

/**
* убирает выделение у всей коллекции
*/
fun <T : Any?, E> Collection<E>.setDeselected()
where E : DataWrapperInterface<T>, E : SelectableDataInterface {
this.forEach {
it.apply {
this.isSelected = false
}
}
}

/**
* ставит выделение на первый элемент
*/
fun <T : Any?, E> Collection<E>.setSelectedFirst()
where E : DataWrapperInterface<T>, E : SelectableDataInterface {
setDeselected()
this.first().apply {
this.isSelected = true
}
}

/**
* @return есть ли выделенный объект?
*/
fun <T : Any?, E> Collection<E>.isAnySelected(): Boolean
where E : DataWrapperInterface<T>, E : SelectableDataInterface =
this.find { it.isSelected } != null

/**
* Найти объект (только один) в коллекции по findPredicate
* и изменить в соответствии с applyConsumer
*/
private fun <T : Any?, E : DataWrapperInterface<T>> findSingleAndApply(
collection: Collection<E>,
findPredicate: (T) -> Boolean,
applyConsumer: (E) -> Unit
) {
val foundedItems = collection.filter { findPredicate(it.data) }
if (foundedItems.size > 1) {
throw IllegalStateException("было найдено больше одного элемента")
} else {
foundedItems.forEach(applyConsumer)
}
}
Loading

0 comments on commit cae68b4

Please sign in to comment.