@@ -27,11 +27,13 @@ interface UtModelConstructorInterface {
2727 *
2828 * @param objectToModelCache cache used for the model construction with respect to stateBefore. For each object, it first
2929 * @param compositeModelStrategy decides whether we should construct a composite model for a certain value or not.
30+ * @param maxDepth determines max depth for composite and assemble model nesting
3031 * searches in [objectToModelCache] for [UtReferenceModel.id].
3132 */
3233class UtModelConstructor (
3334 private val objectToModelCache : IdentityHashMap <Any , UtModel >,
34- private val compositeModelStrategy : UtCompositeModelStrategy = AlwaysConstructStrategy
35+ private val compositeModelStrategy : UtCompositeModelStrategy = AlwaysConstructStrategy ,
36+ private val maxDepth : Long = DEFAULT_MAX_DEPTH
3537) : UtModelConstructorInterface {
3638 private val constructedObjects = IdentityHashMap <Any , UtModel >()
3739
@@ -42,6 +44,8 @@ class UtModelConstructor(
4244 .toMutableSet()
4345
4446 companion object {
47+ private const val DEFAULT_MAX_DEPTH = 7L
48+
4549 fun createOnlyUserClassesConstructor (pathsToUserClasses : Set <String >): UtModelConstructor {
4650 val cache = IdentityHashMap <Any , UtModel >()
4751 val strategy = ConstructOnlyUserClassesOrCachedObjectsStrategy (
@@ -83,7 +87,10 @@ class UtModelConstructor(
8387 *
8488 * Handles cache on stateBefore values.
8589 */
86- override fun construct (value : Any? , classId : ClassId ): UtModel {
90+ override fun construct (value : Any? , classId : ClassId ): UtModel =
91+ construct(value, classId, maxDepth)
92+
93+ private fun construct (value : Any? , classId : ClassId , remainingDepth : Long ): UtModel {
8794 objectToModelCache[value]?.let { model ->
8895 if (model is UtLambdaModel ) {
8996 return model
@@ -102,21 +109,21 @@ class UtModelConstructor(
102109 is Long ,
103110 is Float ,
104111 is Double ,
105- is Boolean -> if (classId.isPrimitive) UtPrimitiveModel (value) else constructFromAny(value)
106-
107- is ByteArray -> constructFromByteArray(value)
108- is ShortArray -> constructFromShortArray(value)
109- is CharArray -> constructFromCharArray(value)
110- is IntArray -> constructFromIntArray(value)
111- is LongArray -> constructFromLongArray(value)
112- is FloatArray -> constructFromFloatArray(value)
113- is DoubleArray -> constructFromDoubleArray(value)
114- is BooleanArray -> constructFromBooleanArray(value)
115- is Array <* > -> constructFromArray(value)
112+ is Boolean -> if (classId.isPrimitive) UtPrimitiveModel (value) else constructFromAny(value, remainingDepth )
113+
114+ is ByteArray -> constructFromByteArray(value, remainingDepth )
115+ is ShortArray -> constructFromShortArray(value, remainingDepth )
116+ is CharArray -> constructFromCharArray(value, remainingDepth )
117+ is IntArray -> constructFromIntArray(value, remainingDepth )
118+ is LongArray -> constructFromLongArray(value, remainingDepth )
119+ is FloatArray -> constructFromFloatArray(value, remainingDepth )
120+ is DoubleArray -> constructFromDoubleArray(value, remainingDepth )
121+ is BooleanArray -> constructFromBooleanArray(value, remainingDepth )
122+ is Array <* > -> constructFromArray(value, remainingDepth )
116123 is Enum <* > -> constructFromEnum(value)
117124 is Class <* > -> constructFromClass(value)
118125 is BaseStream <* , * > -> constructFromStream(value)
119- else -> constructFromAny(value)
126+ else -> constructFromAny(value, remainingDepth )
120127 }
121128 }
122129
@@ -136,109 +143,109 @@ class UtModelConstructor(
136143
137144 // Q: Is there a way to get rid of duplicated code?
138145
139- private fun constructFromDoubleArray (array : DoubleArray ): UtModel =
146+ private fun constructFromDoubleArray (array : DoubleArray , remainingDepth : Long ): UtModel =
140147 constructedObjects.getOrElse(array) {
141148 val stores = mutableMapOf<Int , UtModel >()
142149 val utModel =
143150 UtArrayModel (handleId(array), array::class .java.id, array.size, UtPrimitiveModel (0 .toDouble()), stores)
144151 constructedObjects[array] = utModel
145152 array.forEachIndexed { idx, value ->
146- stores[idx] = construct(value, doubleClassId)
153+ stores[idx] = construct(value, doubleClassId, remainingDepth - 1 )
147154 }
148155 utModel
149156 }
150157
151- private fun constructFromFloatArray (array : FloatArray ): UtModel =
158+ private fun constructFromFloatArray (array : FloatArray , remainingDepth : Long ): UtModel =
152159 constructedObjects.getOrElse(array) {
153160 val stores = mutableMapOf<Int , UtModel >()
154161 val utModel =
155162 UtArrayModel (handleId(array), array::class .java.id, array.size, UtPrimitiveModel (0 .toFloat()), stores)
156163 constructedObjects[array] = utModel
157164 array.forEachIndexed { idx, value ->
158- stores[idx] = construct(value, floatClassId)
165+ stores[idx] = construct(value, floatClassId, remainingDepth - 1 )
159166 }
160167 utModel
161168 }
162169
163- private fun constructFromLongArray (array : LongArray ): UtModel =
170+ private fun constructFromLongArray (array : LongArray , remainingDepth : Long ): UtModel =
164171 constructedObjects.getOrElse(array) {
165172 val stores = mutableMapOf<Int , UtModel >()
166173 val utModel =
167174 UtArrayModel (handleId(array), array::class .java.id, array.size, UtPrimitiveModel (0 .toLong()), stores)
168175 constructedObjects[array] = utModel
169176 array.forEachIndexed { idx, value ->
170- stores[idx] = construct(value, longClassId)
177+ stores[idx] = construct(value, longClassId, remainingDepth - 1 )
171178 }
172179 utModel
173180 }
174181
175- private fun constructFromIntArray (array : IntArray ): UtModel =
182+ private fun constructFromIntArray (array : IntArray , remainingDepth : Long ): UtModel =
176183 constructedObjects.getOrElse(array) {
177184 val stores = mutableMapOf<Int , UtModel >()
178185 val utModel = UtArrayModel (handleId(array), array::class .java.id, array.size, UtPrimitiveModel (0 ), stores)
179186 constructedObjects[array] = utModel
180187 array.forEachIndexed { idx, value ->
181- stores[idx] = construct(value, intClassId)
188+ stores[idx] = construct(value, intClassId, remainingDepth - 1 )
182189 }
183190 utModel
184191 }
185192
186- private fun constructFromCharArray (array : CharArray ): UtModel =
193+ private fun constructFromCharArray (array : CharArray , remainingDepth : Long ): UtModel =
187194 constructedObjects.getOrElse(array) {
188195 val stores = mutableMapOf<Int , UtModel >()
189196 val utModel =
190197 UtArrayModel (handleId(array), array::class .java.id, array.size, UtPrimitiveModel (0 .toChar()), stores)
191198 constructedObjects[array] = utModel
192199 array.forEachIndexed { idx, value ->
193- stores[idx] = construct(value, charClassId)
200+ stores[idx] = construct(value, charClassId, remainingDepth - 1 )
194201 }
195202 utModel
196203 }
197204
198- private fun constructFromShortArray (array : ShortArray ): UtModel =
205+ private fun constructFromShortArray (array : ShortArray , remainingDepth : Long ): UtModel =
199206 constructedObjects.getOrElse(array) {
200207 val stores = mutableMapOf<Int , UtModel >()
201208 val utModel =
202209 UtArrayModel (handleId(array), array::class .java.id, array.size, UtPrimitiveModel (0 .toShort()), stores)
203210 constructedObjects[array] = utModel
204211 array.forEachIndexed { idx, value ->
205- stores[idx] = construct(value, shortClassId)
212+ stores[idx] = construct(value, shortClassId, remainingDepth - 1 )
206213 }
207214 utModel
208215 }
209216
210- private fun constructFromByteArray (array : ByteArray ): UtModel =
217+ private fun constructFromByteArray (array : ByteArray , remainingDepth : Long ): UtModel =
211218 constructedObjects.getOrElse(array) {
212219 val stores = mutableMapOf<Int , UtModel >()
213220 val utModel =
214221 UtArrayModel (handleId(array), array::class .java.id, array.size, UtPrimitiveModel (0 .toByte()), stores)
215222 constructedObjects[array] = utModel
216223 array.forEachIndexed { idx, value ->
217- stores[idx] = construct(value, byteClassId)
224+ stores[idx] = construct(value, byteClassId, remainingDepth - 1 )
218225 }
219226 utModel
220227 }
221228
222- private fun constructFromBooleanArray (array : BooleanArray ): UtModel =
229+ private fun constructFromBooleanArray (array : BooleanArray , remainingDepth : Long ): UtModel =
223230 constructedObjects.getOrElse(array) {
224231 val stores = mutableMapOf<Int , UtModel >()
225232 val utModel =
226233 UtArrayModel (handleId(array), array::class .java.id, array.size, UtPrimitiveModel (false ), stores)
227234 constructedObjects[array] = utModel
228235 array.forEachIndexed { idx, value ->
229- stores[idx] = construct(value, booleanClassId)
236+ stores[idx] = construct(value, booleanClassId, remainingDepth - 1 )
230237 }
231238 utModel
232239 }
233240
234- private fun constructFromArray (array : Array <* >): UtModel =
241+ private fun constructFromArray (array : Array <* >, remainingDepth : Long ): UtModel =
235242 constructedObjects.getOrElse(array) {
236243 val stores = mutableMapOf<Int , UtModel >()
237244 val utModel =
238245 UtArrayModel (handleId(array), array::class .java.id, array.size, UtNullModel (objectClassId), stores)
239246 constructedObjects[array] = utModel
240247 array.forEachIndexed { idx, value ->
241- stores[idx] = construct(value, objectClassId)
248+ stores[idx] = construct(value, objectClassId, remainingDepth - 1 )
242249 }
243250 utModel
244251 }
@@ -276,20 +283,25 @@ class UtModelConstructor(
276283 /* *
277284 * First tries to construct UtAssembleModel. If failure, constructs UtCompositeModel.
278285 */
279- private fun constructFromAny (value : Any ): UtModel =
286+ private fun constructFromAny (value : Any , remainingDepth : Long ): UtModel =
280287 constructedObjects.getOrElse(value) {
281- tryConstructUtAssembleModel(value) ? : constructCompositeModel(value)
288+ tryConstructUtAssembleModel(value, remainingDepth ) ? : constructCompositeModel(value, remainingDepth )
282289 }
283290
284291 /* *
285292 * Constructs UtAssembleModel but does it only for predefined list of classes.
286293 *
287294 * Uses runtime class of an object.
288295 */
289- private fun tryConstructUtAssembleModel (value : Any ): UtModel ? =
296+ private fun tryConstructUtAssembleModel (value : Any , remainingDepth : Long ): UtModel ? =
290297 findUtAssembleModelConstructor(value::class .java.id)?.let { assembleConstructor ->
291298 try {
292- assembleConstructor.constructAssembleModel(this , value, valueToClassId(value), handleId(value)) {
299+ assembleConstructor.constructAssembleModel(
300+ internalConstructor = this .withMaxDepth(remainingDepth - 1 ),
301+ value = value,
302+ valueClassId = valueToClassId(value),
303+ id = handleId(value),
304+ ) {
293305 constructedObjects[value] = it
294306 }
295307 } catch (e: Exception ) { // If UtAssembleModel constructor failed, we need to remove model and return null
@@ -303,12 +315,12 @@ class UtModelConstructor(
303315 *
304316 * Uses runtime javaClass to collect ALL fields, except final static fields, and builds this model recursively.
305317 */
306- private fun constructCompositeModel (value : Any ): UtModel {
318+ private fun constructCompositeModel (value : Any , remainingDepth : Long ): UtModel {
307319 // value can be mock only if it was previously constructed from UtCompositeModel
308320 val isMock = objectToModelCache[value]?.isMockModel() ? : false
309321
310322 val javaClazz = if (isMock) objectToModelCache.getValue(value).classId.jClass else value::class .java
311- if (! compositeModelStrategy.shouldConstruct(value, javaClazz)) {
323+ if (remainingDepth <= 0 || ! compositeModelStrategy.shouldConstruct(value, javaClazz)) {
312324 return UtCompositeModel (
313325 handleId(value),
314326 javaClazz.id,
@@ -326,10 +338,15 @@ class UtModelConstructor(
326338 .asSequence()
327339 .filter { ! (Modifier .isFinal(it.modifiers) && Modifier .isStatic(it.modifiers)) } // TODO: what about static final fields?
328340 .filterNot { it.fieldId.isInaccessibleViaReflection }
329- .forEach { it.withAccessibility { fields[it.fieldId] = construct(it.get(value), it.type.id) } }
341+ .forEach { it.withAccessibility { fields[it.fieldId] = construct(it.get(value), it.type.id, remainingDepth - 1 ) } }
330342 }
331343 return utModel
332344 }
345+
346+ private fun withMaxDepth (newMaxDepth : Long ) = object : UtModelConstructorInterface {
347+ override fun construct (value : Any? , classId : ClassId ): UtModel =
348+ construct(value, classId, newMaxDepth)
349+ }
333350}
334351
335352/* *
0 commit comments