001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.oscal.lib;
007
008import java.net.URI;
009import java.util.List;
010import java.util.regex.Matcher;
011import java.util.regex.Pattern;
012
013import dev.metaschema.core.util.ObjectUtils;
014import dev.metaschema.oscal.lib.model.BackMatter.Resource;
015import dev.metaschema.oscal.lib.model.BackMatter.Resource.Rlink;
016import edu.umd.cs.findbugs.annotations.NonNull;
017import edu.umd.cs.findbugs.annotations.Nullable;
018
019public final class OscalUtils {
020  private static final Pattern INTERNAL_REFERENCE_FRAGMENT_PATTERN = Pattern.compile("^#(.+)$");
021
022  private OscalUtils() {
023    // disable construction
024  }
025
026  @SuppressWarnings("PMD.OnlyOneReturn") // readability
027  public static boolean isInternalReference(@NonNull URI uri) {
028    if (uri.isAbsolute()) {
029      return false;
030    }
031
032    String schemeSpecificPart = uri.getSchemeSpecificPart();
033    return uri.getScheme() == null && (schemeSpecificPart == null || schemeSpecificPart.isEmpty())
034        && uri.getFragment() != null;
035  }
036
037  /**
038   * Get the id based on a URI's fragment.
039   *
040   * @param fragment
041   *          the URI to extract the identifier from
042   * @return the identifier
043   * @throws IllegalArgumentException
044   *           if the fragment does not contain an identifier
045   */
046  @NonNull
047  public static String internalReferenceFragmentToId(@NonNull URI fragment) {
048    return internalReferenceFragmentToId(ObjectUtils.notNull(fragment.toString()));
049  }
050
051  /**
052   * Get the id based on a URI's fragment.
053   *
054   * @param fragment
055   *          the URI to extract the identifier from
056   * @return the identifier
057   * @throws IllegalArgumentException
058   *           if the fragment does not contain an identifier
059   */
060  @NonNull
061  public static String internalReferenceFragmentToId(@NonNull String fragment) {
062    Matcher matcher = INTERNAL_REFERENCE_FRAGMENT_PATTERN.matcher(fragment);
063    String retval;
064    if (matcher.matches()) {
065      retval = ObjectUtils.notNull(matcher.group(1));
066    } else {
067      throw new IllegalArgumentException(String.format("The fragment '%s' does not match the pattern '%s'", fragment,
068          INTERNAL_REFERENCE_FRAGMENT_PATTERN.pattern()));
069    }
070    return retval;
071  }
072
073  @Nullable
074  public static Rlink findMatchingRLink(@NonNull Resource resource, @Nullable String preferredMediaType) {
075    // find a suitable rlink reference
076    List<Rlink> rlinks = resource.getRlinks();
077
078    Rlink retval = null;
079    if (rlinks != null) {
080      // check if there is a matching rlink for the mime type
081      if (preferredMediaType != null) {
082        // find preferred mime type first
083        retval = rlinks.stream().filter(rlink -> preferredMediaType.equals(rlink.getMediaType())).findFirst()
084            .orElse(null);
085      } else {
086        // use the first one instead
087        retval = rlinks.stream().findFirst().orElse(null);
088      }
089    }
090    return retval;
091  }
092}