io.lavagna.service.CardRepository.java Source code

Java tutorial

Introduction

Here is the source code for io.lavagna.service.CardRepository.java

Source

/**
 * This file is part of lavagna.
 *
 * lavagna is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * lavagna is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with lavagna.  If not, see <http://www.gnu.org/licenses/>.
 */
package io.lavagna.service;

import static org.apache.commons.lang3.StringUtils.trimToNull;
import io.lavagna.model.BoardColumn.BoardColumnLocation;
import io.lavagna.model.Card;
import io.lavagna.model.CardFull;
import io.lavagna.model.Event;
import io.lavagna.model.User;
import io.lavagna.query.CardQuery;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional(readOnly = true)
public class CardRepository {

    private static final Logger LOG = LogManager.getLogger();

    private final NamedParameterJdbcTemplate jdbc;
    private final CardQuery queries;

    public CardRepository(NamedParameterJdbcTemplate jdbc, CardQuery queries) {
        this.jdbc = jdbc;
        this.queries = queries;
    }

    // prepare a {:cardOrder, :cardId, :columnId} list
    private static List<SqlParameterSource> prepareOrderParameter(List<Integer> cardIds, int columnId) {
        List<SqlParameterSource> params = new ArrayList<>(cardIds.size());
        for (int i = 0; i < cardIds.size(); i++) {
            SqlParameterSource p = new MapSqlParameterSource("cardOrder", i + 1)//
                    .addValue("cardId", cardIds.get(i))//
                    .addValue("columnId", columnId);
            params.add(p);
        }
        return params;
    }

    public List<CardFull> findAllByBoardShortName(String boardShortName) {
        return queries.findAllByBoardShortName(boardShortName);
    }

    public List<Card> findAllByBoardIdAndLocation(int boardId, BoardColumnLocation location) {
        return queries.findAllByBoardIdAndLocation(boardId, location.toString());
    }

    public List<CardFull> findAllByColumnId(int columnId) {
        return queries.findAllFullByColumnId(columnId);
    }

    public List<CardFull> findAllByIds(Collection<Integer> ids) {
        return ids.isEmpty() ? Collections.<CardFull>emptyList() : queries.findAllByIds(ids);
    }

    public List<Card> findCards(int boardId, String criteria) {
        return queries.findCards(boardId, criteria);
    }

    public List<Event> fetchAllActivityByCardId(int cardId) {
        return queries.fetchAllActivityByCardId(cardId);
    }

    /**
     * 10 element per page. Return 11 elements for signaling if there are more pages
     * 
     * @param boardId
     * @param location
     * @param page
     * @return
     */
    public List<Integer> fetchPaginatedByBoardIdAndLocation(int boardId, BoardColumnLocation location, int page) {
        return queries.fetchPaginatedByBoardIdAndLocation(boardId, location.toString(), 11, page * 10);
    }

    public Card findBy(int cardId) {
        return queries.findBy(cardId);
    }

    public CardFull findFullBy(int cardId) {
        return queries.findFullBy(cardId);
    }

    public CardFull findFullBy(String boardShortName, int seqNumber) {
        return queries.findFullBy(boardShortName, seqNumber);
    }

    public Integer findCardIdByBoardNameAndSeq(String boardShortName, int seqNumber) {
        return queries.findCardIdByBoardNameAndSeq(boardShortName, seqNumber);
    }

    public boolean existCardWith(String boardShortName, int seqNumber) {
        return Integer.valueOf(1).equals(queries.countCardIdByBoardNameAndSeq(boardShortName, seqNumber));
    }

    public Card updateCard(int cardId, String name, User user) {
        queries.updateCard(trimToNull(name), cardId);
        return findBy(cardId);
    }

    /**
     * Returns the new Card
     *
     * @param name
     * @param columnId
     * @return
     */
    @Transactional(readOnly = false)
    public Card createCard(String name, int columnId, User user) {

        LOG.debug("createCard: {name: {}, columnId: {}, userId: {}}", name, columnId, user.getId());

        int sequence = fetchAndLockSequence(columnId);
        queries.createCard(trimToNull(name), columnId, user.getId(), sequence);
        incrementSequence(columnId, sequence);
        return queries.findLastCreatedCard();
    }

    @Transactional(readOnly = false)
    public Card createCardFromTop(String name, int columnId, User user) {
        Card createdCard = createCard(name, columnId, user);
        moveLastCardAtTop(createdCard.getId(), columnId);
        return createdCard;
    }

    @Transactional(readOnly = false)
    private void moveLastCardAtTop(int lastCardId, int columnId) {
        queries.incrementCardsOrder(columnId);
        SqlParameterSource updateParam = new MapSqlParameterSource("columnId", columnId)
                .addValue("cardId", lastCardId).addValue("cardOrder", 0);
        jdbc.update(queries.updateCardOrder(), updateParam);
    }

    /**
     * Fetch the ticket number from the counter and lock the row.
     *
     * @param columnId
     * @return
     */
    private int fetchAndLockSequence(int columnId) {
        return queries.fetchAndLockCardSequence(columnId);
    }

    /**
     * Increment the counter
     *
     * @param columnId
     */
    @Transactional(readOnly = false)
    private void incrementSequence(int columnId, int sequence) {
        int affected = queries.incrementSequence(sequence, columnId);
        Validate.isTrue(affected == 1, "during the update sequence, " + affected
                + " were affected for a card inserted in the columnId " + columnId);
    }

    /**
     * move a card and update the order of the new column. The ids are filtered.
     *
     * @param id
     * @param prevColumnId
     * @param newColumnId
     * @param newOrderForNewColumn
     */
    @Transactional(readOnly = false)
    public void moveCardToColumnAndReorder(int id, int prevColumnId, int newColumnId,
            List<Integer> newOrderForNewColumn) {
        moveCardToColumn(id, prevColumnId, newColumnId);
        updateCardOrder(newOrderForNewColumn, newColumnId);
    }

    @Transactional(readOnly = false)
    public void moveCardToColumn(int cardId, int previousColumnId, int columnId) {

        SqlParameterSource param = new MapSqlParameterSource("cardId", cardId).addValue("columnId", columnId)
                .addValue("previousColumnId", previousColumnId);
        int affected = jdbc.update(queries.moveCardToColumn(), param);
        Validate.isTrue(1 == affected, "moveCardToColumn: must affect exactly one row");
    }

    @Transactional(readOnly = false)
    public List<Integer> moveCardsToColumn(List<Integer> cardIds, int previousColumnId, int columnId, int userId) {

        List<Integer> filteredCardIds = Utils.filter(cardIds,
                queries.findCardIdsInColumnId(cardIds, previousColumnId));

        List<SqlParameterSource> params = new ArrayList<>(filteredCardIds.size());
        for (int cardId : filteredCardIds) {
            SqlParameterSource p = new MapSqlParameterSource("cardId", cardId)//
                    .addValue("previousColumnId", previousColumnId)//
                    .addValue("columnId", columnId);
            params.add(p);
        }

        int[] updateResult = jdbc.batchUpdate(queries.moveCardToColumn(),
                params.toArray(new SqlParameterSource[params.size()]));

        List<Integer> updated = new ArrayList<>();
        for (int i = 0; i < updateResult.length; i++) {
            if (updateResult[i] > 0) {
                updated.add(filteredCardIds.get(i));
            }
        }

        return updated;
    }

    /**
     * Update card order in a given column id. The cardIds are filtered.
     *
     * @param cardIds
     * @param columnId
     */
    @Transactional(readOnly = false)
    public void updateCardOrder(List<Integer> cardIds, int columnId) {

        if (cardIds.isEmpty()) {
            return;
        }

        List<Integer> filteredCardIds = Utils.filter(cardIds, queries.findCardIdsInColumnId(cardIds, columnId));

        List<SqlParameterSource> params = prepareOrderParameter(filteredCardIds, columnId);
        jdbc.batchUpdate(queries.updateCardOrder(), params.toArray(new SqlParameterSource[params.size()]));
    }

    public Map<String, Integer> findCardsIds(List<String> cards) {

        List<Object[]> param = new ArrayList<>(cards.size());
        for (String card : cards) {
            String[] splitted = StringUtils.split(card, '-');
            if (splitted.length > 1) {
                try {
                    Integer cardSequenceNumber = Integer.valueOf(splitted[splitted.length - 1], 10);
                    String boardShortName = StringUtils.join(ArrayUtils.subarray(splitted, 0, splitted.length - 1),
                            '-');
                    param.add(new Object[] { boardShortName, cardSequenceNumber });

                } catch (NumberFormatException nfe) {
                    // skip
                }
            }
        }

        if (param.isEmpty()) {
            return Collections.emptyMap();
        }

        final Map<String, Integer> res = new HashMap<>();
        MapSqlParameterSource paramS = new MapSqlParameterSource("projShortNameAndCardSeq", param);
        jdbc.query(queries.findCardsIs(), paramS, new RowCallbackHandler() {
            @Override
            public void processRow(ResultSet rs) throws SQLException {
                res.put(rs.getString("CARD_IDENTIFIER"), rs.getInt("CARD_ID"));
            }
        });

        return res;
    }

    //TODO: not happy about the interface of this one...
    public List<CardFull> findCardBy(String term, Set<Integer> projectIds) {

        String maybeBoardShortName = null;
        Integer maybeSequenceNumber = null;

        if (term != null) {
            String[] splitted = term.split("-", 2);
            maybeBoardShortName = splitted[0].toUpperCase(Locale.ENGLISH);
            if (splitted.length > 1) {
                try {
                    maybeSequenceNumber = Integer.valueOf(splitted[1]);
                } catch (NumberFormatException ignore) {
                    // ignore
                }
            }
        }
        if (projectIds == null) {
            return queries.findCardBy(term, maybeBoardShortName, maybeSequenceNumber);
        } else if (projectIds.isEmpty()) {
            return Collections.emptyList();
        } else {
            return queries.findCardBy(term, maybeBoardShortName, maybeSequenceNumber, projectIds);
        }
    }

    public int updateCardOrder(int cardId, int order) {
        return queries.updateCardOrder(cardId, order);
    }
}