transcode_stream.h
Go to the documentation of this file.
1 /*
2  * Copyright 2006-2008 The FLWOR Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ZORBA_TRANSCODE_STREAM_API_H
18 #define ZORBA_TRANSCODE_STREAM_API_H
19 
20 #include <stdexcept>
21 #include <streambuf>
22 
23 #include <zorba/config.h>
24 #include <zorba/internal/proxy.h>
27 
28 namespace zorba {
29 
31 
32 namespace transcode {
33 
34 ///////////////////////////////////////////////////////////////////////////////
35 
36 /**
37  * A %transcode::streambuf is-a std::streambuf for transcoding character
38  * encodings from/to UTF-8 on-the-fly.
39  *
40  * To use it, replace a stream's streambuf:
41  * \code
42  * istream is;
43  * // ...
44  * transcode::streambuf tbuf( "ISO-8859-1", is.rdbuf() );
45  * is.ios::rdbuf( &tbuf );
46  * \endcode
47  * Note that the %transcode::streambuf must exist for as long as it's being used
48  * by the stream. If you are replacing the streabuf for a stream you did not
49  * create, you should set it back to the original streambuf:
50  * \code
51  * void f( ostream &os ) {
52  * transcode::streambuf tbuf( "ISO-8859-1", os.rdbuf() );
53  * try {
54  * os.ios::rdbuf( &tbuf );
55  * // ...
56  * }
57  * catch ( ... ) {
58  * os.ios::rdbuf( tbuf.orig_streambuf() );
59  * throw;
60  * }
61  * os.ios::rdbuf( tbuf.orig_streambuf() );
62  * }
63  * \endcode
64  * Alternatively, you may wish to use either \c attach(), \c auto_attach, or
65  * \c transcode::stream instead.
66  *
67  * While %transcode::streambuf does support seeking, the positions are relative
68  * to the original byte stream.
69  */
70 class ZORBA_DLL_PUBLIC streambuf : public std::streambuf {
71 public:
72  /**
73  * Constructs a %transcode::streambuf.
74  *
75  * @param charset The name of the character encoding to convert from/to.
76  * @param orig The original streambuf to read/write from/to.
77  * @throws std::invalid_argument if either \a charset is not supported or
78  * \a orig is null.
79  */
80  streambuf( char const *charset, std::streambuf *orig );
81 
82  /**
83  * Destructs a %transcode::streambuf.
84  */
85  ~streambuf();
86 
87  /**
88  * Gets the original streambuf.
89  *
90  * @return said streambuf.
91  */
92  std::streambuf* orig_streambuf() const {
93  return proxy_buf_->original();
94  }
95 
96 protected:
97  void imbue( std::locale const& );
98  pos_type seekoff( off_type, std::ios_base::seekdir, std::ios_base::openmode );
99  pos_type seekpos( pos_type, std::ios_base::openmode );
100  std::streambuf* setbuf( char_type*, std::streamsize );
101  std::streamsize showmanyc();
102  int sync();
103  int_type overflow( int_type );
104  int_type pbackfail( int_type );
105  int_type uflow();
106  int_type underflow();
107  std::streamsize xsgetn( char_type*, std::streamsize );
108  std::streamsize xsputn( char_type const*, std::streamsize );
109 
110 private:
112 
113  // forbid
114  streambuf( streambuf const& );
115  streambuf& operator=( streambuf const& );
116 };
117 
118 ///////////////////////////////////////////////////////////////////////////////
119 
120 } // namespace transcode
121 
122 namespace internal {
123 namespace transcode {
124 
125 ZORBA_DLL_PUBLIC
126 std::streambuf* alloc_streambuf( char const *charset, std::streambuf *orig );
127 
128 ZORBA_DLL_PUBLIC
129 int get_streambuf_index();
130 
131 } // transcode
132 } // namespace internal
133 
134 namespace transcode {
135 
136 ///////////////////////////////////////////////////////////////////////////////
137 
138 /**
139  * Attaches a transcode::streambuf to a stream. Unlike using a
140  * transcode::streambuf directly, this function will create the streambuf,
141  * attach it to the stream, and manage it for the lifetime of the stream
142  * automatically.
143  *
144  * @param ios The stream to attach the transcode::streambuf to. If the stream
145  * already has a transcode::streambuf attached to it, this function does
146  * nothing.
147  * @param charset The name of the character encoding to convert from/to.
148  */
149 template<typename charT,typename Traits> inline
150 void attach( std::basic_ios<charT,Traits> &ios, char const *charset ) {
151  int const index = internal::transcode::get_streambuf_index();
152  void *&pword = ios.pword( index );
153  if ( !pword ) {
154  std::streambuf *const buf =
155  internal::transcode::alloc_streambuf( charset, ios.rdbuf() );
156  ios.rdbuf( buf );
157  pword = buf;
158  ios.register_callback( internal::stream_callback, index );
159  }
160 }
161 
162 /**
163  * Detaches a previously attached transcode::streambuf from a stream. The
164  * streambuf is destroyed and the stream's original streambuf is restored.
165  *
166  * @param ios The stream to detach the transcode::streambuf from. If the
167  * stream doesn't have a transcode::streambuf attached to it, this function
168  * does nothing.
169  */
170 template<typename charT,typename Traits> inline
171 void detach( std::basic_ios<charT,Traits> &ios ) {
172  int const index = internal::transcode::get_streambuf_index();
173  if ( streambuf *const buf = static_cast<streambuf*>( ios.pword( index ) ) ) {
174  ios.pword( index ) = 0;
175  ios.rdbuf( buf->orig_streambuf() );
177  }
178 }
179 
180 /**
181  * Checks whether the given stream has a transcode::streambuf attached.
182  *
183  * @param ios The stream to check.
184  * @return \c true only if a transcode::streambuf is attached.
185  */
186 template<typename charT,typename Traits> inline
187 bool is_attached( std::basic_ios<charT,Traits> &ios ) {
188  return !!ios.pword( internal::transcode::get_streambuf_index() );
189 }
190 
191 /**
192  * Gets the original streambuf of the given iostream.
193  *
194  * @param ios The stream to get the original streambuf of.
195  * @return the original streambuf.
196  */
197 template<typename charT,typename Traits> inline
198 std::streambuf* orig_streambuf( std::basic_ios<charT,Traits> &ios ) {
199  std::streambuf *const buf = ios.rdbuf();
200  if ( streambuf *const tbuf = dynamic_cast<streambuf*>( buf ) )
201  return tbuf->orig_streambuf();
202  return buf;
203 }
204 
205 /**
206  * A %transcode::auto_attach is a class that attaches a transcode::streambuf to
207  * a stream and automatically detaches it when the %auto_attach object is
208  * destroyed.
209  * \code
210  * void f( ostream &os ) {
211  * transcode::auto_attach<ostream> const raii( os, "ISO-8859-1" );
212  * // ...
213  * }
214  * \endcode
215  * A %transcode::auto_attach is useful for streams not created by you.
216  *
217  * @see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
218  */
219 template<class StreamType>
220 class auto_attach {
221 public:
222  /**
223  * Constructs an %auto_attach object calling attach() on the given stream.
224  *
225  * @param stream The stream to attach the transcode::streambuf to. If the
226  * stream already has a transcode::streambuf attached to it, this contructor
227  * does nothing.
228  * @param charset The name of the character encoding to convert from/to.
229  */
230  auto_attach( StreamType &stream, char const *charset ) : stream_( stream ) {
231  attach( stream, charset );
232  }
233 
234  /**
235  * Destroys this %auto_attach object calling detach() on the previously
236  * attached stream.
237  */
239  detach( stream_ );
240  }
241 
242 private:
243  StreamType &stream_;
244 };
245 
246 ///////////////////////////////////////////////////////////////////////////////
247 
248 /**
249  * A %transcode::stream is used to wrap a C++ standard I/O stream with a
250  * transcode::streambuf so that transcoding and the management of the streambuf
251  * happens automatically.
252  *
253  * A %transcode::stream is useful for streams created by you.
254  *
255  * @tparam StreamType The I/O stream class type to wrap. It must be a concrete
256  * stream class.
257  */
258 template<class StreamType>
259 class stream : public StreamType {
260 public:
261  /**
262  * Constructs a %transcode::stream.
263  *
264  * @param charset The name of the character encoding to convert from/to.
265  * @throws std::invalid_argument if \a charset is not supported.
266  */
267  stream( char const *charset ) :
268 #ifdef WIN32
269 # pragma warning( push )
270 # pragma warning( disable : 4355 )
271 #endif /* WIN32 */
272  tbuf_( charset, this->rdbuf() )
273 #ifdef WIN32
274 # pragma warning( pop )
275 #endif /* WIN32 */
276  {
277  init();
278  }
279 
280  /**
281  * Constructs a %stream.
282  *
283  * @tparam StreamArgType The type of the first argument of \a StreamType's
284  * constructor.
285  * @param charset The name of the character encoding to convert from/to.
286  * @param stream_arg The argument to pass as the first argument to
287  * \a StreamType's constructor.
288  * @throws std::invalid_argument if \a charset is not supported.
289  */
290  template<typename StreamArgType>
291  stream( char const *charset, StreamArgType stream_arg ) :
292  StreamType( stream_arg ),
293 #ifdef WIN32
294 # pragma warning( push )
295 # pragma warning( disable : 4355 )
296 #endif /* WIN32 */
297  tbuf_( charset, this->rdbuf() )
298 #ifdef WIN32
299 # pragma warning( pop )
300 #endif /* WIN32 */
301  {
302  init();
303  }
304 
305  /**
306  * Constructs a %transcode::stream.
307  *
308  * @tparam StreamArgType The type of the first argument of \a StreamType's
309  * constructor.
310  * @param charset The name of the character encoding to convert from/to.
311  * @param stream_arg The argument to pass as the first argument to
312  * \a StreamType's constructor.
313  * @param mode The open-mode to pass to \a StreamType's constructor.
314  * @throws std::invalid_argument if \a charset is not supported.
315  */
316  template<typename StreamArgType>
317  stream( char const *charset, StreamArgType stream_arg,
318  std::ios_base::openmode mode ) :
319  StreamType( stream_arg, mode ),
320 #ifdef WIN32
321 # pragma warning( push )
322 # pragma warning( disable : 4355 )
323 #endif /* WIN32 */
324  tbuf_( charset, this->rdbuf() )
325 #ifdef WIN32
326 # pragma warning( pop )
327 #endif /* WIN32 */
328  {
329  init();
330  }
331 
332 private:
333  streambuf tbuf_;
334 
335  void init() {
336  this->std::ios::rdbuf( &tbuf_ );
337  }
338 };
339 
340 ///////////////////////////////////////////////////////////////////////////////
341 
342 /**
343  * Checks whether it would be necessary to transcode from the given character
344  * encoding to UTF-8.
345  *
346  * @param charset The name of the character encoding to check.
347  * @return \c true only if it would be necessary to transcode from the given
348  * character encoding to UTF-8.
349  */
350 ZORBA_DLL_PUBLIC
351 bool is_necessary( char const *charset );
352 
353 /**
354  * Checks whether the given character set is supported for transcoding.
355  *
356  * @param charset The name of the character encoding to check.
357  * @return \c true only if the character encoding is supported.
358  */
359 ZORBA_DLL_PUBLIC
360 bool is_supported( char const *charset );
361 
362 ///////////////////////////////////////////////////////////////////////////////
363 
364 } // namespace transcode
365 } // namespace zorba
366 #endif /* ZORBA_TRANSCODE_STREAM_API_H */
367 /* vim:set et sw=2 ts=2: */
blog comments powered by Disqus