forked from microsoft/calculator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUnitConverter.h
266 lines (231 loc) · 10.8 KB
/
UnitConverter.h
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
namespace UnitConversionManager
{
enum class Command;
struct Unit
{
Unit(){}
Unit(int id, std::wstring name, std::wstring abbreviation, bool isConversionSource, bool isConversionTarget, bool isWhimsical)
: id(id), name(name), accessibleName(name), abbreviation(abbreviation), isConversionSource(isConversionSource), isConversionTarget(isConversionTarget), isWhimsical(isWhimsical)
{
}
Unit(int id, std::wstring currencyName, std::wstring countryName, std::wstring abbreviation, bool isRtlLanguage, bool isConversionSource, bool isConversionTarget)
: id(id), abbreviation(abbreviation), isConversionSource(isConversionSource), isConversionTarget(isConversionTarget), isWhimsical(false)
{
std::wstring nameValue1 = isRtlLanguage ? currencyName : countryName;
std::wstring nameValue2 = isRtlLanguage ? countryName : currencyName;
name = nameValue1 + L" - " + nameValue2;
accessibleName = nameValue1 + L" " + nameValue2;
}
virtual ~Unit() {}
int id;
std::wstring name;
std::wstring accessibleName;
std::wstring abbreviation;
bool isConversionSource;
bool isConversionTarget;
bool isWhimsical;
bool operator!= (const Unit& that) const
{
return that.id != id;
}
bool operator== (const Unit& that) const
{
return that.id == id;
}
};
// The EMPTY_UNIT acts as a 'null-struct' so that
// Unit pointers can safely be dereferenced without
// null checks.
//
// unitId, name, abbreviation, isConversionSource, isConversionTarget, isWhimsical
const Unit EMPTY_UNIT = Unit{ -1, L"", L"", true, true, false };
struct Category
{
Category(){}
Category(int id, std::wstring name, bool supportsNegative) : id(id), name(name), supportsNegative(supportsNegative)
{
}
int id;
std::wstring name;
bool supportsNegative;
bool operator!= (const Category& that) const
{
return that.id != id;
}
bool operator== (const Category& that) const
{
return that.id == id;
}
};
class UnitHash
{
public:
size_t operator() (const Unit & x) const {
return x.id;
}
};
class CategoryHash
{
public:
size_t operator() (const Category & x) const {
return x.id;
}
};
struct SuggestedValueIntermediate
{
double magnitude;
double value;
Unit type;
};
struct ConversionData
{
ConversionData(){}
ConversionData(double ratio, double offset, bool offsetFirst) : ratio(ratio), offset(offset), offsetFirst(offsetFirst)
{
}
virtual ~ConversionData() {}
double ratio;
double offset;
bool offsetFirst;
};
struct CurrencyStaticData
{
std::wstring countryCode;
std::wstring countryName;
std::wstring currencyCode;
std::wstring currencyName;
std::wstring currencySymbol;
};
struct CurrencyRatio
{
double ratio;
std::wstring sourceCurrencyCode;
std::wstring targetCurrencyCode;
};
typedef std::tuple<std::vector<UnitConversionManager::Unit>, UnitConversionManager::Unit, UnitConversionManager::Unit> CategorySelectionInitializer;
typedef std::unordered_map<UnitConversionManager::Unit, std::unordered_map<UnitConversionManager::Unit, UnitConversionManager::ConversionData, UnitConversionManager::UnitHash>, UnitConversionManager::UnitHash> UnitToUnitToConversionDataMap;
typedef std::unordered_map<UnitConversionManager::Category, std::vector<UnitConversionManager::Unit>, UnitConversionManager::CategoryHash> CategoryToUnitVectorMap;
class IViewModelCurrencyCallback
{
public:
virtual ~IViewModelCurrencyCallback() { };
virtual void CurrencyDataLoadFinished(bool didLoad) = 0;
virtual void CurrencySymbolsCallback(_In_ const std::wstring& fromSymbol, _In_ const std::wstring& toSymbol) = 0;
virtual void CurrencyRatiosCallback(_In_ const std::wstring& ratioEquality, _In_ const std::wstring& accRatioEquality) = 0;
virtual void CurrencyTimestampCallback(_In_ const std::wstring& timestamp, bool isWeekOldData) = 0;
virtual void NetworkBehaviorChanged(_In_ int newBehavior) = 0;
};
class IConverterDataLoader
{
public:
virtual ~IConverterDataLoader() { };
virtual void LoadData() = 0; // prepare data if necessary before calling other functions
virtual std::vector<Category> LoadOrderedCategories() = 0;
virtual std::vector<Unit> LoadOrderedUnits(const Category& c) = 0;
virtual std::unordered_map<Unit, ConversionData, UnitHash> LoadOrderedRatios(const Unit& u) = 0;
virtual bool SupportsCategory(const Category& target) = 0;
};
class ICurrencyConverterDataLoader
{
public:
virtual void SetViewModelCallback(const std::shared_ptr<UnitConversionManager::IViewModelCurrencyCallback>& callback) = 0;
virtual std::pair<std::wstring, std::wstring> GetCurrencySymbols(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) = 0;
virtual std::pair<std::wstring, std::wstring> GetCurrencyRatioEquality(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) = 0;
virtual std::wstring GetCurrencyTimestamp() = 0;
virtual concurrency::task<bool> TryLoadDataFromCacheAsync() = 0;
virtual concurrency::task<bool> TryLoadDataFromWebAsync() = 0;
virtual concurrency::task<bool> TryLoadDataFromWebOverrideAsync() = 0;
};
class IUnitConverterVMCallback
{
public:
virtual ~IUnitConverterVMCallback() { };
virtual void DisplayCallback(const std::wstring& from, const std::wstring& to) = 0;
virtual void SuggestedValueCallback(const std::vector<std::tuple<std::wstring, Unit>>& suggestedValues) = 0;
virtual void MaxDigitsReached() = 0;
};
class IUnitConverter
{
public:
virtual ~IUnitConverter() { }
virtual void Initialize() = 0; // Use to initialize first time, use deserialize instead to rehydrate
virtual std::vector<Category> GetCategories() = 0;
virtual CategorySelectionInitializer SetCurrentCategory(const Category& input) = 0;
virtual Category GetCurrentCategory() = 0;
virtual void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) = 0;
virtual void SwitchActive(const std::wstring& newValue) = 0;
virtual std::wstring Serialize() = 0;
virtual void DeSerialize(const std::wstring& serializedData) = 0;
virtual std::wstring SaveUserPreferences() = 0;
virtual void RestoreUserPreferences(_In_ const std::wstring& userPreferences) = 0;
virtual void SendCommand(Command command) = 0;
virtual void SetViewModelCallback(_In_ const std::shared_ptr<IUnitConverterVMCallback>& newCallback) = 0;
virtual void SetViewModelCurrencyCallback(_In_ const std::shared_ptr<IViewModelCurrencyCallback>& newCallback) = 0;
virtual concurrency::task<std::pair<bool, std::wstring>> RefreshCurrencyRatios() = 0;
};
class UnitConverter : public IUnitConverter, public std::enable_shared_from_this<UnitConverter>
{
public:
UnitConverter(_In_ const std::shared_ptr<IConverterDataLoader>& dataLoader);
UnitConverter(_In_ const std::shared_ptr<IConverterDataLoader>& dataLoader, _In_ const std::shared_ptr<IConverterDataLoader>& currencyDataLoader);
// IUnitConverter
void Initialize() override;
std::vector<Category> GetCategories() override;
CategorySelectionInitializer SetCurrentCategory(const Category& input) override;
Category GetCurrentCategory() override;
void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) override;
void SwitchActive(const std::wstring& newValue) override;
std::wstring Serialize() override;
void DeSerialize(const std::wstring& serializedData) override;
std::wstring SaveUserPreferences() override;
void RestoreUserPreferences(const std::wstring& userPreference) override;
void SendCommand(Command command) override;
void SetViewModelCallback(_In_ const std::shared_ptr<IUnitConverterVMCallback>& newCallback) override;
void SetViewModelCurrencyCallback(_In_ const std::shared_ptr<IViewModelCurrencyCallback>& newCallback) override;
concurrency::task<std::pair<bool, std::wstring>> RefreshCurrencyRatios() override;
// IUnitConverter
static std::vector<std::wstring> StringToVector(const std::wstring& w, const wchar_t * delimiter, bool addRemainder = false);
static std::wstring Quote(const std::wstring& s);
static std::wstring Unquote(const std::wstring& s);
private:
bool CheckLoad();
double Convert(double value, ConversionData conversionData);
std::vector<std::tuple<std::wstring, Unit>> CalculateSuggested();
void Reset();
void ClearValues();
void Calculate();
void TrimString(std::wstring& input);
void InitializeSelectedUnits();
std::wstring RoundSignificant(double num, int numSignificant);
Category StringToCategory(const std::wstring& w);
std::wstring CategoryToString(const Category& c, const wchar_t * delimiter);
std::wstring UnitToString(const Unit& u, const wchar_t * delimiter);
Unit StringToUnit(const std::wstring& w);
ConversionData StringToConversionData(const std::wstring& w);
std::wstring ConversionDataToString(ConversionData d, const wchar_t * delimiter);
void UpdateCurrencySymbols();
void UpdateViewModel();
bool AnyUnitIsEmpty();
std::shared_ptr<IConverterDataLoader> GetDataLoaderForCategory(const Category& category);
std::shared_ptr<ICurrencyConverterDataLoader> GetCurrencyConverterDataLoader();
private:
std::shared_ptr<IConverterDataLoader> m_dataLoader;
std::shared_ptr<IConverterDataLoader> m_currencyDataLoader;
std::shared_ptr<IUnitConverterVMCallback> m_vmCallback;
std::shared_ptr<IViewModelCurrencyCallback> m_vmCurrencyCallback;
std::vector<Category> m_categories;
CategoryToUnitVectorMap m_categoryToUnits;
UnitToUnitToConversionDataMap m_ratioMap;
Category m_currentCategory;
Unit m_fromType;
Unit m_toType;
std::wstring m_currentDisplay;
std::wstring m_returnDisplay;
bool m_currentHasDecimal;
bool m_returnHasDecimal;
bool m_switchedActive;
};
}