atom-languageclient
Version:
Integrate Language Servers with Atom
381 lines • 52.9 kB
JavaScript
"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