
com.twitter.json.extensions.scala Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2009 Robey Pointer
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.twitter.json
import scala.util.matching.Regex
// These are Robey's extensions from Configgy and when Configgy for 2.8 is done,
// we'll move back to using it.
final class ConfiggyString(wrapped: String) {
/**
* For every section of a string that matches a regular expression, call
* a function to determine a replacement (as in python's
* `re.sub`). The function will be passed the Matcher object
* corresponding to the substring that matches the pattern, and that
* substring will be replaced by the function's result.
*
* For example, this call:
*
* "ohio".regexSub("""h.""".r) { m => "n" }
*
* will return the string `"ono"`.
*
* The matches are found using `Matcher.find()` and so
* will obey all the normal java rules (the matches will not overlap,
* etc).
*
* @param re the regex pattern to replace
* @param replace a function that takes Regex.MatchData objects and
* returns a string to substitute
* @return the resulting string with replacements made
*/
def regexSub(re: Regex)(replace: (Regex.MatchData => String)): String = {
var offset = 0
var out = new StringBuilder
for (m <- re.findAllIn(wrapped).matchData) {
if (m.start > offset) {
out.append(wrapped.substring(offset, m.start))
}
out.append(replace(m))
offset = m.end
}
if (offset < wrapped.length) {
out.append(wrapped.substring(offset))
}
out.toString
}
private val QUOTE_RE = "[\u0000-\u001f\u007f-\uffff\\\\\"]".r
/**
* Quote a string so that unprintable chars (in ASCII) are represented by
* C-style backslash expressions. For example, a raw linefeed will be
* translated into "\n"
. Control codes (anything below 0x20)
* and unprintables (anything above 0x7E) are turned into either
* "\xHH"
or "\\uHHHH"
expressions, depending on
* their range. Embedded backslashes and double-quotes are also quoted.
*
* @return a quoted string, suitable for ASCII display
*/
def quoteC(): String = {
regexSub(QUOTE_RE) { m =>
m.matched.charAt(0) match {
case '\r' => "\\r"
case '\n' => "\\n"
case '\t' => "\\t"
case '"' => "\\\""
case '\\' => "\\\\"
case c =>
if (c <= 255) {
"\\x%02x" format c.toInt
} else {
"\\u%04x" format c.toInt
}
}
}
}
// we intentionally don't unquote "\$" here, so it can be used to escape interpolation later.
private val UNQUOTE_RE = """\\(u[\dA-Fa-f]{4}|x[\dA-Fa-f]{2}|[/rnt\"\\])""".r
/**
* Unquote an ASCII string that has been quoted in a style like
* {@link #quoteC} and convert it into a standard unicode string.
* "\\uHHHH"
and "\xHH"
expressions are unpacked
* into unicode characters, as well as "\r"
, "\n",
* "\t"
, "\\", and '\"'
.
*
* @return an unquoted unicode string
*/
def unquoteC() = {
def unhex(s: String): Char = Integer.valueOf(s, 16).intValue.toChar
regexSub(UNQUOTE_RE) { m =>
val ch = m.group(1).charAt(0) match {
case 'u' | 'x' => unhex(m.group(1) drop 1)
case 'r' => '\r'
case 'n' => '\n'
case 't' => '\t'
case x => x
}
ch.toString
}
}
/**
* Turn a string of hex digits into a byte array. This does the exact
* opposite of `Array[Byte]#hexlify`.
*/
def unhexlify(): Array[Byte] = {
val buffer = new Array[Byte](wrapped.length / 2)
for (i <- 0.until(wrapped.length, 2)) {
buffer(i/2) = Integer.parseInt(wrapped.substring(i, i+2), 16).toByte
}
buffer
}
}
final class ConfiggyByteArray(wrapped: Array[Byte]) {
/**
* Turn an Array[Byte] into a string of hex digits.
*/
def hexlify(): String = {
val out = new StringBuffer
for (b <- wrapped) {
val s = (b.toInt & 0xff).toHexString
if (s.length < 2) {
out append '0'
}
out append s
}
out.toString
}
}
object extensions {
implicit def stringToConfiggyString(s: String): ConfiggyString = new ConfiggyString(s)
implicit def byteArrayToConfiggyByteArray(b: Array[Byte]): ConfiggyByteArray = new ConfiggyByteArray(b)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy