identifiers.js

/**
 * Module to calculate JSKOS mapping identifiers.
 */

import sha1 from "./sha1.js"

// Reduce JSKOS set to members with URI.
function reduceSet(set) {
  return set.map(member => member && member.uri).filter(Boolean)
}

// Tell which concept bundle field is used.
function memberField(bundle) {
  return ["memberSet", "memberList", "memberChoice"].find(f => bundle[f])
}

// Reduce JSKOS concept bundle to memberSet/List/Choice with member URIs only.
function reduceBundle(bundle) {
  const field = memberField(bundle)
  const set = bundle[field] ? reduceSet(bundle[field]) : []
  return {
    [set.length > 1 ? field : "memberSet"]: set.map(uri => ({uri})),
  }
}

// Reduce mapping to reduced fields from, to, and type.
export function mappingContent(mapping) {
  const { from, to, type } = mapping
  let result = {
    from: reduceBundle(from || {}),
    to: reduceBundle(to || {}),
    type: [
      type && type[0] || "http://www.w3.org/2004/02/skos/core#mappingRelation",
    ],
  }
  for (let side of ["from", "to"]) {
    if ((result[side][memberField(result[side])] || []).length == 0) {
      let scheme = mapping[side + "Scheme"]
      if (scheme && scheme.uri) {
        // Create new object to remove all unnecessary properties.
        result[side + "Scheme"] = { uri: scheme.uri }
      }
    }
  }
  return result
}

// Get a sorted list of member URIs.
export function mappingMembers(mapping) {
  const { from, to } = mapping
  const memberUris = [ from, to ].filter(Boolean)
    .map(bundle => reduceSet(bundle[memberField(bundle)] || []))
  return [].concat(...memberUris).sort()
}

/**
 * Returns a mapping content identifier. The identifier starts with `urn:jskos:mapping:content:`
 * and takes concepts and type into consideration. It uses the `mappingContent` function to get
 * relevant properties from the mapping.
 * @memberof module:jskos-tools
 */
export function mappingContentIdentifier(mapping) {
  const json = JSON.stringify(mappingContent(mapping), ["from","fromScheme","to","toScheme","type","memberSet","memberList","memberChoice","uri"])
  return "urn:jskos:mapping:content:" + sha1(json+"\n")
}

/**
 * @memberof module:jskos-tools
 */
export function mappingMembersIdentifier(mapping) {
  const json = JSON.stringify(mappingMembers(mapping))
  return "urn:jskos:mapping:members:" + sha1(json+"\n")
}

/**
 * @memberof module:jskos-tools
 */
export function addMappingIdentifiers(mapping) {
  const identifier = (mapping.identifier || []).filter(
    id => id && !id.startsWith("urn:jskos:mapping:"),
  ).concat([
    mappingMembersIdentifier(mapping),
    mappingContentIdentifier(mapping),
  ]).sort()
  return Object.assign({}, mapping, {identifier})
}

function compare(mapping1, mapping2, prefix) {
  mapping1 = mapping1 && addMappingIdentifiers(mapping1)
  mapping2 = mapping2 && addMappingIdentifiers(mapping2)
  let id1 = mapping1 && mapping1.identifier ? mapping1.identifier.find(element => element.startsWith(prefix)) : null
  let id2 = mapping2 && mapping2.identifier ? mapping2.identifier.find(element => element.startsWith(prefix)) : null
  return id1 == id2
}

/**
 * @memberof module:jskos-tools
 */
export function compareMappings(mapping1, mapping2) {
  return compare(mapping1, mapping2, "urn:jskos:mapping:content:")
}
export const compareMappingContent = compareMappings

/**
 * @memberof module:jskos-tools
 */
export function compareMappingMembers(mapping1, mapping2) {
  return compare(mapping1, mapping2, "urn:jskos:mapping:members:")
}