Advertisement
Xetrill

xs_text.script

Aug 19th, 2013
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.34 KB | None | 0 0
  1. --[[-----------------------------------------------------------------------\\--
  2.     :: PRIVATE ::
  3. --\\-----------------------------------------------------------------------]]--
  4.  
  5. local ipairs, tostring
  6.     = ipairs, tostring
  7. local insert, remove, concat
  8.     = table.insert, table.remove, table.concat
  9. local floor, abs
  10.     = math.floor, math.abs
  11. local srepeat
  12.     = string.rep
  13. local translate
  14.     = game.translate_string
  15. local check, join, map, sum
  16.     = xs_utils.check, xs_utils.join, xs_utils.map, xs_utils.sum
  17.  
  18.  
  19. --[[-----------------------------------------------------------------------\\--
  20.     :: PUBLIC ::
  21. --\\-----------------------------------------------------------------------]]--
  22.  
  23. __FILE__ = "xs_text"
  24.  
  25. class "StringBuilder" (XsObj)
  26.  
  27.     --[[-------------------------------------------------------------------\\--
  28.         :: Constructor
  29.     --\\-------------------------------------------------------------------]]--
  30.  
  31.     function StringBuilder:__init() super("StringBuilder")
  32.         self.fragments = {}
  33.         self.length = 0
  34.         self.indent = { d = 0, t = "", l = 0 }
  35.     end
  36.  
  37.     --[[-------------------------------------------------------------------\\--
  38.         :: Methods: Overloads
  39.     --\\-------------------------------------------------------------------]]--
  40.  
  41.     function StringBuilder:__tostring()
  42.         if self:_Compact() then
  43.             return self.fragments[1]
  44.         else
  45.             return ""
  46.         end
  47.     end
  48.  
  49.     function StringBuilder:__len()
  50.         if self.length < 0 then
  51.             if #self.fragments == 1 then
  52.                 self.length = self.fragments[1]:len()
  53.             else
  54.                 self.length = sum(self.fragments, function (v) return v:len() end, ipairs)
  55.             end
  56.         end
  57.         return self.length
  58.     end
  59.  
  60.     --[[-------------------------------------------------------------------\\--
  61.         :: Methods: Private
  62.     --\\-------------------------------------------------------------------]]--
  63.  
  64.     function StringBuilder:_Compact()
  65.         local count = #self.fragments
  66.         if count == 0 then
  67.             return false
  68.         elseif count > 1 then
  69.             self.fragments = { concat(self.fragments, "") }
  70.             self.length = self.fragments[1]:len()
  71.         end
  72.         return true
  73.     end
  74.  
  75.     --[[-------------------------------------------------------------------\\--
  76.         :: Methods: Public
  77.     --\\-------------------------------------------------------------------]]--
  78.  
  79.     function StringBuilder:Apply(func)
  80.         check(type(func) == 'function',
  81.               '[StringBuilder:Apply]: Invalid argument #1; function expected got %s.',
  82.               type(func))
  83.  
  84.         local result = func(self:__tostring())
  85.         if type(result) == 'string' then
  86.             self:Clear()
  87.             self:Append(result)
  88.         end
  89.         return self
  90.     end
  91.  
  92.     function StringBuilder:ToString()
  93.         return self:__tostring()
  94.     end
  95.  
  96.     function StringBuilder:Clear()
  97.         self.fragments = {}
  98.         return self
  99.     end
  100.  
  101.     function StringBuilder:Reset()
  102.         self.fragments = {}
  103.         self.length = 0
  104.         self.indent = { d = 0, t = "", l = 0 }
  105.         return self
  106.     end
  107.  
  108.     function StringBuilder:Length()
  109.         return self:__len()
  110.     end
  111.  
  112.     function StringBuilder:AppendIndent()
  113.         if self.indent.d > 0 then
  114.             insert(self.fragments, self.indent.t)
  115.         end
  116.         return self
  117.     end
  118.  
  119.     function StringBuilder:Append(...)
  120.         check(... ~= nil,
  121.               "[StringBuilder:Append]: No arguments passed.")
  122.         -- 2012-07-13 10:49: ~Xetrill: the next line may fail frequently because of luabind
  123.         --  whenever a userdata object doesn't define a __tostring function.
  124.         --  so using pcall might be a better idea.
  125.         local args = map({ ... }, tostring)
  126.         for _, v in ipairs(args) do
  127.             local l = v:len()
  128.             if l > 0 then
  129.                 insert(self.fragments, v)
  130.                 self.length = self.length + l
  131.             end
  132.         end
  133.         return self
  134.     end
  135.  
  136.     function StringBuilder:AppendLine(...)
  137.         self:AppendIndent()
  138.         if ... ~= nil then
  139.             self:Append(...)
  140.         end
  141.         return self:Append("\n")
  142.     end
  143.  
  144.     function StringBuilder:AppendLineIf(condition, ...)
  145.         if not condition then
  146.             return self
  147.         end
  148.         return self:AppendLine(...)
  149.     end
  150.  
  151.     function StringBuilder:AppendIf(condition, ...)
  152.         if not condition then
  153.             return self
  154.         end
  155.         return self:Append(...)
  156.     end
  157.  
  158.     function StringBuilder:AppendIfOr(condition, ontrue, onfalse)
  159.         if condition then
  160.             self:Append(ontrue)
  161.         else
  162.             self:Append(onfalse)
  163.         end
  164.         return self
  165.     end
  166.  
  167.     function StringBuilder:AppendFormat(fmt, ...)
  168.         return self:Append(fmt:format(...))
  169.     end
  170.  
  171.     function StringBuilder:AppendFormatIf(condition, fmt, ...)
  172.         if not condition then
  173.             return self
  174.         end
  175.         return self:Append(fmt:format(...))
  176.     end
  177.  
  178.     function StringBuilder:AppendRepeat(s, n, sep)
  179.         if not s or tonumber(n) <= 0 then
  180.             return self
  181.         end
  182.         return self:Append(tostring(text):rep(n, sep))
  183.     end
  184.  
  185.     -- Appends a special string which tells the game to change text foreground
  186.     -- color.
  187.     -- Note that if numbers are passed they wont be checked.
  188.     -- @param ... string|number  Can be on of the following four ways:
  189.     --   nil:   Nothing or nil which fill default to emit "default".
  190.     --   string: A single string - the name of the color to use.
  191.     --   3x int: A RGB color.
  192.     --   4x int: A ARGB color.
  193.     function StringBuilder:AppendColor(...)
  194.         local count = select("#", ...)
  195.         if count == 0 then
  196.             self:Append("%c[default]")
  197.         elseif count == 1 then
  198.             self:Append(join("", "%c[", tostring(select(1, ...)), "]"))
  199.         elseif count == 3 then
  200.             self:Append(join("", "%c[255,", join(",", ...), "]"))
  201.         elseif count == 4 then
  202.             self:Append(join("", "%c[", join(",", ...), "]"))
  203.         else
  204.             self:Append("%c[255,255,0,255]")
  205.         end
  206.         return self
  207.     end
  208.  
  209.     function StringBuilder:AppendLocalized(key)
  210.         return self:Append(translate(key))
  211.     end
  212.  
  213.     -- TODO: give option to localize or better do it implicitly
  214.     function StringBuilder:_InsertThousandSeparator(amount)
  215.         local formatted = tostring(amount)
  216.         while true do
  217.             formatted, c = formatted:gsub("^(-?%d+)(%d%d%d)", "%1,%2")
  218.             if c == 0 then
  219.                 break
  220.             end
  221.         end
  222.         return formatted
  223.     end
  224.  
  225.     function StringBuilder:_Round(val, decimal)
  226.         if decimal then
  227.             return floor((val * 10 ^ decimal) + 0.5) / (10 ^ decimal)
  228.         else
  229.             return floor(val + 0.5)
  230.         end
  231.     end
  232.  
  233.     function StringBuilder:AppendMoney(amount, color)
  234.         if color == nil then
  235.             self:AppendNumber(amount)
  236.         else
  237.             self:AppendColor(color)
  238.                 :AppendNumber(amount)
  239.                 :AppendColor()
  240.         end
  241.         return self:Append(" RU")
  242.     end
  243.  
  244.     -- @author sam_lie
  245.     -- @see http:--lua-users.org/wiki/FormattingNumbers
  246.     function StringBuilder:AppendNumber(amount, decimal, prefix, neg_prefix)
  247.         local str_amount, formatted, famount, remainder
  248.  
  249.         prefix   = prefix or ""
  250.         decimal = decimal or 2
  251.         neg_prefix = neg_prefix or "-"
  252.  
  253.         famount = floor(abs(self:_Round(amount, decimal)))
  254.  
  255.         -- comma to separate the thousands
  256.         formatted = self:_InsertThousandSeparator(famount)
  257.  
  258.         -- attach the decimal portion
  259.         if decimal > 0 then
  260.             remainder = tostring(self:_Round(abs(amount) - famount, decimal)):sub(3)
  261.             formatted = join("", formatted, ".", remainder, srepeat("0", decimal - remainder:len()))
  262.         end
  263.  
  264.         -- if value is negative then format accordingly
  265.         if amount < 0 then
  266.             if neg_prefix == "()" then
  267.                 formatted = join(formatted, "(", ")")
  268.             else
  269.                 formatted = join("", neg_prefix, formatted)
  270.             end
  271.         end
  272.  
  273.         -- attach prefix string e.g '$'
  274.         return self:Append(prefix, formatted)
  275.     end
  276.  
  277.     function StringBuilder:IsEmpty()
  278.         if #self.fragments == 0 then
  279.             return true
  280.         else
  281.             return self:__len() == 0
  282.         end
  283.     end
  284.  
  285.     function StringBuilder:GetAt(index)
  286.         if #self.fragments == 1 then
  287.             return self.fragments[1]:sub(index, index)
  288.         end
  289.         local len, head, tail, rel = 0, 0, 0, 0
  290.         for _, fragment in ipairs(self.fragments) do
  291.             len  = fragment:len()
  292.             head = tail
  293.             tail = tail + len
  294.             if index >= head and index <= tail then
  295.                 rel = index - head
  296.                 return fragment:sub(rel, rel)
  297.             end
  298.         end
  299.         return nil
  300.     end
  301.  
  302.     function StringBuilder:Replace(pattern, replacement, count)
  303.         if self:_Compact() then
  304.             self.fragments[1] = self.fragments[1]:gsub(pattern, replacement, count)
  305.             self.length = -1
  306.         end
  307.         return self
  308.     end
  309.  
  310.     function StringBuilder:Insert(index, value)
  311.         value = tostring(value)
  312.         if value:len() == 0 then
  313.             return self
  314.         end
  315.  
  316.         if index == 1 then
  317.             insert(self.fragments, 1, value)
  318.         elseif index == self:Length() then
  319.             self:Append(value)
  320.         else
  321.             -- TODO: optimize, this is IMO too wasteful
  322.             --  A proper way would be to find the fragment the index belongs
  323.             --  to and then create a head and tail from that fragment.
  324.             --  Once that's done the head becomes the fragment and the value
  325.             --  and tail will be inserted via the insert function.
  326.             -- Note: GetAt shows how to map the index / identify the fragment.
  327.             if self:_Compact() then
  328.                 local head = self.fragments[1]:sub(1, index)
  329.                 local tail = self.fragments[1]:sub(index)
  330.                 self.fragments[1] = join(value, head, tail)
  331.             end
  332.         end
  333.         self.length = self.length + value:len()
  334.  
  335.         return self
  336.     end
  337.  
  338.     function StringBuilder:AdjustIndent(adjustment)
  339.         adjustment = tonumber(adjustment)
  340.         if adjustment == 0 then
  341.             return
  342.         end
  343.         local d = self.indent.d + adjustment
  344.         local t = srepeat(" ", d)
  345.         local l = t:len()
  346.         self.indent = { d = d, t = t, l = l }
  347.         return self
  348.     end
  349.  
  350.     function StringBuilder:IncreaseIndent()
  351.         return self:AdjustIndent(1)
  352.     end
  353.  
  354.     function StringBuilder:DecreaseIndent()
  355.         return self:AdjustIndent(-1)
  356.     end
  357.  
  358. --[[
  359.     function StringBuilder:Trim(...)
  360.     end
  361.  
  362.     function StringBuilder:Substring(startIndex, endIndex)
  363.     end
  364.  
  365.     function StringBuilder:Remove(startIndex, length)
  366.     end
  367. ]]--
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement