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

ru.curs.celesta.score.AbstractView Maven / Gradle / Ivy

The newest version!
package ru.curs.celesta.score;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Base class for all view data elements.
 *
 * @author ioann
 * @since 2017-06-08
 */
public abstract class AbstractView extends DataGrainElement {

    static final Map, Function>> EXPR_CLASSES_AND_COLUMN_EXTRACTORS =
            new HashMap<>();

    static {
        EXPR_CLASSES_AND_COLUMN_EXTRACTORS.put(Count.class, (Expr frExpr) -> null);

        EXPR_CLASSES_AND_COLUMN_EXTRACTORS.put(FieldRef.class, (Expr frExpr) -> {
            FieldRef fr = (FieldRef) frExpr;
            return fr.getColumn();
        });
        EXPR_CLASSES_AND_COLUMN_EXTRACTORS.put(Sum.class, (Expr sumExpr) -> {
            Sum sum = (Sum) sumExpr;
            if (sum.term instanceof BinaryTermOp) {
                return null;
            }
            FieldRef fr = (FieldRef) sum.term;
            return fr.getColumn();
        });
    }

    private final List segments = new ArrayList<>();

    public AbstractView(GrainPart grainPart, String name) throws ParseException {
        super(grainPart, name);
    }

    public final List getSegments() {
        return segments;
    }

    abstract String viewType();

    /**
     * Writes SELECT script to the stream.
     *
     * @param bw  output stream
     * @param gen SQL generator (visitor)
     */
    public void selectScript(final PrintWriter bw, SQLGenerator gen) {
        BWWrapper bww = new BWWrapper();

        for (int i = 0; i < segments.size(); i++) {
            AbstractSelectStmt viewSegment = segments.get(i);
            if (i > 0) {
                bw.println(" union all ");
            }
            viewSegment.writeSelectPart(bw, gen, bww);
            viewSegment.writeFromPart(bw, gen);
            viewSegment.writeWherePart(bw, gen);
            viewSegment.writeGroupByPart(bw, gen);
        }
    }

    abstract AbstractSelectStmt newSelectStatement();

    final AbstractSelectStmt addSelectStatement() {
        AbstractSelectStmt result = newSelectStatement();
        segments.add(result);
        return result;
    }

    /**
     * Finalizes view parsing, resolving field references and checking expression types.
     *
     * @throws ParseException Error on types checking or reference resolving.
     */

    void finalizeParsing() throws ParseException {
        //This should never happen: at least one segment must be present
        //before this method is called
        if (segments.isEmpty()) {
            throw new IllegalStateException();
        }

        Expr[] first = segments.get(0).columns.values().toArray(new Expr[0]);
        for (int i = 1; i < segments.size(); i++) {
            Expr[] current = segments.get(i).columns.values().toArray(new Expr[0]);
            if (first.length != current.length) {
                throw new ParseException(
                        String.format("Each UNION query in %s '%s.%s' must have the same number of columns",
                                viewType(), getGrain().getName(), getName()));

            }
            for (int j = 0; j < first.length; j++) {
                try {
                    current[j].assertType(first[j].getMeta().getColumnType());
                    first[j].getMeta().setNullable(
                            first[j].getMeta().isNullable()
                                    | current[j].getMeta().isNullable());
                } catch (ParseException e) {
                    throw new ParseException(
                            String.format("UNION types in %s '%s.%s' must match. %s",
                                    viewType(), getGrain().getName(), getName(),
                                    e.getMessage()));
                }
            }
        }
    }

    /**
     * Returns a map of columns of the view.
     *
     * @return
     */
    public abstract Map> getColumns();


    /**
     * Returns column index by column name.
     */
    @Override
    public int getColumnIndex(String name) {
        int i = -1;
        for (String c : getColumns().keySet()) {
            i++;
            if (c.equals(name)) {
                return i;
            }
        }
        return i;
    }

    public final Map getAggregateColumns() {
        if (!segments.isEmpty()) {
            return segments.get(0).columns.entrySet().stream()
                    .filter(e -> e.getValue() instanceof Aggregate)
                    .collect(Collectors.toMap(
                            Map.Entry::getKey, Map.Entry::getValue,
                            (o, o2) -> {
                                throw new IllegalStateException(String.format("Duplicate key %s", o));
                            }, LinkedHashMap::new));
        } else {
            return Collections.emptyMap();
        }
    }

    /**
     * Returns column reference by column name.
     *
     * @param colName Column name.
     * @return
     */
    public Column getColumnRef(String colName) {
        if (!segments.isEmpty()) {
            Expr expr = segments.get(0).columns.get(colName);
            return EXPR_CLASSES_AND_COLUMN_EXTRACTORS.get(expr.getClass()).apply(expr);
        } else {
            return null;
        }
    }

    /**
     * Wrapper for automatic line-breaks.
     */
    static class BWWrapper {
        private static final int LINE_SIZE = 80;
        private static final String PADDING = "    ";
        private int l = 0;

        void append(String s, PrintWriter bw) {
            bw.write(s);
            l += s.length();
            if (l >= LINE_SIZE) {
                bw.println();
                bw.write(PADDING);
                l = PADDING.length();
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy