/// <summary> /// Adds a ballot to the binder. This method identifies common /// faces, so that they can be reused reducing the final set of PDFs /// </summary> /// <param name="ballot">The ballot.</param> /// <externalUnit/> /// <revision revisor="dev11" date="2/25/2009" version="1.0.8.0801"> /// Member Created /// </revision> public void AddBallot(PaperBallot ballot) { // get the faces of this ballot List <PaperFace> faces = PaperBallot.GetFaces(ballot); // get the filenames for all the faces of the ballot PaperFace face; string[] files = Pdf.CreatePdfFilenames( PaperBallot.GetPdfFilename(ballot), faces.Count); string md5, filename; // for each face, find it in the lookup dictionary. If it is found, // it means that an identical face has already been created, so // don't include it when generating the PDF files and replace the // filename for the exisiting one if (this.dicBallots.ContainsKey(ballot) == false) { // add the ballot to the dictionary along with its set of // file names this.dicBallots.Add(ballot, files); } for (int i = 0; i < faces.Count; i = i + 1) { // PaperFace <--> filename // faces[0] <--> files[0] // faces[1] <--> files[1] // ... face = faces[i]; filename = files[i]; md5 = BitConverter.ToString(ObjectMD5.Generate(face)); if (this.dicFaces.ContainsKey(md5) == false) { // this is a new face that up to this point no other ballot // has, so add it to the dictionary for future comparison // to subsequent ballots this.dicFaces.Add(md5, filename); } else { // an identical face is already in use, so to the current // ballot assign that face and replace the duplicate files[i] = this.dicFaces[md5]; } } }
/// <summary> /// Extracts the cards and faces from the ballot and creates /// duplicates that are added to the corresponding collections, plus /// adding a duplicate of the ballot to the collection. Notice that /// cards and ballot duplicates have the inner collections empty, so /// that the serialization do not create those collections as part /// of the resulting XML. /// </summary> /// <param name="ballot">The ballot.</param> /// <externalUnit cref="Ballot"/> /// <externalUnit cref="Card"/> /// <externalUnit cref="CardList"/> /// <externalUnit cref="Face"/> /// <externalUnit cref="FaceList"/> /// <revision revisor="dev11" date="12/16/2008" version="1.0.0.0"> /// Member Created /// </revision> /// <revision revisor="dev11" date="02/20/2009" version="1.0.8.0301"> /// Added face reuse and removal of duplicates /// </revision> /// <revision revisor="dev11" date="03/04/2009" version="1.0.8.1501"> /// since a ballot style can be used for multiple precincts, and the /// precinct id is now part of the barcode, the ballot style id is /// no longer enough to uniquely identify a ballot on the /// Ballots.xml file. So now, the id is a sequential number. /// </revision> /// <revision revisor="dev11" date="03/06/2009" version="1.0.8.1701"> /// added support for new ballot attributes (ballot style id and /// precinct id). /// </revision> /// <revision revisor="dev11" date="03/16/2009" version="1.0.8.2701"> /// changed local variables name /// </revision> public void AddBallot(Ballot ballot) { // create a dictionary of faces to keep track of unique faces // each key represents an MD5 hash of the collection of marks of // a face. Any two faces with identical collection of marks are // considered the same even if their face ids are different. In that // case, discard the newer one, and reuse the old one as saved on // the dictionary string md5; Face uniqueFace; // create a duplicate of the ballot so that the cards collection is // populated with custom duplicates Ballot ballotToCard = new Ballot(); // make it 1-based identifier ballotToCard.Id = 1 + this.ballots.Count; ballotToCard.BallotStyleId = ballot.BallotStyleId; ballotToCard.PrecinctId = ballot.PrecinctId; ballotToCard.Cards = new CardList(); // add the ballot duplicate to the ballots collection this.ballots.Add(ballotToCard); foreach (Card card in ballot.Cards) { // create a duplicate of the card so that the faces collection // is populated with custom duplicates Card cardToFace = new Card(); cardToFace.Barcode = card.Barcode; cardToFace.Id = card.Id; cardToFace.Faces = new FaceList(); // Add the duplicate card to the cards collection. // This duplicate links a card to a face. this.cards.Add(cardToFace); foreach (Face face in card.Faces) { // get the MD5 hash of the collection of marks of the // current face md5 = BitConverter.ToString(ObjectMD5.Generate(face.Marks)); // check if an identical collection is already in use if (this.faceMap.ContainsKey(md5) == true) { // a face with an identical collection of marks is // already in use so get it from the dictionary uniqueFace = this.faceMap[md5]; } else { // no existing face up to this point has the same // collection of marks, so add the face to the // dictionary using the MD5 hash this.faceMap.Add(md5, face); // add the new face since all marks need to be included // as well on the [Faces.xml] file this.faces.Add(face); // use this face as the [uniqueFace] uniqueFace = face; } // create the face entry that goes on the current card // This is the face instance that appears on the [Cards.xml] // file and doesn't contain any marks in it. It is simply // a pointer to the corresponding face entry on the // [Faces.xml]where all marks are defined Face faceToMark = new Face(); // map to the uniqueFace using the [id] faceToMark.Id = uniqueFace.Id; // add the duplicate of the face to the current card // collection that generates the [Cards.xml] file // this duplicate do not contain the marks cardToFace.Faces.Add(faceToMark); } // create another card duplicate to link the ballot to the card // and add it to the collection of cards of the ballot duplicate Card cardFromBallot = new Card(); cardFromBallot.Barcode = card.Barcode; cardFromBallot.Id = card.Id; ballotToCard.Cards.Add(cardFromBallot); } }