platform/codecs/JSONCodec.hpp

00001 // ------------------------------------------------------------------------
00002 // Pion is a development platform for building Reactors that process Events
00003 // ------------------------------------------------------------------------
00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
00005 //
00006 // Pion is free software: you can redistribute it and/or modify it under the
00007 // terms of the GNU Affero General Public License as published by the Free
00008 // Software Foundation, either version 3 of the License, or (at your option)
00009 // any later version.
00010 //
00011 // Pion is distributed in the hope that it will be useful, but WITHOUT ANY
00012 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00013 // FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for
00014 // more details.
00015 //
00016 // You should have received a copy of the GNU Affero General Public License
00017 // along with Pion.  If not, see <http://www.gnu.org/licenses/>.
00018 //
00019 
00020 #ifndef __PION_JSONCODEC_HEADER__
00021 #define __PION_JSONCODEC_HEADER__
00022 
00023 #include <vector>
00024 #include <map>
00025 #include <queue>
00026 #include <pion/PionConfig.hpp>
00027 #include <pion/platform/Codec.hpp>
00028 #include <yajl/yajl_gen.h>
00029 
00030 
00031 namespace pion {        // begin namespace pion
00032 namespace plugins {     // begin namespace plugins
00033 
00034 
00035 struct Context;
00036 
00041 class JSONCodec :
00042     public pion::platform::Codec
00043 {
00044 public:
00045 
00046     // TODO: EmptyFieldException and EmptyTermException should probably be moved
00047     // to class Codec, and removed from LogCodec.
00048 
00050     class EmptyFieldException : public PionException {
00051     public:
00052         EmptyFieldException(const std::string& codec_id)
00053             : PionException("JSONCodec configuration includes an empty field name: ", codec_id) {}
00054     };
00055 
00057     class EmptyTermException : public PionException {
00058     public:
00059         EmptyTermException(const std::string& codec_id)
00060             : PionException("JSONCodec configuration is missing a term identifier: ", codec_id) {}
00061     };
00062 
00063 
00065     JSONCodec(void)
00066         : pion::platform::Codec(),
00067         m_flush_after_write(false), m_yajl_generator(NULL), m_yajl_handle(NULL),
00068         m_no_events_written(true), m_first_read_attempt(true)
00069     {}
00070     
00072     virtual ~JSONCodec() {
00073         if (m_yajl_generator)
00074             yajl_gen_free(m_yajl_generator);
00075         if (m_yajl_handle)
00076             yajl_free(m_yajl_handle);
00077     }
00078     
00080     virtual const std::string& getContentType(void) const { return CONTENT_TYPE; }
00081 
00087     virtual pion::platform::CodecPtr clone(void) const;
00088 
00095     virtual void write(std::ostream& out, const pion::platform::Event& e);
00096 
00102     virtual void finish(std::ostream& out);
00103 
00111     virtual bool read(std::istream& in, pion::platform::Event& e);
00112 
00120     virtual void setConfig(const pion::platform::Vocabulary& v, const xmlNodePtr config_ptr);
00121     
00128     virtual void updateVocabulary(const pion::platform::Vocabulary& v);
00129 
00131     inline void reset(void) {
00132         m_field_map.clear();
00133         m_format.clear();
00134     }
00135 
00137     struct JSONField {
00138         JSONField(const std::string& f, const pion::platform::Vocabulary::Term& t)
00139             : field_name(f), term(t)
00140         {}
00141 
00143         std::string                         field_name;
00144 
00146         pion::platform::Vocabulary::Term    term;
00147 
00149         PionTimeFacet                       time_facet;
00150     };
00151 
00153     typedef boost::shared_ptr<JSONField>    JSONFieldPtr;
00154     
00156     typedef PION_HASH_MAP<std::string,
00157         JSONFieldPtr,
00158         PION_HASH_STRING>                   FieldMap;
00159 
00161     typedef std::vector<JSONFieldPtr>       CurrentFormat;
00162 
00164     typedef PION_HASH_MULTIMAP<pion::platform::Vocabulary::TermRef, std::string>
00165                                             JSONObject;
00166 
00168     typedef boost::shared_ptr<JSONObject>   JSONObjectPtr;
00169 
00171     typedef std::queue<JSONObjectPtr>       JSONObjectQueue;
00172 
00173 private:
00174 
00176     typedef std::istream::traits_type       traits_type;
00177 
00179     typedef std::basic_streambuf<
00180         std::istream::char_type,
00181         std::istream::traits_type>  streambuf_type;
00182 
00184     typedef std::istream::int_type          int_type;
00185 
00192     inline void mapFieldToTerm(const std::string& field,
00193                                const pion::platform::Vocabulary::Term& term);
00194 
00195 
00197     static const std::string        CONTENT_TYPE;
00198 
00200     static const std::string        FIELD_ELEMENT_NAME;
00201 
00203     static const std::string        TERM_ATTRIBUTE_NAME;
00204 
00206     static const unsigned int       READ_BUFFER_SIZE;
00207 
00209     static std::string              INDENT_STRING;
00210 
00211 
00213     FieldMap                        m_field_map;
00214 
00216     std::map<pion::platform::Vocabulary::TermRef, JSONFieldPtr>
00217                                     m_JSON_field_ptr_map;
00218 
00220     CurrentFormat                   m_format;
00221 
00223     bool                            m_flush_after_write;
00224 
00226     yajl_gen                        m_yajl_generator;
00227 
00229     yajl_handle                     m_yajl_handle;
00230 
00232     JSONObjectQueue                 m_json_object_queue;
00233 
00235     boost::shared_ptr<Context>      m_context;
00236 
00238     bool                            m_no_events_written;
00239 
00241     bool                            m_first_read_attempt;
00242 };
00243 
00244 
00245 // inline member functions for JSONCodec
00246 
00247 inline void JSONCodec::mapFieldToTerm(const std::string& field,
00248                                       const pion::platform::Vocabulary::Term& term)
00249 {
00250     if (m_field_map[field])
00251         throw PionException("Duplicate Field Name");
00252 
00253     // prepare a new JSON field object
00254     JSONFieldPtr field_ptr(new JSONField(field, term));
00255     switch (term.term_type) {
00256         case pion::platform::Vocabulary::TYPE_DATE_TIME:
00257         case pion::platform::Vocabulary::TYPE_DATE:
00258         case pion::platform::Vocabulary::TYPE_TIME:
00259             field_ptr->time_facet.setFormat(term.term_format);
00260             break;
00261         default:
00262             break; // do nothing
00263     }
00264 
00265     // add it to the mapping of field names
00266     m_field_map[field] = field_ptr;
00267 
00268     // append the new field to the current format
00269     m_format.push_back(field_ptr);
00270 }
00271 
00272 // TODO: put this somewhere
00273 struct Context {
00274     Context(const JSONCodec::FieldMap& field_map, JSONCodec::JSONObjectQueue& json_object_queue)
00275         : field_map(field_map), json_object_queue(json_object_queue)
00276         , m_array_started(false), m_array_ended(false)
00277     {}
00278 
00279     const JSONCodec::FieldMap& field_map;
00280     JSONCodec::JSONObjectQueue& json_object_queue;
00281     JSONCodec::JSONObjectPtr json_object_ptr;
00282     pion::platform::Vocabulary::TermRef term_ref;
00283     bool m_array_started;
00284     bool m_array_ended;
00285 };
00286 
00287 }   // end namespace plugins
00288 }   // end namespace pion
00289 
00290 #endif
00291 

Generated on Wed Apr 13 16:38:33 2011 for pion-platform by  doxygen 1.4.7