All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fuzzycsv.Record.groovy Maven / Gradle / Ivy

Go to download

A groovy/java tabular Data (from CSV,SQL,JSON) processing library that supports fuzzy column matching,tranformations/merging/querying etc

There is a newer version: 1.9.1-groovy4
Show newest version
package fuzzycsv

import groovy.transform.CompileStatic

import static fuzzycsv.ResolutionStrategy.*

@CompileStatic
class Record {

    List finalHeaders
    List finalRecord
    FuzzyCSVTable finalTable

    List leftHeaders
    List leftRecord
    FuzzyCSVTable leftTable

    List rightHeaders
    List rightRecord
    FuzzyCSVTable rightTable

    boolean useFuzzy = false
    boolean throwExceptionOnNullColumn = true
    private boolean useDefaultSilentMode = true

    int recordIdx = -1

    ResolutionStrategy resolutionStrategy = FINAL_FIRST

    Record() {}

    Record(List headers, List record) {
        setFinalRecord(record)
        setFinalHeaders(headers)
    }


    def left(String name) {
        return resolveValue(leftHeaders, leftRecord, name)
    }

    def right(String name) {
        return resolveValue(rightHeaders, rightRecord, name)
    }

    def 'final'(String name) {
        return resolveValue(finalHeaders, finalRecord, name)
    }

    //convenience method for left
    def l(String name) {
        return left(name)
    }

    //convenience method for right
    def r(String name) {
        return right(name)
    }

    //convenience method for final
    def f(String name) {
        return 'final'(name)
    }

    FuzzyCSVTable getFinalCsv() {
        return finalTable
    }

    Record setFinalCsv(List finalCsv) {
        this.finalTable = new FuzzyCSVTable(finalCsv)
        return this
    }

    FuzzyCSVTable getLeftCsv() {
        return leftTable
    }

    Record setLeftCsv(List leftCsv) {
        this.leftTable = new FuzzyCSVTable(leftCsv)
        return this
    }

    FuzzyCSVTable getRightCsv() {
        return rightTable
    }

    Record setRightCsv(List rightCsv) {
        this.rightTable = new FuzzyCSVTable(rightCsv)
        return this
    }

    Record up() {
        if (isTop()) {
            return getRecord(finalHeaders, Collections.emptyList(), finalCsv.csv, 1)
        } else {
            return finalTable.row(idx() - 1)
        }
    }

    Record down() {
        def idx = idx()
        if (isBottom()) {
            return getRecord(finalHeaders, Collections.emptyList(), finalCsv.csv, idx)
        } else {
            return finalTable.row(idx + 1)
        }
    }

    boolean isTop() {
        return idx() == 1
    }

    boolean isBottom() {
        return idx() == finalCsv.size()
    }


    private def resolveValue(List headers, List values, String name, boolean throwException = true) {
        Integer nameIndex = headers?.indexOf(name)

        if (nameIndex && nameIndex == -1) {
            if (throwException) throwColumnNotFound(name)
            return null
        }


        if (!values || nameIndex >= values.size()) {
            return null
        }

        return values.get(nameIndex)
    }


    @SuppressWarnings("GrMethodMayBeStatic")
    private def toNegOne(d) {
        d == null ? -1 : d
    }

    def propertyMissing(String name) {

        def origName = name
        def ourResolveStrategy = resolutionStrategy
        if (name?.startsWith('@')) {
            name = name.replaceFirst('@', '')
            ourResolveStrategy = LEFT_FIRST
        }


        def lIdx = toNegOne leftHeaders?.indexOf(name)
        def finalIdx = toNegOne finalHeaders?.indexOf(name)
        def rIdx = toNegOne rightHeaders?.indexOf(name)

        if (lIdx == -1 && finalIdx == -1 && rIdx == -1) {
            if (shouldThrowException())
                throwColumnNotFound(origName)
            else
                return null
        }

        def value
        switch (ourResolveStrategy) {
            case SOURCE_FIRST:
            case LEFT_FIRST:
                value = tryLeftFinalRight(name)
                break
            case RIGHT_FIRST:
                value = tryRightLeftFinal(name)
                break
            default://covers also derived
                value = tryFinalRightLeft(name)
        }

        return value
    }


    def tryLeftFinalRight(String name) {
        def value = tryLeft(name)
        if (value != null) return value

        value = tryFinal(name)
        if (value != null) return value

        return tryRight(name)
    }

    def tryFinalRightLeft(String name) {
        def value = tryFinal(name)
        if (value != null) return value

        value = tryRight(name)
        if (value != null) return value

        return tryLeft(name)
    }

    def tryRightLeftFinal(String name) {
        def value = tryRight(name)
        if (value != null) return value

        value = tryLeft(name)
        if (value != null) return value

        return tryFinal(name)
    }


    private boolean shouldThrowException() {
        if (useDefaultSilentMode) {
            return FuzzyCSV.THROW_EXCEPTION_ON_ABSENT_COLUMN.get()
        } else {
            return throwExceptionOnNullColumn
        }
    }

    Record silentModeOn() {
        throwExceptionOnNullColumn = false
        useDefaultSilentMode = false
        return this
    }

    Record silentModeOff() {
        throwExceptionOnNullColumn = true
        useDefaultSilentMode = false
        return this
    }

    Record silentModeDefault() {
        useDefaultSilentMode = true
        return this
    }


    private def tryRight(String name) { resolveValue(rightHeaders, rightRecord, name, false) }

    private def tryLeft(String name) { resolveValue(leftHeaders, leftRecord, name, false) }

    private def tryFinal(String name) { resolveValue(finalHeaders, finalRecord, name, false) }


    @SuppressWarnings("GrMethodMayBeStatic")
    private def throwColumnNotFound(String name) {
        throw new IllegalArgumentException("Record ${idx()} should have a column[$name]")

    }


    def getAt(int idx, ResolutionStrategy resolutionStrategy1 = resolutionStrategy) {
        switch (resolutionStrategy1) {
            case FINAL_FIRST:
            case DERIVED_FIRST:
                def get = FuzzyCSVUtils.safeGet(finalRecord, idx)
                if (get != null) return get
                get = FuzzyCSVUtils.safeGet(rightRecord, idx)
                if (get != null) return get
                return FuzzyCSVUtils.safeGet(leftRecord, idx)


            case LEFT_FIRST:
            case SOURCE_FIRST:
                def get = FuzzyCSVUtils.safeGet(leftRecord, idx)
                if (get != null) return get
                get = FuzzyCSVUtils.safeGet(finalRecord, idx)
                if (get != null) return get
                return FuzzyCSVUtils.safeGet(rightRecord, idx)

            case RIGHT_FIRST:
                def get = FuzzyCSVUtils.safeGet(rightRecord, idx)
                if (get != null) return get
                get = FuzzyCSVUtils.safeGet(leftRecord, idx)
                if (get != null) return get
                return FuzzyCSVUtils.safeGet(finalRecord, idx)

        }
    }


    def getAt(CharSequence name) { propertyMissing(name as String) }

    def getAt(def name) { throw new UnsupportedOperationException("object column names not supported. $name") }

    Record setAt(String name, def value) {
        def position = Fuzzy.findPosition(finalHeaders, name)
        if (position == -1) {
            throwColumnNotFound(name)
        }
        finalRecord.set(position, value)
        return this
    }

    Record set(String name, def value) {
        return setAt(name, value)
    }

    boolean isHeader() { recordIdx == 0 }

    Map toMap(String... headers) {
        return toMap(headers as List)
    }

    Map toMap(List headers) {
        if (!headers) headers = finalHeaders
        headers.collectEntries { [it, val(it)] }
    }

    def propertyMissing(String name, def arg) {
        set(name, arg)
    }

    int idx() {
        return recordIdx
    }

    static Record getRecord(List csv, int i) {
        def header = csv[0]
        def record = csv[i]
        getRecord(header, record, csv, i)
    }

    static Record getRecord(List header, List record, List csv) {
        return getRecord(header, record, csv, -1)
    }

    static Record getRecord(List header, List record, List csv, int idx) {
        Record record1 = new Record(header, record)
        record1.recordIdx = idx
        record1.setFinalCsv(csv)
        return record1
    }


    def value(String name, boolean required = true, def defaultValue = null) {

        assertHeaderNameExists(name)

        //todo bug casting to string
        def value = propertyMissing(name)?.toString()?.trim()
        if (required && value == null) {
            if (defaultValue) return defaultValue
            throw new IllegalStateException("Record [${idx()}] has an Empty Cell[$name] that is Required")
        }
        return value
    }

    private void assertHeaderNameExists(String name) {
        def nameInHeader = finalHeaders?.contains(name) || leftHeaders?.contains(name)
        if (!nameInHeader) throwColumnNotFound("Record ${idx()} should have a [$name]")
    }


    def val(def col) {
        if (col instanceof RecordFx) {
            col.getValue(this)
        } else {
            propertyMissing(col as String)
        }
    }

    //todo add a try finally block
    def withSilentMode(Closure c) {
        silentModeOn()
        c.delegate = this
        def value = c.call()
        silentModeDefault()
        return value
    }

    def silentVal(def c) {
        silentModeOn()
        def value = val(c)
        silentModeDefault()
        return value
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy