-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathCanonicalOptionListSource.cs
More file actions
139 lines (123 loc) · 4.3 KB
/
Copy pathCanonicalOptionListSource.cs
File metadata and controls
139 lines (123 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright (c) 2016 SIL International
// This software is licensed under the MIT license (http://opensource.org/licenses/MIT)
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
namespace LfMerge.Core.DataConverters.CanonicalSources
{
public abstract class CanonicalOptionListSource
{
// Currently, there are two option lists that should derive from a "canonical" source: grammatical info, and semantic domains
protected Dictionary<Guid, CanonicalItem> byGuid;
protected Dictionary<string, CanonicalItem> byKey;
private string primaryElementName;
private string resourceName;
public CanonicalOptionListSource(string resourceName, string primaryElementName)
{
this.resourceName = resourceName;
this.primaryElementName = primaryElementName;
this.byGuid = new Dictionary<Guid, CanonicalItem>();
this.byKey = new Dictionary<string, CanonicalItem>();
}
public static CanonicalOptionListSource Create(string listCode)
{
// TODO: Consider whether to make singletons, for the sake of not having to re-parse the resources.
// However, I suspect that it won't be necessary, as CanonicalOptionListSource instances get attached
// to ConvertMongoToLcmOptionList instances, and those instances stick around for one entire Lexicon conversion.
// It appears to take 200ms to process semantic domains, and 30ms to process grammatical info. So it might
// not be worth the extra complexity of singletons -- or it might. That remains to be decided.
if (listCode == MagicStrings.LfOptionListCodeForGrammaticalInfo)
return new CanonicalPartOfSpeechSource();
else if (listCode == MagicStrings.LfOptionListCodeForSemanticDomains)
return new CanonicalSemanticDomainSource();
else if (listCode == MagicStrings.LfOptionListCodeForLfTags)
return new CanonicalLfTagSource();
else
return null;
}
public CanonicalItem ByGuidOrNull(Guid g)
{
if (this.byGuid.Count == 0)
{
LoadCanonicalData();
}
CanonicalItem result;
return byGuid.TryGetValue(g, out result) ? result : null;
}
public CanonicalItem ByKeyOrNull(string key){
if (this.byKey.Count == 0)
{
LoadCanonicalData();
}
CanonicalItem result;
return byKey.TryGetValue(key, out result) ? result : null;
}
public bool TryGetByGuid(Guid g, out CanonicalItem result)
{
result = ByGuidOrNull(g);
return (result != null);
}
public bool TryGetByKey(string key, out CanonicalItem result)
{
result = ByKeyOrNull(key);
return (result != null);
}
public Dictionary<Guid, CanonicalItem>.ValueCollection ValuesByGuid => this.byGuid.Values;
public Dictionary<string, CanonicalItem>.ValueCollection ValuesByKey => this.byKey.Values;
public int Count => this.byGuid.Count;
// Descendants will override this to specify the generic type T.
// E.g., LoadCanonicalData<CanonicalPartOfSpeechItem>();
public abstract void LoadCanonicalData();
public void LoadCanonicalData<T>()
where T: CanonicalItem, new()
{
using (Stream stream = typeof(CanonicalOptionListSource).Assembly.GetManifestResourceStream(resourceName))
LoadCanonicalData<T>(stream);
}
public void LoadCanonicalData<T>(Stream stream)
where T: CanonicalItem, new()
{
using (var xmlReader = XmlReader.Create(stream))
LoadCanonicalData<T>(xmlReader);
}
public void LoadCanonicalData<T>(XmlReader xmlReader)
where T: CanonicalItem, new()
{
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == primaryElementName)
{
var item = new T();
item.PopulateFromXml(xmlReader);
UpdateDictsFromItem(item);
}
}
}
protected void UpdateDictsFromItem(CanonicalItem item)
{
Guid g = Guid.Empty;
if (Guid.TryParse(item.GuidStr, out g))
{
// Update either both dicts, or neither.
byGuid[g] = item;
SetAppropriateKey(item);
byKey[item.Key] = item;
}
foreach (CanonicalItem subitem in item.Subitems)
UpdateDictsFromItem(subitem);
}
// Deal with duplicate keys by appending a simple number to them.
// Should be called AFTER PopulateFromXml() has done its work.
protected void SetAppropriateKey(CanonicalItem item)
{
string origKey = item.Key;
int extra = 0;
while (byKey.ContainsKey(item.Key))
{
extra++;
item.Key = origKey + extra.ToString();
}
}
}
}