11import { ERROR_CODES , ErrorCoded } from "jsonld-context-parser" ;
22import * as RDF from "@rdfjs/types" ;
3- import { ParsingContext } from "../ParsingContext" ;
3+ import { AnnotationsBufferEntry , ParsingContext } from "../ParsingContext" ;
44import { Util } from "../Util" ;
55import { IEntryHandler } from "./IEntryHandler" ;
66
@@ -21,17 +21,18 @@ export class EntryHandlerPredicate implements IEntryHandler<boolean> {
2121 * @param {Term } object The object.
2222 * @param {boolean } reverse If the property is reversed.
2323 * @param {boolean } isEmbedded If the property exists in an embedded node as direct child.
24+ * @param {boolean } isAnnotation If the property exists in an annotation object.
2425 * @return {Promise<void> } A promise resolving when handling is done.
2526 */
2627 public static async handlePredicateObject ( parsingContext : ParsingContext , util : Util , keys : any [ ] , depth : number ,
2728 predicate : RDF . Term , object : RDF . Term ,
28- reverse : boolean , isEmbedded : boolean ) {
29+ reverse : boolean , isEmbedded : boolean , isAnnotation : boolean ) {
2930 const depthProperties : number = await util . getPropertiesDepth ( keys , depth ) ;
3031 const depthOffsetGraph = await util . getDepthOffsetGraph ( depth , keys ) ;
3132 const depthPropertiesGraph : number = depth - depthOffsetGraph ;
3233
3334 const subjects = parsingContext . idStack [ depthProperties ] ;
34- if ( subjects ) {
35+ if ( subjects && ! isAnnotation ) {
3536 // Emit directly if the @id was already defined
3637 for ( const subject of subjects ) {
3738 // Check if we're in a @graph context
@@ -65,7 +66,44 @@ export class EntryHandlerPredicate implements IEntryHandler<boolean> {
6566 if ( reverse ) {
6667 util . validateReverseSubject ( object ) ;
6768 }
68- parsingContext . getUnidentifiedValueBufferSafe ( depthProperties ) . push ( { predicate, object, reverse, isEmbedded} ) ;
69+
70+ // Either push to the annotations or the actual value buffer
71+ if ( isAnnotation ) {
72+ // Only add to buffer if rdfstar is enabled
73+ if ( parsingContext . rdfstar ) {
74+ // Error if an @id was defined
75+ if ( parsingContext . idStack [ depth ] ) {
76+ parsingContext . emitError ( new ErrorCoded ( `Found an illegal @id inside an annotation: ${ parsingContext . idStack [ depth ] [ 0 ] . value } ` ,
77+ ERROR_CODES . INVALID_ANNOTATION ) ) ;
78+ }
79+
80+ // Error if we're in an embedded node
81+ for ( let i = 0 ; i < depth ; i ++ ) {
82+ if ( await util . unaliasKeyword ( keys [ i ] , keys , i ) === '@id' ) {
83+ parsingContext . emitError ( new ErrorCoded ( `Found an illegal annotation inside an embedded node` ,
84+ ERROR_CODES . INVALID_ANNOTATION ) ) ;
85+ }
86+ }
87+
88+ // Store new annotation in the buffer
89+ const annotationsBuffer = parsingContext . getAnnotationsBufferSafe ( depthProperties ) ;
90+ const newAnnotation : AnnotationsBufferEntry = { predicate, object, reverse, nestedAnnotations : [ ] , depth : depthProperties } ;
91+ annotationsBuffer . push ( newAnnotation ) ;
92+
93+ // Check in the buffer if any annotations were defined at a deeper depth,
94+ // if so, they are considered nested annotations.
95+ for ( let i = annotationsBuffer . length - 2 ; i >= 0 ; i -- ) {
96+ // We iterate in reverse order, to enable easy item removal from the back.
97+ const existingAnnotation = annotationsBuffer [ i ] ;
98+ if ( existingAnnotation . depth > depthProperties ) {
99+ newAnnotation . nestedAnnotations . push ( existingAnnotation ) ;
100+ annotationsBuffer . splice ( i , 1 ) ;
101+ }
102+ }
103+ }
104+ } else {
105+ parsingContext . getUnidentifiedValueBufferSafe ( depthProperties ) . push ( { predicate, object, reverse, isEmbedded } ) ;
106+ }
69107 }
70108 }
71109
@@ -108,15 +146,22 @@ export class EntryHandlerPredicate implements IEntryHandler<boolean> {
108146 const objects = await util . valueToTerm ( context , key , value , depth , keys ) ;
109147 if ( objects . length ) {
110148 for ( let object of objects ) {
149+ // Based on parent key, check if reverse, embedded, and annotation.
111150 let parentKey = await util . unaliasKeywordParent ( keys , depth ) ;
112151 const reverse = Util . isPropertyReverse ( context , keyOriginal , parentKey ) ;
113- if ( parentKey === '@reverse' ) {
114- // Check parent of parent when checking if we're in an embedded node if in @reverse
115- depth -- ;
116- parentKey = await util . unaliasKeywordParent ( keys , depth ) ;
152+ let parentDepthOffset = 0 ;
153+ while ( parentKey === '@reverse' || typeof parentKey === 'number' ) {
154+ // Check parent of parent when checking while we're in an array or in @reverse
155+ if ( typeof parentKey === 'number' ) {
156+ parentDepthOffset ++ ;
157+ } else {
158+ depth -- ;
159+ }
160+ parentKey = await util . unaliasKeywordParent ( keys , depth - parentDepthOffset ) ;
117161 }
118162 const isEmbedded = Util . isPropertyInEmbeddedNode ( parentKey ) ;
119163 util . validateReverseInEmbeddedNode ( key , reverse , isEmbedded ) ;
164+ const isAnnotation = Util . isPropertyInAnnotationObject ( parentKey ) ;
120165
121166 if ( value ) {
122167 // Special case if our term was defined as an @list , but does not occur in an array,
@@ -143,7 +188,7 @@ export class EntryHandlerPredicate implements IEntryHandler<boolean> {
143188 }
144189
145190 await EntryHandlerPredicate . handlePredicateObject ( parsingContext , util , keys , depth ,
146- predicate , object , reverse , isEmbedded ) ;
191+ predicate , object , reverse , isEmbedded , isAnnotation ) ;
147192 }
148193 }
149194 }
0 commit comments