Skip to content

Commit b8ad2da

Browse files
committed
Emit custom properties as nested object
Completes issue #104. Also fixes long-standing bug in InstallDate formatting for value and text formatters.
1 parent 05d412d commit b8ad2da

19 files changed

+1228
-55
lines changed

src/vswhere.lib/Formatter.cpp

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using namespace std;
99
using namespace std::placeholders;
1010

11+
const std::wstring Formatter::empty_wstring;
12+
1113
Formatter::Formatter()
1214
{
1315
m_properties =
@@ -33,6 +35,7 @@ Formatter::FormatterMap Formatter::Formatters =
3335
{ L"xml", make_tuple(IDS_FORMAT_XML, XmlFormatter::Create) },
3436
};
3537

38+
const wstring Formatter::s_delims(L"./_");
3639
ci_equal Formatter::s_comparer;
3740

3841
std::unique_ptr<Formatter> Formatter::Create(const std::wstring& type)
@@ -113,6 +116,7 @@ wstring Formatter::FormatDate(_In_ const FILETIME& value)
113116
throw win32_error();
114117
}
115118

119+
date.reserve(cch);
116120
date.resize(cch - 1);
117121
cch = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, const_cast<LPWSTR>(date.c_str()), cch);
118122
if (!cch)
@@ -127,7 +131,8 @@ wstring Formatter::FormatDate(_In_ const FILETIME& value)
127131
throw win32_error();
128132
}
129133

130-
time.reserve(cch - 1);
134+
time.reserve(cch);
135+
time.resize(cch - 1);
131136
cch = ::GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, const_cast<LPWSTR>(time.c_str()), cch);
132137
if (!cch)
133138
{
@@ -163,7 +168,28 @@ void Formatter::WriteInternal(_In_ const CommandArgs& args, _In_ Console& consol
163168

164169
if (specified.empty() || !found)
165170
{
166-
WriteProperties(args, console, pInstance);
171+
found = WriteProperties(args, console, pInstance);
172+
if (specified.empty() || !found)
173+
{
174+
ISetupInstance2Ptr instance2;
175+
176+
auto hr = pInstance->QueryInterface(&instance2);
177+
if (SUCCEEDED(hr))
178+
{
179+
ISetupPropertyStorePtr store;
180+
181+
hr = instance2->GetProperties(&store);
182+
if (SUCCEEDED(hr) && !!store)
183+
{
184+
wstring name(L"properties");
185+
StartObject(console, name);
186+
187+
WriteProperties(args, console, store, name);
188+
189+
EndObject(console);
190+
}
191+
}
192+
}
167193
}
168194

169195
EndObject(console);
@@ -193,12 +219,11 @@ void Formatter::WriteProperty(_In_ Console& console, _In_ const wstring& name, _
193219
}
194220
}
195221

196-
void Formatter::WriteProperties(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance)
222+
bool Formatter::WriteProperties(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance)
197223
{
198224
_ASSERTE(pInstance);
199225

200226
ISetupPropertyStorePtr store;
201-
LPSAFEARRAY psaNames = NULL;
202227

203228
auto hr = pInstance->QueryInterface(&store);
204229
if (FAILED(hr))
@@ -208,36 +233,67 @@ void Formatter::WriteProperties(_In_ const CommandArgs& args, _In_ Console& cons
208233
throw win32_error(hr);
209234
}
210235

211-
return;
236+
return false;
212237
}
213238

214-
hr = store->GetNames(&psaNames);
239+
return WriteProperties(args, console, store);
240+
}
241+
242+
bool Formatter::WriteProperties(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupPropertyStore* pProperties, _In_opt_ const wstring& prefix)
243+
{
244+
_ASSERTE(pProperties);
245+
246+
LPSAFEARRAY psaNames = NULL;
247+
auto found = false;
248+
249+
auto hr = pProperties->GetNames(&psaNames);
215250
if (FAILED(hr))
216251
{
217-
return;
252+
return false;
218253
}
219254

220-
const auto& specified = args.get_Property();
221-
SafeArray<BSTR> saNames(psaNames);
255+
// Trim optional nested object name from specified property if matching current scope.
256+
wstring specified = args.get_Property();
257+
if (prefix.size() > 0)
258+
{
259+
auto pos = specified.find_first_of(s_delims);
260+
if (pos != wstring::npos && (pos + 1) < specified.size() && s_comparer(prefix, specified.substr(0, pos)))
261+
{
262+
specified = specified.substr(pos + 1);
263+
}
264+
else if (s_comparer(prefix, specified))
265+
{
266+
// If the current nested object name is specified, clear the prefix to show the whole nested object.
267+
specified.clear();
268+
}
269+
}
222270

271+
SafeArray<BSTR> saNames(psaNames);
223272
for (const auto& bstrName : saNames.Elements())
224273
{
225274
wstring name(bstrName);
226-
if (specified.empty() || s_comparer(name, specified))
275+
if (specified.empty() || (found = s_comparer(name, specified)))
227276
{
228277
variant_t vtValue;
229278

230279
auto it = find_if(m_properties.begin(), m_properties.end(), bind(Formatter::PropertyEqual, name, _1));
231280
if (it == m_properties.end())
232281
{
233-
hr = store->GetValue(bstrName, vtValue.GetAddress());
282+
hr = pProperties->GetValue(bstrName, vtValue.GetAddress());
234283
if (SUCCEEDED(hr))
235284
{
236285
WriteProperty(console, name, vtValue);
237286
}
238287
}
288+
289+
if (found)
290+
{
291+
return true;
292+
}
239293
}
240294
}
295+
296+
return false;
241297
}
242298

243299
bool Formatter::PropertyEqual(_In_ const std::wstring& name, _In_ PropertyArray::const_reference property)

src/vswhere.lib/Formatter.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ class Formatter
3131
typedef std::function<HRESULT(_In_ ISetupInstance*, _Out_ VARIANT*)> PropertyFunction;
3232
typedef std::vector<std::pair<std::wstring, PropertyFunction>> PropertyArray;
3333

34+
static const std::wstring empty_wstring;
35+
3436
Formatter();
3537

3638
static std::wstring FormatDateISO8601(_In_ const FILETIME& value);
3739

3840
virtual void StartDocument(_In_ Console& console) {}
3941
virtual void StartArray(_In_ Console& console) {}
40-
virtual void StartObject(_In_ Console& console) {}
42+
virtual void StartObject(_In_ Console& console, _In_opt_ const std::wstring& name = empty_wstring) {}
4143
virtual void WriteProperty(_In_ Console& console, _In_ const std::wstring& name, _In_ const std::wstring& value) {}
4244
virtual void EndObject(_In_ Console& console) {}
4345
virtual void EndArray(_In_ Console& console) {}
@@ -59,6 +61,7 @@ class Formatter
5961
static bool PropertyEqual(_In_ const std::wstring& name, _In_ PropertyArray::const_reference property);
6062
static HRESULT GetStringProperty(_In_ std::function<HRESULT(_Out_ BSTR*)> pfn, _Out_ VARIANT* pvt);
6163

64+
static const std::wstring s_delims;
6265
static ci_equal s_comparer;
6366

6467
HRESULT GetInstanceId(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtInstanceId);
@@ -74,7 +77,8 @@ class Formatter
7477

7578
void WriteInternal(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance);
7679
void WriteProperty(_In_ Console& console, _In_ const std::wstring& name, _In_ const variant_t& value);
77-
void WriteProperties(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance);
80+
bool WriteProperties(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance);
81+
bool WriteProperties(_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupPropertyStore* pProperties, _In_opt_ const std::wstring& prefix = empty_wstring);
7882

7983
PropertyArray m_properties;
8084
};

src/vswhere.lib/JsonFormatter.cpp

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,83 +9,108 @@ using namespace std;
99

1010
void JsonFormatter::StartArray(_In_ Console& console)
1111
{
12-
m_arrayStart = true;
12+
m_requiresSep.push(false);
1313

1414
console.Write(L"%ls[", m_padding.c_str());
1515
Push();
1616
}
1717

18-
void JsonFormatter::StartObject(_In_ Console& console)
18+
void JsonFormatter::StartObject(_In_ Console& console, _In_opt_ const wstring& name)
1919
{
20-
m_objectStart = true;
20+
if (m_requiresSep.top())
21+
{
22+
console.WriteLine(L",");
23+
}
24+
else
25+
{
26+
// Do not write new line when starting subsequent object immediately after previous object.
27+
if (m_objects.empty())
28+
{
29+
console.WriteLine();
30+
}
31+
32+
m_requiresSep.top() = true;
33+
}
2134

22-
if (m_arrayStart)
35+
m_requiresSep.push(false);
36+
m_objects.push(name);
37+
38+
if (!name.empty())
2339
{
24-
console.WriteLine();
40+
console.WriteLine(L"%ls\"%ls\": {", m_padding.c_str(), name.c_str());
2541
}
2642
else
2743
{
28-
console.WriteLine(L",");
44+
console.WriteLine(L"%ls{", m_padding.c_str());
2945
}
3046

31-
console.WriteLine(L"%ls{", m_padding.c_str());
3247
Push();
33-
34-
m_arrayStart = false;
3548
}
3649

3750
void JsonFormatter::WriteProperty(_In_ Console& console, _In_ const wstring& name, _In_ const wstring& value)
3851
{
39-
if (!m_objectStart)
52+
if (m_requiresSep.top())
4053
{
4154
console.WriteLine(L",");
4255
}
56+
else
57+
{
58+
m_requiresSep.top() = true;
59+
}
4360

4461
auto escaped = replace_all(value, L"\\", L"\\\\");
4562
console.Write(L"%ls\"%ls\": \"%ls\"", m_padding.c_str(), name.c_str(), escaped.c_str());
46-
47-
m_objectStart = false;
4863
}
4964

5065
void JsonFormatter::WriteProperty(_In_ Console& console, _In_ const wstring& name, _In_ bool value)
5166
{
52-
if (!m_objectStart)
67+
if (m_requiresSep.top())
5368
{
5469
console.WriteLine(L",");
5570
}
71+
else
72+
{
73+
m_requiresSep.top() = true;
74+
}
5675

5776
console.Write(L"%ls\"%ls\": %ls", m_padding.c_str(), name.c_str(), (value ? L"true" : L"false"));
58-
59-
m_objectStart = false;
6077
}
6178

6279
void JsonFormatter::WriteProperty(_In_ Console& console, _In_ const wstring& name, _In_ long long value)
6380
{
64-
if (!m_objectStart)
81+
if (m_requiresSep.top())
6582
{
6683
console.WriteLine(L",");
6784
}
85+
else
86+
{
87+
m_requiresSep.top() = true;
88+
}
6889

6990
console.Write(L"%ls\"%ls\": %d", m_padding.c_str(), name.c_str(), value);
70-
71-
m_objectStart = false;
7291
}
7392

7493
void JsonFormatter::EndObject(_In_ Console& console)
7594
{
7695
Pop();
96+
97+
m_requiresSep.pop();
98+
m_objects.pop();
99+
77100
console.Write(L"\n%ls}", m_padding.c_str());
78101
}
79102

80103
void JsonFormatter::EndArray(_In_ Console& console)
81104
{
82105
Pop();
83106

84-
if (!m_arrayStart)
107+
if (m_requiresSep.top())
85108
{
86109
console.WriteLine();
87110
}
88111

112+
m_requiresSep.pop();
113+
89114
console.Write(L"%ls]", m_padding.c_str());
90115
}
91116

src/vswhere.lib/JsonFormatter.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,15 @@ class JsonFormatter :
1515
}
1616

1717
JsonFormatter() :
18-
Formatter(),
19-
m_arrayStart(false),
20-
m_objectStart(false)
18+
Formatter()
2119
{
2220
}
2321

2422
JsonFormatter(_In_ const JsonFormatter& obj) :
2523
Formatter(obj),
26-
m_arrayStart(obj.m_arrayStart),
27-
m_objectStart(obj.m_objectStart),
28-
m_padding(obj.m_padding)
24+
m_padding(obj.m_padding),
25+
m_requiresSep(obj.m_requiresSep),
26+
m_objects(obj.m_objects)
2927
{
3028
}
3129

@@ -36,7 +34,7 @@ class JsonFormatter :
3634

3735
protected:
3836
void StartArray(_In_ Console& console) override;
39-
void StartObject(_In_ Console& console) override;
37+
void StartObject(_In_ Console& console, _In_opt_ const std::wstring& name = empty_wstring) override;
4038
void WriteProperty(_In_ Console& console, _In_ const std::wstring& name, _In_ const std::wstring& value) override;
4139
void WriteProperty(_In_ Console& console, _In_ const std::wstring& name, _In_ bool value) override;
4240
void WriteProperty(_In_ Console& console, _In_ const std::wstring& name, _In_ long long value) override;
@@ -61,7 +59,7 @@ class JsonFormatter :
6159
}
6260
}
6361

64-
bool m_arrayStart;
65-
bool m_objectStart;
6662
std::wstring m_padding;
63+
std::stack<bool> m_requiresSep;
64+
std::stack<std::wstring> m_objects;
6765
};

0 commit comments

Comments
 (0)