Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[-----------------------------------------------------------------------\\--
- :: PRIVATE ::
- --\\-----------------------------------------------------------------------]]--
- local ipairs, tostring
- = ipairs, tostring
- local insert, remove, concat
- = table.insert, table.remove, table.concat
- local floor, abs
- = math.floor, math.abs
- local srepeat
- = string.rep
- local translate
- = game.translate_string
- local check, join, map, sum
- = xs_utils.check, xs_utils.join, xs_utils.map, xs_utils.sum
- --[[-----------------------------------------------------------------------\\--
- :: PUBLIC ::
- --\\-----------------------------------------------------------------------]]--
- __FILE__ = "xs_text"
- class "StringBuilder" (XsObj)
- --[[-------------------------------------------------------------------\\--
- :: Constructor
- --\\-------------------------------------------------------------------]]--
- function StringBuilder:__init() super("StringBuilder")
- self.fragments = {}
- self.length = 0
- self.indent = { d = 0, t = "", l = 0 }
- end
- --[[-------------------------------------------------------------------\\--
- :: Methods: Overloads
- --\\-------------------------------------------------------------------]]--
- function StringBuilder:__tostring()
- if self:_Compact() then
- return self.fragments[1]
- else
- return ""
- end
- end
- function StringBuilder:__len()
- if self.length < 0 then
- if #self.fragments == 1 then
- self.length = self.fragments[1]:len()
- else
- self.length = sum(self.fragments, function (v) return v:len() end, ipairs)
- end
- end
- return self.length
- end
- --[[-------------------------------------------------------------------\\--
- :: Methods: Private
- --\\-------------------------------------------------------------------]]--
- function StringBuilder:_Compact()
- local count = #self.fragments
- if count == 0 then
- return false
- elseif count > 1 then
- self.fragments = { concat(self.fragments, "") }
- self.length = self.fragments[1]:len()
- end
- return true
- end
- --[[-------------------------------------------------------------------\\--
- :: Methods: Public
- --\\-------------------------------------------------------------------]]--
- function StringBuilder:Apply(func)
- check(type(func) == 'function',
- '[StringBuilder:Apply]: Invalid argument #1; function expected got %s.',
- type(func))
- local result = func(self:__tostring())
- if type(result) == 'string' then
- self:Clear()
- self:Append(result)
- end
- return self
- end
- function StringBuilder:ToString()
- return self:__tostring()
- end
- function StringBuilder:Clear()
- self.fragments = {}
- return self
- end
- function StringBuilder:Reset()
- self.fragments = {}
- self.length = 0
- self.indent = { d = 0, t = "", l = 0 }
- return self
- end
- function StringBuilder:Length()
- return self:__len()
- end
- function StringBuilder:AppendIndent()
- if self.indent.d > 0 then
- insert(self.fragments, self.indent.t)
- end
- return self
- end
- function StringBuilder:Append(...)
- check(... ~= nil,
- "[StringBuilder:Append]: No arguments passed.")
- -- 2012-07-13 10:49: ~Xetrill: the next line may fail frequently because of luabind
- -- whenever a userdata object doesn't define a __tostring function.
- -- so using pcall might be a better idea.
- local args = map({ ... }, tostring)
- for _, v in ipairs(args) do
- local l = v:len()
- if l > 0 then
- insert(self.fragments, v)
- self.length = self.length + l
- end
- end
- return self
- end
- function StringBuilder:AppendLine(...)
- self:AppendIndent()
- if ... ~= nil then
- self:Append(...)
- end
- return self:Append("\n")
- end
- function StringBuilder:AppendLineIf(condition, ...)
- if not condition then
- return self
- end
- return self:AppendLine(...)
- end
- function StringBuilder:AppendIf(condition, ...)
- if not condition then
- return self
- end
- return self:Append(...)
- end
- function StringBuilder:AppendIfOr(condition, ontrue, onfalse)
- if condition then
- self:Append(ontrue)
- else
- self:Append(onfalse)
- end
- return self
- end
- function StringBuilder:AppendFormat(fmt, ...)
- return self:Append(fmt:format(...))
- end
- function StringBuilder:AppendFormatIf(condition, fmt, ...)
- if not condition then
- return self
- end
- return self:Append(fmt:format(...))
- end
- function StringBuilder:AppendRepeat(s, n, sep)
- if not s or tonumber(n) <= 0 then
- return self
- end
- return self:Append(tostring(text):rep(n, sep))
- end
- -- Appends a special string which tells the game to change text foreground
- -- color.
- -- Note that if numbers are passed they wont be checked.
- -- @param ... string|number Can be on of the following four ways:
- -- nil: Nothing or nil which fill default to emit "default".
- -- string: A single string - the name of the color to use.
- -- 3x int: A RGB color.
- -- 4x int: A ARGB color.
- function StringBuilder:AppendColor(...)
- local count = select("#", ...)
- if count == 0 then
- self:Append("%c[default]")
- elseif count == 1 then
- self:Append(join("", "%c[", tostring(select(1, ...)), "]"))
- elseif count == 3 then
- self:Append(join("", "%c[255,", join(",", ...), "]"))
- elseif count == 4 then
- self:Append(join("", "%c[", join(",", ...), "]"))
- else
- self:Append("%c[255,255,0,255]")
- end
- return self
- end
- function StringBuilder:AppendLocalized(key)
- return self:Append(translate(key))
- end
- -- TODO: give option to localize or better do it implicitly
- function StringBuilder:_InsertThousandSeparator(amount)
- local formatted = tostring(amount)
- while true do
- formatted, c = formatted:gsub("^(-?%d+)(%d%d%d)", "%1,%2")
- if c == 0 then
- break
- end
- end
- return formatted
- end
- function StringBuilder:_Round(val, decimal)
- if decimal then
- return floor((val * 10 ^ decimal) + 0.5) / (10 ^ decimal)
- else
- return floor(val + 0.5)
- end
- end
- function StringBuilder:AppendMoney(amount, color)
- if color == nil then
- self:AppendNumber(amount)
- else
- self:AppendColor(color)
- :AppendNumber(amount)
- :AppendColor()
- end
- return self:Append(" RU")
- end
- -- @author sam_lie
- -- @see http:--lua-users.org/wiki/FormattingNumbers
- function StringBuilder:AppendNumber(amount, decimal, prefix, neg_prefix)
- local str_amount, formatted, famount, remainder
- prefix = prefix or ""
- decimal = decimal or 2
- neg_prefix = neg_prefix or "-"
- famount = floor(abs(self:_Round(amount, decimal)))
- -- comma to separate the thousands
- formatted = self:_InsertThousandSeparator(famount)
- -- attach the decimal portion
- if decimal > 0 then
- remainder = tostring(self:_Round(abs(amount) - famount, decimal)):sub(3)
- formatted = join("", formatted, ".", remainder, srepeat("0", decimal - remainder:len()))
- end
- -- if value is negative then format accordingly
- if amount < 0 then
- if neg_prefix == "()" then
- formatted = join(formatted, "(", ")")
- else
- formatted = join("", neg_prefix, formatted)
- end
- end
- -- attach prefix string e.g '$'
- return self:Append(prefix, formatted)
- end
- function StringBuilder:IsEmpty()
- if #self.fragments == 0 then
- return true
- else
- return self:__len() == 0
- end
- end
- function StringBuilder:GetAt(index)
- if #self.fragments == 1 then
- return self.fragments[1]:sub(index, index)
- end
- local len, head, tail, rel = 0, 0, 0, 0
- for _, fragment in ipairs(self.fragments) do
- len = fragment:len()
- head = tail
- tail = tail + len
- if index >= head and index <= tail then
- rel = index - head
- return fragment:sub(rel, rel)
- end
- end
- return nil
- end
- function StringBuilder:Replace(pattern, replacement, count)
- if self:_Compact() then
- self.fragments[1] = self.fragments[1]:gsub(pattern, replacement, count)
- self.length = -1
- end
- return self
- end
- function StringBuilder:Insert(index, value)
- value = tostring(value)
- if value:len() == 0 then
- return self
- end
- if index == 1 then
- insert(self.fragments, 1, value)
- elseif index == self:Length() then
- self:Append(value)
- else
- -- TODO: optimize, this is IMO too wasteful
- -- A proper way would be to find the fragment the index belongs
- -- to and then create a head and tail from that fragment.
- -- Once that's done the head becomes the fragment and the value
- -- and tail will be inserted via the insert function.
- -- Note: GetAt shows how to map the index / identify the fragment.
- if self:_Compact() then
- local head = self.fragments[1]:sub(1, index)
- local tail = self.fragments[1]:sub(index)
- self.fragments[1] = join(value, head, tail)
- end
- end
- self.length = self.length + value:len()
- return self
- end
- function StringBuilder:AdjustIndent(adjustment)
- adjustment = tonumber(adjustment)
- if adjustment == 0 then
- return
- end
- local d = self.indent.d + adjustment
- local t = srepeat(" ", d)
- local l = t:len()
- self.indent = { d = d, t = t, l = l }
- return self
- end
- function StringBuilder:IncreaseIndent()
- return self:AdjustIndent(1)
- end
- function StringBuilder:DecreaseIndent()
- return self:AdjustIndent(-1)
- end
- --[[
- function StringBuilder:Trim(...)
- end
- function StringBuilder:Substring(startIndex, endIndex)
- end
- function StringBuilder:Remove(startIndex, length)
- end
- ]]--
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement