com.github.helenusdriver.driver.impl.DeleteImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.github.helenusdriver.driver.impl.DeleteImpl.java

Source

/*
 * Copyright (C) 2015-2015 The Helenus Driver Project Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.helenusdriver.driver.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import com.github.helenusdriver.commons.collections.iterators.CombinationIterator;
import com.github.helenusdriver.driver.Clause;
import com.github.helenusdriver.driver.ColumnPersistenceException;
import com.github.helenusdriver.driver.Delete;
import com.github.helenusdriver.driver.StatementBridge;
import com.github.helenusdriver.driver.Using;
import com.github.helenusdriver.driver.VoidFuture;

/**
 * The <code>DeleteImpl</code> class extends the functionality of Cassandra's
 * {@link com.datastax.driver.core.querybuilder.Delete} class to provide
 * support for POJOs.
 *
 * @copyright 2015-2015 The Helenus Driver Project Authors
 *
 * @author  The Helenus Driver Project Authors
 * @version 1 - Jan 19, 2015 - paouelle - Creation
 *
 * @param <T> The type of POJO associated with this statement.
 *
 * @since 1.0
 */
public class DeleteImpl<T> extends StatementImpl<Void, VoidFuture, T> implements Delete<T> {
    /**
     * List of tables to delete from.
     *
     * @author paouelle
     */
    private final List<TableInfoImpl<T>> tables = new ArrayList<>(8);

    /**
     * List of columns deleted.
     *
     * @author paouelle
     */
    protected List<Object> columnNames;

    /**
     * Holds the where statement part.
     *
     * @author paouelle
     */
    private final WhereImpl<T> where;

    /**
     * Holds the "USING" options.
     *
     * @author paouelle
     */
    private final OptionsImpl<T> usings;

    /**
     * Flag to keep track of whether or not the special* has been selected.
     *
     * @author paouelle
     */
    protected boolean allSelected = false;

    /**
     * Holds a map of primary key values to use instead of those reported by the
     * POJO context.
     *
     * @author paouelle
     */
    private final Map<String, Object> pkeys_override;

    /**
     * Instantiates a new <code>DeleteImpl</code> object.
     *
     * @author paouelle
     *
     * @param  context the non-<code>null</code> class info context associated
     *         with this statement
     * @param  tables the tables to delete from
     * @param  columnNames the columns names that should be deleted by the query
     * @param  allSelected <code>true</code> if the special COUNT() or *
     *         has been selected
     * @param  mgr the non-<code>null</code> statement manager
     * @param  bridge the non-<code>null</code> statement bridge
     * @throws NullPointerException if <code>context</code> is <code>null</code>
     * @throws IllegalArgumentException if unable to compute the keyspace or table
     *         names based from the given object
     */
    public DeleteImpl(ClassInfoImpl<T>.Context context, String[] tables, List<Object> columnNames,
            boolean allSelected, StatementManagerImpl mgr, StatementBridge bridge) {
        this(context, tables, columnNames, allSelected, null, mgr, bridge);
    }

    /**
     * Instantiates a new <code>DeleteImpl</code> object.
     *
     * @author paouelle
     *
     * @param  context the non-<code>null</code> class info context associated
     *         with this statement
     * @param  tables the tables to delete from
     * @param  columnNames the columns names that should be deleted by the query
     * @param  allSelected <code>true</code> if the special COUNT() or *
     *         has been selected
     * @param  pkeys_override an optional map of primary key values to use instead
     *         of those provided by the POJO context
     * @param  mgr the non-<code>null</code> statement manager
     * @param  bridge the non-<code>null</code> statement bridge
     * @throws NullPointerException if <code>context</code> is <code>null</code>
     * @throws IllegalArgumentException if unable to compute the keyspace or table
     *         names based from the given object
     */
    DeleteImpl(ClassInfoImpl<T>.Context context, String[] tables, List<Object> columnNames, boolean allSelected,
            Map<String, Object> pkeys_override, StatementManagerImpl mgr, StatementBridge bridge) {
        super(Void.class, context, mgr, bridge);
        if (tables != null) {
            for (final String table : tables) {
                if (table != null) {
                    this.tables.add((TableInfoImpl<T>) context.getClassInfo().getTable(table));
                }
            }
        } else { // fallback to all
            this.tables.addAll(context.getClassInfo().getTables());
        }
        this.columnNames = columnNames;
        this.allSelected = allSelected;
        org.apache.commons.lang3.Validate.isTrue(!(!allSelected && CollectionUtils.isEmpty(columnNames)),
                "must select at least one column");
        this.where = new WhereImpl<>(this);
        this.usings = new OptionsImpl<>(this);
        this.pkeys_override = pkeys_override;
    }

    /**
     * Ands the specified clause along with others while validating them.
     * <p>
     * <i>Note:</i> Clauses referencing columns that are not defined in the
     * specified table are simply ignored.
     *
     * @author paouelle
     *
     * @param  table the non-<code>null</code> table for which to validate or
     *         extract column's values for
     * @param  clauses the non-<code>null</code> map of clauses to which
     *         to and the clause keyed by the column name
     * @param  clause the non-<code>null</code> assignment to and together
     *         with others
     * @throws IllegalArgumentException if clauses reference columns which are
     *         not primary keys or index columns in the POJO for the specified
     *         table or reference invalid values
     */
    private void andClause(TableInfoImpl<T> table, Map<String, ClauseImpl> clauses, ClauseImpl clause) {
        // checked if the assignment is a delayed one in which case we need to
        // process it with the POJO and continue with the resulting list of
        // assignments instead of it
        if (clause instanceof ClauseImpl.Delayed) {
            for (final ClauseImpl c : ((ClauseImpl.Delayed) clause).processWith(table)) {
                andClause(table, clauses, c); // recurse to add the processed clause
            }
        } else if (clause instanceof ClauseImpl.DelayedWithObject) {
            final ClassInfoImpl<T>.POJOContext pctx = getPOJOContext();

            org.apache.commons.lang3.Validate.isTrue(pctx != null, "unsupported clause '%s' for a DELETE statement",
                    clause.getOperation());
            for (final ClauseImpl c : ((ClauseImpl.DelayedWithObject) clause).processWith(table, pctx)) {
                andClause(table, clauses, c); // recurse to add the processed clause
            }
        } else {
            // only pay attention if the referenced column name is defined in the table
            // as a primary key
            if (table.hasPrimaryKey(clause.getColumnName())) {
                clause.validate(table);
                if (clause instanceof ClauseImpl.InClauseImpl) {
                    // the IN relation is only supported for the last column of the partition key
                    // or if it is for a multi-key
                    org.apache.commons.lang3.Validate.isTrue(
                            table.isLastPartitionKey(clause.getColumnName())
                                    || table.isMultiKey(clause.getColumnName()),
                            "'IN' clause is only supported for the last column of the partition key "
                                    + "or for a multi-key in a DELETE statement: %s",
                            clause.getColumnName());
                }
                clauses.put(clause.getColumnName().toString(), clause);
            }
        }
    }

    /**
     * Builds a query string for the specified table.
     *
     * @author paouelle
     *
     * @param  table the non-<code>null</code> table for which to build a query
     *         string
     * @param  builders the non-<code>null</code> list of builders where to add
     *         the query strings built
     * @throws IllegalArgumentException if the keyspace has not yet been computed
     *         and cannot be computed with the provided suffixes yet or if
     *         assignments reference columns not defined in the POJO or invalid
     *         values or if missing mandatory columns are referenced for the
     *         specified table
     * @throws ColumnPersistenceException if unable to persist a column's value
     */
    @SuppressWarnings("synthetic-access")
    void buildQueryString(TableInfoImpl<T> table, List<StringBuilder> builders) {
        final StringBuilder builder = new StringBuilder();

        builder.append("DELETE ");
        if (columnNames != null) {
            // only pay attention if the referenced column name is defined in the table
            final List<Object> cns = new ArrayList<>(this.columnNames.size());

            for (final Object columnName : this.columnNames) {
                if (table.hasColumn(columnName)) {
                    // make sure it is not mandatory or a primary key column
                    table.validateNotMandatoryOrPrimaryKeyColumn(columnName);
                    cns.add(columnName);
                }
            }
            if (cns.isEmpty()) {
                return;
            }
            Utils.joinAndAppendNames(builder, ",", cns);
            builder.append(" ");
        }
        builder.append("FROM ");
        if (getKeyspace() != null) {
            Utils.appendName(getKeyspace(), builder).append(".");
        }
        Utils.appendName(table.getName(), builder);
        if (!usings.usings.isEmpty()) {
            builder.append(" USING ");
            Utils.joinAndAppend(table, builder, " AND ", usings.usings);
        }
        final Collection<FieldInfoImpl<T>> multiKeys = table.getMultiKeys();

        if (!where.clauses.isEmpty()) {
            // let's first preprocess and validate the clauses for this table
            final List<ClauseImpl> whereClauses = where.getClauses(table);
            final Map<String, ClauseImpl> cs = new LinkedHashMap<>(whereClauses.size()); // preserver order

            for (final ClauseImpl c : whereClauses) {
                andClause(table, cs, c);
            }
            if (cs.isEmpty()) { // nothing to select for this update so skip
                return;
            }
            builder.append(" WHERE ");
            if (!multiKeys.isEmpty()) {
                // prepare all sets of values for all multi-keys present in the clause
                final List<Collection<ClauseImpl.EqClauseImpl>> sets = new ArrayList<>(multiKeys.size());

                for (final FieldInfoImpl<T> finfo : multiKeys) {
                    final ClauseImpl mkc = cs.remove(finfo.getColumnName());

                    if (mkc != null) { // we have a clause for this multi-key column
                        final List<ClauseImpl.EqClauseImpl> set = new ArrayList<>();

                        for (final Object v : mkc.values()) {
                            if (v instanceof Set<?>) {
                                for (final Object sv : (Set<?>) v) {
                                    set.add(new ClauseImpl.EqClauseImpl(
                                            StatementImpl.MK_PREFIX + finfo.getColumnName(), sv));
                                }
                            } else {
                                set.add(new ClauseImpl.EqClauseImpl(StatementImpl.MK_PREFIX + finfo.getColumnName(),
                                        v));
                            }
                        }
                        if (!set.isEmpty()) {
                            sets.add(set);
                        }
                    }
                }
                if (!sets.isEmpty()) {
                    // now iterate all combination of these sets to generate delete statements
                    // for each combination
                    @SuppressWarnings("unchecked")
                    final Collection<ClauseImpl.EqClauseImpl>[] asets = new Collection[sets.size()];

                    for (final Iterator<List<ClauseImpl.EqClauseImpl>> i = new CombinationIterator<>(
                            ClauseImpl.EqClauseImpl.class, sets.toArray(asets)); i.hasNext();) {
                        // add the multi-key clause values from this combination to the map of clauses
                        for (final ClauseImpl.EqClauseImpl kc : i.next()) {
                            cs.put(kc.getColumnName().toString(), kc);
                        }
                        final StringBuilder sb = new StringBuilder(builder);

                        Utils.joinAndAppend(table, sb, " AND ", cs.values());
                        builders.add(sb);
                    }
                    return;
                }
            }
            // we didn't have any multi-keys in the clauses so just delete it based
            // on the given clause
            Utils.joinAndAppend(table, builder, " AND ", cs.values());
        } else {
            // add where clauses for all primary key columns
            final Map<String, Object> pkeys = getPOJOContext().getPrimaryKeyColumnValues(table.getName());

            if (pkeys_override != null) {
                pkeys.putAll(pkeys_override);
            }
            if (!pkeys.isEmpty()) {
                builder.append(" WHERE ");
                if (!multiKeys.isEmpty()) {
                    // prepare all sets of values for all multi-keys present in the clause
                    final List<String> cnames = new ArrayList<>(multiKeys.size());
                    final List<Collection<Object>> sets = new ArrayList<>(multiKeys.size());

                    for (final FieldInfoImpl<T> finfo : multiKeys) {
                        @SuppressWarnings("unchecked")
                        final Set<Object> set = (Set<Object>) pkeys.remove(finfo.getColumnName());

                        if (set != null) { // we have keys for this multi-key column
                            cnames.add(finfo.getColumnName());
                            sets.add(set);
                        }
                    }
                    if (!sets.isEmpty()) {
                        // now iterate all combination of these sets to generate delete statements
                        // for each combination
                        @SuppressWarnings("unchecked")
                        final Collection<Object>[] asets = new Collection[sets.size()];

                        for (final Iterator<List<Object>> i = new CombinationIterator<>(Object.class,
                                sets.toArray(asets)); i.hasNext();) {
                            // add the multi-key clause values from this combination to the map of primary keys
                            int j = -1;
                            for (final Object k : i.next()) {
                                pkeys.put(StatementImpl.MK_PREFIX + cnames.get(++j), k);
                            }
                            final StringBuilder sb = new StringBuilder(builder);

                            Utils.joinAndAppendNamesAndValues(sb, " AND ", "=", pkeys);
                            builders.add(sb);
                        }
                        return;
                    }
                }
                // we didn't have any multi-keys in the list (unlikely) so just delete it
                // based on the provided list
                Utils.joinAndAppendNamesAndValues(builder, " AND ", "=", pkeys);
            }
        }
        builders.add(builder);
    }

    /**
     * {@inheritDoc}
     *
     * @author paouelle
     *
     * @see com.github.helenusdriver.driver.impl.StatementImpl#buildQueryStrings()
     */
    @Override
    protected StringBuilder[] buildQueryStrings() {
        final List<StringBuilder> builders = new ArrayList<>(tables.size());

        for (final TableInfoImpl<T> table : tables) {
            buildQueryString(table, builders);
        }
        if (builders.isEmpty()) {
            return null;
        }
        return builders.toArray(new StringBuilder[builders.size()]);
    }

    /**
     * {@inheritDoc}
     *
     * @author paouelle
     *
     * @see com.github.helenusdriver.driver.impl.StatementImpl#appendGroupType(java.lang.StringBuilder)
     */
    @Override
    protected void appendGroupType(StringBuilder builder) {
        builder.append("BATCH");
    }

    /**
     * {@inheritDoc}
     *
     * @author paouelle
     *
     * @see com.github.helenusdriver.driver.Delete#where(com.github.helenusdriver.driver.Clause)
     */
    @Override
    public Where<T> where(Clause clause) {
        return where.and(clause);
    }

    /**
     * {@inheritDoc}
     *
     * @author paouelle
     *
     * @see com.github.helenusdriver.driver.Delete#where()
     */
    @Override
    public Where<T> where() {
        return where;
    }

    /**
     * {@inheritDoc}
     *
     * @author paouelle
     *
     * @see com.github.helenusdriver.driver.Delete#using(com.github.helenusdriver.driver.Using)
     */
    @Override
    public Options<T> using(Using using) {
        return usings.and(using);
    }

    /**
     * The <code>WhereImpl</code> class defines WHERE clause for a DELETE
     * statement.
     *
     * @copyright 2015-2015 The Helenus Driver Project Authors
     *
     * @author  The Helenus Driver Project Authors
     * @version 1 - Jan 19, 2015 - paouelle - Creation
     *
     * @param <T> The type of POJO associated with the statement.
     *
     * @since 1.0
     */
    public static class WhereImpl<T> extends ForwardingStatementImpl<Void, VoidFuture, T, DeleteImpl<T>>
            implements Where<T> {
        /**
         * Holds the list of clauses for this statement
         *
         * @author paouelle
         */
        private final List<ClauseImpl> clauses = new ArrayList<>(10);

        /**
         * Instantiates a new <code>WhereImpl</code> object.
         *
         * @author paouelle
         *
         * @param statement the non-<code>null</code> statement to which this
         *        "WHERE" part belongs to
         */
        WhereImpl(DeleteImpl<T> statement) {
            super(statement);
        }

        /**
         * Gets the "where" clauses while adding missing final partition keys.
         *
         * @author paouelle
         *
         * @param  table the table for which to get the "where" clauses
         * @return the "where" clauses
         */
        private List<ClauseImpl> getClauses(TableInfoImpl<T> table) {
            // check if the table defines any final primary keys
            // in which case we want to make sure to add clauses for them too
            final List<ClauseImpl> clauses = new ArrayList<>(this.clauses);

            for (final Map.Entry<String, Object> e : table.getFinalPrimaryKeyValues().entrySet()) {
                final String name = e.getKey();

                // check if we already have a clause for that column
                boolean found = false;

                for (final ClauseImpl c : this.clauses) {
                    if (name.equals(c.getColumnName().toString())) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    clauses.add(new ClauseImpl.EqClauseImpl(name, e.getValue()));
                }
            }
            return clauses;
        }

        /**
         * {@inheritDoc}
         *
         * @author paouelle
         *
         * @see com.github.helenusdriver.driver.Delete.Where#and(com.github.helenusdriver.driver.Clause)
         */
        @Override
        public Where<T> and(Clause clause) {
            org.apache.commons.lang3.Validate.notNull(clause, "invalid null clause");
            org.apache.commons.lang3.Validate.isTrue(clause instanceof ClauseImpl,
                    "unsupported class of clauses: %s", clause.getClass().getName());
            final ClauseImpl c = (ClauseImpl) clause;

            if (!(c instanceof ClauseImpl.Delayed) && !(c instanceof ClauseImpl.DelayedWithObject)) {
                // pre-validate against any table
                if (c instanceof Clause.Equality) {
                    getContext().getClassInfo().validateColumnOrSuffix(c.getColumnName().toString());
                    if (statement.getContext().getClassInfo().isSuffixKey(c.getColumnName().toString())) {
                        statement.getContext().addSuffix(c.getColumnName().toString(), c.firstValue());
                    }
                } else {
                    getContext().getClassInfo().validateColumn(c.getColumnName().toString());
                }
            }
            clauses.add(c);
            setDirty();
            return this;
        }

        /**
         * Adds an option to the DELETE statement this WHERE clause is part of.
         *
         * @author paouelle
         *
         * @param  using the using clause to add.
         * @return the options of the DELETE statement this WHERE clause is part of.
         */
        @Override
        public Options<T> using(Using using) {
            return statement.using(using);
        }
    }

    /**
     * The <code>OptionsImpl</code> class defines the options of a DELETE statement.
     *
     * @copyright 2015-2015 The Helenus Driver Project Authors
     *
     * @author  The Helenus Driver Project Authors
     * @version 1 - Jan 19, 2015 - paouelle - Creation
     *
     * @param <T> The type of POJO associated with the statement.
     *
     * @since 1.0
     */
    public static class OptionsImpl<T> extends ForwardingStatementImpl<Void, VoidFuture, T, DeleteImpl<T>>
            implements Options<T> {
        /**
         * Holds the list of "USINGS" options.
         *
         * @author paouelle
         */
        private final List<UsingImpl> usings = new ArrayList<>(5);

        /**
         * Instantiates a new <code>OptionsImpl</code> object.
         *
         * @author paouelle
         *
         * @param statement the non-<code>null</code> statement for which we are
         *        creating options
         */
        OptionsImpl(DeleteImpl<T> statement) {
            super(statement);
        }

        /**
         * {@inheritDoc}
         *
         * @author paouelle
         *
         * @see com.github.helenusdriver.driver.Delete.Options#and(com.github.helenusdriver.driver.Using)
         */
        @Override
        public Options<T> and(Using using) {
            org.apache.commons.lang3.Validate.notNull(using, "invalid null using");
            org.apache.commons.lang3.Validate.isTrue(using instanceof UsingImpl, "unsupported class of usings: %s",
                    using.getClass().getName());
            usings.add((UsingImpl) using);
            setDirty();
            return this;
        }

        /**
         * {@inheritDoc}
         *
         * @author paouelle
         *
         * @see com.github.helenusdriver.driver.Delete.Options#where(com.github.helenusdriver.driver.Clause)
         */
        @Override
        public Where<T> where(Clause clause) {
            return statement.where(clause);
        }
    }

    /**
     * The <code>BuilderImpl</code> class defines an in-construction DELETE statement.
     *
     * @copyright 2015-2015 The Helenus Driver Project Authors
     *
     * @author  The Helenus Driver Project Authors
     * @version 1 - Jan 19, 2015 - paouelle - Creation
     *
     * @param <T> The type of POJO associated with the statement.
     *
     * @since 1.0
     */
    public static class BuilderImpl<T> implements Builder<T> {
        /**
         * Holds the statement manager.
         *
         * @author paouelle
         */
        protected final StatementManagerImpl mgr;

        /**
         * Holds the statement bridge.
         *
         * @author paouelle
         */
        protected final StatementBridge bridge;

        /**
         * Holds the context associated with the POJO class for this statement.
         *
         * @author paouelle
         */
        protected final ClassInfoImpl<T>.Context context;

        /**
         * List of columns added.
         *
         * @author paouelle
         */
        protected List<Object> columnNames;

        /**
         * Flag to keep track of whether or not * has been selected.
         *
         * @author paouelle
         */
        protected boolean allSelected = false;

        /**
         * Instantiates a new <code>BuilderImpl</code> object.
         *
         * @author paouelle
         *
         * @param context the non-<code>null</code> class info context
         *        associated with this statement
         * @param mgr the non-<code>null</code> statement manager
         * @param bridge the non-<code>null</code> statement bridge
         */
        BuilderImpl(ClassInfoImpl<T>.Context context, StatementManagerImpl mgr, StatementBridge bridge) {
            this.mgr = mgr;
            this.bridge = bridge;
            this.context = context;
        }

        /**
         * Instantiates a new <code>BuilderImpl</code> object.
         *
         * @author paouelle
         *
         * @param  context the non-<code>null</code> class info context
         *         associated with this statement
         * @param  columnNames the column names
         * @param  mgr the non-<code>null</code> statement manager
         * @param  bridge the non-<code>null</code> statement bridge
         * @throws NullPointerException if <code>context</code> is <code>null</code>
         * @throws IllegalArgumentException if any of the specified columns are not
         *         defined by the POJO
         */
        BuilderImpl(ClassInfoImpl<T>.Context context, List<Object> columnNames, StatementManagerImpl mgr,
                StatementBridge bridge) {
            this(context, mgr, bridge);
            context.getClassInfo().validateColumns(columnNames);
            this.columnNames = columnNames;
        }

        /**
         * {@inheritDoc}
         *
         * @author paouelle
         *
         * @see com.github.helenusdriver.driver.Delete.Builder#from(java.lang.String[])
         */
        @Override
        public Delete<T> from(String... tables) {
            if (columnNames == null) {
                allSelected = true;
            }
            return new DeleteImpl<>(context, tables, columnNames, allSelected, mgr, bridge);
        }

        /**
         * Specifies to delete from all tables defined in the POJO using the
         * keyspace defined in the POJO.
         * <p>
         * This flavor should be used when the POJO doesn't require suffixes to the
         * keyspace name.
         *
         * @author paouelle
         *
         * @return a newly build DELETE statement that deletes from all tables
         *         defined in the POJO
         * @throws IllegalArgumentException if unable to compute the keyspace name
         *         without any suffixes or any of the referenced columns are not
         *         defined by the POJO
         */
        @Override
        public Delete<T> fromAll() {
            if (columnNames == null) {
                allSelected = true;
            }
            return new DeleteImpl<>(context, null, columnNames, allSelected, mgr, bridge);
        }
    }

    /**
     * The <code>SelectionImpl</code> class defines a selection clause for an
     * in-construction DELETE statement.
     *
     * @copyright 2015-2015 The Helenus Driver Project Authors
     *
     * @author  The Helenus Driver Project Authors
     * @version 1 - Jan 19, 2015 - paouelle - Creation
     *
     * @param <T> The type of POJO associated with the response from this statement.
     *
     * @since 1.0
     */
    public static class SelectionImpl<T> extends BuilderImpl<T> implements Selection<T> {
        /**
         * Instantiates a new <code>SelectionImpl</code> object.
         *
         * @author paouelle
         *
         * @param context the non-<code>null</code> class info context
         *        associated with this statement
         * @param mgr the non-<code>null</code> statement manager
         * @param bridge the non-<code>null</code> statement bridge
         */
        SelectionImpl(ClassInfoImpl<T>.Context context, StatementManagerImpl mgr, StatementBridge bridge) {
            super(context, mgr, bridge);
        }

        /**
         * Adds the specified column name.
         *
         * @author paouelle
         *
         * @param  name the non-<code>null</code> column name to be added
         * @return this for chaining
         * @throws NullPointerException if <code>name</code> is <code>null</code>
         * @throws IllegalArgumentException if the specified column is not defined
         *         by the POJO
         */
        private Selection<T> addName(Object name) {
            context.getClassInfo().validateColumn(name);
            if (columnNames == null) {
                super.columnNames = new ArrayList<>(25);
            }
            columnNames.add(name);
            return this;
        }

        /**
         * {@inheritDoc}
         *
         * @author paouelle
         *
         * @see com.github.helenusdriver.driver.Delete.Selection#all()
         */
        @Override
        public Builder<T> all() {
            org.apache.commons.lang3.Validate.validState(columnNames == null,
                    "some columns (%s) have already been selected", StringUtils.join(columnNames, ", "));
            super.allSelected = true;
            return this;
        }

        /**
         * {@inheritDoc}
         *
         * @author paouelle
         *
         * @see com.github.helenusdriver.driver.Delete.Selection#column(java.lang.String)
         */
        @Override
        public Selection<T> column(String name) {
            return addName(name);
        }

        /**
         * {@inheritDoc}
         *
         * @author paouelle
         *
         * @see com.github.helenusdriver.driver.Delete.Selection#listElt(java.lang.String, int)
         */
        @Override
        public Selection<T> listElt(String columnName, int idx) {
            return addName(new Utils.CNameIndex(columnName, idx));
        }

        /**
         * {@inheritDoc}
         *
         * @author paouelle
         *
         * @see com.github.helenusdriver.driver.Delete.Selection#mapElt(java.lang.String, java.lang.Object)
         */
        @Override
        public Selection<T> mapElt(String columnName, Object key) {
            return addName(new Utils.CNameKey(columnName, key));
        }
    }
}