compactIri method

CompactIri compactIri(
  1. IriTerm term,
  2. IriRole role,
  3. String? baseUri,
  4. Map<String, String> iriToPrefixMap,
  5. Map<String, String> prefixCandidates,
  6. Map<String, String> customPrefixes,
)

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);
}