VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gitem.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gitem.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gitem.h"
23 #include "gstr.h"
24 #include <algorithm>
25 #include <sstream>
26 #include <stdexcept>
27 
29 {
30  return Item(t_list) ;
31 }
32 
34 {
35  return Item(t_map) ;
36 }
37 
39 {
40  return Item(t_string) ;
41 }
42 
43 G::Item G::Item::parse( const std::string & s )
44 {
45  std::istringstream ss ;
46  ss.str( s ) ;
47  Item item ;
48  ss >> item ;
49  return item ;
50 }
51 
52 bool G::Item::parse( const std::string & s , Item & out )
53 {
54  try
55  {
56  std::istringstream ss ;
57  ss.str( s ) ;
58  ss >> out ;
59  return true ;
60  }
61  catch( std::exception & )
62  {
63  return false ;
64  }
65 }
66 
67 std::string G::Item::str( int indent ) const
68 {
69  std::ostringstream ss ;
70  out( ss , indent ) ;
71  return ss.str() ;
72 }
73 
74 G::Item::Item( Type type ) :
75  m_type(type)
76 {
77 }
78 
79 G::Item::Item( const std::string & s ) :
80  m_type(t_string) ,
81  m_string(s)
82 {
83 }
84 
85 G::Item::Type G::Item::type() const
86 {
87  return m_type ;
88 }
89 
90 const G::Item & G::Item::operator[]( const std::string & k ) const
91 {
92  check( t_map ) ; need( k ) ;
93  return (*m_map.find(k)).second ;
94 }
95 
96 G::Item & G::Item::operator[]( const std::string & k )
97 {
98  check( t_map ) ; update( k ) ;
99  return m_map[k] ;
100 }
101 
103 {
104  check( t_list ) ; need( i ) ;
105  return m_list.at(i) ;
106 }
107 
108 void G::Item::add( const std::string & s )
109 {
110  add( Item(s) ) ;
111 }
112 
114 {
115  check( t_list ) ;
116  m_list.push_back( i ) ;
117 }
118 
119 void G::Item::add( const std::string & k , const std::string & s )
120 {
121  add( k , Item(s) ) ;
122 }
123 
124 void G::Item::add( const std::string & k , const Item & i )
125 {
126  check( t_map ) ; update( k ) ;
127  m_map[k] = i ;
128 }
129 
130 void G::Item::remove( const std::string & k )
131 {
132  Map::iterator p = m_map.find( k ) ;
133  if( p != m_map.end() )
134  {
135  m_map.erase( p ) ;
136  m_keys.erase( std::remove(m_keys.begin(),m_keys.end(),k) , m_keys.end() ) ;
137  }
138 }
139 
140 void G::Item::update( const std::string & k )
141 {
142  if( std::find(m_keys.begin(),m_keys.end(),k) == m_keys.end() )
143  m_keys.push_back( k ) ;
144 }
145 
146 bool G::Item::has( const std::string & k ) const
147 {
148  check( t_map ) ;
149  return std::find(m_keys.begin(),m_keys.end(),k) != m_keys.end() ;
150 }
151 
153 {
154  check( t_map ) ;
155  Item out( t_list ) ;
156  out.m_list.reserve( m_keys.size() ) ;
157  for( std::vector<std::string>::const_iterator p = m_keys.begin() ; p != m_keys.end() ; ++p )
158  out.add( *p ) ;
159  return out ;
160 }
161 
162 size_t G::Item::size() const
163 {
164  checkNot( t_string ) ;
165  return std::max(m_list.size(),m_map.size()) ;
166 }
167 
168 bool G::Item::empty() const
169 {
170  if( m_type == t_list )
171  return m_list.empty() ;
172  else if( m_type == t_map)
173  return m_map.empty() ;
174  else
175  return m_string.empty() ;
176 }
177 
178 const G::Item & G::Item::operator[]( size_t i ) const
179 {
180  check( t_list ) ; need( i ) ;
181  return m_list.at(i) ;
182 }
183 
184 std::string G::Item::operator()() const
185 {
186  check( t_string ) ;
187  return m_string ;
188 }
189 
190 void G::Item::checkNot( Type type ) const
191 {
192  if( m_type == type )
193  throw Error("type mismatch") ;
194  check() ;
195 }
196 
197 void G::Item::check( Type type ) const
198 {
199  if( m_type != type )
200  throw Error("type mismatch") ;
201  check() ;
202 }
203 
204 void G::Item::check() const
205 {
206  if( m_type == t_map && m_keys.size() != m_map.size() )
207  throw Error("internal error") ;
208  if( m_map.size() != 0U && m_list.size() != 0U )
209  throw Error("internal error") ;
210 }
211 
212 void G::Item::need( const std::string & k ) const
213 {
214  if( m_map.find(k) == m_map.end() )
215  throw Error("item not found",k) ;
216 }
217 
218 void G::Item::need( size_t i ) const
219 {
220  if( i >= m_list.size() )
221  throw Error("invalid index") ;
222 }
223 
224 bool G::Item::less( const Item & a , const Item & b )
225 {
226  return a.m_string < b.m_string ;
227 }
228 
229 namespace
230 {
231  std::string sp0( int indent )
232  {
233  if( indent < 0 ) return " " ;
234  return "\n" + std::string(indent*2,' ') ;
235  }
236  std::string sp( int indent )
237  {
238  if( indent < 0 ) return ", " ;
239  return ",\n" + std::string(indent*2,' ') ;
240  }
241  std::string spx( int indent )
242  {
243  if( indent < 0 ) return " " ;
244  if( indent ) indent-- ;
245  return "\n" + std::string(indent*2,' ') ;
246  }
247 }
248 
249 void G::Item::out( std::ostream & s , int indent ) const
250 {
251  if( m_type == t_string )
252  {
253  static const std::string specials_in( "\r\n\t" , 4U ) ; // includes nul
254  static const std::string specials_out( "rnt0" ) ;
255  s << "\"" << G::Str::escaped(m_string,'\\',specials_in,specials_out) << "\"" ;
256  }
257  else if( m_type == t_list )
258  {
259  s << "[" ;
260  std::string sep = sp0(indent) ;
261  for( size_t i = 0U ; i < m_list.size() ; i++ , sep = sp(indent) )
262  {
263  s << sep ;
264  m_list.at(i).out( s , indent+(indent>0) ) ;
265  }
266  s << spx(indent) << "]" ;
267  }
268  else if( m_type == t_map )
269  {
270  s << "{" ;
271  std::string sep = sp0(indent) ;
272  for( std::vector<std::string>::const_iterator p = m_keys.begin() ; p != m_keys.end() ; ++p , sep = sp(indent) )
273  {
274  s << sep << "\"" << *p << "\" : " ;
275  if( m_map.find(*p) != m_map.end() ) // always true
276  (*m_map.find(*p)).second.out( s , indent+(indent>0) ) ;
277  }
278  s << spx(indent) << "}" ;
279  }
280 }
281 
282 void G::Item::clear( Type type )
283 {
284  m_string.clear() ;
285  m_map.clear() ;
286  m_keys.clear() ;
287  m_list.clear() ;
288  m_type = type ;
289 }
290 
291 void G::Item::read( std::istream & stream , char c )
292 {
293  for(;;)
294  {
295  std::string part ;
296  if( !std::getline(stream,part,c) )
297  throw Error() ; // (eof is an error)
298  m_string.append( part ) ;
299  if( !escaped(m_string) )
300  break ;
301  m_string.append( 1U , c ) ;
302  }
303  G::Str::unescape( m_string , '\\' , "rnt0" , "\r\n\t" ) ;
304 }
305 
306 bool G::Item::escaped( const std::string & s )
307 {
308  typedef std::string::const_iterator ptr_t ;
309  bool e = false ;
310  for( ptr_t p = s.begin() ; p != s.end() ; ++p )
311  {
312  if( *p == '\\' )
313  e = !e ;
314  else
315  e = false ;
316  }
317  return e ;
318 }
319 
320 void G::Item::readn( std::istream & s , char c0 )
321 {
322  m_string = std::string( 1U , c0 ) ;
323  for(;;)
324  {
325  char c = '\0' ;
326  c = s.get() ;
327  if( c >= '0' && c <= '9' )
328  {
329  m_string.append( 1U , c ) ;
330  }
331  else
332  {
333  s.unget() ;
334  break ;
335  }
336  }
337 }
338 
339 void G::Item::in( std::istream & stream )
340 {
341  stream.setf( std::ios_base::skipws ) ;
342  clear() ;
343  char c ;
344  stream >> c ;
345  if( !stream.good() )
346  {
347  }
348  else if( c == '"' || c == '\'' )
349  {
350  m_type = t_string ;
351  read( stream , c ) ;
352  }
353  else if( c >= '0' && c <= '9' )
354  {
355  m_type = t_string ;
356  readn( stream , c ) ;
357  }
358  else if( c == '[' )
359  {
360  m_type = t_list ;
361  for(;;)
362  {
363  m_list.push_back( Item() ) ;
364  stream >> m_list.back() ;
365  stream >> c ;
366  if( c == ']' ) break ;
367  if( c != ',' ) throw ParseError() ;
368  }
369  }
370  else if( c == '{' )
371  {
372  m_type = t_map ;
373  for(;;)
374  {
375  stream >> c ;
376  if( c == '}' ) break ;
377  if( c != '"' && c != '\'' ) throw ParseError() ;
378  Item key( t_string ) ;
379  key.read( stream , c ) ;
380  if( key.m_string.empty() ) throw ParseError() ;
381  stream >> c ;
382  if( c != ':' ) throw ParseError() ;
383  Item value ;
384  stream >> value ;
385  update(key.m_string) ;
386  m_map[key.m_string] = value ;
387  stream >> c ;
388  if( c == '}' ) break ;
389  if( c != ',' ) throw ParseError() ;
390  }
391  }
392  else
393  {
394  throw ParseError() ;
395  }
396 }
397 
398 /// \file gitem.cpp
void remove(const std::string &k)
Removes an item from this map item.
Definition: gitem.cpp:130
void out(std::ostream &s, int indent=-1) const
Does output streaming, using a json-like format.
Definition: gitem.cpp:249
static Item map()
Factory function for a map item.
Definition: gitem.cpp:33
size_t size() const
Returns the size of this map-or-list item.
Definition: gitem.cpp:162
bool empty() const
Returns true if an empty item.
Definition: gitem.cpp:168
void in(std::istream &s)
Does input parsing. Throws on error.
Definition: gitem.cpp:339
static std::string escaped(const std::string &, char c_escape, const std::string &specials_in, const std::string &specials_out)
Returns the escape()d string.
Definition: gstr.cpp:41
std::string str(int indent=-1) const
Returns a string representation, using a json-like format.
Definition: gitem.cpp:67
static Item parse(const std::string &)
Parses the string representation. Throws on error.
Definition: gitem.cpp:43
static Item list()
Factory function for a list item.
Definition: gitem.cpp:28
const Item & operator[](const std::string &k) const
Indexing operator for a map item. Throws if no such item.
Definition: gitem.cpp:90
std::string operator()() const
Returns the value of this string item.
Definition: gitem.cpp:184
A variant class holding a string, an item map keyed by name, or an ordered list of items...
Definition: gitem.h:41
static void unescape(std::string &s, char c_escape, const char *specials_in, const char *specials_out)
Unescapes the string by replacing e-e with e, e-special-in with special-out, and e-other with other...
Definition: gstr.cpp:104
void add(const std::string &s)
Adds a string item to this list item.
Definition: gitem.cpp:108
static Item string()
Factory function for a string item.
Definition: gitem.cpp:38
Item(Type type=t_string)
Constructor.
Definition: gitem.cpp:74
Item keys() const
Returns the keys of this map item as a list of string items.
Definition: gitem.cpp:152
bool has(const std::string &k) const
Returns true if this map item has the named key.
Definition: gitem.cpp:146
Type type() const
Returns the item type (string, list or map).
Definition: gitem.cpp:85