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 #include <string>
23 
24 #include <zorba/config.h>
25 #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 
124 ZORBA_DLL_PUBLIC
126 alloc_streambuf( char const *charset, std::streambuf *orig );
127 
128 ZORBA_DLL_PUBLIC
130 
131 ZORBA_DLL_PUBLIC
132 int get_streambuf_index();
133 
134 ZORBA_DLL_PUBLIC
135 void stream_callback( std::ios_base::event, std::ios_base&, int index );
136 
137 } // namespace internal
138 
139 namespace transcode {
140 
141 ///////////////////////////////////////////////////////////////////////////////
142 
143 /**
144  * Attaches a transcode::streambuf to a stream. Unlike using a
145  * transcode::streambuf directly, this function will create the streambuf,
146  * attach it to the stream, and manage it for the lifetime of the stream
147  * automatically.
148  *
149  * @param ios The stream to attach the transcode::streambuf to. If the stream
150  * already has a transcode::streambuf attached to it, this function does
151  * nothing.
152  * @param charset The name of the character encoding to convert from/to.
153  */
154 template<typename charT,typename Traits> inline
155 void attach( std::basic_ios<charT,Traits> &ios, char const *charset ) {
156  int const index = internal::get_streambuf_index();
157  void *&pword = ios.pword( index );
158  if ( !pword ) {
159  streambuf *const buf = internal::alloc_streambuf( charset, ios.rdbuf() );
160  ios.rdbuf( buf );
161  pword = buf;
162  ios.register_callback( internal::stream_callback, index );
163  }
164 }
165 
166 /**
167  * Detaches a previously attached transcode::streambuf from a stream. The
168  * streambuf is destroyed and the stream's original streambuf is restored.
169  *
170  * @param ios The stream to detach the transcode::streambuf from. If the
171  * stream doesn't have a transcode::streambuf attached to it, this function
172  * does nothing.
173  */
174 template<typename charT,typename Traits> inline
175 void detach( std::basic_ios<charT,Traits> &ios ) {
176  int const index = internal::get_streambuf_index();
177  if ( streambuf *const buf = static_cast<streambuf*>( ios.pword( index ) ) ) {
178  ios.pword( index ) = 0;
179  ios.rdbuf( buf->orig_streambuf() );
181  }
182 }
183 
184 /**
185  * Checks whether the given stream has a transcode::streambuf attached.
186  *
187  * @param ios The stream to check.
188  * @return \c true only if a transcode::streambuf is attached.
189  */
190 template<typename charT,typename Traits> inline
191 bool is_attached( std::basic_ios<charT,Traits> &ios ) {
192  return !!ios.pword( internal::get_streambuf_index() );
193 }
194 
195 /**
196  * A %transcode::auto_attach is a class that attaches a transcode::streambuf to
197  * a stream and automatically detaches it when the %auto_attach object is
198  * destroyed.
199  * \code
200  * void f( ostream &os ) {
201  * transcode::auto_attach<ostream> const raii( os, "ISO-8859-1" );
202  * // ...
203  * }
204  * \endcode
205  * A %transcode::auto_attach is useful for streams not created by you.
206  *
207  * @see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
208  */
209 template<class StreamType>
210 class auto_attach {
211 public:
212  /**
213  * Constructs an %auto_attach object calling attach() on the given stream.
214  *
215  * @param stream The stream to attach the transcode::streambuf to. If the
216  * stream already has a transcode::streambuf attached to it, this contructor
217  * does nothing.
218  * @param charset The name of the character encoding to convert from/to.
219  */
220  auto_attach( StreamType &stream, char const *charset ) : stream_( stream ) {
221  attach( stream, charset );
222  }
223 
224  /**
225  * Destroys this %auto_attach object calling detach() on the previously
226  * attached stream.
227  */
229  detach( stream_ );
230  }
231 
232 private:
233  StreamType &stream_;
234 };
235 
236 ///////////////////////////////////////////////////////////////////////////////
237 
238 /**
239  * A %transcode::stream is used to wrap a C++ standard I/O stream with a
240  * transcode::streambuf so that transcoding and the management of the streambuf
241  * happens automatically.
242  *
243  * A %transcode::stream is useful for streams created by you.
244  *
245  * @tparam StreamType The I/O stream class type to wrap. It must be a concrete
246  * stream class.
247  */
248 template<class StreamType>
249 class stream : public StreamType {
250 public:
251  /**
252  * Constructs a %transcode::stream.
253  *
254  * @param charset The name of the character encoding to convert from/to.
255  * @throws std::invalid_argument if \a charset is not supported.
256  */
257  stream( char const *charset ) :
258 #ifdef WIN32
259 # pragma warning( push )
260 # pragma warning( disable : 4355 )
261 #endif /* WIN32 */
262  tbuf_( charset, this->rdbuf() )
263 #ifdef WIN32
264 # pragma warning( pop )
265 #endif /* WIN32 */
266  {
267  init();
268  }
269 
270  /**
271  * Constructs a %stream.
272  *
273  * @tparam StreamArgType The type of the first argument of \a StreamType's
274  * constructor.
275  * @param charset The name of the character encoding to convert from/to.
276  * @param stream_arg The argument to pass as the first argument to
277  * \a StreamType's constructor.
278  * @throws std::invalid_argument if \a charset is not supported.
279  */
280  template<typename StreamArgType>
281  stream( char const *charset, StreamArgType stream_arg ) :
282  StreamType( stream_arg ),
283 #ifdef WIN32
284 # pragma warning( push )
285 # pragma warning( disable : 4355 )
286 #endif /* WIN32 */
287  tbuf_( charset, this->rdbuf() )
288 #ifdef WIN32
289 # pragma warning( pop )
290 #endif /* WIN32 */
291  {
292  init();
293  }
294 
295  /**
296  * Constructs a %transcode::stream.
297  *
298  * @tparam StreamArgType The type of the first argument of \a StreamType's
299  * constructor.
300  * @param charset The name of the character encoding to convert from/to.
301  * @param stream_arg The argument to pass as the first argument to
302  * \a StreamType's constructor.
303  * @param mode The open-mode to pass to \a StreamType's constructor.
304  * @throws std::invalid_argument if \a charset is not supported.
305  */
306  template<typename StreamArgType>
307  stream( char const *charset, StreamArgType stream_arg,
308  std::ios_base::openmode mode ) :
309  StreamType( stream_arg, mode ),
310 #ifdef WIN32
311 # pragma warning( push )
312 # pragma warning( disable : 4355 )
313 #endif /* WIN32 */
314  tbuf_( charset, this->rdbuf() )
315 #ifdef WIN32
316 # pragma warning( pop )
317 #endif /* WIN32 */
318  {
319  init();
320  }
321 
322 private:
323  streambuf tbuf_;
324 
325  void init() {
326  this->std::ios::rdbuf( &tbuf_ );
327  }
328 };
329 
330 ///////////////////////////////////////////////////////////////////////////////
331 
332 /**
333  * Checks whether it would be necessary to transcode from the given character
334  * encoding to UTF-8.
335  *
336  * @param charset The name of the character encoding to check.
337  * @return \c true only if it would be necessary to transcode from the given
338  * character encoding to UTF-8.
339  */
340 ZORBA_DLL_PUBLIC
341 bool is_necessary( char const *charset );
342 
343 /**
344  * Checks whether the given character set is supported for transcoding.
345  *
346  * @param charset The name of the character encoding to check.
347  * @return \c true only if the character encoding is supported.
348  */
349 ZORBA_DLL_PUBLIC
350 bool is_supported( char const *charset );
351 
352 ///////////////////////////////////////////////////////////////////////////////
353 
354 } // namespace transcode
355 } // namespace zorba
356 #endif /* ZORBA_TRANSCODE_STREAM_API_H */
357 /* vim:set et sw=2 ts=2: */
blog comments powered by Disqus