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

org.sfm.map.MappingContextFactoryBuilder Maven / Gradle / Ivy

Go to download

Java library to map flat record - ResultSet, csv - to java object with minimum configuration and low footprint.

There is a newer version: 1.10.3
Show newest version
package org.sfm.map;


import org.sfm.jdbc.impl.BreakDetector;
import org.sfm.reflect.Getter;
import org.sfm.reflect.meta.ListElementPropertyMeta;
import org.sfm.reflect.meta.PropertyMeta;
import org.sfm.utils.BooleanProvider;
import org.sfm.utils.ErrorHelper;
import org.sfm.utils.Predicate;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MappingContextFactoryBuilder {

    private final Counter counter;
    private final int currentIndex;
    private final MappingContextFactoryBuilder parent;
    private final List keys;
    private final KeySourceGetter keySourceGetter;
    private final List> children = new ArrayList>();
    private final PropertyMeta owner;

    public MappingContextFactoryBuilder(KeySourceGetter keySourceGetter) {
        this(new Counter(), new ArrayList(), keySourceGetter, null, null);
    }

    protected MappingContextFactoryBuilder(Counter counter, List keys, KeySourceGetter keySourceGetter, MappingContextFactoryBuilder parent, PropertyMeta owner) {
        this.counter = counter;
        this.currentIndex = counter.value;
        this.keys = keys;
        this.keySourceGetter = keySourceGetter;
        this.parent = parent;
        this.counter.value++;
        this.owner = owner;
    }

    public void addKey(K key) {
        if (!keys.contains(key)) {
            keys.add(key);
        }
    }

    public Predicate nullChecker() {
        return new NullChecker(keys, keySourceGetter);
    }

    public Getter, BooleanProvider> breakDetectorGetter() {
        if (isEmpty()) {
            if (parent != null) {
                return parent.breakDetectorGetter();
            } else {
                return new RootGetterProvider();
            }
        } else {
            return new BreakGetter(currentIndex);
        }
    }

    public MappingContextFactoryBuilder newBuilder(List subKeys, PropertyMeta owner) {
        MappingContextFactoryBuilder subBuilder = new MappingContextFactoryBuilder(counter, subKeys, keySourceGetter, this, owner);
        children.add(subBuilder);
        return subBuilder;
    }

    @SuppressWarnings("unchecked")
    public MappingContextFactory newFactory() {
        if (parent != null)  {
            throw new IllegalStateException();
        }

        List> builders = getAllBuilders();

        if (builders.isEmpty()) {
            return new MappingContextFactoryImpl(new BreakDetector[0], null);
        }

        @SuppressWarnings("unchecked")
        BreakDetector[] breakDetectors = new BreakDetector[builders.get(builders.size() -1).currentIndex + 1];

        BreakDetector rootDetector = null;
        for(int i = 0; i < builders.size(); i++) {
            final MappingContextFactoryBuilder builder = builders.get(i);
            BreakDetector parent = null;
            int parentIndex =  builder.getParentNonEmptyIndex();

            if (parentIndex != -1) {
                parent = breakDetectors[parentIndex];
            }
            final BreakDetector detector = builder.newBreakDetector(parent);
            breakDetectors[builder.currentIndex] = detector;
            if (builder.currentIndex == 0 || (rootDetector == null && builder.isRootEligible())) {
                rootDetector = detector;
            }
        }

        return new MappingContextFactoryImpl(breakDetectors, rootDetector);
    }

    private boolean isRootEligible() {
        return !(owner instanceof ListElementPropertyMeta) && (parent == null || parent.isRootEligible());
    }

    private int getParentNonEmptyIndex() {
        if (parent == null) {
            return -1;
        } else {
            if (parent.isEmpty()) {
                return parent.getParentNonEmptyIndex();
            } else {
                return parent.currentIndex;
            }
        }
    }

    private BreakDetector newBreakDetector(BreakDetector parent) {
        return new BreakDetectorImpl(keys, keySourceGetter, parent);
    }


    private List> getAllBuilders() {
        List> list = new ArrayList>();

        if (!isEmpty()) {
            list.add(this);
        }

        for(MappingContextFactoryBuilder child : children) {
            list.addAll(child.getAllBuilders());
        }

        return list;
    }

    public boolean isEmpty() {
        return keys.isEmpty();
    }

    public boolean hasNoDependentKeys() {
        if (!isEmpty()) {
            return false;
        }

        for(MappingContextFactoryBuilder builder : children) {
            if (!builder.hasNoDependentKeys()) {
                return false;
            }
        }
        return true;
    }

    public boolean isRoot() {
        return parent == null;
    }

    private static class Counter {
        int value;
    }

    private static class BreakGetter implements Getter, BooleanProvider> {
        private final int index;
        private BreakGetter(int index) {
            this.index = index;
        }

        @Override
        public BooleanProvider get(MappingContext target) throws Exception {
            return new MappingContextBooleanProvider(target, index);
        }
    }

    private static class RootGetterProvider implements Getter, BooleanProvider> {

        @Override
        public BooleanProvider get(MappingContext target) throws Exception {
            return new RootBooleanProvider(target);
        }
    }

    private static class RootBooleanProvider implements BooleanProvider {
        private final MappingContext target;

        public RootBooleanProvider(MappingContext target) {
            this.target = target;
        }

        @Override
        public boolean getBoolean() {
            return target == null || target.rootBroke();
        }
    }
    private static class MappingContextBooleanProvider implements BooleanProvider {
        private final MappingContext target;
        private final int index;

        public MappingContextBooleanProvider(MappingContext target, int index) {
            this.target = target;
            this.index = index;
        }

        @Override
        public boolean getBoolean() {
            return target == null || target.broke(index);
        }
    }

    private static class NullChecker implements Predicate {

        private final List keys;
        private final KeySourceGetter keySourceGetter;

        private NullChecker(List keys, KeySourceGetter keySourceGetter) {
            this.keys = keys;
            this.keySourceGetter = keySourceGetter;
        }

        @Override
        public boolean test(S s) {
            try {
                if (keys.isEmpty()) return false;
                for (int i = 0; i < keys.size(); i++) {
                    if (keySourceGetter.getValue(keys.get(i), s) != null) {
                        return false;
                    }
                }
                return true;
            } catch(Exception e) {
                ErrorHelper.rethrow(e);
                throw new IllegalStateException();
            }
        }
    }

    public static interface KeySourceGetter {
        public Object getValue(K key, S source) throws SQLException;
    }

    private static class MappingContextFactoryImpl implements MappingContextFactory {
        private final BreakDetector[] breakDetectors;
        private final BreakDetector rootDetector;
        public MappingContextFactoryImpl(BreakDetector[] breakDetectors, BreakDetector rootDetector) {
            this.breakDetectors = breakDetectors;
            this.rootDetector = rootDetector;
        }

        @Override
        public MappingContext newContext() {
            return new MappingContext(breakDetectors, rootDetector);
        }
    }

    private static class BreakDetectorImpl implements BreakDetector {
        private final KeySourceGetter keySourceGetter;
        private final List keys;
        private final BreakDetector parent;

        private Object[] lastValues;
        private boolean isBroken = true;

        public BreakDetectorImpl(List keys, KeySourceGetter keySourceGetter, BreakDetector parent) {
            this.keys = keys;
            this.keySourceGetter = keySourceGetter;
            this.parent = parent;
        }

        @Override
        public void handle(S source) throws MappingException {
            if (keys.isEmpty()) {
                return;
            }

            Object[] newValues = getValues(source);

            isBroken = (parent != null && parent.isBroken())
                    || lastValues == null
                    || !Arrays.equals(lastValues, newValues);

            lastValues = newValues;
        }

        private Object[] getValues(S source) {
            try {
                Object[] values = new Object[keys.size()];
                for (int i = 0; i < values.length; i++) {
                    values[i] = keySourceGetter.getValue(keys.get(i), source);
                }
                return values;
            } catch (Exception e) {
                return ErrorHelper.rethrow(e);
            }
        }

        @Override
        public boolean isBroken() {
            return isBroken;
        }

        @Override
        public void markAsBroken() {
            isBroken = true;
            lastValues = null;
        }
    }

    @Override
    public String toString() {
        return "MappingContextFactoryBuilder{" +
                "currentIndex=" + currentIndex +
                ", keys=" + keys +
                ", children=" + children +
                '}';
    }
}