text: "use strict";
  
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports.default = void 0;
  
  var _IRIs = _interopRequireDefault(require("./IRIs"));
  
  var _N3DataFactory = _interopRequireWildcard(require("./N3DataFactory"));
  
  var _N3Util = require("./N3Util");
  
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
  
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
  
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  
  // **N3Writer** writes N3 documents.
  const DEFAULTGRAPH = _N3DataFactory.default.defaultGraph();
  
  const {
    rdf,
    xsd
  } = _IRIs.default; // Characters in literals that require escaping
  
  const escape = /["\\\t\n\r\b\f\u0000-\u0019\ud800-\udbff]/,
        escapeAll = /["\\\t\n\r\b\f\u0000-\u0019]|[\ud800-\udbff][\udc00-\udfff]/g,
        escapedCharacters = {
    '\\': '\\\\',
    '"': '\\"',
    '\t': '\\t',
    '\n': '\\n',
    '\r': '\\r',
    '\b': '\\b',
    '\f': '\\f'
  }; // ## Placeholder class to represent already pretty-printed terms
  
  class SerializedTerm extends _N3DataFactory.Term {
    // Pretty-printed nodes are not equal to any other node
    // (e.g., [] does not equal [])
    equals() {
      return false;
    }
  
  } // ## Constructor
  
  
  class N3Writer {
    constructor(outputStream, options) {
      // ### `_prefixRegex` matches a prefixed name or IRI that begins with one of the added prefixes
      this._prefixRegex = /$0^/; // Shift arguments if the first argument is not a stream
  
      if (outputStream && typeof outputStream.write !== 'function') options = outputStream, outputStream = null;
      options = options || {};
      this._lists = options.lists; // If no output stream given, send the output as string through the end callback
  
      if (!outputStream) {
        let output = '';
        this._outputStream = {
          write(chunk, encoding, done) {
            output += chunk;
            done && done();
          },
  
          end: done => {
            done && done(null, output);
          }
        };
        this._endStream = true;
      } else {
        this._outputStream = outputStream;
        this._endStream = options.end === undefined ? true : !!options.end;
      } // Initialize writer, depending on the format
  
  
      this._subject = null;
  
      if (!/triple|quad/i.test(options.format)) {
        this._lineMode = false;
        this._graph = DEFAULTGRAPH;
        this._baseIRI = options.baseIRI;
        this._prefixIRIs = Object.create(null);
        options.prefixes && this.addPrefixes(options.prefixes);
      } else {
        this._lineMode = true;
        this._writeQuad = this._writeQuadLine;
      }
    } // ## Private methods
    // ### Whether the current graph is the default graph
  
  
    get _inDefaultGraph() {
      return DEFAULTGRAPH.equals(this._graph);
    } // ### `_write` writes the argument to the output stream
  
  
    _write(string, callback) {
      this._outputStream.write(string, 'utf8', callback);
    } // ### `_writeQuad` writes the quad to the output stream
  
  
    _writeQuad(subject, predicate, object, graph, done) {
      try {
        // Write the graph's label if it has changed
        if (!graph.equals(this._graph)) {
          // Close the previous graph and start the new one
          this._write((this._subject === null ? '' : this._inDefaultGraph ? '.\n' : '\n}\n') + (DEFAULTGRAPH.equals(graph) ? '' : `${this._encodeIriOrBlank(graph)} {\n`));
  
          this._graph = graph;
          this._subject = null;
        } // Don't repeat the subject if it's the same
  
  
        if (subject.equals(this._subject)) {
          // Don't repeat the predicate if it's the same
          if (predicate.equals(this._predicate)) this._write(`, ${this._encodeObject(object)}`, done); // Same subject, different predicate
          else this._write(`;\n    ${this._encodePredicate(this._predicate = predicate)} ${this._encodeObject(object)}`, done);
        } // Different subject; write the whole quad
        else this._write(`${(this._subject === null ? '' : '.\n') + this._encodeSubject(this._subject = subject)} ${this._encodePredicate(this._predicate = predicate)} ${this._encodeObject(object)}`, done);
      } catch (error) {
        done && done(error);
      }
    } // ### `_writeQuadLine` writes the quad to the output stream as a single line
  
  
    _writeQuadLine(subject, predicate, object, graph, done) {
      // Write the quad without prefixes
      delete this._prefixMatch;
  
      this._write(this.quadToString(subject, predicate, object, graph), done);
    } // ### `quadToString` serializes a quad as a string
  
  
    quadToString(subject, predicate, object, graph) {
      return `${this._encodeSubject(subject)} ${this._encodeIriOrBlank(predicate)} ${this._encodeObject(object)}${graph && graph.value ? ` ${this._encodeIriOrBlank(graph)} .\n` : ' .\n'}`;
    } // ### `quadsToString` serializes an array of quads as a string
  
  
    quadsToString(quads) {
      return quads.map(t => {
        return this.quadToString(t.subject, t.predicate, t.object, t.graph);
      }).join('');
    } // ### `_encodeSubject` represents a subject
  
  
    _encodeSubject(entity) {
      return entity.termType === 'Quad' ? this._encodeQuad(entity) : this._encodeIriOrBlank(entity);
    } // ### `_encodeIriOrBlank` represents an IRI or blank node
  
  
    _encodeIriOrBlank(entity) {
      // A blank node or list is represented as-is
      if (entity.termType !== 'NamedNode') {
        // If it is a list head, pretty-print it
        if (this._lists && entity.value in this._lists) entity = this.list(this._lists[entity.value]);
        return 'id' in entity ? entity.id : `_:${entity.value}`;
      }
  
      let iri = entity.value; // Use relative IRIs if requested and possible
  
      if (this._baseIRI && iri.startsWith(this._baseIRI)) iri = iri.substr(this._baseIRI.length); // Escape special characters
  
      if (escape.test(iri)) iri = iri.replace(escapeAll, characterReplacer); // Try to represent the IRI as prefixed name
  
      const prefixMatch = this._prefixRegex.exec(iri);
  
      return !prefixMatch ? `<${iri}>` : !prefixMatch[1] ? iri : this._prefixIRIs[prefixMatch[1]] + prefixMatch[2];
    } // ### `_encodeLiteral` represents a literal
  
  
    _encodeLiteral(literal) {
      // Escape special characters
      let value = literal.value;
      if (escape.test(value)) value = value.replace(escapeAll, characterReplacer); // Write a language-tagged literal
  
      if (literal.language) return `"${value}"@${literal.language}`; // Write dedicated literals per data type
  
      if (this._lineMode) {
        // Only abbreviate strings in N-Triples or N-Quads
        if (literal.datatype.value === xsd.string) return `"${value}"`;
      } else {
        // Use common datatype abbreviations in Turtle or TriG
        switch (literal.datatype.value) {
          case xsd.string:
            return `"${value}"`;
  
          case xsd.boolean:
            if (value === 'true' || value === 'false') return value;
            break;
  
          case xsd.integer:
            if (/^[+-]?\d+$/.test(value)) return value;
            break;
  
          case xsd.decimal:
            if (/^[+-]?\d*\.\d+$/.test(value)) return value;
            break;
  
          case xsd.double:
            if (/^[+-]?(?:\d+\.\d*|\.?\d+)[eE][+-]?\d+$/.test(value)) return value;
            break;
        }
      } // Write a regular datatyped literal
  
  
      return `"${value}"^^${this._encodeIriOrBlank(literal.datatype)}`;
    } // ### `_encodePredicate` represents a predicate
  
  
    _encodePredicate(predicate) {
      return predicate.value === rdf.type ? 'a' : this._encodeIriOrBlank(predicate);
    } // ### `_encodeObject` represents an object
  
  
    _encodeObject(object) {
      switch (object.termType) {
        case 'Quad':
          return this._encodeQuad(object);
  
        case 'Literal':
          return this._encodeLiteral(object);
  
        default:
          return this._encodeIriOrBlank(object);
      }
    } // ### `_encodeQuad` encodes an RDF* quad
  
  
    _encodeQuad({
      subject,
      predicate,
      object,
      graph
    }) {
      return `<<${this._encodeSubject(subject)} ${this._encodePredicate(predicate)} ${this._encodeObject(object)}${(0, _N3Util.isDefaultGraph)(graph) ? '' : ` ${this._encodeIriOrBlank(graph)}`}>>`;
    } // ### `_blockedWrite` replaces `_write` after the writer has been closed
  
  
    _blockedWrite() {
      throw new Error('Cannot write because the writer has been closed.');
    } // ### `addQuad` adds the quad to the output stream
  
  
    addQuad(subject, predicate, object, graph, done) {
      // The quad was given as an object, so shift parameters
      if (object === undefined) this._writeQuad(subject.subject, subject.predicate, subject.object, subject.graph, predicate); // The optional `graph` parameter was not provided
      else if (typeof graph === 'function') this._writeQuad(subject, predicate, object, DEFAULTGRAPH, graph); // The `graph` parameter was provided
        else this._writeQuad(subject, predicate, object, graph || DEFAULTGRAPH, done);
    } // ### `addQuads` adds the quads to the output stream
  
  
    addQuads(quads) {
      for (let i = 0; i < quads.length; i++) this.addQuad(quads[i]);
    } // ### `addPrefix` adds the prefix to the output stream
  
  
    addPrefix(prefix, iri, done) {
      const prefixes = {};
      prefixes[prefix] = iri;
      this.addPrefixes(prefixes, done);
    } // ### `addPrefixes` adds the prefixes to the output stream
  
  
    addPrefixes(prefixes, done) {
      // Ignore prefixes if not supported by the serialization
      if (!this._prefixIRIs) return done && done(); // Write all new prefixes
  
      let hasPrefixes = false;
  
      for (let prefix in prefixes) {
        let iri = prefixes[prefix];
        if (typeof iri !== 'string') iri = iri.value;
        hasPrefixes = true; // Finish a possible pending quad
  
        if (this._subject !== null) {
          this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
  
          this._subject = null, this._graph = '';
        } // Store and write the prefix
  
  
        this._prefixIRIs[iri] = prefix += ':';
  
        this._write(`@prefix ${prefix} <${iri}>.\n`);
      } // Recreate the prefix matcher
  
  
      if (hasPrefixes) {
        let IRIlist = '',
            prefixList = '';
  
        for (const prefixIRI in this._prefixIRIs) {
          IRIlist += IRIlist ? `|${prefixIRI}` : prefixIRI;
          prefixList += (prefixList ? '|' : '') + this._prefixIRIs[prefixIRI];
        }
  
        IRIlist = IRIlist.replace(/[\]\/\(\)\*\+\?\.\\\$]/g, '\\$&');
        this._prefixRegex = new RegExp(`^(?:${prefixList})[^\/]*$|` + `^(${IRIlist})([a-zA-Z][\\-_a-zA-Z0-9]*)$`);
      } // End a prefix block with a newline
  
  
      this._write(hasPrefixes ? '\n' : '', done);
    } // ### `blank` creates a blank node with the given content
  
  
    blank(predicate, object) {
      let children = predicate,
          child,
          length; // Empty blank node
  
      if (predicate === undefined) children = []; // Blank node passed as blank(Term("predicate"), Term("object"))
      else if (predicate.termType) children = [{
          predicate: predicate,
          object: object
        }]; // Blank node passed as blank({ predicate: predicate, object: object })
        else if (!('length' in predicate)) children = [predicate];
  
      switch (length = children.length) {
        // Generate an empty blank node
        case 0:
          return new SerializedTerm('[]');
        // Generate a non-nested one-triple blank node
  
        case 1:
          child = children[0];
          if (!(child.object instanceof SerializedTerm)) return new SerializedTerm(`[ ${this._encodePredicate(child.predicate)} ${this._encodeObject(child.object)} ]`);
        // Generate a multi-triple or nested blank node
  
        default:
          let contents = '['; // Write all triples in order
  
          for (let i = 0; i < length; i++) {
            child = children[i]; // Write only the object is the predicate is the same as the previous
  
            if (child.predicate.equals(predicate)) contents += `, ${this._encodeObject(child.object)}`; // Otherwise, write the predicate and the object
            else {
                contents += `${(i ? ';\n  ' : '\n  ') + this._encodePredicate(child.predicate)} ${this._encodeObject(child.object)}`;
                predicate = child.predicate;
              }
          }
  
          return new SerializedTerm(`${contents}\n]`);
      }
    } // ### `list` creates a list node with the given content
  
  
    list(elements) {
      const length = elements && elements.length || 0,
            contents = new Array(length);
  
      for (let i = 0; i < length; i++) contents[i] = this._encodeObject(elements[i]);
  
      return new SerializedTerm(`(${contents.join(' ')})`);
    } // ### `end` signals the end of the output stream
  
  
    end(done) {
      // Finish a possible pending quad
      if (this._subject !== null) {
        this._write(this._inDefaultGraph ? '.\n' : '\n}\n');
  
        this._subject = null;
      } // Disallow further writing
  
  
      this._write = this._blockedWrite; // Try to end the underlying stream, ensuring done is called exactly one time
  
      let singleDone = done && ((error, result) => {
        singleDone = null, done(error, result);
      });
  
      if (this._endStream) {
        try {
          return this._outputStream.end(singleDone);
        } catch (error) {
          /* error closing stream */
        }
      }
  
      singleDone && singleDone();
    }
  
  } // Replaces a character by its escaped version
  
  
  exports.default = N3Writer;
  
  function characterReplacer(character) {
    // Replace a single character by its escaped version
    let result = escapedCharacters[character];
  
    if (result === undefined) {
      // Replace a single character with its 4-bit unicode escape sequence
      if (character.length === 1) {
        result = character.charCodeAt(0).toString(16);
        result = '\\u0000'.substr(0, 6 - result.length) + result;
      } // Replace a surrogate pair with its 8-bit unicode escape sequence
      else {
          result = ((character.charCodeAt(0) - 0xD800) * 0x400 + character.charCodeAt(1) + 0x2400).toString(16);
          result = '\\U00000000'.substr(0, 10 - result.length) + result;
        }
    }
  
    return result;
  }