UNPKG

atom-languageclient

Version:
381 lines 52.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TextEditorSyncAdapter = void 0; const convert_1 = require("../convert"); const languageclient_1 = require("../languageclient"); const apply_edit_adapter_1 = require("./apply-edit-adapter"); const atom_1 = require("atom"); const Utils = require("../utils"); /** * Public: Synchronizes the documents between Atom and the language server by notifying each end of changes, opening, * closing and other events as well as sending and applying changes either in whole or in part depending on what the * language server supports. */ class DocumentSyncAdapter { /** * Public: Create a new {DocumentSyncAdapter} for the given language server. * * @param _connection A {LanguageClientConnection} to the language server to be kept in sync. * @param documentSync The document syncing options. * @param _editorSelector A predicate function that takes a {TextEditor} and returns a {boolean} indicating whether * this adapter should care about the contents of the editor. * @param _getLanguageIdFromEditor A function that returns a {string} of `languageId` used for `textDocument/didOpen` * notification. */ constructor(_connection, _editorSelector, documentSync, _reportBusyWhile, _getLanguageIdFromEditor) { this._connection = _connection; this._editorSelector = _editorSelector; this._reportBusyWhile = _reportBusyWhile; this._getLanguageIdFromEditor = _getLanguageIdFromEditor; this._disposable = new atom_1.CompositeDisposable(); this._editors = new WeakMap(); this._versions = new Map(); if (typeof documentSync === "object") { this._documentSync = documentSync; } else { this._documentSync = { change: documentSync || languageclient_1.TextDocumentSyncKind.Full, }; } this._disposable.add(atom.textEditors.observe(this.observeTextEditor.bind(this))); } /** * Public: Determine whether this adapter can be used to adapt a language server based on the serverCapabilities * matrix textDocumentSync capability either being Full or Incremental. * * @param serverCapabilities The {ServerCapabilities} of the language server to consider. * @returns A {Boolean} indicating adapter can adapt the server based on the given serverCapabilities. */ static canAdapt(serverCapabilities) { return this.canAdaptV2(serverCapabilities) || this.canAdaptV3(serverCapabilities); } static canAdaptV2(serverCapabilities) { return (serverCapabilities.textDocumentSync === languageclient_1.TextDocumentSyncKind.Incremental || serverCapabilities.textDocumentSync === languageclient_1.TextDocumentSyncKind.Full); } static canAdaptV3(serverCapabilities) { const options = serverCapabilities.textDocumentSync; return (options !== null && typeof options === "object" && (options.change === languageclient_1.TextDocumentSyncKind.Incremental || options.change === languageclient_1.TextDocumentSyncKind.Full)); } /** Dispose this adapter ensuring any resources are freed and events unhooked. */ dispose() { this._disposable.dispose(); } /** * Examine a {TextEditor} and decide if we wish to observe it. If so ensure that we stop observing it when it is * closed or otherwise destroyed. * * @param editor A {TextEditor} to consider for observation. */ observeTextEditor(editor) { const listener = editor.observeGrammar((_grammar) => this._handleGrammarChange(editor)); this._disposable.add(editor.onDidDestroy(() => { this._disposable.remove(listener); listener.dispose(); })); this._disposable.add(listener); if (!this._editors.has(editor) && this._editorSelector(editor)) { this._handleNewEditor(editor); } } _handleGrammarChange(editor) { const sync = this._editors.get(editor); if (sync != null && !this._editorSelector(editor)) { this._editors.delete(editor); this._disposable.remove(sync); sync.didClose(); sync.dispose(); } else if (sync == null && this._editorSelector(editor)) { this._handleNewEditor(editor); } } _handleNewEditor(editor) { const sync = new TextEditorSyncAdapter(editor, this._connection, this._documentSync, this._versions, this._reportBusyWhile, this._getLanguageIdFromEditor); this._editors.set(editor, sync); this._disposable.add(sync); this._disposable.add(editor.onDidDestroy(() => { const destroyedSync = this._editors.get(editor); if (destroyedSync) { this._editors.delete(editor); this._disposable.remove(destroyedSync); destroyedSync.dispose(); } })); } getEditorSyncAdapter(editor) { return this._editors.get(editor); } } exports.default = DocumentSyncAdapter; /** Public: Keep a single {TextEditor} in sync with a given language server. */ class TextEditorSyncAdapter { /** * Public: Create a {TextEditorSyncAdapter} in sync with a given language server. * * @param _editor A {TextEditor} to keep in sync. * @param _connection A {LanguageClientConnection} to a language server to keep in sync. * @param _documentSync The document syncing options. */ constructor(_editor, _connection, _documentSync, _versions, _reportBusyWhile, _getLanguageIdFromEditor) { this._editor = _editor; this._connection = _connection; this._documentSync = _documentSync; this._versions = _versions; this._reportBusyWhile = _reportBusyWhile; this._getLanguageIdFromEditor = _getLanguageIdFromEditor; this._disposable = new atom_1.CompositeDisposable(); this._fakeDidChangeWatchedFiles = atom.project.onDidChangeFiles == null; const changeTracking = this.setupChangeTracking(_documentSync); if (changeTracking != null) { this._disposable.add(changeTracking); } // These handlers are attached only if server supports them if (_documentSync.willSave) { this._disposable.add(_editor.getBuffer().onWillSave(this.willSave.bind(this))); } if (_documentSync.willSaveWaitUntil) { this._disposable.add(_editor.getBuffer().onWillSave(this.willSaveWaitUntil.bind(this))); } // Send close notifications unless it's explicitly disabled if (_documentSync.openClose !== false) { this._disposable.add(_editor.onDidDestroy(this.didClose.bind(this))); } this._disposable.add(_editor.onDidSave(this.didSave.bind(this)), _editor.onDidChangePath(this.didRename.bind(this))); this._currentUri = this.getEditorUri(); if (_documentSync.openClose !== false) { this.didOpen(); } } /** The change tracking disposable listener that will ensure that changes are sent to the language server as appropriate. */ setupChangeTracking(documentSync) { switch (documentSync.change) { case languageclient_1.TextDocumentSyncKind.Full: return this._editor.onDidChange(this.sendFullChanges.bind(this)); case languageclient_1.TextDocumentSyncKind.Incremental: return this._editor.getBuffer().onDidChangeText(this.sendIncrementalChanges.bind(this)); } return null; } /** Dispose this adapter ensuring any resources are freed and events unhooked. */ dispose() { this._disposable.dispose(); } /** Get the languageId field that will be sent to the language server by simply using the `_getLanguageIdFromEditor`. */ getLanguageId() { return this._getLanguageIdFromEditor(this._editor); } /** * Public: Create a {VersionedTextDocumentIdentifier} for the document observed by this adapter including both the Uri * and the current Version. */ getVersionedTextDocumentIdentifier() { return { uri: this.getEditorUri(), version: this._getVersion(this._editor.getPath() || ""), }; } /** Public: Send the entire document to the language server. This is used when operating in Full (1) sync mode. */ sendFullChanges() { if (!this._isPrimaryAdapter()) { return; } // Multiple editors, we are not first this._bumpVersion(); this._connection.didChangeTextDocument({ textDocument: this.getVersionedTextDocumentIdentifier(), contentChanges: [{ text: this._editor.getText() }], }); } /** * Public: Send the incremental text changes to the language server. This is used when operating in Incremental (2) sync mode. * * @param event The event fired by Atom to indicate the document has stopped changing including a list of changes * since the last time this event fired for this text editor. NOTE: The order of changes in the event is guaranteed * top to bottom. Language server expects this in reverse. */ sendIncrementalChanges(event) { if (event.changes.length > 0) { if (!this._isPrimaryAdapter()) { return; } // Multiple editors, we are not first this._bumpVersion(); this._connection.didChangeTextDocument({ textDocument: this.getVersionedTextDocumentIdentifier(), contentChanges: event.changes.map(TextEditorSyncAdapter.textEditToContentChange).reverse(), }); } } /** * Public: Convert an Atom {TextEditEvent} to a language server {TextDocumentContentChangeEvent} object. * * @param change The Atom {TextEditEvent} to convert. * @returns A {TextDocumentContentChangeEvent} that represents the converted {TextEditEvent}. */ static textEditToContentChange(change) { return { range: convert_1.default.atomRangeToLSRange(change.oldRange), rangeLength: change.oldText.length, text: change.newText, }; } _isPrimaryAdapter() { const lowestIdForBuffer = Math.min(...atom.workspace .getTextEditors() .filter((t) => t.getBuffer() === this._editor.getBuffer()) .map((t) => t.id)); return lowestIdForBuffer === this._editor.id; } _bumpVersion() { const filePath = this._editor.getPath(); if (filePath == null) { return; } this._versions.set(filePath, this._getVersion(filePath) + 1); } /** * Ensure when the document is opened we send notification to the language server so it can load it in and keep track * of diagnostics etc. */ didOpen() { const filePath = this._editor.getPath(); if (filePath == null) { return; } // Not yet saved if (!this._isPrimaryAdapter()) { return; } // Multiple editors, we are not first this._connection.didOpenTextDocument({ textDocument: { uri: this.getEditorUri(), languageId: this.getLanguageId().toLowerCase(), version: this._getVersion(filePath), text: this._editor.getText(), }, }); } _getVersion(filePath) { return this._versions.get(filePath) || 1; } /** Called when the {TextEditor} is closed and sends the 'didCloseTextDocument' notification to the connected language server. */ didClose() { if (this._editor.getPath() == null) { return; } // Not yet saved const fileStillOpen = atom.workspace.getTextEditors().find((t) => t.getBuffer() === this._editor.getBuffer()); if (fileStillOpen) { return; // Other windows or editors still have this file open } this._connection.didCloseTextDocument({ textDocument: { uri: this.getEditorUri() } }); } /** Called just before the {TextEditor} saves and sends the 'willSaveTextDocument' notification to the connected language server. */ willSave() { if (!this._isPrimaryAdapter()) { return; } const uri = this.getEditorUri(); this._connection.willSaveTextDocument({ textDocument: { uri }, reason: languageclient_1.TextDocumentSaveReason.Manual, }); } /** * Called just before the {TextEditor} saves, sends the 'willSaveWaitUntilTextDocument' request to the connected * language server and waits for the response before saving the buffer. */ willSaveWaitUntil() { return __awaiter(this, void 0, void 0, function* () { if (!this._isPrimaryAdapter()) { return Promise.resolve(); } const buffer = this._editor.getBuffer(); const uri = this.getEditorUri(); const title = this._editor.getLongTitle(); const applyEditsOrTimeout = Utils.promiseWithTimeout(2500, // 2.5 seconds timeout this._connection.willSaveWaitUntilTextDocument({ textDocument: { uri }, reason: languageclient_1.TextDocumentSaveReason.Manual, })) .then((edits) => { const cursor = this._editor.getCursorBufferPosition(); apply_edit_adapter_1.default.applyEdits(buffer, convert_1.default.convertLsTextEdits(edits)); this._editor.setCursorBufferPosition(cursor); }) .catch((err) => { atom.notifications.addError("On-save action failed", { description: `Failed to apply edits to ${title}`, detail: err.message, }); return; }); const withBusySignal = this._reportBusyWhile(`Applying on-save edits for ${title}`, () => applyEditsOrTimeout); return withBusySignal || applyEditsOrTimeout; }); } /** * Called when the {TextEditor} saves and sends the 'didSaveTextDocument' notification to the connected language * server. Note: Right now this also sends the `didChangeWatchedFiles` notification as well but that will be sent from * elsewhere soon. */ didSave() { if (!this._isPrimaryAdapter()) { return; } const uri = this.getEditorUri(); const didSaveNotification = { textDocument: { uri, version: this._getVersion(uri) }, }; if (typeof this._documentSync.save === "object" && this._documentSync.save.includeText) { didSaveNotification.text = this._editor.getText(); } this._connection.didSaveTextDocument(didSaveNotification); if (this._fakeDidChangeWatchedFiles) { this._connection.didChangeWatchedFiles({ changes: [{ uri, type: languageclient_1.FileChangeType.Changed }], }); } } didRename() { if (!this._isPrimaryAdapter()) { return; } const oldUri = this._currentUri; this._currentUri = this.getEditorUri(); if (!oldUri) { return; // Didn't previously have a name } if (this._documentSync.openClose !== false) { this._connection.didCloseTextDocument({ textDocument: { uri: oldUri } }); } if (this._fakeDidChangeWatchedFiles) { this._connection.didChangeWatchedFiles({ changes: [ { uri: oldUri, type: languageclient_1.FileChangeType.Deleted }, { uri: this._currentUri, type: languageclient_1.FileChangeType.Created }, ], }); } // Send an equivalent open event for this editor, which will now use the new // file path. if (this._documentSync.openClose !== false) { this.didOpen(); } } /** Public: Obtain the current {TextEditor} path and convert it to a Uri. */ getEditorUri() { return convert_1.default.pathToUri(this._editor.getPath() || ""); } } exports.TextEditorSyncAdapter = TextEditorSyncAdapter; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG9jdW1lbnQtc3luYy1hZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vbGliL2FkYXB0ZXJzL2RvY3VtZW50LXN5bmMtYWRhcHRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQSx3Q0FBZ0M7QUFDaEMsc0RBVTBCO0FBQzFCLDZEQUFtRDtBQUNuRCwrQkFBMEc7QUFDMUcsa0NBQWlDO0FBRWpDOzs7O0dBSUc7QUFDSCxNQUFxQixtQkFBbUI7SUFpQ3RDOzs7Ozs7Ozs7T0FTRztJQUNILFlBQ1UsV0FBcUMsRUFDckMsZUFBZ0QsRUFDeEQsWUFBd0UsRUFDaEUsZ0JBQXVDLEVBQ3ZDLHdCQUF3RDtRQUp4RCxnQkFBVyxHQUFYLFdBQVcsQ0FBMEI7UUFDckMsb0JBQWUsR0FBZixlQUFlLENBQWlDO1FBRWhELHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBdUI7UUFDdkMsNkJBQXdCLEdBQXhCLHdCQUF3QixDQUFnQztRQS9DMUQsZ0JBQVcsR0FBRyxJQUFJLDBCQUFtQixFQUFFLENBQUE7UUFFdkMsYUFBUSxHQUErQyxJQUFJLE9BQU8sRUFBRSxDQUFBO1FBQ3BFLGNBQVMsR0FBd0IsSUFBSSxHQUFHLEVBQUUsQ0FBQTtRQThDaEQsSUFBSSxPQUFPLFlBQVksS0FBSyxRQUFRLEVBQUU7WUFDcEMsSUFBSSxDQUFDLGFBQWEsR0FBRyxZQUFZLENBQUE7U0FDbEM7YUFBTTtZQUNMLElBQUksQ0FBQyxhQUFhLEdBQUc7Z0JBQ25CLE1BQU0sRUFBRSxZQUFZLElBQUkscUNBQW9CLENBQUMsSUFBSTthQUNsRCxDQUFBO1NBQ0Y7UUFDRCxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUNuRixDQUFDO0lBcEREOzs7Ozs7T0FNRztJQUNJLE1B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QUFhLENBQUMsU0FBUyxLQUFLLEtBQUssRUFBRTtZQUMxQyxJQUFJLENBQUMsV0FBVyxDQUFDLG9CQUFvQixDQUFDLEVBQUUsWUFBWSxFQUFFLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQTtTQUN6RTtRQUVELElBQUksSUFBSSxDQUFDLDBCQUEwQixFQUFFO1lBQ25DLElBQUksQ0FBQyxXQUFXLENBQUMscUJBQXFCLENBQUM7Z0JBQ3JDLE9BQU8sRUFBRTtvQkFDUCxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLCtCQUFjLENBQUMsT0FBTyxFQUFFO29CQUM3QyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLElBQUksRUFBRSwrQkFBYyxDQUFDLE9BQU8sRUFBRTtpQkFDeEQ7YUFDRixDQUFDLENBQUE7U0FDSDtRQUVELDRFQUE0RTtRQUM1RSxhQUFhO1FBQ2IsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsS0FBSyxLQUFLLEVBQUU7WUFDMUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFBO1NBQ2Y7SUFDSCxDQUFDO0lBRUQsNEVBQTRFO0lBQ3JFLFlBQVk7UUFDakIsT0FBTyxpQkFBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0lBQ3hELENBQUM7Q0FDRjtBQXpTRCxzREF5U0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgQ29udmVydCBmcm9tIFwiLi4vY29udmVydFwiXG5pbXBvcnQge1xuICBMYW5ndWFnZUNsaWVudENvbm5lY3Rpb24sXG4gIEZpbGVDaGFuZ2VUeXBlLFxuICBUZXh0RG9jdW1lbnRTYXZlUmVhc29uLFxuICBUZXh0RG9jdW1lbnRTeW5jS2luZCxcbiAgVGV4dERvY3VtZW50U3luY09wdGlvbnMsXG4gIFRleHREb2N1bWVudENvbnRlbnRDaGFuZ2VFdmVudCxcbiAgVmVyc2lvbmVkVGV4dERvY3VtZW50SWRlbnRpZmllcixcbiAgU2VydmVyQ2FwYWJpbGl0aWVzLFxuICBEaWRTYXZlVGV4dERvY3VtZW50UGFyYW1zLFxufSBmcm9tIFwiLi4vbGFuZ3VhZ2VjbGllbnRcIlxuaW1wb3J0IEFwcGx5RWRpdEFkYXB0ZXIgZnJvbSBcIi4vYXBwbHktZWRpdC1hZGFwdGVyXCJcbmltcG9ydCB7IENvbXBvc2l0ZURpc3Bvc2FibGUsIERpc3Bvc2FibGUsIFRleHRFZGl0b3IsIEJ1ZmZlclN0b3BwZWRDaGFuZ2luZ0V2ZW50LCBUZXh0Q2hhbmdlIH0gZnJvbSBcImF0b21cIlxuaW1wb3J0ICogYXMgVXRpbHMgZnJvbSBcIi4uL3V0aWxzXCJcblxuLyoqXG4gKiBQdWJsaWM6IFN5bmNocm9uaXplcyB0aGUgZG9jdW1lbnRzIGJldHdlZW4gQXRvbSBhbmQgdGhlIGxhbmd1YWdlIHNlcnZlciBieSBub3RpZnlpbmcgZWFjaCBlbmQgb2YgY2hhbmdlcywgb3BlbmluZyxcbiAqIGNsb3NpbmcgYW5kIG90aGVyIGV2ZW50cyBhcyB3ZWxsIGFzIHNlbmRpbmcgYW5kIGFwcGx5aW5nIGNoYW5nZXMgZWl0aGVyIGluIHdob2xlIG9yIGluIHBhcnQgZGVwZW5kaW5nIG9uIHdoYXQgdGhlXG4gKiBsYW5ndWFnZSBzZXJ2ZXIgc3VwcG9ydHMuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIERvY3VtZW50U3luY0FkYXB0ZXIge1xuICBwcml2YXRlIF9kaXNwb3NhYmxlID0gbmV3IENvbXBvc2l0ZURpc3Bvc2FibGUoKVxuICBwdWJsaWMgX2RvY3VtZW50U3luYzogVGV4dERvY3VtZW50U3luY09wdGlvbnNcbiAgcHJpdmF0ZSBfZWRpdG9yczogV2Vha01hcDxUZXh0RWRpdG9yLCBUZXh0RWRpdG9yU3luY0FkYXB0ZXI+ID0gbmV3IFdlYWtNYXAoKVxuICBwcml2YXRlIF92ZXJzaW9uczogTWFwPHN0cmluZywgbnVtYmVyPiA9IG5ldyBNYXAoKVxuXG4gIC8qKlxuICAgKiBQdWJsaWM6IERldGVybWluZSB3aGV0aGVyIHRoaXMgYWRhcHRlciBjYW4gYmUgdXNlZCB0byBhZGFwdCBhIGxhbmd1YWdlIHNlcnZlciBiYXNlZCBvbiB0aGUgc2VydmVyQ2FwYWJpbGl0aWVzXG4gICAqIG1hdHJpeCB0ZXh0RG9jdW1lbnRTeW5jIGNhcGFiaWxpdHkgZWl0aGVyIGJlaW5nIEZ1bGwgb3IgSW5jcmVtZW50YWwuXG4gICAqXG4gICAqIEBwYXJhbSBzZXJ2ZXJDYXBhYmlsaXRpZXMgVGhlIHtTZXJ2ZXJDYXBhYmlsaXRpZXN9IG9mIHRoZSBsYW5ndWFnZSBzZXJ2ZXIgdG8gY29uc2lkZXIuXG4gICAqIEByZXR1cm5zIEEge0Jvb2xlYW59IGluZGljYXRpbmcgYWRhcHRlciBjYW4gYWRhcHQgdGhlIHNlcnZlciBiYXNlZCBvbiB0aGUgZ2l2ZW4gc2VydmVyQ2FwYWJpbGl0aWVzLlxuICAgKi9cbiAgcHVibGljIHN0YXRpYyBjYW5BZGFwdChzZXJ2ZXJDYXBhYmlsaXRpZXM6IFNlcnZlckNhcGFiaWxpdGllcyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNhbkFkYXB0VjIoc2VydmVyQ2FwYWJpbGl0aWVzKSB8fCB0aGlzLmNhbkFkYXB0VjMoc2VydmVyQ2FwYWJpbGl0aWVzKVxuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY2FuQWRhcHRWMihzZXJ2ZXJDYXBhYmlsaXRpZXM6IFNlcnZlckNhcGFiaWxpdGllcyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICBzZXJ2ZXJDYXBhYmlsaXRpZXMudGV4dERvY3VtZW50U3luYyA9PT0gVGV4dERvY3VtZW50U3luY0tpbmQuSW5jcmVtZW50YWwgfHxcbiAgICAgIHNlcnZlckNhcGFiaWxpdGllcy50ZXh0RG9jdW1lbnRTeW5jID09PSBUZXh0RG9jdW1lbnRTeW5jS2luZC5GdWxsXG4gICAgKVxuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY2FuQWRhcHRWMyhzZXJ2ZXJDYXBhYmlsaXRpZXM6IFNlcnZlckNhcGFiaWxpdGllcyk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IG9wdGlvbnMgPSBzZXJ2ZXJDYXBhYmlsaXRpZXMudGV4dERvY3VtZW50U3luY1xuICAgIHJldHVybiAoXG4gICAgICBvcHRpb25zICE9PSBudWxsICYmXG4gICAgICB0eXBlb2Ygb3B0aW9ucyA9PT0gXCJvYmplY3RcIiAmJlxuICAgICAgKG9wdGlvbnMuY2hhbmdlID09PSBUZXh0RG9jdW1lbnRTeW5jS2luZC5JbmNyZW1lbnRhbCB8fCBvcHRpb25zLmNoYW5nZSA9PT0gVGV4dERvY3VtZW50U3luY0tpbmQuRnVsbClcbiAgICApXG4gIH1cblxuICAvKipcbiAgICogUHVibGljOiBDcmVhdGUgYSBuZXcge0RvY3VtZW50U3luY0FkYXB0ZXJ9IGZvciB0aGUgZ2l2ZW4gbGFuZ3VhZ2Ugc2VydmVyLlxuICAgKlxuICAgKiBAcGFyYW0gX2Nvbm5lY3Rpb24gQSB7TGFuZ3VhZ2VDbGllbnRDb25uZWN0aW9ufSB0byB0aGUgbGFuZ3VhZ2Ugc2VydmVyIHRvIGJlIGtlcHQgaW4gc3luYy5cbiAgICogQHBhcmFtIGRvY3VtZW50U3luYyBUaGUgZG9jdW1lbnQgc3luY2luZyBvcHRpb25zLlxuICAgKiBAcGFyYW0gX2VkaXRvclNlbGVjdG9yIEEgcHJlZGljYXRlIGZ1bmN0aW9uIHRoYXQgdGFrZXMgYSB7VGV4dEVkaXRvcn0gYW5kIHJldHVybnMgYSB7Ym9vbGVhbn0gaW5kaWNhdGluZyB3aGV0aGVyXG4gICAqICAgdGhpcyBhZGFwdGVyIHNob3VsZCBjYXJlIGFib3V0IHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLlxuICAgKiBAcGFyYW0gX2dldExhbmd1YWdlSWRGcm9tRWRpdG9yIEEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEge3N0cmluZ30gb2YgYGxhbmd1YWdlSWRgIHVzZWQgZm9yIGB0ZXh0RG9jdW1lbnQvZGlkT3BlbmBcbiAgICogICBub3RpZmljYXRpb24uXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIF9jb25uZWN0aW9uOiBMYW5ndWFnZUNsaWVudENvbm5lY3Rpb24sXG4gICAgcHJpdmF0ZSBfZWRpdG9yU2VsZWN0b3I6IChlZGl0b3I6IFRleHRFZGl0b3IpID0+IGJvb2xlYW4sXG4gICAgZG9jdW1lbnRTeW5jOiBUZXh0RG9jdW1lbnRTeW5jT3B0aW9ucyB8IFRleHREb2N1bWVudFN5bmNLaW5kIHwgdW5kZWZpbmVkLFxuICAgIHByaXZhdGUgX3JlcG9ydEJ1c3lXaGlsZTogVXRpbHMuUmVwb3J0QnVzeVdoaWxlLFxuICAgIHByaXZhdGUgX2dldExhbmd1YWdlSWRGcm9tRWRpdG9yOiAoZWRpdG9yOiBUZXh0RWRpdG9yKSA9PiBzdHJpbmdcbiAgKSB7XG4gICAgaWYgKHR5cGVvZiBkb2N1bWVudFN5bmMgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgIHRoaXMuX2RvY3VtZW50U3luYyA9IGRvY3VtZW50U3luY1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl9kb2N1bWVudFN5bmMgPSB7XG4gICAgICAgIGNoYW5nZTogZG9jdW1lbnRTeW5jIHx8IFRleHREb2N1bWVudFN5bmNLaW5kLkZ1bGwsXG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuX2Rpc3Bvc2FibGUuYWRkKGF0b20udGV4dEVkaXRvcnMub2JzZXJ2ZSh0aGlzLm9ic2VydmVUZXh0RWRpdG9yLmJpbmQodGhpcykpKVxuICB9XG5cbiAgLyoqIERpc3Bvc2UgdGhpcyBhZGFwdGVyIGVuc3VyaW5nIGFueSByZXNvdXJjZXMgYXJlIGZyZWVkIGFuZCBldmVudHMgdW5ob29rZWQuICovXG4gIHB1YmxpYyBkaXNwb3NlKCk6IHZvaWQge1xuICAgIHRoaXMuX2Rpc3Bvc2FibGUuZGlzcG9zZSgpXG4gIH1cblxuICAvKipcbiAgICogRXhhbWluZSBhIHtUZXh0RWRpdG9yfSBhbmQgZGVjaWRlIGlmIHdlIHdpc2ggdG8gb2JzZXJ2ZSBpdC4gSWYgc28gZW5zdXJlIHRoYXQgd2Ugc3RvcCBvYnNlcnZpbmcgaXQgd2hlbiBpdCBpc1xuICAgKiBjbG9zZWQgb3Igb3RoZXJ3aXNlIGRlc3Ryb3llZC5cbiAgICpcbiAgICogQHBhcmFtIGVkaXRvciBBIHtUZXh0RWRpdG9yfSB0byBjb25zaWRlciBmb3Igb2JzZXJ2YXRpb24uXG4gICAqL1xuICBwdWJsaWMgb2JzZXJ2ZVRleHRFZGl0b3IoZWRpdG9yOiBUZXh0RWRpdG9yKTogdm9pZCB7XG4gICAgY29uc3QgbGlzdGVuZXIgPSBlZGl0b3Iub2JzZXJ2ZUdyYW1tYXIoKF9ncmFtbWFyKSA9PiB0aGlzLl9oYW5kbGVHcmFtbWFyQ2hhbmdlKGVkaXRvcikpXG4gICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoXG4gICAgICBlZGl0b3Iub25EaWREZXN0cm95KCgpID0+IHtcbiAgICAgICAgdGhpcy5fZGlzcG9zYWJsZS5yZW1vdmUobGlzdGVuZXIpXG4gICAgICAgIGxpc3RlbmVyLmRpc3Bvc2UoKVxuICAgICAgfSlcbiAgICApXG4gICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQobGlzdGVuZXIpXG4gICAgaWYgKCF0aGlzLl9lZGl0b3JzLmhhcyhlZGl0b3IpICYmIHRoaXMuX2VkaXRvclNlbGVjdG9yKGVkaXRvcikpIHtcbiAgICAgIHRoaXMuX2hhbmRsZU5ld0VkaXRvcihlZGl0b3IpXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfaGFuZGxlR3JhbW1hckNoYW5nZShlZGl0b3I6IFRleHRFZGl0b3IpOiB2b2lkIHtcbiAgICBjb25zdCBzeW5jID0gdGhpcy5fZWRpdG9ycy5nZXQoZWRpdG9yKVxuICAgIGlmIChzeW5jICE9IG51bGwgJiYgIXRoaXMuX2VkaXRvclNlbGVjdG9yKGVkaXRvcikpIHtcbiAgICAgIHRoaXMuX2VkaXRvcnMuZGVsZXRlKGVkaXRvcilcbiAgICAgIHRoaXMuX2Rpc3Bvc2FibGUucmVtb3ZlKHN5bmMpXG4gICAgICBzeW5jLmRpZENsb3NlKClcbiAgICAgIHN5bmMuZGlzcG9zZSgpXG4gICAgfSBlbHNlIGlmIChzeW5jID09IG51bGwgJiYgdGhpcy5fZWRpdG9yU2VsZWN0b3IoZWRpdG9yKSkge1xuICAgICAgdGhpcy5faGFuZGxlTmV3RWRpdG9yKGVkaXRvcilcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIF9oYW5kbGVOZXdFZGl0b3IoZWRpdG9yOiBUZXh0RWRpdG9yKTogdm9pZCB7XG4gICAgY29uc3Qgc3luYyA9IG5ldyBUZXh0RWRpdG9yU3luY0FkYXB0ZXIoXG4gICAgICBlZGl0b3IsXG4gICAgICB0aGlzLl9jb25uZWN0aW9uLFxuICAgICAgdGhpcy5fZG9jdW1lbnRTeW5jLFxuICAgICAgdGhpcy5fdmVyc2lvbnMsXG4gICAgICB0aGlzLl9yZXBvcnRCdXN5V2hpbGUsXG4gICAgICB0aGlzLl9nZXRMYW5ndWFnZUlkRnJvbUVkaXRvclxuICAgIClcbiAgICB0aGlzLl9lZGl0b3JzLnNldChlZGl0b3IsIHN5bmMpXG4gICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoc3luYylcbiAgICB0aGlzLl9kaXNwb3NhYmxlLmFkZChcbiAgICAgIGVkaXRvci5vbkRpZERlc3Ryb3koKCkgPT4ge1xuICAgICAgICBjb25zdCBkZXN0cm95ZWRTeW5jID0gdGhpcy5fZWRpdG9ycy5nZXQoZWRpdG9yKVxuICAgICAgICBpZiAoZGVzdHJveWVkU3luYykge1xuICAgICAgICAgIHRoaXMuX2VkaXRvcnMuZGVsZXRlKGVkaXRvcilcbiAgICAgICAgICB0aGlzLl9kaXNwb3NhYmxlLnJlbW92ZShkZXN0cm95ZWRTeW5jKVxuICAgICAgICAgIGRlc3Ryb3llZFN5bmMuZGlzcG9zZSgpXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgKVxuICB9XG5cbiAgcHVibGljIGdldEVkaXRvclN5bmNBZGFwdGVyKGVkaXRvcjogVGV4dEVkaXRvcik6IFRleHRFZGl0b3JTeW5jQWRhcHRlciB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuX2VkaXRvcnMuZ2V0KGVkaXRvcilcbiAgfVxufVxuXG4vKiogUHVibGljOiBLZWVwIGEgc2luZ2xlIHtUZXh0RWRpdG9yfSBpbiBzeW5jIHdpdGggYSBnaXZlbiBsYW5ndWFnZSBzZXJ2ZXIuICovXG5leHBvcnQgY2xhc3MgVGV4dEVkaXRvclN5bmNBZGFwdGVyIHtcbiAgcHJpdmF0ZSBfZGlzcG9zYWJsZSA9IG5ldyBDb21wb3NpdGVEaXNwb3NhYmxlKClcbiAgcHJpdmF0ZSBfY3VycmVudFVyaTogc3RyaW5nXG4gIHByaXZhdGUgX2Zha2VEaWRDaGFuZ2VXYXRjaGVkRmlsZXM6IGJvb2xlYW5cblxuICAvKipcbiAgICogUHVibGljOiBDcmVhdGUgYSB7VGV4dEVkaXRvclN5bmNBZGFwdGVyfSBpbiBzeW5jIHdpdGggYSBnaXZlbiBsYW5ndWFnZSBzZXJ2ZXIuXG4gICAqXG4gICAqIEBwYXJhbSBfZWRpdG9yIEEge1RleHRFZGl0b3J9IHRvIGtlZXAgaW4gc3luYy5cbiAgICogQHBhcmFtIF9jb25uZWN0aW9uIEEge0xhbmd1YWdlQ2xpZW50Q29ubmVjdGlvbn0gdG8gYSBsYW5ndWFnZSBzZXJ2ZXIgdG8ga2VlcCBpbiBzeW5jLlxuICAgKiBAcGFyYW0gX2RvY3VtZW50U3luYyBUaGUgZG9jdW1lbnQgc3luY2luZyBvcHRpb25zLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBfZWRpdG9yOiBUZXh0RWRpdG9yLFxuICAgIHByaXZhdGUgX2Nvbm5lY3Rpb246IExhbmd1YWdlQ2xpZW50Q29ubmVjdGlvbixcbiAgICBwcml2YXRlIF9kb2N1bWVudFN5bmM6IFRleHREb2N1bWVudFN5bmNPcHRpb25zLFxuICAgIHByaXZhdGUgX3ZlcnNpb25zOiBNYXA8c3RyaW5nLCBudW1iZXI+LFxuICAgIHByaXZhdGUgX3JlcG9ydEJ1c3lXaGlsZTogVXRpbHMuUmVwb3J0QnVzeVdoaWxlLFxuICAgIHByaXZhdGUgX2dldExhbmd1YWdlSWRGcm9tRWRpdG9yOiAoZWRpdG9yOiBUZXh0RWRpdG9yKSA9PiBzdHJpbmdcbiAgKSB7XG4gICAgdGhpcy5fZmFrZURpZENoYW5nZVdhdGNoZWRGaWxlcyA9IGF0b20ucHJvamVjdC5vbkRpZENoYW5nZUZpbGVzID09IG51bGxcblxuICAgIGNvbnN0IGNoYW5nZVRyYWNraW5nID0gdGhpcy5zZXR1cENoYW5nZVRyYWNraW5nKF9kb2N1bWVudFN5bmMpXG4gICAgaWYgKGNoYW5nZVRyYWNraW5nICE9IG51bGwpIHtcbiAgICAgIHRoaXMuX2Rpc3Bvc2FibGUuYWRkKGNoYW5nZVRyYWNraW5nKVxuICAgIH1cblxuICAgIC8vIFRoZXNlIGhhbmRsZXJzIGFyZSBhdHRhY2hlZCBvbmx5IGlmIHNlcnZlciBzdXBwb3J0cyB0aGVtXG4gICAgaWYgKF9kb2N1bWVudFN5bmMud2lsbFNhdmUpIHtcbiAgICAgIHRoaXMuX2Rpc3Bvc2FibGUuYWRkKF9lZGl0b3IuZ2V0QnVmZmVyKCkub25XaWxsU2F2ZSh0aGlzLndpbGxTYXZlLmJpbmQodGhpcykpKVxuICAgIH1cbiAgICBpZiAoX2RvY3VtZW50U3luYy53aWxsU2F2ZVdhaXRVbnRpbCkge1xuICAgICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoX2VkaXRvci5nZXRCdWZmZXIoKS5vbldpbGxTYXZlKHRoaXMud2lsbFNhdmVXYWl0VW50aWwuYmluZCh0aGlzKSkpXG4gICAgfVxuICAgIC8vIFNlbmQgY2xvc2Ugbm90aWZpY2F0aW9ucyB1bmxlc3MgaXQncyBleHBsaWNpdGx5IGRpc2FibGVkXG4gICAgaWYgKF9kb2N1bWVudFN5bmMub3BlbkNsb3NlICE9PSBmYWxzZSkge1xuICAgICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoX2VkaXRvci5vbkRpZERlc3Ryb3kodGhpcy5kaWRDbG9zZS5iaW5kKHRoaXMpKSlcbiAgICB9XG4gICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoX2VkaXRvci5vbkRpZFNhdmUodGhpcy5kaWRTYXZlLmJpbmQodGhpcykpLCBfZWRpdG9yLm9uRGlkQ2hhbmdlUGF0aCh0aGlzLmRpZFJlbmFtZS5iaW5kKHRoaXMpKSlcblxuICAgIHRoaXMuX2N1cnJlbnRVcmkgPSB0aGlzLmdldEVkaXRvclVyaSgpXG5cbiAgICBpZiAoX2RvY3VtZW50U3luYy5vcGVuQ2xvc2UgIT09IGZhbHNlKSB7XG4gICAgICB0aGlzLmRpZE9wZW4oKVxuICAgIH1cbiAgfVxuXG4gIC8qKiBUaGUgY2hhbmdlIHRyYWNraW5nIGRpc3Bvc2FibGUgbGlzdGVuZXIgdGhhdCB3aWxsIGVuc3VyZSB0aGF0IGNoYW5nZXMgYXJlIHNlbnQgdG8gdGhlIGxhbmd1YWdlIHNlcnZlciBhcyBhcHByb3ByaWF0ZS4gKi9cbiAgcHVibGljIHNldHVwQ2hhbmdlVHJhY2tpbmcoZG9jdW1lbnRTeW5jOiBUZXh0RG9jdW1lbnRTeW5jT3B0aW9ucyk6IERpc3Bvc2FibGUgfCBudWxsIHtcbiAgICBzd2l0Y2ggKGRvY3VtZW50U3luYy5jaGFuZ2UpIHtcbiAgICAgIGNhc2UgVGV4dERvY3VtZW50U3luY0tpbmQuRnVsbDpcbiAgICAgICAgcmV0dXJuIHRoaXMuX2VkaXRvci5vbkRpZENoYW5nZSh0aGlzLnNlbmRGdWxsQ2hhbmdlcy5iaW5kKHRoaXMpKVxuICAgICAgY2FzZSBUZXh0RG9jdW1lbnRTeW5jS2luZC5JbmNyZW1lbnRhbDpcbiAgICAgICAgcmV0dXJuIHRoaXMuX2VkaXRvci5nZXRCdWZmZXIoKS5vbkRpZENoYW5nZVRleHQodGhpcy5zZW5kSW5jcmVtZW50YWxDaGFuZ2VzLmJpbmQodGhpcykpXG4gICAgfVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvKiogRGlzcG9zZSB0aGlzIGFkYXB0ZXIgZW5zdXJpbmcgYW55IHJlc291cmNlcyBhcmUgZnJlZWQgYW5kIGV2ZW50cyB1bmhvb2tlZC4gKi9cbiAgcHVibGljIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5fZGlzcG9zYWJsZS5kaXNwb3NlKClcbiAgfVxuXG4gIC8qKiBHZXQgdGhlIGxhbmd1YWdlSWQgZmllbGQgdGhhdCB3aWxsIGJlIHNlbnQgdG8gdGhlIGxhbmd1YWdlIHNlcnZlciBieSBzaW1wbHkgdXNpbmcgdGhlIGBfZ2V0TGFuZ3VhZ2VJZEZyb21FZGl0b3JgLiAqL1xuICBwdWJsaWMgZ2V0TGFuZ3VhZ2VJZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9nZXRMYW5ndWFnZUlkRnJvbUVkaXRvcih0aGlzLl9lZGl0b3IpXG4gIH1cblxuICAvKipcbiAgICogUHVibGljOiBDcmVhdGUgYSB7VmVyc2lvbmVkVGV4dERvY3VtZW50SWRlbnRpZmllcn0gZm9yIHRoZSBkb2N1bWVudCBvYnNlcnZlZCBieSB0aGlzIGFkYXB0ZXIgaW5jbHVkaW5nIGJvdGggdGhlIFVyaVxuICAgKiBhbmQgdGhlIGN1cnJlbnQgVmVyc2lvbi5cbiAgICovXG4gIHB1YmxpYyBnZXRWZXJzaW9uZWRUZXh0RG9jdW1lbnRJZGVudGlmaWVyKCk6IFZlcnNpb25lZFRleHREb2N1bWVudElkZW50aWZpZXIge1xuICAgIHJldHVybiB7XG4gICAgICB1cmk6IHRoaXMuZ2V0RWRpdG9yVXJpKCksXG4gICAgICB2ZXJzaW9uOiB0aGlzLl9nZXRWZXJzaW9uKHRoaXMuX2VkaXRvci5nZXRQYXRoKCkgfHwgXCJcIiksXG4gICAgfVxuICB9XG5cbiAgLyoqIFB1YmxpYzogU2VuZCB0aGUgZW50aXJlIGRvY3VtZW50IHRvIHRoZSBsYW5ndWFnZSBzZXJ2ZXIuIFRoaXMgaXMgdXNlZCB3aGVuIG9wZXJhdGluZyBpbiBGdWxsICgxKSBzeW5jIG1vZGUuICovXG4gIHB1YmxpYyBzZW5kRnVsbENoYW5nZXMoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLl9pc1ByaW1hcnlBZGFwdGVyKCkpIHtcbiAgICAgIHJldHVyblxuICAgIH0gLy8gTXVsdGlwbGUgZWRpdG9ycywgd2UgYXJlIG5vdCBmaXJzdFxuXG4gICAgdGhpcy5fYnVtcFZlcnNpb24oKVxuICAgIHRoaXMuX2Nvbm5lY3Rpb24uZGlkQ2hhbmdlVGV4dERvY3VtZW50KHtcbiAgICAgIHRleHREb2N1bWVudDogdGhpcy5nZXRWZXJzaW9uZWRUZXh0RG9jdW1lbnRJZGVudGlmaWVyKCksXG4gICAgICBjb250ZW50Q2hhbmdlczogW3sgdGV4dDogdGhpcy5fZWRpdG9yLmdldFRleHQoKSB9XSxcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFB1YmxpYzogU2VuZCB0aGUgaW5jcmVtZW50YWwgdGV4dCBjaGFuZ2VzIHRvIHRoZSBsYW5ndWFnZSBzZXJ2ZXIuIFRoaXMgaXMgdXNlZCB3aGVuIG9wZXJhdGluZyBpbiBJbmNyZW1lbnRhbCAoMikgc3luYyBtb2RlLlxuICAgKlxuICAgKiBAcGFyYW0gZXZlbnQgVGhlIGV2ZW50IGZpcmVkIGJ5IEF0b20gdG8gaW5kaWNhdGUgdGhlIGRvY3VtZW50IGhhcyBzdG9wcGVkIGNoYW5naW5nIGluY2x1ZGluZyBhIGxpc3Qgb2YgY2hhbmdlc1xuICAgKiAgIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhpcyBldmVudCBmaXJlZCBmb3IgdGhpcyB0ZXh0IGVkaXRvci4gTk9URTogVGhlIG9yZGVyIG9mIGNoYW5nZXMgaW4gdGhlIGV2ZW50IGlzIGd1YXJhbnRlZWRcbiAgICogICB0b3AgdG8gYm90dG9tLiBMYW5ndWFnZSBzZXJ2ZXIgZXhwZWN0cyB0aGlzIGluIHJldmVyc2UuXG4gICAqL1xuICBwdWJsaWMgc2VuZEluY3JlbWVudGFsQ2hhbmdlcyhldmVudDogQnVmZmVyU3RvcHBlZENoYW5naW5nRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoZXZlbnQuY2hhbmdlcy5sZW5ndGggPiAwKSB7XG4gICAgICBpZiAoIXRoaXMuX2lzUHJpbWFyeUFkYXB0ZXIoKSkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH0gLy8gTXVsdGlwbGUgZWRpdG9ycywgd2UgYXJlIG5vdCBmaXJzdFxuXG4gICAgICB0aGlzLl9idW1wVmVyc2lvbigpXG4gICAgICB0aGlzLl9jb25uZWN0aW9uLmRpZENoYW5nZVRleHREb2N1bWVudCh7XG4gICAgICAgIHRleHREb2N1bWVudDogdGhpcy5nZXRWZXJzaW9uZWRUZXh0RG9jdW1lbnRJZGVudGlmaWVyKCksXG4gICAgICAgIGNvbnRlbnRDaGFuZ2VzOiBldmVudC5jaGFuZ2VzLm1hcChUZXh0RWRpdG9yU3luY0FkYXB0ZXIudGV4dEVkaXRUb0NvbnRlbnRDaGFuZ2UpLnJldmVyc2UoKSxcbiAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFB1YmxpYzogQ29udmVydCBhbiBBdG9tIHtUZXh0RWRpdEV2ZW50fSB0byBhIGxhbmd1YWdlIHNlcnZlciB7VGV4dERvY3VtZW50Q29udGVudENoYW5nZUV2ZW50fSBvYmplY3QuXG4gICAqXG4gICAqIEBwYXJhbSBjaGFuZ2UgVGhlIEF0b20ge1RleHRFZGl0RXZlbnR9IHRvIGNvbnZlcnQuXG4gICAqIEByZXR1cm5zIEEge1RleHREb2N1bWVudENvbnRlbnRDaGFuZ2VFdmVudH0gdGhhdCByZXByZXNlbnRzIHRoZSBjb252ZXJ0ZWQge1RleHRFZGl0RXZlbnR9LlxuICAgKi9cbiAgcHVibGljIHN0YXRpYyB0ZXh0RWRpdFRvQ29udGVudENoYW5nZShjaGFuZ2U6IFRleHRDaGFuZ2UpOiBUZXh0RG9jdW1lbnRDb250ZW50Q2hhbmdlRXZlbnQge1xuICAgIHJldHVybiB7XG4gICAgICByYW5nZTogQ29udmVydC5hdG9tUmFuZ2VUb0xTUmFuZ2UoY2hhbmdlLm9sZFJhbmdlKSxcbiAgICAgIHJhbmdlTGVuZ3RoOiBjaGFuZ2Uub2xkVGV4dC5sZW5ndGgsXG4gICAgICB0ZXh0OiBjaGFuZ2UubmV3VGV4dCxcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIF9pc1ByaW1hcnlBZGFwdGVyKCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGxvd2VzdElkRm9yQnVmZmVyID0gTWF0aC5taW4oXG4gICAgICAuLi5hdG9tLndvcmtzcGFjZVxuICAgICAgICAuZ2V0VGV4dEVkaXRvcnMoKVxuICAgICAgICAuZmlsdGVyKCh0KSA9PiB0LmdldEJ1ZmZlcigpID09PSB0aGlzLl9lZGl0b3IuZ2V0QnVmZmVyKCkpXG4gICAgICAgIC5tYXAoKHQpID0+IHQuaWQpXG4gICAgKVxuICAgIHJldHVybiBsb3dlc3RJZEZvckJ1ZmZlciA9PT0gdGhpcy5fZWRpdG9yLmlkXG4gIH1cblxuICBwcml2YXRlIF9idW1wVmVyc2lvbigpOiB2b2lkIHtcbiAgICBjb25zdCBmaWxlUGF0aCA9IHRoaXMuX2VkaXRvci5nZXRQYXRoKClcbiAgICBpZiAoZmlsZVBhdGggPT0gbnVsbCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIHRoaXMuX3ZlcnNpb25zLnNldChmaWxlUGF0aCwgdGhpcy5fZ2V0VmVyc2lvbihmaWxlUGF0aCkgKyAxKVxuICB9XG5cbiAgLyoqXG4gICAqIEVuc3VyZSB3aGVuIHRoZSBkb2N1bWVudCBpcyBvcGVuZWQgd2Ugc2VuZCBub3RpZmljYXRpb24gdG8gdGhlIGxhbmd1YWdlIHNlcnZlciBzbyBpdCBjYW4gbG9hZCBpdCBpbiBhbmQga2VlcCB0cmFja1xuICAgKiBvZiBkaWFnbm9zdGljcyBldGMuXG4gICAqL1xuICBwcml2YXRlIGRpZE9wZW4oKTogdm9pZCB7XG4gICAgY29uc3QgZmlsZVBhdGggPSB0aGlzLl9lZGl0b3IuZ2V0UGF0aCgpXG4gICAgaWYgKGZpbGVQYXRoID09IG51bGwpIHtcbiAgICAgIHJldHVyblxuICAgIH0gLy8gTm90IHlldCBzYXZlZFxuXG4gICAgaWYgKCF0aGlzLl9pc1ByaW1hcnlBZGFwdGVyKCkpIHtcbiAgICAgIHJldHVyblxuICAgIH0gLy8gTXVsdGlwbGUgZWRpdG9ycywgd2UgYXJlIG5vdCBmaXJzdFxuXG4gICAgdGhpcy5fY29ubmVjdGlvbi5kaWRPcGVuVGV4dERvY3VtZW50KHtcbiAgICAgIHRleHREb2N1bWVudDoge1xuICAgICAgICB1cmk6IHRoaXMuZ2V0RWRpdG9yVXJpKCksXG4gICAgICAgIGxhbmd1YWdlSWQ6IHRoaXMuZ2V0TGFuZ3VhZ2VJZCgpLnRvTG93ZXJDYXNlKCksXG4gICAgICAgIHZlcnNpb246IHRoaXMuX2dldFZlcnNpb24oZmlsZVBhdGgpLFxuICAgICAgICB0ZXh0OiB0aGlzLl9lZGl0b3IuZ2V0VGV4dCgpLFxuICAgICAgfSxcbiAgICB9KVxuICB9XG5cbiAgcHJpdmF0ZSBfZ2V0VmVyc2lvbihmaWxlUGF0aDogc3RyaW5nKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fdmVyc2lvbnMuZ2V0KGZpbGVQYXRoKSB8fCAxXG4gIH1cblxuICAvKiogQ2FsbGVkIHdoZW4gdGhlIHtUZXh0RWRpdG9yfSBpcyBjbG9zZWQgYW5kIHNlbmRzIHRoZSAnZGlkQ2xvc2VUZXh0RG9jdW1lbnQnIG5vdGlmaWNhdGlvbiB0byB0aGUgY29ubmVjdGVkIGxhbmd1YWdlIHNlcnZlci4gKi9cbiAgcHVibGljIGRpZENsb3NlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLl9lZGl0b3IuZ2V0UGF0aCgpID09IG51bGwpIHtcbiAgICAgIHJldHVyblxuICAgIH0gLy8gTm90IHlldCBzYXZlZFxuXG4gICAgY29uc3QgZmlsZVN0aWxsT3BlbiA9IGF0b20ud29ya3NwYWNlLmdldFRleHRFZGl0b3JzKCkuZmluZCgodCkgPT4gdC5nZXRCdWZmZXIoKSA9PT0gdGhpcy5fZWRpdG9yLmdldEJ1ZmZlcigpKVxuICAgIGlmIChmaWxlU3RpbGxPcGVuKSB7XG4gICAgICByZXR1cm4gLy8gT3RoZXIgd2luZG93cyBvciBlZGl0b3JzIHN0aWxsIGhhdmUgdGhpcyBmaWxlIG9wZW5cbiAgICB9XG5cbiAgICB0aGlzLl9jb25uZWN0aW9uLmRpZENsb3NlVGV4dERvY3VtZW50KHsgdGV4dERvY3VtZW50OiB7IHVyaTogdGhpcy5nZXRFZGl0b3JVcmkoKSB9IH0pXG4gIH1cblxuICAvKiogQ2FsbGVkIGp1c3QgYmVmb3JlIHRoZSB7VGV4dEVkaXRvcn0gc2F2ZXMgYW5kIHNlbmRzIHRoZSAnd2lsbFNhdmVUZXh0RG9jdW1lbnQnIG5vdGlmaWNhdGlvbiB0byB0aGUgY29ubmVjdGVkIGxhbmd1YWdlIHNlcnZlci4gKi9cbiAgcHVibGljIHdpbGxTYXZlKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5faXNQcmltYXJ5QWRhcHRlcigpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjb25zdCB1cmkgPSB0aGlzLmdldEVkaXRvclVyaSgpXG4gICAgdGhpcy5fY29ubmVjdGlvbi53aWxsU2F2ZVRleHREb2N1bWVudCh7XG4gICAgICB0ZXh0RG9jdW1lbnQ6IHsgdXJpIH0sXG4gICAgICByZWFzb246IFRleHREb2N1bWVudFNhdmVSZWFzb24uTWFudWFsLFxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQ2FsbGVkIGp1c3QgYmVmb3JlIHRoZSB7VGV4dEVkaXRvcn0gc2F2ZXMsIHNlbmRzIHRoZSAnd2lsbFNhdmVXYWl0VW50aWxUZXh0RG9jdW1lbnQnIHJlcXVlc3QgdG8gdGhlIGNvbm5lY3RlZFxuICAgKiBsYW5ndWFnZSBzZXJ2ZXIgYW5kIHdhaXRzIGZvciB0aGUgcmVzcG9uc2UgYmVmb3JlIHNhdmluZyB0aGUgYnVmZmVyLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHdpbGxTYXZlV2FpdFVudGlsKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghdGhpcy5faXNQcmltYXJ5QWRhcHRlcigpKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKClcbiAgICB9XG5cbiAgICBjb25zdCBidWZmZXIgPSB0aGlzLl9lZGl0b3IuZ2V0QnVmZmVyKClcbiAgICBjb25zdCB1cmkgPSB0aGlzLmdldEVkaXRvclVyaSgpXG4gICAgY29uc3QgdGl0bGUgPSB0aGlzLl9lZGl0b3IuZ2V0TG9uZ1RpdGxlKClcblxuICAgIGNvbnN0IGFwcGx5RWRpdHNPclRpbWVvdXQgPSBVdGlscy5wcm9taXNlV2l0aFRpbWVvdXQoXG4gICAgICAyNTAwLCAvLyAyLjUgc2Vjb25kcyB0aW1lb3V0XG4gICAgICB0aGlzLl9jb25uZWN0aW9uLndpbGxTYXZlV2FpdFVudGlsVGV4dERvY3VtZW50KHtcbiAgICAgICAgdGV4dERvY3VtZW50OiB7IHVyaSB9LFxuICAgICAgICByZWFzb246IFRleHREb2N1bWVudFNhdmVSZWFzb24uTWFudWFsLFxuICAgICAgfSlcbiAgICApXG4gICAgICAudGhlbigoZWRpdHMpID0+IHtcbiAgICAgICAgY29uc3QgY3Vyc29yID0gdGhpcy5fZWRpdG9yLmdldEN1cnNvckJ1ZmZlclBvc2l0aW9uKClcbiAgICAgICAgQXBwbHlFZGl0QWRhcHRlci5hcHBseUVkaXRzKGJ1ZmZlciwgQ29udmVydC5jb252ZXJ0THNUZXh0RWRpdHMoZWRpdHMpKVxuICAgICAgICB0aGlzLl9lZGl0b3Iuc2V0Q3Vyc29yQnVmZmVyUG9zaXRpb24oY3Vyc29yKVxuICAgICAgfSlcbiAgICAgIC5jYXRjaCgoZXJyKSA9PiB7XG4gICAgICAgIGF0b20ubm90aWZpY2F0aW9ucy5hZGRFcnJvcihcIk9uLXNhdmUgYWN0aW9uIGZhaWxlZFwiLCB7XG4gICAgICAgICAgZGVzY3JpcHRpb246IGBGYWlsZWQgdG8gYXBwbHkgZWRpdHMgdG8gJHt0aXRsZX1gLFxuICAgICAgICAgIGRldGFpbDogZXJyLm1lc3NhZ2UsXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVyblxuICAgICAgfS