@magic-xpa/utils
Version:
magic utils package
734 lines • 65.5 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
import { NString, List, ApplicationException, StringBuilder, NChar, NNumber } from "@magic-xpa/mscorelib";
import { XMLConstants } from "../XMLConstants";
/// <summary> a helper class for the parsing of the XML</summary>
var XmlParser = /** @class */ (function () {
/// <summary>
///
/// </summary>
/// <param name="data"></param>
function XmlParser(data) {
if (data === void 0) { data = NString.Empty; }
this._currIndex = 0;
this._xmLdata = "";
this._history = new List(); // In order to allow recursive parsing we save prev data
this.setXMLdata(data);
this.setCurrIndex(0);
}
/// <summary> parse a string according to a set of delimiters and return the result in a vector</summary>
/// <param name="str">the String which need be parted </param>
/// <param name="delimiter">the delimiter which part different parts of str </param>
/// <param name="isMagicXML">is needed tokenizer working on Magic XML, so the "=" sign will be delited in the end of every first token </param>
/// <returns> tmpVector dynamically array, which consist tokens in every element, every token is String </returns>
/// <summary> parse a string according to a set of delimiters and return the result in a vector</summary>
/// <param name="str">the String which need be parted </param>
/// <param name="delimiter">the delimiter which part different parts of str </param>
/// <param name="isMagicXML">is needed tokenizer working on Magic XML, so the "=" sign will be delited in the end of every first token </param>
/// <returns> tmpVector dynamically array, which consist tokens in every element, every token is String </returns>
/**
* @param {?} str
* @param {?} delimiter
* @param {?=} isMagicXML
* @return {?}
*/
XmlParser.getTokens =
/// <summary> parse a string according to a set of delimiters and return the result in a vector</summary>
/// <param name="str">the String which need be parted </param>
/// <param name="delimiter">the delimiter which part different parts of str </param>
/// <param name="isMagicXML">is needed tokenizer working on Magic XML, so the "=" sign will be delited in the end of every first token </param>
/// <returns> tmpVector dynamically array, which consist tokens in every element, every token is String </returns>
/**
* @param {?} str
* @param {?} delimiter
* @param {?=} isMagicXML
* @return {?}
*/
function (str, delimiter, isMagicXML) {
if (isMagicXML === void 0) { isMagicXML = true; }
/** @type {?} */
var tokensVec = new List();
/** @type {?} */
var token = null;
if (isMagicXML) {
str = str.trim();
}
/** @type {?} */
var strTok = str.split(delimiter.charAt(0));
for (var i = 0; i < strTok.length; i = i + 1) {
// Split in C# creates a last empty string token if the source string ends with
// the delimiter or if the string is empty (as opposed to Java that will ignore it)
// therefore we have to break this loop if such case occurs.
if (isMagicXML && i === strTok.length - 1 && strTok.length % 2 === 1) {
break;
}
token = strTok[i];
if (isMagicXML) {
// the 1st token in the pair comes with "=", remove it.
if (i % 2 === 0) {
token = token.trim();
if (token.endsWith("=")) {
token = token.substr(0, token.length - 1);
}
}
else if (token === "")
token = " ";
}
if (token === null)
throw new ApplicationException("in ClientManager.Instance.XMLParser.getTokens() null token value");
tokensVec.push(token);
}
return tokensVec;
};
/// <summary>unscape from:
/// {"&",\\, \q, \o, \l, \g, \e, \\r, \\n}, to:
/// {"&", \, ", ', <, >, =, \r, \n}
/// <param name="str">String to be converted</param>
/// <returns>unescaped string</returns>
/// <summary>unscape from:
/// {"&",\\, \q, \o, \l, \g, \e, \\r, \\n}, to:
/// {"&", \, ", ', <, >, =, \r, \n}
/// <param name="str">String to be converted</param>
/// <returns>unescaped string</returns>
/**
* @param {?} str
* @return {?}
*/
XmlParser.unescape =
/// <summary>unscape from:
/// {"&",\\, \q, \o, \l, \g, \e, \\r, \\n}, to:
/// {"&", \, ", ', <, >, =, \r, \n}
/// <param name="str">String to be converted</param>
/// <returns>unescaped string</returns>
/**
* @param {?} str
* @return {?}
*/
function (str) {
/** @type {?} */
var unescapedString = new StringBuilder(str.length);
for (var i = 0; i < str.length; i++) {
if (str[i] !== '\\') {
unescapedString.Append(str[i]);
continue;
}
switch (str[++i]) {
case 'q':
unescapedString.Append('\"');
break;
case 'o':
unescapedString.Append('\'');
break;
case 'l':
unescapedString.Append('<');
break;
case 'g':
unescapedString.Append('>');
break;
case 'e':
unescapedString.Append('=');
break;
case 'r':
unescapedString.Append('\r');
break;
case 'n':
unescapedString.Append('\n');
break;
default:
unescapedString.Append(str[i]);
break;
}
}
return (unescapedString.ToString());
};
/// <summary>escape from:
/// {\, ", ', <, >, =, \r, \n}, to:
/// {\\, \q, \0, \l, \g, \e, \\r, \\n}
/// <param name="str">String to be converted</param>
/// <returns>escaped string</returns>
/// <summary>escape from:
/// {\, ", ', <, >, =, \r, \n}, to:
/// {\\, \q, \0, \l, \g, \e, \\r, \\n}
/// <param name="str">String to be converted</param>
/// <returns>escaped string</returns>
/**
* @param {?} str
* @return {?}
*/
XmlParser.escape =
/// <summary>escape from:
/// {\, ", ', <, >, =, \r, \n}, to:
/// {\\, \q, \0, \l, \g, \e, \\r, \\n}
/// <param name="str">String to be converted</param>
/// <returns>escaped string</returns>
/**
* @param {?} str
* @return {?}
*/
function (str) {
/** @type {?} */
var escapedString = new StringBuilder(str.length * 2);
for (var i = 0; i < str.length; i++) {
switch (str[i]) {
case '\\':
escapedString.Append("\\\\");
break;
case '"':
escapedString.Append("\\q");
break;
case '\'':
escapedString.Append("\\o");
break;
case '<':
escapedString.Append("\\l");
break;
case '>':
escapedString.Append("\\g");
break;
case '=':
escapedString.Append("\\e");
break;
case '\r':
escapedString.Append("\r");
break;
case '\n':
escapedString.Append("\n");
break;
default:
escapedString.Append(str[i]);
break;
}
}
return (escapedString.ToString());
};
/// <summary>
/// here we only need to take care of "&" so that Sax parser will be able to handle url
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
/// <summary>
/// here we only need to take care of "&" so that Sax parser will be able to handle url
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
/**
* @param {?} str
* @return {?}
*/
XmlParser.escapeUrl =
/// <summary>
/// here we only need to take care of "&" so that Sax parser will be able to handle url
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
/**
* @param {?} str
* @return {?}
*/
function (str) {
return NString.Replace(str, "&", "&");
};
/// <summary>get next tag name from current index in XML string</summary>
/// <returns> next tag name </returns>
/// <summary>get next tag name from current index in XML string</summary>
/// <returns> next tag name </returns>
/**
* @return {?}
*/
XmlParser.prototype.getNextTag =
/// <summary>get next tag name from current index in XML string</summary>
/// <returns> next tag name </returns>
/**
* @return {?}
*/
function () {
if (this._xmLdata.length - this._currIndex <= 1) {
return null; // end of XML string
}
for (var tmpIndx = this._currIndex + 1; tmpIndx < this._xmLdata.length; tmpIndx++) {
/** @type {?} */
var tmpChar = this._xmLdata[tmpIndx];
// a letter starts an element and ends with " ". "/" starts an element closing and ends with '>'.
if (NChar.IsLetter(tmpChar) || tmpChar === '/') {
/** @type {?} */
var endOfTag = NString.IndexOfAny(this._xmLdata, XmlParser.endOfNameChar, tmpIndx, this._xmLdata.length - tmpIndx);
if (endOfTag === -1)
return null;
else
return this._xmLdata.substr(tmpIndx, endOfTag - tmpIndx);
}
}
return null;
};
/// <summary>Substring of XMLstring</summary>
/// <returns> substring of XML string -from currIndex to endContext </returns>
/// <summary>Substring of XMLstring</summary>
/// <returns> substring of XML string -from currIndex to endContext </returns>
/**
* @param {?} endContext
* @return {?}
*/
XmlParser.prototype.getXMLsubstring =
/// <summary>Substring of XMLstring</summary>
/// <returns> substring of XML string -from currIndex to endContext </returns>
/**
* @param {?} endContext
* @return {?}
*/
function (endContext) {
return this._xmLdata.substr(this._currIndex, endContext - this._currIndex);
};
/// <summary>get current element value</summary>
/// <returns> element's value </returns>
/// <summary>get current element value</summary>
/// <returns> element's value </returns>
/**
* @return {?}
*/
XmlParser.prototype.GetCurrentElementValue =
/// <summary>get current element value</summary>
/// <returns> element's value </returns>
/**
* @return {?}
*/
function () {
this.setCurrIndex2EndOfTag();
/** @type {?} */
var endContext = this.getXMLdata().indexOf(XMLConstants.TAG_OPEN, this.getCurrIndex());
// read value of xml element
/** @type {?} */
var value = this.getXMLsubstring(endContext);
this.setCurrIndex2EndOfTag();
return value;
};
/// <summary>set current index (on parsing time) to the end of current tag</summary>
/// <summary>set current index (on parsing time) to the end of current tag</summary>
/**
* @return {?}
*/
XmlParser.prototype.setCurrIndex2EndOfTag =
/// <summary>set current index (on parsing time) to the end of current tag</summary>
/**
* @return {?}
*/
function () {
this._currIndex = this._xmLdata.indexOf(XMLConstants.TAG_CLOSE, this._currIndex) + 1;
};
/// <summary>get int from string at parsing time</summary>
/// <summary>get int from string at parsing time</summary>
/**
* @param {?} valueStr
* @return {?}
*/
XmlParser.getInt =
/// <summary>get int from string at parsing time</summary>
/**
* @param {?} valueStr
* @return {?}
*/
function (valueStr) {
return NNumber.Parse(valueStr.trim());
};
/// <summary>get boolean from string at parsing time</summary>
/// <summary>get boolean from string at parsing time</summary>
/**
* @param {?} valueStr
* @return {?}
*/
XmlParser.getBoolean =
/// <summary>get boolean from string at parsing time</summary>
/**
* @param {?} valueStr
* @return {?}
*/
function (valueStr) {
return valueStr[0] === '1';
};
/// <summary>get/set functions 4 XMLstring & currIndex, for parser</summary>
/// <summary>get/set functions 4 XMLstring & currIndex, for parser</summary>
/**
* @return {?}
*/
XmlParser.prototype.getCurrIndex =
/// <summary>get/set functions 4 XMLstring & currIndex, for parser</summary>
/**
* @return {?}
*/
function () {
return this._currIndex;
};
/**
* @return {?}
*/
XmlParser.prototype.getXMLdata = /**
* @return {?}
*/
function () {
return this._xmLdata;
};
/**
* @param {?} add
* @return {?}
*/
XmlParser.prototype.add2CurrIndex = /**
* @param {?} add
* @return {?}
*/
function (add) {
this._currIndex = this._currIndex + add;
};
/**
* @param {?} index
* @return {?}
*/
XmlParser.prototype.setCurrIndex = /**
* @param {?} index
* @return {?}
*/
function (index) {
this._currIndex = index;
};
/**
* @param {?} data
* @return {?}
*/
XmlParser.prototype.setXMLdata = /**
* @param {?} data
* @return {?}
*/
function (data) {
if (data !== null)
this._xmLdata = data.trim();
else {
this._xmLdata = null;
this.setCurrIndex(0);
}
};
/// <summary>
/// prepare the parser to read from the newXmlString
/// </summary>
/// <param name="newXmlString"></param>
/// <summary>
/// prepare the parser to read from the newXmlString
/// </summary>
/// <param name="newXmlString"></param>
/**
* @param {?} newXmlString
* @return {?}
*/
XmlParser.prototype.PrepareFormReadString =
/// <summary>
/// prepare the parser to read from the newXmlString
/// </summary>
/// <param name="newXmlString"></param>
/**
* @param {?} newXmlString
* @return {?}
*/
function (newXmlString) {
this.setXMLdata(newXmlString);
this.setCurrIndex(0);
};
/// <summary> push the current parsing information into the history stack</summary>
/// <summary> push the current parsing information into the history stack</summary>
/**
* @return {?}
*/
XmlParser.prototype.push =
/// <summary> push the current parsing information into the history stack</summary>
/**
* @return {?}
*/
function () {
this._history.push(this._currIndex);
this._history.push(this._xmLdata);
};
/// <summary> restore the previous parsing information from the history stack</summary>
/// <summary> restore the previous parsing information from the history stack</summary>
/**
* @return {?}
*/
XmlParser.prototype.pop =
/// <summary> restore the previous parsing information from the history stack</summary>
/**
* @return {?}
*/
function () {
/** @type {?} */
var count = this._history.length;
this._xmLdata = (/** @type {?} */ (this._history.get_Item(count - 1)));
this._currIndex = (/** @type {?} */ (this._history.get_Item(count - 2)));
this._history.SetSize(count - 2);
};
/// <summary>gets a table cache xml and set the xmlparser data and index accordingly</summary>
/// <summary>gets a table cache xml and set the xmlparser data and index accordingly</summary>
/**
* @param {?} data
* @return {?}
*/
XmlParser.prototype.loadTableCacheData =
/// <summary>gets a table cache xml and set the xmlparser data and index accordingly</summary>
/**
* @param {?} data
* @return {?}
*/
function (data) {
this.setXMLdata(data);
this.setCurrIndex(0);
};
/// <summary>
/// Reads the XML from the element at the current position until the end of
/// the element, returning the contents as a string. This allows deferring the
/// processing of an element until the time is right to do so.<br/>
/// The returned string contains the element tag itself. For example:<br/>
/// - Assuming that the current element is 'element1', with 2 'innerElement' elements, the
/// resulting string will look like this:<br/>
/// <element1>
/// <innerelement/>
/// <innerelement/>
/// </element1>
///
/// This makes the result valid for processing by this XML parser.
/// </summary>
/// <returns></returns>
/// <summary>
/// Reads the XML from the element at the current position until the end of
/// the element, returning the contents as a string. This allows deferring the
/// processing of an element until the time is right to do so.<br/>
/// The returned string contains the element tag itself. For example:<br/>
/// - Assuming that the current element is 'element1', with 2 'innerElement' elements, the
/// resulting string will look like this:<br/>
/// <element1>
/// <innerelement/>
/// <innerelement/>
/// </element1>
///
/// This makes the result valid for processing by this XML parser.
/// </summary>
/// <returns></returns>
/**
* @return {?}
*/
XmlParser.prototype.ReadToEndOfCurrentElement =
/// <summary>
/// Reads the XML from the element at the current position until the end of
/// the element, returning the contents as a string. This allows deferring the
/// processing of an element until the time is right to do so.<br/>
/// The returned string contains the element tag itself. For example:<br/>
/// - Assuming that the current element is 'element1', with 2 'innerElement' elements, the
/// resulting string will look like this:<br/>
/// <element1>
/// <innerelement/>
/// <innerelement/>
/// </element1>
///
/// This makes the result valid for processing by this XML parser.
/// </summary>
/// <returns></returns>
/**
* @return {?}
*/
function () {
// Get the current tag according to the value of _currIndex.
/** @type {?} */
var currentTag = this.getNextTag();
/** @type {?} */
var currentTagIndex = this._xmLdata.indexOf(XMLConstants.TAG_OPEN + currentTag, this.getCurrIndex());
// Find the end of the element's block in the XML.
// find next open tag
/** @type {?} */
var nextOpenTagIndex = this._xmLdata.indexOf(XMLConstants.TAG_OPEN, currentTagIndex + 1);
if (nextOpenTagIndex === -1)
nextOpenTagIndex = this._xmLdata.length;
// find a close tag BEFORE the next open tag
/** @type {?} */
var elementEndIndex = NString.IndexOf(this._xmLdata, XMLConstants.TAG_TERM, this.getCurrIndex(), nextOpenTagIndex - this.getCurrIndex());
if (elementEndIndex === -1)
// close tag was not found in range - we have inner elements, look for the full close tag
elementEndIndex = this._xmLdata.indexOf("/" + currentTag, this.getCurrIndex()) + currentTag.length + XMLConstants.TAG_TERM.length;
else
elementEndIndex = elementEndIndex + XMLConstants.TAG_TERM.length;
// Copy the element data so it can be returned.
/** @type {?} */
var elementBlock = this.getXMLsubstring(elementEndIndex);
// Move the parser to the end of the element block.
this.setCurrIndex(elementEndIndex);
return elementBlock;
};
/**
* @return {?}
*/
XmlParser.prototype.ReadContentOfCurrentElement = /**
* @return {?}
*/
function () {
// Get the current tag according to the value of _currIndex.
/** @type {?} */
var currentTag = this.getNextTag();
// Find the end of the element's block in the XML.
/** @type {?} */
var elementEndIndex = this._xmLdata.indexOf("</" + currentTag + ">", this.getCurrIndex());
if (elementEndIndex === -1)
// Can't find the end of the current element - either XML is faulty or the element is empty.
return NString.Empty;
// Move to the end of the opening tag
this.setCurrIndex2EndOfTag();
// Copy the content of the element (from the end of the opening tag to the beginning of the closing tag).
/** @type {?} */
var elementBlock = this.getXMLsubstring(elementEndIndex);
// Move the parser to the end of the element block.
this.setCurrIndex(elementEndIndex);
this.setCurrIndex2EndOfTag();
return elementBlock;
};
/**
* @param {?=} headCharCount
* @param {?=} tailCharCount
* @return {?}
*/
XmlParser.prototype.toString = /**
* @param {?=} headCharCount
* @param {?=} tailCharCount
* @return {?}
*/
function (headCharCount, tailCharCount) {
if (arguments.length === 0) {
return this.ToString_0();
}
return this.ToString_1(headCharCount, tailCharCount);
};
/// <summary>
/// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/>
/// The method will show the XML data, trimming it to 20 characters before the
/// current position (_currIndex) and up to 50 characters after the current position.
/// The current position itself will be marked with a marker that looks like:
/// |-{current index}-| <br/>
/// The marker will be placed immediately before _xmlData[_currIndex].
/// </summary>
/// <returns></returns>
/// <summary>
/// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/>
/// The method will show the XML data, trimming it to 20 characters before the
/// current position (_currIndex) and up to 50 characters after the current position.
/// The current position itself will be marked with a marker that looks like:
/// |-{current index}-| <br/>
/// The marker will be placed immediately before _xmlData[_currIndex].
/// </summary>
/// <returns></returns>
/**
* @return {?}
*/
XmlParser.prototype.ToString_0 =
/// <summary>
/// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/>
/// The method will show the XML data, trimming it to 20 characters before the
/// current position (_currIndex) and up to 50 characters after the current position.
/// The current position itself will be marked with a marker that looks like:
/// |-{current index}-| <br/>
/// The marker will be placed immediately before _xmlData[_currIndex].
/// </summary>
/// <returns></returns>
/**
* @return {?}
*/
function () {
return this.toString(20, 50);
};
/// <summary>
/// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/>
/// The method will show the XML data, trimming it to headCharCount characters before the
/// current position (_currIndex) and up to tailCharCount characters after the current position.
/// The current position itself will be marked with a marker that looks like:
/// |-{current index}-| <br/>
/// The marker will be placed immediately before _xmlData[_currIndex].
/// </summary>
/// <param name="headCharCount">Number of characters to show before the current position marker.</param>
/// <param name="tailCharCount">Number of characters to show after the current position marker.</param>
/// <returns></returns>
/// <summary>
/// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/>
/// The method will show the XML data, trimming it to headCharCount characters before the
/// current position (_currIndex) and up to tailCharCount characters after the current position.
/// The current position itself will be marked with a marker that looks like:
/// |-{current index}-| <br/>
/// The marker will be placed immediately before _xmlData[_currIndex].
/// </summary>
/// <param name="headCharCount">Number of characters to show before the current position marker.</param>
/// <param name="tailCharCount">Number of characters to show after the current position marker.</param>
/// <returns></returns>
/**
* @param {?} headCharCount
* @param {?} tailCharCount
* @return {?}
*/
XmlParser.prototype.ToString_1 =
/// <summary>
/// Generates a string that visualizes the XML parser state (e.g. for debug watch list.)<br/>
/// The method will show the XML data, trimming it to headCharCount characters before the
/// current position (_currIndex) and up to tailCharCount characters after the current position.
/// The current position itself will be marked with a marker that looks like:
/// |-{current index}-| <br/>
/// The marker will be placed immediately before _xmlData[_currIndex].
/// </summary>
/// <param name="headCharCount">Number of characters to show before the current position marker.</param>
/// <param name="tailCharCount">Number of characters to show after the current position marker.</param>
/// <returns></returns>
/**
* @param {?} headCharCount
* @param {?} tailCharCount
* @return {?}
*/
function (headCharCount, tailCharCount) {
/** @type {?} */
var markerPosition = Math.min(this._currIndex, this._xmLdata.length);
/** @type {?} */
var segmentStartIndex = Math.max(0, markerPosition - headCharCount);
/** @type {?} */
var segmentEndIndex = Math.min(this._xmLdata.length, markerPosition + tailCharCount);
/** @type {?} */
var headLength = markerPosition - segmentStartIndex;
/** @type {?} */
var tailLength = segmentEndIndex - markerPosition;
/** @type {?} */
var segment = new StringBuilder();
if (segmentStartIndex > 0)
segment.Append("...");
if (headLength > 0)
segment.Append(this._xmLdata, segmentStartIndex, headLength);
segment.Append("|-{").Append(this._currIndex).Append("}-|");
if (tailLength > 0)
segment.Append(this._xmLdata, this._currIndex, tailLength);
if (segmentEndIndex < this._xmLdata.length)
segment.Append("...");
return segment.ToString();
};
/**
* @return {?}
*/
XmlParser.prototype.SkipXMLElement = /**
* @return {?}
*/
function () {
/** @type {?} */
var endContext = this.getXMLdata().indexOf(XMLConstants.TAG_TERM, this.getCurrIndex());
if (endContext !== -1 && endContext < this.getXMLdata().length) {
this.setCurrIndex2EndOfTag();
}
};
XmlParser.endOfNameChar = [' ', '>'];
return XmlParser;
}());
export { XmlParser };
if (false) {
/** @type {?} */
XmlParser.endOfNameChar;
/** @type {?} */
XmlParser.prototype._currIndex;
/** @type {?} */
XmlParser.prototype._xmLdata;
/** @type {?} */
XmlParser.prototype._history;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiWG1sUGFyc2VyLmpzIiwic291cmNlUm9vdCI6Im5nOi8vQG1hZ2ljLXhwYS91dGlscy8iLCJzb3VyY2VzIjpbInNyYy9YbWwvWG1sUGFyc2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSxPQUFPLEVBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxvQkFBb0IsRUFBRSxhQUFhLEVBQWMsS0FBSyxFQUFFLE9BQU8sRUFBQyxNQUFNLHNCQUFzQixDQUFDO0FBQ3BILE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxpQkFBaUIsQ0FBQzs7QUFHN0M7SUFPRSxhQUFhO0lBQ2IsR0FBRztJQUNILGNBQWM7SUFDZCwrQkFBK0I7SUFDL0IsbUJBQVksSUFBNEI7UUFBNUIscUJBQUEsRUFBQSxPQUFlLE9BQU8sQ0FBQyxLQUFLO1FBUmhDLGVBQVUsR0FBVyxDQUFDLENBQUM7UUFDdkIsYUFBUSxHQUFXLEVBQUUsQ0FBQztRQUN0QixhQUFRLEdBQTBCLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQyx3REFBd0Q7UUFPNUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN0QixJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCx5R0FBeUc7SUFDekcsOERBQThEO0lBQzlELG9GQUFvRjtJQUNwRiwrSUFBK0k7SUFDL0ksa0hBQWtIOzs7Ozs7Ozs7Ozs7SUFDM0csbUJBQVM7Ozs7Ozs7Ozs7OztJQUFoQixVQUFpQixHQUFXLEVBQUUsU0FBaUIsRUFBRSxVQUEwQjtRQUExQiwyQkFBQSxFQUFBLGlCQUEwQjs7WUFDckUsU0FBUyxHQUFpQixJQUFJLElBQUksRUFBVTs7WUFDNUMsS0FBSyxHQUFXLElBQUk7UUFFeEIsRUFBRSxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztZQUNmLEdBQUcsR0FBRyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDbkIsQ0FBQzs7WUFFRyxNQUFNLEdBQWEsR0FBRyxD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RSxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBOVpjLHVCQUFhLEdBQWEsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7SUErWnRELGdCQUFDO0NBQUEsQUFoYUQsSUFnYUM7U0FoYVksU0FBUzs7O0lBQ3BCLHdCQUFvRDs7SUFFcEQsK0JBQStCOztJQUMvQiw2QkFBOEI7O0lBQzlCLDZCQUFxRCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7TlN0cmluZywgTGlzdCwgQXBwbGljYXRpb25FeGNlcHRpb24sIFN0cmluZ0J1aWxkZXIsIERpY3Rpb25hcnksIE5DaGFyLCBOTnVtYmVyfSBmcm9tIFwiQG1hZ2ljLXhwYS9tc2NvcmVsaWJcIjtcclxuaW1wb3J0IHtYTUxDb25zdGFudHN9IGZyb20gXCIuLi9YTUxDb25zdGFudHNcIjtcclxuXHJcbi8vLyA8c3VtbWFyeT4gYSBoZWxwZXIgY2xhc3MgZm9yIHRoZSBwYXJzaW5nIG9mIHRoZSBYTUw8L3N1bW1hcnk+XHJcbmV4cG9ydCBjbGFzcyBYbWxQYXJzZXIge1xyXG4gIHByaXZhdGUgc3RhdGljIGVuZE9mTmFtZUNoYXI6IHN0cmluZ1tdID0gWycgJywgJz4nXTtcclxuXHJcbiAgcHJpdmF0ZSBfY3VyckluZGV4OiBudW1iZXIgPSAwO1xyXG4gIHByaXZhdGUgX3htTGRhdGE6IHN0cmluZyA9IFwiXCI7XHJcbiAgcHJpdmF0ZSBfaGlzdG9yeTogTGlzdDxudW1iZXIgfCBzdHJpbmc+ID0gbmV3IExpc3QoKTsgLy8gSW4gb3JkZXIgdG8gYWxsb3cgcmVjdXJzaXZlIHBhcnNpbmcgd2Ugc2F2ZSBwcmV2IGRhdGFcclxuXHJcbiAgLy8vIDxzdW1tYXJ5PlxyXG4gIC8vL1xyXG4gIC8vLyA8L3N1bW1hcnk+XHJcbiAgLy8vIDxwYXJhbSBuYW1lPVwiZGF0YVwiPjwvcGFyYW0+XHJcbiAgY29uc3RydWN0b3IoZGF0YTogc3RyaW5nID0gTlN0cmluZy5FbXB0eSkge1xyXG4gICAgdGhpcy5zZXRYTUxkYXRhKGRhdGEpO1xyXG4gICAgdGhpcy5zZXRDdXJySW5kZXgoMCk7XHJcbiAgfVxyXG5cclxuICAvLy8gPHN1bW1hcnk+IHBhcnNlIGEgc3RyaW5nIGFjY29yZGluZyB0byBhIHNldCBvZiBkZWxpbWl0ZXJzIGFuZCByZXR1cm4gdGhlIHJlc3VsdCBpbiBhIHZlY3Rvcjwvc3VtbWFyeT5cclxuICAvLy8gPHBhcmFtIG5hbWU9XCJzdHJcIj50aGUgU3RyaW5nIHdoaWNoIG5lZWQgYmUgcGFydGVkIDwvcGFyYW0+XHJcbiAgLy8vIDxwYXJhbSBuYW1lPVwiZGVsaW1pdGVyXCI+dGhlIGRlbGltaXRlciB3aGljaCBwYXJ0IGRpZmZlcmVudCBwYXJ0cyBvZiBzdHIgPC9wYXJhbT5cclxuICAvLy8gPHBhcmFtIG5hbWU9XCJpc01hZ2ljWE1MXCI+aXMgbmVlZGVkIHRva2VuaXplciB3b3JraW5nIG9uIE1hZ2ljIFhNTCwgc28gdGhlIFwiPVwiIHNpZ24gd2lsbCBiZSBkZWxpdGVkIGluIHRoZSBlbmQgb2YgZXZlcnkgZmlyc3QgdG9rZW4gPC9wYXJhbT5cclxuICAvLy8gPHJldHVybnM+IHRtcFZlY3RvciBkeW5hbWljYWxseSBhcnJheSwgd2hpY2ggY29uc2lzdCB0b2tlbnMgaW4gZXZlcnkgZWxlbWVudCwgZXZlcnkgdG9rZW4gaXMgU3RyaW5nIDwvcmV0dXJucz5cclxuICBzdGF0aWMgZ2V0VG9rZW5zKHN0cjogc3RyaW5nLCBkZWxpbWl0ZXI6IHN0cmluZywgaXNNYWdpY1hNTDogYm9vbGVhbiA9IHRydWUpOiBMaXN0PHN0cmluZz4ge1xyXG4gICAgbGV0IHRva2Vuc1ZlYzogTGlzdDxzdHJpbmc+ID0gbmV3IExpc3Q8c3RyaW5nPigpO1xyXG4gICAgbGV0IHRva2VuOiBzdHJpbmcgPSBudWxsO1xyXG5cclxuICAgIGlmIChpc01hZ2ljWE1MKSB7XHJcbiAgICAgIHN0ciA9IHN0ci50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IHN0clRvazogc3RyaW5nW10gPSBzdHIuc3BsaXQoZGVsaW1pdGVyLmNoYXJBdCgwKSk7XHJcblxyXG4gICAgZm9yIChsZXQgaTogbnVtYmVyID0gMDsgaSA8IHN0clRvay5sZW5ndGg7IGkgPSBpICsgMSkge1xyXG4gICAgICAvLyBTcGxpdCBpbiBDIyBjcmVhdGVzIGEgbGFzdCBlbXB0eSBzdHJpbmcgdG9rZW4gaWYgdGhlIHNvdXJjZSBzdHJpbmcgZW5kcyB3aXRoXHJcbiAgICAgIC8vIHRoZSBkZWxpbWl0ZXIgb3IgaWYgdGhlIHN0cmluZyBpcyBlbXB0eSAoYXMgb3Bwb3NlZCB0byBKYXZhIHRoYXQgd2lsbCBpZ25vcmUgaXQpXHJcbiAgICAgIC8vIHRoZXJlZm9yZSB3ZSBoYXZlIHRvIGJyZWFrIHRoaXMgbG9vcCBpZiBzdWNoIGNhc2Ugb2NjdXJzLlxyXG4gICAgICBpZiAoaXNNYWdpY1hNTCAmJiBpID09PSBzdHJUb2subGVuZ3RoIC0gMSAmJiBzdHJUb2subGVuZ3RoICUgMiA9PT0gMSkge1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICB9XHJcblxyXG4gICAgICB0b2tlbiA9IHN0clRva1tpXTtcclxuICAgICAgaWYgKGlzTWFnaWNYTUwpIHtcclxuICAgICAgICAvLyB0aGUgMXN0IHRva2VuIGluIHRoZSBwYWlyIGNvbWVzIHdpdGggXCI9XCIsIHJlbW92ZSBpdC5cclxuICAgICAgICBpZiAoaSAlIDIgPT09IDApIHtcclxuICAgICAgICAgIHRva2VuID0gdG9rZW4udHJpbSgpO1xyXG4gICAgICAgICAgaWYgKHRva2VuLmVuZHNXaXRoKFwiPVwiKSkge1xyXG4gICAgICAgICAgICB0b2tlbiA9IHRva2VuLnN1YnN0cigwLCB0b2tlbi5sZW5ndGggLSAxKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgLy8gMm5kIHRva2VuIGluIHRoZSBwYWlyIGNhbiBiZSBhbiBlbXB0eSBzdHJpbmcsIGluIHRoYXQgY2FzZSBzZXQgaXQgdG8gXCIgXCIuXHJcbiAgICAgICAgZWxzZSBpZiAodG9rZW4gPT09IFwiXCIpXHJcbiAgICAgICAgICB0b2tlbiA9IFwiIFwiO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAodG9rZW4gPT09IG51bGwpXHJcbiAgICAgICAgdGhyb3cgbmV3IEFwcGxpY2F0aW9uRXhjZXB0aW9uKFwiaW4gQ2xpZW50TWFuYWdlci5JbnN0YW5jZS5YTUxQYXJzZXIuZ2V0VG9rZW5zKCkgbnVsbCB0b2tlbiB2YWx1ZVwiKTtcclxuXHJcbiAgICAgIHRva2Vuc1ZlYy5wdXNoKHRva2VuKTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gdG9rZW5zVmVjO1xyXG4gIH1cclxuXHJcbiAgLy8vIDxzdW1tYXJ5PnVuc2NhcGUgZnJvbTpcclxuICAvLy8ge1wiJmFtcDtcIixcXFxcLCBcXHEsIFxcbywgXFxsLCBcXGcsIFxcZSwgXFxcXHIsIFxcXFxufSwgdG86XHJcbiAgLy8vIHtcIiZcIiwgICAgIFxcLCAgXCIsICAnLCAgPCwgID4sICA9LCAgXFxyLCAgXFxufVxyXG4gIC8vLyA8cGFyYW0gbmFtZT1cInN0clwiPlN0cmluZyB0byBiZSBjb252ZXJ0ZWQ8L3BhcmFtPlxyXG4gIC8vLyA8cmV0dXJucz51bmVzY2FwZWQgc3RyaW5nPC9yZXR1cm5zPlxyXG4gIHB1YmxpYyBzdGF0aWMgdW5lc2NhcGUoc3RyOiBzdHJpbmcpOiBzdHJpbmdcclxuICB7XHJcbiAgICBsZXQgdW5lc2NhcGVkU3RyaW5nOiBTdHJpbmdCdWlsZGVyID0gbmV3IFN0cmluZ0J1aWxkZXIoc3RyLmxlbmd0aCk7XHJcblxyXG4gICAgZm9yIChsZXQgaTogbnVtYmVyID0gMDsgaSA8IHN0ci5sZW5ndGg7IGkrKykge1xyXG4gICAgICBpZiAoc3RyW2ldICE9PSAnXFxcXCcpIHtcclxuICAgICAgICB1bmVzY2FwZWRTdHJpbmcuQXBwZW5kKHN0cltpXSk7XHJcbiAgICAgICAgY29udGludWU7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHN3aXRjaCAoc3RyWysraV0pIHtcclxuICAgICAgICBjYXNlICdxJzpcclxuICAgICAgICAgIHVuZXNjYXBlZFN0cmluZy5BcHBlbmQoJ1xcXCInKTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGNhc2UgJ28nOlxyXG4gICAgICAgICAgdW5lc2NhcGVkU3RyaW5nLkFwcGVuZCgnXFwnJyk7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICdsJzpcclxuICAgICAgICAgIHVuZXNjYXBlZFN0cmluZy5BcHBlbmQoJzwnKTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGNhc2UgJ2cnOlxyXG4gICAgICAgICAgdW5lc2NhcGVkU3RyaW5nLkFwcGVuZCgnPicpO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnZSc6XHJcbiAgICAgICAgICB1bmVzY2FwZWRTdHJpbmcuQXBwZW5kKCc9Jyk7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICdyJzpcclxuICAgICAgICAgIHVuZXNjYXBlZFN0cmluZy5BcHBlbmQoJ1xccicpO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnbic6XHJcbiAgICAgICAgICB1bmVzY2FwZWRTdHJpbmcuQXBwZW5kKCdcXG4nKTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICB1bmVzY2FwZWRTdHJpbmcuQXBwZW5kKHN0cltpXSk7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuICh1bmVzY2FwZWRTdHJpbmcuVG9TdHJpbmcoKSk7XHJcbiAgfVxyXG5cclxuICAvLy8gPHN1bW1hcnk+ZXNjYXBlIGZyb206XHJcbiAgLy8vIHtcXCwgIFwiLCAgJywgIDwsICAgPiwgID0sICBcXHIsICBcXG59LCB0bzpcclxuICAvLy8ge1xcXFwsIFxccSwgXFwwLCBcXGwsICBcXGcsIFxcZSwgXFxcXHIsIFxcXFxufVxyXG4gIC8vLyA8cGFyYW0gbmFtZT1cInN0clwiPlN0cmluZyB0byBiZSBjb252ZXJ0ZWQ8L3BhcmFtPlxyXG4gIC8vLyA8cmV0dXJucz5lc2NhcGVkIHN0cmluZzwvcmV0dXJucz5cclxuICBwdWJsaWMgc3RhdGljIGVzY2FwZShzdHI6IHN0cmluZyk6IHN0cmluZ1xyXG4gIHtcclxuICAgIGxldCBlc2NhcGVkU3RyaW5nOiBTdHJpbmdCdWlsZGVyID0gbmV3IFN0cmluZ0J1aWxkZXIoc3RyLmxlbmd0aCAqIDIpO1xyXG5cclxuICAgIGZvciAobGV0IGk6IG51bWJlciA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHtcclxuICAgICAgc3dpdGNoIChzdHJbaV0pIHtcclxuICAgICAgICBjYXNlICdcXFxcJzpcclxuICAgICAgICAgIGVzY2FwZWRTdHJpbmcuQXBwZW5kKFwiXFxcXFxcXFxcIik7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICdcIic6XHJcbiAgICAgICAgICBlc2NhcGVkU3RyaW5nLkFwcGVuZChcIlxcXFxxXCIpO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnXFwnJzpcclxuICAgICAgICAgIGVzY2FwZWRTdHJpbmcuQXBwZW5kKFwiXFxcXG9cIik7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICc8Jz