compactIri method
Implementation
CompactIri compactIri(
IriTerm term,
IriRole role,
String? baseUri,
Map<String, String> iriToPrefixMap,
Map<String, String> prefixCandidates,
Map<String, String> customPrefixes) {
if (role == IriRole.predicate &&
_settings.specialPredicates.contains(term)) {
return SpecialIri(term);
}
if (role == IriRole.datatype && _settings.specialDatatypes.contains(term)) {
return SpecialIri(term);
}
// In Turtle, predicates cannot be relativized (they must use prefixes or full IRIs)
final allowedTypes = _settings.allowedCompactionTypes[role] ??
IriCompactionType.values.toSet();
final relativized = allowedTypes.contains(IriCompactionType.relative)
? relativizeIri(term.iri, baseUri)
: term.iri;
final relativeUrl = relativized == term.iri ? null : relativized;
if (relativeUrl != null && relativeUrl.isEmpty) {
// If we have a relative URL that is empty, we do not need to check
// for better matching prefixes, but use the relative URL directly
return RelativeIri(relativeUrl);
}
final iri = term.iri;
final prefixAllowed = allowedTypes.contains(IriCompactionType.prefixed);
final fullAllowed = allowedTypes.contains(IriCompactionType.full);
if (prefixAllowed && iriToPrefixMap.containsKey(iri)) {
final prefix = iriToPrefixMap[iri]!;
return PrefixedIri(prefix, iri, null);
}
if (relativeUrl != null) {
// Special case: if we have a relative URL, check the custom prefixes
// to see if any of them lead to a shorter local part than the relative URL
if (prefixAllowed) {
if (_bestMatch(iri, customPrefixes)
case (String bestPrefix, String bestMatch)) {
final localPart = _extractLocalPart(iri, bestMatch);
if (localPart.length < relativeUrl.length &&
_isValidIriLocalPart(localPart)) {
// If the local part of the best match is shorter than the relative one, use it instead
return PrefixedIri(bestPrefix, bestMatch, localPart);
}
}
}
// Usually we want to use the relative URL if we have one
return RelativeIri(relativeUrl);
}
// For prefix match, use the longest matching prefix (most specific)
// This handles overlapping prefixes correctly (e.g., http://example.org/ and http://example.org/vocabulary/)
if (prefixAllowed) {
if (_bestMatch(iri, prefixCandidates)
case (String bestPrefix, String bestMatch)) {
// If we have a prefix match, use it
final localPart = _extractLocalPart(iri, bestMatch);
if (_isValidIriLocalPart(localPart)) {
return PrefixedIri(bestPrefix, bestMatch, localPart);
}
}
}
if (prefixAllowed && _settings.generateMissingPrefixes) {
// No existing prefix found, generate a new one using namespace mappings
// Extract namespace from IRI
final (
namespace,
localPart,
) = RdfNamespaceMappings.extractNamespaceAndLocalPart(
iri,
);
if (fullAllowed &&
(localPart.isEmpty || !_isValidIriLocalPart(localPart))) {
// If we have no local part, we cannot generate a prefix
return FullIri(iri);
}
// Warn if https:// is used and http:// is in the prefix map for the same path (or the other way around)
_warnSchemaNamespaceMismatch(
iri, namespace, prefixCandidates, "http://", "https://");
_warnSchemaNamespaceMismatch(
iri, namespace, prefixCandidates, "https://", "http://");
// Skip generating prefixes for protocol-only URIs like "http://" or "https://"
if (fullAllowed &&
(namespace == "http://" ||
namespace == "https://" ||
namespace == "ftp://" ||
namespace == "file://")) {
// If it's just a protocol URI, don't add a prefix
return FullIri(iri);
}
// Skip generating prefixes for namespaces that don't end with "/" or "#"
// since these are not proper namespace delimiters in RDF
if (fullAllowed &&
(!namespace.endsWith('/') && !namespace.endsWith('#'))) {
// For IRIs without proper namespace delimiters, don't add a prefix
return FullIri(iri);
}
// Get or generate a prefix for this namespace
final (prefix, _) = _namespaceMappings.getOrGeneratePrefix(
namespace,
customMappings: prefixCandidates,
);
return PrefixedIri(prefix, namespace, localPart);
}
if (!fullAllowed) {
throw ArgumentError(
'Cannot compact IRI "$iri" with role $role: '
'no allowed compaction types for this role.',
);
}
return FullIri(iri);
}