1 /*
2  *  "GEDKeeper", the personal genealogical database editor.
3  *  Copyright (C) 2009-2021 by Sergey V. Zhdanovskih.
4  *
5  *  This file is part of "GEDKeeper".
6  *
7  *  This program is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 using System;
22 using BSLib.Calendar;
23 using GDModel.Providers.GEDCOM;
24 
25 namespace GDModel
26 {
27     public enum GDMRestriction
28     {
29         rnNone,
30         rnLocked,
31         rnConfidential,
32         rnPrivacy,
33 
34         rnLast = rnPrivacy
35     }
36 
37 
38     public abstract class GDMRecordWithEvents : GDMRecord, IGDMRecordWithEvents
39     {
40         private GDMList<GDMCustomEvent> fEvents;
41         private GDMRestriction fRestriction;
42 
43 
44         public bool HasEvents
45         {
46             get { return fEvents != null && fEvents.Count != 0; }
47         }
48 
49         public GDMList<GDMCustomEvent> Events
50         {
51             get {
52                 if (fEvents == null) {
53                     fEvents = new GDMList<GDMCustomEvent>();
54                 }
55 
56                 return fEvents;
57             }
58         }
59 
60         public GDMRestriction Restriction
61         {
62             get { return fRestriction; }
63             set { fRestriction = value; }
64         }
65 
66 
GDMRecordWithEvents(GDMTree tree)67         protected GDMRecordWithEvents(GDMTree tree) : base(tree)
68         {
69         }
70 
Dispose(bool disposing)71         protected override void Dispose(bool disposing)
72         {
73             if (disposing) {
74                 if (fEvents != null) fEvents.Dispose();
75             }
76             base.Dispose(disposing);
77         }
78 
TrimExcess()79         internal override void TrimExcess()
80         {
81             base.TrimExcess();
82 
83             if (fEvents != null) fEvents.TrimExcess();
84         }
85 
Clear()86         public override void Clear()
87         {
88             base.Clear();
89 
90             if (fEvents != null) fEvents.Clear();
91             fRestriction = GDMRestriction.rnNone;
92         }
93 
IsEmpty()94         public override bool IsEmpty()
95         {
96             // Restrictions are not checked because they are not important if other fields are empty.
97             return base.IsEmpty() && (fEvents == null || fEvents.Count == 0);
98         }
99 
Assign(GDMTag source)100         public override void Assign(GDMTag source)
101         {
102             GDMRecordWithEvents sourceRec = source as GDMRecordWithEvents;
103             if (sourceRec == null)
104                 throw new ArgumentException(@"Argument is null or wrong type", "source");
105 
106             base.Assign(source);
107 
108             if (sourceRec.fEvents != null) {
109                 for (int i = 0, count = sourceRec.fEvents.Count; i < count; i++) {
110                     GDMCustomEvent sourceEvent = sourceRec.fEvents[i];
111                     GDMCustomEvent copy = (GDMCustomEvent)Activator.CreateInstance(sourceEvent.GetType());
112                     copy.Assign(sourceEvent);
113                     AddEvent(copy);
114                 }
115             }
116 
117             fRestriction = sourceRec.Restriction;
118         }
119 
MoveTo(GDMRecord targetRecord)120         public override void MoveTo(GDMRecord targetRecord)
121         {
122             GDMRecordWithEvents target = targetRecord as GDMRecordWithEvents;
123             if (target == null)
124                 throw new ArgumentException(@"Argument is null or wrong type", "targetRecord");
125 
126             base.MoveTo(targetRecord);
127 
128             while (fEvents != null && fEvents.Count > 0) {
129                 GDMCustomEvent obj = fEvents.Extract(0);
130                 target.AddEvent(obj);
131             }
132 
133             target.Restriction = fRestriction;
134         }
135 
ReplaceXRefs(GDMXRefReplacer map)136         public override void ReplaceXRefs(GDMXRefReplacer map)
137         {
138             base.ReplaceXRefs(map);
139             if (fEvents != null) fEvents.ReplaceXRefs(map);
140         }
141 
FindEvent(string eventName)142         public GDMCustomEvent FindEvent(string eventName)
143         {
144             GDMCustomEvent result = null;
145             if (fEvents == null) return result;
146 
147             int num = fEvents.Count;
148             for (int i = 0; i < num; i++) {
149                 GDMCustomEvent evt = fEvents[i];
150 
151                 if (evt.GetTagName() == eventName) {
152                     result = evt;
153                     break;
154                 }
155             }
156 
157             return result;
158         }
159 
FindEvent(GEDCOMTagType eventType)160         public GDMCustomEvent FindEvent(GEDCOMTagType eventType)
161         {
162             GDMCustomEvent result = null;
163             if (fEvents == null) return result;
164 
165             int evtType = (int)eventType;
166             int num = fEvents.Count;
167             for (int i = 0; i < num; i++) {
168                 GDMCustomEvent evt = fEvents[i];
169 
170                 if (evt.Id == evtType) {
171                     result = evt;
172                     break;
173                 }
174             }
175 
176             return result;
177         }
178 
AddEvent(GDMCustomEvent evt)179         public abstract GDMCustomEvent AddEvent(GDMCustomEvent evt);
180 
181         private static readonly float[] CA_VALUES = new float[] { 0.25f, 0.5f, 0.75f, 1.0f };
182 
GetCertaintyAssessment()183         public float GetCertaintyAssessment()
184         {
185             float result = 0;
186             float wsum = 0;
187 
188             if (fEvents != null) {
189                 int num1 = fEvents.Count;
190                 for (int i = 0; i < num1; i++) {
191                     GDMCustomEvent evt = fEvents[i];
192                     if (!evt.HasSourceCitations) continue;
193 
194                     int num2 = evt.SourceCitations.Count;
195                     for (int k = 0; k < num2; k++) {
196                         GDMSourceCitation cit = evt.SourceCitations[k];
197 
198                         int ca = cit.GetValidCertaintyAssessment();
199                         int weight = (ca + 1);
200 
201                         result += (CA_VALUES[ca] * weight);
202                         wsum += weight;
203                     }
204                 }
205             }
206 
207             if (HasSourceCitations) {
208                 int num3 = SourceCitations.Count;
209                 for (int i = 0; i < num3; i++) {
210                     GDMSourceCitation cit = SourceCitations[i];
211 
212                     int ca = cit.GetValidCertaintyAssessment();
213                     int weight = (ca + 1);
214 
215                     result += (CA_VALUES[ca] * weight);
216                     wsum += weight;
217                 }
218             }
219 
220             if (wsum != 0.0f) {
221                 result /= wsum;
222             } else {
223                 result = 0.0f;
224             }
225 
226             return result;
227         }
228 
GetUDN(string eventSign)229         public UDN GetUDN(string eventSign)
230         {
231             GDMCustomEvent evt = FindEvent(eventSign);
232             return (evt == null) ? UDN.CreateEmpty() : evt.GetUDN();
233         }
234 
235         /// <summary>
236         /// In the historical chronology of the year 0 does not exist.
237         /// Therefore, the digit 0 in the year value can be used as a sign of lack or error.
238         /// ChronologicalYear - introduced for the purposes of uniform chronology years in the Gregorian calendar.
239         /// Is estimated from -4714 BC to 3268 AD.
240         /// </summary>
241         /// <returns>chronological year</returns>
GetChronologicalYear(string eventSign)242         public int GetChronologicalYear(string eventSign)
243         {
244             GDMCustomEvent evt = FindEvent(eventSign);
245             return (evt == null) ? 0 : evt.GetChronologicalYear();
246         }
247     }
248 }
249