lib/core: Allow comparison operators to work on any subclass of FlatText
[nit.git] / lib / core / text / flat.nit
index 5bc76fc..4d35126 100644 (file)
@@ -36,18 +36,18 @@ end
 
 redef class FlatText
 
-       private fun first_byte: Int do return 0
+       fun first_byte: Int do return 0
 
-       private fun last_byte: Int do return _bytelen - 1
+       fun last_byte: Int do return _bytelen - 1
 
        # Cache of the latest position (char) explored in the string
-       private var position: Int = 0
+       var position: Int = 0
 
        # Cached position (bytes) in the NativeString underlying the String
-       private var bytepos: Int = 0
+       var bytepos: Int = 0
 
        # Index of the character `index` in `_items`
-       private fun char_to_byte_index(index: Int): Int do
+       fun char_to_byte_index(index: Int): Int do
                var ln = length
                assert index >= 0
                assert index < ln
@@ -85,12 +85,110 @@ redef class FlatText
                return ns_i
        end
 
+       # By escaping `self` to HTML, how many more bytes will be needed ?
+       fun chars_to_html_escape: Int do
+               var its = _items
+               var max = last_byte
+               var pos = first_byte
+               var endlen = 0
+               while pos <= max do
+                       var c = its[pos]
+                       if c == 0x3Cu8 then
+                               endlen += 3
+                       else if c == 0x3Eu8 then
+                               endlen += 3
+                       else if c == 0x26u8 then
+                               endlen += 4
+                       else if c == 0x22u8 then
+                               endlen += 4
+                       else if c == 0x27u8 then
+                               endlen += 4
+                       else if c == 0x2Fu8 then
+                               endlen += 4
+                       end
+                       pos += 1
+               end
+               return endlen
+       end
+
+       redef fun html_escape
+       do
+               var extra = chars_to_html_escape
+               if extra == 0 then return to_s
+               var its = _items
+               var max = last_byte
+               var pos = first_byte
+               var nlen = extra + _bytelen
+               var nits = new NativeString(nlen)
+               var outpos = 0
+               while pos <= max do
+                       var c = its[pos]
+                       # Special codes:
+                       # Some HTML characters are used as meta-data, they need
+                       # to be replaced by an HTML-Escaped equivalent
+                       #
+                       # * 0x3C (<) => &lt;
+                       # * 0x3E (>) => &gt;
+                       # * 0x26 (&) => &amp;
+                       # * 0x22 (") => &#34;
+                       # * 0x27 (') => &#39;
+                       # * 0x2F (/) => &#47;
+                       if c == 0x3Cu8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x6Cu8
+                               nits[outpos + 2] = 0x74u8
+                               nits[outpos + 3] = 0x3Bu8
+                               outpos += 4
+                       else if c == 0x3Eu8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x67u8
+                               nits[outpos + 2] = 0x74u8
+                               nits[outpos + 3] = 0x3Bu8
+                               outpos += 4
+                       else if c == 0x26u8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x61u8
+                               nits[outpos + 2] = 0x6Du8
+                               nits[outpos + 3] = 0x70u8
+                               nits[outpos + 4] = 0x3Bu8
+                               outpos += 5
+                       else if c == 0x22u8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x23u8
+                               nits[outpos + 2] = 0x33u8
+                               nits[outpos + 3] = 0x34u8
+                               nits[outpos + 4] = 0x3Bu8
+                               outpos += 5
+                       else if c == 0x27u8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x23u8
+                               nits[outpos + 2] = 0x33u8
+                               nits[outpos + 3] = 0x39u8
+                               nits[outpos + 4] = 0x3Bu8
+                               outpos += 5
+                       else if c == 0x2Fu8 then
+                               nits[outpos] = 0x26u8
+                               nits[outpos + 1] = 0x23u8
+                               nits[outpos + 2] = 0x34u8
+                               nits[outpos + 3] = 0x37u8
+                               nits[outpos + 4] = 0x3Bu8
+                               outpos += 5
+                       else
+                               nits[outpos] = c
+                               outpos += 1
+                       end
+                       pos += 1
+               end
+               var s = new FlatString.with_infos(nits, nlen, 0, nlen - 1)
+               return s
+       end
+
        # By escaping `self` to C, how many more bytes will be needed ?
        #
        # This enables a double-optimization in `escape_to_c` since if this
        # method returns 0, then `self` does not need escaping and can be
        # returned as-is
-       protected fun chars_to_escape_to_c: Int do
+       fun chars_to_escape_to_c: Int do
                var its = _items
                var max = last_byte
                var pos = first_byte
@@ -314,7 +412,7 @@ class FlatString
 
        redef fun ==(other)
        do
-               if not other isa FlatString then return super
+               if not other isa FlatText then return super
 
                if self.object_id == other.object_id then return true
 
@@ -323,7 +421,7 @@ class FlatString
                if other._bytelen != my_length then return false
 
                var my_index = _first_byte
-               var its_index = other._first_byte
+               var its_index = other.first_byte
 
                var last_iteration = my_index + my_length
 
@@ -341,29 +439,32 @@ class FlatString
 
        redef fun <(other)
        do
-               if not other isa FlatString then return super
+               if not other isa FlatText then return super
 
                if self.object_id == other.object_id then return false
 
-               var my_length = self._bytelen
-               var its_length = other._bytelen
+               var myits = _items
+               var itsits = other._items
 
-               var max = if my_length < its_length then my_length else its_length
+               var mbt = _bytelen
+               var obt = other.bytelen
 
-               var myits = self.bytes
-               var itsits = other.bytes
+               var minln = if mbt < obt then mbt else obt
+               var mst = _first_byte
+               var ost = other.first_byte
 
-               for i in [0 .. max[ do
-                       var my_curr_char = myits[i]
-                       var its_curr_char = itsits[i]
+               for i in [0 .. minln[ do
+                       var my_curr_char = myits[mst]
+                       var its_curr_char = itsits[ost]
 
-                       if my_curr_char != its_curr_char then
-                               if my_curr_char < its_curr_char then return true
-                               return false
-                       end
+                       if my_curr_char > its_curr_char then return false
+                       if my_curr_char < its_curr_char then return true
+
+                       mst += 1
+                       ost += 1
                end
 
-               return my_length < its_length
+               return mbt < obt
        end
 
        redef fun +(o) do
@@ -435,11 +536,6 @@ private class FlatStringCharReverseIterator
 
        var curr_pos: Int
 
-       init with_pos(tgt: FlatString, pos: Int)
-       do
-               init(tgt, pos)
-       end
-
        redef fun is_ok do return curr_pos >= 0
 
        redef fun item do return target[curr_pos]
@@ -455,14 +551,11 @@ private class FlatStringCharIterator
 
        var target: FlatString
 
-       var max: Int
+       var max: Int is noautoinit
 
        var curr_pos: Int
 
-       init with_pos(tgt: FlatString, pos: Int)
-       do
-               init(tgt, tgt.length - 1, pos)
-       end
+       init do max = target.length - 1
 
        redef fun is_ok do return curr_pos <= max
 
@@ -481,9 +574,9 @@ private class FlatStringCharView
 
        redef fun [](index) do return target[index]
 
-       redef fun iterator_from(start) do return new FlatStringCharIterator.with_pos(target, start)
+       redef fun iterator_from(start) do return new FlatStringCharIterator(target, start)
 
-       redef fun reverse_iterator_from(start) do return new FlatStringCharReverseIterator.with_pos(target, start)
+       redef fun reverse_iterator_from(start) do return new FlatStringCharReverseIterator(target, start)
 
 end
 
@@ -492,13 +585,15 @@ private class FlatStringByteReverseIterator
 
        var target: FlatString
 
-       var target_items: NativeString
+       var target_items: NativeString is noautoinit
 
        var curr_pos: Int
 
-       init with_pos(tgt: FlatString, pos: Int)
+       init
        do
-               init(tgt, tgt._items, pos + tgt._first_byte)
+               var tgt = target
+               target_items = tgt._items
+               curr_pos += tgt._first_byte
        end
 
        redef fun is_ok do return curr_pos >= target._first_byte
@@ -516,13 +611,15 @@ private class FlatStringByteIterator
 
        var target: FlatString
 
-       var target_items: NativeString
+       var target_items: NativeString is noautoinit
 
        var curr_pos: Int
 
-       init with_pos(tgt: FlatString, pos: Int)
+       init
        do
-               init(tgt, tgt._items, pos + tgt._first_byte)
+               var tgt = target
+               target_items = tgt._items
+               curr_pos += tgt._first_byte
        end
 
        redef fun is_ok do return curr_pos <= target._last_byte
@@ -551,9 +648,9 @@ private class FlatStringByteView
                return target._items[ind]
        end
 
-       redef fun iterator_from(start) do return new FlatStringByteIterator.with_pos(target, start)
+       redef fun iterator_from(start) do return new FlatStringByteIterator(target, start)
 
-       redef fun reverse_iterator_from(start) do return new FlatStringByteReverseIterator.with_pos(target, start)
+       redef fun reverse_iterator_from(start) do return new FlatStringByteReverseIterator(target, start)
 
 end
 
@@ -780,18 +877,15 @@ class FlatBuffer
                assert count >= 0
                if from < 0 then from = 0
                if (from + count) > length then count = length - from
-               if count != 0 then
-                       var its = _items
-                       var bytefrom = its.char_to_byte_index(from)
-                       var byteto = its.char_to_byte_index(count + from - 1)
-                       byteto += its.char_at(byteto).u8char_len - 1
-                       var byte_length = byteto - bytefrom + 1
-                       var r_items = new NativeString(byte_length)
-                       its.copy_to(r_items, byte_length, bytefrom, 0)
-                       return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
-               else
-                       return new Buffer
-               end
+               if count <= 0 then return new Buffer
+               var its = _items
+               var bytefrom = its.char_to_byte_index(from)
+               var byteto = its.char_to_byte_index(count + from - 1)
+               byteto += its.char_at(byteto).u8char_len - 1
+               var byte_length = byteto - bytefrom + 1
+               var r_items = new NativeString(byte_length)
+               its.copy_to(r_items, byte_length, bytefrom, 0)
+               return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
        end
 
        redef fun reverse
@@ -829,14 +923,11 @@ private class FlatBufferByteReverseIterator
 
        var target: FlatBuffer
 
-       var target_items: NativeString
+       var target_items: NativeString is noautoinit
 
        var curr_pos: Int
 
-       init with_pos(tgt: FlatBuffer, pos: Int)
-       do
-               init(tgt, tgt._items, pos)
-       end
+       init do target_items = target._items
 
        redef fun index do return curr_pos
 
@@ -855,9 +946,9 @@ private class FlatBufferByteView
 
        redef fun [](index) do return target._items[index]
 
-       redef fun iterator_from(pos) do return new FlatBufferByteIterator.with_pos(target, pos)
+       redef fun iterator_from(pos) do return new FlatBufferByteIterator(target, pos)
 
-       redef fun reverse_iterator_from(pos) do return new FlatBufferByteReverseIterator.with_pos(target, pos)
+       redef fun reverse_iterator_from(pos) do return new FlatBufferByteReverseIterator(target, pos)
 
 end
 
@@ -866,14 +957,11 @@ private class FlatBufferByteIterator
 
        var target: FlatBuffer
 
-       var target_items: NativeString
+       var target_items: NativeString is noautoinit
 
        var curr_pos: Int
 
-       init with_pos(tgt: FlatBuffer, pos: Int)
-       do
-               init(tgt, tgt._items, pos)
-       end
+       init do target_items = target._items
 
        redef fun index do return curr_pos
 
@@ -892,11 +980,6 @@ private class FlatBufferCharReverseIterator
 
        var curr_pos: Int
 
-       init with_pos(tgt: FlatBuffer, pos: Int)
-       do
-               init(tgt, pos)
-       end
-
        redef fun index do return curr_pos
 
        redef fun is_ok do return curr_pos >= 0
@@ -946,9 +1029,9 @@ private class FlatBufferCharView
                for i in s do target.add i
        end
 
-       redef fun iterator_from(pos) do return new FlatBufferCharIterator.with_pos(target, pos)
+       redef fun iterator_from(pos) do return new FlatBufferCharIterator(target, pos)
 
-       redef fun reverse_iterator_from(pos) do return new FlatBufferCharReverseIterator.with_pos(target, pos)
+       redef fun reverse_iterator_from(pos) do return new FlatBufferCharReverseIterator(target, pos)
 
 end
 
@@ -957,14 +1040,11 @@ private class FlatBufferCharIterator
 
        var target: FlatBuffer
 
-       var max: Int
+       var max: Int is noautoinit
 
        var curr_pos: Int
 
-       init with_pos(tgt: FlatBuffer, pos: Int)
-       do
-               init(tgt, tgt.length - 1, pos)
-       end
+       init do max = target.length - 1
 
        redef fun index do return curr_pos
 
@@ -1148,8 +1228,9 @@ redef class Array[E]
        do
                var l = length
                if l == 0 then return ""
-               if l == 1 then if self[0] == null then return "" else return self[0].to_s
-               var its = _items
+               var its = _items.as(not null)
+               var first = its[0]
+               if l == 1 then if first == null then return "" else return first.to_s
                var na = new NativeArray[String](l)
                var i = 0
                var sl = 0