note description: "[ A JSON_STRING represent a string in JSON. A string is a collection of zero or more Unicodes characters, wrapped in double quotes, using blackslash espaces. ]" author: "Javier Velilla" date: "2008/08/24" revision: "Revision 0.1" license:"MIT (see http://www.opensource.org/licenses/mit-license.php)" class JSON_STRING inherit JSON_VALUE redefine is_equal end create make_json, make_json_from_string_32, make_with_escaped_json convert make_json ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8}) feature {NONE} -- Initialization make_json (an_item: READABLE_STRING_8) -- Initialize. require item_not_void: an_item /= Void do make_with_escaped_json (escaped_json_string (an_item)) end make_json_from_string_32 (an_item: READABLE_STRING_32) -- Initialize. require item_not_void: an_item /= Void do make_with_escaped_json (escaped_json_string_32 (an_item)) end make_with_escaped_json (an_item: READABLE_STRING_8) -- Initialize with an_item already escaped require item_not_void: an_item /= Void do item := an_item end feature -- Access item: STRING -- Contents unescaped_string: STRING_8 local s: like item i, n: INTEGER c: CHARACTER do s := item n := s.count create Result.make (n) from i := 1 until i > n loop c := s[i] if c = '\' then if i < n then inspect s[i+1] when '\' then Result.append_character ('\') i := i + 2 when '%"' then Result.append_character ('%"') i := i + 2 when 'n' then Result.append_character ('%N') i := i + 2 when 'r' then Result.append_character ('%R') i := i + 2 when 'u' then --| Leave unicode \uXXXX unescaped Result.append_character ('\') i := i + 1 else Result.append_character ('\') i := i + 1 end else Result.append_character ('\') i := i + 1 end else Result.append_character (c) i := i + 1 end end end unescaped_string_32: STRING_32 local s: like item i, n: INTEGER c: CHARACTER hex: STRING do s := item n := s.count create Result.make (n) from i := 1 until i > n loop c := s[i] if c = '\' then if i < n then inspect s[i+1] when '\' then Result.append_character ('\') i := i + 2 when '%"' then Result.append_character ('%"') i := i + 2 when 'n' then Result.append_character ('%N') i := i + 2 when 'r' then Result.append_character ('%R') i := i + 2 when 'u' then hex := s.substring (i+2, i+2+4 - 1) if hex.count = 4 then Result.append_code (hexadecimal_to_natural_32 (hex)) end i := i + 2 + 4 else Result.append_character ('\') i := i + 1 end else Result.append_character ('\') i := i + 1 end else Result.append_character (c.to_character_32) i := i + 1 end end end representation: STRING do Result := "%"" Result.append (item) Result.append_character ('%"') end feature -- Visitor pattern accept (a_visitor: JSON_VISITOR) -- Accept `a_visitor'. -- (Call `visit_json_string' procedure on `a_visitor'.) do a_visitor.visit_json_string (Current) end feature -- Comparison is_equal (other: like Current): BOOLEAN -- Is JSON_STRING made of same character sequence as `other' -- (possibly with a different capacity)? do Result := item.is_equal (other.item) end feature -- Change Element append (a_string: STRING) -- Add an_item require a_string_not_void: a_string /= Void do item.append_string (a_string) end feature -- Status report hash_code: INTEGER -- Hash code value do Result := item.hash_code end feature -- Status report debug_output: STRING -- String that should be displayed in debugger to represent `Current'. do Result := item end feature {NONE} -- Implementation is_hexadecimal (s: READABLE_STRING_8): BOOLEAN do Result := across s as scur all scur.item.is_hexa_digit end end hexadecimal_to_natural_32 (s: READABLE_STRING_8): NATURAL_32 -- Hexadecimal string `s' converted to NATURAL_32 value require s_not_void: s /= Void is_hexadecimal: is_hexadecimal (s) local i, nb: INTEGER char: CHARACTER do nb := s.count if nb >= 2 and then s.item (2) = 'x' then i := 3 else i := 1 end from until i > nb loop Result := Result * 16 char := s.item (i) if char >= '0' and then char <= '9' then Result := Result + (char |-| '0').to_natural_32 else Result := Result + (char.lower |-| 'a' + 10).to_natural_32 end i := i + 1 end end escaped_json_string (s: READABLE_STRING_8): STRING_8 -- JSON string with '"' and '\' characters escaped require s_not_void: s /= Void local i, n: INTEGER c: CHARACTER_8 do n := s.count create Result.make (n + n // 10) from i := 1 until i > n loop c := s.item (i) inspect c when '%"' then Result.append_string ("\%"") when '\' then Result.append_string ("\\") when '%R' then Result.append_string ("\r") when '%N' then Result.append_string ("\n") else Result.extend (c) end i := i + 1 end end escaped_json_string_32 (s: READABLE_STRING_32): STRING_8 -- JSON string with '"' and '\' characters and unicode escaped require s_not_void: s /= Void local i, j, n: INTEGER uc: CHARACTER_32 c: CHARACTER_8 h: STRING_8 do n := s.count create Result.make (n + n // 10) from i := 1 until i > n loop uc := s.item (i) if uc.is_character_8 then c := uc.to_character_8 inspect c when '%"' then Result.append_string ("\%"") when '\' then Result.append_string ("\\") when '%R' then Result.append_string ("\r") when '%N' then Result.append_string ("\n") else Result.extend (c) end else Result.append ("\u") h := uc.code.to_hex_string -- Remove first 0 and keep 4 hexa digit from j := 1 until h.count = 4 or (j <= h.count and then h.item (j) /= '0') loop j := j + 1 end h := h.substring (j, h.count) from until h.count >= 4 loop h.prepend_integer (0) end check h.count = 4 end Result.append (h) end i := i + 1 end end invariant value_not_void: item /= Void end