summaryrefslogtreecommitdiff
path: root/librdfa/triple.c
blob: be1caf2052cccf878b8f1d2031ccd7195699d108 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
/**
 * Copyright 2008 Digital Bazaar, Inc.
 *
 * This file is part of librdfa.
 *
 * librdfa is Free Software, and can be licensed under any of the
 * following three licenses:
 *
 *   1. GNU Lesser General Public License (LGPL) V2.1 or any
 *      newer version
 *   2. GNU General Public License (GPL) V2 or any newer version
 *   3. Apache License, V2.0 or any newer version
 *
 * You may not use this file except in compliance with at least one of
 * the above three licenses.
 *
 * See LICENSE-* at the top of this software distribution for more
 * information regarding the details of each license.
 *
 * Handles all triple functionality including all incomplete triple
 * functionality.
 *
 * @author Manu Sporny
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "rdfa_utils.h"
#include "rdfa.h"

rdftriple* rdfa_create_triple(const char* subject, const char* predicate,
   const char* object, rdfresource_t object_type, const char* datatype,
   const char* language)
{
   rdftriple* rval = (rdftriple*)malloc(sizeof(rdftriple));

   /* clear the memory */
   rval->subject = NULL;
   rval->predicate = NULL;
   rval->object = NULL;
   rval->object_type = object_type;
   rval->datatype = NULL;
   rval->language = NULL;

#if 0
   printf("SUBJECT  : %s\n", subject);
   printf("PREDICATE: %s\n", predicate);
   printf("OBJECT   : %s\n", object);
   printf("DATATYPE : %s\n", datatype);
   printf("LANG     : %s\n", language);
#endif

   /* a triple needs a subject, predicate and object at minimum to be
    * considered a triple. */
   if((subject != NULL) && (predicate != NULL) && (object != NULL))
   {
      rval->subject = rdfa_replace_string(rval->subject, subject);
      rval->predicate = rdfa_replace_string(rval->predicate, predicate);
      rval->object = rdfa_replace_string(rval->object, object);

      /* if the datatype is present, set it */
      if(datatype != NULL)
      {
         rval->datatype = rdfa_replace_string(rval->datatype, datatype);
      }

      /* if the language was specified, set it */
      if(language != NULL)
      {
         rval->language = rdfa_replace_string(rval->language, language);
      }
   }

   return rval;
}

void rdfa_print_triple(rdftriple* triple)
{
   if(triple->object_type == RDF_TYPE_NAMESPACE_PREFIX)
   {
      printf("%s %s: <%s> .\n",
         triple->subject, triple->predicate, triple->object);
   }
   else
   {
      if(triple->subject != NULL)
      {
         if((triple->subject[0] == '_') && (triple->subject[1] == ':'))
         {
            printf("%s\n", triple->subject);
         }
         else
         {
            printf("<%s>\n", triple->subject);
         }
      }
      else
      {
         printf("INCOMPLETE\n");
      }

      if(triple->predicate != NULL)
      {
         printf("   <%s>\n", triple->predicate);
      }
      else
      {
         printf("   INCOMPLETE\n");
      }

      if(triple->object != NULL)
      {
         if(triple->object_type == RDF_TYPE_IRI)
         {
            if((triple->object[0] == '_') && (triple->object[1] == ':'))
            {
               printf("      %s", triple->object);
            }
            else
            {
               printf("      <%s>", triple->object);
            }
         }
         else if(triple->object_type == RDF_TYPE_PLAIN_LITERAL)
         {
            printf("      \"%s\"", triple->object);
            if(triple->language != NULL)
            {
               printf("@%s", triple->language);
            }
         }
         else if(triple->object_type == RDF_TYPE_XML_LITERAL)
         {
            printf("      \"%s\"^^rdf:XMLLiteral", triple->object);
         }
         else if(triple->object_type == RDF_TYPE_TYPED_LITERAL)
         {
            if((triple->datatype != NULL) && (triple->language != NULL))
            {
               printf("      \"%s\"@%s^^<%s>",
                  triple->object, triple->language, triple->datatype);
            }
            else if(triple->datatype != NULL)
            {
               printf("      \"%s\"^^<%s>", triple->object, triple->datatype);
            }
         }
         else
         {
            printf("      <%s> <---- UNKNOWN OBJECT TYPE", triple->object);
         }

         printf(" .\n");
      }
      else
      {
         printf("      INCOMPLETE .");
      }
   }
}

void rdfa_free_triple(rdftriple* triple)
{
   free(triple->subject);
   free(triple->predicate);
   free(triple->object);
   free(triple->datatype);
   free(triple->language);
   free(triple);
}

#ifndef LIBRDFA_IN_RAPTOR
/**
 * Generates a namespace prefix triple for any application that is
 * interested in processing namespace changes.
 *
 * @param context the RDFa context.
 * @param prefix the name of the prefix
 * @param IRI the fully qualified IRI that the prefix maps to.
 */
void rdfa_generate_namespace_triple(
   rdfacontext* context, const char* prefix, const char* iri)
{
   if(context->processor_graph_triple_callback != NULL)
   {
      rdftriple* triple = rdfa_create_triple(
         "@prefix", prefix, iri, RDF_TYPE_NAMESPACE_PREFIX, NULL, NULL);
      context->processor_graph_triple_callback(triple, context->callback_data);
   }
}

/**
 * Generates a set of triples that describe the location of a warning or
 * error in a document.
 *
 * @param context the currently active context.
 * @param subject the name of the subject that is associated with the triples.
 */
#if 1 /* remove when the prototype is in the header */
void rdfa_processor_location_triples(rdfacontext* context, const char* subject);
#endif
void rdfa_processor_location_triples(rdfacontext* context, const char* subject)
{
}

/**
 * Generates a set of triples in the processor graph including the processor's
 * position in the byte stream.
 *
 * @param context the current active context.
 * @param type the type of the message, which may be any of the RDF Classes
 *             defined in the RDFa Core specification:
 *             http://www.w3.org/TR/rdfa-core/#processor-graph-reporting
 * @param msg the message associated with the processor warning.
 */
void rdfa_processor_triples(
   rdfacontext* context, const char* type, const char* msg)
{
   if(context->processor_graph_triple_callback != NULL)
   {
      char buffer[32];
      char* subject = rdfa_create_bnode(context);
      char* context_subject = rdfa_create_bnode(context);

      /* generate the RDFa Processing Graph warning type triple */
      rdftriple* triple = rdfa_create_triple(
         subject, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
         type, RDF_TYPE_IRI, NULL, NULL);
      context->processor_graph_triple_callback(triple, context->callback_data);

      /* generate the description */
      triple = rdfa_create_triple(
         subject, "http://purl.org/dc/terms/description", msg,
         RDF_TYPE_PLAIN_LITERAL, NULL, "en");
      context->processor_graph_triple_callback(triple, context->callback_data);

      /* generate the context triple for the error */
      triple = rdfa_create_triple(
         subject, "http://www.w3.org/ns/rdfa#context",
         context_subject, RDF_TYPE_IRI, NULL, NULL);
      context->processor_graph_triple_callback(triple, context->callback_data);

      /* generate the type for the context triple */
      triple = rdfa_create_triple(
         context_subject, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
         "http://www.w3.org/2009/pointers#LineCharPointer",
         RDF_TYPE_IRI, NULL, NULL);
      context->processor_graph_triple_callback(triple, context->callback_data);

      /* generate the line number */
      snprintf(buffer, sizeof(buffer) - 1, "%d",
         (int)xmlSAX2GetLineNumber(context->parser));
      triple = rdfa_create_triple(
         context_subject, "http://www.w3.org/2009/pointers#lineNumber",
         buffer, RDF_TYPE_TYPED_LITERAL,
         "http://www.w3.org/2001/XMLSchema#positiveInteger", NULL);
      context->processor_graph_triple_callback(triple, context->callback_data);

      free(context_subject);
      free(subject);
   }
}
#endif

/**
 * Completes all incomplete triples that are part of the current
 * context by matching the new_subject with the list of incomplete
 * triple predicates.
 *
 * @param context the RDFa context.
 */
void rdfa_complete_incomplete_triples(rdfacontext* context)
{
   /* 10. If the [ skip element ] flag is 'false', and [ new subject ]
    *     was set to a non-null value, then any [ incomplete triple ]s
    *     within the current context should be completed:
    *
    * The [ list of incomplete triples ] from the current [ evaluation
    * context ] ( not the [ local list of incomplete triples ]) will
    * contain zero or more predicate URIs. This list is iterated, and
    * each of the predicates is used with [ parent subject ] and
    * [ new subject ] to generate a triple. Note that at each level
    * there are two , lists of [ incomplete triple ]s; one for the
    * current processing level (which is passed to each child element
    * in the previous step), and one that was received as part of the
    * [ evaluation context ]. It is the latter that is used in
    * processing during this step. */
   unsigned int i;
   for(i = 0; i < context->incomplete_triples->num_items; i++)
   {
      rdfalist* incomplete_triples = context->incomplete_triples;
      rdfalistitem* incomplete_triple = incomplete_triples->items[i];

      if(incomplete_triple->flags & RDFALIST_FLAG_DIR_NONE)
      {
         /* If direction is 'none', the new subject is added to the list
          * from the iterated incomplete triple. */
         const char* predicate = (const char*)incomplete_triple->data;
         rdftriple* triple = rdfa_create_triple(context->parent_subject,
            predicate, context->new_subject, RDF_TYPE_IRI, NULL, NULL);

         /* ensure the list mapping exists */
         rdfa_create_list_mapping(
            context, context->local_list_mappings,
            context->parent_subject, predicate);

         /* add the predicate to the list mapping */
         rdfa_append_to_list_mapping(context->local_list_mappings,
            context->parent_subject, predicate, (void*)triple);
      }
      else if(incomplete_triple->flags & RDFALIST_FLAG_DIR_FORWARD)
      {
         /* If [direction] is 'forward' then the following triple is generated:
          *
          * subject
          *    [parent subject]
          * predicate
          *    the predicate from the iterated incomplete triple
          * object
          *    [new subject] */
         rdftriple* triple =
            rdfa_create_triple(context->parent_subject,
               (const char*)incomplete_triple->data, context->new_subject,
               RDF_TYPE_IRI, NULL, NULL);
         context->default_graph_triple_callback(triple, context->callback_data);
      }
      else
      {
         /* If [direction] is not 'forward' then this is the triple generated:
          *
          * subject
          *    [new subject]
          * predicate
          *    the predicate from the iterated incomplete triple
          * object
          *    [parent subject] */
         rdftriple* triple =
            rdfa_create_triple(context->new_subject,
               (const char*)incomplete_triple->data, context->parent_subject,
               RDF_TYPE_IRI, NULL, NULL);
         context->default_graph_triple_callback(triple, context->callback_data);
      }
      free(incomplete_triple->data);
      free(incomplete_triple);
   }
   context->incomplete_triples->num_items = 0;
}

void rdfa_complete_type_triples(
   rdfacontext* context, const rdfalist* type_of)
{
   unsigned int i;
   rdfalistitem** iptr = type_of->items;
   const char* subject;
   const char* type;

   if(context->rdfa_version == RDFA_VERSION_1_0)
   {
      /* RDFa 1.0: 6.1 One or more 'types' for the [new subject] can be set by
       * using @type_of. If present, the attribute must contain one or
       * more URIs, obtained according to the section on URI and CURIE
       * Processing, each of which is used to generate a triple as follows:
       *
       * subject
       *    [new subject]
       * predicate
       *    http://www.w3.org/1999/02/22-rdf-syntax-ns#type
       * object
       *    full URI of 'type'
       */
      subject = context->new_subject;
   }
   else
   {
      /* RDFa 1.1: 7. One or more 'types' for the typed resource can be set by
       * using @typeof. If present, the attribute may contain one or more IRIs,
       * obtained according to the section on CURIE and IRI Processing, each of
       * which is used to generate a triple as follows:
       *
       * subject
       *    typed resource
       * predicate
       *    http://www.w3.org/1999/02/22-rdf-syntax-ns#type
       * object
       *    current full IRI of 'type' from typed resource
       */
      subject = context->typed_resource;
   }

   for(i = 0; i < type_of->num_items; i++)
   {
      rdfalistitem* iri = *iptr;
      rdftriple* triple;
      type = (const char*)iri->data;

      triple = rdfa_create_triple(subject,
         "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", type, RDF_TYPE_IRI,
         NULL, NULL);

      context->default_graph_triple_callback(triple, context->callback_data);
      iptr++;
   }
}

void rdfa_complete_relrev_triples(
   rdfacontext* context, const rdfalist* rel, const rdfalist* rev)
{
   /* 7. If in any of the previous steps a [current object  resource]
    * was set to a non-null value, it is now used to generate triples */
   unsigned int i;

   /* Predicates for the [current object resource] can be set by using
    * one or both of the @rel and @rev attributes. */

   /* If present, @rel will contain one or more URIs, obtained
    * according to the section on CURIE and URI Processing each of
    * which is used to generate a triple as follows:
    *
    * subject
    *    [new subject]
    * predicate
    *    full URI
    * object
    *    [current object resource] */
   if(rel != NULL)
   {
      rdfalistitem** relptr = rel->items;
      for(i = 0; i < rel->num_items; i++)
      {
         rdfalistitem* curie = *relptr;

         rdftriple* triple = rdfa_create_triple(context->new_subject,
            (const char*)curie->data, context->current_object_resource,
            RDF_TYPE_IRI, NULL, NULL);

         context->default_graph_triple_callback(triple, context->callback_data);
         relptr++;
      }
   }

   /* If present, @rev will contain one or more URIs, obtained
    * according to the section on CURIE and URI Processing each of which
    * is used to generate a triple as follows:
    *
    * subject
    *    [current object resource]
    * predicate
    *    full URI
    * object
    *    [new subject] */
   if(rev != NULL)
   {
      rdfalistitem** revptr = rev->items;
      for(i = 0; i < rev->num_items; i++)
      {
         rdfalistitem* curie = *revptr;

         rdftriple* triple = rdfa_create_triple(
            context->current_object_resource, (const char*)curie->data,
            context->new_subject, RDF_TYPE_IRI, NULL, NULL);

         context->default_graph_triple_callback(triple, context->callback_data);
         revptr++;
      }
   }
}

void rdfa_save_incomplete_triples(
   rdfacontext* context, const rdfalist* rel, const rdfalist* rev)
{
   unsigned int i;
   /* 8. If however [current object resource] was set to null, but
    * there are predicates present, then they must be stored as
    * [incomplete triple]s, pending the discovery of a subject that
    * can be used as the object. Also, [current object resource]
    * should be set to a newly created [bnode] */
   if(context->current_object_resource == NULL)
   {
      context->current_object_resource = rdfa_create_bnode(context);
   }

   /* If present, @rel must contain one or more URIs, obtained
    * according to the section on CURIE and URI Processing each of
    * which is added to the [local local list of incomplete triples]
    * as follows:
    *
    * predicate
    *    full URI
    * direction
    *    forward */
   if(rel != NULL)
   {
      rdfalistitem** relptr = rel->items;
      for(i = 0; i < rel->num_items; i++)
      {
         rdfalistitem* curie = *relptr;

         rdfa_add_item(
            context->local_incomplete_triples, curie->data,
               (liflag_t)(RDFALIST_FLAG_DIR_FORWARD | RDFALIST_FLAG_TEXT));

         relptr++;
      }
   }

   /* If present, @rev must contain one or more URIs, obtained
    * according to the section on CURIE and URI Processing, each of
    * which is added to the [local list of incomplete triples] as follows:
    *
    * predicate
    *    full URI
    * direction
    *    reverse */
   if(rev != NULL)
   {
      rdfalistitem** revptr = rev->items;
      for(i = 0; i < rev->num_items; i++)
      {
         rdfalistitem* curie = *revptr;

         rdfa_add_item(
            context->local_incomplete_triples, curie->data,
               (liflag_t)(RDFALIST_FLAG_DIR_REVERSE | RDFALIST_FLAG_TEXT));

         revptr++;
      }
   }
}

void rdfa_complete_object_literal_triples(rdfacontext* context)
{
   /* 9. The next step of the iteration is to establish any
    * [current object literal];
    *
    * Predicates for the [current object literal] can be set by using
    * @property. If present, a URI is obtained according to the
    * section on CURIE and URI Processing, and then the actual literal
    * value is obtained as follows: */
   const char* current_object_literal = NULL;
   rdfresource_t type = RDF_TYPE_UNKNOWN;

   unsigned int i;
   rdfalistitem** pptr;

   /* * as a [plain literal] if:
    *   o @content is present;
    *   o or all children of the [current element] are text nodes;
    *   o or there are no child nodes; TODO: Is this needed?
    *   o or the body of the [current element] does have non-text
    *     child nodes but @datatype is present, with an empty value.
    *
    * Additionally, if there is a value for [current language] then
    * the value of the [plain literal] should include this language
    * information, as described in [RDF-CONCEPTS]. The actual literal
    * is either the value of @content (if present) or a string created
    * by concatenating the text content of each of the descendant
    * elements of the [current element] in document order. */
   if((context->content != NULL))
   {
      current_object_literal = context->content;
      type = RDF_TYPE_PLAIN_LITERAL;
   }
   else if(context->xml_literal && strchr(context->xml_literal, '<') == NULL)
   {
      current_object_literal = context->plain_literal;
      type = RDF_TYPE_PLAIN_LITERAL;
   }
   else if(strlen(context->plain_literal) == 0)
   {
      current_object_literal = (const char*)"";
      type = RDF_TYPE_PLAIN_LITERAL;
   }
   else if((context->xml_literal != NULL) &&
           (context->datatype != NULL) &&
           (strlen(context->xml_literal) > 0) &&
           (strcmp(context->datatype, "") == 0))
   {
      current_object_literal = context->plain_literal;
      type = RDF_TYPE_PLAIN_LITERAL;
   }


   /* * as an [XML literal] if:
    *    o the [current element] has any child nodes that are not
    *      simply text nodes, and @datatype is not present, or is
    *      present, but is set to rdf:XMLLiteral.
    *
    * The value of the [XML literal] is a string created by
    * serializing to text, all nodes that are descendants of the
    * [current element], i.e., not including the element itself, and
    * giving it a datatype of rdf:XMLLiteral. */
   if((context->xml_literal != NULL) &&
      (current_object_literal == NULL) &&
      (strchr(context->xml_literal, '<') != NULL) &&
      ((context->datatype == NULL) ||
       (strcmp(context->datatype,
               "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral") == 0)))
   {
      current_object_literal = context->xml_literal;
      type = RDF_TYPE_XML_LITERAL;
   }

   /* * as a [typed literal] if:
    *    o @datatype is present, and does not have an empty
    *      value.
    *
    * The actual literal is either the value of @content (if present)
    * or a string created by concatenating the value of all descendant
    * text nodes, of the [current element] in turn. The final string
    * includes the datatype URI, as described in [RDF-CONCEPTS], which
    * will have been obtained according to the section on CURIE and
    * URI Processing. */
   if((context->datatype != NULL) && (strlen(context->datatype) > 0))
   {
      if(context->content != NULL)
      {
         /* Static code analyzer clang says next line is not needed;
          * "Assigned value is always the same as the existing value"
          */
         /* current_object_literal = context->content; */
         type = RDF_TYPE_TYPED_LITERAL;
      }
      else if(strcmp(context->datatype,
               "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral") != 0)
      {
         current_object_literal = context->plain_literal;
         type = RDF_TYPE_TYPED_LITERAL;
      }
   }

   /* TODO: Setting the current object literal to the plain literal in
    *       the case of xsd:string isn't mentioned in the syntax
    *       processing document. */
   if((current_object_literal == NULL) && (context->datatype != NULL) &&
      (strcmp(
         context->datatype, "http://www.w3.org/2001/XMLSchema#string") == 0))
   {
      current_object_literal = context->plain_literal;
      type = RDF_TYPE_TYPED_LITERAL;
   }

   /* The [current object literal] is then used with each predicate to
    * generate a triple as follows:
    *
    * subject
    *    [new subject]
    * predicate
    *    full URI
    * object
    *    [current object literal] */
   pptr = context->property->items;
   for(i = 0; i < context->property->num_items; i++)
   {

      rdfalistitem* curie = *pptr;
      rdftriple* triple = NULL;

      triple = rdfa_create_triple(context->new_subject,
         (const char*)curie->data, current_object_literal, type,
         context->datatype, context->language);

      context->default_graph_triple_callback(triple, context->callback_data);
      pptr++;
   }

   /* TODO: Implement recurse flag being set to false
    *
    * Once the triple has been created, if the [datatype] of the
    * [current object literal] is rdf:XMLLiteral, then the [recurse]
    * flag is set to false */
   context->recurse = 0;
}

void rdfa_complete_current_property_value_triples(rdfacontext* context)
{
   /* 11. The next step of the iteration is to establish any current property
    *     value;
    * Predicates for the current property value can be set by using @property.
    * If present, one or more resources are obtained according to the section
    * on CURIE and IRI Processing, and then the actual literal value is
    * obtained as follows: */
   char* current_property_value = NULL;
   rdfresource_t type = RDF_TYPE_UNKNOWN;

   unsigned int i;
   rdfalistitem** pptr;

   /* as a typed literal if @datatype is present, does not have an empty
    * value according to the section on CURIE and IRI Processing, and is not
    * set to XMLLiteral in the vocabulary
    * http://www.w3.org/1999/02/22-rdf-syntax-ns#. */
   if((context->datatype != NULL) && (strcmp(context->datatype,
      "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral") != 0))
   {
      /* The actual literal is either the value of @content (if present) or a
       * string created by concatenating the value of all descendant text nodes,
       * of the current element in turn. */
      if(context->content != NULL)
      {
         current_property_value = context->content;
      }
      else
      {
         current_property_value = context->plain_literal;
      }

      /* The final string includes the datatype
       * IRI, as described in [RDF-CONCEPTS], which will have been obtained
       * according to the section on CURIE and IRI Processing.
       * otherwise, as a plain literal if @datatype is present but has an
       * empty value according to the section on CURIE and IRI Processing. */
      if(strlen(context->datatype) > 0)
      {
         type = RDF_TYPE_TYPED_LITERAL;
      }
      else
      {
         type = RDF_TYPE_PLAIN_LITERAL;
      }
   }
   else if((context->datatype != NULL) && (strcmp(context->datatype,
      "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral") == 0))
   {
      /* otherwise, as an XML literal if @datatype is present and is set to
       * XMLLiteral in the vocabulary
       * http://www.w3.org/1999/02/22-rdf-syntax-ns#.
       * The value of the XML literal is a string created by serializing to
       * text, all nodes that are descendants of the current element, i.e., not
       * including the element itself, and giving it a datatype of XMLLiteral
       * in the vocabulary http://www.w3.org/1999/02/22-rdf-syntax-ns#. The
       * format of the resulting serialized content is as defined in Exclusive
       * XML Canonicalization Version [XML-EXC-C14N].
       * In order to maintain maximum portability of this literal, any children
       * of the current node that are elements must have the current XML
       * namespace declarations (if any) declared on the serialized element.
       * Since the child element node could also declare new XML namespaces,
       * the RDFa Processor must be careful to merge these together when
       * generating the serialized element definition. For avoidance of doubt,
       * any re-declarations on the child node must take precedence over
       * declarations that were active on the current node. */
      current_property_value = context->xml_literal;
      type = RDF_TYPE_XML_LITERAL;
   }
   else if(context->content != NULL)
   {
      /* otherwise, as an plain literal using the value of @content if
       * @content is present. */
      current_property_value = context->content;
      type = RDF_TYPE_PLAIN_LITERAL;
   }
   else if((context->rel_present == 0) && (context->rev_present == 0) &&
      (context->content == NULL))
   {
      /* otherwise, if the @rel, @rev, and @content attributes are not present,
       * as a resource obtained from one of the following: */
      if(context->resource != NULL)
      {
         /* by using the resource from @resource, if present, obtained
          * according to the section on CURIE and IRI Processing; */
         current_property_value = context->resource;
         type = RDF_TYPE_IRI;
      }
      else if(context->href != NULL)
      {
         /* otherwise, by using the IRI from @href, if present, obtained
          * according to the section on CURIE and IRI Processing; */
         current_property_value = context->href;
         type = RDF_TYPE_IRI;
      }
      else if(context->src != NULL)
      {
         /* otherwise, by using the IRI from @src, if present, obtained
          * according to the section on CURIE and IRI Processing. */
         current_property_value = context->src;
         type = RDF_TYPE_IRI;
      }
      else if((context->about == NULL) && (context->typed_resource != NULL))
      {
         /* otherwise, if @typeof is present and @about is not, the value of
          * typed resource. */
         current_property_value = context->typed_resource;
         type = RDF_TYPE_IRI;
      }
      else
      {
         /* otherwise as a plain literal. */
         current_property_value = context->plain_literal;
         type = RDF_TYPE_PLAIN_LITERAL;
      }
   }
   else
   {
      /* otherwise as a plain literal. */
      current_property_value = context->plain_literal;
      type = RDF_TYPE_PLAIN_LITERAL;
   }

   /* Additionally, if there is a value for current language then the value
    * of the plain literal should include this language information, as
    * described in [RDF-CONCEPTS]. The actual literal is either the value
    * of @content (if present) or a string created by concatenating the text
    * content of each of the descendant elements of the current element in
    * document order.
    *
    * NOTE: This happens automatically due to the way the code is setup. */

   if(context->inlist_present)
   {
      /* The current property value is then used with each predicate as
       * follows:
       * If the element also includes the @inlist attribute, the current
       * property value is added to the local list mapping as follows:
       * if the local list mapping does not contain a list associated with
       * the predicate IRI, instantiate a new list and add to local list
       * mappings add the current property value to the list associated
       * with the predicate IRI in the local list mapping */
      rdfa_establish_new_inlist_triples(
         context, context->property, current_property_value, type);
   }
   else
   {
      pptr = context->property->items;
      for(i = 0; i < context->property->num_items; i++)
      {
         /* Otherwise the current property value is used to generate a triple
          * as follows:
          * subject
          *   new subject
          * predicate
          *   full IRI
          * object
          *   current property value */
         rdfalistitem* curie = *pptr;
         rdftriple* triple = rdfa_create_triple(context->new_subject,
            (const char*)curie->data, current_property_value, type,
            context->datatype, context->language);

         context->default_graph_triple_callback(triple, context->callback_data);

         pptr++;
      }
   }
}