concept-scheme.js

const regexChars = /[\\^$.*+?()[\]{}|]/g
const regexEscape = string => string.replace(regexChars, "\\$&")

// Special characters that uriPattern can be build with
// Some other special characters such as [ and ] will not work!
const notationEscapeChars = /[%ÄÖÜäöü ]/g

/**
 * A [JSKOS Concept Scheme](http://gbv.github.io/jskos/jskos.html#concept-schemes).
 * @memberof module:jskos-tools
 * @example
 * const { ConceptScheme } = require('jskos-tools')
 * let scheme = new ConceptScheme({
 *   namespace: "http://example.org/",
 *   notationPattern: "[0-9]+"
 * })
 */
class ConceptScheme {

  /**
   * @param {object} [jskos] - object that's copied as scheme (shallow copy)
   */
  constructor (scheme={}) {
    Object.assign(this, scheme)

    if (!this.notationPattern) {
      this.notationPattern = ".+"
    }

    if (!this.uriPattern && this.namespace) {
      this.uriPattern = "^" + regexEscape(this.namespace)
      const escaped = this.notationPattern.replace(notationEscapeChars, encodeURI)
      this.uriPattern += "(" + escaped  + ")$"
    }

    this.NOTATION_REGEX = RegExp("^(" + this.notationPattern + ")$")

    if (this.uriPattern) {
      this.URI_REGEX = RegExp(this.uriPattern)
    }
  }

  /**
   * Check whether a string is a valid notation as defined by `notationPattern`.
   * @param {string} notation
   * @returns {array|null}
   */
  isValidNotation(notation) {
    return this.NOTATION_REGEX.exec(notation)
  }

  /**
   * Check whether URI belongs to the scheme, return local notation on success.
   * Requires scheme to have `uriPattern` or `namespace`.
   * @param {string} uri
   * @returns {string|undefined}
   */
  notationFromUri(uri) {
    if (this.URI_REGEX) {
      const match = this.URI_REGEX.exec(uri)
      if (match) {
        return decodeURI(match[1])
      }
    }
  }

  /**
   * Map local notation to URI. Does not check whether notation is valid!
   * Requires scheme to have `uriPattern` or `namespace`.
   * @param {string} notation
   * @example scheme.uriFromNotation("123") // http://example.org/123
   */
  uriFromNotation (notation) {
    if (this.uriPattern) {
      notation = encodeURIComponent(notation)
      return this.uriPattern
        .replace(/^\^|\$$/g, "").replace(/\\/g, "").replace(/\(.*\)/, notation)
    }
  }

  /**
   * Check whether URI belongs to the scheme, return concept on success.
   * Requires scheme to have `uriPattern` or `namespace`.
   * @param {string} uri
   * @param {object} [options] boolean flags `inScheme` and `toConcept`
   */
  conceptFromUri (uri, options={}) {
    const notation = this.notationFromUri(uri)
    if (notation === undefined) {
      return
    }
    const concept = { uri, notation: [notation] }
    if (options.inScheme) {
      concept.inScheme = [{uri: this.uri }]
    }
    if (options.topConcept) {
      concept.topConceptOf = [{uri: this.uri }]
    }
    return concept
  }

  /**
   * Map local notation to concept, if notation is valid. Requires scheme to
   * have `uriPattern` or `namespace`.
   * @param {string} notation
   * @param {object} [options] same as conceptFromUri options
   */
  conceptFromNotation (notation, options) {
    if (this.isValidNotation(notation)) {
      return this.conceptFromUri(this.uriFromNotation(notation), options)
    }
  }
}

export default ConceptScheme