From 177dc0745b96d7bc25d1e1439eec0207a14c76c1 Mon Sep 17 00:00:00 2001 From: Andriy Romanov Date: Sun, 24 Feb 2013 23:38:55 -0800 Subject: [PATCH 1/2] Used "go fmt" for formatting go code --- go/src/closure/template/soyutil/bidi.go | 183 +- go/src/closure/template/soyutil/const.go | 308 ++-- go/src/closure/template/soyutil/data.go | 1305 +++++++------ go/src/closure/template/soyutil/data_test.go | 156 +- .../template/soyutil/escaping_conventions.go | 1635 +++++++++-------- .../template/soyutil/sanitized_content.go | 63 +- go/src/closure/template/soyutil/sanitizers.go | 460 +++-- .../template/soyutil/sanitizers_test.go | 24 +- go/src/closure/template/soyutil/utils.go | 559 +++--- go/src/closure/template/soyutil/utils_test.go | 48 +- 10 files changed, 2348 insertions(+), 2393 deletions(-) diff --git a/go/src/closure/template/soyutil/bidi.go b/go/src/closure/template/soyutil/bidi.go index 0b81641..663337c 100644 --- a/go/src/closure/template/soyutil/bidi.go +++ b/go/src/closure/template/soyutil/bidi.go @@ -1,10 +1,9 @@ -package soyutil; +package soyutil import ( - "strings" + "strings" ) - /** * Strips str of any HTML mark-up and escapes. Imprecise in several ways, but * precision is not very important, since the result is only meant to be used @@ -16,13 +15,12 @@ import ( * @private */ func BidiStripHtmlIfNecessary(str string, opt_isHtml bool) string { - if opt_isHtml { - return _BIDI_HTML_SKIP_RE.ReplaceAllString(str, " ") - } - return str + if opt_isHtml { + return _BIDI_HTML_SKIP_RE.ReplaceAllString(str, " ") + } + return str } - /** * Estimate the overall directionality of text. If opt_isHtml, makes sure to * ignore the LTR nature of the mark-up and escapes in text, making the logic @@ -33,16 +31,15 @@ func BidiStripHtmlIfNecessary(str string, opt_isHtml bool) string { * @return {number} 1 if text is LTR, -1 if it is RTL, and 0 if it is neutral. */ func BidiTextDir(text string, opt_isHtml bool) int { - text = BidiStripHtmlIfNecessary(text, opt_isHtml); - if len(text) == 0 { - return 0 - } - if BidiDetectRtlDirectionality(text) { - return -1 - } - return 1 -}; - + text = BidiStripHtmlIfNecessary(text, opt_isHtml) + if len(text) == 0 { + return 0 + } + if BidiDetectRtlDirectionality(text) { + return -1 + } + return 1 +} /** * Returns "dir=ltr" or "dir=rtl", depending on text's estimated @@ -59,18 +56,18 @@ func BidiTextDir(text string, opt_isHtml bool) int { * text in non-LTR context; else, the empty string. */ func BidiDirAttr(bidiGlobalDir int, text string, opt_isHtml bool) string { - dir := BidiTextDir(text, opt_isHtml) - switch { - case dir == bidiGlobalDir: - return "" - case dir < 0: - return "dir=rtl" - case dir > 0: - return "dir=ltr" - default: - return "" - } - return "" + dir := BidiTextDir(text, opt_isHtml) + switch { + case dir == bidiGlobalDir: + return "" + case dir < 0: + return "dir=rtl" + case dir > 0: + return "dir=ltr" + default: + return "" + } + return "" } /** @@ -89,11 +86,10 @@ func BidiDirAttr(bidiGlobalDir int, text string, opt_isHtml bool) string { * bidiGlobalDir. */ func BidiMarkAfter(bidiGlobalDir int, text string, opt_isHtml bool) string { - dir := BidiTextDir(text, opt_isHtml) - return BidiMarkAfterKnownDir(bidiGlobalDir, dir, text, opt_isHtml) + dir := BidiTextDir(text, opt_isHtml) + return BidiMarkAfterKnownDir(bidiGlobalDir, dir, text, opt_isHtml) } - /** * Returns a Unicode BiDi mark matching bidiGlobalDir (LRM or RLM) if the * directionality or the exit directionality of text are opposite to @@ -111,18 +107,17 @@ func BidiMarkAfter(bidiGlobalDir int, text string, opt_isHtml bool) string { * bidiGlobalDir. */ func BidiMarkAfterKnownDir(bidiGlobalDir int, dir int, text string, opt_isHtml bool) string { - switch { - case bidiGlobalDir > 0 && (dir < 0 || BidiIsRtlExitText(text, opt_isHtml)): - return "\u200E" // LRM - case bidiGlobalDir < 0 && (dir > 0 || BidiIsLtrExitText(text, opt_isHtml)): - return "\u200F" // RLM - default: - return "" - } - return "" + switch { + case bidiGlobalDir > 0 && (dir < 0 || BidiIsRtlExitText(text, opt_isHtml)): + return "\u200E" // LRM + case bidiGlobalDir < 0 && (dir > 0 || BidiIsLtrExitText(text, opt_isHtml)): + return "\u200F" // RLM + default: + return "" + } + return "" } - /** * Returns str wrapped in a according to its directionality - * but only if that is neither neutral nor the same as the global context. @@ -137,21 +132,20 @@ func BidiMarkAfterKnownDir(bidiGlobalDir int, dir int, text string, opt_isHtml b * @return {string} The wrapped string. */ func BidiSpanWrap(bidiGlobalDir int, str string, isHtml bool) string { - var output string - textDir := BidiTextDir(str, isHtml) - reset := BidiMarkAfterKnownDir(bidiGlobalDir, textDir, str, isHtml) - switch { - case textDir > 0 && bidiGlobalDir <= 0: - output = "" + str + "" - case textDir < 0 && bidiGlobalDir >= 0: - output = "" + str + "" - default: - output = str - } - return output + reset + var output string + textDir := BidiTextDir(str, isHtml) + reset := BidiMarkAfterKnownDir(bidiGlobalDir, textDir, str, isHtml) + switch { + case textDir > 0 && bidiGlobalDir <= 0: + output = "" + str + "" + case textDir < 0 && bidiGlobalDir >= 0: + output = "" + str + "" + default: + output = str + } + return output + reset } - /** * Returns str wrapped in Unicode BiDi formatting characters according to its * directionality, i.e. either LRE or RLE at the beginning and PDF at the end - @@ -167,21 +161,20 @@ func BidiSpanWrap(bidiGlobalDir int, str string, isHtml bool) string { * @return {string} The wrapped string. */ func BidiUnicodeWrap(bidiGlobalDir int, str string, isHtml bool) string { - var output string - textDir := BidiTextDir(str, isHtml) - reset := BidiMarkAfterKnownDir(bidiGlobalDir, textDir, str, isHtml) - switch { - case textDir > 0 && bidiGlobalDir <= 0: - output = "\u202A" + str + "\u202C" - case textDir < 0 && bidiGlobalDir >= 0: - output = "\u202B" + str + "\u202C" - default: - output = str - } - return output + reset + var output string + textDir := BidiTextDir(str, isHtml) + reset := BidiMarkAfterKnownDir(bidiGlobalDir, textDir, str, isHtml) + switch { + case textDir > 0 && bidiGlobalDir <= 0: + output = "\u202A" + str + "\u202C" + case textDir < 0 && bidiGlobalDir >= 0: + output = "\u202B" + str + "\u202C" + default: + output = str + } + return output + reset } - /** * Check the directionality of the a piece of text based on the first character * with strong directionality. @@ -190,10 +183,9 @@ func BidiUnicodeWrap(bidiGlobalDir int, str string, isHtml bool) string { * @private */ func BidiIsRtlText(str string) bool { - return _BIDI_RTL_DIR_CHECK_RE.MatchString(str) + return _BIDI_RTL_DIR_CHECK_RE.MatchString(str) } - /** * Check the directionality of the a piece of text based on the first character * with strong directionality. @@ -202,10 +194,9 @@ func BidiIsRtlText(str string) bool { * @private */ func BidiIsNeutralText(str string) bool { - return _BIDI_NEUTRAL_DIR_CHECK_RE.MatchString(str) + return _BIDI_NEUTRAL_DIR_CHECK_RE.MatchString(str) } - /** * Returns the RTL ratio based on word count. * @param {string} str the string that need to be checked. @@ -213,24 +204,23 @@ func BidiIsNeutralText(str string) bool { * @private */ func BidiRtlWordRatio(str string) float64 { - rtlCount := 0 - totalCount := 0 - tokens := strings.SplitN(str, " ", -1) - for _, token := range tokens { - if BidiIsRtlText(token) { - rtlCount++ - totalCount++ - } else if BidiIsNeutralText(token) { - totalCount++ - } - } - if totalCount == 0 { - return 0 - } - return float64(rtlCount) / float64(totalCount) + rtlCount := 0 + totalCount := 0 + tokens := strings.SplitN(str, " ", -1) + for _, token := range tokens { + if BidiIsRtlText(token) { + rtlCount++ + totalCount++ + } else if BidiIsNeutralText(token) { + totalCount++ + } + } + if totalCount == 0 { + return 0 + } + return float64(rtlCount) / float64(totalCount) } - /** * Check the directionality of a piece of text, return true if the piece of * text should be laid out in RTL direction. @@ -239,10 +229,9 @@ func BidiRtlWordRatio(str string) float64 { * @private */ func BidiDetectRtlDirectionality(str string) bool { - return BidiRtlWordRatio(str) > _BIDI_RTL_DETECTION_THRESHOLD + return BidiRtlWordRatio(str) > _BIDI_RTL_DETECTION_THRESHOLD } - /** * Check if the exit directionality a piece of text is LTR, i.e. if the last * strongly-directional character in the string is LTR. @@ -253,11 +242,10 @@ func BidiDetectRtlDirectionality(str string) bool { * @private */ func BidiIsLtrExitText(str string, opt_isHtml bool) bool { - testString := BidiStripHtmlIfNecessary(str, opt_isHtml) - return _BIDI_LTR_EXIT_DIR_CHECK_RE.MatchString(testString) + testString := BidiStripHtmlIfNecessary(str, opt_isHtml) + return _BIDI_LTR_EXIT_DIR_CHECK_RE.MatchString(testString) } - /** * Check if the exit directionality a piece of text is RTL, i.e. if the last * strongly-directional character in the string is RTL. @@ -268,9 +256,6 @@ func BidiIsLtrExitText(str string, opt_isHtml bool) bool { * @private */ func BidiIsRtlExitText(str string, opt_isHtml bool) bool { - testString := BidiStripHtmlIfNecessary(str, opt_isHtml) - return _BIDI_RTL_EXIT_DIR_CHECK_RE.MatchString(testString) + testString := BidiStripHtmlIfNecessary(str, opt_isHtml) + return _BIDI_RTL_EXIT_DIR_CHECK_RE.MatchString(testString) } - - - diff --git a/go/src/closure/template/soyutil/const.go b/go/src/closure/template/soyutil/const.go index a71f1b8..ea4ec4b 100644 --- a/go/src/closure/template/soyutil/const.go +++ b/go/src/closure/template/soyutil/const.go @@ -1,178 +1,172 @@ -package soyutil; +package soyutil import ( - "regexp" + "regexp" ) const ( - /** - * A practical pattern to identify strong LTR character. This pattern is not - * theoretically correct according to unicode standard. It is simplified for - * performance and small code size. - * @type {string} - * @private - */ - _BIDI_LTR_CHARS = "A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF" - - /** - * A practical pattern to identify strong neutral and weak character. This - * pattern is not theoretically correct according to unicode standard. It is - * simplified for performance and small code size. - * @type {string} - * @private - */ - _BIDI_NEUTRAL_CHARS = "\u0000-\u0020!-@[-`{-\u00BF\u00D7\u00F7\u02B9-\u02FF\u2000-\u2BFF" - - /** - * A practical pattern to identify strong RTL character. This pattern is not - * theoretically correct according to unicode standard. It is simplified for - * performance and small code size. - * @type {string} - * @private - */ - _BIDI_RTL_CHARS = "\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC" - - - /** - * This constant controls threshold of rtl directionality. - * @type {number} - * @private - */ - _BIDI_RTL_DETECTION_THRESHOLD = 0.40 + /** + * A practical pattern to identify strong LTR character. This pattern is not + * theoretically correct according to unicode standard. It is simplified for + * performance and small code size. + * @type {string} + * @private + */ + _BIDI_LTR_CHARS = "A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF" + /** + * A practical pattern to identify strong neutral and weak character. This + * pattern is not theoretically correct according to unicode standard. It is + * simplified for performance and small code size. + * @type {string} + * @private + */ + _BIDI_NEUTRAL_CHARS = "\u0000-\u0020!-@[-`{-\u00BF\u00D7\u00F7\u02B9-\u02FF\u2000-\u2BFF" + + /** + * A practical pattern to identify strong RTL character. This pattern is not + * theoretically correct according to unicode standard. It is simplified for + * performance and small code size. + * @type {string} + * @private + */ + _BIDI_RTL_CHARS = "\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC" + + /** + * This constant controls threshold of rtl directionality. + * @type {number} + * @private + */ + _BIDI_RTL_DETECTION_THRESHOLD = 0.40 ) type ContentKind int const ( - - /** - * A snippet of HTML that does not start or end inside a tag, comment, entity, or DOCTYPE; and - * that does not contain any executable code (JS, {@code }s, etc.) from a different - * trust domain. - */ - CONTENT_KIND_HTML ContentKind = iota + 1 - - /** - * A sequence of code units that can appear between quotes (either single or double) in a JS - * program without causing a parse error, and without causing any side effects. - *

- * The content should not contain unescaped quotes, newlines, or anything else that would - * cause parsing to fail or to cause a JS parser to finish the string it is parsing inside - * the content. - *

- * The content must also not end inside an escape sequence ; no partial octal escape sequences - * or odd number of '{@code \}'s at the end. - */ - CONTENT_KIND_JS_STR_CHARS - - /** A properly encoded portion of a URI. */ - CONTENT_KIND_URI - - /** An attribute name and value, such as {@code dir="ltr"}. */ - CONTENT_KIND_HTML_ATTRIBUTE + + /** + * A snippet of HTML that does not start or end inside a tag, comment, entity, or DOCTYPE; and + * that does not contain any executable code (JS, {@code }s, etc.) from a different + * trust domain. + */ + CONTENT_KIND_HTML ContentKind = iota + 1 + + /** + * A sequence of code units that can appear between quotes (either single or double) in a JS + * program without causing a parse error, and without causing any side effects. + *

+ * The content should not contain unescaped quotes, newlines, or anything else that would + * cause parsing to fail or to cause a JS parser to finish the string it is parsing inside + * the content. + *

+ * The content must also not end inside an escape sequence ; no partial octal escape sequences + * or odd number of '{@code \}'s at the end. + */ + CONTENT_KIND_JS_STR_CHARS + + /** A properly encoded portion of a URI. */ + CONTENT_KIND_URI + + /** An attribute name and value, such as {@code dir="ltr"}. */ + CONTENT_KIND_HTML_ATTRIBUTE ) func (p ContentKind) String() string { - switch p { - case CONTENT_KIND_HTML: - return "HTML" - case CONTENT_KIND_JS_STR_CHARS: - return "JS_STR_CHARS" - case CONTENT_KIND_HTML_ATTRIBUTE: - return "HTML_ATTRIBUTE" - } - return "UNKNOWN_CONTENT_KIND" + switch p { + case CONTENT_KIND_HTML: + return "HTML" + case CONTENT_KIND_JS_STR_CHARS: + return "JS_STR_CHARS" + case CONTENT_KIND_HTML_ATTRIBUTE: + return "HTML_ATTRIBUTE" + } + return "UNKNOWN_CONTENT_KIND" } var ( - /** - * Simplified regular expression for am HTML tag (opening or closing) or an HTML - * escape - the things we want to skip over in order to ignore their ltr - * characters. - * @type {RegExp} - * @private - */ - _BIDI_HTML_SKIP_RE *regexp.Regexp - - /** - * Regular expressions to check if a piece of text is of RTL directionality - * on first character with strong directionality. - * @type {RegExp} - * @private - */ - _BIDI_RTL_DIR_CHECK_RE *regexp.Regexp - - /** - * Regular expressions to check if a piece of text is of neutral directionality. - * Url are considered as neutral. - * @type {RegExp} - * @private - */ - _BIDI_NEUTRAL_DIR_CHECK_RE *regexp.Regexp - - /** - * Regular expressions to check if the last strongly-directional character in a - * piece of text is LTR. - * @type {RegExp} - * @private - */ - _BIDI_LTR_EXIT_DIR_CHECK_RE *regexp.Regexp - - /** - * Regular expressions to check if the last strongly-directional character in a - * piece of text is RTL. - * @type {RegExp} - * @private - */ - _BIDI_RTL_EXIT_DIR_CHECK_RE *regexp.Regexp - - /** - * Regular expression used within $$changeNewlineToBr(). - * @type {RegExp} - * @private - */ - _CHANGE_NEWLINE_TO_BR_RE *regexp.Regexp - - _CHANGE_NEWLINE_TO_BR2_RE *regexp.Regexp - - - /** - * Regular expression used for determining if a string needs to be encoded. - * @type {RegExp} - * @private - */ - _ENCODE_URI_RE *regexp.Regexp - - - /** - * Character mappings used internally for soy.$$escapeJs - * @private - * @type {Object} - */ - _EscapeCharJs map[string]string - + /** + * Simplified regular expression for am HTML tag (opening or closing) or an HTML + * escape - the things we want to skip over in order to ignore their ltr + * characters. + * @type {RegExp} + * @private + */ + _BIDI_HTML_SKIP_RE *regexp.Regexp + + /** + * Regular expressions to check if a piece of text is of RTL directionality + * on first character with strong directionality. + * @type {RegExp} + * @private + */ + _BIDI_RTL_DIR_CHECK_RE *regexp.Regexp + + /** + * Regular expressions to check if a piece of text is of neutral directionality. + * Url are considered as neutral. + * @type {RegExp} + * @private + */ + _BIDI_NEUTRAL_DIR_CHECK_RE *regexp.Regexp + + /** + * Regular expressions to check if the last strongly-directional character in a + * piece of text is LTR. + * @type {RegExp} + * @private + */ + _BIDI_LTR_EXIT_DIR_CHECK_RE *regexp.Regexp + + /** + * Regular expressions to check if the last strongly-directional character in a + * piece of text is RTL. + * @type {RegExp} + * @private + */ + _BIDI_RTL_EXIT_DIR_CHECK_RE *regexp.Regexp + + /** + * Regular expression used within $$changeNewlineToBr(). + * @type {RegExp} + * @private + */ + _CHANGE_NEWLINE_TO_BR_RE *regexp.Regexp + + _CHANGE_NEWLINE_TO_BR2_RE *regexp.Regexp + + /** + * Regular expression used for determining if a string needs to be encoded. + * @type {RegExp} + * @private + */ + _ENCODE_URI_RE *regexp.Regexp + + /** + * Character mappings used internally for soy.$$escapeJs + * @private + * @type {Object} + */ + _EscapeCharJs map[string]string ) func init() { - _BIDI_HTML_SKIP_RE, _ = regexp.Compile("<[^>]*>|&[^;]+;") - _BIDI_RTL_DIR_CHECK_RE, _ = regexp.Compile("^[^" + _BIDI_LTR_CHARS + "]*[" + _BIDI_RTL_CHARS + "]") - _BIDI_NEUTRAL_DIR_CHECK_RE, _ = regexp.Compile("^[" + _BIDI_NEUTRAL_CHARS + "]*$|^http://") - _BIDI_LTR_EXIT_DIR_CHECK_RE, _ = regexp.Compile("[" + _BIDI_LTR_CHARS + "][^" + _BIDI_RTL_CHARS + "]*$") - _BIDI_RTL_EXIT_DIR_CHECK_RE, _ = regexp.Compile("[" + _BIDI_RTL_CHARS + "][^" + _BIDI_LTR_CHARS + "]*$") - _CHANGE_NEWLINE_TO_BR_RE, _ = regexp.Compile("[\r\n]") - _CHANGE_NEWLINE_TO_BR2_RE, _ = regexp.Compile("(\r\n|\r|\n)") - _ENCODE_URI_RE, _ = regexp.Compile("^[a-zA-Z0-9\\-_.!~*'()]*$") - _EscapeCharJs = map[string]string{ - "\b": "\\b", - "\f": "\\f", - "\n": "\\n", - "\r": "\\r", - "\t": "\\t", - "\x0B": "\\x0B", // '\v' is not supported in JScript - "\"": "\\\"", - "'": "\\'", - "\\": "\\\\", - } + _BIDI_HTML_SKIP_RE, _ = regexp.Compile("<[^>]*>|&[^;]+;") + _BIDI_RTL_DIR_CHECK_RE, _ = regexp.Compile("^[^" + _BIDI_LTR_CHARS + "]*[" + _BIDI_RTL_CHARS + "]") + _BIDI_NEUTRAL_DIR_CHECK_RE, _ = regexp.Compile("^[" + _BIDI_NEUTRAL_CHARS + "]*$|^http://") + _BIDI_LTR_EXIT_DIR_CHECK_RE, _ = regexp.Compile("[" + _BIDI_LTR_CHARS + "][^" + _BIDI_RTL_CHARS + "]*$") + _BIDI_RTL_EXIT_DIR_CHECK_RE, _ = regexp.Compile("[" + _BIDI_RTL_CHARS + "][^" + _BIDI_LTR_CHARS + "]*$") + _CHANGE_NEWLINE_TO_BR_RE, _ = regexp.Compile("[\r\n]") + _CHANGE_NEWLINE_TO_BR2_RE, _ = regexp.Compile("(\r\n|\r|\n)") + _ENCODE_URI_RE, _ = regexp.Compile("^[a-zA-Z0-9\\-_.!~*'()]*$") + _EscapeCharJs = map[string]string{ + "\b": "\\b", + "\f": "\\f", + "\n": "\\n", + "\r": "\\r", + "\t": "\\t", + "\x0B": "\\x0B", // '\v' is not supported in JScript + "\"": "\\\"", + "'": "\\'", + "\\": "\\\\", + } } - diff --git a/go/src/closure/template/soyutil/data.go b/go/src/closure/template/soyutil/data.go index f760c6f..5a250ec 100644 --- a/go/src/closure/template/soyutil/data.go +++ b/go/src/closure/template/soyutil/data.go @@ -1,230 +1,228 @@ -package soyutil; +package soyutil import ( - "container/list" - "fmt" - "strconv" - "reflect" + "container/list" + "fmt" + "reflect" + "strconv" ) var NilDataInstance = &NilData{} type Equalser interface { - Equals(other interface{}) bool + Equals(other interface{}) bool } type Stringer interface { - String() string + String() string } type SoyDataException struct { - msg string + msg string } func NewSoyDataException(msg string) *SoyDataException { - return &SoyDataException{msg:msg} + return &SoyDataException{msg: msg} } func (p *SoyDataException) String() string { - return p.msg + return p.msg } func (p *SoyDataException) Error() string { - return p.msg + return p.msg } - type SoyData interface { - /** - * Converts this data object into a string (e.g. when used in a string context). - * @return The value of this data object if coerced into a string. - */ - String() string - - /** - * Converts this data object into a boolean (e.g. when used in a boolean context). In other words, - * this method tells whether this object is truthy. - * @return The value of this data object if coerced into a boolean. I.e. true if this object is - * truthy, false if this object is falsy. - */ - Bool() bool - - /** - * Precondition: Only call this method if you know that this SoyData object is a boolean. - * This method gets the boolean value of this boolean object. - * @return The boolean value of this boolean object. - * @throws SoyDataException If this object is not actually a boolean. - */ - BooleanValue() (bool) - - /** - * Precondition: Only call this method if you know that this SoyData object is an integer. - * This method gets the integer value of this integer object. - * @return The integer value of this integer object. - * @throws SoyDataException If this object is not actually an integer. - */ - IntegerValue() (int) - - /** - * Precondition: Only call this method if you know that this SoyData object is a float. - * This method gets the float value of this float object. - * @return The float value of this float object. - * @throws SoyDataException If this object is not actually a float. - */ - FloatValue() (float32) - - /** - * Precondition: Only call this method if you know that this SoyData object is a float64. - * This method gets the float value of this number object (converting integer to float if - * necessary). - * @return The float value of this number object. - * @throws SoyDataException If this object is not actually a number. - */ - Float64Value() (float64) - - /** - * Precondition: Only call this method if you know that this SoyData object is a number. - * This method gets the float value of this number object (converting integer to float if - * necessary). - * @return The float value of this number object. - * @throws SoyDataException If this object is not actually a number. - */ - NumberValue() (float64) - - /** - * Precondition: Only call this method if you know that this SoyData object is a string. - * This method gets the string value of this string object. - * @return The string value of this string object. - * @throws SoyDataException If this object is not actually a string. - */ - StringValue() (string) - - SoyData() SoyData - - /** - * Compares this data object against another for equality in the sense of the operator '==' for - * Soy expressions. - * - * @param other The other data object to compare against. - * @return True if the two objects are equal. - */ - Equals(other interface{}) bool + /** + * Converts this data object into a string (e.g. when used in a string context). + * @return The value of this data object if coerced into a string. + */ + String() string + + /** + * Converts this data object into a boolean (e.g. when used in a boolean context). In other words, + * this method tells whether this object is truthy. + * @return The value of this data object if coerced into a boolean. I.e. true if this object is + * truthy, false if this object is falsy. + */ + Bool() bool + + /** + * Precondition: Only call this method if you know that this SoyData object is a boolean. + * This method gets the boolean value of this boolean object. + * @return The boolean value of this boolean object. + * @throws SoyDataException If this object is not actually a boolean. + */ + BooleanValue() bool + + /** + * Precondition: Only call this method if you know that this SoyData object is an integer. + * This method gets the integer value of this integer object. + * @return The integer value of this integer object. + * @throws SoyDataException If this object is not actually an integer. + */ + IntegerValue() int + + /** + * Precondition: Only call this method if you know that this SoyData object is a float. + * This method gets the float value of this float object. + * @return The float value of this float object. + * @throws SoyDataException If this object is not actually a float. + */ + FloatValue() float32 + + /** + * Precondition: Only call this method if you know that this SoyData object is a float64. + * This method gets the float value of this number object (converting integer to float if + * necessary). + * @return The float value of this number object. + * @throws SoyDataException If this object is not actually a number. + */ + Float64Value() float64 + + /** + * Precondition: Only call this method if you know that this SoyData object is a number. + * This method gets the float value of this number object (converting integer to float if + * necessary). + * @return The float value of this number object. + * @throws SoyDataException If this object is not actually a number. + */ + NumberValue() float64 + + /** + * Precondition: Only call this method if you know that this SoyData object is a string. + * This method gets the string value of this string object. + * @return The string value of this string object. + * @throws SoyDataException If this object is not actually a string. + */ + StringValue() string + + SoyData() SoyData + + /** + * Compares this data object against another for equality in the sense of the operator '==' for + * Soy expressions. + * + * @param other The other data object to compare against. + * @return True if the two objects are equal. + */ + Equals(other interface{}) bool } /** * Default function implementations for SoyData types */ -type soyData struct {} +type soyData struct{} -func defaultBooleanValue() (bool) { - return false +func defaultBooleanValue() bool { + return false } -func defaultIntegerValue() (int) { - return 0 +func defaultIntegerValue() int { + return 0 } -func defaultFloatValue() (float32) { - return 0.0 +func defaultFloatValue() float32 { + return 0.0 } -func defaultFloat64Value() (float64) { - return 0.0 +func defaultFloat64Value() float64 { + return 0.0 } -func defaultNumberValue() (float64) { - return 0.0 +func defaultNumberValue() float64 { + return 0.0 } -func defaultStringValue() (string) { - return "" +func defaultStringValue() string { + return "" } +type NilData struct{} -type NilData struct {} - -func (p NilData) BooleanValue() (bool) { - return false +func (p NilData) BooleanValue() bool { + return false } -func (p NilData) IntegerValue() (int) { - return 0 +func (p NilData) IntegerValue() int { + return 0 } -func (p NilData) FloatValue() (float32) { - return 0.0 +func (p NilData) FloatValue() float32 { + return 0.0 } -func (p NilData) Float64Value() (float64) { - return 0.0 +func (p NilData) Float64Value() float64 { + return 0.0 } -func (p NilData) NumberValue() (float64) { - return 0.0 +func (p NilData) NumberValue() float64 { + return 0.0 } -func (p NilData) StringValue() (string) { - return "null" +func (p NilData) StringValue() string { + return "null" } func (p NilData) Value() interface{} { - return nil + return nil } func (p NilData) String() string { - return "null" + return "null" } func (p NilData) Bool() bool { - return false + return false } func (p NilData) Equals(other interface{}) bool { - return p == other || other == nil + return p == other || other == nil } func (p NilData) HashCode() int { - return 827 + return 827 } func (p NilData) SoyData() SoyData { - return p + return p } func (p NilData) At(index int) SoyData { - return p + return p } func (p NilData) Back() *list.Element { - return nil + return nil } func (p NilData) Front() *list.Element { - return nil + return nil } func (p NilData) HasElements() bool { - return false + return false } func (p NilData) Init() SoyListData { - return p + return p } func (p NilData) InsertAfter(value SoyData, mark *list.Element) *list.Element { - return nil + return nil } func (p NilData) InsertBefore(value SoyData, mark *list.Element) *list.Element { - return nil + return nil } func (p NilData) IsEmpty() bool { - return true + return true } func (p NilData) Len() int { - return 0 + return 0 } func (p NilData) MoveToBack(e *list.Element) { @@ -234,776 +232,770 @@ func (p NilData) MoveToFront(e *list.Element) { } func (p NilData) PushBack(value SoyData) *list.Element { - return nil + return nil } func (p NilData) PushBackList(ol SoyListData) { } func (p NilData) PushFront(value SoyData) *list.Element { - return nil + return nil } func (p NilData) PushFrontList(ol SoyListData) { } func (p NilData) Remove(e *list.Element) SoyData { - return p + return p } - type BooleanData bool func NewBooleanData(value bool) BooleanData { - return BooleanData(value) + return BooleanData(value) } func (p BooleanData) Value() bool { - return bool(p) + return bool(p) } -func (p BooleanData) BooleanValue() (bool) { - return bool(p) +func (p BooleanData) BooleanValue() bool { + return bool(p) } -func (p BooleanData) IntegerValue() (int) { - if p { - return 1 - } - return 0 +func (p BooleanData) IntegerValue() int { + if p { + return 1 + } + return 0 } -func (p BooleanData) FloatValue() (float32) { - if p { - return 1 - } - return 0 +func (p BooleanData) FloatValue() float32 { + if p { + return 1 + } + return 0 } -func (p BooleanData) Float64Value() (float64) { - if p { - return 1 - } - return 0 +func (p BooleanData) Float64Value() float64 { + if p { + return 1 + } + return 0 } -func (p BooleanData) NumberValue() (float64) { - if p { - return 1 - } - return 0 +func (p BooleanData) NumberValue() float64 { + if p { + return 1 + } + return 0 } -func (p BooleanData) StringValue() (string) { - return p.String() +func (p BooleanData) StringValue() string { + return p.String() } func (p BooleanData) String() string { - if p { - return "true" - } - return "false" + if p { + return "true" + } + return "false" } func (p BooleanData) Bool() bool { - return bool(p) + return bool(p) } func (p BooleanData) Equals(other interface{}) bool { - if other == nil { - return false - } - switch o := other.(type) { - case *NilData: - return false; - case bool: - return bool(p) == o - case SoyData: - return bool(p) == o.Bool() - } - return false + if other == nil { + return false + } + switch o := other.(type) { + case *NilData: + return false + case bool: + return bool(p) == o + case SoyData: + return bool(p) == o.Bool() + } + return false } func (p BooleanData) HashCode() int { - if p { - return 1 - } - return 0 + if p { + return 1 + } + return 0 } func (p BooleanData) SoyData() SoyData { - return p + return p } - type IntegerData int func NewIntegerData(value int) IntegerData { - return IntegerData(value) + return IntegerData(value) } func (p IntegerData) Value() int { - return int(p) + return int(p) } -func (p IntegerData) BooleanValue() (bool) { - return p.Value() != 0 +func (p IntegerData) BooleanValue() bool { + return p.Value() != 0 } -func (p IntegerData) IntegerValue() (int) { - return p.Value() +func (p IntegerData) IntegerValue() int { + return p.Value() } -func (p IntegerData) FloatValue() (float32) { - return float32(p.Value()) +func (p IntegerData) FloatValue() float32 { + return float32(p.Value()) } -func (p IntegerData) Float64Value() (float64) { - return float64(p.Value()) +func (p IntegerData) Float64Value() float64 { + return float64(p.Value()) } -func (p IntegerData) NumberValue() (float64) { - return float64(p.Value()) +func (p IntegerData) NumberValue() float64 { + return float64(p.Value()) } -func (p IntegerData) StringValue() (string) { - return string(p.Value()) +func (p IntegerData) StringValue() string { + return string(p.Value()) } func (p IntegerData) String() string { - return strconv.Itoa(p.Value()) + return strconv.Itoa(p.Value()) } func (p IntegerData) Bool() bool { - return p.Value() != 0 + return p.Value() != 0 } func (p IntegerData) Equals(other interface{}) bool { - if other == nil { - return false - } - switch o := other.(type) { - case *NilData: - return false; - case int: - return int(p) == o - case int32: - return int(p) == int(o) - case int64: - return int(p) == int(o) - case float32: - return float64(p) == float64(o) - case float64: - return float64(p) == o - case SoyData: - return int(p) == o.IntegerValue() - } - return false + if other == nil { + return false + } + switch o := other.(type) { + case *NilData: + return false + case int: + return int(p) == o + case int32: + return int(p) == int(o) + case int64: + return int(p) == int(o) + case float32: + return float64(p) == float64(o) + case float64: + return float64(p) == o + case SoyData: + return int(p) == o.IntegerValue() + } + return false } func (p IntegerData) HashCode() int { - return int(p) + return int(p) } func (p IntegerData) SoyData() SoyData { - return p + return p } - type Float64Data float64 func NewFloat64Data(value float64) Float64Data { - return Float64Data(value) + return Float64Data(value) } -func (p Float64Data) BooleanValue() (bool) { - return p != 0.0 +func (p Float64Data) BooleanValue() bool { + return p != 0.0 } -func (p Float64Data) IntegerValue() (int) { - return int(p) +func (p Float64Data) IntegerValue() int { + return int(p) } func (p Float64Data) Value() float64 { - return float64(p) + return float64(p) } -func (p Float64Data) FloatValue() (float32) { - return float32(p) +func (p Float64Data) FloatValue() float32 { + return float32(p) } -func (p Float64Data) Float64Value() (float64) { - return float64(p) +func (p Float64Data) Float64Value() float64 { + return float64(p) } -func (p Float64Data) NumberValue() (float64) { - return float64(p) +func (p Float64Data) NumberValue() float64 { + return float64(p) } func (p Float64Data) StringValue() string { - return strconv.FormatFloat(float64(p), 'g', -1, 64) + return strconv.FormatFloat(float64(p), 'g', -1, 64) } func (p Float64Data) String() string { - return strconv.FormatFloat(float64(p), 'g', -1, 64) + return strconv.FormatFloat(float64(p), 'g', -1, 64) } func (p Float64Data) Bool() bool { - return p != 0.0 + return p != 0.0 } func (p Float64Data) Equals(other interface{}) bool { - if other == nil { - return false - } - switch o := other.(type) { - case *NilData: - return false; - case int: - return float64(p) == float64(o) - case int32: - return float64(p) == float64(o) - case int64: - return float64(p) == float64(o) - case float32: - return float64(p) == float64(o) - case float64: - return float64(p) == o - case SoyData: - return float64(p) == o.Float64Value() - } - return false + if other == nil { + return false + } + switch o := other.(type) { + case *NilData: + return false + case int: + return float64(p) == float64(o) + case int32: + return float64(p) == float64(o) + case int64: + return float64(p) == float64(o) + case float32: + return float64(p) == float64(o) + case float64: + return float64(p) == o + case SoyData: + return float64(p) == o.Float64Value() + } + return false } func (p Float64Data) HashCode() int { - return int(p) + return int(p) } func (p Float64Data) SoyData() SoyData { - return p + return p } - type StringData string func NewStringData(value string) StringData { - return StringData(value) + return StringData(value) } func (p StringData) Value() string { - return string(p) + return string(p) } -func (p StringData) BooleanValue() (bool) { - return defaultBooleanValue() +func (p StringData) BooleanValue() bool { + return defaultBooleanValue() } -func (p StringData) IntegerValue() (int) { - return defaultIntegerValue() +func (p StringData) IntegerValue() int { + return defaultIntegerValue() } -func (p StringData) FloatValue() (float32) { - return defaultFloatValue() +func (p StringData) FloatValue() float32 { + return defaultFloatValue() } -func (p StringData) Float64Value() (float64) { - return defaultFloat64Value() +func (p StringData) Float64Value() float64 { + return defaultFloat64Value() } -func (p StringData) NumberValue() (float64) { - return defaultNumberValue() +func (p StringData) NumberValue() float64 { + return defaultNumberValue() } -func (p StringData) StringValue() (string) { - return string(p) +func (p StringData) StringValue() string { + return string(p) } func (p StringData) String() string { - return string(p) + return string(p) } func (p StringData) Bool() bool { - return len(p) > 0 + return len(p) > 0 } func (p StringData) Len() int { - return len(p) + return len(p) } func (p StringData) Equals(other interface{}) bool { - if other == nil { - return false - } - switch o := other.(type) { - case *NilData: - return false; - case string: - return string(p) == o - case SoyData: - return string(p) == o.StringValue() - } - return false + if other == nil { + return false + } + switch o := other.(type) { + case *NilData: + return false + case string: + return string(p) == o + case SoyData: + return string(p) == o.StringValue() + } + return false } func (p StringData) HashCode() int { - // todo create efficient string hashcode function - return 123 + // todo create efficient string hashcode function + return 123 } func (p StringData) SoyData() SoyData { - return p + return p } type SoyListData interface { - SoyData - At(index int) SoyData - Back() *list.Element - Front() *list.Element - HasElements() bool - Init() SoyListData - InsertAfter(value SoyData, mark *list.Element) *list.Element - InsertBefore(value SoyData, mark *list.Element) *list.Element - IsEmpty() bool - Len() int - MoveToBack(e *list.Element) - MoveToFront(e *list.Element) - PushBack(value SoyData) *list.Element - PushBackList(ol SoyListData) - PushFront(value SoyData) *list.Element - PushFrontList(ol SoyListData) - Remove(e *list.Element) SoyData + SoyData + At(index int) SoyData + Back() *list.Element + Front() *list.Element + HasElements() bool + Init() SoyListData + InsertAfter(value SoyData, mark *list.Element) *list.Element + InsertBefore(value SoyData, mark *list.Element) *list.Element + IsEmpty() bool + Len() int + MoveToBack(e *list.Element) + MoveToFront(e *list.Element) + PushBack(value SoyData) *list.Element + PushBackList(ol SoyListData) + PushFront(value SoyData) *list.Element + PushFrontList(ol SoyListData) + Remove(e *list.Element) SoyData } type soyListData struct { - l *list.List + l *list.List } func NewSoyListData() SoyListData { - return &soyListData{l:list.New()} + return &soyListData{l: list.New()} } -func NewSoyListDataFromArgs(args... interface{}) SoyListData { - l := list.New() - for _, v := range args { - s, _ := ToSoyData(v) - l.PushBack(s) - } - o := &soyListData{l:l} - return o +func NewSoyListDataFromArgs(args ...interface{}) SoyListData { + l := list.New() + for _, v := range args { + s, _ := ToSoyData(v) + l.PushBack(s) + } + o := &soyListData{l: l} + return o } func NewSoyListDataFromSoyListData(o SoyListData) SoyListData { - if o == nil { - return &soyListData{l:list.New()} - } - a := &soyListData{l:list.New()} - a.PushBackList(o) - return a + if o == nil { + return &soyListData{l: list.New()} + } + a := &soyListData{l: list.New()} + a.PushBackList(o) + return a } func NewSoyListDataFromList(o *list.List) SoyListData { - if o == nil { - return &soyListData{l:list.New()} - } - l := list.New() - l.PushBackList(o) - a := &soyListData{l:l} - return a + if o == nil { + return &soyListData{l: list.New()} + } + l := list.New() + l.PushBackList(o) + a := &soyListData{l: l} + return a } func NewSoyListDataFromVector(o []SoyData) SoyListData { - if o == nil { - return &soyListData{l:list.New()} - } - l := list.New() - for i := 0; i < len(o); i++ { - l.PushBack(o[i]) - } - a := &soyListData{l:l} - return a + if o == nil { + return &soyListData{l: list.New()} + } + l := list.New() + for i := 0; i < len(o); i++ { + l.PushBack(o[i]) + } + a := &soyListData{l: l} + return a } func (p *soyListData) Bool() bool { - return p.Len() > 0 + return p.Len() > 0 } func (p *soyListData) String() string { - return fmt.Sprintf("[%#v]", p.l) + return fmt.Sprintf("[%#v]", p.l) } -func (p *soyListData) BooleanValue() (bool) { - return defaultBooleanValue() +func (p *soyListData) BooleanValue() bool { + return defaultBooleanValue() } -func (p *soyListData) IntegerValue() (int) { - return defaultIntegerValue() +func (p *soyListData) IntegerValue() int { + return defaultIntegerValue() } -func (p *soyListData) FloatValue() (float32) { - return defaultFloatValue() +func (p *soyListData) FloatValue() float32 { + return defaultFloatValue() } -func (p *soyListData) Float64Value() (float64) { - return defaultFloat64Value() +func (p *soyListData) Float64Value() float64 { + return defaultFloat64Value() } -func (p *soyListData) NumberValue() (float64) { - return defaultNumberValue() +func (p *soyListData) NumberValue() float64 { + return defaultNumberValue() } -func (p *soyListData) StringValue() (string) { - return p.String() +func (p *soyListData) StringValue() string { + return p.String() } func (p *soyListData) Equals(other interface{}) bool { - if p == other { - return true - } - if other == nil { - return false - } - if o, ok := other.(SoyListData); ok { - if p.Len() != o.Len() { - return false - } - for oe, pe := o.Front(), p.Front(); oe != nil && pe != nil; oe, pe = oe.Next(), pe.Next() { - if oe.Value == pe.Value { - continue - } - if oe.Value != nil { - if e, ok := oe.Value.(Equalser); ok { - if e.Equals(pe.Value) { - continue - } - } - } - return false - } - return true - } - return false + if p == other { + return true + } + if other == nil { + return false + } + if o, ok := other.(SoyListData); ok { + if p.Len() != o.Len() { + return false + } + for oe, pe := o.Front(), p.Front(); oe != nil && pe != nil; oe, pe = oe.Next(), pe.Next() { + if oe.Value == pe.Value { + continue + } + if oe.Value != nil { + if e, ok := oe.Value.(Equalser); ok { + if e.Equals(pe.Value) { + continue + } + } + } + return false + } + return true + } + return false } func (p *soyListData) SoyData() SoyData { - return p + return p } func (p *soyListData) At(index int) SoyData { - e := p.l.Front() - for i := 0; i < index && e != nil; i++ { - e = e.Next() - } - if e == nil { - return NilDataInstance - } - return e.Value.(SoyData) + e := p.l.Front() + for i := 0; i < index && e != nil; i++ { + e = e.Next() + } + if e == nil { + return NilDataInstance + } + return e.Value.(SoyData) } func (p *soyListData) Back() *list.Element { - return p.l.Back() + return p.l.Back() } func (p *soyListData) Front() *list.Element { - return p.l.Front() + return p.l.Front() } func (p *soyListData) HasElements() bool { - return p.l.Len() > 0 + return p.l.Len() > 0 } func (p *soyListData) Init() SoyListData { - p.l.Init() - return p + p.l.Init() + return p } func (p *soyListData) InsertAfter(value SoyData, mark *list.Element) *list.Element { - return p.l.InsertAfter(value, mark) + return p.l.InsertAfter(value, mark) } func (p *soyListData) InsertBefore(value SoyData, mark *list.Element) *list.Element { - return p.l.InsertBefore(value, mark) + return p.l.InsertBefore(value, mark) } func (p *soyListData) IsEmpty() bool { - return p.l.Len() == 0 + return p.l.Len() == 0 } func (p *soyListData) Len() int { - return p.l.Len() + return p.l.Len() } func (p *soyListData) MoveToBack(e *list.Element) { - p.l.MoveToBack(e) + p.l.MoveToBack(e) } func (p *soyListData) MoveToFront(e *list.Element) { - p.l.MoveToFront(e) + p.l.MoveToFront(e) } func (p *soyListData) PushBack(value SoyData) *list.Element { - return p.l.PushBack(value) + return p.l.PushBack(value) } func (p *soyListData) PushBackList(ol SoyListData) { - if ol == nil { - return - } - if osld, ok := ol.(*soyListData); ok { - p.l.PushBackList(osld.l) - } else { - for e := ol.Front(); e != nil; e = e.Next() { - p.l.PushBack(e.Value) - } - } + if ol == nil { + return + } + if osld, ok := ol.(*soyListData); ok { + p.l.PushBackList(osld.l) + } else { + for e := ol.Front(); e != nil; e = e.Next() { + p.l.PushBack(e.Value) + } + } } func (p *soyListData) PushFront(value SoyData) *list.Element { - return p.l.PushFront(value) + return p.l.PushFront(value) } func (p *soyListData) PushFrontList(ol SoyListData) { - if ol == nil { - return - } - if osld, ok := ol.(*soyListData); ok { - p.l.PushFrontList(osld.l) - } else { - for e := ol.Back(); e != nil; e = e.Prev() { - p.l.PushFront(e.Value) - } - } + if ol == nil { + return + } + if osld, ok := ol.(*soyListData); ok { + p.l.PushFrontList(osld.l) + } else { + for e := ol.Back(); e != nil; e = e.Prev() { + p.l.PushFront(e.Value) + } + } } func (p *soyListData) Remove(e *list.Element) SoyData { - return p.l.Remove(e).(SoyData) + return p.l.Remove(e).(SoyData) } - type SoyMapData map[string]SoyData func NewSoyMapData() SoyMapData { - return make(SoyMapData) + return make(SoyMapData) } func NewSoyMapDataFromArgs(args ...interface{}) SoyMapData { - m := make(map[string]SoyData) - isKey := true - var key string - for _, arg := range args { - if isKey { - sdk, err := ToSoyData(arg) - if err != nil { - return nil - } - key = sdk.String() - } else { - value, err := ToSoyData(arg) - if err != nil { - return nil - } - m[key] = value - } - isKey = !isKey - } - return SoyMapData(m) + m := make(map[string]SoyData) + isKey := true + var key string + for _, arg := range args { + if isKey { + sdk, err := ToSoyData(arg) + if err != nil { + return nil + } + key = sdk.String() + } else { + value, err := ToSoyData(arg) + if err != nil { + return nil + } + m[key] = value + } + isKey = !isKey + } + return SoyMapData(m) } func NewSoyMapDataFromGenericMap(o map[string]interface{}) SoyMapData { - m := make(map[string]SoyData) - for key, v := range o { - value, err := ToSoyData(v) - if err != nil { - return nil - } - m[key] = value - } - return SoyMapData(m) + m := make(map[string]SoyData) + for key, v := range o { + value, err := ToSoyData(v) + if err != nil { + return nil + } + m[key] = value + } + return SoyMapData(m) } func NewSoyMapDataFromMap(o map[string]SoyData) SoyMapData { - return SoyMapData(o) + return SoyMapData(o) } -func (p SoyMapData) BooleanValue() (bool) { - return defaultBooleanValue() +func (p SoyMapData) BooleanValue() bool { + return defaultBooleanValue() } -func (p SoyMapData) IntegerValue() (int) { - return defaultIntegerValue() +func (p SoyMapData) IntegerValue() int { + return defaultIntegerValue() } -func (p SoyMapData) FloatValue() (float32) { - return defaultFloatValue() +func (p SoyMapData) FloatValue() float32 { + return defaultFloatValue() } -func (p SoyMapData) Float64Value() (float64) { - return defaultFloat64Value() +func (p SoyMapData) Float64Value() float64 { + return defaultFloat64Value() } -func (p SoyMapData) NumberValue() (float64) { - return defaultNumberValue() +func (p SoyMapData) NumberValue() float64 { + return defaultNumberValue() } -func (p SoyMapData) StringValue() (string) { - return defaultStringValue() +func (p SoyMapData) StringValue() string { + return defaultStringValue() } - func (p SoyMapData) Len() int { - return len(p) + return len(p) } func (p SoyMapData) Get(key string) SoyData { - value, ok := p[key] - if !ok { - return NilDataInstance - } - return value + value, ok := p[key] + if !ok { + return NilDataInstance + } + return value } func (p SoyMapData) Contains(key string) bool { - _, ok := p[key] - return ok + _, ok := p[key] + return ok } func (p SoyMapData) Keys() []string { - arr := make([]string, len(p)) - i := 0 - for k := range p { - arr[i] = k - i++ - } - return arr + arr := make([]string, len(p)) + i := 0 + for k := range p { + arr[i] = k + i++ + } + return arr } func (p SoyMapData) Set(key string, value SoyData) { - p[key] = value + p[key] = value } func (p SoyMapData) Bool() bool { - return len(p) > 0 + return len(p) > 0 } func (p SoyMapData) String() string { - return fmt.Sprintf("%#v", map[string]SoyData(p)) + return fmt.Sprintf("%#v", map[string]SoyData(p)) } func (p SoyMapData) Equals(other interface{}) bool { - if other == nil { - return false - } - if o, ok := other.(SoyMapData); ok && &p == &o { - return true - } - if o, ok := other.(SoyMapData); ok { - if len(p) != len(o) { - return false - } - // TODO check each element - return true - } - return false + if other == nil { + return false + } + if o, ok := other.(SoyMapData); ok && &p == &o { + return true + } + if o, ok := other.(SoyMapData); ok { + if len(p) != len(o) { + return false + } + // TODO check each element + return true + } + return false } func (p SoyMapData) SoyData() SoyData { - return p + return p } func (p SoyMapData) HasElements() bool { - return len(p) > 0 + return len(p) > 0 } func (p SoyMapData) IsEmpty() bool { - return len(p) == 0 + return len(p) == 0 } func ToBooleanData(obj interface{}) BooleanData { - if obj == nil || obj == NilDataInstance { - return NewBooleanData(false) - } - if o, ok := obj.(BooleanData); ok { - return o - } - s := ToSoyDataNoErr(obj) - if o, ok := s.(BooleanData); ok { - return o - } - return NewBooleanData(s.BooleanValue()) + if obj == nil || obj == NilDataInstance { + return NewBooleanData(false) + } + if o, ok := obj.(BooleanData); ok { + return o + } + s := ToSoyDataNoErr(obj) + if o, ok := s.(BooleanData); ok { + return o + } + return NewBooleanData(s.BooleanValue()) } func ToIntegerData(obj interface{}) IntegerData { - if obj == nil || obj == NilDataInstance { - return NewIntegerData(0) - } - if o, ok := obj.(IntegerData); ok { - return o - } - s := ToSoyDataNoErr(obj) - if o, ok := s.(IntegerData); ok { - return o - } - return NewIntegerData(s.IntegerValue()) + if obj == nil || obj == NilDataInstance { + return NewIntegerData(0) + } + if o, ok := obj.(IntegerData); ok { + return o + } + s := ToSoyDataNoErr(obj) + if o, ok := s.(IntegerData); ok { + return o + } + return NewIntegerData(s.IntegerValue()) } func ToFloat64Data(obj interface{}) Float64Data { - if obj == nil || obj == NilDataInstance { - return NewFloat64Data(0.0) - } - if o, ok := obj.(Float64Data); ok { - return o - } - s := ToSoyDataNoErr(obj) - if o, ok := s.(Float64Data); ok { - return o - } - return NewFloat64Data(s.Float64Value()) + if obj == nil || obj == NilDataInstance { + return NewFloat64Data(0.0) + } + if o, ok := obj.(Float64Data); ok { + return o + } + s := ToSoyDataNoErr(obj) + if o, ok := s.(Float64Data); ok { + return o + } + return NewFloat64Data(s.Float64Value()) } func ToStringData(obj interface{}) StringData { - if obj == nil || obj == NilDataInstance { - return NewStringData("") - } - if o, ok := obj.(StringData); ok { - return o - } - s := ToSoyDataNoErr(obj) - if o, ok := s.(StringData); ok { - return o - } - return NewStringData(s.StringValue()) + if obj == nil || obj == NilDataInstance { + return NewStringData("") + } + if o, ok := obj.(StringData); ok { + return o + } + s := ToSoyDataNoErr(obj) + if o, ok := s.(StringData); ok { + return o + } + return NewStringData(s.StringValue()) } func ToSoyListData(obj interface{}) SoyListData { - if obj == nil || obj == NilDataInstance { - return NewSoyListData() - } - if o, ok := obj.(SoyListData); ok { - return o - } - s := ToSoyDataNoErr(obj) - if o, ok := s.(SoyListData); ok { - return o - } - return NewSoyListData() + if obj == nil || obj == NilDataInstance { + return NewSoyListData() + } + if o, ok := obj.(SoyListData); ok { + return o + } + s := ToSoyDataNoErr(obj) + if o, ok := s.(SoyListData); ok { + return o + } + return NewSoyListData() } func ToSoyMapData(obj interface{}) SoyMapData { - if obj == nil || obj == NilDataInstance { - return NewSoyMapData() - } - if o, ok := obj.(SoyMapData); ok { - return o - } - s := ToSoyDataNoErr(obj) - if o, ok := s.(SoyMapData); ok { - return o - } - return NewSoyMapData() + if obj == nil || obj == NilDataInstance { + return NewSoyMapData() + } + if o, ok := obj.(SoyMapData); ok { + return o + } + s := ToSoyDataNoErr(obj) + if o, ok := s.(SoyMapData); ok { + return o + } + return NewSoyMapData() } func ToSoyDataNoErr(obj interface{}) SoyData { - s, _ := ToSoyData(obj) - return s + s, _ := ToSoyData(obj) + return s } /** @@ -1032,92 +1024,89 @@ func ToSoyDataNoErr(obj interface{}) SoyData { * @throws SoyDataException If the given object cannot be converted to SoyData. */ func ToSoyData(obj interface{}) (SoyData, error) { - if obj == nil { - return NilDataInstance, nil - } - if o, ok := obj.(SoyData); ok && o != nil { - return o, nil - } - switch o := obj.(type) { - case nil: - return NilDataInstance, nil - case SoyData: - return o, nil - case string: - return NewStringData(o), nil - case bool: - return NewBooleanData(o), nil - case uint: - return NewIntegerData(int(o)), nil - case int: - return NewIntegerData(o), nil - case int32: - return NewIntegerData(int(o)), nil - case int64: - return NewIntegerData(int(o)), nil - case float32: - return NewFloat64Data(float64(o)), nil - case float64: - return NewFloat64Data(o), nil - case *list.List: - return NewSoyListDataFromList(o), nil - case []SoyData: - return NewSoyListDataFromVector(o), nil - } - rv := reflect.ValueOf(obj) - switch rv.Kind() { - case reflect.Array, reflect.Slice: - l := NewSoyListData() - for i := 0; i < rv.Len(); i++ { - v := rv.Index(i) - var sv SoyData - if v.Interface() == nil { - sv = NilDataInstance - } else { - sv, _ = ToSoyData(v.Interface()) - } - l.PushBack(sv) - } - return l, nil - case reflect.Map: - m := NewSoyMapData() - if !rv.IsNil() { - for _, key := range rv.MapKeys() { - var k string - var sv SoyData - if key.Interface() == nil { - k = "null" - } else if st, ok := key.Interface().(Stringer); ok { - k = st.String() - } else if k, ok = key.Interface().(string); ok { - } else { - s, _ := ToSoyData(key.Interface()) - k = s.StringValue() - } - av := rv.MapIndex(key) - if av.Interface() == nil { - sv = NilDataInstance - } else { - sv, _ = ToSoyData(av.Interface()) - } - m.Set(k, sv) - } - } - return m, nil - case reflect.Struct: - m := NewSoyMapData() - rt := rv.Type() - for i := 0; i < rt.NumField(); i++ { - f := rt.Field(i) - k := f.Name - v, _ := ToSoyData(rv.Field(i).Interface()) - m.Set(k, v) - } - return m, nil - } - str := fmt.Sprintf("Attempting to convert unrecognized object to Soy data (object type %t).", obj) - return NilDataInstance, NewSoyDataException(str) + if obj == nil { + return NilDataInstance, nil + } + if o, ok := obj.(SoyData); ok && o != nil { + return o, nil + } + switch o := obj.(type) { + case nil: + return NilDataInstance, nil + case SoyData: + return o, nil + case string: + return NewStringData(o), nil + case bool: + return NewBooleanData(o), nil + case uint: + return NewIntegerData(int(o)), nil + case int: + return NewIntegerData(o), nil + case int32: + return NewIntegerData(int(o)), nil + case int64: + return NewIntegerData(int(o)), nil + case float32: + return NewFloat64Data(float64(o)), nil + case float64: + return NewFloat64Data(o), nil + case *list.List: + return NewSoyListDataFromList(o), nil + case []SoyData: + return NewSoyListDataFromVector(o), nil + } + rv := reflect.ValueOf(obj) + switch rv.Kind() { + case reflect.Array, reflect.Slice: + l := NewSoyListData() + for i := 0; i < rv.Len(); i++ { + v := rv.Index(i) + var sv SoyData + if v.Interface() == nil { + sv = NilDataInstance + } else { + sv, _ = ToSoyData(v.Interface()) + } + l.PushBack(sv) + } + return l, nil + case reflect.Map: + m := NewSoyMapData() + if !rv.IsNil() { + for _, key := range rv.MapKeys() { + var k string + var sv SoyData + if key.Interface() == nil { + k = "null" + } else if st, ok := key.Interface().(Stringer); ok { + k = st.String() + } else if k, ok = key.Interface().(string); ok { + } else { + s, _ := ToSoyData(key.Interface()) + k = s.StringValue() + } + av := rv.MapIndex(key) + if av.Interface() == nil { + sv = NilDataInstance + } else { + sv, _ = ToSoyData(av.Interface()) + } + m.Set(k, sv) + } + } + return m, nil + case reflect.Struct: + m := NewSoyMapData() + rt := rv.Type() + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + k := f.Name + v, _ := ToSoyData(rv.Field(i).Interface()) + m.Set(k, v) + } + return m, nil + } + str := fmt.Sprintf("Attempting to convert unrecognized object to Soy data (object type %t).", obj) + return NilDataInstance, NewSoyDataException(str) } - - - diff --git a/go/src/closure/template/soyutil/data_test.go b/go/src/closure/template/soyutil/data_test.go index 7a490ed..c08cf2f 100644 --- a/go/src/closure/template/soyutil/data_test.go +++ b/go/src/closure/template/soyutil/data_test.go @@ -1,106 +1,104 @@ -package soyutil_test; +package soyutil_test import ( - . "closure/template/soyutil" - "testing" + . "closure/template/soyutil" + "testing" ) func assertBoolEquals(t *testing.T, expected, actual bool, errormsg string) { - if expected != actual { - if len(errormsg) > 0 { - t.Errorf("%s\nExpected: %s but was: %d %v", errormsg, expected, actual, expected == actual) - } else { - t.Errorf("Expected: %s but was: %d %v", expected, actual, expected == actual) - } - } + if expected != actual { + if len(errormsg) > 0 { + t.Errorf("%s\nExpected: %s but was: %d %v", errormsg, expected, actual, expected == actual) + } else { + t.Errorf("Expected: %s but was: %d %v", expected, actual, expected == actual) + } + } } func assertIntEquals(t *testing.T, expected, actual int, errormsg string) { - if expected != actual { - if len(errormsg) > 0 { - t.Errorf("%s\nExpected: %d but was: %d %v", errormsg, expected, actual, expected == actual) - } else { - t.Errorf("Expected: %d but was: %d %v", expected, actual, expected == actual) - } - } + if expected != actual { + if len(errormsg) > 0 { + t.Errorf("%s\nExpected: %d but was: %d %v", errormsg, expected, actual, expected == actual) + } else { + t.Errorf("Expected: %d but was: %d %v", expected, actual, expected == actual) + } + } } func assertFloat64Equals(t *testing.T, expected, actual float64, errormsg string) { - if expected != actual { - if len(errormsg) > 0 { - t.Errorf("%s\nExpected: %g but was: %g %v", errormsg, expected, actual, expected == actual) - } else { - t.Errorf("Expected: %g but was: %g %v", expected, actual, expected == actual) - } - } + if expected != actual { + if len(errormsg) > 0 { + t.Errorf("%s\nExpected: %g but was: %g %v", errormsg, expected, actual, expected == actual) + } else { + t.Errorf("Expected: %g but was: %g %v", expected, actual, expected == actual) + } + } } func assertStringEquals(t *testing.T, expected, actual, errormsg string) { - if expected != actual { - if len(errormsg) > 0 { - t.Errorf("%s\nExpected: \"%s\"\n but was: \"%s\", %d %d %s", errormsg, expected, actual, len(expected), len(actual), expected == actual) - } else { - t.Errorf("Expected: \"%s\"\n but was: \"%s\" %d %d %s", expected, actual, len(expected), len(actual), expected == actual) - } - } + if expected != actual { + if len(errormsg) > 0 { + t.Errorf("%s\nExpected: \"%s\"\n but was: \"%s\", %d %d %s", errormsg, expected, actual, len(expected), len(actual), expected == actual) + } else { + t.Errorf("Expected: \"%s\"\n but was: \"%s\" %d %d %s", expected, actual, len(expected), len(actual), expected == actual) + } + } } func assertSoyDataEquals(t *testing.T, expected, actual SoyData, errormsg string) { - if expected != actual { - if len(errormsg) > 0 { - t.Errorf("%s\nExpected: %v\n but was: %v, %s", errormsg, expected, actual, expected.Equals(actual)) - } else { - t.Errorf("Expected: %v\n but was: %v, %s", expected, actual, expected.Equals(actual)) - } - } + if expected != actual { + if len(errormsg) > 0 { + t.Errorf("%s\nExpected: %v\n but was: %v, %s", errormsg, expected, actual, expected.Equals(actual)) + } else { + t.Errorf("Expected: %v\n but was: %v, %s", expected, actual, expected.Equals(actual)) + } + } } func TestStringDataBool(t *testing.T) { - assertBoolEquals(t, false, NewStringData("").Bool(), "Empty String") - assertBoolEquals(t, true, NewStringData(" ").Bool(), "Whitespace String") - assertBoolEquals(t, true, NewStringData("blah blah").Bool(), "String with value") + assertBoolEquals(t, false, NewStringData("").Bool(), "Empty String") + assertBoolEquals(t, true, NewStringData(" ").Bool(), "Whitespace String") + assertBoolEquals(t, true, NewStringData("blah blah").Bool(), "String with value") } func TestNewSoyMapDataFromArgs(t *testing.T) { - s := NewSoyMapDataFromArgs("name", "Albert Einstein", "occupation", NewStringData("Patent Clerk"), "birth_year", 1879) - assertIntEquals(t, 3, s.Len(), "Size of SoyMapData incorrect") - assertStringEquals(t, "Albert Einstein", s["name"].String(), "Invalid value for name in SoyMapData") - assertStringEquals(t, "Patent Clerk", s["occupation"].String(), "Invalid value for occupation in SoyMapData") - assertStringEquals(t, "1879", s["birth_year"].String(), "Invalid value for birth_year in SoyMapData") - assertIntEquals(t, 1879, s["birth_year"].IntegerValue(), "Invalid value for birth_year in SoyMapData") + s := NewSoyMapDataFromArgs("name", "Albert Einstein", "occupation", NewStringData("Patent Clerk"), "birth_year", 1879) + assertIntEquals(t, 3, s.Len(), "Size of SoyMapData incorrect") + assertStringEquals(t, "Albert Einstein", s["name"].String(), "Invalid value for name in SoyMapData") + assertStringEquals(t, "Patent Clerk", s["occupation"].String(), "Invalid value for occupation in SoyMapData") + assertStringEquals(t, "1879", s["birth_year"].String(), "Invalid value for birth_year in SoyMapData") + assertIntEquals(t, 1879, s["birth_year"].IntegerValue(), "Invalid value for birth_year in SoyMapData") } func TestToSoyData(t *testing.T) { - m := map[string]SoyData{"name" : NewStringData("John Doe"), "count": NewIntegerData(15)} - m2, _ := ToSoyData(m) - sm, _ := m2.(SoyMapData) - assertIntEquals(t, 2, sm.Len(), "Invalid length for map") - assertSoyDataEquals(t, NewStringData("John Doe"), sm["name"], "Invalid value in map") - assertSoyDataEquals(t, NewIntegerData(15), sm["count"], "Invalid value in map") - - l := []interface{} {"name", NewStringData("John Doe"), "count", NewIntegerData(15)} - l2, _ := ToSoyData(l) - t.Logf("list: %#v\nas soy: %#v\n", l, l2) - sl, _ := l2.(SoyListData) - assertIntEquals(t, 4, sl.Len(), "Invalid length for list") - e := sl.Front() - t.Logf("Elem 0: %#v\n", e.Value) - assertSoyDataEquals(t, NewStringData("name"), e.Value.(SoyData), "Invalid value in list") - e = e.Next() - t.Logf("Elem 1: %#v\n", e.Value) - assertSoyDataEquals(t, NewStringData("John Doe"), e.Value.(SoyData), "Invalid value in list") - e = e.Next() - t.Logf("Elem 2: %#v\n", e.Value) - assertSoyDataEquals(t, NewStringData("count"), e.Value.(SoyData), "Invalid value in list") - e = e.Next() - t.Logf("Elem 3: %#v\n", e.Value) - assertSoyDataEquals(t, NewIntegerData(15), e.Value.(SoyData), "Invalid value in list") - - - assertSoyDataEquals(t, NewStringData("name"), sl.At(0), "Invalid value in list") - assertSoyDataEquals(t, NewStringData("John Doe"), sl.At(1), "Invalid value in list") - assertSoyDataEquals(t, NewStringData("count"), sl.At(2), "Invalid value in list") - assertSoyDataEquals(t, NewIntegerData(15), sl.At(3), "Invalid value in list") - -} + m := map[string]SoyData{"name": NewStringData("John Doe"), "count": NewIntegerData(15)} + m2, _ := ToSoyData(m) + sm, _ := m2.(SoyMapData) + assertIntEquals(t, 2, sm.Len(), "Invalid length for map") + assertSoyDataEquals(t, NewStringData("John Doe"), sm["name"], "Invalid value in map") + assertSoyDataEquals(t, NewIntegerData(15), sm["count"], "Invalid value in map") + + l := []interface{}{"name", NewStringData("John Doe"), "count", NewIntegerData(15)} + l2, _ := ToSoyData(l) + t.Logf("list: %#v\nas soy: %#v\n", l, l2) + sl, _ := l2.(SoyListData) + assertIntEquals(t, 4, sl.Len(), "Invalid length for list") + e := sl.Front() + t.Logf("Elem 0: %#v\n", e.Value) + assertSoyDataEquals(t, NewStringData("name"), e.Value.(SoyData), "Invalid value in list") + e = e.Next() + t.Logf("Elem 1: %#v\n", e.Value) + assertSoyDataEquals(t, NewStringData("John Doe"), e.Value.(SoyData), "Invalid value in list") + e = e.Next() + t.Logf("Elem 2: %#v\n", e.Value) + assertSoyDataEquals(t, NewStringData("count"), e.Value.(SoyData), "Invalid value in list") + e = e.Next() + t.Logf("Elem 3: %#v\n", e.Value) + assertSoyDataEquals(t, NewIntegerData(15), e.Value.(SoyData), "Invalid value in list") + assertSoyDataEquals(t, NewStringData("name"), sl.At(0), "Invalid value in list") + assertSoyDataEquals(t, NewStringData("John Doe"), sl.At(1), "Invalid value in list") + assertSoyDataEquals(t, NewStringData("count"), sl.At(2), "Invalid value in list") + assertSoyDataEquals(t, NewIntegerData(15), sl.At(3), "Invalid value in list") + +} diff --git a/go/src/closure/template/soyutil/escaping_conventions.go b/go/src/closure/template/soyutil/escaping_conventions.go index 388e829..b242d7b 100644 --- a/go/src/closure/template/soyutil/escaping_conventions.go +++ b/go/src/closure/template/soyutil/escaping_conventions.go @@ -1,125 +1,120 @@ -package soyutil; +package soyutil // This is a direct port of the Java class // com.google.template.soy.shared.restricted.EscapingConventions import ( - "bytes" - "regexp" - "fmt" - "io" - "sort" - "strconv" - "strings" + "bytes" + "fmt" + "io" + "regexp" + "sort" + "strconv" + "strings" ) const ( - /** - * A string, used as the result of a filter when the filter pattern does not match the input, that - * is not a substring of any keyword or well-known identifier in HTML, JS, or CSS and that is a - * valid identifier part in all those languages, and which cannot terminate a string, comment, or - * other bracketed section. - *

- * This string is also longer than necessary so that developers can use grep when it starts - * showing up in their output. - *

- * If grep directed you here, then one of your Soy templates is using a filter directive that - * is receiving a potentially unsafe input. Run your app in debug mode and you should get the - * name of the directive and the input deemed unsafe. - */ - INNOCUOUS_OUTPUT = "zSoyz" + /** + * A string, used as the result of a filter when the filter pattern does not match the input, that + * is not a substring of any keyword or well-known identifier in HTML, JS, or CSS and that is a + * valid identifier part in all those languages, and which cannot terminate a string, comment, or + * other bracketed section. + *

+ * This string is also longer than necessary so that developers can use grep when it starts + * showing up in their output. + *

+ * If grep directed you here, then one of your Soy templates is using a filter directive that + * is receiving a potentially unsafe input. Run your app in debug mode and you should get the + * name of the directive and the input deemed unsafe. + */ + INNOCUOUS_OUTPUT = "zSoyz" ) - var ( - HEX_DIGITS = []byte{ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - } - - CSS_WORD = regexp.MustCompile( - // See http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet - // #RULE_.234_-_CSS_Escape_Before_Inserting_Untrusted_Data_into_HTML_Style_Property_Values - // for an explanation of why expression and moz-binding are bad. - "^(?!-*(?:(expression|(?:moz-)?binding))(?:" + - // A latin class name or ID, CSS identifier, hex color or unicode range. - "[.#]?-?(?:[_a-zA-Z0-9-]+)(?:-[_a-zA-Z0-9-]+)*-?|" + - // A quantity - "-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[a-zA-Z]{1,2}|%)?|" + - // The special value !important. - "!important|" + - // Nothing. - "" + - ")\\z/i", - ) - - /** - * Loose matcher for HTML tags, DOCTYPEs, and HTML comments. - * This will reliably find HTML tags (though not CDATA tags and not XML tags whose name or - * namespace starts with a non-latin character), and will do a good job with DOCTYPES (though - * will have trouble with complex doctypes that define their own entities) and does a decent job - * with simple HTML comments. - *

- * This should be food enough since HTML sanitizers do not typically output comments, or CDATA, - * or RCDATA content. - */ - HTML_TAG_CONTENT = regexp.MustCompile( - // Matches a left angle bracket followed by either - // (1) a "!" which indicates a doctype or comment, or - // (2) an optional solidus (/, indicating an end tag) and an HTML tag name. - // followed by any number of quoted strings (found in tags and doctypes) or other content - // terminated by a right angle bracket. - "<(?:!|/?[a-zA-Z])(?:[^>'\"]|\"[^\"]*\"|'[^']*')*>", - ) - - _BYTE_ARRAY_PERCENT = []byte{'%'} - - - _FILTER_NORMALIZE_URI_RE = regexp.MustCompile( - "^(?:(?:https?|mailto):|[^&:\\/?#]*(?:[\\/?#]|\\z))/i", - ) - - _FILTER_HTML_ATTRIBUTE_RE = regexp.MustCompile( - "^" + - // Disallow special attribute names - "(?!style|on|action|archive|background|cite|classid|codebase|data|dsync|href" + - "|longdesc|src|usemap)" + - "(?:" + - // Must match letters - "[a-z0-9_$:-]*" + - // Match until the end. - ")\\z/i", - ) - - _FILTER_HTML_ELEMENT_NAME_RE = regexp.MustCompile( - "^" + - // Disallow special element names. - "(?!script|style|title|textarea|xmp|no)" + - "[a-z0-9_$:-]*\\z/i", - ) + HEX_DIGITS = []byte{ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + } + + CSS_WORD = regexp.MustCompile( + // See http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet + // #RULE_.234_-_CSS_Escape_Before_Inserting_Untrusted_Data_into_HTML_Style_Property_Values + // for an explanation of why expression and moz-binding are bad. + "^(?!-*(?:(expression|(?:moz-)?binding))(?:" + + // A latin class name or ID, CSS identifier, hex color or unicode range. + "[.#]?-?(?:[_a-zA-Z0-9-]+)(?:-[_a-zA-Z0-9-]+)*-?|" + + // A quantity + "-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[a-zA-Z]{1,2}|%)?|" + + // The special value !important. + "!important|" + + // Nothing. + "" + + ")\\z/i", + ) + + /** + * Loose matcher for HTML tags, DOCTYPEs, and HTML comments. + * This will reliably find HTML tags (though not CDATA tags and not XML tags whose name or + * namespace starts with a non-latin character), and will do a good job with DOCTYPES (though + * will have trouble with complex doctypes that define their own entities) and does a decent job + * with simple HTML comments. + *

+ * This should be food enough since HTML sanitizers do not typically output comments, or CDATA, + * or RCDATA content. + */ + HTML_TAG_CONTENT = regexp.MustCompile( + // Matches a left angle bracket followed by either + // (1) a "!" which indicates a doctype or comment, or + // (2) an optional solidus (/, indicating an end tag) and an HTML tag name. + // followed by any number of quoted strings (found in tags and doctypes) or other content + // terminated by a right angle bracket. + "<(?:!|/?[a-zA-Z])(?:[^>'\"]|\"[^\"]*\"|'[^']*')*>", + ) + + _BYTE_ARRAY_PERCENT = []byte{'%'} + + _FILTER_NORMALIZE_URI_RE = regexp.MustCompile( + "^(?:(?:https?|mailto):|[^&:\\/?#]*(?:[\\/?#]|\\z))/i", + ) + + _FILTER_HTML_ATTRIBUTE_RE = regexp.MustCompile( + "^" + + // Disallow special attribute names + "(?!style|on|action|archive|background|cite|classid|codebase|data|dsync|href" + + "|longdesc|src|usemap)" + + "(?:" + + // Must match letters + "[a-z0-9_$:-]*" + + // Match until the end. + ")\\z/i", + ) + + _FILTER_HTML_ELEMENT_NAME_RE = regexp.MustCompile( + "^" + + // Disallow special element names. + "(?!script|style|title|textarea|xmp|no)" + + "[a-z0-9_$:-]*\\z/i", + ) ) - - var ( - /** Implements the {@code |escapeHtml} directive. */ - EscapeHtmlInstance = newEscapeHtmlEscaper() - NormalizeHtmlInstance = newNormalizeHtmlEscaper() - EscapeHtmlNospaceInstance = newEscapeHtmlNospaceEscaper() - NormalizeHtmlNospaceInstance = newNormalizeHtmlNospaceEscaper() - EscapeJsStringInstance = newEscapeJsStringEscaper() - EscapeJsRegexInstance = newEscapeJsRegexEscaper() - EscapeCssStringInstance = newEscapeCssStringEscaper() - FilterCssValueInstance = newFilterCssValueEscaper() - NormalizeUriInstance = newNormalizeUriEscaper() - FilterNormalizeUriInstance = newFilterNormalizeUriEscaper() - EscapeUriInstance = newEscapeUriEscaper() - FilterHtmlAttributeInstance = newFilterHtmlAttributeEscaper() - FilterHtmlElementNameInstance = newFilterHtmlElementNameEscaper() + /** Implements the {@code |escapeHtml} directive. */ + EscapeHtmlInstance = newEscapeHtmlEscaper() + NormalizeHtmlInstance = newNormalizeHtmlEscaper() + EscapeHtmlNospaceInstance = newEscapeHtmlNospaceEscaper() + NormalizeHtmlNospaceInstance = newNormalizeHtmlNospaceEscaper() + EscapeJsStringInstance = newEscapeJsStringEscaper() + EscapeJsRegexInstance = newEscapeJsRegexEscaper() + EscapeCssStringInstance = newEscapeCssStringEscaper() + FilterCssValueInstance = newFilterCssValueEscaper() + NormalizeUriInstance = newNormalizeUriEscaper() + FilterNormalizeUriInstance = newFilterNormalizeUriEscaper() + EscapeUriInstance = newEscapeUriEscaper() + FilterHtmlAttributeInstance = newFilterHtmlAttributeEscaper() + FilterHtmlElementNameInstance = newFilterHtmlElementNameEscaper() ) - type stringer interface { - String() string + String() string } /** @@ -128,103 +123,97 @@ type stringer interface { * treat supplementary codepoints as special. */ type Escape interface { - /** - * A character in the input language. - */ - PlainText() rune - /** - * A string in the output language that corresponds to {@link #getPlainText} - * in the input language. - */ - Escaped() string - CompareTo(b Escape) int -} - - + /** + * A character in the input language. + */ + PlainText() rune + /** + * A string in the output language that corresponds to {@link #getPlainText} + * in the input language. + */ + Escaped() string + CompareTo(b Escape) int +} + type escape struct { - plainText rune - escaped string + plainText rune + escaped string } func NewEscape(plainText rune, escaped string) Escape { - return &escape{ - plainText: plainText, - escaped: escaped, - } + return &escape{ + plainText: plainText, + escaped: escaped, + } } func (p *escape) PlainText() rune { - return p.plainText + return p.plainText } func (p *escape) Escaped() string { - return p.escaped + return p.escaped } func (p *escape) CompareTo(b Escape) int { - if p == b { - return 0 - } - if b == nil { - return 1 - } - if p.plainText == b.PlainText() { - return 0 - } - if p.plainText > b.PlainText() { - return 1 - } - return -1 + if p == b { + return 0 + } + if b == nil { + return 1 + } + if p.plainText == b.PlainText() { + return 0 + } + if p.plainText > b.PlainText() { + return 1 + } + return -1 } - type appendableEscapedWriter struct { - clsx *crossLanguageStringXform - w io.Writer + clsx *crossLanguageStringXform + w io.Writer } func newAppendableEscapedWriter(clsx *crossLanguageStringXform, w io.Writer) io.Writer { - return &appendableEscapedWriter { - clsx: clsx, - w: w, - } + return &appendableEscapedWriter{ + clsx: clsx, + w: w, + } } - func (p *appendableEscapedWriter) WriteString(s string) (int, error) { - _, err := p.clsx.maybeEscapeOnto(s, p.w) - return len(s), err + _, err := p.clsx.maybeEscapeOnto(s, p.w) + return len(s), err } - func (p *appendableEscapedWriter) Write(b []byte) (int, error) { - _, err := p.clsx.maybeEscapeOnto(string(b), p.w) - return len(b), err + _, err := p.clsx.maybeEscapeOnto(string(b), p.w) + return len(b), err } -func (p *appendableEscapedWriter) Close() (error) { - if cls, ok := p.w.(io.WriteCloser); ok { - return cls.Close() - } - return nil +func (p *appendableEscapedWriter) Close() error { + if cls, ok := p.w.(io.WriteCloser); ok { + return cls.Close() + } + return nil } - type defineEscapers interface { - DefineEscapes() []Escape + DefineEscapes() []Escape } type CrossLanguageStringXform interface { - DirectiveName() string - ValueFilter() *regexp.Regexp - NonAsciiPrefix() string - Escapes() []Escape - Escape(s string) (string, error) - EscapedWriter(w io.Writer) (io.Writer) - DefineEscapes() []Escape + DirectiveName() string + ValueFilter() *regexp.Regexp + NonAsciiPrefix() string + Escapes() []Escape + Escape(s string) (string, error) + EscapedWriter(w io.Writer) io.Writer + DefineEscapes() []Escape } - /** * A transformation on strings that preserves some correctness or safety properties. * Subclasses come in three varieties: @@ -248,26 +237,25 @@ type CrossLanguageStringXform interface { * */ type crossLanguageStringXform struct { - directiveName string - valueFilter *regexp.Regexp - jsNames []string - escapes []Escape - - /** - * A dense mapping mirroring escapes. - * I.e. for each element of {@link #escapes} {@code e} such that {@code e.plainText < 0x80}, - * {@code escapesByCodeUnit[e.plainText] == e.escaped}. - */ - escapesByCodeUnit []string - /** Keys in a sparse mapping for the non ASCII {@link #escapes}. */ - nonAsciiCodeUnits []int - /** Values in a sparse mapping corresponding to {@link #nonAsciiCodeUnits}. */ - nonAsciiEscapes []string - /** @see #getNonAsciiPrefix */ - nonAsciiPrefix string + directiveName string + valueFilter *regexp.Regexp + jsNames []string + escapes []Escape + + /** + * A dense mapping mirroring escapes. + * I.e. for each element of {@link #escapes} {@code e} such that {@code e.plainText < 0x80}, + * {@code escapesByCodeUnit[e.plainText] == e.escaped}. + */ + escapesByCodeUnit []string + /** Keys in a sparse mapping for the non ASCII {@link #escapes}. */ + nonAsciiCodeUnits []int + /** Values in a sparse mapping corresponding to {@link #nonAsciiCodeUnits}. */ + nonAsciiEscapes []string + /** @see #getNonAsciiPrefix */ + nonAsciiPrefix string } - /** * @param valueFilter {@code null} if the directive accepts all strings as inputs. Otherwise * a regular expression that accepts only strings that can be escaped by this directive. @@ -278,64 +266,64 @@ type crossLanguageStringXform struct { * If null, then non-ASCII code units outside the sparse map can appear unescaped. */ func initCrossLanguageStringXform(clsx *crossLanguageStringXform, simpleName string, valueFilter *regexp.Regexp, jsNames []string, nonAsciiPrefix string, escapeDefiner defineEscapers) { - // EscapeHtml -> |escapeHtml - clsx.directiveName = "|" + strings.ToLower(simpleName[0:1]) + simpleName[1:] - clsx.valueFilter = valueFilter - if jsNames == nil { - jsNames = make([]string, 0) - } - escapes := escapeDefiner.DefineEscapes() - if escapes == nil { - escapes = make([]Escape, 0) - } - newJsNames := make([]string, len(jsNames)) - copy(newJsNames, jsNames) - clsx.jsNames = newJsNames - clsx.escapes = escapes - numEscapes := len(escapes) - numAsciiEscapes := numEscapes - // Now create the maps used by the escape methods. The below depends on defineEscapes() - // returning sorted escapes. EscapeListBuilder.build() sorts its escapes. - for numAsciiEscapes > 0 && escapes[numAsciiEscapes - 1].PlainText() >= 0x80 { - numAsciiEscapes-- - } - // Create the dense ASCII map. - if numAsciiEscapes != 0 { - escapesByCodeUnit := make([]string, escapes[numAsciiEscapes-1].PlainText() + 1) - for _, escape := range escapes[0:numAsciiEscapes] { - escapesByCodeUnit[int(escape.PlainText())] = escape.Escaped() - } - clsx.escapesByCodeUnit = escapesByCodeUnit - } else { - clsx.escapesByCodeUnit = make([]string, 0) - } - // Create the sparse non-ASCII map. - if numEscapes != numAsciiEscapes { - numNonAsciiEscapes := numEscapes - numAsciiEscapes - nonAsciiCodeUnits := make([]int, numNonAsciiEscapes) - nonAsciiEscapes := make([]string, numNonAsciiEscapes) - for i := 0; i < numNonAsciiEscapes; i++ { - esc := escapes[numAsciiEscapes + i] - nonAsciiCodeUnits[i] = int(esc.PlainText()) - nonAsciiEscapes[i] = esc.Escaped() - } - clsx.nonAsciiCodeUnits = nonAsciiCodeUnits - clsx.nonAsciiEscapes = nonAsciiEscapes - } else { - clsx.nonAsciiCodeUnits = make([]int, 0) - clsx.nonAsciiEscapes = make([]string, 0) - } - - // The fallback mode if neither the ASCII nor non-ASCII escaping maps contain a mapping. - clsx.nonAsciiPrefix = nonAsciiPrefix + // EscapeHtml -> |escapeHtml + clsx.directiveName = "|" + strings.ToLower(simpleName[0:1]) + simpleName[1:] + clsx.valueFilter = valueFilter + if jsNames == nil { + jsNames = make([]string, 0) + } + escapes := escapeDefiner.DefineEscapes() + if escapes == nil { + escapes = make([]Escape, 0) + } + newJsNames := make([]string, len(jsNames)) + copy(newJsNames, jsNames) + clsx.jsNames = newJsNames + clsx.escapes = escapes + numEscapes := len(escapes) + numAsciiEscapes := numEscapes + // Now create the maps used by the escape methods. The below depends on defineEscapes() + // returning sorted escapes. EscapeListBuilder.build() sorts its escapes. + for numAsciiEscapes > 0 && escapes[numAsciiEscapes-1].PlainText() >= 0x80 { + numAsciiEscapes-- + } + // Create the dense ASCII map. + if numAsciiEscapes != 0 { + escapesByCodeUnit := make([]string, escapes[numAsciiEscapes-1].PlainText()+1) + for _, escape := range escapes[0:numAsciiEscapes] { + escapesByCodeUnit[int(escape.PlainText())] = escape.Escaped() + } + clsx.escapesByCodeUnit = escapesByCodeUnit + } else { + clsx.escapesByCodeUnit = make([]string, 0) + } + // Create the sparse non-ASCII map. + if numEscapes != numAsciiEscapes { + numNonAsciiEscapes := numEscapes - numAsciiEscapes + nonAsciiCodeUnits := make([]int, numNonAsciiEscapes) + nonAsciiEscapes := make([]string, numNonAsciiEscapes) + for i := 0; i < numNonAsciiEscapes; i++ { + esc := escapes[numAsciiEscapes+i] + nonAsciiCodeUnits[i] = int(esc.PlainText()) + nonAsciiEscapes[i] = esc.Escaped() + } + clsx.nonAsciiCodeUnits = nonAsciiCodeUnits + clsx.nonAsciiEscapes = nonAsciiEscapes + } else { + clsx.nonAsciiCodeUnits = make([]int, 0) + clsx.nonAsciiEscapes = make([]string, 0) + } + + // The fallback mode if neither the ASCII nor non-ASCII escaping maps contain a mapping. + clsx.nonAsciiPrefix = nonAsciiPrefix } /** * The name of the directive associated with this escaping function. * @return E.g. {@code |escapeHtml} */ -func (p* crossLanguageStringXform) DirectiveName() string { - return p.directiveName +func (p *crossLanguageStringXform) DirectiveName() string { + return p.directiveName } /** @@ -343,16 +331,16 @@ func (p* crossLanguageStringXform) DirectiveName() string { * units not in the sparse mapping. * If null, then non-ASCII code units outside the sparse map can appear unescaped. */ -func (p* crossLanguageStringXform) NonAsciiPrefix() string { - return p.nonAsciiPrefix +func (p *crossLanguageStringXform) NonAsciiPrefix() string { + return p.nonAsciiPrefix } /** * Null if the escaper accepts all strings as inputs, or otherwise a regular expression * that accepts only strings that can be escaped by this escaper. */ -func (p* crossLanguageStringXform) ValueFilter() *regexp.Regexp { - return p.valueFilter +func (p *crossLanguageStringXform) ValueFilter() *regexp.Regexp { + return p.valueFilter } /** @@ -360,36 +348,33 @@ func (p* crossLanguageStringXform) ValueFilter() *regexp.Regexp { * the escaping convention. * @return {@code null} if there is no such function. */ -func (p* crossLanguageStringXform) JsFunctionNames() []string { - return p.jsNames +func (p *crossLanguageStringXform) JsFunctionNames() []string { + return p.jsNames } /** * The escapes need to translate the input language to the output language. */ -func (p* crossLanguageStringXform) Escapes() []Escape { - return p.escapes +func (p *crossLanguageStringXform) Escapes() []Escape { + return p.escapes } - // Methods that satisfy the Escaper interface. -func (p* crossLanguageStringXform) Escape(s string) (string, error) { - // We pass null so that we don't unnecessarily allocate (and zero) or copy char arrays. - buf, err := p.maybeEscapeOnto(s, nil) - if buf != nil { - if sbuf, ok := buf.(stringer); ok { - return sbuf.String(), err - } - } - return s, err +func (p *crossLanguageStringXform) Escape(s string) (string, error) { + // We pass null so that we don't unnecessarily allocate (and zero) or copy char arrays. + buf, err := p.maybeEscapeOnto(s, nil) + if buf != nil { + if sbuf, ok := buf.(stringer); ok { + return sbuf.String(), err + } + } + return s, err } - -func (p* crossLanguageStringXform) EscapedWriter(w io.Writer) (io.Writer) { - return newAppendableEscapedWriter(p, w) +func (p *crossLanguageStringXform) EscapedWriter(w io.Writer) io.Writer { + return newAppendableEscapedWriter(p, w) } - /** * Escapes the given char sequence onto the given buffer iff it contains characters that need to * be escaped. @@ -397,7 +382,7 @@ func (p* crossLanguageStringXform) EscapedWriter(w io.Writer) (io.Writer) { * escaping. Otherwise out, or a StringBuilder if one needed to be allocated. */ func (p *crossLanguageStringXform) maybeEscapeOnto(s string, out io.Writer) (io.Writer, error) { - return p.maybeEscapeOntoSubstring(s, out, 0, len(s)) + return p.maybeEscapeOntoSubstring(s, out, 0, len(s)) } /** @@ -407,52 +392,64 @@ func (p *crossLanguageStringXform) maybeEscapeOnto(s string, out io.Writer) (io. * escaping. Otherwise out, or a StringBuilder if one needed to be allocated. */ func (p *crossLanguageStringXform) maybeEscapeOntoSubstring(s string, out io.Writer, start, end int) (io.Writer, error) { - var err error - pos := start - escapesByCodeUnitLen := len(p.escapesByCodeUnit) - for j, c := range s[start:end] { - i := start + j - if int(c) < escapesByCodeUnitLen { // Use the dense map. - esc := p.escapesByCodeUnit[c]; - if esc != "" { - if out == nil { - // Create a new buffer if we need to escape a character in s. - // We add 32 to the size to leave a decent amount of space for escape characters. - out = bytes.NewBuffer(make([]byte, 0)) - } - _, err = io.WriteString(out, s[pos:i]) - if err != nil { return out, err } - _, err = io.WriteString(out, esc); - if err != nil { return out, err } - pos = i + 1 - } - } else if c >= 0x80 { // Use the sparse map. - index := sort.SearchInts(p.nonAsciiCodeUnits, int(c)) - if index >= 0 { - if out == nil { - out = bytes.NewBuffer(make([]byte, 0)) - } - _, err = io.WriteString(out, s[pos:i]) - if err != nil { return out, err } - _, err = io.WriteString(out, p.nonAsciiEscapes[index]) - if err != nil { return out, err } - pos = i + 1 - } else if p.nonAsciiPrefix != "" { // Fallback to the prefix based escaping. - if out == nil { - out = bytes.NewBuffer(make([]byte, 0)) - } - _, err = io.WriteString(out, s[pos:i]) - if err != nil { return out, err } - err = p.escapeUsingPrefix(c, out) - if err != nil { return out, err } - pos = i + 1 - } - } - } - if out != nil { - _, err = io.WriteString(out, s[pos:end]) - } - return out, err + var err error + pos := start + escapesByCodeUnitLen := len(p.escapesByCodeUnit) + for j, c := range s[start:end] { + i := start + j + if int(c) < escapesByCodeUnitLen { // Use the dense map. + esc := p.escapesByCodeUnit[c] + if esc != "" { + if out == nil { + // Create a new buffer if we need to escape a character in s. + // We add 32 to the size to leave a decent amount of space for escape characters. + out = bytes.NewBuffer(make([]byte, 0)) + } + _, err = io.WriteString(out, s[pos:i]) + if err != nil { + return out, err + } + _, err = io.WriteString(out, esc) + if err != nil { + return out, err + } + pos = i + 1 + } + } else if c >= 0x80 { // Use the sparse map. + index := sort.SearchInts(p.nonAsciiCodeUnits, int(c)) + if index >= 0 { + if out == nil { + out = bytes.NewBuffer(make([]byte, 0)) + } + _, err = io.WriteString(out, s[pos:i]) + if err != nil { + return out, err + } + _, err = io.WriteString(out, p.nonAsciiEscapes[index]) + if err != nil { + return out, err + } + pos = i + 1 + } else if p.nonAsciiPrefix != "" { // Fallback to the prefix based escaping. + if out == nil { + out = bytes.NewBuffer(make([]byte, 0)) + } + _, err = io.WriteString(out, s[pos:i]) + if err != nil { + return out, err + } + err = p.escapeUsingPrefix(c, out) + if err != nil { + return out, err + } + pos = i + 1 + } + } + } + if out != nil { + _, err = io.WriteString(out, s[pos:end]) + } + return out, err } /** @@ -462,79 +459,101 @@ func (p *crossLanguageStringXform) maybeEscapeOntoSubstring(s string, out io.Wri * @param c A code unit greater than or equal to 0x80. * @param out written to. */ -func (p* crossLanguageStringXform) escapeUsingPrefix(c rune, out io.Writer) (err error) { - if "%" == p.nonAsciiPrefix { // Use a UTF-8 - if c < 0x800 { - _, err = out.Write(_BYTE_ARRAY_PERCENT) - if err != nil { return } - err = appendHexPair(((c >> 6) & 0x1f) | 0xc0, out); - if err != nil { return } - } else { - _, err = out.Write(_BYTE_ARRAY_PERCENT); - if err != nil { return } - err = appendHexPair(((c >> 12) & 0xf) | 0xe0, out); - if err != nil { return } - _, err = out.Write(_BYTE_ARRAY_PERCENT); - if err != nil { return } - err = appendHexPair(((c >> 6) & 0x3f) | 0x80, out); - if err != nil { return } - } - _, err = out.Write(_BYTE_ARRAY_PERCENT); - if err != nil { return } - err = appendHexPair((c & 0x3f) | 0x80, out); - if err != nil { return } - } else { - _, err = io.WriteString(out, p.nonAsciiPrefix); - if err != nil { return } - err = appendHexPair((c >> 8) & 0xff, out); - if err != nil { return } - err = appendHexPair(c & 0xff, out); - if err != nil { return } - if "\\" == p.nonAsciiPrefix { - // Append with a space so that CSS escape doesn't pull in any hex digits following. - _, err = out.Write([]byte{' '}); - } - } - return +func (p *crossLanguageStringXform) escapeUsingPrefix(c rune, out io.Writer) (err error) { + if "%" == p.nonAsciiPrefix { // Use a UTF-8 + if c < 0x800 { + _, err = out.Write(_BYTE_ARRAY_PERCENT) + if err != nil { + return + } + err = appendHexPair(((c>>6)&0x1f)|0xc0, out) + if err != nil { + return + } + } else { + _, err = out.Write(_BYTE_ARRAY_PERCENT) + if err != nil { + return + } + err = appendHexPair(((c>>12)&0xf)|0xe0, out) + if err != nil { + return + } + _, err = out.Write(_BYTE_ARRAY_PERCENT) + if err != nil { + return + } + err = appendHexPair(((c>>6)&0x3f)|0x80, out) + if err != nil { + return + } + } + _, err = out.Write(_BYTE_ARRAY_PERCENT) + if err != nil { + return + } + err = appendHexPair((c&0x3f)|0x80, out) + if err != nil { + return + } + } else { + _, err = io.WriteString(out, p.nonAsciiPrefix) + if err != nil { + return + } + err = appendHexPair((c>>8)&0xff, out) + if err != nil { + return + } + err = appendHexPair(c&0xff, out) + if err != nil { + return + } + if "\\" == p.nonAsciiPrefix { + // Append with a space so that CSS escape doesn't pull in any hex digits following. + _, err = out.Write([]byte{' '}) + } + } + return } /** * Given {@code 0x20} appends {@code "20"} to the given output buffer. */ func appendHexPair(b rune, out io.Writer) error { - _, err := out.Write([]byte{HEX_DIGITS[b >> 4]}) - if err != nil { - return err - } - _, err = out.Write([]byte{HEX_DIGITS[b & 0xf]}) - return err + _, err := out.Write([]byte{HEX_DIGITS[b>>4]}) + if err != nil { + return err + } + _, err = out.Write([]byte{HEX_DIGITS[b&0xf]}) + return err } type numericEscaperFor interface { - /** - * Computes the numeric escape in the output language for the given codepoint in the input - * language. - * E.g. in C, the numeric escape for space is {@code \x20}. - */ - NumericEscapeFor(plainText rune) string + /** + * Computes the numeric escape in the output language for the given codepoint in the input + * language. + * E.g. in C, the numeric escape for space is {@code \x20}. + */ + NumericEscapeFor(plainText rune) string } type escapeListBuilder struct { - escapes []Escape - escaper numericEscaperFor + escapes []Escape + escaper numericEscaperFor } func initEscapeListBuilder(elb *escapeListBuilder, escaper numericEscaperFor) { - elb.escapes = make([]Escape, 0) - elb.escaper = escaper + elb.escapes = make([]Escape, 0) + elb.escaper = escaper } /** * Adds an escape for the given code unit in the input language to the given escaped text. */ func (p *escapeListBuilder) EscapeWithValue(plainText rune, escaped string) *escapeListBuilder { - p.escapes = append(p.escapes, NewEscape(plainText, escaped)) - return p + p.escapes = append(p.escapes, NewEscape(plainText, escaped)) + return p } /** @@ -542,78 +561,76 @@ func (p *escapeListBuilder) EscapeWithValue(plainText rune, escaped string) *esc * scheme. */ func (p *escapeListBuilder) Escape(plainText rune) *escapeListBuilder { - return p.EscapeWithValue(plainText, p.escaper.NumericEscapeFor(plainText)) + return p.EscapeWithValue(plainText, p.escaper.NumericEscapeFor(plainText)) } /** * Adds a numeric escape for each code unit in the input string. */ func (p *escapeListBuilder) EscapeAll(plainTextCodeUnits string) *escapeListBuilder { - for _, r := range plainTextCodeUnits { - p.Escape(r) - } - return p + for _, r := range plainTextCodeUnits { + p.Escape(r) + } + return p } /** * Adds numeric escapes for each code unit in the given range not in the exclusion set. */ func (p *escapeListBuilder) EscapeAllInRangeExcept(startInclusive, endExclusive rune, notEscaped ...rune) *escapeListBuilder { - notEscaped2 := make([]rune, len(notEscaped)) - copy(notEscaped2, notEscaped) - //sort.Ints(notEscaped2) - k := 0 - numNotEscaped2 := len(notEscaped2); - for i := startInclusive; i < endExclusive; i++ { - for k < numNotEscaped2 && notEscaped2[k] < i { - k++ - } - if k < numNotEscaped2 && notEscaped2[k] == i { - continue - } - p.Escape(i); - } - return p + notEscaped2 := make([]rune, len(notEscaped)) + copy(notEscaped2, notEscaped) + //sort.Ints(notEscaped2) + k := 0 + numNotEscaped2 := len(notEscaped2) + for i := startInclusive; i < endExclusive; i++ { + for k < numNotEscaped2 && notEscaped2[k] < i { + k++ + } + if k < numNotEscaped2 && notEscaped2[k] == i { + continue + } + p.Escape(i) + } + return p } func (p *escapeListBuilder) Len() int { - return len(p.escapes) + return len(p.escapes) } func (p *escapeListBuilder) Less(i, j int) bool { - return p.escapes[i].CompareTo(p.escapes[j]) < 0 + return p.escapes[i].CompareTo(p.escapes[j]) < 0 } func (p *escapeListBuilder) Swap(i, j int) { - p.escapes[i], p.escapes[j] = p.escapes[j], p.escapes[i] + p.escapes[i], p.escapes[j] = p.escapes[j], p.escapes[i] } func (p *escapeListBuilder) Build() []Escape { - sort.Sort(p) - escapes := make([]Escape, len(p.escapes)) - copy(escapes, p.escapes) - return escapes + sort.Sort(p) + escapes := make([]Escape, len(p.escapes)) + copy(escapes, p.escapes) + return escapes } /** * Escapes using HTML/XML numeric entities : {@code 'A' -> "A"}. */ type htmlEscapeListBuilder struct { - escapeListBuilder + escapeListBuilder } func newHtmlEscapeListBuilder() *htmlEscapeListBuilder { - builder := new(htmlEscapeListBuilder) - initEscapeListBuilder(&builder.escapeListBuilder, builder) - return builder + builder := new(htmlEscapeListBuilder) + initEscapeListBuilder(&builder.escapeListBuilder, builder) + return builder } -func (p* htmlEscapeListBuilder) NumericEscapeFor(plainText rune) string { - return "&#" + strconv.Itoa(int(plainText)) + ";" +func (p *htmlEscapeListBuilder) NumericEscapeFor(plainText rune) string { + return "&#" + strconv.Itoa(int(plainText)) + ";" } - - // Implementations of particular escapers. // These names follow the convention defined in Escaper's constructor above where // class EscapeFoo @@ -625,37 +642,36 @@ func (p* htmlEscapeListBuilder) NumericEscapeFor(plainText rune) string { * Implements the {@code |escapeHtml} directive. */ type escapeHtmlEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newEscapeHtmlEscaper() *escapeHtmlEscaper { - p := new(escapeHtmlEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "EscapeHtml", - nil, - // TODO: enable goog.string.htmlEscape after it escapes single quotes. - []string{/*"goog.string.htmlEscape"*/}, - "", - p, - ) - return p + p := new(escapeHtmlEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "EscapeHtml", + nil, + // TODO: enable goog.string.htmlEscape after it escapes single quotes. + []string{ /*"goog.string.htmlEscape"*/}, + "", + p, + ) + return p } func (p *escapeHtmlEscaper) DefineEscapes() []Escape { - escapes := newHtmlEscapeListBuilder(). - EscapeWithValue('&', "&"). - EscapeWithValue('<', "<"). - EscapeWithValue('>', ">"). - EscapeWithValue('"', """). - EscapeAll( - // It escapes ' to ' instead of ' which is not standardized in XML. - "\000'", - ).Build() - return escapes + escapes := newHtmlEscapeListBuilder(). + EscapeWithValue('&', "&"). + EscapeWithValue('<', "<"). + EscapeWithValue('>', ">"). + EscapeWithValue('"', """). + EscapeAll( + // It escapes ' to ' instead of ' which is not standardized in XML. + "\000'", + ).Build() + return escapes } - /** * A directive that encodes any HTML special characters that can appear in RCDATA unescaped but * that can be escaped without changing semantics. @@ -678,297 +694,287 @@ func (p *escapeHtmlEscaper) DefineEscapes() []Escape { * lead to overescaping of legitimate HTML entities. */ type normalizeHtmlEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newNormalizeHtmlEscaper() *normalizeHtmlEscaper { - p := new(normalizeHtmlEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "NormalizeHtml", - nil, - []string{}, - "", - p, - ) - return p + p := new(normalizeHtmlEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "NormalizeHtml", + nil, + []string{}, + "", + p, + ) + return p } func (p *normalizeHtmlEscaper) DefineEscapes() []Escape { - escapes := EscapeHtmlInstance.DefineEscapes() - arr := make([]Escape, len(escapes)) - i := 0 - for _, esc := range escapes { - if esc.PlainText() != '&' { - arr[i] = esc - i++ - } - } - return arr[0:i] + escapes := EscapeHtmlInstance.DefineEscapes() + arr := make([]Escape, len(escapes)) + i := 0 + for _, esc := range escapes { + if esc.PlainText() != '&' { + arr[i] = esc + i++ + } + } + return arr[0:i] } - - /** * Implements the {@code |escapeHtmlNoSpace} directive which allows arbitrary content * to be included in the value of an unquoted HTML attribute. */ type escapeHtmlNospaceEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newEscapeHtmlNospaceEscaper() *escapeHtmlNospaceEscaper { - p := new(escapeHtmlNospaceEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "EscapeHtmlNospace", - nil, - []string{}, - "", - p, - ) - return p + p := new(escapeHtmlNospaceEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "EscapeHtmlNospace", + nil, + []string{}, + "", + p, + ) + return p } func (p *escapeHtmlNospaceEscaper) DefineEscapes() []Escape { - escapes := newHtmlEscapeListBuilder().EscapeWithValue( - '&', "&").EscapeWithValue( - '<', "<").EscapeWithValue( - '>', ">").EscapeWithValue( - '"', """).EscapeAll( - // The below list of characters are all those that need to be encode to prevent unquoted - // value splitting. - // - // From the XML spec, - // [3] S ::= (#x20 | #x9 | #xD | #xA)+ - // From section 2.4.1 of the HTML5 draft, - // The space characters, for the purposes of this specification, are - // U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), - // U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR). - // The White_Space characters are those that have the Unicode property - // "White_Space" in the Unicode PropList.txt data file. - // From XML processing notes: - // [XML1.1] also normalizes NEL (U+0085) and U+2028 LINE SEPARATOR, but - // U+2029 PARAGRAPH SEPARATOR is not treated that way. - // Those newline characters are described at - // http://unicode.org/reports/tr13/tr13-9.html - // - // Empirically, we need to quote - // U+0009 - U+000d, U+0020, double quote, single quote, '>', and back quote. - // based on running - // - //

- // - // in a variety of browsers. - // - // We supplement that set with the quotes and equal sign which have special - // meanings in attributes, and with the XML normalized spaces. - "\u0000\u0009\n\u000B\u000C\r '-/=\u0060\u0085\u00a0\u2028\u2029").Build() - return escapes + escapes := newHtmlEscapeListBuilder().EscapeWithValue( + '&', "&").EscapeWithValue( + '<', "<").EscapeWithValue( + '>', ">").EscapeWithValue( + '"', """).EscapeAll( + // The below list of characters are all those that need to be encode to prevent unquoted + // value splitting. + // + // From the XML spec, + // [3] S ::= (#x20 | #x9 | #xD | #xA)+ + // From section 2.4.1 of the HTML5 draft, + // The space characters, for the purposes of this specification, are + // U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), + // U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR). + // The White_Space characters are those that have the Unicode property + // "White_Space" in the Unicode PropList.txt data file. + // From XML processing notes: + // [XML1.1] also normalizes NEL (U+0085) and U+2028 LINE SEPARATOR, but + // U+2029 PARAGRAPH SEPARATOR is not treated that way. + // Those newline characters are described at + // http://unicode.org/reports/tr13/tr13-9.html + // + // Empirically, we need to quote + // U+0009 - U+000d, U+0020, double quote, single quote, '>', and back quote. + // based on running + // + //
+ // + // in a variety of browsers. + // + // We supplement that set with the quotes and equal sign which have special + // meanings in attributes, and with the XML normalized spaces. + "\u0000\u0009\n\u000B\u000C\r '-/=\u0060\u0085\u00a0\u2028\u2029").Build() + return escapes } - - /** * A directive that encodes any HTML special characters and unquoted attribute terminators that * can appear in RCDATA unescaped but that can be escaped without changing semantics. */ type normalizeHtmlNospaceEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newNormalizeHtmlNospaceEscaper() *normalizeHtmlNospaceEscaper { - p := new(normalizeHtmlNospaceEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "NormalizeHtmlNospace", - nil, - []string{}, - "", - p, - ) - return p + p := new(normalizeHtmlNospaceEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "NormalizeHtmlNospace", + nil, + []string{}, + "", + p, + ) + return p } func (p *normalizeHtmlNospaceEscaper) DefineEscapes() []Escape { - escapes := EscapeHtmlNospaceInstance.DefineEscapes() - arr := make([]Escape, len(escapes)) - i := 0 - for _, esc := range escapes { - if esc.PlainText() != '&' { - arr[i] = esc - i++ - } - } - return arr[0:i] + escapes := EscapeHtmlNospaceInstance.DefineEscapes() + arr := make([]Escape, len(escapes)) + i := 0 + for _, esc := range escapes { + if esc.PlainText() != '&' { + arr[i] = esc + i++ + } + } + return arr[0:i] } - /** * Escapes using hex escapes since octal are non-standard. 'A' -> "\\x41" */ type jsEscapeListBuilder struct { - escapeListBuilder + escapeListBuilder } func newJsEscapeListBuilder() *jsEscapeListBuilder { - builder := new(jsEscapeListBuilder) - initEscapeListBuilder(&builder.escapeListBuilder, builder) - return builder + builder := new(jsEscapeListBuilder) + initEscapeListBuilder(&builder.escapeListBuilder, builder) + return builder } -func (p* jsEscapeListBuilder) NumericEscapeFor(plainText rune) (s string) { - if plainText < 0x100 { - s = fmt.Sprintf("\\x%02x", plainText) - } else { - s = fmt.Sprintf("\\u%04x", plainText) - } - return +func (p *jsEscapeListBuilder) NumericEscapeFor(plainText rune) (s string) { + if plainText < 0x100 { + s = fmt.Sprintf("\\x%02x", plainText) + } else { + s = fmt.Sprintf("\\u%04x", plainText) + } + return } - /** * Implements the {@code |escapeJsString} directive which allows arbitrary content * to be included inside a quoted JavaScript string. */ type escapeJsStringEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newEscapeJsStringEscaper() *escapeJsStringEscaper { - p := new(escapeJsStringEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "EscapeJsString", - nil, - []string{}, - "", - p, - ) - return p + p := new(escapeJsStringEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "EscapeJsString", + nil, + []string{}, + "", + p, + ) + return p } func (p *escapeJsStringEscaper) DefineEscapes() []Escape { - escapes := newJsEscapeListBuilder().Escape( - // Some control characters. - '\u0000').Escape( - // \\b means word-break inside RegExps - '\b').EscapeWithValue( - '\t', "\\t").EscapeWithValue( - '\n', "\\n").Escape( - // \\v not consistently supported on IE - '\u000b').EscapeWithValue( - '\f', "\\f").EscapeWithValue( - '\r', "\\r").EscapeWithValue( - '\\', "\\\\").Escape( - // Quoting characters. / is also instrumental in . - '"').Escape( - '\'').EscapeWithValue( - '/', "\\/").EscapeAll( - // JavaScript newlines - "\u2028\u2029").Escape( - // A JavaScript newline according to at least one draft spec. - '\u0085').EscapeAll( - // HTML special characters. Note, that this provides added protection against problems - // with , , etc. - "<>&=").Build() - return escapes + escapes := newJsEscapeListBuilder().Escape( + // Some control characters. + '\u0000').Escape( + // \\b means word-break inside RegExps + '\b').EscapeWithValue( + '\t', "\\t").EscapeWithValue( + '\n', "\\n").Escape( + // \\v not consistently supported on IE + '\u000b').EscapeWithValue( + '\f', "\\f").EscapeWithValue( + '\r', "\\r").EscapeWithValue( + '\\', "\\\\").Escape( + // Quoting characters. / is also instrumental in . + '"').Escape( + '\'').EscapeWithValue( + '/', "\\/").EscapeAll( + // JavaScript newlines + "\u2028\u2029").Escape( + // A JavaScript newline according to at least one draft spec. + '\u0085').EscapeAll( + // HTML special characters. Note, that this provides added protection against problems + // with , , etc. + "<>&=").Build() + return escapes } - - /** * Implements the {@code |escapeJsRegex} directive which allows arbitrary content * to be included inside a JavaScript regular expression. */ type escapeJsRegexEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newEscapeJsRegexEscaper() *escapeJsRegexEscaper { - p := new(escapeJsRegexEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "EscapeJsString", - nil, - []string{}, - "", - p, - ) - return p + p := new(escapeJsRegexEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "EscapeJsString", + nil, + []string{}, + "", + p, + ) + return p } func (p *escapeJsRegexEscaper) DefineEscapes() []Escape { - escapes := newJsEscapeListBuilder().Escape( - // Some control characters. - '\u0000').Escape( - // \\b means word-break inside RegExps - '\b').EscapeWithValue( - '\t', "\\t").EscapeWithValue( - '\n', "\\n").Escape( - // \\v not consistently supported on IE - '\u000b').EscapeWithValue( - '\f', "\\f").EscapeWithValue( - '\r', "\\r").EscapeWithValue( - // Escape prefx - '\\', "\\\\").EscapeAll( - // JavaScript newlines - "\u2028\u2029").Escape( - // A JavaScript newline according to at least one draft spec. - '\u0085').Escape( - // Quoting characters. / is also instrumental in . - '"').Escape( - '\'').EscapeWithValue( - '/', "\\/").EscapeAll( - // HTML special characters. Note, that this provides added protection against problems - // with , , etc. - "<>&=").EscapeAll( - // Special in regular expressions. / is also special, but is escaped above. - "$()*+-.:?[]^{|},").Build() - return escapes + escapes := newJsEscapeListBuilder().Escape( + // Some control characters. + '\u0000').Escape( + // \\b means word-break inside RegExps + '\b').EscapeWithValue( + '\t', "\\t").EscapeWithValue( + '\n', "\\n").Escape( + // \\v not consistently supported on IE + '\u000b').EscapeWithValue( + '\f', "\\f").EscapeWithValue( + '\r', "\\r").EscapeWithValue( + // Escape prefx + '\\', "\\\\").EscapeAll( + // JavaScript newlines + "\u2028\u2029").Escape( + // A JavaScript newline according to at least one draft spec. + '\u0085').Escape( + // Quoting characters. / is also instrumental in . + '"').Escape( + '\'').EscapeWithValue( + '/', "\\/").EscapeAll( + // HTML special characters. Note, that this provides added protection against problems + // with , , etc. + "<>&=").EscapeAll( + // Special in regular expressions. / is also special, but is escaped above. + "$()*+-.:?[]^{|},").Build() + return escapes } - - /** * Escapes using CSS hex escapes with a space at the end in case a hex digit is the next * character : {@code 'A' => "\41 "} */ type cssEscapeListBuilder struct { - escapeListBuilder + escapeListBuilder } func newCssEscapeListBuilder() *cssEscapeListBuilder { - builder := new(cssEscapeListBuilder) - initEscapeListBuilder(&builder.escapeListBuilder, builder) - return builder + builder := new(cssEscapeListBuilder) + initEscapeListBuilder(&builder.escapeListBuilder, builder) + return builder } -func (p* cssEscapeListBuilder) NumericEscapeFor(plainText rune) (s string) { - return "\\" + strconv.FormatInt(int64(plainText), 16) +func (p *cssEscapeListBuilder) NumericEscapeFor(plainText rune) (s string) { + return "\\" + strconv.FormatInt(int64(plainText), 16) } /** @@ -976,38 +982,38 @@ func (p* cssEscapeListBuilder) NumericEscapeFor(plainText rune) (s string) { * included in a CSS quoted string or identifier. */ type escapeCssStringEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newEscapeCssStringEscaper() *escapeCssStringEscaper { - p := new(escapeCssStringEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "EscapeCssString", - nil, - []string{}, - "", - p, - ) - return p + p := new(escapeCssStringEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "EscapeCssString", + nil, + []string{}, + "", + p, + ) + return p } func (p *escapeCssStringEscaper) DefineEscapes() []Escape { - escapes := newCssEscapeListBuilder().EscapeAll( - // Escape newlines and similar control characters, quotes, HTML special characters, and - // CSS punctuation that might cause CSS error recovery code to restart parsing in the - // middle of a string. - // Semicolons, close curlies, and @ (which precedes top-level directives like @media), - // and slashes in comment delimiters are all good places for CSS error recovery code to - // skip to. - // Quotes and parentheses are used as string and URL delimiters. - // Angle brackets and slashes appear in escaping text spans allowed in HTML5 which could prematurely close a style element. - // Newlines are disallowed in strings, so not escaping them can trigger CSS error - // recovery. - "\u0000\b\t\n\u000b\f\r\u0085\u00a0\u2028\u2029\"'\\<>&{};:()@/=*").Build() - return escapes + escapes := newCssEscapeListBuilder().EscapeAll( + // Escape newlines and similar control characters, quotes, HTML special characters, and + // CSS punctuation that might cause CSS error recovery code to restart parsing in the + // middle of a string. + // Semicolons, close curlies, and @ (which precedes top-level directives like @media), + // and slashes in comment delimiters are all good places for CSS error recovery code to + // skip to. + // Quotes and parentheses are used as string and URL delimiters. + // Angle brackets and slashes appear in escaping text spans allowed in HTML5 which could prematurely close a style element. + // Newlines are disallowed in strings, so not escaping them can trigger CSS error + // recovery. + "\u0000\b\t\n\u000b\f\r\u0085\u00a0\u2028\u2029\"'\\<>&{};:()@/=*").Build() + return escapes } /** @@ -1015,52 +1021,52 @@ func (p *escapeCssStringEscaper) DefineEscapes() []Escape { * CSS property names, keyword values, quantities, hex colors, or ID or class literals. */ type filterCssValueEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newFilterCssValueEscaper() *filterCssValueEscaper { - p := new(filterCssValueEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "FilterCssValue", - CSS_WORD, - []string{}, - "", - p, - ) - return p + p := new(filterCssValueEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "FilterCssValue", + CSS_WORD, + []string{}, + "", + p, + ) + return p } func (p *filterCssValueEscaper) DefineEscapes() []Escape { - return []Escape{} + return []Escape{} } /** * Escapes using URI percent encoding : {@code 'A' => "%41"} */ type uriEscapeListBuilder struct { - escapeListBuilder + escapeListBuilder } func newUriEscapeListBuilder() *uriEscapeListBuilder { - builder := new(uriEscapeListBuilder) - initEscapeListBuilder(&builder.escapeListBuilder, builder) - return builder -} - -func (p* uriEscapeListBuilder) NumericEscapeFor(plainText rune) (s string) { - // URI encoding is different from the other escaping schemes. - // The others are transformations on strings of UTF-16 code units, but URIs are composed of - // strings of bytes. We assume UTF-8 as the standard way to convert between bytes and code - // units below. - theBytes := []byte(string(plainText)) - numBytes := len(theBytes) - buf := bytes.NewBuffer([]byte{}) - for i := 0; i < numBytes; i++ { - // Use uppercase escapes for consistency with CharEscapers.uriEscaper(). - buf.WriteString(fmt.Sprintf("%%%02X", theBytes[i])) - } - return buf.String() + builder := new(uriEscapeListBuilder) + initEscapeListBuilder(&builder.escapeListBuilder, builder) + return builder +} + +func (p *uriEscapeListBuilder) NumericEscapeFor(plainText rune) (s string) { + // URI encoding is different from the other escaping schemes. + // The others are transformations on strings of UTF-16 code units, but URIs are composed of + // strings of bytes. We assume UTF-8 as the standard way to convert between bytes and code + // units below. + theBytes := []byte(string(plainText)) + numBytes := len(theBytes) + buf := bytes.NewBuffer([]byte{}) + for i := 0; i < numBytes; i++ { + // Use uppercase escapes for consistency with CharEscapers.uriEscaper(). + buf.WriteString(fmt.Sprintf("%%%02X", theBytes[i])) + } + return buf.String() } /** @@ -1070,82 +1076,82 @@ func (p* uriEscapeListBuilder) NumericEscapeFor(plainText rune) (s string) { * instead escapes HTML, CSS, and JS delimiters. */ type normalizeUriEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newNormalizeUriEscaper() *normalizeUriEscaper { - p := new(normalizeUriEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "normalizeUri", - nil, - []string{}, - "", - p, - ) - return p + p := new(normalizeUriEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "normalizeUri", + nil, + []string{}, + "", + p, + ) + return p } func (p *normalizeUriEscaper) DefineEscapes() []Escape { - escapes := newUriEscapeListBuilder().EscapeAll( - // Escape all ASCII control characters. - "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"). - EscapeAll("\u0008\u0009\n\u000B\u000C\r\u000E\u000F"). - EscapeAll("\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"). - EscapeAll("\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"). - Escape('\u007f'). - EscapeAll( - // Escape non-special URI characters that might prematurely close an unquoted CSS URI or - // HTML attribute. - // Parentheses and single quote are technically sub-delims, but not in HTTP or HTTPS, - // only appearing in the obsolete mark rule in section D.2. of RFC 3986. - // It is important to encode parentheses to prevent CSS URIs from being broken as in: - // background: {lb} background-image: url( /foo/{print $x}.png ) {rb} - // It is important to encode both quote characters to prevent broken CSS URIs and HTML - // attributes as in: - // background: {lb} background-image: url('/foo/{print $x}.png') {rb} - // and - // - " (){}\"'\\<>"). - EscapeAll( - // More spaces and newlines. - "\u0085\u00A0\u2028\u2029"). - EscapeAll( - // Make sure that full-width versions of reserved characters are escaped. - // Some user-agents treat full-width characters in URIs entered in the URL bar the same - // as the ASCII version so that URLs copied and pasted from written Chinese work. - // Each Latin printable character has a full-width equivalent in the U+FF00 code plane, - // e.g. the full-width colon is \uFF1A. - // http://www.cisco.com/en/US/products/products_security_response09186a008083f82e.html - // says that it is possible to route malicious URLs through intervening layers to the - // browser by using the full-width equivalents of special characters. - toFullWidth(":/?#[]@!$&'()*+,;=")). - Build() - return escapes + escapes := newUriEscapeListBuilder().EscapeAll( + // Escape all ASCII control characters. + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"). + EscapeAll("\u0008\u0009\n\u000B\u000C\r\u000E\u000F"). + EscapeAll("\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"). + EscapeAll("\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"). + Escape('\u007f'). + EscapeAll( + // Escape non-special URI characters that might prematurely close an unquoted CSS URI or + // HTML attribute. + // Parentheses and single quote are technically sub-delims, but not in HTTP or HTTPS, + // only appearing in the obsolete mark rule in section D.2. of RFC 3986. + // It is important to encode parentheses to prevent CSS URIs from being broken as in: + // background: {lb} background-image: url( /foo/{print $x}.png ) {rb} + // It is important to encode both quote characters to prevent broken CSS URIs and HTML + // attributes as in: + // background: {lb} background-image: url('/foo/{print $x}.png') {rb} + // and + // + " (){}\"'\\<>"). + EscapeAll( + // More spaces and newlines. + "\u0085\u00A0\u2028\u2029"). + EscapeAll( + // Make sure that full-width versions of reserved characters are escaped. + // Some user-agents treat full-width characters in URIs entered in the URL bar the same + // as the ASCII version so that URLs copied and pasted from written Chinese work. + // Each Latin printable character has a full-width equivalent in the U+FF00 code plane, + // e.g. the full-width colon is \uFF1A. + // http://www.cisco.com/en/US/products/products_security_response09186a008083f82e.html + // says that it is possible to route malicious URLs through intervening layers to the + // browser by using the full-width equivalents of special characters. + toFullWidth(":/?#[]@!$&'()*+,;=")). + Build() + return escapes } /** * Like {@link NormalizeUri} but filters out dangerous protocols. */ type filterNormalizeUriEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newFilterNormalizeUriEscaper() *filterNormalizeUriEscaper { - p := new(filterNormalizeUriEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "FilterNormalizeUri", - _FILTER_NORMALIZE_URI_RE, - []string{}, - "", - p, - ) - return p + p := new(filterNormalizeUriEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "FilterNormalizeUri", + _FILTER_NORMALIZE_URI_RE, + []string{}, + "", + p, + ) + return p } func (p *filterNormalizeUriEscaper) DefineEscapes() []Escape { - return NormalizeUriInstance.DefineEscapes() + return NormalizeUriInstance.DefineEscapes() } /** @@ -1153,40 +1159,40 @@ func (p *filterNormalizeUriEscaper) DefineEscapes() []Escape { * URI regardless of the string delimiters of the the surrounding language. */ type escapeUriEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newEscapeUriEscaper() *escapeUriEscaper { - p := new(escapeUriEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "EscapeUri", - nil, - []string{"goog.string.urlEncode", "encodeURIComponent"}, - "%", - p, - ) - return p + p := new(escapeUriEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "EscapeUri", + nil, + []string{"goog.string.urlEncode", "encodeURIComponent"}, + "%", + p, + ) + return p } func (p *escapeUriEscaper) DefineEscapes() []Escape { - escapes := newUriEscapeListBuilder(). - EscapeAllInRangeExcept( - 0, 0x80, - // From Appendix A of RFC 3986 - // unreserved := ALPHA / DIGIT / "-" / "." / "_" / "~" - '-', '.', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', - 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - '_', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '~', - ). - // All non-ASCII codepoints escaped per the constructor above. - Build() - return escapes + escapes := newUriEscapeListBuilder(). + EscapeAllInRangeExcept( + 0, 0x80, + // From Appendix A of RFC 3986 + // unreserved := ALPHA / DIGIT / "-" / "." / "_" / "~" + '-', '.', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '_', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '~', + ). + // All non-ASCII codepoints escaped per the constructor above. + Build() + return escapes } /** @@ -1194,24 +1200,24 @@ func (p *escapeUriEscaper) DefineEscapes() []Escape { * can't appear as part of an HTML tag or attribute name. */ type filterHtmlAttributeEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newFilterHtmlAttributeEscaper() *filterHtmlAttributeEscaper { - p := new(filterHtmlAttributeEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "FilterHtmlAttribute", - _FILTER_HTML_ATTRIBUTE_RE, - []string{}, - "", - p, - ) - return p + p := new(filterHtmlAttributeEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "FilterHtmlAttribute", + _FILTER_HTML_ATTRIBUTE_RE, + []string{}, + "", + p, + ) + return p } func (p *filterHtmlAttributeEscaper) DefineEscapes() []Escape { - return []Escape{} + return []Escape{} } /** @@ -1219,47 +1225,46 @@ func (p *filterHtmlAttributeEscaper) DefineEscapes() []Escape { * can't appear as part of an HTML tag or attribute name. */ type filterHtmlElementNameEscaper struct { - crossLanguageStringXform + crossLanguageStringXform } func newFilterHtmlElementNameEscaper() *filterHtmlElementNameEscaper { - p := new(filterHtmlElementNameEscaper) - initCrossLanguageStringXform( - &p.crossLanguageStringXform, - "FilterHtmlElementName", - _FILTER_HTML_ELEMENT_NAME_RE, - []string{}, - "", - p, - ) - return p + p := new(filterHtmlElementNameEscaper) + initCrossLanguageStringXform( + &p.crossLanguageStringXform, + "FilterHtmlElementName", + _FILTER_HTML_ELEMENT_NAME_RE, + []string{}, + "", + p, + ) + return p } func (p *filterHtmlElementNameEscaper) DefineEscapes() []Escape { - return []Escape{} + return []Escape{} } /** * An accessor for all string transforms defined above. */ func AllEscapers() []CrossLanguageStringXform { - return []CrossLanguageStringXform { - EscapeHtmlInstance, - NormalizeHtmlInstance, - EscapeHtmlNospaceInstance, - EscapeJsStringInstance, - EscapeJsRegexInstance, - EscapeCssStringInstance, - FilterCssValueInstance, - EscapeUriInstance, - NormalizeUriInstance, - FilterNormalizeUriInstance, - FilterHtmlAttributeInstance, - FilterHtmlElementNameInstance, - } + return []CrossLanguageStringXform{ + EscapeHtmlInstance, + NormalizeHtmlInstance, + EscapeHtmlNospaceInstance, + EscapeJsStringInstance, + EscapeJsRegexInstance, + EscapeCssStringInstance, + FilterCssValueInstance, + EscapeUriInstance, + NormalizeUriInstance, + FilterNormalizeUriInstance, + FilterHtmlAttributeInstance, + FilterHtmlElementNameInstance, + } } - /** * Convert an ASCII string to full-width. * Full-width characters are in Unicode page U+FFxx and are used to allow ASCII characters to be @@ -1271,11 +1276,11 @@ func AllEscapers() []CrossLanguageStringXform { * CJK fullwidth forms and unicode.org. */ func toFullWidth(ascii string) string { - chars := []rune(ascii) - for i, ch := range chars { - if ch < 0x80 { - chars[i] = ch + 0xff00 - 0x20 - } - } - return string(chars) + chars := []rune(ascii) + for i, ch := range chars { + if ch < 0x80 { + chars[i] = ch + 0xff00 - 0x20 + } + } + return string(chars) } diff --git a/go/src/closure/template/soyutil/sanitized_content.go b/go/src/closure/template/soyutil/sanitized_content.go index 40269ce..fc7c319 100644 --- a/go/src/closure/template/soyutil/sanitized_content.go +++ b/go/src/closure/template/soyutil/sanitized_content.go @@ -1,74 +1,73 @@ -package soyutil; +package soyutil type SanitizedContent struct { - content string - contentKind ContentKind + content string + contentKind ContentKind } func NewSanitizedContent(content string, contentKind ContentKind) *SanitizedContent { - return &SanitizedContent{ - content: content, - contentKind: contentKind, - } + return &SanitizedContent{ + content: content, + contentKind: contentKind, + } } func (p *SanitizedContent) Content() string { - return p.content + return p.content } func (p *SanitizedContent) ContentKind() ContentKind { - return p.contentKind + return p.contentKind } func (p *SanitizedContent) Bool() bool { - return len(p.content) != 0 + return len(p.content) != 0 } func (p *SanitizedContent) BooleanValue() bool { - return len(p.content) != 0 + return len(p.content) != 0 } func (p *SanitizedContent) IntegerValue() int { - return len(p.content) + return len(p.content) } func (p *SanitizedContent) FloatValue() float32 { - return float32(len(p.content)) + return float32(len(p.content)) } func (p *SanitizedContent) Float64Value() float64 { - return float64(len(p.content)) + return float64(len(p.content)) } func (p *SanitizedContent) NumberValue() float64 { - return float64(len(p.content)) + return float64(len(p.content)) } func (p *SanitizedContent) String() string { - return p.content + return p.content } func (p *SanitizedContent) StringValue() string { - return p.content + return p.content } func (p *SanitizedContent) SoyData() SoyData { - return p + return p } func (p *SanitizedContent) Equals(other interface{}) bool { - if other == nil { - return false - } - if o, ok := other.(*SanitizedContent); ok { - if o == nil { - return false - } - return o.content == p.content && o.contentKind == p.contentKind - } - if o, ok := other.(SanitizedContent); ok { - return o.content == p.content && o.contentKind == p.contentKind - } - return false + if other == nil { + return false + } + if o, ok := other.(*SanitizedContent); ok { + if o == nil { + return false + } + return o.content == p.content && o.contentKind == p.contentKind + } + if o, ok := other.(SanitizedContent); ok { + return o.content == p.content && o.contentKind == p.contentKind + } + return false } - diff --git a/go/src/closure/template/soyutil/sanitizers.go b/go/src/closure/template/soyutil/sanitizers.go index dbaab7c..500a647 100644 --- a/go/src/closure/template/soyutil/sanitizers.go +++ b/go/src/closure/template/soyutil/sanitizers.go @@ -1,84 +1,81 @@ -package soyutil; +package soyutil import ( - "bytes" - "io" - "encoding/json" - "strconv" - "strings" - "net/url" + "bytes" + "encoding/json" + "io" + "net/url" + "strconv" + "strings" ) /** * Converts the input to HTML by entity escaping. */ func EscapeHtml(s string) string { - value, _ := EscapeHtmlInstance.Escape(s) - return value + value, _ := EscapeHtmlInstance.Escape(s) + return value } - /** * Converts the input to HTML by entity escaping. */ func EscapeHtmlSoyData(s SoyData) string { - if s == nil { - return "" - } - if v, ok := s.(*SanitizedContent); ok && v.contentKind == CONTENT_KIND_HTML { - return v.String() - } - return EscapeHtml(s.String()) + if s == nil { + return "" + } + if v, ok := s.(*SanitizedContent); ok && v.contentKind == CONTENT_KIND_HTML { + return v.String() + } + return EscapeHtml(s.String()) } /** * Converts the input to HTML suitable for use inside {@code