using System; using System.Linq; using System.Collections.Generic; using SwissAcademic.Citavi; using SwissAcademic.Citavi.Metadata; namespace SwissAcademic.Citavi.Citations { public class ComponentPartFilter : IComponentPartFilter { //Version 1.10 New variable "outputInSmallCaps", "outputInBold", "outputUnderlined" //Version 1.9 Component deactivates itself if the previous citation is from the same reference as the one before the previous citation (avoiding a sequence such as e.g.: [2] Ebd. -> [3] Ders.) //Version 1.8 GetPreviousVisibleCitation() method gets first previous citation where nobib = false or bibonly = false //Version 1.7 Single N(neutral) or unknown author yields "ders." instead of "dies." //Version 1.6 New variable "deactivateFilterWithOption1Switch" //Version 1.5 New variable "outputInItalics": output can quickly be changed between font style italics and neutral //Version 1.4 New variable "deactivateFilterInsideMultipleCitations": inside multiple citations, filter can be switched off to allow for cite collapsing (see below) //Version 1.3 Footnote index difference must not be > 0 //Version 1.2 Takes all combinations of number/sex into account //Version 1.1 Takes organizations into account public IEnumerable GetTextUnits(ComponentPart componentPart, Template template, Citation citation, out bool handled) { //new in Version 1.6 //if the following is set to true a placeholder citation's output will not show ders./dies. although this would normally be the case //as e.g. the second one of the following two: {Meier 2010 #2} and {Meier 2010 #2:17-19 /opt1} var deactivateFilterWithOption1Switch = true; //new in Version 1.4: //if the following is set to true, the citation collapsing can take place (if the style is set to omit author names) //true: (Mueller 2010, S. 10; 2011, S. 12f.; 2012, S. 17) //false: (Mueller 2010, S. 10; ders. 2011, S. 12 f; ders. 2012, S. 17) var deactivateFilterInsideMultipleCitations = true; var outputInItalics = true; var outputInSmallCaps = false; var outputInBold = false; var outputUnderlined = false; handled = false; var thisCitationIsPartOfMultipleCitation = false; if (citation == null) return null; if (citation.Reference == null) return null; var previousVisibleCitation = GetPreviousVisibleCitation(citation); if (previousVisibleCitation == null) return null; if (previousVisibleCitation.Reference == null) return null; //new in Version 1.9: avoiding a sequence such as e.g.: [2] Ebd. -> [3] Ders. var secondPreviousVisibleCitation = GetPreviousVisibleCitation(previousVisibleCitation); if (secondPreviousVisibleCitation != null && secondPreviousVisibleCitation.Reference != null) { if (previousVisibleCitation.Reference == secondPreviousVisibleCitation.Reference) return null; } //SPECIAL: if this citation has the /opt1 switch set, this filter should be deactivated var placeholderCitation = citation as PlaceholderCitation; if (deactivateFilterWithOption1Switch && placeholderCitation != null && placeholderCitation.FormatOption1) { return null; } //if this citation is a footnote citation it must share the same footnote index with its predecessor var thisFootnoteCitation = citation as FootnoteCitation; if (thisFootnoteCitation != null) { var previousVisibleFootnoteCitation = previousVisibleCitation as FootnoteCitation; if (previousVisibleFootnoteCitation == null) return null; if (thisFootnoteCitation.FootnoteIndex != previousVisibleFootnoteCitation.FootnoteIndex) return null; } //if this citation is part of a multiple citation ... var thisInTextCitation = citation as InTextCitation; if (thisInTextCitation != null) { var printingEntries = thisInTextCitation.Entry.Placeholder.GetPrintingEntries(); if (printingEntries != null && printingEntries.Count() > 1) { //thisInTextCitation IS part of a multiple citation thisCitationIsPartOfMultipleCitation = true; //... and we switch of "ders./dies." completely ... or ... if (deactivateFilterInsideMultipleCitations) { return null; } //... at least for the very first printing entry in a multiple citation var index = printingEntries.IndexOf(thisInTextCitation.Entry); if (index != null && index == 0) return null; } else { thisCitationIsPartOfMultipleCitation = false; } } //if this citations predecessor is part of a multiple citation, but THIS is NOT, switch off filter if (thisInTextCitation != null && !thisCitationIsPartOfMultipleCitation) { var previousVisibleInTextCitation = previousVisibleCitation as InTextCitation; if (previousVisibleInTextCitation == null) return null; var printingEntries = previousVisibleInTextCitation.Entry.Placeholder.GetPrintingEntries(); if (printingEntries != null && printingEntries.Count() > 1) { //previousVisibleInTextCitation IS part of a multiple citation return null; } } //check for 1st PersonFieldElement in ComponentPart var firstPersonFieldElement = componentPart.Elements.FirstOrDefault(item => item is PersonFieldElement) as PersonFieldElement; if (firstPersonFieldElement == null) return null; //determine Persons to compare List thesePersons = null; List previousPersons = null; switch (firstPersonFieldElement.PropertyId) { #region Authors case ReferencePropertyId.Authors: { thesePersons = citation.Reference.GetAuthors() as List; previousPersons = previousVisibleCitation.Reference.GetAuthors() as List; } break; #endregion Authors #region Editors case ReferencePropertyId.Editors: { thesePersons = citation.Reference.GetEditors() as List; previousPersons = previousVisibleCitation.Reference.GetEditors() as List; } break; #endregion Editors #region AuthorsEditorsOrganizations case ReferencePropertyId.AuthorsOrEditorsOrOrganizations: { thesePersons = citation.Reference.GetAuthorsOrEditorsOrOrganizations() as List; previousPersons = previousVisibleCitation.Reference.GetAuthorsOrEditorsOrOrganizations() as List; } break; #endregion AuthorsEditorsOrganizations #region Collaborators case ReferencePropertyId.Collaborators: { thesePersons = citation.Reference.GetCollaborators() as List; previousPersons = previousVisibleCitation.Reference.GetCollaborators() as List; } break; #endregion Collaborators #region OthersInvolved case ReferencePropertyId.OthersInvolved: { thesePersons = citation.Reference.GetOthersInvolved() as List; previousPersons = previousVisibleCitation.Reference.GetOthersInvolved() as List; } break; #endregion OthersInvolved } if (thesePersons == null || thesePersons.Count == 0) return null; if (previousPersons == null || previousPersons.Count == 0) return null; var equality = CheckPersonEquality(thesePersons, previousPersons); if (equality == PersonEquality.None) return null; #region Equality detected - generate output //we DO have some equality, so let's check what we need to output instead of the person's name(s) var text = string.Empty; switch (equality) { case PersonEquality.M: case PersonEquality.N: text = "ders."; break; default: //all others text = "dies."; break; } var output = new TextUnitCollection(); //SwissAcademic.Drawing.FontStyle fontStyle = outputInItalics ? SwissAcademic.Drawing.FontStyle.Italic : SwissAcademic.Drawing.FontStyle.Neutral; SwissAcademic.Drawing.FontStyle fontStyle; fontStyle = SwissAcademic.Drawing.FontStyle.Neutral; if (outputInItalics) fontStyle |= SwissAcademic.Drawing.FontStyle.Italic; if (outputInSmallCaps) fontStyle |= SwissAcademic.Drawing.FontStyle.SmallCaps; if (outputInBold) fontStyle |= SwissAcademic.Drawing.FontStyle.Bold; if (outputUnderlined) fontStyle |= SwissAcademic.Drawing.FontStyle.Underline; output.Add(new LiteralTextUnit(text, fontStyle)); handled = true; return output; #endregion Equality detected - generate output } #region GetPreviousVisibleCitation private static Citation GetPreviousVisibleCitation(Citation citation) { if (citation == null) return null; #region Bibliography if (citation.CitationType == CitationType.Bibliography) { BibliographyCitation previousBibliographyCitation = citation as BibliographyCitation; if (previousBibliographyCitation == null) return null; //consider nobib do { previousBibliographyCitation = previousBibliographyCitation.PreviousBibliographyCitation; if (previousBibliographyCitation == null) return null; } while (previousBibliographyCitation.NoBib == true); //still here? found one! return previousBibliographyCitation; } #endregion Bibliography #region InText if (citation.CitationType == CitationType.InText) { InTextCitation previousInTextCitation = citation as InTextCitation; if (previousInTextCitation == null) return null; //consider bibonly do { previousInTextCitation = previousInTextCitation.PreviousInTextCitation; if (previousInTextCitation == null) return null; } while (previousInTextCitation.BibOnly == true); //still here? found one! return previousInTextCitation; } #endregion InText #region Footnote if (citation.CitationType == CitationType.Footnote) { FootnoteCitation previousFootnoteCitation = citation as FootnoteCitation; if (previousFootnoteCitation == null) return null; //consider bibonly do { previousFootnoteCitation = previousFootnoteCitation.PreviousFootnoteCitation; if (previousFootnoteCitation == null) return null; } while (previousFootnoteCitation.BibOnly == true); //still here? found one! return previousFootnoteCitation; } #endregion Footnote //still here? no previous citation found! return null; } #endregion GetPreviousCitation #region CheckPersonEquality private static PersonEquality CheckPersonEquality(IList personsA, IList personsB) { var personListA = personsA as List; var personListB = personsB as List; if (personListA == null || personListA.Count == 0) return PersonEquality.None; if (personListB == null || personListB.Count == 0) return PersonEquality.None; if (personListA.Count != personListB.Count) return PersonEquality.None; //we DO have two lists of persons of same length //FIRST sort by id for comparison var personIdComparer = new PersonIdComparer(); personListA.Sort(personIdComparer); personListB.Sort(personIdComparer); var allCounter = personListA.Count; var maleCounter = 0; var femaleCounter = 0; var neutralCounter = 0; //loop, compare GUID/id and determine/count sex for (int i = 0; i < personListA.Count; i++) { var idA = personListA[i].GetValue(PersonPropertyId.Id).ToString(); var idB = personListB[i].GetValue(PersonPropertyId.Id).ToString(); if (!idA.Equals(idB, StringComparison.Ordinal)) return PersonEquality.None; //identical! //determine sex (just need to look at one of them, because they are identical) if (personListA[i].Sex == Sex.Male) maleCounter++; if (personListA[i].Sex == Sex.Female) femaleCounter++; if (personListA[i].Sex == Sex.Neutral || personListA[i].Sex == Sex.Unknown) neutralCounter++; } //still here, so ALL persons are equal, now return equality based on sex if (allCounter == 1 && maleCounter == 1) return PersonEquality.M; else if (allCounter == 1 && femaleCounter == 1) return PersonEquality.F; else if (allCounter == 1 && neutralCounter == 1) return PersonEquality.N; else if (allCounter > 1 && maleCounter == allCounter) return PersonEquality.MM; else if (allCounter > 1 && femaleCounter == allCounter) return PersonEquality.FF; else if (allCounter > 1 && neutralCounter == allCounter) return PersonEquality.NN; else if (allCounter > 1 && maleCounter + femaleCounter == allCounter) return PersonEquality.FM; else if (allCounter > 1 && femaleCounter + neutralCounter == allCounter) return PersonEquality.FN; else if (allCounter > 1 && maleCounter + neutralCounter == allCounter) return PersonEquality.MN; else if (allCounter >= 3 && maleCounter >= 1 && femaleCounter >= 1 && neutralCounter >= 1 && maleCounter + femaleCounter + neutralCounter == allCounter) return PersonEquality.FMN; else return PersonEquality.None; } #endregion CheckPersonEquality #region Enum PersonEquality public enum PersonEquality { /// /// None: Different persons and/or different numbers of persons. /// /// None, /// /// Identical person, a single female (Latin: eadem) /// F, /// /// Identical person, a single male (Latin: idem) /// M, /// /// Identical persons, a single (neutral) organization (Latin: idem) /// N, /// /// Identical persons, only females, 2 or more (Latin: eaedem) /// FF, /// /// Identical persons, only males, 2 or more (Latin: eidem) /// MM, /// /// Identical persons, only (neutral) organizations, 2 or more (Latin: eadem) /// NN, /// /// Identical persons, mixed group of females and males only /// FM, /// /// Identical persons, mixed group of females and neutrals only /// FN, /// /// Identical persons, mixed group of males and neutrals only /// MN, /// /// Identical persons, mixed group of females, males and neutrals /// FMN } #endregion PersonEquality #region PersonIdComparer //The following is a sort comparer that will bring all person collections into a well defined order //namely in the order of their internal GUID values. public class PersonIdComparer : IComparer { public int Compare(IPerson person1, IPerson person2) { int returnValue = 1; if (person1 != null && person2 != null) { returnValue = person1.GetValue(PersonPropertyId.Id).ToString().CompareTo(person2.GetValue(PersonPropertyId.Id).ToString()); } return returnValue; } } #endregion PersonIdComparer } }