@@ -24,12 +24,14 @@ namespace uml4net.Reporting.Generators
24
24
using System . Collections . Generic ;
25
25
using System . Diagnostics ;
26
26
using System . IO ;
27
+ using System . IO . Packaging ;
27
28
using System . Linq ;
28
29
using System . Text ;
29
30
30
31
using Microsoft . Extensions . Logging ;
31
32
using Microsoft . Extensions . Logging . Abstractions ;
32
33
34
+ using uml4net . Classification ;
33
35
using uml4net . CommonStructure ;
34
36
using uml4net . Extensions ;
35
37
using uml4net . Packages ;
@@ -80,12 +82,66 @@ public string QueryReportType()
80
82
/// Returns a report of the classes of interest in the provided package
81
83
/// </returns>
82
84
public string Inspect ( IPackage package )
85
+ {
86
+ // Step 1: Map class to property variations
87
+ var classPropertyVariations = this . MapClassPropertyVariation ( package ) ;
88
+
89
+ // Step 2: Greedy algorithm to cover all property variations with the fewest classes
90
+ var result = this . ReduceClassPropertyVariationToInterestingClasses ( classPropertyVariations ) ;
91
+
92
+ var sb = new StringBuilder ( ) ;
93
+
94
+ sb . AppendLine ( $ "----- PACKAGE { package . Name } ANALYSIS ------") ;
95
+ sb . AppendLine ( "" ) ;
96
+
97
+ sb . AppendLine ( "" ) ;
98
+ sb . AppendLine ( "----- MULTIPLICITY RESULTS ------" ) ;
99
+ sb . AppendLine ( "" ) ;
100
+
101
+ var uniqueAndOrderedPropertyVariations = new HashSet < string > ( classPropertyVariations . Values . SelectMany ( p => p ) . ToList ( ) )
102
+ . OrderBy ( x => x ) . ToList ( ) ;
103
+
104
+ foreach ( var orderedPropertyVariation in uniqueAndOrderedPropertyVariations )
105
+ {
106
+ sb . AppendLine ( orderedPropertyVariation ) ;
107
+ }
108
+
109
+ sb . AppendLine ( "" ) ;
110
+ sb . AppendLine ( "----- INTERESTING CLASSES ------" ) ;
111
+ sb . AppendLine ( "" ) ;
112
+
113
+ var orderedClasses = result . OrderBy ( x => x . Name ) . ToList ( ) ;
114
+
115
+ foreach ( var @class in orderedClasses )
116
+ {
117
+ var isAbstract = "" ;
118
+ if ( @class . IsAbstract )
119
+ {
120
+ isAbstract = " [Abstract]" ;
121
+ }
122
+
123
+ sb . AppendLine ( $ "class : { @class . QueryQualifiedName ( ) } { isAbstract } ") ;
124
+ }
125
+
126
+ return sb . ToString ( ) ;
127
+ }
128
+
129
+ /// <summary>
130
+ /// Map class to property variations
131
+ /// </summary>
132
+ /// <param name="package">
133
+ /// The <see cref="IPackage"/> that needs to be inspected
134
+ /// </param>
135
+ /// <returns>
136
+ /// a Dictionary of <see cref="IClass"/> and associated <see cref="HashSet{T}"/> of property
137
+ /// variations for that class
138
+ /// </returns>
139
+ private Dictionary < IClass , HashSet < string > > MapClassPropertyVariation ( IPackage package )
83
140
{
84
141
var classPropertyVariations = new Dictionary < IClass , HashSet < string > > ( ) ;
85
142
86
143
var classes = package . QueryPackages ( ) . SelectMany ( x => x . PackagedElement . OfType < IClass > ( ) ) . ToList ( ) ;
87
144
88
- // Step 1: Map class to property variations
89
145
foreach ( var @class in classes )
90
146
{
91
147
var propertyVariations = new HashSet < string > ( ) ;
@@ -164,64 +220,72 @@ public string Inspect(IPackage package)
164
220
classPropertyVariations . Add ( @class , propertyVariations ) ;
165
221
}
166
222
223
+ return classPropertyVariations ;
224
+ }
225
+
226
+ /// <summary>
227
+ /// Reduces the <see cref="IClass"/>> and property variations in a greedy fashion such
228
+ /// that the least amount of classes is returned
229
+ /// </summary>
230
+ /// <param name="classPropertyVariations">
231
+ /// The <see cref="IClass"/>> and property variations in a greedy fashion such that needs to
232
+ /// be reduced.
233
+ /// </param>
234
+ /// <returns>
235
+ /// a reduced set of <see cref="IClass"/> and <see cref="IProperty"/> variations.
236
+ /// </returns>
237
+ private IReadOnlyList < IClass > ReduceClassPropertyVariationToInterestingClasses ( Dictionary < IClass , HashSet < string > > classPropertyVariations )
238
+ {
239
+ var dictionaryClone = new Dictionary < IClass , HashSet < string > > ( classPropertyVariations ) ;
240
+
167
241
// Step 2: Get all unique property variations
168
- var allPropertyVariations = new HashSet < string > ( classPropertyVariations . Values . SelectMany ( p => p ) ) ;
242
+ var propertyVariations = dictionaryClone . Values . SelectMany ( p => p ) . ToList ( ) ;
169
243
170
- // Step 3: Greedy algorithm to cover all property variations with fewest classes
244
+ var uniquePropertyVariations = new HashSet < string > ( propertyVariations ) ;
245
+
246
+ // Step 3: Greedy algorithm to cover all property variations with the fewest classes
171
247
var result = new List < IClass > ( ) ;
172
248
var covered = new HashSet < string > ( ) ;
173
249
174
- while ( covered . Count < allPropertyVariations . Count )
250
+ while ( covered . Count < uniquePropertyVariations . Count )
175
251
{
176
252
// Pick the class that contributes the most uncovered properties
177
- var bestClass = classPropertyVariations
253
+ var bestClass = dictionaryClone
178
254
. OrderByDescending ( kvp => kvp . Value . Count ( p => ! covered . Contains ( p ) ) )
179
255
. First ( ) . Key ;
180
256
181
257
result . Add ( bestClass ) ;
182
258
183
- foreach ( var prop in classPropertyVariations [ bestClass ] )
259
+ foreach ( var prop in dictionaryClone [ bestClass ] )
184
260
covered . Add ( prop ) ;
185
261
186
- classPropertyVariations . Remove ( bestClass ) ; // avoid reusing the same class
187
- }
188
-
189
- var sw = Stopwatch . StartNew ( ) ;
190
-
191
- var sb = new StringBuilder ( ) ;
192
-
193
- sb . AppendLine ( $ "----- PACKAGE { package . Name } ANALYSIS ------") ;
194
- sb . AppendLine ( "" ) ;
195
-
196
- sb . AppendLine ( "" ) ;
197
- sb . AppendLine ( "----- MULTIPLICITY RESULTS ------" ) ;
198
- sb . AppendLine ( "" ) ;
199
-
200
- var orderedPropertyVariations = allPropertyVariations . OrderBy ( x => x ) . ToList ( ) ;
201
-
202
- foreach ( var orderedPropertyVariation in orderedPropertyVariations )
203
- {
204
- sb . AppendLine ( orderedPropertyVariation ) ;
262
+ dictionaryClone . Remove ( bestClass ) ; // avoid reusing the same class
205
263
}
206
264
207
- sb . AppendLine ( "" ) ;
208
- sb . AppendLine ( "----- INTERESTING CLASSES ------" ) ;
209
- sb . AppendLine ( "" ) ;
210
-
211
- var orderedClasses = result . OrderBy ( x => x . Name ) . ToList ( ) ;
265
+ return result ;
266
+ }
212
267
213
- foreach ( var @class in orderedClasses )
214
- {
215
- var isAbstract = "" ;
216
- if ( @class . IsAbstract )
217
- {
218
- isAbstract = " [Abstract]" ;
219
- }
268
+ /// <summary>
269
+ /// Inspect the content of the provided <see cref="IPackage"/> and returns a
270
+ /// read-only collection of interesting <see cref="IClass"/>
271
+ /// </summary>
272
+ /// <param name="package">
273
+ /// The <see cref="IPackage"/> that needs to be inspected
274
+ /// </param>
275
+ /// <returns>
276
+ /// A read-only collection of interesting <see cref="IClass"/> that cover the variations
277
+ /// of <see cref="IProperty"/>> and <see cref="IOperation"/> variations
278
+ /// </returns>
279
+ public IReadOnlyCollection < IClass > QueryInterestingClasses ( IPackage package )
280
+ {
281
+ // Step 1: Map class to property variations
282
+ var classPropertyVariations = this . MapClassPropertyVariation ( package ) ;
220
283
221
- sb . AppendLine ( $ "class : { @class . QueryQualifiedName ( ) } { isAbstract } ") ;
222
- }
284
+ // Step 2: Greedy algorithm to cover all property variations with the fewest classes
285
+ var result = this . ReduceClassPropertyVariationToInterestingClasses ( classPropertyVariations )
286
+ . OrderBy ( x => x . Name ) . ToList ( ) ;
223
287
224
- return sb . ToString ( ) ;
288
+ return result ;
225
289
}
226
290
227
291
/// <summary>
0 commit comments