package hirondelle.fish.main.member;
import java.util.*;
import hirondelle.web4j.database.DAOException;
import hirondelle.web4j.database.DuplicateException;
import hirondelle.web4j.model.Id;
import hirondelle.web4j.model.ModelCtorException;
import static hirondelle.fish.test.doubles.FakeDAOBehavior.*;
import hirondelle.web4j.util.Util;
/**
Test double for {@link MemberDAO}.
<P>This is a 'fake' class is intended for unit testing only. This implementation
stores its data in memory, using a static member of this class.
This class is safe for access from multiple threads.
<P>Note: this class uses <tt>synchronized</tt> to ensure thread
safety. However, this may not really be necessary in many cases.
It's only really necessary when your testing involves more than a single
thread of execution. When running stand-alone unit tests, there is usually
only one such thread. When using tools such as HttpUnit, however,
which throw multiple HTTP requests at your app, then 'synchronized' is required.
<P>This class goes to some lengths to mimic the real behavior as closely as possible.
For example, the items returned by {@link #list()} are sorted correctly using a specific
<tt>Comparator</tt>. In addition, there is a mechanism for forcing particular
exceptions to occur (important for exercising all branches.)
*/
public class MemberDAOFake extends MemberDAO {
/*
Note : javadoc is not provided for these methods, since they are
designed to behave the same as the superclass. Links to the overridden
method will generated automcatically by javadoc tool.
*/
@Override List<Member> list() throws DAOException {
possiblyThrowExceptionFor(DbOperation.List);
synchronized (fMembers){
ArrayList<Member> result = new ArrayList<Member>(fMembers.values());
Collections.sort(result, sortByMemberName());
return result;
}
}
@Override public Member fetch(Id aMemberId) throws DAOException {
possiblyThrowExceptionFor(DbOperation.FetchForChange);
synchronized (fMembers){
return fMembers.get(aMemberId);
}
}
@Override Id add(Member aMember) throws DAOException, DuplicateException {
possiblyThrowExceptionFor(DbOperation.Add);
synchronized (fMembers){
for(Member existingMember: fMembers.values()){
if ( existingMember.getName().equals(aMember.getName())){
throw new DuplicateException("Duplicate name: " + Util.quote(aMember.getName()), new Throwable());
}
}
Id id = getNewId();
//need to attach the id to the member in two ways here
Member member = getMemberWithId(aMember, id);
fMembers.put(id, member);
return id;
}
}
@Override boolean change(Member aMember) throws DAOException, DuplicateException {
possiblyThrowExceptionFor(DbOperation.Change);
synchronized (fMembers){
Member originalMember = fMembers.put(aMember.getId(), aMember);
return originalMember != null;
}
}
@Override int delete(Id aMemberId) throws DAOException {
possiblyThrowExceptionFor(DbOperation.Delete);
Member existingMember = null;
synchronized (fMembers){
existingMember = fMembers.remove(aMemberId);
}
return existingMember != null ? 1 : 0;
}
@Override Integer getNumActiveMembers() throws DAOException {
possiblyThrowExceptionFor(DbOperation.List);
int result = 0;
for (Member member: fMembers.values()){
if(member.getIsActive()){
++result;
}
}
return result;
}
// PRIVATE //
private static final Map<Id, Member> fMembers = new LinkedHashMap<Id, Member>();
private static Integer fIdGenerator = 0;
/** Define a total sort of Member objects. */
private Comparator<Member> sortByMemberName(){
final int EQUAL = 0;
return new Comparator<Member>() {
public int compare(Member aThis, Member aThat) {
if (aThis == aThat) return EQUAL;
int comparison = aThis.getName().compareTo(aThat.getName());
if(comparison != EQUAL) return comparison;
comparison = aThis.getIsActive().compareTo(aThat.getIsActive());
if(comparison != EQUAL) return comparison;
comparison = aThis.getDisposition().getId().compareTo(aThat.getDisposition().getId());
if(comparison != EQUAL) return comparison;
return EQUAL;
}
};
}
private static Id getNewId(){
fIdGenerator++;
return Id.from(fIdGenerator.toString());
}
private Member getMemberWithId(Member aMember, Id aId) {
Member result = null;
try {
result = new Member(aId, aMember.getName(), aMember.getIsActive(), aMember.getDisposition().getId());
}
catch(ModelCtorException ex){
throw new RuntimeException("Cannot build Member as expected");
}
return result;
}
}