/*
* Copyright (C) 2014 GG-Net GmbH - Oliver Günther
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.ggnet.dwoss.stock.emo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.TreeSet;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import eu.ggnet.dwoss.stock.eao.LogicTransactionEao;
import eu.ggnet.dwoss.stock.eao.StockUnitEao;
import eu.ggnet.dwoss.stock.entity.LogicTransaction;
import eu.ggnet.dwoss.stock.entity.StockUnit;
/**
*
* @author pascal.perau
*/
public class LogicTransactionEmo {
private EntityManager em;
public LogicTransactionEmo(EntityManager em) {
this.em = em;
}
public LogicTransaction request(long dossierId, LockModeType lockModeType) {
try {
return em.createNamedQuery("LogicTransaction.findByDossierId", LogicTransaction.class).setParameter(1, dossierId).setLockMode(lockModeType).getSingleResult();
} catch (NoResultException e) {
LogicTransaction lt = new LogicTransaction();
lt.setDossierId(dossierId);
em.persist(lt);
em.refresh(lt, lockModeType);
return lt;
}
}
/**
* Request a LogicTransaction by its DossierId. <br />
* If no LogicTransaction is found, a new is created and the DossierId will be set.
* <p/>
* @param dossierId The Dossier Id
* @return the found or a new LogicTransaction
*/
public LogicTransaction request(long dossierId) {
Query query = em.createNamedQuery("LogicTransaction.findByDossierId");
query.setParameter(1, dossierId);
try {
return (LogicTransaction)query.getSingleResult();
} catch (NoResultException e) {
LogicTransaction lt = new LogicTransaction();
lt.setDossierId(dossierId);
em.persist(lt);
return lt;
}
}
/**
* Removes StockUnits identified by UniqueUnitIds from a LogicTransaction, if it exists.
*
* @param dossierId the dossierId of the LogicTransaction
* @param uniqueUnitIds the uniqueUnitIds
* @return the LogicTransaction after the removal, always returns a list.
*/
// TODO: Test
public List<StockUnit> optionalRemoveUnits(long dossierId, Collection<Integer> uniqueUnitIds) {
if ( uniqueUnitIds == null ) throw new NullPointerException("uniqueUnitIds must not be null");
LogicTransaction logicTransaction = new LogicTransactionEao(em).findByDossierId(dossierId); // Null is possible
if ( logicTransaction == null ) return new ArrayList<>();
List<StockUnit> stockUnits = new ArrayList<>();
for (Integer uniqueUnitId : uniqueUnitIds) {
// check stockunit if not in LogicTransaction.
StockUnit unit = new StockUnitEao(em).findByUniqueUnitId(uniqueUnitId);
if ( unit == null ) continue;
if ( unit.getLogicTransaction() == null ) throw new IllegalStateException(unit + " is not on a LogicTransaction, shoud be on " + logicTransaction);
if ( !logicTransaction.equals(unit.getLogicTransaction()) )
throw new IllegalStateException(unit + " is on a different LogicTransaction than " + logicTransaction);
logicTransaction.remove(unit);
stockUnits.add(unit);
}
return stockUnits;
}
/**
* Brings the LogicTransaction identified by the dossierId and the supplied uniqueUnitIds in equilibrium.
* <p/>
* Handles the following states:
* <ul>
* <li>If uniqueUnitIds is empty a possible LogicTransaction is emptied and removed</li>
* <li>Otherwise the LogicTransaction is synchronised with the uniqueUnitIds. possibly creating the LogicTransaction</li>
* </ul>
* TODO: Test (is Tested in RedTapeOperationTest)
* <p/>
* @param dossierId the id of the dossier
* @param newUniqueUnitIds List of uniqueUnitIds may not be null
* @return a Result containing uniqueUnitIds which have been added an removed, or null if nothing happend.
* @throws NullPointerException if uniqueUnitIds is null
* @throws IllegalArgumentException if a uniqueUnitId has no StockUnit
* @throws IllegalStateException if a StockUnit, that should be free, is already on a LogicTransaction.
*/
// TODO: Return removed, added.
public EquilibrationResult equilibrate(long dossierId, final Collection<Integer> newUniqueUnitIds) throws IllegalArgumentException, IllegalStateException, NullPointerException {
if ( newUniqueUnitIds == null ) throw new NullPointerException("uniqueUnitIds must not be null");
LogicTransaction logicTransaction = new LogicTransactionEao(em).findByDossierId(dossierId, LockModeType.PESSIMISTIC_WRITE); // Null is possible
if ( logicTransaction == null && newUniqueUnitIds.isEmpty() ) return null;
NavigableSet<Integer> adding = new TreeSet<>(newUniqueUnitIds);
Map<Integer, StockUnit> oldStockUnits = new HashMap<>();
NavigableSet<Integer> removal = new TreeSet<>();
if ( logicTransaction != null ) {
for (StockUnit stockUnit : logicTransaction.getUnits()) {
oldStockUnits.put(stockUnit.getUniqueUnitId(), stockUnit);
}
removal.addAll(oldStockUnits.keySet());
removal.removeAll(newUniqueUnitIds);
adding.removeAll(oldStockUnits.keySet());
for (Integer uniqueUnitId : removal) {
logicTransaction.remove(oldStockUnits.get(uniqueUnitId));
}
if ( newUniqueUnitIds.isEmpty() ) {
em.remove(logicTransaction);
return null;
}
}
if ( logicTransaction == null ) logicTransaction = request(dossierId, LockModeType.PESSIMISTIC_WRITE);
for (Integer uniqueUnitId : adding) {
// check stockunit if not in LogicTransaction.
StockUnit unit = new StockUnitEao(em).findByUniqueUnitId(uniqueUnitId);
if ( unit == null ) throw new IllegalArgumentException("The supplied uniqueUnitId=" + uniqueUnitId + " has no StockUnit");
if ( unit.getLogicTransaction() != null ) throw new IllegalStateException(unit + " is already in a LogicTransaction");
else logicTransaction.add(unit);
}
return new EquilibrationResult(adding, removal, logicTransaction);
}
}