@@ -497,6 +497,11 @@ module json_value_module
497
497
generic,public :: swap = > json_value_swap
498
498
procedure :: json_value_swap
499
499
500
+ ! >
501
+ ! Check if a [[json_value]] is a child of another.
502
+ generic,public :: is_child_of = > json_value_is_child_of
503
+ procedure :: json_value_is_child_of
504
+
500
505
! >
501
506
! Throw an exception.
502
507
generic,public :: throw_exception = > MAYBEWRAP(json_throw_exception)
@@ -1299,19 +1304,15 @@ end subroutine json_value_remove
1299
1304
! [[json_value]] linked list (so the normal `parent`, `previous`,
1300
1305
! `next`, etc. pointers are properly associated if necessary).
1301
1306
!
1302
- ! @warning This should not be used to swap an element with one of its
1303
- ! direct children (along the first in the lists), since that would
1304
- ! produce a circular linkage. A check should be added for this...
1305
- ! Only the simple cases where p1/p2 or p2/p1 are parent/child
1306
- ! are currently checked.
1307
+ ! @warning This cannot be used to swap a parent/child pair, since that
1308
+ ! could lead to a circular linkage. An exception is thrown if
1309
+ ! this is tried.
1307
1310
!
1308
- ! @warning There are also other situations where using this routine would
1309
- ! produce a malformed JSON structure, such as swapping an array
1310
- ! with one of its children . This is not checked for.
1311
+ ! @warning There are also other situations where using this routine may
1312
+ ! produce a malformed JSON structure, such as moving an array
1313
+ ! element outside of an array . This is not checked for.
1311
1314
!
1312
1315
! @note If `p1` and `p2` have a common parent, it is always safe to swap them.
1313
- !
1314
- ! @warning This is a work-in-progress and has not yet been fully validated.
1315
1316
1316
1317
subroutine json_value_swap (json ,p1 ,p2 )
1317
1318
@@ -1333,18 +1334,18 @@ subroutine json_value_swap(json,p1,p2)
1333
1334
! aren't pointing to the same thing:
1334
1335
if (.not. associated (p1,p2)) then
1335
1336
1336
- ! TODO Need to check *all* the `children` pointers, so make sure
1337
- ! cases like p1%child%...%child => p2 don't occur...
1338
- if (associated (p1% parent,p2) .or. associated (p2% parent,p1)) then
1337
+ ! we will not allow swapping an item with one of its descendants:
1338
+ if (json% is_child_of(p1,p2) .or. json% is_child_of(p2,p1)) then
1339
1339
call json% throw_exception(' Error in json_value_swap: ' // &
1340
- ' cannot swap a parent/child pair ' )
1340
+ ' cannot swap an item with one of its descendants ' )
1341
1341
else
1342
1342
1343
1343
same_parent = ( associated (p1% parent) .and. &
1344
1344
associated (p2% parent) .and. &
1345
1345
associated (p1% parent,p2% parent) )
1346
1346
if (same_parent) then
1347
- ! if p1,p2 are the first,last or last,first children of a common parent
1347
+ ! if p1,p2 are the first,last or last,first
1348
+ ! children of a common parent
1348
1349
first_last = (associated (p1% parent% children,p1) .and. &
1349
1350
associated (p2% parent% tail,p2)) .or. &
1350
1351
(associated (p1% parent% tail,p1) .and. &
@@ -1447,6 +1448,48 @@ end subroutine swap_pointers
1447
1448
end subroutine json_value_swap
1448
1449
! *****************************************************************************************
1449
1450
1451
+ ! *****************************************************************************************
1452
+ ! > author: Jacob Williams
1453
+ ! date: 4/28/2016
1454
+ !
1455
+ ! Returns True if `p2` is a descendant of `p1`
1456
+ ! (i.e, a child, or a child of child, etc.)
1457
+
1458
+ function json_value_is_child_of (json ,p1 ,p2 ) result(is_child_of)
1459
+
1460
+ implicit none
1461
+
1462
+ class(json_core),intent (inout ) :: json
1463
+ type (json_value),pointer :: p1
1464
+ type (json_value),pointer :: p2
1465
+ logical :: is_child_of
1466
+
1467
+ is_child_of = .false.
1468
+
1469
+ if (associated (p1) .and. associated (p2)) then
1470
+ if (associated (p1% children)) then
1471
+ call json% traverse(p1% children,is_child_of_callback)
1472
+ end if
1473
+ end if
1474
+
1475
+ contains
1476
+
1477
+ subroutine is_child_of_callback (json ,p ,finished )
1478
+ ! ! Traverse until `p` is `p2`.
1479
+ implicit none
1480
+
1481
+ class(json_core),intent (inout ) :: json
1482
+ type (json_value),pointer ,intent (in ) :: p
1483
+ logical (LK),intent (out ) :: finished
1484
+
1485
+ is_child_of = associated (p,p2)
1486
+ finished = is_child_of ! stop searching if found
1487
+
1488
+ end subroutine is_child_of_callback
1489
+
1490
+ end function json_value_is_child_of
1491
+ ! *****************************************************************************************
1492
+
1450
1493
! *****************************************************************************************
1451
1494
! > author: Jacob Williams
1452
1495
! date: 12/6/2014
@@ -4155,6 +4198,11 @@ subroutine json_get_array(json, me, array_callback)
4155
4198
count = json% count (me)
4156
4199
element = > me% children
4157
4200
do i = 1 , count ! callback for each child
4201
+ if (.not. associated (element)) then
4202
+ call json% throw_exception(' Error in json_get_array: ' // &
4203
+ ' Malformed JSON linked list' )
4204
+ return
4205
+ end if
4158
4206
call array_callback(json, element, i, count)
4159
4207
element = > element% next
4160
4208
end do
@@ -4173,45 +4221,64 @@ end subroutine json_get_array
4173
4221
4174
4222
! *****************************************************************************************
4175
4223
! > author: Jacob Williams
4176
- ! date: 09/02/2015
4224
+ ! date: 4/28/2016
4177
4225
!
4178
4226
! Traverse a JSON structure.
4179
4227
! This routine calls the user-specified [[traverse_callback_func]]
4180
4228
! for each element of the structure.
4181
- !
4182
- recursive subroutine json_traverse (json ,me ,traverse_callback )
4229
+
4230
+ subroutine json_traverse (json ,me ,traverse_callback )
4183
4231
4184
4232
implicit none
4185
4233
4186
4234
class(json_core),intent (inout ) :: json
4187
4235
type (json_value),pointer ,intent (in ) :: me
4188
4236
procedure (traverse_callback_func) :: traverse_callback
4189
4237
4190
- type (json_value),pointer :: element ! ! a child element
4191
- integer (IK) :: i ! ! counter
4192
- integer (IK) :: icount ! ! number of children
4193
4238
logical (LK) :: finished ! ! can be used to stop the process
4194
4239
4195
- if (json% exception_thrown) return
4240
+ if (.not. json% exception_thrown) call traverse(me)
4196
4241
4197
- call traverse_callback(json,me,finished) ! first call for this object
4198
- if (finished) return
4242
+ contains
4199
4243
4200
- ! for arrays and objects, have to also call for all children:
4201
- if (me% var_type== json_array .or. me% var_type== json_object) then
4244
+ recursive subroutine traverse (p )
4245
+
4246
+ ! ! recursive [[json_value]] traversal.
4247
+
4248
+ implicit none
4249
+
4250
+ type (json_value),pointer ,intent (in ) :: p
4251
+
4252
+ type (json_value),pointer :: element ! ! a child element
4253
+ integer (IK) :: i ! ! counter
4254
+ integer (IK) :: icount ! ! number of children
4255
+
4256
+ if (json% exception_thrown) return
4257
+ call traverse_callback(json,p,finished) ! first call for this object
4258
+ if (finished) return
4259
+
4260
+ ! for arrays and objects, have to also call for all children:
4261
+ if (p% var_type== json_array .or. p% var_type== json_object) then
4262
+
4263
+ icount = json% count (p) ! number of children
4264
+ if (icount> 0 ) then
4265
+ element = > p% children ! first one
4266
+ do i = 1 , icount ! call for each child
4267
+ if (.not. associated (element)) then
4268
+ call json% throw_exception(' Error in json_traverse: ' // &
4269
+ ' Malformed JSON linked list' )
4270
+ return
4271
+ end if
4272
+ call traverse(element)
4273
+ if (finished) exit
4274
+ element = > element% next
4275
+ end do
4276
+ end if
4277
+ nullify(element)
4202
4278
4203
- icount = json% count (me) ! number of children
4204
- if (icount> 0 ) then
4205
- element = > me% children ! first one
4206
- do i = 1 , icount ! call for each child
4207
- call json% traverse(element,traverse_callback)
4208
- if (finished) exit
4209
- element = > element% next
4210
- end do
4211
4279
end if
4212
- nullify(element)
4213
4280
4214
- end if
4281
+ end subroutine traverse
4215
4282
4216
4283
end subroutine json_traverse
4217
4284
! *****************************************************************************************
0 commit comments