writeTerm method
Convert RDF terms to Turtle syntax string representation
Implementation
String writeTerm(
RdfTerm term, {
Map<String, String> prefixesByIri = const {},
Map<BlankNodeTerm, String> blankNodeLabels = const {},
String? baseUri,
bool isPredicate = false,
}) {
switch (term) {
case IriTerm _:
if (term == Rdf.type) {
return 'a';
} else {
// Check if the predicate is a known prefix
final iri = term.iri;
final (
baseIri,
localPart,
) = RdfNamespaceMappings.extractNamespaceAndLocalPart(
iri,
allowNumericLocalNames: _options.useNumericLocalNames,
);
// If we have a valid local part
if (localPart.isNotEmpty || baseIri == iri) {
final prefix = prefixesByIri[baseIri];
if (prefix != null) {
// Handle empty prefix specially
return prefix.isEmpty ? ':$localPart' : '$prefix:$localPart';
} else {
final prefix = prefixesByIri[iri];
if (prefix != null) {
return prefix.isEmpty ? ':' : '$prefix:';
}
}
}
}
// For predicates or terms with baseUri:
// - For predicates, always use prefixes if they exist (handled above)
// - For subject/object under baseUri, check if there's a better prefix
// that is longer than baseUri, if not, use relative IRI
// If we have a baseUri and this term starts with it
if (baseUri != null && term.iri.startsWith(baseUri)) {
// For non-predicates that start with baseUri, check if there's a
// namespace prefix that's longer than baseUri
if (!isPredicate) {
bool betterPrefixExists = false;
for (final entry in prefixesByIri.entries) {
final namespace = entry.key;
// If there's a namespace that:
// 1. Is a prefix of this IRI
// 2. Is longer than baseUri
// 3. Has an associated prefix
if (term.iri.startsWith(namespace) &&
namespace.length > baseUri.length) {
betterPrefixExists = true;
break;
}
}
// If no better prefix exists, use relative IRI
if (!betterPrefixExists) {
final localPart = term.iri.substring(baseUri.length);
return '<$localPart>';
}
// Otherwise, fall through to the full IRI case below
// (which might still find a prefix to use)
}
}
return '<${term.iri}>';
case BlankNodeTerm blankNode:
// Use the pre-generated label for this blank node
var label = blankNodeLabels[blankNode];
if (label == null) {
// This shouldn't happen if all blank nodes were collected correctly
_log.warning(
'No label generated for blank node, using fallback label',
);
label = 'b${identityHashCode(blankNode)}';
blankNodeLabels[blankNode] = label;
}
return '_:$label';
case LiteralTerm literal:
// Special cases for native Turtle literal representations
if (literal.datatype == Xsd.integer) {
return literal.value;
}
if (literal.datatype == Xsd.decimal) {
return literal.value;
}
if (literal.datatype == Xsd.boolean) {
return literal.value;
}
var escapedLiteralValue = _escapeTurtleString(literal.value);
if (literal.language != null) {
return '"$escapedLiteralValue"@${literal.language}';
}
if (literal.datatype != Xsd.string) {
return '"$escapedLiteralValue"^^${writeTerm(literal.datatype, prefixesByIri: prefixesByIri, blankNodeLabels: blankNodeLabels)}';
}
return '"$escapedLiteralValue"';
}
}