Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/persistence.lisp
| Kind | Covered | All | % |
| expression | 340 | 1480 | 23.0 |
| branch | 42 | 182 | 23.1 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.implementation; -*-
3
(in-package :org.datagraph.spocq.implementation)
5
(:documentation "persistant classes"
6
"Mediate data between a persistent store and an application or user interface
7
by interposing instances which combine a projection of the persistent state
8
with access logic, to stage the data for application processing or for presentation and
9
to render changes back to the store.
10
Instance state is held as slot values where each slot is associated with predicates from the store.
11
The state is exchanged with the store as graph field deltas -- added and removed triple statements.
12
The initial state arrives from the store as a complete field and updates take the form of paired
13
triple sets - one additions and one deletions. The additions are used to reinitialize the instance
14
and the delete/add pait is merged with the existing cache.
16
At the clos/store interface quad field statements are read the form (c s p o),
17
but written as (|quad| s p o c), as those are the current library APIs.
18
At the presentation interface, the statement encoding is as triples in the form (s p o) in both directions.
19
In order to permit simpler login in the application model, some value carrry special meaning:
21
presentation clos store
23
----------------------------------------------------------------------
25
false nil false^^boolean
27
Deletion is specified with the explicit deletion set.
32
(defparameter *decode-presentation-graph.mode* :eval)
33
(defparameter *decode-presentation-graph.verbose* nil)
34
(defparameter *decode-store-graph.mode* :eval)
37
(defparameter *persistence-context* (make-registry :weakness :value :test 'equalp)
38
"Serves as the context to identify instances known to the UI and store interfaces in terms of
39
a subject iri. The operators which compute or modified instances from a field use this to resolve
40
cross-references against existing instances. They cache all newly constructed result, but It is weak in
41
terms of the value, such that once a value is no longer reachable, it will be released.")
45
(defclass identified-class (standard-class)
47
(:documentation "declare support for instantiation protocols where the initargs
48
include an identifier."))
50
(defclass cached-class (identified-class)
52
:initform (make-registry :weakness :value :test 'equalp)
54
(:documentation "provide support to cache instances based on identifier."))
56
(defclass persistent-class (identified-class)
59
:reader get-class-persistent-slots :writer setf-class-persistent-slots))
61
"The persistent-class meta-class provides the slot meta-data to correlate state with statements
62
based on the predicate terms. It specializes its definitions wth persistent-class-slot-definition
63
to include a store property for exchanges with the store and encode and decode properties for
64
echanges with the ui."))
66
(defclass cached-persistent-class (cached-class persistent-class)
68
(:documentation "combine support for cached and persistent instances."))
71
(eval-when (#+lispworks :compile-toplevel :load-toplevel :execute)
72
(defmethod c2mop:validate-superclass ((subclass cached-class)
73
(superclass standard-class))
75
(defmethod c2mop:validate-superclass ((subclass persistent-class)
76
(superclass standard-class))
78
;; standard subclasses to permit specialized forms as caches
79
(defmethod c2mop:validate-superclass ((subclass standard-class)
80
(superclass persistent-class))
82
(defmethod c2mop:validate-superclass ((subclass cached-persistent-class)
83
(superclass standard-class))
85
(defmethod c2mop:validate-superclass ((subclass cached-persistent-class)
86
(superclass cached-class))
88
(defmethod c2mop:validate-superclass ((subclass cached-persistent-class)
89
(superclass persistent-class))
94
(defclass identified-object ()
96
;; :initarg :id this is used for things like repository <account>/<name> id values
98
;; no initform: initialize-instance computes it as required
99
:reader object-identifier :reader instance-identifier
100
:writer setf-instance-identifier
102
"The global internal identifier for site-global persistent metadata.
103
The site can be a single host, in which case the value would be identical to a resource
104
uri, or it can, instead, reflect an installation name, in which case a
105
value is shared across all hosts which constitute the site.
106
The id combines the installation site, account, and repository names and is unique within
107
the installation. This may not always identical with either the
108
repository-uri, as that would be specific to an individual host within a multi-host
109
installation, or with any request uri which identifies the object as a resource to an http
110
server as those things reflect the namespaces of the request interface rather than the internal
111
namespace managed by the store.
112
It is used for comparisons to persisted and autheorization data.
113
If the value is not provided at initialization, it is computed for the
114
instance on demand."))
116
"The abstract root class for for data which can be associated with a persistent resource binds just
117
the identifier, but none of the meta-data to manager persistence."))
119
(deftype persistent-object-state () '(member :clean :deleted :dirty :hollow :new :detached))
121
(defclass persistent-object (identified-object)
123
:initform :hollow :accessor instance-state
124
:type persistent-object-state
126
"Indicates the state of the resource instance wrt the last merge from store.
127
Allows one of :hollow, :dirty :clean :new :deleted nil
128
If set to nil, implicit read/write operations for synchronization are suppressed")
130
:initform nil :initarg :store-repository-id
131
:reader get-resource-store-repository-id :writer (setf resource-store-repository-id)
133
"Specify the store which backs the instance.")
135
:initform nil :accessor instance-store-revision
136
:type (or null integer)
138
"Indicates the state of the store itself, in the form of the revision identifier
139
current for the instance's repository at the point where the instance was last read.")
141
:initform nil :reader get-instance-store-graph
142
:writer setf-instance-store-graph
144
"Caches the resource-specific graph as present in the field which was merged from the store
145
when reading the resource or written to the store when updating."))
146
(:metaclass persistent-class))
148
(defclass persistent-graph-object (persistent-object)
150
(:metaclass persistent-class))
151
(defclass persistent-graph-subject-object (persistent-object)
153
(:metaclass persistent-class))
154
(defclass persistent-subject-object (persistent-object)
156
(:metaclass persistent-class))
159
(defclass cached-object (identified-object)
161
(:metaclass cached-class))
163
(defclass cached-persistent-object (cached-object persistent-object)
165
(:metaclass cached-persistent-class))
167
(defclass cached-persistent-graph-object (persistent-graph-object cached-persistent-object)
169
(:metaclass persistent-class))
170
(defclass cached-persistent-graph-subject-object (persistent-graph-subject-object cached-persistent-object)
172
(:metaclass persistent-class))
173
(defclass cached-persistent-subject-object (persistent-subject-object cached-persistent-object)
175
(:metaclass persistent-class))
178
(defclass described-object (persistent-object)
181
#+(or) '(|acl|:|accessTo| |acl|:|agent| |acl|:|agentClass| |acl|:|mode| |rdf|:|type| |sioc|:|ip_address|)
183
:reader instance-predicates))
184
(:metaclass persistent-class)
186
"A described object is a persistent object for which the fields exchanged with the store constitute
187
(excerpts from) a concise bounded description, rather than just a sequence of statements with a common
189
It adds the predicate slot which, if bound to a set of properties, will
190
limit the description to those properties."))
193
(defclass persistent-class-slot-definition ()
195
:initform nil :initarg :store-property
196
:accessor slot-definition-store-property
197
:documentation "The statement property recognized when decoding a graph and
198
generated when encoding a graph for reading and committing to the store.")
199
(encode-presentation-property
200
:initform nil :initarg :encode-presentation-property
201
:accessor slot-definition-encode-presentation-property
202
:documentation "The statement property generated when encoding a graph for
204
(decode-presentation-property
205
:initform nil :initarg :decode-presentation-property
206
:accessor slot-definition-decode-presentation-property
207
:documentation "The statement property recognized when decoding a graph from
209
(:documentation "Mix-in additional slot attributes to specify the property terms respective
210
the given slot for data exchanges with the store and with the ui."))
212
(defmethod slot-definition-store-property ((sd t)) nil)
213
(defmethod slot-definition-encode-presentation-property ((sd t)) nil)
214
(defmethod slot-definition-decode-presentation-property ((sd t)) nil)
216
(defun persistent-class-slot-definition-p (datum)
217
(typep datum 'persistent-class-slot-definition))
219
(defclass persistent-class-direct-slot-definition (persistent-class-slot-definition
220
c2mop:standard-direct-slot-definition)
222
(:documentation "Mix persistent slot attributes into direct slot definitions."))
224
(defclass persistent-class-effective-slot-definition (persistent-class-slot-definition
225
c2mop:standard-effective-slot-definition)
226
((writer :accessor slot-definition-writer)
227
(reader :accessor slot-definition-reader))
228
(:documentation "Mix persistent slot attributes into effective slot definitions.
229
Add cached values for a single reader and a single writer functions for use when enxchanging
230
adaa with the store."))
232
(defmethod c2mop:direct-slot-definition-class ((class persistent-class) &key &allow-other-keys)
233
'persistent-class-direct-slot-definition)
235
(defmethod c2mop:effective-slot-definition-class ((class persistent-class) &key property store-property
236
encode-presentation-property decode-presentation-property
238
"Make specialization contingent on the presence of requisite initargs."
241
encode-presentation-property
242
decode-presentation-property)
243
'persistent-class-effective-slot-definition
247
(defmethod sb-pcl::compute-effective-slot-definition-initargs ((class persistent-class) direct-sds)
248
(let ((store-property (some #'slot-definition-store-property direct-sds))
249
(encode-presentation-property (some #'slot-definition-encode-presentation-property direct-sds))
250
(decode-presentation-property (some #'slot-definition-decode-presentation-property direct-sds)))
251
(append (when store-property `(:store-property ,store-property))
252
(when encode-presentation-property `(:encode-presentation-property ,encode-presentation-property))
253
(when decode-presentation-property `(:decode-presentation-property ,decode-presentation-property))
254
(call-next-method))))
259
(defmethod initialize-instance ((instance persistent-class-direct-slot-definition) &rest initargs
261
(store-property property)
262
(presentation-property property)
263
(encode-presentation-property presentation-property)
264
(decode-presentation-property presentation-property))
265
(apply #'call-next-method instance
266
:store-property store-property
267
:encode-presentation-property encode-presentation-property
268
:decode-presentation-property decode-presentation-property
272
(defmethod c2mop:compute-effective-slot-definition ((class persistent-class) (slot-name t) direct-slot-definitions)
273
(let ((sd (call-next-method)))
274
(when (typep sd 'persistent-class-slot-definition)
275
;; rdf-specific slot definition fields
276
(setf (slot-definition-store-property sd) (some #'slot-definition-store-property direct-slot-definitions))
277
(setf (slot-definition-encode-presentation-property sd) (some #'slot-definition-encode-presentation-property direct-slot-definitions))
278
(setf (slot-definition-decode-presentation-property sd) (some #'slot-definition-decode-presentation-property direct-slot-definitions))
279
;; cache one reader/writer; leave in symbolic form as the functions may not have yet been defined
280
(setf (slot-definition-reader sd) (first (some #'c2mop:slot-definition-readers direct-slot-definitions)))
281
(setf (slot-definition-writer sd) (first (some #'c2mop:slot-definition-writers direct-slot-definitions))))
284
(defgeneric compute-instance-identifier (instance)
285
(:method ((instance identified-object))
286
"the default method knows no identifier"
289
(defmethod initialize-instance ((instance identified-object) &key)
291
(unless (cond ((slot-boundp instance 'identifier)
292
(with-slots (identifier) instance
295
(string (setf-instance-identifier (intern-iri identifier) instance))
297
(t (log-warn "anomalous identifier: ~a: ~s"
298
(with-output-to-string (stream) (print-unreadable-object (instance stream :identity t :type t)))
301
(setf-instance-identifier (compute-instance-identifier instance) instance)))
305
(defgeneric make-persistent-instance (class &rest initargs)
306
(:documentation "make a new persistent insance by delegating to make-instance.
307
this in order to ensure that any default values and initial state is bound.")
308
(:method ((class symbol) &rest args)
309
(declare (dynamic-extent args))
310
(apply #'make-persistent-instance (find-class class) args))
311
(:method ((class persistent-class) &rest args)
312
(declare (dynamic-extent args))
313
(apply #'make-instance class args)))
315
(defgeneric make-synchronized-instance (class &rest initargs)
316
(:documentation "Create a new instance of the given class and read the
317
initial state from the store. The base constructor is make- variant, rather
318
than an allocate-variant")
319
(:method ((class t) &rest initargs)
320
(declare (dynamic-extent initargs))
321
(read-resource (apply #'make-persistent-instance class initargs))))
323
(defgeneric instance-graph-identifier (resource)
324
(:method ((instance identified-object))
325
(instance-identifier instance)))
327
(defgeneric instance-repository-id (persistent-instance)
328
(:method ((instance persistent-object))
329
(resource-store-repository-id instance)))
331
(defgeneric instance-repository (persistent-instance)
332
(:method ((instance persistent-object))
333
(repository (instance-repository-id instance))))
335
(defgeneric resource-store-repository-id (persistent-instance)
336
(:method ((instance persistent-object))
337
(or (get-resource-store-repository-id instance)
338
(setf (resource-store-repository-id instance)
339
(compute-resource-store-repository-id instance)))))
341
(defgeneric compute-resource-store-repository-id (persistent-instance)
343
"Compute the persistent store for a new persistent instance from its state.
344
This allows an instance-specific, inherent repository for backing store."))
346
(defgeneric compute-class-instance-identifier (class initargs)
347
(:method ((class class) (initargs list))
348
(or (getf initargs :identifier)
349
(getf initargs :id))))
351
(defgeneric ensure-instance (class &rest initargs)
352
(:method ((designator symbol) &rest initargs)
353
(declare (dynamic-extent initargs))
354
(apply #'ensure-instance (find-class designator) initargs))
355
(:method ((class class) &rest initargs)
356
(declare (dynamic-extent initargs))
357
(apply #'make-instance class initargs))
358
(:method ((class cached-class) &rest initargs)
359
(declare (dynamic-extent initargs))
360
(let* ((identifier (compute-class-instance-identifier class initargs))
361
(existing-instance (get-registry identifier (class-cache class))))
362
(cond (existing-instance
363
(unless (eq (type-of existing-instance) (class-name class))
364
(change-class existing-instance class))
366
(apply #'reinitialize-instance existing-instance initargs)
370
(apply #'make-instance class initargs))))))
372
(defmethod make-instance ((class cached-class) &key)
373
"Register each new instance - replacing any existing one."
374
(register-instance class (call-next-method)))
376
(defgeneric register-instance (context instance)
377
(:documentation "register the given instance relative to the context class -
378
likely the class hierarchy and returnt the instance.")
379
(:method ((class cached-class) (instance cached-object))
380
(setf (get-registry (instance-identifier instance) (class-cache class))
383
(defgeneric unregister-instance (context instance)
384
(:documentation "register the given instance relative to the context class -
385
likely the class hierarchy and returnt the instance.")
386
(:method ((class cached-class) (instance cached-object))
387
(rem-registry (instance-identifier instance) (class-cache class))))
390
;;; administer instance state wrt the store
392
(defgeneric resource-clean-p (resource)
393
(:method ((resource persistent-object))
394
(eq (instance-state resource) :clean)))
396
(defgeneric resource-deleted-p (resource)
398
"Return true if the instance has been marked for deletion since the last store synchronization.")
399
(:method ((resource persistent-object))
400
(eq (instance-state resource) :deleted)))
402
(defgeneric resource-detached-p (resource)
404
"Return true if the instance has been detached from the store.
405
This means that i remains as is, independent store changes")
406
(:method ((resource persistent-object))
407
(eq (instance-state resource) :detached)))
409
(defgeneric resource-dirty-p (resource)
411
"Return true if the instance's attributes have been modified since the last store synchronization.")
412
(:method ((resource persistent-object))
413
(eq (instance-state resource) :dirty)))
415
(defgeneric resource-hollow-p (resource)
417
"Return true if the instance has been neither synchronized nor modified.")
418
(:method ((resource persistent-object))
419
(eq (instance-state resource) :hollow)))
421
(defgeneric resource-new-p (resource)
423
"Return true if the resource contains data which has never been persisted. That is, it has been
424
created and modified, but it is not present in the store, as it is not yet synchronized.")
425
(:method ((resource persistent-object))
426
(eq (instance-state resource) :new)))
428
(defgeneric resource-synchronized-p (resource)
430
"Return true if the resource state in unmodified since the latest merge to-or-from the store.
431
The reference revision is the store repository's last write revision,")
432
(:method ((resource t))
434
(:method ((resource persistent-object))
435
(case (instance-state resource)
437
(let ((instance-revision (instance-store-revision resource))
438
(repository-revision (repository-last-revision (instance-repository-id resource))))
439
(values (equalp instance-revision repository-revision) repository-revision)))
448
(:documentation "codec operators"
449
"manage a class' metadata about persistent slots and coding/decoding operators by checking
450
slot definition class and the function signature and the presence of the name
451
in the respective package for presenation and store operators.")
453
(defgeneric presentation-reader-p (function)
454
(:method ((method standard-method))
455
(presentation-reader-p (c2mop:method-generic-function method)))
456
(:method ((function standard-generic-function))
457
(and (= 1 (length (c2mop:generic-function-lambda-list function)))
458
(presentation-reader-p (c2mop:generic-function-name function))))
459
(:method ((name cons))
461
(:method ((name symbol))
462
(find-symbol (symbol-name name) *presentation-codec-package*)))
464
(defgeneric presentation-writer-p (function)
465
(:method ((method standard-method))
466
(presentation-writer-p (c2mop:method-generic-function method)))
467
(:method ((function standard-generic-function))
468
(and (= 2 (length (c2mop:generic-function-lambda-list function)))
469
(presentation-writer-p (c2mop:generic-function-name function))))
470
(:method ((name cons))
471
(and (eq (first name) 'setf) (presentation-writer-p (second name))))
472
(:method ((name symbol))
473
(find-symbol (symbol-name name) *presentation-codec-package*)))
476
(defgeneric store-reader-p (function)
477
(:method ((method standard-method))
478
(store-reader-p (c2mop:method-generic-function method)))
479
(:method ((function standard-generic-function))
480
(and (= 1 (length (c2mop:generic-function-lambda-list function)))
481
(store-reader-p (c2mop:generic-function-name function))))
482
(:method ((name cons))
484
(:method ((name symbol))
485
(find-symbol (symbol-name name) *store-codec-package*)))
487
(defgeneric store-writer-p (function)
488
(:method ((method standard-method))
489
(store-writer-p (c2mop:method-generic-function method)))
490
(:method ((function standard-generic-function))
491
(and (= 2 (length (c2mop:generic-function-lambda-list function)))
492
(store-writer-p (c2mop:generic-function-name function))))
493
(:method ((name cons))
494
(and (eq (first name) 'setf) (store-writer-p (second name))))
495
(:method ((name symbol))
496
(find-symbol (symbol-name name) *store-codec-package*)))
499
(defgeneric method-property (method)
500
(:method ((method standard-method))
501
(method-property (c2mop:method-generic-function method)))
502
(:method ((function standard-generic-function))
503
(method-property (c2mop:generic-function-name function)))
504
(:method ((name cons))
506
(:method ((name symbol))
510
(defgeneric class-presentation-readers (object)
511
(:method ((object persistent-object))
512
(class-presentation-readers (class-of object)))
513
(:method ((class persistent-class))
514
(if (slot-boundp class 'presentation-readers)
515
(slot-value class 'presentation-readers)
516
(setf (slot-value class 'presentation-readers)
517
(remove-duplicates (loop for class in (c2mop:class-precedence-list class)
518
append (loop for method in (c2mop:specializer-direct-methods class)
519
for function = (c2mop:method-generic-function method)
520
when (presentation-reader-p function)
521
collect function)))))))
523
(defgeneric class-presentation-writers (object)
524
(:method ((object persistent-object))
525
(class-presentation-writers (class-of object)))
526
(:method ((class standard-class))
527
(if (slot-boundp class 'presentation-writers)
528
(slot-value class 'presentation-writers)
529
(setf (slot-value class 'presentation-writers)
530
(remove-duplicates (loop for class in (c2mop:class-precedence-list class)
531
append (loop for method in (c2mop:specializer-direct-methods class)
532
for function = (c2mop:method-generic-function method)
533
when (presentation-writer-p function)
534
collect function)))))))
536
(defgeneric class-store-readers (object)
537
(:method ((object persistent-object))
538
(class-store-readers (class-of object)))
539
(:method ((class persistent-class))
540
(if (slot-boundp class 'store-readers)
541
(slot-value class 'store-readers)
542
(setf (slot-value class 'store-readers)
543
(remove-duplicates (loop for class in (c2mop:class-precedence-list class)
544
append (loop for method in (c2mop:specializer-direct-methods class)
545
for function = (c2mop:method-generic-function method)
546
when (store-reader-p function)
547
collect function)))))))
549
(defgeneric class-store-writers (object)
550
(:method ((object persistent-object))
551
(class-store-writers (class-of object)))
552
(:method ((class standard-class))
553
(if (slot-boundp class 'store-writers)
554
(slot-value class 'store-writers)
555
(setf (slot-value class 'store-writers)
556
(remove-duplicates (loop for class in (c2mop:class-precedence-list class)
557
append (loop for method in (c2mop:specializer-direct-methods class)
558
for function = (c2mop:method-generic-function method)
559
when (store-writer-p function)
560
collect function)))))))
562
(defgeneric class-persistent-slots (class)
563
(:method ((class persistent-class))
564
(or (get-class-persistent-slots class)
565
(setf-class-persistent-slots (compute-class-persistent-slots class) class)))
567
(:method ((object persistent-object))
568
(class-persistent-slots (class-of object))))
571
(defgeneric compute-class-persistent-slots (class)
572
(:method ((class persistent-class))
573
(loop for sd in (c2mop:class-slots (c2mop:ensure-finalized class))
574
when (persistent-class-slot-definition-p sd)
579
;;; the graph and presentation implementation are identical at the code, but
580
;;; constrain based on different packages and would generate different
581
;;; implementation for the respective projections
584
(defgeneric encode-presentation (resource)
585
(:documentation "convert a resource into the respective statement sequence
586
for projection as a request response. Govern the constitution according to
587
the readers visible in the 'presentation-codec' package.
588
The base method either interprets, or if the class supports compilation,
589
generates a class specific method and delegates to it. (NYI)")
591
(:method ((resource standard-object))
592
(let* ((class (class-of resource))
593
(slot-definitions (class-persistent-slots class))
594
(identifier (instance-identifier resource)))
595
(loop for sd in slot-definitions
596
for reader = (some #'presentation-reader-p (c2mop:slot-definition-readers sd))
598
collect `(,identifier
599
,(some #'iri-p (c2mop:slot-definition-initargs sd))
600
,(funcall reader resource))))))
602
(defgeneric encode-presentation-graph (resource)
603
(:documentation "convert a resource into the respective statement sequence
604
for projection as a request response. Govern the constitution according to
605
the readers visible in the 'presentation-codec' package.
606
The base method either interprets, or if the class supports compilation,
607
generates a class specific method and delegates to it. (NYI)")
609
(:method ((resource standard-object))
610
(let* ((identifier (instance-identifier resource))
611
(slot-definitions (class-persistent-slots (class-of resource))))
612
(loop for sd in slot-definitions
613
for reader = (slot-definition-reader sd)
614
for property = (slot-definition-encode-presentation-property sd)
615
for boundp = (slot-boundp resource (c2mop:slot-definition-name sd))
616
;; do (print (list reader property boundp))
617
when (and reader property boundp)
618
collect (let ((object (funcall (fdefinition reader) resource)))
619
`(,identifier ,property ,(case object
621
((nil) spocq.a:|false|)
626
(defgeneric decode-presentation-graph (resource graph)
627
(:documentation "Given a resource and the intended content encoded as a graph
628
- that is, as a list of statements, project the respective statement value
629
into the resource. Govern the operation according to
630
the writers visible in the 'presentation-codec' package.
631
The base method either interprets, or if the class supports compilation,
632
generates a class specific method and delegates to it. (NYI)")
634
(:method ((resource persistent-object) (graph list))
635
(ecase *decode-presentation-graph.mode*
637
(let* ((identifier (instance-identifier resource))
638
(slot-definitions (class-persistent-slots (class-of resource)))
639
(new-state (instance-state resource)))
640
(loop for (subject predicate object) in graph ; presentation produces a triple field
641
for sd = (find predicate slot-definitions :key #'slot-definition-decode-presentation-property)
642
for writer = (when sd (slot-definition-writer sd))
643
when (and writer (equalp subject identifier))
644
do (progn (when (equal object "") (setf object nil))
645
;; rdf:nil remains as the indication to unbind
646
(when *decode-presentation-graph.verbose*
647
(format *trace-output* "~%d-p-g: ~a: ~s: ~s ~s"
648
identifier writer predicate object))
649
(funcall (fdefinition writer) object resource)
650
;; account for detached state
651
(when new-state (setf new-state :dirty))))
652
(when (and *decode-presentation-graph.verbose* (not (eq new-state :dirty)))
653
(warn "no change to resource: ~s ~%~s" resource graph))
654
(setf (instance-state resource) new-state)
657
(let* ((slot-definitions (class-persistent-slots (class-of resource)))
658
(method-lambda `(lambda (resource graph)
659
(let ((identifier (instance-identifier resource))
660
(new-state (instance-state resource)))
661
(loop for (subject predicate object) in graph
662
when (equalp subject identifier)
664
,@(loop for sd in slot-definitions
665
for property = (slot-definition-decode-presentation-property sd)
666
for writer = (slot-definition-writer sd)
667
when (and property writer)
668
collect `(,property (when new-state (setf new-state :dirty))
669
(funcall ,(fdefinition writer) object resource)))))
670
(setf (instance-state resource) new-state))
672
(eval `(defmethod decode-presentation-graph ((resource ,(type-of resource)) (graph list))
673
,@(cddr method-lambda)))
674
(decode-presentation-graph resource graph))))))
678
(defgeneric encode-persistent-graph (resource)
679
(:documentation "Convert a resource into the respective statement sequence
680
for projection as a request response. Govern the constitution according to
681
the readers visible in the 'persistence-codec' package.
682
The base method either interprets, or if the class supports compilation,
683
generates a class specific method and delegates to it. (NYI)")
685
(:method ((resource persistent-object))
686
(let* ((class (class-of resource))
687
(slot-definitions (class-persistent-slots class))
688
(identifier (instance-identifier resource)))
689
(loop for sd in slot-definitions
690
for reader = (some #'store-reader-p (c2mop:slot-definition-readers sd))
692
collect `(,identifier
693
,(some #'iri-p (c2mop:slot-definition-initargs sd))
694
,(funcall reader resource))))))
696
(defgeneric (setf instance-store-graph) (graph resource)
697
(:method ((graph list) (resource persistent-object))
698
(setf (instance-state resource)
699
(ecase (instance-state resource)
700
((:clean :deleted :dirty) :dirty)
701
;; it is hollow, that this operator presumes a new instance
702
((:hollow :new) :new)
703
(:detached :detached)))
704
(setf-instance-store-graph graph resource)))
706
(defgeneric instance-graph-identifier (resource)
707
(:documentation "return the graph to contain assertions about the resource.
708
The default method indicates all graphs which specializations cann limit this
709
to a specific graph, for example that of the resource identifier.")
710
(:method ((instance persistent-object))
713
(defgeneric instance-store-graph (resource)
714
(:method ((resource persistent-object))
715
(synchronize-resource resource)
716
(get-instance-store-graph resource)))
718
(defgeneric encode-store-graph (resource)
719
(:documentation "Convert a resource into the respective statement sequence
720
for projection as a request response. Govern the constitution according to
721
the readers visible in the 'persistence-codec' package.
722
The base method either interprets, or if the class supports compilation,
723
generates a class specific method and delegates to it. (NYI)")
725
(:method ((resource standard-object))
726
(let* ((identifier (instance-identifier resource))
727
(graph-identifier (instance-graph-identifier resource))
728
(slot-definitions (class-persistent-slots (class-of resource))))
729
(flet ((normalize (object)
732
((nil) spocq.a:|false|)
734
(loop for sd in slot-definitions
735
for reader = (slot-definition-reader sd)
736
for property = (slot-definition-store-property sd)
737
for boundp = (slot-boundp resource (c2mop:slot-definition-name sd))
738
when (and reader property boundp)
739
collect `(spocq.a:|quad| ,identifier ,property ,(normalize (funcall (fdefinition reader) resource)) ,graph-identifier))))))
741
(defmethod encode-store-graph :before ((resource t))
744
(defgeneric decode-store-graph (resource graph)
746
"Given a resource and the intended content encoded as a graph
747
- that is, as a list of statements, project the respective statement values
749
Govern the operation according to the writers visible in the 'persistence-codec' package.
750
The base method either interprets, or if the class supports compilation,
751
generates a class specific method and delegates to it.
752
Specilizations can provide :around and/or :after methods can constrain state.")
754
(:method ((resource persistent-object) (graph list))
756
;; (describe resource)
757
(ecase *decode-store-graph.mode*
759
(let* ((identifier (instance-identifier resource))
760
(slot-definitions (class-persistent-slots (class-of resource)))
763
(loop for statement in graph ; store produces a quad field
764
for (nil subject predicate object) = statement
765
when (equalp subject identifier)
766
do (let* ((sd (find predicate slot-definitions :key #'slot-definition-store-property))
767
(writer (when sd (slot-definition-writer sd))))
768
; do (print (list (list subject predicate object) sd writer))
769
;;(push statement new-graph)
771
(funcall (fdefinition writer) (compute-cspo-term-value object graph) resource))))
772
;; (setf-instance-store-graph new-graph resource)
773
(setf-instance-store-graph graph resource))
777
(let ((method-lambda (compute-decode-store-graph-lambda (class-of resource))))
778
(eval `(defmethod decode-store-graph ((resource ,(type-of resource)) (graph list))
779
,@(cddr method-lambda)))
780
(decode-store-graph resource graph))))
783
(let* ((class (class-of resource))
784
(slot-definitions (class-persistent-slots class))
785
(identifier (instance-identifier resource)))
786
(loop for (nil subject predicate object) in graph
787
for sd = (loop for sd in slot-definitions
788
for slot-property = (some #'iri-p (c2mop:slot-definition-initargs sd))
789
when (equalp slot-property predicate)
791
for writer = (when sd (some #'store-writer-p (c2mop:slot-definition-writers sd)))
792
when (and writer (equalp subject identifier))
793
do (funcall writer object resource)))))
795
(defgeneric compute-decode-store-graph-lambda (class)
796
(:method ((class symbol))
797
(compute-decode-store-graph-lambda (find-class class)))
798
(:method ((class persistent-class))
799
(let ((slot-definitions (class-persistent-slots class)))
800
`(lambda (resource graph)
801
(let ((identifier (instance-identifier resource))
803
(loop for statement in graph
804
for (nil subject predicate object) = statement
805
when (equalp subject identifier)
807
,@(loop for sd in slot-definitions
808
for property = (slot-definition-store-property sd)
809
for writer = (slot-definition-writer sd)
810
when (and property writer)
811
collect `(,property (funcall ,(fdefinition writer) object resource)
812
(push statement new-graph)))))
813
(setf-instance-store-graph new-graph resource))
816
(defgeneric compute-decode-ui-graph-lambda (class)
817
(:method ((class symbol))
818
(compute-decode-store-graph-lambda (find-class class)))
819
(:method ((class persistent-class))
820
(let ((slot-definitions (class-persistent-slots class)))
821
`(lambda (resource graph)
822
(let ((identifier (instance-identifier resource))
824
(loop for statement in graph
825
for (subject predicate object) = statement
826
when (equalp subject identifier)
828
,@(loop for sd in slot-definitions
829
for property = (slot-definition-store-property sd)
830
for writer = (slot-definition-writer sd)
831
when (and property writer)
832
collect `(,property (funcall ,(fdefinition writer) object resource)
833
(push statement new-graph)))))
834
(setf (resource-ui-graph resource) new-graph))
838
(:documentation "term -> value conversion"
839
"Compute the value which corresponds to data from the store.
840
This involves transforming a graph field (or a subset of the statements) into an instance,
841
decoding lists, and converting atomic term values.
842
Two field types are supported, (c s p o), for data from the store and (s p o), for data from the ui.
844
The primary interface is the respective compute-*-term-value operator. It accepts a term and the cbd field.
845
The operator combines the term with the field in one of three ways:
846
- if the term is the subject of a typed resource, in which case, it delegated to compute-*-term-instance
847
and returns the described instance.
848
- if the term is the head of a pair, in which case it delegates to compute-*-term-list returns the
850
- otherwise, for a literal term, it returns the value unchanged.
852
The secondary interface is the respective merge-*-term-value. It accepts the term and the paired addition
853
and deletion fields emitted from the store as a the CRDT notification from an update operation.
855
The compute—*-term-instance operators check the subject term against a cache to see whether the instance
856
is already present. If so, then if the field revision id matches, the iri is a reference to the existing
857
state and the result is the unmodified cached instance. If the revision id does not match, then the
858
existing instance is cleared and reinitialized with the new state.
860
The merge-*-term-instance operators perform a similar check, aan modify know instances.
861
In the normal case, an iri identifies a known instance and the store revision id indicates that its state
862
has change since the most recent synchronization. In that case the +/- fields are merged into the instance,
863
whereby those deletions which are not also reflected as additions cause a slot to be unbound all other
864
changes are effected by on the basis of additions, which requires any list values to be modified in their
865
entirety. If no instance exists, then the operation has no effect. Given a known instance, if the revision
866
id matches, then it is returned unmodified. but a revision change causes the +/-")
869
(defun compute-cspo-term-value (term statements)
870
"Given a subject term and a statement field, compute the model value, which may be an instance
871
or a list of values."
873
((or spocq:blank-node iri)
874
(flet ((get-property (test-property)
875
(loop for (nil subject property object) in statements
876
when (and (equalp subject term) (equalp property test-property))
878
(let* ((type (get-property |rdf|:|type|)))
879
(cond ((and type (symbolp type))
880
(compute-cspo-term-instance term
881
(or (find-class type nil)
882
(error "Invalid type in library resource graph: ~s" type))
884
((get-property |rdf|:|first|)
885
(compute-cspo-term-list term statements))
891
(defun compute-cspo-term-instance (term class statements)
892
"Given a subject term, the respective class and a statement field, compute the model instance
893
- correllate the field's statements with slot definitions
894
- construct the value respective each slot definition
895
- accumulate the values and initargs as an initialization list
896
- create the instance with the constructed initargs "
897
(let ((slot-definitions (class-persistent-slots class))
899
(loop for (nil subject property object) in statements
900
for sd = (when (equalp subject term)
901
(loop for sd in slot-definitions
902
if (equalp property (slot-definition-decode-presentation-property sd))
905
do (let ((value (compute-cspo-term-value object statements))
906
(type (sb-mop:slot-definition-type sd)))
907
(when type (assert (typep value type) () "invalid property value: ~s: ~s." property value))
908
(setf (getf initargs (first (c2mop:slot-definition-initargs sd))) value))
909
else do (unless (member property '(|rdf|:|type| |rdf|:|first| |rdf|:|rest|
910
|http://www.w3.org/2000/01/rdf-schema#|:|subClassOf|
911
|http://www.w3.org/2000/01/rdf-schema#|:|comment|
912
|http://www.w3.org/2000/01/rdf-schema#|:|label|))
913
(log-warn "Unsupported property: ~s: ~s." class property)))
914
(apply #'ensure-instance class :identifier term initargs)))
916
(defun compute-cspo-term-list (term statements)
917
"given a graph as a list of statements and the term for the list head, compute the list of objects."
918
(if (eq term |rdf|:|nil|)
920
(loop with first = nil
922
for (nil subject predicate object) in statements
923
when (equal term subject)
926
(assert (null first) () "duplicate first in list graph: ~s" statements)
927
(setf first (compute-cspo-term-value object statements)))
929
(assert (null rest) () "duplicate rest in list graph: ~s" statements)
930
(setf rest (compute-cspo-term-value object statements)))
932
(error "invalid predicate in list graph: ~s: ~s" predicate statements)))
933
when (and first rest)
934
return (cons first (compute-cspo-term-list rest statements))
935
finally (error "head not in list graph: ~s: ~s" term statements))))
936
;;; (compute-cspo-term-list <_:a> '((<_:a> |rdf|:|first| 1) (<_:a> |rdf|:|rest| <_:b>) (<_:b> |rdf|:|first| 2) (<_:b> |rdf|:|rest| |rdf|:|nil|)))
939
;;; extract a single resource value from a triple graph
941
(defun compute-spo-term-value (term statements)
943
((or spocq:blank-node iri)
944
(flet ((get-property (test-property)
945
(loop for (subject property object) in statements
946
when (and (equalp subject term) (equalp property test-property))
948
(let* ((type (get-property |rdf|:|type|)))
949
(cond ((and type (symbolp type))
950
(compute-spo-term-instance term
951
(or (find-class type nil)
952
(error "Invalid type in library resource graph: ~s" type))
954
((get-property |rdf|:|first|)
955
(compute-spo-term-list term statements))
962
(defun compute-spo-term-instance (term class statements)
963
;; extract the instance from the graph
964
(let ((slot-definitions (class-persistent-slots class))
966
(loop for (subject property object) in statements
967
when (equalp subject term)
968
do (let ((sd (loop for sd in slot-definitions
969
if (equalp property (slot-definition-decode-presentation-property sd))
972
(let ((value (compute-spo-term-value object statements))
973
(type (sb-mop:slot-definition-type sd)))
975
(print (list property
976
(c2mop:slot-definition-name sd)
977
(first (c2mop:slot-definition-initargs sd))
979
(when type (assert (typep value type) () "invalid property value: ~s: ~s." property value))
980
(setf (getf initargs (first (c2mop:slot-definition-initargs sd))) value))
981
(unless (member property '(|rdf|:|type| |rdf|:|first| |rdf|:|rest|
982
|http://www.w3.org/2000/01/rdf-schema#|:|subClassOf|
983
|http://www.w3.org/2000/01/rdf-schema#|:|comment|
984
|http://www.w3.org/2000/01/rdf-schema#|:|label|))
985
(log-warn "Unsupported property: ~s: ~s." class property)))))
986
(apply #'ensure-instance class :identifier term initargs)))
988
(defun compute-spo-term-list (term statements)
989
"Given a graph as a list of statements and the term for the list head, compute the list of values."
990
(if (eq term |rdf|:|nil|)
992
(loop with first = nil
994
for (nil subject predicate object) in statements
995
when (equal term subject)
998
(assert (null first) () "duplicate first in list graph: ~s" statements)
999
(setf first (compute-spo-term-value object statements)))
1001
(assert (null rest) () "duplicate rest in list graph: ~s" statements)
1002
(setf rest (compute-spo-term-value object statements)))
1004
(error "invalid predicate in list graph: ~s: ~s" predicate statements)))
1005
when (and first rest)
1006
return (cons first (compute-spo-term-list rest statements))
1007
finally (error "head not in list graph: ~s: ~s" term statements))))
1010
(defun repository-match-cbd (transaction context subject slots)
1011
"Project the concise bounded description of a resource from a given store graph into a model instance.
1012
This walks the description graph from the resource identifier, through all predicates
1013
for which slots are defined, to collect the description of all sub-resources."
1015
(let* ((field (repository-match-field transaction context '?::s '?::p '?::o)))
1016
;; given the complete field, construct the cbd, subject to the given predicates
1017
(let ((description ())
1019
(labels ((walk-term (term)
1020
(unless (member term terms :test #'equalp)
1022
(loop for statement in field
1023
for (nil s nil o) = statement
1024
when (and (equalp o term)
1025
(not (member statement description)))
1026
do (progn (push statement description)
1028
when (and (equalp s term)
1029
(not (member statement description)))
1030
do (progn (push statement description)
1032
;; start rooted at the given subject
1033
(loop for statement in field
1034
for (nil s p o) = statement
1035
;; for all declared predicates
1036
when (and (member p slots :key #'slot-definition-store-property)
1037
(not (member statement description)))
1038
;; walk the path from the root
1039
do (cond ((equalp o subject)
1040
(push statement description)
1043
(push statement description)
1045
(values description))))
1047
(defgeneric slot-definition-unbinder (sd)
1051
(defgeneric merge-store-graph (resource addition-graph deletion-graph)
1052
(:method ((resource persistent-object) (addition-graph list) (deletion-graph list))
1054
;; (describe resource)
1055
(ecase *decode-store-graph.mode*
1057
(let* ((identifier (instance-identifier resource))
1058
(slot-definitions (class-persistent-slots (class-of resource)))
1059
(new-graph (get-instance-store-graph resource)))
1060
(setf deletion-graph (set-difference deletion-graph addition-graph :key #'third :test #'equalp))
1061
(loop for statement in deletion-graph ; store produces a quad field
1062
for (nil subject predicate object) = statement
1063
when (equalp subject identifier)
1064
do (let* ((sd (find predicate slot-definitions :key #'slot-definition-store-property))
1065
(unbinder (when sd (slot-definition-unbinder sd))))
1066
; do (print (list (list subject predicate object) sd writer))
1068
(setf new-graph (remove statement new-graph :test #'equalp))
1069
(funcall (fdefinition unbinder) resource) (sb-mop:slot-definition-name sd))))
1070
(loop for statement in addition-graph ; store produces a quad field
1071
for (nil subject predicate object) = statement
1072
when (equalp subject identifier)
1073
do (let* ((sd (find predicate slot-definitions :key #'slot-definition-store-property))
1074
(writer (when sd (slot-definition-writer sd))))
1075
; do (print (list (list subject predicate object) sd writer))
1077
(pushnew statement new-graph :test #'equalp)
1078
(funcall (fdefinition writer) (compute-cspo-term-value object addition-graph) resource))))
1079
(setf-instance-store-graph new-graph resource))
1080
;(describe resource)
1083
(let ((method-lambda (compute-merge-store-graph-lambda (class-of resource))))
1084
(eval `(defmethod merge-store-graph ((resource ,(type-of resource)) (addition-graph list) (deletion-graph list))
1085
,@(cddr method-lambda)))
1086
(merge-store-graph resource addition-graph deletion-graph))))
1089
(let* ((class (class-of resource))
1090
(slot-definitions (class-persistent-slots class))
1091
(identifier (instance-identifier resource)))
1092
(loop for (nil subject predicate object) in graph
1093
for sd = (loop for sd in slot-definitions
1094
for slot-property = (some #'iri-p (c2mop:slot-definition-initargs sd))
1095
when (equalp slot-property predicate)
1097
for writer = (when sd (some #'store-writer-p (c2mop:slot-definition-writers sd)))
1098
when (and writer (equalp subject identifier))
1099
do (funcall writer object resource)))))
1101
(defun merge-cspo-term-value (term statements)
1102
"Given a subject term and a statement field, compute the model value, which may be an instance
1103
or a list of values."
1105
((or spocq:blank-node iri)
1106
(flet ((get-property (test-property)
1107
(loop for (nil subject property object) in statements
1108
when (and (equalp subject term) (equalp property test-property))
1110
(let* ((type (get-property |rdf|:|type|)))
1111
(cond ((and type (symbolp type))
1112
(merge-cspo-term-instance term
1113
(or (find-class type nil)
1114
(error "Invalid type in library resource graph: ~s" type))
1116
((get-property |rdf|:|first|)
1117
(merge-cspo-term-list term statements))
1123
(defun merge-cspo-term-instance (term class statements)
1124
"Given a subject term, the respective class and a statement field, compute the model instance
1125
- correllate the field's statements with slot definitions
1126
- construct the value respective each slot definition
1127
- accumulate the values and initargs as an initialization list
1128
- create the instance with the constructed initargs "
1129
(let ((slot-definitions (class-persistent-slots class))
1131
(loop for (nil subject property object) in statements
1132
for sd = (when (equalp subject term)
1133
(loop for sd in slot-definitions
1134
if (equalp property (slot-definition-decode-presentation-property sd))
1137
do (let ((value (merge-cspo-term-value object statements))
1138
(type (sb-mop:slot-definition-type sd)))
1139
(when type (assert (typep value type) () "invalid property value: ~s: ~s." property value))
1140
(setf (getf initargs (first (c2mop:slot-definition-initargs sd))) value))
1141
else do (unless (member property '(|rdf|:|type| |rdf|:|first| |rdf|:|rest|
1142
|http://www.w3.org/2000/01/rdf-schema#|:|subClassOf|
1143
|http://www.w3.org/2000/01/rdf-schema#|:|comment|
1144
|http://www.w3.org/2000/01/rdf-schema#|:|label|))
1145
(log-warn "Unsupported property: ~s: ~s." class property)))
1146
(apply #'make-instance class initargs)))
1148
(defun merge-cspo-term-list (term statements)
1149
"given a graph as a list of statements and the term for the list head, compute the list of objects."
1150
(if (eq term |rdf|:|nil|)
1152
(loop with first = nil
1154
for (nil subject predicate object) in statements
1155
when (equal term subject)
1158
(assert (null first) () "duplicate first in list graph: ~s" statements)
1159
(setf first (merge-cspo-term-value object statements)))
1161
(assert (null rest) () "duplicate rest in list graph: ~s" statements)
1162
(setf rest (merge-cspo-term-value object statements)))
1164
(error "invalid predicate in list graph: ~s: ~s" predicate statements)))
1165
when (and first rest)
1166
return (cons first (merge-cspo-term-list rest statements))
1167
finally (error "head not in list graph: ~s: ~s" term statements))))
1169
(defgeneric compute-merge-store-graph-lambda (class)
1170
(:method ((class symbol))
1171
(compute-merge-store-graph-lambda (find-class class)))
1172
(:method ((class persistent-class))
1175
;;; higher level operators
1177
(defgeneric unbind-resource (resource)
1178
(:documentation "Given a persistent resource, unbind all slots which reflect
1179
persistent state, The consequence is a dirty resource, not a hollow instance,
1180
as the semantics are to remove the state from the store.")
1182
(:method ((resource persistent-object))
1183
(let* ((class (class-of resource))
1184
(slot-definitions (class-persistent-slots class)))
1185
(loop for sd in slot-definitions
1186
when (slot-definition-store-property sd)
1187
do (slot-makunbound resource (c2mop:slot-definition-name sd))
1188
finally (setf (instance-state resource) :hollow))
1191
(defgeneric synchronize-resource (resource)
1192
(:documentation "Given a resource, ensure that it is clean and that its state reflects
1193
the revision which is current in its store. Return the possible re-read resource.
1194
It is split into two steps. first jusde if the instance is already synchrized partially
1195
by status and for active instances based on the revision.
1196
second, delegate to specilaized step to read the data from the store.")
1197
(:method :around ((resource persistent-object))
1198
(case (instance-state resource)
1199
((:deleted :detached) resource)
1200
(:new (call-next-method)) ; check if something has appeared
1201
(t (multiple-value-bind (s-p repository-revision)
1202
(resource-synchronized-p resource)
1206
(setf (instance-state resource) :hollow)
1207
(setf (instance-store-revision resource) repository-revision)
1208
(call-next-method)))))))
1209
(:method ((resource persistent-object))
1210
(case (instance-state resource)
1212
(unbind-resource resource)
1213
(read-resource resource))
1215
(read-resource resource))
1217
(warn "Anomalous resource state in synchronize-resource base method: ~s" (instance-state resource))
1220
;;; methods require repository class
1222
(defgeneric align-resource-to-store (resource reference &key revision)
1223
(:documentation "On read, the revision is the resolved repository revision
1224
on write its is the revision to be created")
1228
(defgeneric read-resource (resource &key revision)
1229
(:documentation "Read the state of the given resource from the store at a specified resource.
1230
If the resource state coincides, then decode the read field to
1231
bind the values to the respective slots. The respective graph comprises
1232
- all statements with the resource as subject
1233
- all first/rest statements in the resource's graph
1234
if a divergent revision is supplied, just read it, but do not decode.")
1236
(:method ((resource persistent-object) &key (revision (instance-store-revision resource)))
1237
(let* ((repository (instance-repository resource))
1238
(identifier (instance-identifier resource))
1239
(graph (instance-graph-identifier resource)))
1240
(let ((field (append (repository-match-field repository graph identifier nil nil)
1241
(repository-match-field repository identifier nil |rdf|:|first| nil)
1242
(repository-match-field repository identifier nil |rdf|:|rest| nil))))
1244
(or (resource-hollow-p resource) (resource-new-p resource))
1245
(equalp revision (instance-store-revision resource)))
1246
(decode-store-graph resource field))
1247
(values resource field))))
1249
(:method ((resource persistent-graph-object) &key (revision (instance-store-revision resource)))
1250
(let* ((repository (instance-repository resource))
1251
(graph (instance-graph-identifier resource)))
1252
(let ((field (repository-match-field repository graph nil nil nil)))
1253
(when (equalp revision (instance-store-revision resource))
1254
(decode-store-graph resource field))
1255
(values resource field))))
1257
(:method ((resource persistent-subject-object) &key (revision (instance-store-revision resource)))
1258
(let* ((repository (instance-repository resource))
1259
(identifier (instance-identifier resource)))
1260
(let ((field (repository-match-field repository |urn:dydra|:|all| identifier nil nil)))
1262
(or (resource-hollow-p resource) (resource-new-p resource))
1263
(equalp revision (instance-store-revision resource)))
1264
(decode-store-graph resource field))
1265
(values resource field))))
1267
(:method ((resource persistent-graph-subject-object) &key (revision (instance-store-revision resource)))
1268
(let* ((repository (instance-repository resource))
1269
(identifier (instance-identifier resource))
1270
(graph (instance-graph-identifier resource)))
1271
(let ((field (repository-match-field repository graph identifier nil nil)))
1273
(or (resource-hollow-p resource) (resource-new-p resource))
1274
(equalp revision (instance-store-revision resource)))
1275
(decode-store-graph resource field))
1276
(values resource field))))
1278
(:method ((resource described-object) &key (revision (instance-store-revision resource)))
1279
(let ((repository-id (instance-repository-id resource))
1280
(identifier (instance-identifier resource)))
1281
(with-open-repository (repository-id :normal-disposition :continue :read-only-p t)
1282
(let ((field (repository-match-cbd *transaction* identifier identifier (class-persistent-slots resource))))
1284
(or (resource-hollow-p resource) (resource-new-p resource))
1285
(equalp revision (instance-store-revision resource)))
1286
(decode-store-graph resource field))
1287
(values resource field)))))
1289
(:method :around ((resource persistent-object) &rest args)
1290
;; do not add a second synchronized-p test. leave that for the control flow through syncronize-resource
1291
(declare (ignore args) (dynamic-extent args))
1292
(handler-bind ((error (lambda (c)
1293
(log-stacktrace "read-resource: ~a ~a : ~a"
1294
(instance-repository-id resource)
1295
(instance-identifier resource)
1297
;; if the read fails, leave the state unchanged
1298
(return-from read-resource nil))))
1299
(multiple-value-prog1 (call-next-method)
1300
(setf (instance-state resource) :clean)))))
1302
;;; 2019-01-08 nxp-prod
1303
;;; (read-resource (repository "nxp/plm"))
1305
(defparameter *commit-resource.verbose* nil)
1307
(defgeneric commit-resource (resource)
1308
(:method :around ((resource standard-object))
1309
(case (instance-state resource)
1311
(let ((repository-id (instance-repository-id resource)))
1312
(cond ((and *transaction*
1313
(equal (repository-id *transaction*) repository-id))
1315
(setf (instance-store-revision resource) (1+ (repository-last-revision repository-id))))
1317
(with-open-repository (repository-id :normal-disposition :commit)
1319
(setf (instance-store-revision resource) (repository-last-revision repository-id))
1320
(setf (instance-state resource) :clean)))))
1321
(*commit-resource.verbose*
1322
(warn "skipped commit: [~s] ~s" (bt:thread-name sb-thread:*current-thread*) resource)))
1325
(:method ((resource persistent-object))
1326
(let* ((graph-to-insert (encode-store-graph resource)) ;; c-s-p-o
1327
(graph-to-delete (get-instance-store-graph resource)))
1328
(when graph-to-delete
1329
(repository-delete-field *transaction* graph-to-delete))
1330
(when graph-to-insert
1331
(multiple-value-bind (interned-graph node-map)
1332
(repository-intern-statements *transaction* graph-to-insert :skolemize t)
1333
;; insert the field and then cache the blank-node-mapped staement sequence
1335
(repository-insert-field *transaction* interned-graph)
1336
(flet ((map-term (term)
1337
(or (rest (assoc term node-map)) term)))
1338
;; blank nodes cannot appear as predicates as those are slots
1339
(setf-instance-store-graph (loop for (nil s p o c) in graph-to-insert
1340
collect (list (map-term c) (map-term s) p (map-term o)))
1344
;;; utility operators
1346
(defun compute-cspo-roots (graph)
1347
(let ((object-cache (make-hash-table :test #'equalp)))
1348
(loop for statement in graph
1349
for object = (fourth statement)
1350
do (setf (gethash object object-cache) t))
1351
(loop with roots = ()
1352
for statement in graph
1353
for subject = (second statement)
1354
unless (gethash subject object-cache)
1355
do (pushnew subject roots :test #'equalp)
1356
finally (return roots))))
1357
;;; (compute-cspo-roots '((c root 1 o1) (c root 2 o2) (c o1 3 o4)))
1359
(defun compute-spo-roots (graph)
1360
(let ((object-cache (make-hash-table :test #'equalp)))
1361
(loop for statement in graph
1362
for object = (third statement)
1363
do (setf (gethash object object-cache) t))
1364
(loop with roots = ()
1365
for statement in graph
1366
for subject = (first statement)
1367
unless (gethash subject object-cache)
1368
do (pushnew subject roots :test #'equalp)
1369
finally (return roots))))
1373
(defgeneric boxed-+ (arg1 arg2)
1374
(:method ((arg1 number) (arg2 number))
1376
(:method ((arg1 spocq:integer) (arg2 spocq:integer))
1377
(let ((value (+ (literal-value arg1) (literal-value arg2))))
1378
(make-integer-literal :cached-value value :string (prin1-to-string value)))))
1380
(defun do-generic (arg1 arg2 &key (count 1000000))
1382
(dotimes (i count v) (setf v (boxed-+ arg1 arg2)))
1385
(defun do-native (arg1 arg2 &key (count 1000000))
1387
(dotimes (i count v) (setf v (+ arg1 arg2)))
1390
;; (time (do-generic 100 100))
1391
;; (time (do-native 100 100))
1392
;; (time (do-generic (spocq:make-integer :lexical-form "100") (spocq:make-integer :lexical-form "100")))