diff options
author | etobi <git@e-tobi.net> | 2009-11-03 19:02:47 +0100 |
---|---|---|
committer | etobi <git@e-tobi.net> | 2009-11-03 19:02:47 +0100 |
commit | 90b872ed164d8cf004b40f0128fd7a9b3ca7d27e (patch) | |
tree | b558cc69a5f750c98b90d4aac228d9d031d2316a | |
download | vdr-plugin-vodcatcher-90b872ed164d8cf004b40f0128fd7a9b3ca7d27e.tar.gz vdr-plugin-vodcatcher-90b872ed164d8cf004b40f0128fd7a9b3ca7d27e.tar.bz2 |
initial commit
197 files changed, 23156 insertions, 0 deletions
@@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. @@ -0,0 +1,21 @@ +vdr-vodcatcher makes use of the following OpenSource tools and components: + +CxxTest +------- + +CxxTest is a JUnit/CppUnit/xUnit-like framework for C++. + +Author : Erez Volk <erez_v at users.sourceforge.net> +Homepage : http://cxxtest.sourceforge.net/ +License : GNU Lesser General Public License + + +TinyXml +------- + +TinyXml is a simple, small, C++ XML parser. + +Authors : Lee Thomason <leethomason at mindspring.com> + Yves Berquin +Homepage : http://www.grinninglizard.com/tinyxml/index.html +License : zlib/libpng License @@ -0,0 +1,25 @@ +VDR Plug-in 'vodcatcher' Revision History +----------------------------------------- + +2008-08-10: Version 0.2.1 + - Update of the Italian translation by Diego Pierotto <vdr-italian@tiscali.it> + - Updated README + +2008-08-09: Version 0.2.0 + - Fixed Galacticast feed in example + - Updated GPL license text + - Added Italian translation from the Italian VDR Community <vdr-italian@tiscali.it> + - Added support for Xineliboutput as media player + - Dropped non-gettext I18N support + +2008-07-26: Version 0.1.2 + - Fixed GCC 4.3 include issue + +2007-11-27: Version 0.1.1 + - Added support for <videocast:subfeed>, <link> tag is ignored now + - Changed user agent to vdr-vodcatcher/0.1 + - Introduced DEBUG variable/define in Makefile, which currently only controls, + if Curl will output some verbose messages to stderr/stdout. + +2007-11-25: Version 0.1.0 + * Initial Release diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..73e553b --- /dev/null +++ b/Makefile @@ -0,0 +1,175 @@ +# +# Makefile for the Vodcatcher VDR plugin +# +# $Id$ + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# +PLUGIN = vodcatcher + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char VERSION\[\] =' src/Version.h | \ + awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual + +### The directory environment: + +DVBDIR = ../../../../DVB +VDRDIR = ../../.. +LIBDIR = ../../lib +TMPDIR = /tmp + +### Conditionals: +#DEBUG=1 + +### Some defaults that can be overriden: + +DEFAULT_CACHE_DIR = /var/cache/vdr-plugin-vodcatcher + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR's plugin API (taken from VDR's "config.h"): + +APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' \ + $(VDRDIR)/config.h) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I. -I$(VDRDIR)/include -I$(DVBDIR)/include +INCLUDES += `curl-config --cflags` +INCLUDES += -I./tinyxml + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -DCXXTEST_ABORT_TEST_ON_FAIL +DEFINES += -DCXXTEST_HAVE_EH +DEFINES += -DCXXTEST_HAVE_STD +DEFINES += -DTIXML_USE_STL + +DEFINES += -DDEFAULT_CACHE_DIR='"$(DEFAULT_CACHE_DIR)"' + +ifdef DEBUG + DEFINES += -DDEBUG +endif + +# libcurl, libxml, libxml++ +LIBS += `curl-config --libs` + +### The source files (add further files here): + +-include sources.mk + +SRCS_TESTS := $(wildcard src/*_test.cc) +SRCS_TESTPARTS := $(addsuffix .cxx.cc,$(basename ${SRCS_TESTS})) + +### The object files + +OBJS := $(addsuffix .o,$(basename ${SRCS})) +OBJS_TESTABLE = $(addsuffix .o,$(basename ${SRCS_TESTABLE})) +OBJS_TESTS := $(addsuffix .o,$(basename ${SRCS_TESTS})) +OBJS_TESTONLY := $(addsuffix .o,$(basename ${SRCS_TESTONLY})) +OBJS_TESTPARTS := $(addsuffix .o,$(basename ${SRCS_TESTPARTS})) +OBJS += $(OBJS_TESTABLE) + +### Implicit rules: + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< -o $@ + +%.o: %.cc + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< -o $@ + +#.PRECIOUS: %_test.cxx.cc +%_test.cxx.cc: %_test.cc + ./cxxtestgen --have-std --part -o $@ $< + +# Dependencies: + +MAKEDEP = $(CXX) -MM +BUILD_DEPFILE = .dependencies +TESTS_DEPFILE = .dependencies.tests + +$(BUILD_DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SRCS) $(SRCS_TESTABLE) \ + | sed "s/.*: \([^ ]*\/\).*/\1\0/" > $@ + +$(TESTS_DEPFILE): Makefile $(SRCS_TESTPARTS) + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SRCS_TESTABLE) $(SRCS_TESTONLY) \ + $(SRCS_TESTPARTS) | sed "s/.*: \([^ ]*\/\).*/\1\0/" > $@ + +ifeq ($(MAKECMDGOALS),runtests) + -include $(TESTS_DEPFILE) +else + -include $(BUILD_DEPFILE) +endif + +### Internationalization (I18N): + +PODIR = po +LOCALEDIR = $(VDRDIR)/locale +I18Npo = $(wildcard $(PODIR)/*.po) +I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) +I18Npot = $(PODIR)/$(PLUGIN).pot + +%.mo: %.po + msgfmt -c -o $@ $< + +$(I18Npot): $(SRCS) $(SRCS_TESTABLE) + xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<tg@e-tobi.net>' -o $@ $^ + +%.po: $(I18Npot) + msgmerge -U --no-wrap --no-location --backup=none -q $@ $< + @touch $@ + +$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo + @mkdir -p $(dir $@) + mv $< $@ + +.PHONY: i18n +i18n: $(I18Nmsgs) + +### Targets: + +all: libvdr-$(PLUGIN).so i18n + +testrunner.cc: + ./cxxtestgen -o testrunner.cc --error-printer --root + +testrunner: $(OBJS_TESTPARTS) $(OBJS_TESTABLE) $(OBJS_TESTONLY) testrunner.cc + $(CXX) $(CXXFLAGS) testrunner.cc $(OBJS_TESTPARTS) $(OBJS_TESTABLE) \ + $(OBJS_TESTONLY) $(DEFINES) $(INCLUDES) $(LIBS) -o $@ + +runtests: testrunner + ./testrunner + +libvdr-$(PLUGIN).so: $(OBJS) + $(CXX) $(CXXFLAGS) -shared $(OBJS) -L. $(LIBS) -o $@ + @cp $@ $(LIBDIR)/$@.$(APIVERSION) + +dist: clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tar.gz -C $(TMPDIR) --exclude debian --exclude CVS \ + --exclude .svn --exclude tools --exclude .cproject --exclude .project \ + --exclude continuoustesting $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tar.gz + +clean: + @-rm -f $(BUILD_DEPFILE) $(TESTS_DEPFILE) *.so* *.tar.gz core* *~ + @-find . -name \*.\o -exec rm -f {} \; + @-rm -f testrunner testrunner.cc src/*_test.cxx.cc @@ -0,0 +1,135 @@ +vdr-vodcatcher - a video podcast plug-in for the Video Disk Recorder (VDR) +========================================================================== + +Written by: + Tobias Grimm <tg@e-tobi.net> + +Project's homepage: + http://www.e-tobi.net/blog/pages/vdr-vodcatcher + +Copyright: + (C) 2007 - 2008 Tobias Grimm + +License: + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program (see file `COPYING`); if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA + +Translations: + it: Diego Pierotto <vdr-italian@tiscali.it> + + +Description: +----------- + +The Vodcatcher plug-in allows to parse RSS feeds containing video streams as +enclosures. The plug-in will play such streams by passing them to the +MPlayer plug-in [1] or the Xineliboutput plug-in [2]. + +Besides the standard RSS feeds with video enclosures, the RSS services of +videocast.srtwist.net are supported too. + + +Requirements: +------------ + +The plugin requires libcurl, vdr 1.6.0 or above and the Mplayer or Xineliboutput +plug-in. + + +Building: +-------- + +The following make variables can be overriden: + + DEFAULT_CACHE_DIR Specifies the default cache dir, where the xml feeds + get cached. If not specified, this defaults to + /var/cache/vdr-plugin-vodcatcher. The cache dir + can also be changed at runtime using the + --cache option. + + +Command line arguments: +----------------------- + +The following parameters are available: + + --cache=<DIR> Specify the cache dir, where the xml feeds get cached. + This defaults to /var/cache/vdr-plugin-vodcatcher. + + +Configuration +------------- + +In order to allow the MPlayer plug-in to play back the streams passed in by the +Vodcatcher, you must add the following entry to the mplayersources.conf file: + + /tmp;Vodcatcher;0 + +An external configuration file named "vodcatchersources.conf" must exist in the +subdirectory "plugins/" in VDR's configuration directory. + +In vodcatchersources.conf the URL's of the RSS feeds can be configured in the +following way: + + # + # videocast.srtwist.net + # + FEED_URL=http://videocast.srtwist.net + +Multiple FEED_URL-entries can be specified. A '#' indicates a comment just like +in shell scripts. + + +Setup parameters: +----------------- + + Max. download cache age (days) The number of days, files should be kept + in the local cache. + default: 30 days + + Media Player The media player used for playback. Can + be "MPlayer" or "Xineliboutput". + + +Usage: +------ + +By opening the plug-in's main menu entry in VDR's OSD, you enter the feed menu. +This menu lists all the feeds you have configured in the file +"vodcatchersources.conf". If the feed could not be downloaded or is not yet +downloaded, it will be listed as "unknown". In this case, just try again later +or check the URL in the config file. + +By selecting a feed from the menu and pressing OK, a menu listing all the feed +items will open. If a feed item is linked to another RSS feed, you you press +OK again to open this feed. If the selected feed item contains a video stream, +you can start playback with the GREEN key. If the stream is available in +different qualities (only possible with videocast.srtwist.net), you can toggle +the quality with the YELLOW key, before playing it. + +The play back plug-in might need a while to cache the video stream, before +playback starts. This can be several seconds depending on your plugin +settings and the download speed. + + +Known problems and todo's: +-------------------------- + +- Recording of streams is not yet implemented + +--- + +[1]: http://www.muempf.de +[2]: http://sourceforge.net/projects/xineliboutput diff --git a/cxxtest/Descriptions.cpp b/cxxtest/Descriptions.cpp new file mode 100644 index 0000000..143f8f8 --- /dev/null +++ b/cxxtest/Descriptions.cpp @@ -0,0 +1,58 @@ +#ifndef __cxxtest__Descriptions_cpp__ +#define __cxxtest__Descriptions_cpp__ + +#include <cxxtest/Descriptions.h> + +namespace CxxTest +{ + TestDescription::~TestDescription() {} + SuiteDescription::~SuiteDescription() {} + WorldDescription::~WorldDescription() {} + + // + // Convert total tests to string + // +#ifndef _CXXTEST_FACTOR + char *WorldDescription::strTotalTests( char *s ) const + { + numberToString( numTotalTests(), s ); + return s; + } +#else // _CXXTEST_FACTOR + char *WorldDescription::strTotalTests( char *s ) const + { + char *p = numberToString( numTotalTests(), s ); + + if ( numTotalTests() <= 1 ) + return s; + + unsigned n = numTotalTests(); + unsigned numFactors = 0; + + for ( unsigned factor = 2; (factor * factor) <= n; factor += (factor == 2) ? 1 : 2 ) { + unsigned power; + + for ( power = 0; (n % factor) == 0; n /= factor ) + ++ power; + + if ( !power ) + continue; + + p = numberToString( factor, copyString( p, (numFactors == 0) ? " = " : " * " ) ); + if ( power > 1 ) + p = numberToString( power, copyString( p, "^" ) ); + ++ numFactors; + } + + if ( n > 1 ) { + if ( !numFactors ) + copyString( p, tracker().failedTests() ? " :(" : tracker().warnings() ? " :|" : " :)" ); + else + numberToString( n, copyString( p, " * " ) ); + } + return s; + } +#endif // _CXXTEST_FACTOR +}; + +#endif // __cxxtest__Descriptions_cpp__ diff --git a/cxxtest/Descriptions.h b/cxxtest/Descriptions.h new file mode 100644 index 0000000..bd373ec --- /dev/null +++ b/cxxtest/Descriptions.h @@ -0,0 +1,74 @@ +#ifndef __cxxtest__Descriptions_h__ +#define __cxxtest__Descriptions_h__ + +// +// TestDescription, SuiteDescription and WorldDescription +// hold information about tests so they can be run and reported. +// + +#include <cxxtest/LinkedList.h> + +namespace CxxTest +{ + class TestSuite; + + class TestDescription : public Link + { + public: + virtual ~TestDescription(); + + virtual const char *file() const = 0; + virtual unsigned line() const = 0; + virtual const char *testName() const = 0; + virtual const char *suiteName() const = 0; + + virtual void run() = 0; + + virtual const TestDescription *next() const = 0; + virtual TestDescription *next() = 0; + }; + + class SuiteDescription : public Link + { + public: + virtual ~SuiteDescription(); + + virtual const char *file() const = 0; + virtual unsigned line() const = 0; + virtual const char *suiteName() const = 0; + virtual TestSuite *suite() const = 0; + + virtual unsigned numTests() const = 0; + virtual const TestDescription &testDescription( unsigned /*i*/ ) const = 0; + + virtual TestDescription *firstTest() = 0; + virtual const TestDescription *firstTest() const = 0; + virtual SuiteDescription *next() = 0; + virtual const SuiteDescription *next() const = 0; + + virtual void activateAllTests() = 0; + virtual bool leaveOnly( const char * /*testName*/ ) = 0; + }; + + class WorldDescription : public Link + { + public: + virtual ~WorldDescription(); + + virtual unsigned numSuites( void ) const = 0; + virtual unsigned numTotalTests( void ) const = 0; + virtual const SuiteDescription &suiteDescription( unsigned /*i*/ ) const = 0; + + enum { MAX_STRLEN_TOTAL_TESTS = 32 }; + char *strTotalTests( char * /*buffer*/ ) const; + + virtual SuiteDescription *firstSuite() = 0; + virtual const SuiteDescription *firstSuite() const = 0; + + virtual void activateAllTests() = 0; + virtual bool leaveOnly( const char * /*suiteName*/, const char * /*testName*/ = 0 ) = 0; + }; +} + +#endif // __cxxtest__Descriptions_h__ + diff --git a/cxxtest/DummyDescriptions.cpp b/cxxtest/DummyDescriptions.cpp new file mode 100644 index 0000000..c862e43 --- /dev/null +++ b/cxxtest/DummyDescriptions.cpp @@ -0,0 +1,49 @@ +#include <cxxtest/DummyDescriptions.h> + +namespace CxxTest +{ + DummyTestDescription::DummyTestDescription() {} + + const char *DummyTestDescription::file() const { return "<no file>"; } + unsigned DummyTestDescription::line() const { return 0; } + const char *DummyTestDescription::testName() const { return "<no test>"; } + const char *DummyTestDescription::suiteName() const { return "<no suite>"; } + bool DummyTestDescription::setUp() { return true;} + void DummyTestDescription::run() {} + bool DummyTestDescription::tearDown() { return true;} + + TestDescription *DummyTestDescription::next() { return 0; } + const TestDescription *DummyTestDescription::next() const { return 0; } + + DummySuiteDescription::DummySuiteDescription() : _test() {} + + const char *DummySuiteDescription::file() const { return "<no file>"; } + unsigned DummySuiteDescription::line() const { return 0; } + const char *DummySuiteDescription::suiteName() const { return "<no suite>"; } + TestSuite *DummySuiteDescription::suite() const { return 0; } + unsigned DummySuiteDescription::numTests() const { return 0; } + const TestDescription &DummySuiteDescription::testDescription( unsigned ) const { return _test; } + SuiteDescription *DummySuiteDescription::next() { return 0; } + TestDescription *DummySuiteDescription::firstTest() { return 0; } + const SuiteDescription *DummySuiteDescription::next() const { return 0; } + const TestDescription *DummySuiteDescription::firstTest() const { return 0; } + void DummySuiteDescription::activateAllTests() {} + bool DummySuiteDescription::leaveOnly( const char * /*testName*/ ) { return false; } + + bool DummySuiteDescription::setUp() { return true;} + bool DummySuiteDescription::tearDown() { return true;} + + DummyWorldDescription::DummyWorldDescription() : _suite() {} + + unsigned DummyWorldDescription::numSuites( void ) const { return 0; } + unsigned DummyWorldDescription::numTotalTests( void ) const { return 0; } + const SuiteDescription &DummyWorldDescription::suiteDescription( unsigned ) const { return _suite; } + SuiteDescription *DummyWorldDescription::firstSuite() { return 0; } + const SuiteDescription *DummyWorldDescription::firstSuite() const { return 0; } + void DummyWorldDescription::activateAllTests() {} + bool DummyWorldDescription::leaveOnly( const char * /*suiteName*/, const char * /*testName*/ ) { return false; } + + bool DummyWorldDescription::setUp() { return true;} + bool DummyWorldDescription::tearDown() { return true;} +} + diff --git a/cxxtest/DummyDescriptions.h b/cxxtest/DummyDescriptions.h new file mode 100644 index 0000000..c9215d1 --- /dev/null +++ b/cxxtest/DummyDescriptions.h @@ -0,0 +1,76 @@ +#ifndef __cxxtest__DummyDescriptions_h__ +#define __cxxtest__DummyDescriptions_h__ + +// +// DummyTestDescription, DummySuiteDescription and DummyWorldDescription +// + +#include <cxxtest/Descriptions.h> + +namespace CxxTest +{ + class DummyTestDescription : public TestDescription + { + public: + DummyTestDescription(); + + const char *file() const; + unsigned line() const; + const char *testName() const; + const char *suiteName() const; + bool setUp(); + void run(); + bool tearDown(); + + TestDescription *next(); + const TestDescription *next() const; + }; + + class DummySuiteDescription : public SuiteDescription + { + public: + DummySuiteDescription(); + + const char *file() const; + unsigned line() const; + const char *suiteName() const; + TestSuite *suite() const; + unsigned numTests() const; + const TestDescription &testDescription( unsigned ) const; + SuiteDescription *next(); + TestDescription *firstTest(); + const SuiteDescription *next() const; + const TestDescription *firstTest() const; + void activateAllTests(); + bool leaveOnly( const char * /*testName*/ ); + + bool setUp(); + bool tearDown(); + + private: + DummyTestDescription _test; + }; + + class DummyWorldDescription : public WorldDescription + { + public: + DummyWorldDescription(); + + unsigned numSuites( void ) const; + unsigned numTotalTests( void ) const; + const SuiteDescription &suiteDescription( unsigned ) const; + SuiteDescription *firstSuite(); + const SuiteDescription *firstSuite() const; + void activateAllTests(); + bool leaveOnly( const char * /*suiteName*/, const char * /*testName*/ = 0 ); + + bool setUp(); + bool tearDown(); + + private: + DummySuiteDescription _suite; + }; +} + +#endif // __cxxtest__DummyDescriptions_h__ + diff --git a/cxxtest/ErrorFormatter.h b/cxxtest/ErrorFormatter.h new file mode 100644 index 0000000..968c310 --- /dev/null +++ b/cxxtest/ErrorFormatter.h @@ -0,0 +1,281 @@ +#ifndef __cxxtest__ErrorFormatter_h__ +#define __cxxtest__ErrorFormatter_h__ + +// +// The ErrorFormatter is a TestListener that +// prints reports of the errors to an output +// stream. Since we cannot rely ou the standard +// iostreams, this header defines a base class +// analogout to std::ostream. +// + +#include <cxxtest/TestRunner.h> +#include <cxxtest/TestListener.h> +#include <cxxtest/TestTracker.h> +#include <cxxtest/ValueTraits.h> + +namespace CxxTest +{ + class OutputStream + { + public: + virtual ~OutputStream() {} + virtual void flush() {}; + virtual OutputStream &operator<<( unsigned /*number*/ ) { return *this; } + virtual OutputStream &operator<<( const char * /*string*/ ) { return *this; } + + typedef void (*Manipulator)( OutputStream & ); + + virtual OutputStream &operator<<( Manipulator m ) { m( *this ); return *this; } + static void endl( OutputStream &o ) { (o << "\n").flush(); } + }; + + class ErrorFormatter : public TestListener + { + public: + ErrorFormatter( OutputStream *o, const char *preLine = ":", const char *postLine = "" ) : + _dotting( true ), + _reported( false ), + _o(o), + _preLine(preLine), + _postLine(postLine) + { + } + + int run() + { + TestRunner::runAllTests( *this ); + return tracker().failedTests(); + } + + void enterWorld( const WorldDescription & /*desc*/ ) + { + (*_o) << "Running " << totalTests; + _o->flush(); + _dotting = true; + _reported = false; + } + + static void totalTests( OutputStream &o ) + { + char s[WorldDescription::MAX_STRLEN_TOTAL_TESTS]; + const WorldDescription &wd = tracker().world(); + o << wd.strTotalTests( s ) << (wd.numTotalTests() == 1 ? " test" : " tests"); + } + + void enterSuite( const SuiteDescription & ) + { + _reported = false; + } + + void enterTest( const TestDescription & ) + { + _reported = false; + } + + void leaveTest( const TestDescription & ) + { + if ( !tracker().testFailed() ) { + ((*_o) << ".").flush(); + _dotting = true; + } + } + + void leaveWorld( const WorldDescription &desc ) + { + if ( !tracker().failedTests() ) { + (*_o) << "OK!" << endl; + return; + } + newLine(); + (*_o) << "Failed " << tracker().failedTests() << " of " << totalTests << endl; + unsigned numPassed = desc.numTotalTests() - tracker().failedTests(); + (*_o) << "Success rate: " << (numPassed * 100 / desc.numTotalTests()) << "%" << endl; + } + + void trace( const char *file, unsigned line, const char *expression ) + { + stop( file, line ) << "Trace: " << + expression << endl; + } + + void warning( const char *file, unsigned line, const char *expression ) + { + stop( file, line ) << "Warning: " << + expression << endl; + } + + void failedTest( const char *file, unsigned line, const char *expression ) + { + stop( file, line ) << "Error: Test failed: " << + expression << endl; + } + + void failedAssert( const char *file, unsigned line, const char *expression ) + { + stop( file, line ) << "Error: Assertion failed: " << + expression << endl; + } + + void failedAssertEquals( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + stop( file, line ) << "Error: Expected (" << + xStr << " == " << yStr << "), found (" << + x << " != " << y << ")" << endl; + } + + void failedAssertSameData( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *sizeStr, const void *x, + const void *y, unsigned size ) + { + stop( file, line ) << "Error: Expected " << sizeStr << " (" << size << ") bytes to be equal at (" << + xStr << ") and (" << yStr << "), found:" << endl; + dump( x, size ); + (*_o) << " differs from" << endl; + dump( y, size ); + } + + void failedAssertDelta( const char *file, unsigned line, + const char *xStr, const char *yStr, const char *dStr, + const char *x, const char *y, const char *d ) + { + stop( file, line ) << "Error: Expected (" << + xStr << " == " << yStr << ") up to " << dStr << " (" << d << "), found (" << + x << " != " << y << ")" << endl; + } + + void failedAssertDiffers( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *value ) + { + stop( file, line ) << "Error: Expected (" << + xStr << " != " << yStr << "), found (" << + value << ")" << endl; + } + + void failedAssertLessThan( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + stop( file, line ) << "Error: Expected (" << + xStr << " < " << yStr << "), found (" << + x << " >= " << y << ")" << endl; + } + + void failedAssertLessThanEquals( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + stop( file, line ) << "Error: Expected (" << + xStr << " <= " << yStr << "), found (" << + x << " > " << y << ")" << endl; + } + + void failedAssertRelation( const char *file, unsigned line, + const char *relation, const char *xStr, const char *yStr, + const char *x, const char *y ) + { + stop( file, line ) << "Error: Expected " << relation << "( " << + xStr << ", " << yStr << " ), found !" << relation << "( " << x << ", " << y << " )" << endl; + } + + void failedAssertPredicate( const char *file, unsigned line, + const char *predicate, const char *xStr, const char *x ) + { + stop( file, line ) << "Error: Expected " << predicate << "( " << + xStr << " ), found !" << predicate << "( " << x << " )" << endl; + } + + void failedAssertThrows( const char *file, unsigned line, + const char *expression, const char *type, + bool otherThrown ) + { + stop( file, line ) << "Error: Expected (" << expression << ") to throw (" << + type << ") but it " << (otherThrown ? "threw something else" : "didn't throw") << + endl; + } + + void failedAssertThrowsNot( const char *file, unsigned line, const char *expression ) + { + stop( file, line ) << "Error: Expected (" << expression << ") not to throw, but it did" << + endl; + } + + protected: + OutputStream *outputStream() const + { + return _o; + } + + private: + ErrorFormatter( const ErrorFormatter & ); + ErrorFormatter &operator=( const ErrorFormatter & ); + + OutputStream &stop( const char *file, unsigned line ) + { + newLine(); + reportTest(); + return (*_o) << file << _preLine << line << _postLine << ": "; + } + + void newLine( void ) + { + if ( _dotting ) { + (*_o) << endl; + _dotting = false; + } + } + + void reportTest( void ) + { + if( _reported ) + return; + (*_o) << "In " << tracker().suite().suiteName() << "::" << tracker().test().testName() << ":" << endl; + _reported = true; + } + + void dump( const void *buffer, unsigned size ) + { + if ( !buffer ) + dumpNull(); + else + dumpBuffer( buffer, size ); + } + + void dumpNull() + { + (*_o) << " (null)" << endl; + } + + void dumpBuffer( const void *buffer, unsigned size ) + { + unsigned dumpSize = size; + if ( maxDumpSize() && dumpSize > maxDumpSize() ) + dumpSize = maxDumpSize(); + + const unsigned char *p = (const unsigned char *)buffer; + (*_o) << " { "; + for ( unsigned i = 0; i < dumpSize; ++ i ) + (*_o) << byteToHex( *p++ ) << " "; + if ( dumpSize < size ) + (*_o) << "... "; + (*_o) << "}" << endl; + } + + static void endl( OutputStream &o ) + { + OutputStream::endl( o ); + } + + bool _dotting; + bool _reported; + OutputStream *_o; + const char *_preLine; + const char *_postLine; + }; +}; + +#endif // __cxxtest__ErrorFormatter_h__ diff --git a/cxxtest/ErrorPrinter.h b/cxxtest/ErrorPrinter.h new file mode 100644 index 0000000..53d9425 --- /dev/null +++ b/cxxtest/ErrorPrinter.h @@ -0,0 +1,55 @@ +#ifndef __cxxtest__ErrorPrinter_h__ +#define __cxxtest__ErrorPrinter_h__ + +// +// The ErrorPrinter is a simple TestListener that +// just prints "OK" if everything goes well, otherwise +// reports the error in the format of compiler messages. +// The ErrorPrinter uses std::cout +// + +#include <cxxtest/Flags.h> + +#ifndef _CXXTEST_HAVE_STD +# define _CXXTEST_HAVE_STD +#endif // _CXXTEST_HAVE_STD + +#include <cxxtest/ErrorFormatter.h> +#include <cxxtest/StdValueTraits.h> + +#ifdef _CXXTEST_OLD_STD +# include <iostream.h> +#else // !_CXXTEST_OLD_STD +# include <iostream> +#endif // _CXXTEST_OLD_STD + +namespace CxxTest +{ + class ErrorPrinter : public ErrorFormatter + { + public: + ErrorPrinter( CXXTEST_STD(ostream) &o = CXXTEST_STD(cout), const char *preLine = ":", const char *postLine = "" ) : + ErrorFormatter( new Adapter(o), preLine, postLine ) {} + virtual ~ErrorPrinter() { delete outputStream(); } + + private: + class Adapter : public OutputStream + { + CXXTEST_STD(ostream) &_o; + public: + Adapter( CXXTEST_STD(ostream) &o ) : _o(o) {} + void flush() { _o.flush(); } + OutputStream &operator<<( const char *s ) { _o << s; return *this; } + OutputStream &operator<<( Manipulator m ) { return OutputStream::operator<<( m ); } + OutputStream &operator<<( unsigned i ) + { + char s[1 + 3 * sizeof(unsigned)]; + numberToString( i, s ); + _o << s; + return *this; + } + }; + }; +} + +#endif // __cxxtest__ErrorPrinter_h__ diff --git a/cxxtest/Flags.h b/cxxtest/Flags.h new file mode 100644 index 0000000..be2f9f2 --- /dev/null +++ b/cxxtest/Flags.h @@ -0,0 +1,121 @@ +#ifndef __cxxtest__Flags_h__ +#define __cxxtest__Flags_h__ + +// +// These are the flags that control CxxTest +// + +#if !defined(CXXTEST_FLAGS) +# define CXXTEST_FLAGS +#endif // !CXXTEST_FLAGS + +#if defined(CXXTEST_HAVE_EH) && !defined(_CXXTEST_HAVE_EH) +# define _CXXTEST_HAVE_EH +#endif // CXXTEST_HAVE_EH + +#if defined(CXXTEST_HAVE_STD) && !defined(_CXXTEST_HAVE_STD) +# define _CXXTEST_HAVE_STD +#endif // CXXTEST_HAVE_STD + +#if defined(CXXTEST_OLD_TEMPLATE_SYNTAX) && !defined(_CXXTEST_OLD_TEMPLATE_SYNTAX) +# define _CXXTEST_OLD_TEMPLATE_SYNTAX +#endif // CXXTEST_OLD_TEMPLATE_SYNTAX + +#if defined(CXXTEST_OLD_STD) && !defined(_CXXTEST_OLD_STD) +# define _CXXTEST_OLD_STD +#endif // CXXTEST_OLD_STD + +#if defined(CXXTEST_ABORT_TEST_ON_FAIL) && !defined(_CXXTEST_ABORT_TEST_ON_FAIL) +# define _CXXTEST_ABORT_TEST_ON_FAIL +#endif // CXXTEST_ABORT_TEST_ON_FAIL + +#if defined(CXXTEST_NO_COPY_CONST) && !defined(_CXXTEST_NO_COPY_CONST) +# define _CXXTEST_NO_COPY_CONST +#endif // CXXTEST_NO_COPY_CONST + +#if defined(CXXTEST_FACTOR) && !defined(_CXXTEST_FACTOR) +# define _CXXTEST_FACTOR +#endif // CXXTEST_FACTOR + +#if defined(CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION) && !defined(_CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION) +# define _CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION +#endif // CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION + +#if defined(CXXTEST_LONGLONG) +# if defined(_CXXTEST_LONGLONG) +# undef _CXXTEST_LONGLONG +# endif +# define _CXXTEST_LONGLONG CXXTEST_LONGLONG +#endif // CXXTEST_LONGLONG + +#ifndef CXXTEST_MAX_DUMP_SIZE +# define CXXTEST_MAX_DUMP_SIZE 0 +#endif // CXXTEST_MAX_DUMP_SIZE + +#if defined(_CXXTEST_ABORT_TEST_ON_FAIL) && !defined(CXXTEST_DEFAULT_ABORT) +# define CXXTEST_DEFAULT_ABORT true +#endif // _CXXTEST_ABORT_TEST_ON_FAIL && !CXXTEST_DEFAULT_ABORT + +#if !defined(CXXTEST_DEFAULT_ABORT) +# define CXXTEST_DEFAULT_ABORT false +#endif // !CXXTEST_DEFAULT_ABORT + +#if defined(_CXXTEST_ABORT_TEST_ON_FAIL) && !defined(_CXXTEST_HAVE_EH) +# warning "CXXTEST_ABORT_TEST_ON_FAIL is meaningless without CXXTEST_HAVE_EH" +# undef _CXXTEST_ABORT_TEST_ON_FAIL +#endif // _CXXTEST_ABORT_TEST_ON_FAIL && !_CXXTEST_HAVE_EH + +// +// Some minimal per-compiler configuration to allow us to compile +// + +#ifdef __BORLANDC__ +# if __BORLANDC__ <= 0x520 // Borland C++ 5.2 or earlier +# ifndef _CXXTEST_OLD_STD +# define _CXXTEST_OLD_STD +# endif +# ifndef _CXXTEST_OLD_TEMPLATE_SYNTAX +# define _CXXTEST_OLD_TEMPLATE_SYNTAX +# endif +# endif +# if __BORLANDC__ >= 0x540 // C++ Builder 4.0 or later +# ifndef _CXXTEST_NO_COPY_CONST +# define _CXXTEST_NO_COPY_CONST +# endif +# ifndef _CXXTEST_LONGLONG +# define _CXXTEST_LONGLONG __int64 +# endif +# endif +#endif // __BORLANDC__ + +#ifdef _MSC_VER // Visual C++ +# ifndef _CXXTEST_LONGLONG +# define _CXXTEST_LONGLONG __int64 +# endif +# if (_MSC_VER >= 0x51E) +# ifndef _CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION +# define _CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION +# endif +# endif +# pragma warning( disable : 4127 ) +# pragma warning( disable : 4290 ) +# pragma warning( disable : 4511 ) +# pragma warning( disable : 4512 ) +# pragma warning( disable : 4514 ) +#endif // _MSC_VER + +#ifdef __GNUC__ +# if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 9) +# ifndef _CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION +# define _CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION +# endif +# endif +#endif // __GNUC__ + +#ifdef __DMC__ // Digital Mars +# ifndef _CXXTEST_OLD_STD +# define _CXXTEST_OLD_STD +# endif +#endif + +#endif // __cxxtest__Flags_h__ diff --git a/cxxtest/GlobalFixture.cpp b/cxxtest/GlobalFixture.cpp new file mode 100644 index 0000000..29c6ab8 --- /dev/null +++ b/cxxtest/GlobalFixture.cpp @@ -0,0 +1,23 @@ +#ifndef __cxxtest__GlobalFixture_cpp__ +#define __cxxtest__GlobalFixture_cpp__ + +#include <cxxtest/GlobalFixture.h> + +namespace CxxTest +{ + bool GlobalFixture::setUpWorld() { return true; } + bool GlobalFixture::tearDownWorld() { return true; } + bool GlobalFixture::setUp() { return true; } + bool GlobalFixture::tearDown() { return true; } + + GlobalFixture::GlobalFixture() { attach( _list ); } + GlobalFixture::~GlobalFixture() { detach( _list ); } + + GlobalFixture *GlobalFixture::firstGlobalFixture() { return (GlobalFixture *)_list.head(); } + GlobalFixture *GlobalFixture::lastGlobalFixture() { return (GlobalFixture *)_list.tail(); } + GlobalFixture *GlobalFixture::nextGlobalFixture() { return (GlobalFixture *)next(); } + GlobalFixture *GlobalFixture::prevGlobalFixture() { return (GlobalFixture *)prev(); } +} + +#endif // __cxxtest__GlobalFixture_cpp__ + diff --git a/cxxtest/GlobalFixture.h b/cxxtest/GlobalFixture.h new file mode 100644 index 0000000..c817363 --- /dev/null +++ b/cxxtest/GlobalFixture.h @@ -0,0 +1,30 @@ +#ifndef __cxxtest__GlobalFixture_h__ +#define __cxxtest__GlobalFixture_h__ + +#include <cxxtest/LinkedList.h> + +namespace CxxTest +{ + class GlobalFixture : public Link + { + public: + virtual bool setUpWorld(); + virtual bool tearDownWorld(); + virtual bool setUp(); + virtual bool tearDown(); + + GlobalFixture(); + ~GlobalFixture(); + + static GlobalFixture *firstGlobalFixture(); + static GlobalFixture *lastGlobalFixture(); + GlobalFixture *nextGlobalFixture(); + GlobalFixture *prevGlobalFixture(); + + private: + static List _list; + }; +} + +#endif // __cxxtest__GlobalFixture_h__ + diff --git a/cxxtest/Gui.h b/cxxtest/Gui.h new file mode 100644 index 0000000..ac53b29 --- /dev/null +++ b/cxxtest/Gui.h @@ -0,0 +1,178 @@ +#ifndef __CXXTEST__GUI_H +#define __CXXTEST__GUI_H + +// +// GuiListener is a simple base class for the differes GUIs +// GuiTuiRunner<GuiT, TuiT> combines a GUI with a text-mode error formatter +// + +#include <cxxtest/TeeListener.h> + +namespace CxxTest +{ + class GuiListener : public TestListener + { + public: + GuiListener() : _state( GREEN_BAR ) {} + virtual ~GuiListener() {} + + virtual void runGui( int &argc, char **argv, TestListener &listener ) + { + enterGui( argc, argv ); + TestRunner::runAllTests( listener ); + leaveGui(); + } + + virtual void enterGui( int & /*argc*/, char ** /*argv*/ ) {} + virtual void leaveGui() {} + + // + // The easy way is to implement these functions: + // + virtual void guiEnterWorld( unsigned /*numTotalTests*/ ) {} + virtual void guiEnterSuite( const char * /*suiteName*/ ) {} + virtual void guiEnterTest( const char * /*suiteName*/, const char * /*testName*/ ) {} + virtual void yellowBar() {} + virtual void redBar() {} + + // + // The hard way is this: + // + void enterWorld( const WorldDescription &d ) { guiEnterWorld( d.numTotalTests() ); } + void enterSuite( const SuiteDescription &d ) { guiEnterSuite( d.suiteName() ); } + void enterTest( const TestDescription &d ) { guiEnterTest( d.suiteName(), d.testName() ); } + void leaveTest( const TestDescription & ) {} + void leaveSuite( const SuiteDescription & ) {} + void leaveWorld( const WorldDescription & ) {} + + void warning( const char * /*file*/, unsigned /*line*/, const char * /*expression*/ ) + { + yellowBarSafe(); + } + + void failedTest( const char * /*file*/, unsigned /*line*/, const char * /*expression*/ ) + { + redBarSafe(); + } + + void failedAssert( const char * /*file*/, unsigned /*line*/, const char * /*expression*/ ) + { + redBarSafe(); + } + + void failedAssertEquals( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*x*/, const char * /*y*/ ) + { + redBarSafe(); + } + + void failedAssertSameData( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*sizeStr*/, const void * /*x*/, + const void * /*y*/, unsigned /*size*/ ) + { + redBarSafe(); + } + + void failedAssertDelta( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, const char * /*dStr*/, + const char * /*x*/, const char * /*y*/, const char * /*d*/ ) + { + redBarSafe(); + } + + void failedAssertDiffers( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*value*/ ) + { + redBarSafe(); + } + + void failedAssertLessThan( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*x*/, const char * /*y*/ ) + { + redBarSafe(); + } + + void failedAssertLessThanEquals( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*x*/, const char * /*y*/ ) + { + redBarSafe(); + } + + void failedAssertPredicate( const char * /*file*/, unsigned /*line*/, + const char * /*predicate*/, const char * /*xStr*/, const char * /*x*/ ) + { + redBarSafe(); + } + + void failedAssertRelation( const char * /*file*/, unsigned /*line*/, + const char * /*relation*/, const char * /*xStr*/, const char * /*yStr*/, + const char * /*x*/, const char * /*y*/ ) + { + redBarSafe(); + } + + void failedAssertThrows( const char * /*file*/, unsigned /*line*/, + const char * /*expression*/, const char * /*type*/, + bool /*otherThrown*/ ) + { + redBarSafe(); + } + + void failedAssertThrowsNot( const char * /*file*/, unsigned /*line*/, + const char * /*expression*/ ) + { + redBarSafe(); + } + + protected: + void yellowBarSafe() + { + if ( _state < YELLOW_BAR ) { + yellowBar(); + _state = YELLOW_BAR; + } + } + + void redBarSafe() + { + if ( _state < RED_BAR ) { + redBar(); + _state = RED_BAR; + } + } + + private: + enum { GREEN_BAR, YELLOW_BAR, RED_BAR } _state; + }; + + template<class GuiT, class TuiT> + class GuiTuiRunner : public TeeListener + { + int &_argc; + char **_argv; + GuiT _gui; + TuiT _tui; + + public: + GuiTuiRunner( int &argc, char **argv ) : + _argc( argc ), + _argv( argv ) + { + setFirst( _gui ); + setSecond( _tui ); + } + + int run() + { + _gui.runGui( _argc, _argv, *this ); + return tracker().failedTests(); + } + }; +}; + +#endif //__CXXTEST__GUI_H diff --git a/cxxtest/LinkedList.cpp b/cxxtest/LinkedList.cpp new file mode 100644 index 0000000..fb81d64 --- /dev/null +++ b/cxxtest/LinkedList.cpp @@ -0,0 +1,172 @@ +#ifndef __cxxtest__LinkedList_cpp__ +#define __cxxtest__LinkedList_cpp__ + +#include <cxxtest/LinkedList.h> + +namespace CxxTest +{ + List GlobalFixture::_list = { 0, 0 }; + List RealSuiteDescription::_suites = { 0, 0 }; + + void List::initialize() + { + _head = _tail = 0; + } + + Link *List::head() + { + Link *l = _head; + while ( l && !l->active() ) + l = l->next(); + return l; + } + + const Link *List::head() const + { + Link *l = _head; + while ( l && !l->active() ) + l = l->next(); + return l; + } + + Link *List::tail() + { + Link *l = _tail; + while ( l && !l->active() ) + l = l->prev(); + return l; + } + + const Link *List::tail() const + { + Link *l = _tail; + while ( l && !l->active() ) + l = l->prev(); + return l; + } + + bool List::empty() const + { + return (_head == 0); + } + + unsigned List::size() const + { + unsigned count = 0; + for ( const Link *l = head(); l != 0; l = l->next() ) + ++ count; + return count; + } + + Link *List::nth( unsigned n ) + { + Link *l = head(); + while ( n -- ) + l = l->next(); + return l; + } + + void List::activateAll() + { + for ( Link *l = _head; l != 0; l = l->justNext() ) + l->setActive( true ); + } + + void List::leaveOnly( const Link &link ) + { + for ( Link *l = head(); l != 0; l = l->next() ) + if ( l != &link ) + l->setActive( false ); + } + + Link::Link() : + _next( 0 ), + _prev( 0 ), + _active( true ) + { + } + + Link::~Link() + { + } + + bool Link::active() const + { + return _active; + } + + void Link::setActive( bool value ) + { + _active = value; + } + + Link * Link::justNext() + { + return _next; + } + + Link * Link::justPrev() + { + return _prev; + } + + Link * Link::next() + { + Link *l = _next; + while ( l && !l->_active ) + l = l->_next; + return l; + } + + Link * Link::prev() + { + Link *l = _prev; + while ( l && !l->_active ) + l = l->_prev; + return l; + } + + const Link * Link::next() const + { + Link *l = _next; + while ( l && !l->_active ) + l = l->_next; + return l; + } + + const Link * Link::prev() const + { + Link *l = _prev; + while ( l && !l->_active ) + l = l->_prev; + return l; + } + + void Link::attach( List &l ) + { + if ( l._tail ) + l._tail->_next = this; + + _prev = l._tail; + _next = 0; + + if ( l._head == 0 ) + l._head = this; + l._tail = this; + } + + void Link::detach( List &l ) + { + if ( _prev ) + _prev->_next = _next; + else + l._head = _next; + + if ( _next ) + _next->_prev = _prev; + else + l._tail = _prev; + } +}; + +#endif // __cxxtest__LinkedList_cpp__ diff --git a/cxxtest/LinkedList.h b/cxxtest/LinkedList.h new file mode 100644 index 0000000..983a6e2 --- /dev/null +++ b/cxxtest/LinkedList.h @@ -0,0 +1,65 @@ +#ifndef __cxxtest__LinkedList_h__ +#define __cxxtest__LinkedList_h__ + +#include <cxxtest/Flags.h> + +namespace CxxTest +{ + struct List; + class Link; + + struct List + { + Link *_head; + Link *_tail; + + void initialize(); + + Link *head(); + const Link *head() const; + Link *tail(); + const Link *tail() const; + + bool empty() const; + unsigned size() const; + Link *nth( unsigned n ); + + void activateAll(); + void leaveOnly( const Link &link ); + }; + + class Link + { + public: + Link(); + virtual ~Link(); + + bool active() const; + void setActive( bool value = true ); + + Link *justNext(); + Link *justPrev(); + + Link *next(); + Link *prev(); + const Link *next() const; + const Link *prev() const; + + virtual bool setUp() = 0; + virtual bool tearDown() = 0; + + void attach( List &l ); + void detach( List &l ); + + private: + Link *_next; + Link *_prev; + bool _active; + + Link( const Link & ); + Link &operator=( const Link & ); + }; +} + +#endif // __cxxtest__LinkedList_h__ + diff --git a/cxxtest/Mock.h b/cxxtest/Mock.h new file mode 100644 index 0000000..647088e --- /dev/null +++ b/cxxtest/Mock.h @@ -0,0 +1,350 @@ +#ifndef __cxxtest__Mock_h__ +#define __cxxtest__Mock_h__ + +// +// The default namespace is T:: +// +#ifndef CXXTEST_MOCK_NAMESPACE +# define CXXTEST_MOCK_NAMESPACE T +#endif // CXXTEST_MOCK_NAMESPACE + +// +// MockTraits: What to return when no mock object has been created +// +#define __CXXTEST_MOCK__TRAITS \ + namespace CXXTEST_MOCK_NAMESPACE \ + { \ + template<class T> \ + class MockTraits \ + { \ + public: \ + static T defaultValue() { return 0; } \ + }; \ + }; + +// +// extern "C" when needed +// +#ifdef __cplusplus +# define CXXTEST_EXTERN_C extern "C" +#else +# define CXXTEST_EXTERN_C +#endif // __cplusplus + +// +// Prototypes: For "normal" headers +// +#define __CXXTEST_MOCK__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + namespace CXXTEST_MOCK_NAMESPACE { TYPE NAME ARGS; } + +#define __CXXTEST_MOCK_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__PROTOTYPE( MOCK, void, NAME, ARGS, REAL, CALL ) + +#define __CXXTEST_SUPPLY__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + TYPE REAL ARGS; + +#define __CXXTEST_SUPPLY_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY__PROTOTYPE( MOCK, void, NAME, ARGS, REAL, CALL ) + +// +// Class declarations: For test files +// +#define __CXXTEST_MOCK__CLASS_DECLARATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + namespace CXXTEST_MOCK_NAMESPACE { \ + class Base_##MOCK : public CxxTest::Link \ + { \ + public: \ + Base_##MOCK(); \ + ~Base_##MOCK(); \ + bool setUp(); \ + bool tearDown(); \ + \ + static Base_##MOCK ¤t(); \ + \ + virtual TYPE NAME ARGS = 0; \ + \ + private: \ + static CxxTest::List _list; \ + }; \ + \ + class Real_##MOCK : public Base_##MOCK \ + { \ + public: \ + TYPE NAME ARGS; \ + }; \ + \ + class _Unimplemented_##MOCK : public Base_##MOCK \ + { \ + public: \ + TYPE NAME ARGS; \ + }; \ + } + +#define __CXXTEST_MOCK_VOID__CLASS_DECLARATION( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__CLASS_DECLARATION( MOCK, void, NAME, ARGS, REAL, CALL ) + +#define __CXXTEST_SUPPLY__CLASS_DECLARATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + namespace CXXTEST_MOCK_NAMESPACE { \ + class Base_##MOCK : public CxxTest::Link \ + { \ + public: \ + Base_##MOCK(); \ + ~Base_##MOCK(); \ + bool setUp(); \ + bool tearDown(); \ + \ + static Base_##MOCK ¤t(); \ + \ + virtual TYPE NAME ARGS = 0; \ + \ + private: \ + static CxxTest::List _list; \ + }; \ + \ + class _Unimplemented_##MOCK : public Base_##MOCK \ + { \ + public: \ + TYPE NAME ARGS; \ + }; \ + } + +#define __CXXTEST_SUPPLY_VOID__CLASS_DECLARATION( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY__CLASS_DECLARATION( MOCK, void, NAME, ARGS, REAL, CALL ) + +// +// Class implementation: For test source files +// +#define __CXXTEST_MOCK__COMMON_CLASS_IMPLEMENTATION( MOCK, NAME ) \ + namespace CXXTEST_MOCK_NAMESPACE { \ + \ + CxxTest::List Base_##MOCK::_list = { 0, 0 }; \ + \ + Base_##MOCK::Base_##MOCK() { attach( _list ); } \ + Base_##MOCK::~Base_##MOCK() { detach( _list ); } \ + bool Base_##MOCK::setUp() { return true; } \ + bool Base_##MOCK::tearDown() { return true; } \ + \ + Base_##MOCK &Base_##MOCK::current() \ + { \ + if ( _list.empty() ) \ + static _Unimplemented_##MOCK unimplemented; \ + return *(Base_##MOCK *)_list.tail(); \ + } \ + } + +#define __CXXTEST_MOCK__CLASS_IMPLEMENTATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__COMMON_CLASS_IMPLEMENTATION( MOCK, NAME ) \ + namespace CXXTEST_MOCK_NAMESPACE { \ + TYPE Real_##MOCK::NAME ARGS \ + { \ + return REAL CALL; \ + } \ + \ + TYPE _Unimplemented_##MOCK::NAME ARGS \ + { \ + while ( false ) \ + return NAME CALL; \ + __CXXTEST_MOCK_UNIMPLEMENTED( NAME, ARGS ); \ + return MockTraits<TYPE>::defaultValue(); \ + } \ + \ + TYPE NAME ARGS \ + { \ + return Base_##MOCK::current().NAME CALL; \ + } \ + } + +#define __CXXTEST_MOCK_VOID__CLASS_IMPLEMENTATION( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__COMMON_CLASS_IMPLEMENTATION( MOCK, NAME ) \ + namespace CXXTEST_MOCK_NAMESPACE { \ + void Real_##MOCK::NAME ARGS \ + { \ + REAL CALL; \ + } \ + \ + void _Unimplemented_##MOCK::NAME ARGS \ + { \ + while ( false ) \ + NAME CALL; \ + __CXXTEST_MOCK_UNIMPLEMENTED( NAME, ARGS ); \ + } \ + \ + void NAME ARGS \ + { \ + Base_##MOCK::current().NAME CALL; \ + } \ + } + +#define __CXXTEST_SUPPLY__CLASS_IMPLEMENTATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__COMMON_CLASS_IMPLEMENTATION( MOCK, NAME ) \ + namespace CXXTEST_MOCK_NAMESPACE { \ + TYPE _Unimplemented_##MOCK::NAME ARGS \ + { \ + while ( false ) \ + return NAME CALL; \ + __CXXTEST_MOCK_UNIMPLEMENTED( NAME, ARGS ); \ + return MockTraits<TYPE>::defaultValue(); \ + } \ + } \ + \ + TYPE REAL ARGS \ + { \ + return CXXTEST_MOCK_NAMESPACE::Base_##MOCK::current().NAME CALL; \ + } + +#define __CXXTEST_SUPPLY_VOID__CLASS_IMPLEMENTATION( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__COMMON_CLASS_IMPLEMENTATION( MOCK, NAME ) \ + namespace CXXTEST_MOCK_NAMESPACE { \ + void _Unimplemented_##MOCK::NAME ARGS \ + { \ + while ( false ) \ + NAME CALL; \ + __CXXTEST_MOCK_UNIMPLEMENTED( NAME, ARGS ); \ + } \ + } \ + \ + void REAL ARGS \ + { \ + CXXTEST_MOCK_NAMESPACE::Base_##MOCK::current().NAME CALL; \ + } \ + +// +// Error for calling mock function w/o object +// +#define __CXXTEST_MOCK_UNIMPLEMENTED( NAME, ARGS ) \ + TS_FAIL( CXXTEST_MOCK_NAMESPACE_STR #NAME #ARGS " called with no " \ + CXXTEST_MOCK_NAMESPACE_STR "Base_" #NAME " object" ); \ + +#define CXXTEST_MOCK_NAMESPACE_STR __CXXTEST_STR(CXXTEST_MOCK_NAMESPACE) "::" +#define __CXXTEST_STR(X) __CXXTEST_XSTR(X) +#define __CXXTEST_XSTR(X) #X + +#if defined(CXXTEST_MOCK_TEST_SOURCE_FILE) +// +// Test source file: Prototypes, class declarations and implementation +// +#include <cxxtest/TestSuite.h> + +__CXXTEST_MOCK__TRAITS; + +#define CXXTEST_MOCK( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__CLASS_DECLARATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__CLASS_IMPLEMENTATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_MOCK_VOID( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK_VOID__CLASS_DECLARATION( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK_VOID__CLASS_IMPLEMENTATION( MOCK, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_SUPPLY( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY__CLASS_DECLARATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY__CLASS_IMPLEMENTATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_SUPPLY_VOID( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY_VOID__CLASS_DECLARATION( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY_VOID__CLASS_IMPLEMENTATION( MOCK, NAME, ARGS, REAL, CALL ) + +#elif defined(CXXTEST_FLAGS) || defined(CXXTEST_RUNNING) +// +// Test file other than source: Prototypes and class declarations +// +#include <cxxtest/TestSuite.h> + +__CXXTEST_MOCK__TRAITS; + +#define CXXTEST_MOCK( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__CLASS_DECLARATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_MOCK_VOID( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK_VOID__CLASS_DECLARATION( MOCK, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_SUPPLY( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY__CLASS_DECLARATION( MOCK, TYPE, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_SUPPLY_VOID( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY_VOID__CLASS_DECLARATION( MOCK, NAME, ARGS, REAL, CALL ) + +#elif defined(CXXTEST_MOCK_REAL_SOURCE_FILE) +// +// Real source file: "Real" implementations +// +#define CXXTEST_MOCK( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + namespace CXXTEST_MOCK_NAMESPACE { TYPE NAME ARGS { return REAL CALL; } } + +#define CXXTEST_MOCK_VOID( MOCK, NAME, ARGS, REAL, CALL ) \ + namespace CXXTEST_MOCK_NAMESPACE { void NAME ARGS { REAL CALL; } } + +#else +// +// Ordinary header file: Just prototypes +// + +#define CXXTEST_MOCK( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_MOCK_VOID( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_MOCK_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_SUPPLY( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_SUPPLY_VOID( MOCK, NAME, ARGS, REAL, CALL ) \ + __CXXTEST_SUPPLY_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) + +#endif // Ordinary header file + +// +// How to supply extern "C" functions +// +#define CXXTEST_SUPPLY_C( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + CXXTEST_EXTERN_C __CXXTEST_SUPPLY__PROTOTYPE( MOCK, TYPE, NAME, ARGS, REAL, CALL ) \ + CXXTEST_SUPPLY( MOCK, TYPE, NAME, ARGS, REAL, CALL ) + +#define CXXTEST_SUPPLY_VOID_C( MOCK, NAME, ARGS, REAL, CALL ) \ + CXXTEST_EXTERN_C __CXXTEST_SUPPLY_VOID__PROTOTYPE( MOCK, NAME, ARGS, REAL, CALL ) \ + CXXTEST_SUPPLY_VOID( MOCK, NAME, ARGS, REAL, CALL ) + +// +// Usually we mean the global namespace +// +#define CXXTEST_MOCK_GLOBAL( TYPE, NAME, ARGS, CALL ) \ + CXXTEST_MOCK( NAME, TYPE, NAME, ARGS, ::NAME, CALL ) + +#define CXXTEST_MOCK_VOID_GLOBAL( NAME, ARGS, CALL ) \ + CXXTEST_MOCK_VOID( NAME, NAME, ARGS, ::NAME, CALL ) + +#define CXXTEST_SUPPLY_GLOBAL( TYPE, NAME, ARGS, CALL ) \ + CXXTEST_SUPPLY( NAME, TYPE, NAME, ARGS, NAME, CALL ) + +#define CXXTEST_SUPPLY_VOID_GLOBAL( NAME, ARGS, CALL ) \ + CXXTEST_SUPPLY_VOID( NAME, NAME, ARGS, NAME, CALL ) + +#define CXXTEST_SUPPLY_GLOBAL_C( TYPE, NAME, ARGS, CALL ) \ + CXXTEST_SUPPLY_C( NAME, TYPE, NAME, ARGS, NAME, CALL ) + +#define CXXTEST_SUPPLY_VOID_GLOBAL_C( NAME, ARGS, CALL ) \ + CXXTEST_SUPPLY_VOID_C( NAME, NAME, ARGS, NAME, CALL ) + +// +// What to return when no mock object has been created. +// The default value of 0 usually works, but some cases may need this. +// +#define CXXTEST_MOCK_DEFAULT_VALUE( TYPE, VALUE ) \ + namespace CXXTEST_MOCK_NAMESPACE \ + { \ + template<> \ + class MockTraits<TYPE> \ + { \ + public: \ + static TYPE defaultValue() { return VALUE; } \ + }; \ + } + +#endif // __cxxtest__Mock_h__ diff --git a/cxxtest/ParenPrinter.h b/cxxtest/ParenPrinter.h new file mode 100644 index 0000000..9ecf310 --- /dev/null +++ b/cxxtest/ParenPrinter.h @@ -0,0 +1,21 @@ +#ifndef __cxxtest__ParenPrinter_h__ +#define __cxxtest__ParenPrinter_h__ + +// +// The ParenPrinter is identical to the ErrorPrinter, except it +// prints the line number in a format expected by some compilers +// (notably, MSVC). +// + +#include <cxxtest/ErrorPrinter.h> + +namespace CxxTest +{ + class ParenPrinter : public ErrorPrinter + { + public: + ParenPrinter( CXXTEST_STD(ostream) &o = CXXTEST_STD(cout) ) : ErrorPrinter( o, "(", ")" ) {} + }; +} + +#endif // __cxxtest__ParenPrinter_h__ diff --git a/cxxtest/QtGui.h b/cxxtest/QtGui.h new file mode 100644 index 0000000..8898251 --- /dev/null +++ b/cxxtest/QtGui.h @@ -0,0 +1,271 @@ +#ifndef __cxxtest__QtGui_h__ +#define __cxxtest__QtGui_h__ + +// +// The QtGui displays a simple progress bar using the Qt Toolkit. It +// has been tested with versions 2.x and 3.x. +// +// Apart from normal Qt command-line arguments, it accepts the following options: +// -minimized Start minimized, pop up on error +// -keep Don't close the window at the end +// -title TITLE Set the window caption +// +// If both are -minimized and -keep specified, GUI will only keep the +// window if it's in focus. +// + +#include <cxxtest/Gui.h> + +#include <qapplication.h> +#include <qglobal.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qmessagebox.h> +#include <qpixmap.h> +#include <qprogressbar.h> +#include <qstatusbar.h> + +namespace CxxTest +{ + class QtGui : public GuiListener + { + public: + void enterGui( int &argc, char **argv ) + { + parseCommandLine( argc, argv ); + createApplication( argc, argv ); + } + + void enterWorld( const WorldDescription &wd ) + { + createWindow( wd ); + processEvents(); + } + + void guiEnterSuite( const char *suiteName ) + { + showSuiteName( suiteName ); + } + + void guiEnterTest( const char *suiteName, const char *testName ) + { + setCaption( suiteName, testName ); + advanceProgressBar(); + showTestName( testName ); + showTestsDone( _progressBar->progress() ); + processEvents(); + } + + void yellowBar() + { + setColor( 255, 255, 0 ); + setIcon( QMessageBox::Warning ); + getTotalTests(); + processEvents(); + } + + void redBar() + { + if ( _startMinimized && _mainWindow->isMinimized() ) + showNormal(); + setColor( 255, 0, 0 ); + setIcon( QMessageBox::Critical ); + getTotalTests(); + processEvents(); + } + + void leaveGui() + { + if ( keep() ) { + showSummary(); + _application->exec(); + } + else + _mainWindow->close( true ); + } + + private: + QString _title; + bool _startMinimized, _keep; + unsigned _numTotalTests; + QString _strTotalTests; + QApplication *_application; + QWidget *_mainWindow; + QVBoxLayout *_layout; + QProgressBar *_progressBar; + QStatusBar *_statusBar; + QLabel *_suiteName, *_testName, *_testsDone; + + void parseCommandLine( int argc, char **argv ) + { + _startMinimized = _keep = false; + _title = argv[0]; + + for ( int i = 1; i < argc; ++ i ) { + QString arg( argv[i] ); + if ( arg == "-minimized" ) + _startMinimized = true; + else if ( arg == "-keep" ) + _keep = true; + else if ( arg == "-title" && (i + 1 < argc) ) + _title = argv[++i]; + } + } + + void createApplication( int &argc, char **argv ) + { + _application = new QApplication( argc, argv ); + } + + void createWindow( const WorldDescription &wd ) + { + getTotalTests( wd ); + createMainWindow(); + createProgressBar(); + createStatusBar(); + setMainWidget(); + if ( _startMinimized ) + showMinimized(); + else + showNormal(); + } + + void getTotalTests() + { + getTotalTests( tracker().world() ); + } + + void getTotalTests( const WorldDescription &wd ) + { + _numTotalTests = wd.numTotalTests(); + char s[WorldDescription::MAX_STRLEN_TOTAL_TESTS]; + _strTotalTests = wd.strTotalTests( s ); + } + + void createMainWindow() + { + _mainWindow = new QWidget(); + _layout = new QVBoxLayout( _mainWindow ); + } + + void createProgressBar() + { + _layout->addWidget( _progressBar = new QProgressBar( _numTotalTests, _mainWindow ) ); + _progressBar->setProgress( 0 ); + setColor( 0, 255, 0 ); + setIcon( QMessageBox::Information ); + } + + void createStatusBar() + { + _layout->addWidget( _statusBar = new QStatusBar( _mainWindow ) ); + _statusBar->addWidget( _suiteName = new QLabel( _statusBar ), 2 ); + _statusBar->addWidget( _testName = new QLabel( _statusBar ), 4 ); + _statusBar->addWidget( _testsDone = new QLabel( _statusBar ), 1 ); + } + + void setMainWidget() + { + _application->setMainWidget( _mainWindow ); + } + + void showMinimized() + { + _mainWindow->showMinimized(); + } + + void showNormal() + { + _mainWindow->showNormal(); + centerWindow(); + } + + void setCaption( const QString &suiteName, const QString &testName ) + { + _mainWindow->setCaption( _title + " - " + suiteName + "::" + testName + "()" ); + } + + void showSuiteName( const QString &suiteName ) + { + _suiteName->setText( "class " + suiteName ); + } + + void advanceProgressBar() + { + _progressBar->setProgress( _progressBar->progress() + 1 ); + } + + void showTestName( const QString &testName ) + { + _testName->setText( testName + "()" ); + } + + void showTestsDone( unsigned testsDone ) + { + _testsDone->setText( asString( testsDone ) + " of " + _strTotalTests ); + } + + static QString asString( unsigned n ) + { + return QString::number( n ); + } + + void setColor( int r, int g, int b ) + { + QPalette palette = _progressBar->palette(); + palette.setColor( QColorGroup::Highlight, QColor( r, g, b ) ); + _progressBar->setPalette( palette ); + } + + void setIcon( QMessageBox::Icon icon ) + { +#if QT_VERSION >= 0x030000 + _mainWindow->setIcon( QMessageBox::standardIcon( icon ) ); +#else // Qt version < 3.0.0 + _mainWindow->setIcon( QMessageBox::standardIcon( icon, QApplication::style().guiStyle() ) ); +#endif // QT_VERSION + } + + void processEvents() + { + _application->processEvents(); + } + + void centerWindow() + { + QWidget *desktop = QApplication::desktop(); + int xCenter = desktop->x() + (desktop->width() / 2); + int yCenter = desktop->y() + (desktop->height() / 2); + + int windowWidth = (desktop->width() * 4) / 5; + int windowHeight = _mainWindow->height(); + _mainWindow->setGeometry( xCenter - (windowWidth / 2), yCenter - (windowHeight / 2), windowWidth, windowHeight ); + } + + bool keep() + { + if ( !_keep ) + return false; + if ( !_startMinimized ) + return true; + return (_mainWindow == _application->activeWindow()); + } + + void showSummary() + { + QString summary = _strTotalTests + (_numTotalTests == 1 ? " test" : " tests"); + if ( tracker().failedTests() ) + summary = "Failed " + asString( tracker().failedTests() ) + " of " + summary; + else + summary = summary + " passed"; + + _mainWindow->setCaption( _title + " - " + summary ); + + _statusBar->removeWidget( _suiteName ); + _statusBar->removeWidget( _testName ); + _testsDone->setText( summary ); + } + }; +}; + +#endif // __cxxtest__QtGui_h__ diff --git a/cxxtest/RealDescriptions.cpp b/cxxtest/RealDescriptions.cpp new file mode 100644 index 0000000..1e21ca7 --- /dev/null +++ b/cxxtest/RealDescriptions.cpp @@ -0,0 +1,311 @@ +#ifndef __cxxtest__RealDescriptions_cpp__ +#define __cxxtest__RealDescriptions_cpp__ + +// +// NOTE: If an error occur during world construction/deletion, CxxTest cannot +// know where the error originated. +// + +#include <cxxtest/RealDescriptions.h> + +namespace CxxTest +{ + RealTestDescription::RealTestDescription() + { + } + + RealTestDescription::RealTestDescription( List &argList, + SuiteDescription &argSuite, + unsigned argLine, + const char *argTestName ) + { + initialize( argList, argSuite, argLine, argTestName ); + } + + void RealTestDescription::initialize( List &argList, + SuiteDescription &argSuite, + unsigned argLine, + const char *argTestName ) + { + _suite = &argSuite; + _line = argLine; + _testName = argTestName; + attach( argList ); + } + + bool RealTestDescription::setUp() + { + if ( !suite() ) + return false; + + for ( GlobalFixture *gf = GlobalFixture::firstGlobalFixture(); gf != 0; gf = gf->nextGlobalFixture() ) { + bool ok; + _TS_TRY { ok = gf->setUp(); } + _TS_LAST_CATCH( { ok = false; } ); + + if ( !ok ) { + doFailTest( file(), line(), "Error in GlobalFixture::setUp()" ); + return false; + } + } + + _TS_TRY { + _TSM_ASSERT_THROWS_NOTHING( file(), line(), "Exception thrown from setUp()", suite()->setUp() ); + } + _TS_CATCH_ABORT( { return false; } ); + + return true; + } + + bool RealTestDescription::tearDown() + { + if ( !suite() ) + return false; + + _TS_TRY { + _TSM_ASSERT_THROWS_NOTHING( file(), line(), "Exception thrown from tearDown()", suite()->tearDown() ); + } + _TS_CATCH_ABORT( { return false; } ); + + for ( GlobalFixture *gf = GlobalFixture::lastGlobalFixture(); gf != 0; gf = gf->prevGlobalFixture() ) { + bool ok; + _TS_TRY { ok = gf->tearDown(); } + _TS_LAST_CATCH( { ok = false; } ); + + if ( !ok ) { + doFailTest( file(), line(), "Error in GlobalFixture::tearDown()" ); + return false; + } + } + + return true; + } + + const char *RealTestDescription::file() const { return _suite->file(); } + unsigned RealTestDescription::line() const { return _line; } + const char *RealTestDescription::testName() const { return _testName; } + const char *RealTestDescription::suiteName() const { return _suite->suiteName(); } + + TestDescription *RealTestDescription::next() { return (RealTestDescription *)Link::next(); } + const TestDescription *RealTestDescription::next() const { return (const RealTestDescription *)Link::next(); } + + TestSuite *RealTestDescription::suite() const { return _suite->suite(); } + + void RealTestDescription::run() + { + _TS_TRY { runTest(); } + _TS_CATCH_ABORT( {} ) + ___TSM_CATCH( file(), line(), "Exception thrown from test" ); + } + + RealSuiteDescription::RealSuiteDescription() {} + RealSuiteDescription::RealSuiteDescription( const char *argFile, + unsigned argLine, + const char *argSuiteName, + List &argTests ) + { + initialize( argFile, argLine, argSuiteName, argTests ); + } + + void RealSuiteDescription::initialize( const char *argFile, + unsigned argLine, + const char *argSuiteName, + List &argTests ) + { + _file = argFile; + _line = argLine; + _suiteName = argSuiteName; + _tests = &argTests; + + attach( _suites ); + } + + const char *RealSuiteDescription::file() const { return _file; } + unsigned RealSuiteDescription::line() const { return _line; } + const char *RealSuiteDescription::suiteName() const { return _suiteName; } + + TestDescription *RealSuiteDescription::firstTest() { return (RealTestDescription *)_tests->head(); } + const TestDescription *RealSuiteDescription::firstTest() const { return (const RealTestDescription *)_tests->head(); } + SuiteDescription *RealSuiteDescription::next() { return (RealSuiteDescription *)Link::next(); } + const SuiteDescription *RealSuiteDescription::next() const { return (const RealSuiteDescription *)Link::next(); } + + unsigned RealSuiteDescription::numTests() const { return _tests->size(); } + + const TestDescription &RealSuiteDescription::testDescription( unsigned i ) const + { + return *(RealTestDescription *)_tests->nth( i ); + } + + void RealSuiteDescription::activateAllTests() + { + _tests->activateAll(); + } + + bool RealSuiteDescription::leaveOnly( const char *testName ) + { + for ( TestDescription *td = firstTest(); td != 0; td = td->next() ) { + if ( stringsEqual( td->testName(), testName ) ) { + _tests->leaveOnly( *td ); + return true; + } + } + return false; + } + + StaticSuiteDescription::StaticSuiteDescription() {} + StaticSuiteDescription::StaticSuiteDescription( const char *argFile, unsigned argLine, + const char *argSuiteName, TestSuite &argSuite, + List &argTests ) : + RealSuiteDescription( argFile, argLine, argSuiteName, argTests ) + { + doInitialize( argSuite ); + } + + void StaticSuiteDescription::initialize( const char *argFile, unsigned argLine, + const char *argSuiteName, TestSuite &argSuite, + List &argTests ) + { + RealSuiteDescription::initialize( argFile, argLine, argSuiteName, argTests ); + doInitialize( argSuite ); + } + + void StaticSuiteDescription::doInitialize( TestSuite &argSuite ) + { + _suite = &argSuite; + } + + TestSuite *StaticSuiteDescription::suite() const + { + return _suite; + } + + bool StaticSuiteDescription::setUp() { return true; } + bool StaticSuiteDescription::tearDown() { return true; } + + CommonDynamicSuiteDescription::CommonDynamicSuiteDescription() {} + CommonDynamicSuiteDescription::CommonDynamicSuiteDescription( const char *argFile, unsigned argLine, + const char *argSuiteName, List &argTests, + unsigned argCreateLine, unsigned argDestroyLine ) : + RealSuiteDescription( argFile, argLine, argSuiteName, argTests ) + { + doInitialize( argCreateLine, argDestroyLine ); + } + + void CommonDynamicSuiteDescription::initialize( const char *argFile, unsigned argLine, + const char *argSuiteName, List &argTests, + unsigned argCreateLine, unsigned argDestroyLine ) + { + RealSuiteDescription::initialize( argFile, argLine, argSuiteName, argTests ); + doInitialize( argCreateLine, argDestroyLine ); + } + + void CommonDynamicSuiteDescription::doInitialize( unsigned argCreateLine, unsigned argDestroyLine ) + { + _createLine = argCreateLine; + _destroyLine = argDestroyLine; + } + + List &RealWorldDescription::suites() + { + return RealSuiteDescription::_suites; + } + + unsigned RealWorldDescription::numSuites( void ) const + { + return suites().size(); + } + + unsigned RealWorldDescription::numTotalTests( void ) const + { + unsigned count = 0; + for ( const SuiteDescription *sd = firstSuite(); sd != 0; sd = sd->next() ) + count += sd->numTests(); + return count; + } + + SuiteDescription *RealWorldDescription::firstSuite() + { + return (RealSuiteDescription *)suites().head(); + } + + const SuiteDescription *RealWorldDescription::firstSuite() const + { + return (const RealSuiteDescription *)suites().head(); + } + + const SuiteDescription &RealWorldDescription::suiteDescription( unsigned i ) const + { + return *(const RealSuiteDescription *)suites().nth( i ); + } + + void RealWorldDescription::activateAllTests() + { + suites().activateAll(); + for ( SuiteDescription *sd = firstSuite(); sd != 0; sd = sd->next() ) + sd->activateAllTests(); + } + + bool RealWorldDescription::leaveOnly( const char *suiteName, const char *testName ) + { + for ( SuiteDescription *sd = firstSuite(); sd != 0; sd = sd->next() ) { + if ( stringsEqual( sd->suiteName(), suiteName ) ) { + if ( testName ) + if ( !sd->leaveOnly( testName ) ) + return false; + suites().leaveOnly( *sd ); + return true; + } + } + return false; + } + + bool RealWorldDescription::setUp() + { + for ( GlobalFixture *gf = GlobalFixture::firstGlobalFixture(); gf != 0; gf = gf->nextGlobalFixture() ) { + bool ok; + _TS_TRY { ok = gf->setUpWorld(); } + _TS_LAST_CATCH( { ok = false; } ); + + if ( !ok ) { + reportError( "Error setting up world" ); + return false; + } + } + + return true; + } + + bool RealWorldDescription::tearDown() + { + for ( GlobalFixture *gf = GlobalFixture::lastGlobalFixture(); gf != 0; gf = gf->prevGlobalFixture() ) { + bool ok; + _TS_TRY { ok = gf->tearDownWorld(); } + _TS_LAST_CATCH( { ok = false; } ); + + if ( !ok ) { + reportError( "Error tearing down world" ); + return false; + } + } + + return true; + } + + void RealWorldDescription::reportError( const char *message ) + { + doWarn( __FILE__, 5, message ); + } + + void activateAllTests() + { + RealWorldDescription().activateAllTests(); + } + + bool leaveOnly( const char *suiteName, const char *testName ) + { + return RealWorldDescription().leaveOnly( suiteName, testName ); + } +} + +#endif // __cxxtest__RealDescriptions_cpp__ + diff --git a/cxxtest/RealDescriptions.h b/cxxtest/RealDescriptions.h new file mode 100644 index 0000000..14c457d --- /dev/null +++ b/cxxtest/RealDescriptions.h @@ -0,0 +1,223 @@ +#ifndef __cxxtest__RealDescriptions_h__ +#define __cxxtest__RealDescriptions_h__ + +// +// The "real" description classes +// + +#include <cxxtest/Descriptions.h> +#include <cxxtest/TestSuite.h> +#include <cxxtest/GlobalFixture.h> + +namespace CxxTest +{ + class RealTestDescription : public TestDescription + { + public: + RealTestDescription(); + RealTestDescription( List &argList, SuiteDescription &argSuite, unsigned argLine, const char *argTestName ); + void initialize( List &argList, SuiteDescription &argSuite, unsigned argLine, const char *argTestName ); + + const char *file() const; + unsigned line() const; + const char *testName() const; + const char *suiteName() const; + + TestDescription *next(); + const TestDescription *next() const; + + TestSuite *suite() const; + + bool setUp(); + void run(); + bool tearDown(); + + private: + RealTestDescription( const RealTestDescription & ); + RealTestDescription &operator=( const RealTestDescription & ); + + virtual void runTest() = 0; + + SuiteDescription *_suite; + unsigned _line; + const char *_testName; + }; + + class RealSuiteDescription : public SuiteDescription + { + public: + RealSuiteDescription(); + RealSuiteDescription( const char *argFile, unsigned argLine, const char *argSuiteName, List &argTests ); + + void initialize( const char *argFile, unsigned argLine, const char *argSuiteName, List &argTests ); + + const char *file() const; + unsigned line() const; + const char *suiteName() const; + + TestDescription *firstTest(); + const TestDescription *firstTest() const; + SuiteDescription *next(); + const SuiteDescription *next() const; + + unsigned numTests() const; + const TestDescription &testDescription( unsigned i ) const; + + void activateAllTests(); + bool leaveOnly( const char *testName ); + + private: + RealSuiteDescription( const RealSuiteDescription & ); + RealSuiteDescription &operator=( const RealSuiteDescription & ); + + const char *_file; + unsigned _line; + const char *_suiteName; + List *_tests; + + static List _suites; + friend class RealWorldDescription; + }; + + class StaticSuiteDescription : public RealSuiteDescription + { + public: + StaticSuiteDescription(); + StaticSuiteDescription( const char *argFile, unsigned argLine, + const char *argSuiteName, TestSuite &argSuite, + List &argTests ); + + void initialize( const char *argFile, unsigned argLine, + const char *argSuiteName, TestSuite &argSuite, + List &argTests ); + TestSuite *suite() const; + + bool setUp(); + bool tearDown(); + + private: + StaticSuiteDescription( const StaticSuiteDescription & ); + StaticSuiteDescription &operator=( const StaticSuiteDescription & ); + + void doInitialize( TestSuite &argSuite ); + + TestSuite *_suite; + }; + + class CommonDynamicSuiteDescription : public RealSuiteDescription + { + public: + CommonDynamicSuiteDescription(); + CommonDynamicSuiteDescription( const char *argFile, unsigned argLine, + const char *argSuiteName, List &argTests, + unsigned argCreateLine, unsigned argDestroyLine ); + + void initialize( const char *argFile, unsigned argLine, + const char *argSuiteName, List &argTests, + unsigned argCreateLine, unsigned argDestroyLine ); + + protected: + unsigned _createLine, _destroyLine; + + private: + void doInitialize( unsigned argCreateLine, unsigned argDestroyLine ); + }; + + template<class S> + class DynamicSuiteDescription : public CommonDynamicSuiteDescription + { + public: + DynamicSuiteDescription() {} + DynamicSuiteDescription( const char *argFile, unsigned argLine, + const char *argSuiteName, List &argTests, + S *&argSuite, unsigned argCreateLine, + unsigned argDestroyLine ) : + CommonDynamicSuiteDescription( argFile, argLine, argSuiteName, argTests, argCreateLine, argDestroyLine ) + { + _suite = &argSuite; + } + + void initialize( const char *argFile, unsigned argLine, + const char *argSuiteName, List &argTests, + S *&argSuite, unsigned argCreateLine, + unsigned argDestroyLine ) + { + CommonDynamicSuiteDescription::initialize( argFile, argLine, + argSuiteName, argTests, + argCreateLine, argDestroyLine ); + _suite = &argSuite; + } + + TestSuite *suite() const { return realSuite(); } + + bool setUp(); + bool tearDown(); + + private: + S *realSuite() const { return *_suite; } + void setSuite( S *s ) { *_suite = s; } + + void createSuite() + { + setSuite( S::createSuite() ); + } + + void destroySuite() + { + S *s = realSuite(); + setSuite( 0 ); + S::destroySuite( s ); + } + + S **_suite; + }; + + template<class S> + bool DynamicSuiteDescription<S>::setUp() + { + _TS_TRY { + _TSM_ASSERT_THROWS_NOTHING( file(), _createLine, "Exception thrown from createSuite()", createSuite() ); + _TSM_ASSERT( file(), _createLine, "createSuite() failed", suite() != 0 ); + } + _TS_CATCH_ABORT( { return false; } ); + + return (suite() != 0); + } + + template<class S> + bool DynamicSuiteDescription<S>::tearDown() + { + if ( !_suite ) + return true; + + _TS_TRY { + _TSM_ASSERT_THROWS_NOTHING( file(), _destroyLine, "destroySuite() failed", destroySuite() ); + } + _TS_CATCH_ABORT( { return false; } ); + + return true; + } + + class RealWorldDescription : public WorldDescription + { + public: + static List &suites(); + unsigned numSuites( void ) const; + unsigned numTotalTests( void ) const; + SuiteDescription *firstSuite(); + const SuiteDescription *firstSuite() const; + const SuiteDescription &suiteDescription( unsigned i ) const; + void activateAllTests(); + bool leaveOnly( const char *suiteName, const char *testName = 0 ); + + bool setUp(); + bool tearDown(); + static void reportError( const char *message ); + }; + + void activateAllTests(); + bool leaveOnly( const char *suiteName, const char *testName = 0 ); +} + +#endif // __cxxtest__RealDescriptions_h__ + diff --git a/cxxtest/Root.cpp b/cxxtest/Root.cpp new file mode 100644 index 0000000..c4320fd --- /dev/null +++ b/cxxtest/Root.cpp @@ -0,0 +1,18 @@ +#ifndef __cxxtest__Root_cpp__ +#define __cxxtest__Root_cpp__ + +// +// This file holds the "root" of CxxTest, i.e. +// the parts that must be in a source file file. +// + +#include <cxxtest/Descriptions.cpp> +#include <cxxtest/DummyDescriptions.cpp> +#include <cxxtest/GlobalFixture.cpp> +#include <cxxtest/LinkedList.cpp> +#include <cxxtest/RealDescriptions.cpp> +#include <cxxtest/TestSuite.cpp> +#include <cxxtest/TestTracker.cpp> +#include <cxxtest/ValueTraits.cpp> + +#endif // __cxxtest__Root_cpp__ diff --git a/cxxtest/SelfTest.h b/cxxtest/SelfTest.h new file mode 100644 index 0000000..6d6b96e --- /dev/null +++ b/cxxtest/SelfTest.h @@ -0,0 +1,7 @@ +#ifndef __cxxtest_SelfTest_h__ +#define __cxxtest_SelfTest_h__ + +#define CXXTEST_SUITE(name) +#define CXXTEST_CODE(member) + +#endif // __cxxtest_SelfTest_h__ diff --git a/cxxtest/StdHeaders.h b/cxxtest/StdHeaders.h new file mode 100644 index 0000000..7c80b76 --- /dev/null +++ b/cxxtest/StdHeaders.h @@ -0,0 +1,25 @@ +#ifndef __cxxtest_StdHeaders_h__ +#define __cxxtest_StdHeaders_h__ + +// +// This file basically #includes the STL headers. +// It exists to support warning level 4 in Visual C++ +// + +#ifdef _MSC_VER +# pragma warning( push, 1 ) +#endif // _MSC_VER + +#include <complex> +#include <deque> +#include <list> +#include <map> +#include <set> +#include <string> +#include <vector> + +#ifdef _MSC_VER +# pragma warning( pop ) +#endif // _MSC_VER + +#endif // __cxxtest_StdHeaders_h__ diff --git a/cxxtest/StdValueTraits.h b/cxxtest/StdValueTraits.h new file mode 100644 index 0000000..036796b --- /dev/null +++ b/cxxtest/StdValueTraits.h @@ -0,0 +1,229 @@ +#ifndef __cxxtest_StdValueTraits_h__ +#define __cxxtest_StdValueTraits_h__ + +// +// This file defines ValueTraits for std:: stuff. +// It is #included by <cxxtest/ValueTraits.h> if you +// define CXXTEST_HAVE_STD +// + +#include <cxxtest/ValueTraits.h> +#include <cxxtest/StdHeaders.h> + +#ifdef _CXXTEST_OLD_STD +# define CXXTEST_STD(x) x +#else // !_CXXTEST_OLD_STD +# define CXXTEST_STD(x) std::x +#endif // _CXXTEST_OLD_STD + +#ifndef CXXTEST_USER_VALUE_TRAITS + +namespace CxxTest +{ + // + // NOTE: This should have been + // template<class Char, class Traits, class Allocator> + // class ValueTraits< std::basic_string<Char, Traits, Allocator> > {}; + // But MSVC doesn't support it (yet). + // + + // + // If we have std::string, we might as well use it + // + class StdTraitsBase + { + public: + StdTraitsBase &operator<<( const CXXTEST_STD(string) &s ) { _s += s; return *this; } + const char *asString() const { return _s.c_str(); } + + private: + CXXTEST_STD(string) _s; + }; + + // + // std::string + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const CXXTEST_STD(string)> : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(string) &s ) + { + *this << "\""; + for ( unsigned i = 0; i < s.length(); ++ i ) { + char c[sizeof("\\xXX")]; + charToString( s[i], c ); + *this << c; + } + *this << "\""; + } + }; + + CXXTEST_COPY_CONST_TRAITS( CXXTEST_STD(string) ); + +#ifndef _CXXTEST_OLD_STD + // + // std::wstring + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const CXXTEST_STD(basic_string<wchar_t>)> : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(basic_string<wchar_t>) &s ) + { + *this << "L\""; + for ( unsigned i = 0; i < s.length(); ++ i ) { + char c[sizeof("\\x12345678")]; + charToString( (unsigned long)s[i], c ); + *this << c; + } + *this << "\""; + } + }; + + CXXTEST_COPY_CONST_TRAITS( CXXTEST_STD(basic_string<wchar_t>) ); +#endif // _CXXTEST_OLD_STD + + // + // Convert a range defined by iterators to a string + // This is useful for almost all STL containers + // + template<class Stream, class Iterator> + void dumpRange( Stream &s, Iterator first, Iterator last ) + { + s << "{ "; + while ( first != last ) { + s << TS_AS_STRING(*first); + ++ first; + s << ((first == last) ? " }" : ", "); + } + } + +#ifdef _CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION + // + // std::pair + // + template<class First, class Second> + class ValueTraits< CXXTEST_STD(pair)<First, Second> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(pair)<First, Second> &p ) + { + *this << "<" << TS_AS_STRING( p.first ) << ", " << TS_AS_STRING( p.second ) << ">"; + } + }; + + // + // std::vector + // + template<class Element> + class ValueTraits< CXXTEST_STD(vector)<Element> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(vector)<Element> &v ) + { + dumpRange( *this, v.begin(), v.end() ); + } + }; + + // + // std::list + // + template<class Element> + class ValueTraits< CXXTEST_STD(list)<Element> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(list)<Element> &l ) + { + dumpRange( *this, l.begin(), l.end() ); + } + }; + + // + // std::set + // + template<class Element> + class ValueTraits< CXXTEST_STD(set)<Element> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(set)<Element> &s ) + { + dumpRange( *this, s.begin(), s.end() ); + } + }; + + // + // std::map + // + template<class Key, class Value> + class ValueTraits< CXXTEST_STD(map)<Key, Value> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(map)<Key, Value> &m ) + { + dumpRange( *this, m.begin(), m.end() ); + } + }; + + // + // std::deque + // + template<class Element> + class ValueTraits< CXXTEST_STD(deque)<Element> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(deque)<Element> &d ) + { + dumpRange( *this, d.begin(), d.end() ); + } + }; + + // + // std::multiset + // + template<class Element> + class ValueTraits< CXXTEST_STD(multiset)<Element> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(multiset)<Element> &ms ) + { + dumpRange( *this, ms.begin(), ms.end() ); + } + }; + + // + // std::multimap + // + template<class Key, class Value> + class ValueTraits< CXXTEST_STD(multimap)<Key, Value> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(multimap)<Key, Value> &mm ) + { + dumpRange( *this, mm.begin(), mm.end() ); + } + }; + + // + // std::complex + // + template<class Number> + class ValueTraits< CXXTEST_STD(complex)<Number> > : public StdTraitsBase + { + public: + ValueTraits( const CXXTEST_STD(complex)<Number> &c ) + { + if ( !c.imag() ) + *this << TS_AS_STRING(c.real()); + else if ( !c.real() ) + *this << "(" << TS_AS_STRING(c.imag()) << " * i)"; + else + *this << "(" << TS_AS_STRING(c.real()) << " + " << TS_AS_STRING(c.imag()) << " * i)"; + } + }; +#endif // _CXXTEST_PARTIAL_TEMPLATE_SPECIALIZATION +}; + +#endif // CXXTEST_USER_VALUE_TRAITS + +#endif // __cxxtest_StdValueTraits_h__ diff --git a/cxxtest/StdioFilePrinter.h b/cxxtest/StdioFilePrinter.h new file mode 100644 index 0000000..47984b6 --- /dev/null +++ b/cxxtest/StdioFilePrinter.h @@ -0,0 +1,41 @@ +#ifndef __cxxtest__StdioFilePrinter_h__ +#define __cxxtest__StdioFilePrinter_h__ + +// +// The StdioFilePrinter is a simple TestListener that +// just prints "OK" if everything goes well, otherwise +// reports the error in the format of compiler messages. +// This class uses <stdio.h>, i.e. FILE * and fprintf(). +// + +#include <cxxtest/ErrorFormatter.h> +#include <stdio.h> + +namespace CxxTest +{ + class StdioFilePrinter : public ErrorFormatter + { + public: + StdioFilePrinter( FILE *o, const char *preLine = ":", const char *postLine = "" ) : + ErrorFormatter( new Adapter(o), preLine, postLine ) {} + virtual ~StdioFilePrinter() { delete outputStream(); } + + private: + class Adapter : public OutputStream + { + Adapter( const Adapter & ); + Adapter &operator=( const Adapter & ); + + FILE *_o; + + public: + Adapter( FILE *o ) : _o(o) {} + void flush() { fflush( _o ); } + OutputStream &operator<<( unsigned i ) { fprintf( _o, "%u", i ); return *this; } + OutputStream &operator<<( const char *s ) { fputs( s, _o ); return *this; } + OutputStream &operator<<( Manipulator m ) { return OutputStream::operator<<( m ); } + }; + }; +} + +#endif // __cxxtest__StdioFilePrinter_h__ diff --git a/cxxtest/StdioPrinter.h b/cxxtest/StdioPrinter.h new file mode 100644 index 0000000..af5bc6b --- /dev/null +++ b/cxxtest/StdioPrinter.h @@ -0,0 +1,22 @@ +#ifndef __cxxtest__StdioPrinter_h__ +#define __cxxtest__StdioPrinter_h__ + +// +// The StdioPrinter is an StdioFilePrinter which defaults to stdout. +// This should have been called StdOutPrinter or something, but the name +// has been historically used. +// + +#include <cxxtest/StdioFilePrinter.h> + +namespace CxxTest +{ + class StdioPrinter : public StdioFilePrinter + { + public: + StdioPrinter( FILE *o = stdout, const char *preLine = ":", const char *postLine = "" ) : + StdioFilePrinter( o, preLine, postLine ) {} + }; +} + +#endif // __cxxtest__StdioPrinter_h__ diff --git a/cxxtest/TeeListener.h b/cxxtest/TeeListener.h new file mode 100644 index 0000000..88afdd3 --- /dev/null +++ b/cxxtest/TeeListener.h @@ -0,0 +1,182 @@ +#ifndef __cxxtest__TeeListener_h__ +#define __cxxtest__TeeListener_h__ + +// +// A TeeListener notifies two "reular" TestListeners +// + +#include <cxxtest/TestListener.h> +#include <cxxtest/TestListener.h> + +namespace CxxTest +{ + class TeeListener : public TestListener + { + public: + TeeListener() + { + setFirst( _dummy ); + setSecond( _dummy ); + } + + virtual ~TeeListener() + { + } + + void setFirst( TestListener &first ) + { + _first = &first; + } + + void setSecond( TestListener &second ) + { + _second = &second; + } + + void enterWorld( const WorldDescription &d ) + { + _first->enterWorld( d ); + _second->enterWorld( d ); + } + + void enterSuite( const SuiteDescription &d ) + { + _first->enterSuite( d ); + _second->enterSuite( d ); + } + + void enterTest( const TestDescription &d ) + { + _first->enterTest( d ); + _second->enterTest( d ); + } + + void trace( const char *file, unsigned line, const char *expression ) + { + _first->trace( file, line, expression ); + _second->trace( file, line, expression ); + } + + void warning( const char *file, unsigned line, const char *expression ) + { + _first->warning( file, line, expression ); + _second->warning( file, line, expression ); + } + + void failedTest( const char *file, unsigned line, const char *expression ) + { + _first->failedTest( file, line, expression ); + _second->failedTest( file, line, expression ); + } + + void failedAssert( const char *file, unsigned line, const char *expression ) + { + _first->failedAssert( file, line, expression ); + _second->failedAssert( file, line, expression ); + } + + void failedAssertEquals( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + _first->failedAssertEquals( file, line, xStr, yStr, x, y ); + _second->failedAssertEquals( file, line, xStr, yStr, x, y ); + } + + void failedAssertSameData( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *sizeStr, const void *x, + const void *y, unsigned size ) + { + _first->failedAssertSameData( file, line, xStr, yStr, sizeStr, x, y, size ); + _second->failedAssertSameData( file, line, xStr, yStr, sizeStr, x, y, size ); + } + + void failedAssertDelta( const char *file, unsigned line, + const char *xStr, const char *yStr, const char *dStr, + const char *x, const char *y, const char *d ) + { + _first->failedAssertDelta( file, line, xStr, yStr, dStr, x, y, d ); + _second->failedAssertDelta( file, line, xStr, yStr, dStr, x, y, d ); + } + + void failedAssertDiffers( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *value ) + { + _first->failedAssertDiffers( file, line, xStr, yStr, value ); + _second->failedAssertDiffers( file, line, xStr, yStr, value ); + } + + void failedAssertLessThan( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + _first->failedAssertLessThan( file, line, xStr, yStr, x, y ); + _second->failedAssertLessThan( file, line, xStr, yStr, x, y ); + } + + void failedAssertLessThanEquals( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + _first->failedAssertLessThanEquals( file, line, xStr, yStr, x, y ); + _second->failedAssertLessThanEquals( file, line, xStr, yStr, x, y ); + } + + void failedAssertPredicate( const char *file, unsigned line, + const char *predicate, const char *xStr, const char *x ) + { + _first->failedAssertPredicate( file, line, predicate, xStr, x ); + _second->failedAssertPredicate( file, line, predicate, xStr, x ); + } + + void failedAssertRelation( const char *file, unsigned line, + const char *relation, const char *xStr, const char *yStr, + const char *x, const char *y ) + { + _first->failedAssertRelation( file, line, relation, xStr, yStr, x, y ); + _second->failedAssertRelation( file, line, relation, xStr, yStr, x, y ); + } + + void failedAssertThrows( const char *file, unsigned line, + const char *expression, const char *type, + bool otherThrown ) + { + _first->failedAssertThrows( file, line, expression, type, otherThrown ); + _second->failedAssertThrows( file, line, expression, type, otherThrown ); + } + + void failedAssertThrowsNot( const char *file, unsigned line, + const char *expression ) + { + _first->failedAssertThrowsNot( file, line, expression ); + _second->failedAssertThrowsNot( file, line, expression ); + } + + void leaveTest( const TestDescription &d ) + { + _first->leaveTest(d); + _second->leaveTest(d); + } + + void leaveSuite( const SuiteDescription &d ) + { + _first->leaveSuite(d); + _second->leaveSuite(d); + } + + void leaveWorld( const WorldDescription &d ) + { + _first->leaveWorld(d); + _second->leaveWorld(d); + } + + private: + TestListener *_first, *_second; + TestListener _dummy; + }; +}; + + +#endif // __cxxtest__TeeListener_h__ diff --git a/cxxtest/TestListener.h b/cxxtest/TestListener.h new file mode 100644 index 0000000..0eeb523 --- /dev/null +++ b/cxxtest/TestListener.h @@ -0,0 +1,70 @@ +#ifndef __cxxtest__TestListener_h__ +#define __cxxtest__TestListener_h__ + +// +// TestListener is the base class for all "listeners", +// i.e. classes that receive notifications of the +// testing process. +// +// The names of the parameters are in comments to avoid +// "unused parameter" warnings. +// + +#include <cxxtest/Descriptions.h> + +namespace CxxTest +{ + class TestListener + { + public: + TestListener() {} + virtual ~TestListener() {} + + virtual void enterWorld( const WorldDescription & /*desc*/ ) {} + virtual void enterSuite( const SuiteDescription & /*desc*/ ) {} + virtual void enterTest( const TestDescription & /*desc*/ ) {} + virtual void trace( const char * /*file*/, unsigned /*line*/, + const char * /*expression*/ ) {} + virtual void warning( const char * /*file*/, unsigned /*line*/, + const char * /*expression*/ ) {} + virtual void failedTest( const char * /*file*/, unsigned /*line*/, + const char * /*expression*/ ) {} + virtual void failedAssert( const char * /*file*/, unsigned /*line*/, + const char * /*expression*/ ) {} + virtual void failedAssertEquals( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*x*/, const char * /*y*/ ) {} + virtual void failedAssertSameData( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*sizeStr*/, const void * /*x*/, + const void * /*y*/, unsigned /*size*/ ) {} + virtual void failedAssertDelta( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*dStr*/, const char * /*x*/, + const char * /*y*/, const char * /*d*/ ) {} + virtual void failedAssertDiffers( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*value*/ ) {} + virtual void failedAssertLessThan( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*x*/, const char * /*y*/ ) {} + virtual void failedAssertLessThanEquals( const char * /*file*/, unsigned /*line*/, + const char * /*xStr*/, const char * /*yStr*/, + const char * /*x*/, const char * /*y*/ ) {} + virtual void failedAssertPredicate( const char * /*file*/, unsigned /*line*/, + const char * /*predicate*/, const char * /*xStr*/, const char * /*x*/ ) {} + virtual void failedAssertRelation( const char * /*file*/, unsigned /*line*/, + const char * /*relation*/, const char * /*xStr*/, const char * /*yStr*/, + const char * /*x*/, const char * /*y*/ ) {} + virtual void failedAssertThrows( const char * /*file*/, unsigned /*line*/, + const char * /*expression*/, const char * /*type*/, + bool /*otherThrown*/ ) {} + virtual void failedAssertThrowsNot( const char * /*file*/, unsigned /*line*/, + const char * /*expression*/ ) {} + virtual void leaveTest( const TestDescription & /*desc*/ ) {} + virtual void leaveSuite( const SuiteDescription & /*desc*/ ) {} + virtual void leaveWorld( const WorldDescription & /*desc*/ ) {} + }; +} + +#endif // __cxxtest__TestListener_h__ diff --git a/cxxtest/TestRunner.h b/cxxtest/TestRunner.h new file mode 100644 index 0000000..43f0832 --- /dev/null +++ b/cxxtest/TestRunner.h @@ -0,0 +1,125 @@ +#ifndef __cxxtest_TestRunner_h__ +#define __cxxtest_TestRunner_h__ + +// +// TestRunner is the class that runs all the tests. +// To use it, create an object that implements the TestListener +// interface and call TestRunner::runAllTests( myListener ); +// + +#include <cxxtest/TestListener.h> +#include <cxxtest/RealDescriptions.h> +#include <cxxtest/TestSuite.h> +#include <cxxtest/TestTracker.h> + +namespace CxxTest +{ + class TestRunner + { + public: + static void runAllTests( TestListener &listener ) + { + tracker().setListener( &listener ); + _TS_TRY { TestRunner().runWorld(); } + _TS_LAST_CATCH( { tracker().failedTest( __FILE__, __LINE__, "Exception thrown from world" ); } ); + tracker().setListener( 0 ); + } + + static void runAllTests( TestListener *listener ) + { + if ( listener ) { + listener->warning( __FILE__, __LINE__, "Deprecated; Use runAllTests( TestListener & )" ); + runAllTests( *listener ); + } + } + + private: + void runWorld() + { + RealWorldDescription wd; + WorldGuard sg; + + tracker().enterWorld( wd ); + if ( wd.setUp() ) { + for ( SuiteDescription *sd = wd.firstSuite(); sd; sd = sd->next() ) + if ( sd->active() ) + runSuite( *sd ); + + wd.tearDown(); + } + tracker().leaveWorld( wd ); + } + + void runSuite( SuiteDescription &sd ) + { + StateGuard sg; + + tracker().enterSuite( sd ); + if ( sd.setUp() ) { + for ( TestDescription *td = sd.firstTest(); td; td = td->next() ) + if ( td->active() ) + runTest( *td ); + + sd.tearDown(); + } + tracker().leaveSuite( sd ); + } + + void runTest( TestDescription &td ) + { + StateGuard sg; + + tracker().enterTest( td ); + if ( td.setUp() ) { + td.run(); + td.tearDown(); + } + tracker().leaveTest( td ); + } + + class StateGuard + { +#ifdef _CXXTEST_HAVE_EH + bool _abortTestOnFail; +#endif // _CXXTEST_HAVE_EH + unsigned _maxDumpSize; + + public: + StateGuard() + { +#ifdef _CXXTEST_HAVE_EH + _abortTestOnFail = abortTestOnFail(); +#endif // _CXXTEST_HAVE_EH + _maxDumpSize = maxDumpSize(); + } + + ~StateGuard() + { +#ifdef _CXXTEST_HAVE_EH + setAbortTestOnFail( _abortTestOnFail ); +#endif // _CXXTEST_HAVE_EH + setMaxDumpSize( _maxDumpSize ); + } + }; + + class WorldGuard : public StateGuard + { + public: + WorldGuard() : StateGuard() + { +#ifdef _CXXTEST_HAVE_EH + setAbortTestOnFail( CXXTEST_DEFAULT_ABORT ); +#endif // _CXXTEST_HAVE_EH + setMaxDumpSize( CXXTEST_MAX_DUMP_SIZE ); + } + }; + }; + + // + // For --no-static-init + // + void initialize(); +}; + + +#endif // __cxxtest_TestRunner_h__ diff --git a/cxxtest/TestSuite.cpp b/cxxtest/TestSuite.cpp new file mode 100644 index 0000000..bc14c2c --- /dev/null +++ b/cxxtest/TestSuite.cpp @@ -0,0 +1,138 @@ +#ifndef __cxxtest__TestSuite_cpp__ +#define __cxxtest__TestSuite_cpp__ + +#include <cxxtest/TestSuite.h> + +namespace CxxTest +{ + // + // TestSuite members + // + TestSuite::~TestSuite() {} + void TestSuite::setUp() {} + void TestSuite::tearDown() {} + + // + // Test-aborting stuff + // + static bool currentAbortTestOnFail = false; + + bool abortTestOnFail() + { + return currentAbortTestOnFail; + } + + void setAbortTestOnFail( bool value ) + { + currentAbortTestOnFail = value; + } + + void doAbortTest() + { +# if defined(_CXXTEST_HAVE_EH) + if ( currentAbortTestOnFail ) + throw AbortTest(); +# endif // _CXXTEST_HAVE_EH + } + + // + // Max dump size + // + static unsigned currentMaxDumpSize = CXXTEST_MAX_DUMP_SIZE; + + unsigned maxDumpSize() + { + return currentMaxDumpSize; + } + + void setMaxDumpSize( unsigned value ) + { + currentMaxDumpSize = value; + } + + // + // Some non-template functions + // + void doTrace( const char *file, unsigned line, const char *message ) + { + tracker().trace( file, line, message ); + } + + void doWarn( const char *file, unsigned line, const char *message ) + { + tracker().warning( file, line, message ); + } + + void doFailTest( const char *file, unsigned line, const char *message ) + { + tracker().failedTest( file, line, message ); + TS_ABORT(); + } + + void doFailAssert( const char *file, unsigned line, + const char *expression, const char *message ) + { + if ( message ) + tracker().failedTest( file, line, message ); + tracker().failedAssert( file, line, expression ); + TS_ABORT(); + } + + bool sameData( const void *x, const void *y, unsigned size ) + { + if ( size == 0 ) + return true; + + if ( x == y ) + return true; + + if ( !x || !y ) + return false; + + const char *cx = (const char *)x; + const char *cy = (const char *)y; + while ( size -- ) + if ( *cx++ != *cy++ ) + return false; + + return true; + } + + void doAssertSameData( const char *file, unsigned line, + const char *xExpr, const void *x, + const char *yExpr, const void *y, + const char *sizeExpr, unsigned size, + const char *message ) + { + if ( !sameData( x, y, size ) ) { + if ( message ) + tracker().failedTest( file, line, message ); + tracker().failedAssertSameData( file, line, xExpr, yExpr, sizeExpr, x, y, size ); + TS_ABORT(); + } + } + + void doFailAssertThrows( const char *file, unsigned line, + const char *expr, const char *type, + bool otherThrown, + const char *message ) + { + if ( message ) + tracker().failedTest( file, line, message ); + + tracker().failedAssertThrows( file, line, expr, type, otherThrown ); + TS_ABORT(); + } + + void doFailAssertThrowsNot( const char *file, unsigned line, + const char *expression, const char *message ) + { + if ( message ) + tracker().failedTest( file, line, message ); + + tracker().failedAssertThrowsNot( file, line, expression ); + TS_ABORT(); + } +}; + +#endif // __cxxtest__TestSuite_cpp__ diff --git a/cxxtest/TestSuite.h b/cxxtest/TestSuite.h new file mode 100644 index 0000000..fc5a206 --- /dev/null +++ b/cxxtest/TestSuite.h @@ -0,0 +1,512 @@ +#ifndef __cxxtest__TestSuite_h__ +#define __cxxtest__TestSuite_h__ + +// +// class TestSuite is the base class for all test suites. +// To define a test suite, derive from this class and add +// member functions called void test*(); +// + +#include <cxxtest/Flags.h> +#include <cxxtest/TestTracker.h> +#include <cxxtest/Descriptions.h> +#include <cxxtest/ValueTraits.h> + +#ifdef _CXXTEST_HAVE_STD +# include <stdexcept> +#endif // _CXXTEST_HAVE_STD + +namespace CxxTest +{ + class TestSuite + { + public: + virtual ~TestSuite(); + virtual void setUp(); + virtual void tearDown(); + }; + + class AbortTest {}; + void doAbortTest(); +# define TS_ABORT() CxxTest::doAbortTest() + + bool abortTestOnFail(); + void setAbortTestOnFail( bool value = CXXTEST_DEFAULT_ABORT ); + + unsigned maxDumpSize(); + void setMaxDumpSize( unsigned value = CXXTEST_MAX_DUMP_SIZE ); + + void doTrace( const char *file, unsigned line, const char *message ); + void doWarn( const char *file, unsigned line, const char *message ); + void doFailTest( const char *file, unsigned line, const char *message ); + void doFailAssert( const char *file, unsigned line, const char *expression, const char *message ); + + template<class X, class Y> + bool equals( X x, Y y ) + { + return (x == y); + } + + template<class X, class Y> + void doAssertEquals( const char *file, unsigned line, + const char *xExpr, X x, + const char *yExpr, Y y, + const char *message ) + { + if ( !equals( x, y ) ) { + if ( message ) + tracker().failedTest( file, line, message ); + tracker().failedAssertEquals( file, line, xExpr, yExpr, TS_AS_STRING(x), TS_AS_STRING(y) ); + TS_ABORT(); + } + } + + void doAssertSameData( const char *file, unsigned line, + const char *xExpr, const void *x, + const char *yExpr, const void *y, + const char *sizeExpr, unsigned size, + const char *message ); + + template<class X, class Y> + bool differs( X x, Y y ) + { + return !(x == y); + } + + template<class X, class Y> + void doAssertDiffers( const char *file, unsigned line, + const char *xExpr, X x, + const char *yExpr, Y y, + const char *message ) + { + if ( !differs( x, y ) ) { + if ( message ) + tracker().failedTest( file, line, message ); + tracker().failedAssertDiffers( file, line, xExpr, yExpr, TS_AS_STRING(x) ); + TS_ABORT(); + } + } + + template<class X, class Y> + bool lessThan( X x, Y y ) + { + return (x < y); + } + + template<class X, class Y> + void doAssertLessThan( const char *file, unsigned line, + const char *xExpr, X x, + const char *yExpr, Y y, + const char *message ) + { + if ( !lessThan(x, y) ) { + if ( message ) + tracker().failedTest( file, line, message ); + tracker().failedAssertLessThan( file, line, xExpr, yExpr, TS_AS_STRING(x), TS_AS_STRING(y) ); + TS_ABORT(); + } + } + + template<class X, class Y> + bool lessThanEquals( X x, Y y ) + { + return (x <= y); + } + + template<class X, class Y> + void doAssertLessThanEquals( const char *file, unsigned line, + const char *xExpr, X x, + const char *yExpr, Y y, + const char *message ) + { + if ( !lessThanEquals( x, y ) ) { + if ( message ) + tracker().failedTest( file, line, message ); + tracker().failedAssertLessThanEquals( file, line, xExpr, yExpr, TS_AS_STRING(x), TS_AS_STRING(y) ); + TS_ABORT(); + } + } + + template<class X, class P> + void doAssertPredicate( const char *file, unsigned line, + const char *pExpr, const P &p, + const char *xExpr, X x, + const char *message ) + { + if ( !p( x ) ) { + if ( message ) + tracker().failedTest( file, line, message ); + tracker().failedAssertPredicate( file, line, pExpr, xExpr, TS_AS_STRING(x) ); + TS_ABORT(); + } + } + + template<class X, class Y, class R> + void doAssertRelation( const char *file, unsigned line, + const char *rExpr, const R &r, + const char *xExpr, X x, + const char *yExpr, Y y, + const char *message ) + { + if ( !r( x, y ) ) { + if ( message ) + tracker().failedTest( file, line, message ); + tracker().failedAssertRelation( file, line, rExpr, xExpr, yExpr, TS_AS_STRING(x), TS_AS_STRING(y) ); + TS_ABORT(); + } + } + + template<class X, class Y, class D> + bool delta( X x, Y y, D d ) + { + return ((y >= x - d) && (y <= x + d)); + } + + template<class X, class Y, class D> + void doAssertDelta( const char *file, unsigned line, + const char *xExpr, X x, + const char *yExpr, Y y, + const char *dExpr, D d, + const char *message ) + { + if ( !delta( x, y, d ) ) { + if ( message ) + tracker().failedTest( file, line, message ); + + tracker().failedAssertDelta( file, line, xExpr, yExpr, dExpr, + TS_AS_STRING(x), TS_AS_STRING(y), TS_AS_STRING(d) ); + TS_ABORT(); + } + } + + void doFailAssertThrows( const char *file, unsigned line, + const char *expr, const char *type, + bool otherThrown, + const char *message ); + + void doFailAssertThrowsNot( const char *file, unsigned line, + const char *expression, const char *message ); + +# ifdef _CXXTEST_HAVE_EH +# define _TS_TRY try +# define _TS_CATCH_TYPE(t, b) catch t b +# define _TS_CATCH_ABORT(b) _TS_CATCH_TYPE( (const CxxTest::AbortTest &), b ) +# define _TS_LAST_CATCH(b) _TS_CATCH_TYPE( (...), b ) +# define _TSM_LAST_CATCH(f,l,m) _TS_LAST_CATCH( { (CxxTest::tracker()).failedTest(f,l,m); } ) +# ifdef _CXXTEST_HAVE_STD +# define ___TSM_CATCH(f,l,m) \ + catch(const std::exception &e) { (CxxTest::tracker()).failedTest(f,l,e.what()); } \ + _TSM_LAST_CATCH(f,l,m) +# else // !_CXXTEST_HAVE_STD +# define ___TSM_CATCH(f,l,m) _TSM_LAST_CATCH(f,l,m) +# endif // _CXXTEST_HAVE_STD +# define __TSM_CATCH(f,l,m) \ + _TS_CATCH_ABORT( { throw; } ) \ + ___TSM_CATCH(f,l,m) +# define __TS_CATCH(f,l) __TSM_CATCH(f,l,"Unhandled exception") +# define _TS_CATCH __TS_CATCH(__FILE__,__LINE__) +# else // !_CXXTEST_HAVE_EH +# define _TS_TRY +# define ___TSM_CATCH(f,l,m) +# define __TSM_CATCH(f,l,m) +# define __TS_CATCH(f,l) +# define _TS_CATCH +# define _TS_CATCH_TYPE(t, b) +# define _TS_LAST_CATCH(b) +# define _TS_CATCH_ABORT(b) +# endif // _CXXTEST_HAVE_EH + + // TS_TRACE +# define _TS_TRACE(f,l,e) CxxTest::doTrace( (f), (l), TS_AS_STRING(e) ) +# define TS_TRACE(e) _TS_TRACE( __FILE__, __LINE__, e ) + + // TS_WARN +# define _TS_WARN(f,l,e) CxxTest::doWarn( (f), (l), TS_AS_STRING(e) ) +# define TS_WARN(e) _TS_WARN( __FILE__, __LINE__, e ) + + // TS_FAIL +# define _TS_FAIL(f,l,e) CxxTest::doFailTest( (f), (l), TS_AS_STRING(e) ) +# define TS_FAIL(e) _TS_FAIL( __FILE__, __LINE__, e ) + + // TS_ASSERT +# define ___ETS_ASSERT(f,l,e,m) { if ( !(e) ) CxxTest::doFailAssert( (f), (l), #e, (m) ); } +# define ___TS_ASSERT(f,l,e,m) { _TS_TRY { ___ETS_ASSERT(f,l,e,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT(f,l,e) ___ETS_ASSERT(f,l,e,0) +# define _TS_ASSERT(f,l,e) ___TS_ASSERT(f,l,e,0) + +# define ETS_ASSERT(e) _ETS_ASSERT(__FILE__,__LINE__,e) +# define TS_ASSERT(e) _TS_ASSERT(__FILE__,__LINE__,e) + +# define _ETSM_ASSERT(f,l,m,e) ___ETS_ASSERT(f,l,e,TS_AS_STRING(m) ) +# define _TSM_ASSERT(f,l,m,e) ___TS_ASSERT(f,l,e,TS_AS_STRING(m) ) + +# define ETSM_ASSERT(m,e) _ETSM_ASSERT(__FILE__,__LINE__,m,e) +# define TSM_ASSERT(m,e) _TSM_ASSERT(__FILE__,__LINE__,m,e) + + // TS_ASSERT_EQUALS +# define ___ETS_ASSERT_EQUALS(f,l,x,y,m) CxxTest::doAssertEquals( (f), (l), #x, (x), #y, (y), (m) ) +# define ___TS_ASSERT_EQUALS(f,l,x,y,m) { _TS_TRY { ___ETS_ASSERT_EQUALS(f,l,x,y,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT_EQUALS(f,l,x,y) ___ETS_ASSERT_EQUALS(f,l,x,y,0) +# define _TS_ASSERT_EQUALS(f,l,x,y) ___TS_ASSERT_EQUALS(f,l,x,y,0) + +# define ETS_ASSERT_EQUALS(x,y) _ETS_ASSERT_EQUALS(__FILE__,__LINE__,x,y) +# define TS_ASSERT_EQUALS(x,y) _TS_ASSERT_EQUALS(__FILE__,__LINE__,x,y) + +# define _ETSM_ASSERT_EQUALS(f,l,m,x,y) ___ETS_ASSERT_EQUALS(f,l,x,y,TS_AS_STRING(m)) +# define _TSM_ASSERT_EQUALS(f,l,m,x,y) ___TS_ASSERT_EQUALS(f,l,x,y,TS_AS_STRING(m)) + +# define ETSM_ASSERT_EQUALS(m,x,y) _ETSM_ASSERT_EQUALS(__FILE__,__LINE__,m,x,y) +# define TSM_ASSERT_EQUALS(m,x,y) _TSM_ASSERT_EQUALS(__FILE__,__LINE__,m,x,y) + + // TS_ASSERT_SAME_DATA +# define ___ETS_ASSERT_SAME_DATA(f,l,x,y,s,m) CxxTest::doAssertSameData( (f), (l), #x, (x), #y, (y), #s, (s), (m) ) +# define ___TS_ASSERT_SAME_DATA(f,l,x,y,s,m) { _TS_TRY { ___ETS_ASSERT_SAME_DATA(f,l,x,y,s,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT_SAME_DATA(f,l,x,y,s) ___ETS_ASSERT_SAME_DATA(f,l,x,y,s,0) +# define _TS_ASSERT_SAME_DATA(f,l,x,y,s) ___TS_ASSERT_SAME_DATA(f,l,x,y,s,0) + +# define ETS_ASSERT_SAME_DATA(x,y,s) _ETS_ASSERT_SAME_DATA(__FILE__,__LINE__,x,y,s) +# define TS_ASSERT_SAME_DATA(x,y,s) _TS_ASSERT_SAME_DATA(__FILE__,__LINE__,x,y,s) + +# define _ETSM_ASSERT_SAME_DATA(f,l,m,x,y,s) ___ETS_ASSERT_SAME_DATA(f,l,x,y,s,TS_AS_STRING(m)) +# define _TSM_ASSERT_SAME_DATA(f,l,m,x,y,s) ___TS_ASSERT_SAME_DATA(f,l,x,y,s,TS_AS_STRING(m)) + +# define ETSM_ASSERT_SAME_DATA(m,x,y,s) _ETSM_ASSERT_SAME_DATA(__FILE__,__LINE__,m,x,y,s) +# define TSM_ASSERT_SAME_DATA(m,x,y,s) _TSM_ASSERT_SAME_DATA(__FILE__,__LINE__,m,x,y,s) + + // TS_ASSERT_DIFFERS +# define ___ETS_ASSERT_DIFFERS(f,l,x,y,m) CxxTest::doAssertDiffers( (f), (l), #x, (x), #y, (y), (m) ) +# define ___TS_ASSERT_DIFFERS(f,l,x,y,m) { _TS_TRY { ___ETS_ASSERT_DIFFERS(f,l,x,y,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT_DIFFERS(f,l,x,y) ___ETS_ASSERT_DIFFERS(f,l,x,y,0) +# define _TS_ASSERT_DIFFERS(f,l,x,y) ___TS_ASSERT_DIFFERS(f,l,x,y,0) + +# define ETS_ASSERT_DIFFERS(x,y) _ETS_ASSERT_DIFFERS(__FILE__,__LINE__,x,y) +# define TS_ASSERT_DIFFERS(x,y) _TS_ASSERT_DIFFERS(__FILE__,__LINE__,x,y) + +# define _ETSM_ASSERT_DIFFERS(f,l,m,x,y) ___ETS_ASSERT_DIFFERS(f,l,x,y,TS_AS_STRING(m)) +# define _TSM_ASSERT_DIFFERS(f,l,m,x,y) ___TS_ASSERT_DIFFERS(f,l,x,y,TS_AS_STRING(m)) + +# define ETSM_ASSERT_DIFFERS(m,x,y) _ETSM_ASSERT_DIFFERS(__FILE__,__LINE__,m,x,y) +# define TSM_ASSERT_DIFFERS(m,x,y) _TSM_ASSERT_DIFFERS(__FILE__,__LINE__,m,x,y) + + // TS_ASSERT_LESS_THAN +# define ___ETS_ASSERT_LESS_THAN(f,l,x,y,m) CxxTest::doAssertLessThan( (f), (l), #x, (x), #y, (y), (m) ) +# define ___TS_ASSERT_LESS_THAN(f,l,x,y,m) { _TS_TRY { ___ETS_ASSERT_LESS_THAN(f,l,x,y,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT_LESS_THAN(f,l,x,y) ___ETS_ASSERT_LESS_THAN(f,l,x,y,0) +# define _TS_ASSERT_LESS_THAN(f,l,x,y) ___TS_ASSERT_LESS_THAN(f,l,x,y,0) + +# define ETS_ASSERT_LESS_THAN(x,y) _ETS_ASSERT_LESS_THAN(__FILE__,__LINE__,x,y) +# define TS_ASSERT_LESS_THAN(x,y) _TS_ASSERT_LESS_THAN(__FILE__,__LINE__,x,y) + +# define _ETSM_ASSERT_LESS_THAN(f,l,m,x,y) ___ETS_ASSERT_LESS_THAN(f,l,x,y,TS_AS_STRING(m)) +# define _TSM_ASSERT_LESS_THAN(f,l,m,x,y) ___TS_ASSERT_LESS_THAN(f,l,x,y,TS_AS_STRING(m)) + +# define ETSM_ASSERT_LESS_THAN(m,x,y) _ETSM_ASSERT_LESS_THAN(__FILE__,__LINE__,m,x,y) +# define TSM_ASSERT_LESS_THAN(m,x,y) _TSM_ASSERT_LESS_THAN(__FILE__,__LINE__,m,x,y) + + // TS_ASSERT_LESS_THAN_EQUALS +# define ___ETS_ASSERT_LESS_THAN_EQUALS(f,l,x,y,m) \ + CxxTest::doAssertLessThanEquals( (f), (l), #x, (x), #y, (y), (m) ) +# define ___TS_ASSERT_LESS_THAN_EQUALS(f,l,x,y,m) \ + { _TS_TRY { ___ETS_ASSERT_LESS_THAN_EQUALS(f,l,x,y,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT_LESS_THAN_EQUALS(f,l,x,y) ___ETS_ASSERT_LESS_THAN_EQUALS(f,l,x,y,0) +# define _TS_ASSERT_LESS_THAN_EQUALS(f,l,x,y) ___TS_ASSERT_LESS_THAN_EQUALS(f,l,x,y,0) + +# define ETS_ASSERT_LESS_THAN_EQUALS(x,y) _ETS_ASSERT_LESS_THAN_EQUALS(__FILE__,__LINE__,x,y) +# define TS_ASSERT_LESS_THAN_EQUALS(x,y) _TS_ASSERT_LESS_THAN_EQUALS(__FILE__,__LINE__,x,y) + +# define _ETSM_ASSERT_LESS_THAN_EQUALS(f,l,m,x,y) ___ETS_ASSERT_LESS_THAN_EQUALS(f,l,x,y,TS_AS_STRING(m)) +# define _TSM_ASSERT_LESS_THAN_EQUALS(f,l,m,x,y) ___TS_ASSERT_LESS_THAN_EQUALS(f,l,x,y,TS_AS_STRING(m)) + +# define ETSM_ASSERT_LESS_THAN_EQUALS(m,x,y) _ETSM_ASSERT_LESS_THAN_EQUALS(__FILE__,__LINE__,m,x,y) +# define TSM_ASSERT_LESS_THAN_EQUALS(m,x,y) _TSM_ASSERT_LESS_THAN_EQUALS(__FILE__,__LINE__,m,x,y) + + // TS_ASSERT_PREDICATE +# define ___ETS_ASSERT_PREDICATE(f,l,p,x,m) \ + CxxTest::doAssertPredicate( (f), (l), #p, p(), #x, (x), (m) ) +# define ___TS_ASSERT_PREDICATE(f,l,p,x,m) \ + { _TS_TRY { ___ETS_ASSERT_PREDICATE(f,l,p,x,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT_PREDICATE(f,l,p,x) ___ETS_ASSERT_PREDICATE(f,l,p,x,0) +# define _TS_ASSERT_PREDICATE(f,l,p,x) ___TS_ASSERT_PREDICATE(f,l,p,x,0) + +# define ETS_ASSERT_PREDICATE(p,x) _ETS_ASSERT_PREDICATE(__FILE__,__LINE__,p,x) +# define TS_ASSERT_PREDICATE(p,x) _TS_ASSERT_PREDICATE(__FILE__,__LINE__,p,x) + +# define _ETSM_ASSERT_PREDICATE(f,l,m,p,x) ___ETS_ASSERT_PREDICATE(f,l,p,x,TS_AS_STRING(m)) +# define _TSM_ASSERT_PREDICATE(f,l,m,p,x) ___TS_ASSERT_PREDICATE(f,l,p,x,TS_AS_STRING(m)) + +# define ETSM_ASSERT_PREDICATE(m,p,x) _ETSM_ASSERT_PREDICATE(__FILE__,__LINE__,m,p,x) +# define TSM_ASSERT_PREDICATE(m,p,x) _TSM_ASSERT_PREDICATE(__FILE__,__LINE__,m,p,x) + + // TS_ASSERT_RELATION +# define ___ETS_ASSERT_RELATION(f,l,r,x,y,m) \ + CxxTest::doAssertRelation( (f), (l), #r, r(), #x, (x), #y, (y), (m) ) +# define ___TS_ASSERT_RELATION(f,l,r,x,y,m) \ + { _TS_TRY { ___ETS_ASSERT_RELATION(f,l,r,x,y,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT_RELATION(f,l,r,x,y) ___ETS_ASSERT_RELATION(f,l,r,x,y,0) +# define _TS_ASSERT_RELATION(f,l,r,x,y) ___TS_ASSERT_RELATION(f,l,r,x,y,0) + +# define ETS_ASSERT_RELATION(r,x,y) _ETS_ASSERT_RELATION(__FILE__,__LINE__,r,x,y) +# define TS_ASSERT_RELATION(r,x,y) _TS_ASSERT_RELATION(__FILE__,__LINE__,r,x,y) + +# define _ETSM_ASSERT_RELATION(f,l,m,r,x,y) ___ETS_ASSERT_RELATION(f,l,r,x,y,TS_AS_STRING(m)) +# define _TSM_ASSERT_RELATION(f,l,m,r,x,y) ___TS_ASSERT_RELATION(f,l,r,x,y,TS_AS_STRING(m)) + +# define ETSM_ASSERT_RELATION(m,r,x,y) _ETSM_ASSERT_RELATION(__FILE__,__LINE__,m,r,x,y) +# define TSM_ASSERT_RELATION(m,r,x,y) _TSM_ASSERT_RELATION(__FILE__,__LINE__,m,r,x,y) + + // TS_ASSERT_DELTA +# define ___ETS_ASSERT_DELTA(f,l,x,y,d,m) CxxTest::doAssertDelta( (f), (l), #x, (x), #y, (y), #d, (d), (m) ) +# define ___TS_ASSERT_DELTA(f,l,x,y,d,m) { _TS_TRY { ___ETS_ASSERT_DELTA(f,l,x,y,d,m); } __TS_CATCH(f,l) } + +# define _ETS_ASSERT_DELTA(f,l,x,y,d) ___ETS_ASSERT_DELTA(f,l,x,y,d,0) +# define _TS_ASSERT_DELTA(f,l,x,y,d) ___TS_ASSERT_DELTA(f,l,x,y,d,0) + +# define ETS_ASSERT_DELTA(x,y,d) _ETS_ASSERT_DELTA(__FILE__,__LINE__,x,y,d) +# define TS_ASSERT_DELTA(x,y,d) _TS_ASSERT_DELTA(__FILE__,__LINE__,x,y,d) + +# define _ETSM_ASSERT_DELTA(f,l,m,x,y,d) ___ETS_ASSERT_DELTA(f,l,x,y,d,TS_AS_STRING(m)) +# define _TSM_ASSERT_DELTA(f,l,m,x,y,d) ___TS_ASSERT_DELTA(f,l,x,y,d,TS_AS_STRING(m)) + +# define ETSM_ASSERT_DELTA(m,x,y,d) _ETSM_ASSERT_DELTA(__FILE__,__LINE__,m,x,y,d) +# define TSM_ASSERT_DELTA(m,x,y,d) _TSM_ASSERT_DELTA(__FILE__,__LINE__,m,x,y,d) + + // TS_ASSERT_THROWS +# define ___TS_ASSERT_THROWS(f,l,e,t,m) { \ + bool _ts_threw_expected = false, _ts_threw_else = false; \ + _TS_TRY { e; } \ + _TS_CATCH_TYPE( (t), { _ts_threw_expected = true; } ) \ + _TS_CATCH_ABORT( { throw; } ) \ + _TS_LAST_CATCH( { _ts_threw_else = true; } ) \ + if ( !_ts_threw_expected ) { CxxTest::doFailAssertThrows( (f), (l), #e, #t, _ts_threw_else, (m) ); } } + +# define _TS_ASSERT_THROWS(f,l,e,t) ___TS_ASSERT_THROWS(f,l,e,t,0) +# define TS_ASSERT_THROWS(e,t) _TS_ASSERT_THROWS(__FILE__,__LINE__,e,t) + +# define _TSM_ASSERT_THROWS(f,l,m,e,t) ___TS_ASSERT_THROWS(f,l,e,t,TS_AS_STRING(m)) +# define TSM_ASSERT_THROWS(m,e,t) _TSM_ASSERT_THROWS(__FILE__,__LINE__,m,e,t) + + // TS_ASSERT_THROWS_ASSERT +# define ___TS_ASSERT_THROWS_ASSERT(f,l,e,t,a,m) { \ + bool _ts_threw_expected = false, _ts_threw_else = false; \ + _TS_TRY { e; } \ + _TS_CATCH_TYPE( (t), { a; _ts_threw_expected = true; } ) \ + _TS_CATCH_ABORT( { throw; } ) \ + _TS_LAST_CATCH( { _ts_threw_else = true; } ) \ + if ( !_ts_threw_expected ) { CxxTest::doFailAssertThrows( (f), (l), #e, #t, _ts_threw_else, (m) ); } } + +# define _TS_ASSERT_THROWS_ASSERT(f,l,e,t,a) ___TS_ASSERT_THROWS_ASSERT(f,l,e,t,a,0) +# define TS_ASSERT_THROWS_ASSERT(e,t,a) _TS_ASSERT_THROWS_ASSERT(__FILE__,__LINE__,e,t,a) + +# define _TSM_ASSERT_THROWS_ASSERT(f,l,m,e,t,a) ___TS_ASSERT_THROWS_ASSERT(f,l,e,t,a,TS_AS_STRING(m)) +# define TSM_ASSERT_THROWS_ASSERT(m,e,t,a) _TSM_ASSERT_THROWS_ASSERT(__FILE__,__LINE__,m,e,t,a) + + // TS_ASSERT_THROWS_EQUALS +# define TS_ASSERT_THROWS_EQUALS(e,t,x,y) TS_ASSERT_THROWS_ASSERT(e,t,TS_ASSERT_EQUALS(x,y)) +# define TSM_ASSERT_THROWS_EQUALS(m,e,t,x,y) TSM_ASSERT_THROWS_ASSERT(m,e,t,TSM_ASSERT_EQUALS(m,x,y)) + + // TS_ASSERT_THROWS_DIFFERS +# define TS_ASSERT_THROWS_DIFFERS(e,t,x,y) TS_ASSERT_THROWS_ASSERT(e,t,TS_ASSERT_DIFFERS(x,y)) +# define TSM_ASSERT_THROWS_DIFFERS(m,e,t,x,y) TSM_ASSERT_THROWS_ASSERT(m,e,t,TSM_ASSERT_DIFFERS(m,x,y)) + + // TS_ASSERT_THROWS_DELTA +# define TS_ASSERT_THROWS_DELTA(e,t,x,y,d) TS_ASSERT_THROWS_ASSERT(e,t,TS_ASSERT_DELTA(x,y,d)) +# define TSM_ASSERT_THROWS_DELTA(m,e,t,x,y,d) TSM_ASSERT_THROWS_ASSERT(m,e,t,TSM_ASSERT_DELTA(m,x,y,d)) + + // TS_ASSERT_THROWS_SAME_DATA +# define TS_ASSERT_THROWS_SAME_DATA(e,t,x,y,s) TS_ASSERT_THROWS_ASSERT(e,t,TS_ASSERT_SAME_DATA(x,y,s)) +# define TSM_ASSERT_THROWS_SAME_DATA(m,e,t,x,y,s) TSM_ASSERT_THROWS_ASSERT(m,e,t,TSM_ASSERT_SAME_DATA(m,x,y,s)) + + // TS_ASSERT_THROWS_LESS_THAN +# define TS_ASSERT_THROWS_LESS_THAN(e,t,x,y) TS_ASSERT_THROWS_ASSERT(e,t,TS_ASSERT_LESS_THAN(x,y)) +# define TSM_ASSERT_THROWS_LESS_THAN(m,e,t,x,y) TSM_ASSERT_THROWS_ASSERT(m,e,t,TSM_ASSERT_LESS_THAN(m,x,y)) + + // TS_ASSERT_THROWS_LESS_THAN_EQUALS +# define TS_ASSERT_THROWS_LESS_THAN_EQUALS(e,t,x,y) TS_ASSERT_THROWS_ASSERT(e,t,TS_ASSERT_LESS_THAN_EQUALS(x,y)) +# define TSM_ASSERT_THROWS_LESS_THAN_EQUALS(m,e,t,x,y) TSM_ASSERT_THROWS_ASSERT(m,e,t,TSM_ASSERT_LESS_THAN_EQUALS(m,x,y)) + + // TS_ASSERT_THROWS_PREDICATE +# define TS_ASSERT_THROWS_PREDICATE(e,t,p,v) TS_ASSERT_THROWS_ASSERT(e,t,TS_ASSERT_PREDICATE(p,v)) +# define TSM_ASSERT_THROWS_PREDICATE(m,e,t,p,v) TSM_ASSERT_THROWS_ASSERT(m,e,t,TSM_ASSERT_PREDICATE(m,p,v)) + + // TS_ASSERT_THROWS_RELATION +# define TS_ASSERT_THROWS_RELATION(e,t,r,x,y) TS_ASSERT_THROWS_ASSERT(e,t,TS_ASSERT_RELATION(r,x,y)) +# define TSM_ASSERT_THROWS_RELATION(m,e,t,r,x,y) TSM_ASSERT_THROWS_ASSERT(m,e,t,TSM_ASSERT_RELATION(m,r,x,y)) + + // TS_ASSERT_THROWS_ANYTHING +# define ___TS_ASSERT_THROWS_ANYTHING(f,l,e,m) { \ + bool _ts_threw = false; \ + _TS_TRY { e; } \ + _TS_LAST_CATCH( { _ts_threw = true; } ) \ + if ( !_ts_threw ) { CxxTest::doFailAssertThrows( (f), (l), #e, "...", false, (m) ); } } + +# define _TS_ASSERT_THROWS_ANYTHING(f,l,e) ___TS_ASSERT_THROWS_ANYTHING(f,l,e,0) +# define TS_ASSERT_THROWS_ANYTHING(e) _TS_ASSERT_THROWS_ANYTHING(__FILE__, __LINE__, e) + +# define _TSM_ASSERT_THROWS_ANYTHING(f,l,m,e) ___TS_ASSERT_THROWS_ANYTHING(f,l,e,TS_AS_STRING(m)) +# define TSM_ASSERT_THROWS_ANYTHING(m,e) _TSM_ASSERT_THROWS_ANYTHING(__FILE__,__LINE__,m,e) + + // TS_ASSERT_THROWS_NOTHING +# define ___TS_ASSERT_THROWS_NOTHING(f,l,e,m) { \ + _TS_TRY { e; } \ + _TS_CATCH_ABORT( { throw; } ) \ + _TS_LAST_CATCH( { CxxTest::doFailAssertThrowsNot( (f), (l), #e, (m) ); } ) } + +# define _TS_ASSERT_THROWS_NOTHING(f,l,e) ___TS_ASSERT_THROWS_NOTHING(f,l,e,0) +# define TS_ASSERT_THROWS_NOTHING(e) _TS_ASSERT_THROWS_NOTHING(__FILE__,__LINE__,e) + +# define _TSM_ASSERT_THROWS_NOTHING(f,l,m,e) ___TS_ASSERT_THROWS_NOTHING(f,l,e,TS_AS_STRING(m)) +# define TSM_ASSERT_THROWS_NOTHING(m,e) _TSM_ASSERT_THROWS_NOTHING(__FILE__,__LINE__,m,e) + + + // + // This takes care of "signed <-> unsigned" warnings + // +# define CXXTEST_COMPARISONS(CXXTEST_X, CXXTEST_Y, CXXTEST_T) \ + inline bool equals( CXXTEST_X x, CXXTEST_Y y ) { return (((CXXTEST_T)x) == ((CXXTEST_T)y)); } \ + inline bool equals( CXXTEST_Y y, CXXTEST_X x ) { return (((CXXTEST_T)y) == ((CXXTEST_T)x)); } \ + inline bool differs( CXXTEST_X x, CXXTEST_Y y ) { return (((CXXTEST_T)x) != ((CXXTEST_T)y)); } \ + inline bool differs( CXXTEST_Y y, CXXTEST_X x ) { return (((CXXTEST_T)y) != ((CXXTEST_T)x)); } \ + inline bool lessThan( CXXTEST_X x, CXXTEST_Y y ) { return (((CXXTEST_T)x) < ((CXXTEST_T)y)); } \ + inline bool lessThan( CXXTEST_Y y, CXXTEST_X x ) { return (((CXXTEST_T)y) < ((CXXTEST_T)x)); } \ + inline bool lessThanEquals( CXXTEST_X x, CXXTEST_Y y ) { return (((CXXTEST_T)x) <= ((CXXTEST_T)y)); } \ + inline bool lessThanEquals( CXXTEST_Y y, CXXTEST_X x ) { return (((CXXTEST_T)y) <= ((CXXTEST_T)x)); } + +# define CXXTEST_INTEGRAL(CXXTEST_T) \ + CXXTEST_COMPARISONS( signed CXXTEST_T, unsigned CXXTEST_T, unsigned CXXTEST_T ) + + CXXTEST_INTEGRAL( char ) + CXXTEST_INTEGRAL( short ) + CXXTEST_INTEGRAL( int ) + CXXTEST_INTEGRAL( long ) +# ifdef _CXXTEST_LONGLONG + CXXTEST_INTEGRAL( _CXXTEST_LONGLONG ) +# endif // _CXXTEST_LONGLONG + +# define CXXTEST_SMALL_BIG(CXXTEST_SMALL, CXXTEST_BIG) \ + CXXTEST_COMPARISONS( signed CXXTEST_SMALL, unsigned CXXTEST_BIG, unsigned CXXTEST_BIG ) \ + CXXTEST_COMPARISONS( signed CXXTEST_BIG, unsigned CXXTEST_SMALL, unsigned CXXTEST_BIG ) + + CXXTEST_SMALL_BIG( char, short ) + CXXTEST_SMALL_BIG( char, int ) + CXXTEST_SMALL_BIG( short, int ) + CXXTEST_SMALL_BIG( char, long ) + CXXTEST_SMALL_BIG( short, long ) + CXXTEST_SMALL_BIG( int, long ) + +# ifdef _CXXTEST_LONGLONG + CXXTEST_SMALL_BIG( char, _CXXTEST_LONGLONG ) + CXXTEST_SMALL_BIG( short, _CXXTEST_LONGLONG ) + CXXTEST_SMALL_BIG( int, _CXXTEST_LONGLONG ) + CXXTEST_SMALL_BIG( long, _CXXTEST_LONGLONG ) +# endif // _CXXTEST_LONGLONG +} + +#endif // __cxxtest__TestSuite_h__ diff --git a/cxxtest/TestTracker.cpp b/cxxtest/TestTracker.cpp new file mode 100644 index 0000000..f3ce781 --- /dev/null +++ b/cxxtest/TestTracker.cpp @@ -0,0 +1,248 @@ +#ifndef __cxxtest__TestTracker_cpp__ +#define __cxxtest__TestTracker_cpp__ + +#include <cxxtest/TestTracker.h> + +namespace CxxTest +{ + bool TestTracker::_created = false; + + TestTracker::TestTracker() + { + if ( !_created ) { + initialize(); + _created = true; + } + } + + TestTracker::~TestTracker() + { + } + + TestTracker & TestTracker::tracker() + { + static TestTracker theTracker; + return theTracker; + } + + void TestTracker::initialize() + { + _warnings = 0; + _failedTests = 0; + _testFailedAsserts = 0; + _suiteFailedTests = 0; + _failedSuites = 0; + setListener( 0 ); + _world = 0; + _suite = 0; + _test = 0; + } + + const TestDescription *TestTracker::fixTest( const TestDescription *d ) const + { + return d ? d : &dummyTest(); + } + + const SuiteDescription *TestTracker::fixSuite( const SuiteDescription *d ) const + { + return d ? d : &dummySuite(); + } + + const WorldDescription *TestTracker::fixWorld( const WorldDescription *d ) const + { + return d ? d : &dummyWorld(); + } + + const TestDescription &TestTracker::dummyTest() const + { + return dummySuite().testDescription(0); + } + + const SuiteDescription &TestTracker::dummySuite() const + { + return dummyWorld().suiteDescription(0); + } + + const WorldDescription &TestTracker::dummyWorld() const + { + return _dummyWorld; + } + + void TestTracker::setListener( TestListener *l ) + { + _l = l ? l : &_dummyListener; + } + + void TestTracker::enterWorld( const WorldDescription &wd ) + { + setWorld( &wd ); + _warnings = _failedTests = _testFailedAsserts = _suiteFailedTests = _failedSuites = 0; + _l->enterWorld( wd ); + } + + void TestTracker::enterSuite( const SuiteDescription &sd ) + { + setSuite( &sd ); + _testFailedAsserts = _suiteFailedTests = 0; + _l->enterSuite(sd); + } + + void TestTracker::enterTest( const TestDescription &td ) + { + setTest( &td ); + _testFailedAsserts = false; + _l->enterTest(td); + } + + void TestTracker::leaveTest( const TestDescription &td ) + { + _l->leaveTest( td ); + setTest( 0 ); + } + + void TestTracker::leaveSuite( const SuiteDescription &sd ) + { + _l->leaveSuite( sd ); + setSuite( 0 ); + } + + void TestTracker::leaveWorld( const WorldDescription &wd ) + { + _l->leaveWorld( wd ); + setWorld( 0 ); + } + + void TestTracker::trace( const char *file, unsigned line, const char *expression ) + { + _l->trace( file, line, expression ); + } + + void TestTracker::warning( const char *file, unsigned line, const char *expression ) + { + countWarning(); + _l->warning( file, line, expression ); + } + + void TestTracker::failedTest( const char *file, unsigned line, const char *expression ) + { + countFailure(); + _l->failedTest( file, line, expression ); + } + + void TestTracker::failedAssert( const char *file, unsigned line, const char *expression ) + { + countFailure(); + _l->failedAssert( file, line, expression ); + } + + void TestTracker::failedAssertEquals( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + countFailure(); + _l->failedAssertEquals( file, line, xStr, yStr, x, y ); + } + + void TestTracker::failedAssertSameData( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *sizeStr, const void *x, + const void *y, unsigned size ) + { + countFailure(); + _l->failedAssertSameData( file, line, xStr, yStr, sizeStr, x, y, size ); + } + + void TestTracker::failedAssertDelta( const char *file, unsigned line, + const char *xStr, const char *yStr, const char *dStr, + const char *x, const char *y, const char *d ) + { + countFailure(); + _l->failedAssertDelta( file, line, xStr, yStr, dStr, x, y, d ); + } + + void TestTracker::failedAssertDiffers( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *value ) + { + countFailure(); + _l->failedAssertDiffers( file, line, xStr, yStr, value ); + } + + void TestTracker::failedAssertLessThan( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + countFailure(); + _l->failedAssertLessThan( file, line, xStr, yStr, x, y ); + } + + void TestTracker::failedAssertLessThanEquals( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ) + { + countFailure(); + _l->failedAssertLessThanEquals( file, line, xStr, yStr, x, y ); + } + + void TestTracker::failedAssertPredicate( const char *file, unsigned line, + const char *predicate, const char *xStr, const char *x ) + { + countFailure(); + _l->failedAssertPredicate( file, line, predicate, xStr, x ); + } + + void TestTracker::failedAssertRelation( const char *file, unsigned line, + const char *relation, const char *xStr, const char *yStr, + const char *x, const char *y ) + { + countFailure(); + _l->failedAssertRelation( file, line, relation, xStr, yStr, x, y ); + } + + void TestTracker::failedAssertThrows( const char *file, unsigned line, + const char *expression, const char *type, + bool otherThrown ) + { + countFailure(); + _l->failedAssertThrows( file, line, expression, type, otherThrown ); + } + + void TestTracker::failedAssertThrowsNot( const char *file, unsigned line, const char *expression ) + { + countFailure(); + _l->failedAssertThrowsNot( file, line, expression ); + } + + void TestTracker::setWorld( const WorldDescription *w ) + { + _world = fixWorld( w ); + setSuite( 0 ); + } + + void TestTracker::setSuite( const SuiteDescription *s ) + { + _suite = fixSuite( s ); + setTest( 0 ); + } + + void TestTracker::setTest( const TestDescription *t ) + { + _test = fixTest( t ); + } + + void TestTracker::countWarning() + { + ++ _warnings; + } + + void TestTracker::countFailure() + { + if ( ++ _testFailedAsserts == 1 ) { + ++ _failedTests; + if ( ++ _suiteFailedTests == 1 ) + ++ _failedSuites; + } + } +}; + +#endif // __cxxtest__TestTracker_cpp__ diff --git a/cxxtest/TestTracker.h b/cxxtest/TestTracker.h new file mode 100644 index 0000000..c85abff --- /dev/null +++ b/cxxtest/TestTracker.h @@ -0,0 +1,114 @@ +#ifndef __cxxtest__TestTracker_h__ +#define __cxxtest__TestTracker_h__ + +// +// The TestTracker tracks running tests +// The actual work is done in CountingListenerProxy, +// but this way avoids cyclic references TestListener<->CountingListenerProxy +// + +#include <cxxtest/TestListener.h> +#include <cxxtest/DummyDescriptions.h> + +namespace CxxTest +{ + class TestListener; + + class TestTracker : public TestListener + { + public: + virtual ~TestTracker(); + + static TestTracker &tracker(); + + const TestDescription *fixTest( const TestDescription *d ) const; + const SuiteDescription *fixSuite( const SuiteDescription *d ) const; + const WorldDescription *fixWorld( const WorldDescription *d ) const; + + const TestDescription &test() const { return *_test; } + const SuiteDescription &suite() const { return *_suite; } + const WorldDescription &world() const { return *_world; } + + bool testFailed() const { return (testFailedAsserts() > 0); } + bool suiteFailed() const { return (suiteFailedTests() > 0); } + bool worldFailed() const { return (failedSuites() > 0); } + + unsigned warnings() const { return _warnings; } + unsigned failedTests() const { return _failedTests; } + unsigned testFailedAsserts() const { return _testFailedAsserts; } + unsigned suiteFailedTests() const { return _suiteFailedTests; } + unsigned failedSuites() const { return _failedSuites; } + + void enterWorld( const WorldDescription &wd ); + void enterSuite( const SuiteDescription &sd ); + void enterTest( const TestDescription &td ); + void leaveTest( const TestDescription &td ); + void leaveSuite( const SuiteDescription &sd ); + void leaveWorld( const WorldDescription &wd ); + void trace( const char *file, unsigned line, const char *expression ); + void warning( const char *file, unsigned line, const char *expression ); + void failedTest( const char *file, unsigned line, const char *expression ); + void failedAssert( const char *file, unsigned line, const char *expression ); + void failedAssertEquals( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ); + void failedAssertSameData( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *sizeStr, const void *x, + const void *y, unsigned size ); + void failedAssertDelta( const char *file, unsigned line, + const char *xStr, const char *yStr, const char *dStr, + const char *x, const char *y, const char *d ); + void failedAssertDiffers( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *value ); + void failedAssertLessThan( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ); + void failedAssertLessThanEquals( const char *file, unsigned line, + const char *xStr, const char *yStr, + const char *x, const char *y ); + void failedAssertPredicate( const char *file, unsigned line, + const char *predicate, const char *xStr, const char *x ); + void failedAssertRelation( const char *file, unsigned line, + const char *relation, const char *xStr, const char *yStr, + const char *x, const char *y ); + void failedAssertThrows( const char *file, unsigned line, + const char *expression, const char *type, + bool otherThrown ); + void failedAssertThrowsNot( const char *file, unsigned line, const char *expression ); + + private: + TestTracker( const TestTracker & ); + TestTracker &operator=( const TestTracker & ); + + static bool _created; + TestListener _dummyListener; + DummyWorldDescription _dummyWorld; + unsigned _warnings, _failedTests, _testFailedAsserts, _suiteFailedTests, _failedSuites; + TestListener *_l; + const WorldDescription *_world; + const SuiteDescription *_suite; + const TestDescription *_test; + + const TestDescription &dummyTest() const; + const SuiteDescription &dummySuite() const; + const WorldDescription &dummyWorld() const; + + void setWorld( const WorldDescription *w ); + void setSuite( const SuiteDescription *s ); + void setTest( const TestDescription *t ); + void countWarning(); + void countFailure(); + + friend class TestRunner; + + TestTracker(); + void initialize(); + void setListener( TestListener *l ); + }; + + inline TestTracker &tracker() { return TestTracker::tracker(); } +}; + +#endif // __cxxtest__TestTracker_h__ diff --git a/cxxtest/ValueTraits.cpp b/cxxtest/ValueTraits.cpp new file mode 100644 index 0000000..7d29ada --- /dev/null +++ b/cxxtest/ValueTraits.cpp @@ -0,0 +1,140 @@ +#ifndef __cxxtest__ValueTraits_cpp__ +#define __cxxtest__ValueTraits_cpp__ + +#include <cxxtest/ValueTraits.h> + +namespace CxxTest +{ + // + // Non-inline functions from ValueTraits.h + // + + char digitToChar( unsigned digit ) + { + if ( digit < 10 ) + return (char)('0' + digit); + if ( digit <= 10 + 'Z' - 'A' ) + return (char)('A' + digit - 10); + return '?'; + } + + const char *byteToHex( unsigned char byte ) + { + static char asHex[3]; + asHex[0] = digitToChar( byte >> 4 ); + asHex[1] = digitToChar( byte & 0x0F ); + asHex[2] = '\0'; + return asHex; + } + + char *copyString( char *dst, const char *src ) + { + while ( (*dst = *src) != '\0' ) { + ++ dst; + ++ src; + } + return dst; + } + + bool stringsEqual( const char *s1, const char *s2 ) + { + char c; + while ( (c = *s1++) == *s2++ ) + if ( c == '\0' ) + return true; + return false; + } + + char *charToString( unsigned long c, char *s ) + { + switch( c ) { + case '\\': return copyString( s, "\\\\" ); + case '\"': return copyString( s, "\\\"" ); + case '\'': return copyString( s, "\\\'" ); + case '\0': return copyString( s, "\\0" ); + case '\a': return copyString( s, "\\a" ); + case '\b': return copyString( s, "\\b" ); + case '\n': return copyString( s, "\\n" ); + case '\r': return copyString( s, "\\r" ); + case '\t': return copyString( s, "\\t" ); + } + if ( c >= 32 && c <= 127 ) { + s[0] = (char)c; + s[1] = '\0'; + return s + 1; + } + else { + s[0] = '\\'; + s[1] = 'x'; + if ( c < 0x10 ) { + s[2] = '0'; + ++ s; + } + return numberToString( c, s + 2, 16UL ); + } + } + + char *charToString( char c, char *s ) + { + return charToString( (unsigned long)(unsigned char)c, s ); + } + + char *bytesToString( const unsigned char *bytes, unsigned numBytes, unsigned maxBytes, char *s ) + { + bool truncate = (numBytes > maxBytes); + if ( truncate ) + numBytes = maxBytes; + + s = copyString( s, "{ " ); + for ( unsigned i = 0; i < numBytes; ++ i, ++ bytes ) + s = copyString( copyString( s, byteToHex( *bytes ) ), " " ); + if ( truncate ) + s = copyString( s, "..." ); + return copyString( s, " }" ); + } + +#ifndef CXXTEST_USER_VALUE_TRAITS + unsigned ValueTraits<const double>::requiredDigitsOnLeft( double t ) + { + unsigned digits = 1; + for ( t = (t < 0.0) ? -t : t; t > 1.0; t /= BASE ) + ++ digits; + return digits; + } + + char *ValueTraits<const double>::doNegative( double &t ) + { + if ( t >= 0 ) + return _asString; + _asString[0] = '-'; + t = -t; + return _asString + 1; + } + + void ValueTraits<const double>::hugeNumber( double t ) + { + char *s = doNegative( t ); + s = doubleToString( t, s, 0, 1 ); + s = copyString( s, "." ); + s = doubleToString( t, s, 1, DIGITS_ON_RIGHT ); + s = copyString( s, "E" ); + s = numberToString( requiredDigitsOnLeft( t ) - 1, s ); + } + + void ValueTraits<const double>::normalNumber( double t ) + { + char *s = doNegative( t ); + s = doubleToString( t, s ); + s = copyString( s, "." ); + for ( unsigned i = 0; i < DIGITS_ON_RIGHT; ++ i ) + s = numberToString( (unsigned)(t *= BASE) % BASE, s ); + } + + char *ValueTraits<const double>::doubleToString( double t, char *s, unsigned skip, unsigned max ) + { + return numberToString<double>( t, s, BASE, skip, max ); + } +#endif // !CXXTEST_USER_VALUE_TRAITS +}; + +#endif // __cxxtest__ValueTraits_cpp__ diff --git a/cxxtest/ValueTraits.h b/cxxtest/ValueTraits.h new file mode 100644 index 0000000..71145ad --- /dev/null +++ b/cxxtest/ValueTraits.h @@ -0,0 +1,377 @@ +#ifndef __cxxtest__ValueTraits_h__ +#define __cxxtest__ValueTraits_h__ + +// +// ValueTraits are used by CxxTest to convert arbitrary +// values used in TS_ASSERT_EQUALS() to a string representation. +// +// This header file contains value traits for builtin integral types. +// To declare value traits for new types you should instantiate the class +// ValueTraits<YourClass>. +// + +#include <cxxtest/Flags.h> + +#ifdef _CXXTEST_OLD_TEMPLATE_SYNTAX +# define CXXTEST_TEMPLATE_INSTANTIATION +#else // !_CXXTEST_OLD_TEMPLATE_SYNTAX +# define CXXTEST_TEMPLATE_INSTANTIATION template<> +#endif // _CXXTEST_OLD_TEMPLATE_SYNTAX + +namespace CxxTest +{ + // + // This is how we use the value traits + // +# define TS_AS_STRING(x) CxxTest::traits(x).asString() + + // + // Char representation of a digit + // + char digitToChar( unsigned digit ); + + // + // Convert byte value to hex digits + // Returns pointer to internal buffer + // + const char *byteToHex( unsigned char byte ); + + // + // Convert byte values to string + // Returns one past the copied data + // + char *bytesToString( const unsigned char *bytes, unsigned numBytes, unsigned maxBytes, char *s ); + + // + // Copy a string. + // Returns one past the end of the destination string + // Remember -- we can't use the standard library! + // + char *copyString( char *dst, const char *src ); + + // + // Compare two strings. + // Remember -- we can't use the standard library! + // + bool stringsEqual( const char *s1, const char *s2 ); + + // + // Represent a character value as a string + // Returns one past the end of the string + // This will be the actual char if printable or '\xXXXX' otherwise + // + char *charToString( unsigned long c, char *s ); + + // + // Prevent problems with negative (signed char)s + // + char *charToString( char c, char *s ); + + // + // The default ValueTraits class dumps up to 8 bytes as hex values + // + template <class T> + class ValueTraits + { + enum { MAX_BYTES = 8 }; + char _asString[sizeof("{ ") + sizeof("XX ") * MAX_BYTES + sizeof("... }")]; + + public: + ValueTraits( const T &t ) { bytesToString( (const unsigned char *)&t, sizeof(T), MAX_BYTES, _asString ); } + const char *asString( void ) const { return _asString; } + }; + + // + // traits( T t ) + // Creates an object of type ValueTraits<T> + // + template <class T> + inline ValueTraits<T> traits( T t ) + { + return ValueTraits<T>( t ); + } + + // + // You can duplicate the implementation of an existing ValueTraits + // +# define CXXTEST_COPY_TRAITS(CXXTEST_NEW_CLASS, CXXTEST_OLD_CLASS) \ + CXXTEST_TEMPLATE_INSTANTIATION \ + class ValueTraits< CXXTEST_NEW_CLASS > \ + { \ + ValueTraits< CXXTEST_OLD_CLASS > _old; \ + public: \ + ValueTraits( CXXTEST_NEW_CLASS n ) : _old( (CXXTEST_OLD_CLASS)n ) {} \ + const char *asString( void ) const { return _old.asString(); } \ + } + + // + // Certain compilers need separate declarations for T and const T + // +# ifdef _CXXTEST_NO_COPY_CONST +# define CXXTEST_COPY_CONST_TRAITS(CXXTEST_CLASS) +# else // !_CXXTEST_NO_COPY_CONST +# define CXXTEST_COPY_CONST_TRAITS(CXXTEST_CLASS) CXXTEST_COPY_TRAITS(CXXTEST_CLASS, const CXXTEST_CLASS) +# endif // _CXXTEST_NO_COPY_CONST + + // + // Avoid compiler warnings about unsigned types always >= 0 + // + template<class N> inline bool negative( N n ) { return n < 0; } + template<class N> inline N abs( N n ) { return negative(n) ? -n : n; } + +# define CXXTEST_NON_NEGATIVE(Type) \ + CXXTEST_TEMPLATE_INSTANTIATION \ + inline bool negative<Type>( Type ) { return false; } \ + CXXTEST_TEMPLATE_INSTANTIATION \ + inline Type abs<Type>( Type value ) { return value; } + + CXXTEST_NON_NEGATIVE( bool ) + CXXTEST_NON_NEGATIVE( unsigned char ) + CXXTEST_NON_NEGATIVE( unsigned short int ) + CXXTEST_NON_NEGATIVE( unsigned int ) + CXXTEST_NON_NEGATIVE( unsigned long int ) +# ifdef _CXXTEST_LONGLONG + CXXTEST_NON_NEGATIVE( unsigned _CXXTEST_LONGLONG ) +# endif // _CXXTEST_LONGLONG + + // + // Represent (integral) number as a string + // Returns one past the end of the string + // Remember -- we can't use the standard library! + // + template<class N> + char *numberToString( N n, char *s, + N base = 10, + unsigned skipDigits = 0, + unsigned maxDigits = (unsigned)-1 ) + { + if ( negative(n) ) { + *s++ = '-'; + n = abs(n); + } + + N digit = 1; + while ( digit <= (n / base) ) + digit *= base; + N digitValue; + for ( ; digit >= 1 && skipDigits; n -= digit * digitValue, digit /= base, -- skipDigits ) + digitValue = (unsigned)(n / digit); + for ( ; digit >= 1 && maxDigits; n -= digit * digitValue, digit /= base, -- maxDigits ) + *s++ = digitToChar( (unsigned)(digitValue = (unsigned)(n / digit)) ); + + *s = '\0'; + return s; + } + + // + // All the specific ValueTraits follow. + // You can #define CXXTEST_USER_VALUE_TRAITS if you don't want them + // + +#ifndef CXXTEST_USER_VALUE_TRAITS + // + // ValueTraits: const char * const & + // This is used for printing strings, as in TS_FAIL( "Message" ) + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const char * const &> + { + ValueTraits &operator=( const ValueTraits & ); + const char *_asString; + + public: + ValueTraits( const char * const &value ) : _asString( value ) {} + ValueTraits( const ValueTraits &other ) : _asString( other._asString ) {} + const char *asString( void ) const { return _asString; } + }; + + CXXTEST_COPY_TRAITS( const char *, const char * const & ); + CXXTEST_COPY_TRAITS( char *, const char * const & ); + + // + // ValueTraits: bool + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const bool> + { + bool _value; + + public: + ValueTraits( const bool value ) : _value( value ) {} + const char *asString( void ) const { return _value ? "true" : "false"; } + }; + + CXXTEST_COPY_CONST_TRAITS( bool ); + +# ifdef _CXXTEST_LONGLONG + // + // ValueTraits: signed long long + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const signed _CXXTEST_LONGLONG> + { + typedef _CXXTEST_LONGLONG T; + char _asString[2 + 3 * sizeof(T)]; + public: + ValueTraits( T t ) { numberToString<T>( t, _asString ); } + const char *asString( void ) const { return _asString; } + }; + + CXXTEST_COPY_CONST_TRAITS( signed _CXXTEST_LONGLONG ); + + // + // ValueTraits: unsigned long long + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const unsigned _CXXTEST_LONGLONG> + { + typedef unsigned _CXXTEST_LONGLONG T; + char _asString[1 + 3 * sizeof(T)]; + public: + ValueTraits( T t ) { numberToString<T>( t, _asString ); } + const char *asString( void ) const { return _asString; } + }; + + CXXTEST_COPY_CONST_TRAITS( unsigned _CXXTEST_LONGLONG ); +# endif // _CXXTEST_LONGLONG + + // + // ValueTraits: signed long + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const signed long int> + { + typedef signed long int T; + char _asString[2 + 3 * sizeof(T)]; + public: + ValueTraits( T t ) { numberToString<T>( t, _asString ); } + const char *asString( void ) const { return _asString; } + }; + + CXXTEST_COPY_CONST_TRAITS( signed long int ); + + // + // ValueTraits: unsigned long + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const unsigned long int> + { + typedef unsigned long int T; + char _asString[1 + 3 * sizeof(T)]; + public: + ValueTraits( T t ) { numberToString<T>( t, _asString ); } + const char *asString( void ) const { return _asString; } + }; + + CXXTEST_COPY_CONST_TRAITS( unsigned long int ); + + // + // All decimals are the same as the long version + // + + CXXTEST_COPY_TRAITS( const signed int, const signed long int ); + CXXTEST_COPY_TRAITS( const unsigned int, const unsigned long int ); + CXXTEST_COPY_TRAITS( const signed short int, const signed long int ); + CXXTEST_COPY_TRAITS( const unsigned short int, const unsigned long int ); + CXXTEST_COPY_TRAITS( const unsigned char, const unsigned long int ); + + CXXTEST_COPY_CONST_TRAITS( signed int ); + CXXTEST_COPY_CONST_TRAITS( unsigned int ); + CXXTEST_COPY_CONST_TRAITS( signed short int ); + CXXTEST_COPY_CONST_TRAITS( unsigned short int ); + CXXTEST_COPY_CONST_TRAITS( unsigned char ); + + // + // ValueTraits: char + // Returns 'x' for printable chars, '\x??' for others + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const char> + { + char _asString[sizeof("'\\xXX'")]; + public: + ValueTraits( char c ) { copyString( charToString( c, copyString( _asString, "'" ) ), "'" ); } + const char *asString( void ) const { return _asString; } + }; + + CXXTEST_COPY_CONST_TRAITS( char ); + + // + // ValueTraits: signed char + // Same as char, some compilers need it + // + CXXTEST_COPY_TRAITS( const signed char, const char ); + CXXTEST_COPY_CONST_TRAITS( signed char ); + + // + // ValueTraits: double + // + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits<const double> + { + public: + ValueTraits( double t ) + { + ( requiredDigitsOnLeft( t ) > MAX_DIGITS_ON_LEFT ) ? + hugeNumber( t ) : + normalNumber( t ); + } + + const char *asString( void ) const { return _asString; } + + private: + enum { MAX_DIGITS_ON_LEFT = 24, DIGITS_ON_RIGHT = 4, BASE = 10 }; + char _asString[1 + MAX_DIGITS_ON_LEFT + 1 + DIGITS_ON_RIGHT + 1]; + + static unsigned requiredDigitsOnLeft( double t ); + char *doNegative( double &t ); + void hugeNumber( double t ); + void normalNumber( double t ); + char *doubleToString( double t, char *s, unsigned skip = 0, unsigned max = (unsigned)-1 ); + }; + + CXXTEST_COPY_CONST_TRAITS( double ); + + // + // ValueTraits: float + // + CXXTEST_COPY_TRAITS( const float, const double ); + CXXTEST_COPY_CONST_TRAITS( float ); +#endif // !CXXTEST_USER_VALUE_TRAITS +}; + +#ifdef _CXXTEST_HAVE_STD +# include <cxxtest/StdValueTraits.h> +#endif // _CXXTEST_HAVE_STD + +// +// CXXTEST_ENUM_TRAITS +// +#define CXXTEST_ENUM_TRAITS( TYPE, VALUES ) \ + namespace CxxTest \ + { \ + CXXTEST_TEMPLATE_INSTANTIATION \ + class ValueTraits<TYPE> \ + { \ + TYPE _value; \ + char _fallback[sizeof("(" #TYPE ")") + 3 * sizeof(TYPE)]; \ + public: \ + ValueTraits( TYPE value ) { \ + _value = value; \ + numberToString<unsigned long int>( _value, copyString( _fallback, "(" #TYPE ")" ) ); \ + } \ + const char *asString( void ) const \ + { \ + switch ( _value ) \ + { \ + VALUES \ + default: return _fallback; \ + } \ + } \ + }; \ + } + +#define CXXTEST_ENUM_MEMBER( MEMBER ) \ + case MEMBER: return #MEMBER; + +#endif // __cxxtest__ValueTraits_h__ diff --git a/cxxtest/Win32Gui.h b/cxxtest/Win32Gui.h new file mode 100644 index 0000000..6b3e758 --- /dev/null +++ b/cxxtest/Win32Gui.h @@ -0,0 +1,531 @@ +#ifndef __cxxtest__Win32Gui_h__ +#define __cxxtest__Win32Gui_h__ + +// +// The Win32Gui displays a simple progress bar using the Win32 API. +// +// It accepts the following command line options: +// -minimized Start minimized, pop up on error +// -keep Don't close the window at the end +// -title TITLE Set the window caption +// +// If both -minimized and -keep are specified, GUI will only keep the +// window if it's in focus. +// +// N.B. If you're wondering why this class doesn't use any standard +// library or STL (<string> would have been nice) it's because it only +// uses "straight" Win32 API. +// + +#include <cxxtest/Gui.h> + +#include <windows.h> +#include <commctrl.h> + +namespace CxxTest +{ + class Win32Gui : public GuiListener + { + public: + void enterGui( int &argc, char **argv ) + { + parseCommandLine( argc, argv ); + } + + void enterWorld( const WorldDescription &wd ) + { + getTotalTests( wd ); + _testsDone = 0; + startGuiThread(); + } + + void guiEnterSuite( const char *suiteName ) + { + showSuiteName( suiteName ); + reset( _suiteStart ); + } + + void guiEnterTest( const char *suiteName, const char *testName ) + { + ++ _testsDone; + setTestCaption( suiteName, testName ); + showTestName( testName ); + showTestsDone(); + progressBarMessage( PBM_STEPIT ); + reset( _testStart ); + } + + void yellowBar() + { + setColor( 255, 255, 0 ); + setIcon( IDI_WARNING ); + getTotalTests(); + } + + void redBar() + { + if ( _startMinimized ) + showMainWindow( SW_SHOWNORMAL ); + setColor( 255, 0, 0 ); + setIcon( IDI_ERROR ); + getTotalTests(); + } + + void leaveGui() + { + if ( keep() ) + { + showSummary(); + WaitForSingleObject( _gui, INFINITE ); + } + DestroyWindow( _mainWindow ); + } + + private: + const char *_title; + bool _startMinimized, _keep; + HANDLE _gui; + WNDCLASSEX _windowClass; + HWND _mainWindow, _progressBar, _statusBar; + HANDLE _canStartTests; + unsigned _numTotalTests, _testsDone; + char _strTotalTests[WorldDescription::MAX_STRLEN_TOTAL_TESTS]; + enum { + STATUS_SUITE_NAME, STATUS_SUITE_TIME, + STATUS_TEST_NAME, STATUS_TEST_TIME, + STATUS_TESTS_DONE, STATUS_WORLD_TIME, + STATUS_TOTAL_PARTS + }; + int _statusWidths[STATUS_TOTAL_PARTS]; + unsigned _statusOffsets[STATUS_TOTAL_PARTS]; + unsigned _statusTotal; + char _statusTestsDone[sizeof("1000000000 of (100%)") + WorldDescription::MAX_STRLEN_TOTAL_TESTS]; + DWORD _worldStart, _suiteStart, _testStart; + char _timeString[sizeof("00:00:00")]; + + void parseCommandLine( int argc, char **argv ) + { + _startMinimized = _keep = false; + _title = argv[0]; + + for ( int i = 1; i < argc; ++ i ) + { + if ( !lstrcmpA( argv[i], "-minimized" ) ) + _startMinimized = true; + else if ( !lstrcmpA( argv[i], "-keep" ) ) + _keep = true; + else if ( !lstrcmpA( argv[i], "-title" ) && (i + 1 < argc) ) + _title = argv[++i]; + } + } + + void getTotalTests() + { + getTotalTests( tracker().world() ); + } + + void getTotalTests( const WorldDescription &wd ) + { + _numTotalTests = wd.numTotalTests(); + wd.strTotalTests( _strTotalTests ); + } + + void startGuiThread() + { + _canStartTests = CreateEvent( NULL, TRUE, FALSE, NULL ); + DWORD threadId; + _gui = CreateThread( NULL, 0, &(Win32Gui::guiThread), (LPVOID)this, 0, &threadId ); + WaitForSingleObject( _canStartTests, INFINITE ); + } + + static DWORD WINAPI guiThread( LPVOID parameter ) + { + ((Win32Gui *)parameter)->gui(); + return 0; + } + + void gui() + { + registerWindowClass(); + createMainWindow(); + initCommonControls(); + createProgressBar(); + createStatusBar(); + centerMainWindow(); + showMainWindow(); + startTimer(); + startTests(); + + messageLoop(); + } + + void registerWindowClass() + { + _windowClass.cbSize = sizeof(_windowClass); + _windowClass.style = CS_HREDRAW | CS_VREDRAW; + _windowClass.lpfnWndProc = &(Win32Gui::windowProcedure); + _windowClass.cbClsExtra = 0; + _windowClass.cbWndExtra = sizeof(LONG); + _windowClass.hInstance = (HINSTANCE)NULL; + _windowClass.hIcon = (HICON)NULL; + _windowClass.hCursor = (HCURSOR)NULL; + _windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + _windowClass.lpszMenuName = NULL; + _windowClass.lpszClassName = TEXT("CxxTest Window Class"); + _windowClass.hIconSm = (HICON)NULL; + + RegisterClassEx( &_windowClass ); + } + + void createMainWindow() + { + _mainWindow = createWindow( _windowClass.lpszClassName, WS_OVERLAPPEDWINDOW ); + } + + void initCommonControls() + { + HMODULE dll = LoadLibraryA( "comctl32.dll" ); + if ( !dll ) + return; + + typedef void (WINAPI *FUNC)( void ); + FUNC func = (FUNC)GetProcAddress( dll, "InitCommonControls" ); + if ( !func ) + return; + + func(); + } + + void createProgressBar() + { + _progressBar = createWindow( PROGRESS_CLASS, WS_CHILD | WS_VISIBLE | PBS_SMOOTH, _mainWindow ); + +#ifdef PBM_SETRANGE32 + progressBarMessage( PBM_SETRANGE32, 0, _numTotalTests ); +#else // No PBM_SETRANGE32, use PBM_SETRANGE + progressBarMessage( PBM_SETRANGE, 0, MAKELPARAM( 0, (WORD)_numTotalTests ) ); +#endif // PBM_SETRANGE32 + progressBarMessage( PBM_SETPOS, 0 ); + progressBarMessage( PBM_SETSTEP, 1 ); + greenBar(); + UpdateWindow( _progressBar ); + } + + void createStatusBar() + { + _statusBar = createWindow( STATUSCLASSNAME, WS_CHILD | WS_VISIBLE, _mainWindow ); + setRatios( 4, 1, 3, 1, 3, 1 ); + } + + void setRatios( unsigned suiteNameRatio, unsigned suiteTimeRatio, + unsigned testNameRatio, unsigned testTimeRatio, + unsigned testsDoneRatio, unsigned worldTimeRatio ) + { + _statusTotal = 0; + _statusOffsets[STATUS_SUITE_NAME] = (_statusTotal += suiteNameRatio); + _statusOffsets[STATUS_SUITE_TIME] = (_statusTotal += suiteTimeRatio); + _statusOffsets[STATUS_TEST_NAME] = (_statusTotal += testNameRatio); + _statusOffsets[STATUS_TEST_TIME] = (_statusTotal += testTimeRatio); + _statusOffsets[STATUS_TESTS_DONE] = (_statusTotal += testsDoneRatio); + _statusOffsets[STATUS_WORLD_TIME] = (_statusTotal += worldTimeRatio); + } + + HWND createWindow( LPCTSTR className, DWORD style, HWND parent = (HWND)NULL ) + { + return CreateWindow( className, NULL, style, 0, 0, 0, 0, parent, + (HMENU)NULL, (HINSTANCE)NULL, (LPVOID)this ); + } + + void progressBarMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ) + { + SendMessage( _progressBar, message, wParam, lParam ); + } + + void centerMainWindow() + { + RECT screen; + getScreenArea( screen ); + + LONG screenWidth = screen.right - screen.left; + LONG screenHeight = screen.bottom - screen.top; + + LONG xCenter = (screen.right + screen.left) / 2; + LONG yCenter = (screen.bottom + screen.top) / 2; + + LONG windowWidth = (screenWidth * 4) / 5; + LONG windowHeight = screenHeight / 10; + LONG minimumHeight = 2 * (GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYFRAME )); + if ( windowHeight < minimumHeight ) + windowHeight = minimumHeight; + + SetWindowPos( _mainWindow, HWND_TOP, + xCenter - (windowWidth / 2), yCenter - (windowHeight / 2), + windowWidth, windowHeight, 0 ); + } + + void getScreenArea( RECT &area ) + { + if ( !getScreenAreaWithoutTaskbar( area ) ) + getWholeScreenArea( area ); + } + + bool getScreenAreaWithoutTaskbar( RECT &area ) + { + return (SystemParametersInfo( SPI_GETWORKAREA, sizeof(RECT), &area, 0 ) != 0); + } + + void getWholeScreenArea( RECT &area ) + { + area.left = area.top = 0; + area.right = GetSystemMetrics( SM_CXSCREEN ); + area.bottom = GetSystemMetrics( SM_CYSCREEN ); + } + + void showMainWindow() + { + showMainWindow( _startMinimized ? SW_MINIMIZE : SW_SHOWNORMAL ); + UpdateWindow( _mainWindow ); + } + + void showMainWindow( int mode ) + { + ShowWindow( _mainWindow, mode ); + } + + enum { TIMER_ID = 1, TIMER_DELAY = 1000 }; + + void startTimer() + { + reset( _worldStart ); + reset( _suiteStart ); + reset( _testStart ); + SetTimer( _mainWindow, TIMER_ID, TIMER_DELAY, 0 ); + } + + void reset( DWORD &tick ) + { + tick = GetTickCount(); + } + + void startTests() + { + SetEvent( _canStartTests ); + } + + void messageLoop() + { + MSG message; + while ( BOOL haveMessage = GetMessage( &message, NULL, 0, 0 ) ) + if ( haveMessage != -1 ) + DispatchMessage( &message ); + } + + static LRESULT CALLBACK windowProcedure( HWND window, UINT message, WPARAM wParam, LPARAM lParam ) + { + if ( message == WM_CREATE ) + setUp( window, (LPCREATESTRUCT)lParam ); + + Win32Gui *that = (Win32Gui *)GetWindowLong( window, GWL_USERDATA ); + return that->handle( window, message, wParam, lParam ); + } + + static void setUp( HWND window, LPCREATESTRUCT create ) + { + SetWindowLong( window, GWL_USERDATA, (LONG)create->lpCreateParams ); + } + + LRESULT handle( HWND window, UINT message, WPARAM wParam, LPARAM lParam ) + { + switch ( message ) + { + case WM_SIZE: resizeControls(); break; + + case WM_TIMER: updateTime(); break; + + case WM_CLOSE: + case WM_DESTROY: + case WM_QUIT: + ExitProcess( 0 ); + + default: return DefWindowProc( window, message, wParam, lParam ); + } + return 0; + } + + void resizeControls() + { + RECT r; + GetClientRect( _mainWindow, &r ); + LONG width = r.right - r.left; + LONG height = r.bottom - r.top; + + GetClientRect( _statusBar, &r ); + LONG statusHeight = r.bottom - r.top; + LONG resizeGripWidth = statusHeight; + LONG progressHeight = height - statusHeight; + + SetWindowPos( _progressBar, HWND_TOP, 0, 0, width, progressHeight, 0 ); + SetWindowPos( _statusBar, HWND_TOP, 0, progressHeight, width, statusHeight, 0 ); + setStatusParts( width - resizeGripWidth ); + } + + void setStatusParts( LONG width ) + { + for ( unsigned i = 0; i < STATUS_TOTAL_PARTS; ++ i ) + _statusWidths[i] = (width * _statusOffsets[i]) / _statusTotal; + + statusBarMessage( SB_SETPARTS, STATUS_TOTAL_PARTS, _statusWidths ); + } + + void statusBarMessage( UINT message, WPARAM wParam = 0, const void *lParam = 0 ) + { + SendMessage( _statusBar, message, wParam, (LPARAM)lParam ); + } + + void greenBar() + { + setColor( 0, 255, 0 ); + setIcon( IDI_INFORMATION ); + } + +#ifdef PBM_SETBARCOLOR + void setColor( BYTE red, BYTE green, BYTE blue ) + { + progressBarMessage( PBM_SETBARCOLOR, 0, RGB( red, green, blue ) ); + } +#else // !PBM_SETBARCOLOR + void setColor( BYTE, BYTE, BYTE ) + { + } +#endif // PBM_SETBARCOLOR + + void setIcon( LPCTSTR icon ) + { + SendMessage( _mainWindow, WM_SETICON, ICON_BIG, (LPARAM)loadStandardIcon( icon ) ); + } + + HICON loadStandardIcon( LPCTSTR icon ) + { + return LoadIcon( (HINSTANCE)NULL, icon ); + } + + void setTestCaption( const char *suiteName, const char *testName ) + { + setCaption( suiteName, "::", testName, "()" ); + } + + void setCaption( const char *a = "", const char *b = "", const char *c = "", const char *d = "" ) + { + unsigned length = lstrlenA( _title ) + sizeof( " - " ) + + lstrlenA( a ) + lstrlenA( b ) + lstrlenA( c ) + lstrlenA( d ); + char *name = allocate( length ); + lstrcpyA( name, _title ); + lstrcatA( name, " - " ); + lstrcatA( name, a ); + lstrcatA( name, b ); + lstrcatA( name, c ); + lstrcatA( name, d ); + SetWindowTextA( _mainWindow, name ); + deallocate( name ); + } + + void showSuiteName( const char *suiteName ) + { + setStatusPart( STATUS_SUITE_NAME, suiteName ); + } + + void showTestName( const char *testName ) + { + setStatusPart( STATUS_TEST_NAME, testName ); + } + + void showTestsDone() + { + wsprintfA( _statusTestsDone, "%u of %s (%u%%)", + _testsDone, _strTotalTests, + (_testsDone * 100) / _numTotalTests ); + setStatusPart( STATUS_TESTS_DONE, _statusTestsDone ); + } + + void updateTime() + { + setStatusTime( STATUS_WORLD_TIME, _worldStart ); + setStatusTime( STATUS_SUITE_TIME, _suiteStart ); + setStatusTime( STATUS_TEST_TIME, _testStart ); + } + + void setStatusTime( unsigned part, DWORD start ) + { + unsigned total = (GetTickCount() - start) / 1000; + unsigned hours = total / 3600; + unsigned minutes = (total / 60) % 60; + unsigned seconds = total % 60; + + if ( hours ) + wsprintfA( _timeString, "%u:%02u:%02u", hours, minutes, seconds ); + else + wsprintfA( _timeString, "%02u:%02u", minutes, seconds ); + + setStatusPart( part, _timeString ); + } + + bool keep() + { + if ( !_keep ) + return false; + if ( !_startMinimized ) + return true; + return (_mainWindow == GetForegroundWindow()); + } + + void showSummary() + { + stopTimer(); + setSummaryStatusBar(); + setSummaryCaption(); + } + + void setStatusPart( unsigned part, const char *text ) + { + statusBarMessage( SB_SETTEXTA, part, text ); + } + + void stopTimer() + { + KillTimer( _mainWindow, TIMER_ID ); + setStatusTime( STATUS_WORLD_TIME, _worldStart ); + } + + void setSummaryStatusBar() + { + setRatios( 0, 0, 0, 0, 1, 1 ); + resizeControls(); + + const char *tests = (_numTotalTests == 1) ? "test" : "tests"; + if ( tracker().failedTests() ) + wsprintfA( _statusTestsDone, "Failed %u of %s %s", + tracker().failedTests(), _strTotalTests, tests ); + else + wsprintfA( _statusTestsDone, "%s %s passed", _strTotalTests, tests ); + + setStatusPart( STATUS_TESTS_DONE, _statusTestsDone ); + } + + void setSummaryCaption() + { + setCaption( _statusTestsDone ); + } + + char *allocate( unsigned length ) + { + return (char *)HeapAlloc( GetProcessHeap(), 0, length ); + } + + void deallocate( char *data ) + { + HeapFree( GetProcessHeap(), 0, data ); + } + }; +}; + +#endif // __cxxtest__Win32Gui_h__ diff --git a/cxxtest/X11Gui.h b/cxxtest/X11Gui.h new file mode 100644 index 0000000..f14431d --- /dev/null +++ b/cxxtest/X11Gui.h @@ -0,0 +1,327 @@ +#ifndef __cxxtest__X11Gui_h__ +#define __cxxtest__X11Gui_h__ + +// +// X11Gui displays a simple progress bar using X11 +// +// It accepts the following command-line arguments: +// -title <title> - Sets the application title +// -fn or -font <font> - Sets the font +// -bg or -background <color> - Sets the background color (default=Grey) +// -fg or -foreground <color> - Sets the text color (default=Black) +// -green/-yellow/-red <color> - Sets the colors of the bar +// + +#include <cxxtest/Gui.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +namespace CxxTest +{ + class X11Gui : public GuiListener + { + public: + void enterGui( int &argc, char **argv ) + { + parseCommandLine( argc, argv ); + } + + void enterWorld( const WorldDescription &wd ) + { + openDisplay(); + if ( _display ) { + createColors(); + createWindow(); + createGc(); + createFont(); + centerWindow(); + initializeEvents(); + initializeBar( wd ); + processEvents(); + } + } + + void guiEnterTest( const char *suiteName, const char *testName ) + { + if ( _display ) { + ++ _testsDone; + setWindowName( suiteName, testName ); + redraw(); + } + } + + void yellowBar() + { + if ( _display ) { + _barColor = getColor( _yellowName ); + getTotalTests(); + processEvents(); + } + } + + void redBar() + { + if ( _display ) { + _barColor = getColor( _redName ); + getTotalTests(); + processEvents(); + } + } + + void leaveGui() + { + if ( _display ) { + freeFontInfo(); + destroyGc(); + destroyWindow(); + closeDisplay(); + } + } + + private: + const char *_programName; + Display *_display; + Window _window; + unsigned _numTotalTests, _testsDone; + char _strTotalTests[WorldDescription::MAX_STRLEN_TOTAL_TESTS]; + const char *_foregroundName, *_backgroundName; + const char *_greenName, *_yellowName, *_redName; + unsigned long _foreground, _background, _barColor; + int _width, _height; + GC _gc; + const char *_fontName; + XID _fontId; + XFontStruct *_fontInfo; + int _textHeight, _textDescent; + long _eventMask; + Colormap _colormap; + + void parseCommandLine( int &argc, char **argv ) + { + _programName = argv[0]; + + _fontName = 0; + _foregroundName = "Black"; + _backgroundName = "Grey"; + _greenName = "Green"; + _yellowName = "Yellow"; + _redName = "Red"; + + for ( int i = 1; i + 1 < argc; ++ i ) { + if ( !strcmp( argv[i], "-title" ) ) + _programName = argv[++ i]; + else if ( !strcmp( argv[i], "-fn" ) || !strcmp( argv[i], "-font" ) ) + _fontName = argv[++ i]; + else if ( !strcmp( argv[i], "-fg" ) || !strcmp( argv[i], "-foreground" ) ) + _foregroundName = argv[++ i]; + else if ( !strcmp( argv[i], "-bg" ) || !strcmp( argv[i], "-background" ) ) + _backgroundName = argv[++ i]; + else if ( !strcmp( argv[i], "-green" ) ) + _greenName = argv[++ i]; + else if ( !strcmp( argv[i], "-yellow" ) ) + _yellowName = argv[++ i]; + else if ( !strcmp( argv[i], "-red" ) ) + _redName = argv[++ i]; + } + } + + void openDisplay() + { + _display = XOpenDisplay( NULL ); + } + + void createColors() + { + _colormap = DefaultColormap( _display, 0 ); + _foreground = getColor( _foregroundName ); + _background = getColor( _backgroundName ); + } + + unsigned long getColor( const char *colorName ) + { + XColor color; + XParseColor( _display, _colormap, colorName, &color ); + XAllocColor( _display, _colormap, &color ); + return color.pixel; + } + + void createWindow() + { + _window = XCreateSimpleWindow( _display, RootWindow( _display, 0 ), 0, 0, 1, 1, 0, 0, _background ); + } + + void createGc() + { + _gc = XCreateGC( _display, _window, 0, 0 ); + } + + void createFont() + { + if ( !loadFont() ) + useDefaultFont(); + getFontInfo(); + _textHeight = _fontInfo->ascent + _fontInfo->descent; + _textDescent = _fontInfo->descent; + } + + bool loadFont() + { + if ( !_fontName ) + return false; + _fontId = XLoadFont( _display, _fontName ); + return (XSetFont( _display, _gc, _fontId ) == Success); + } + + void useDefaultFont() + { + _fontId = XGContextFromGC( _gc ); + } + + void getFontInfo() + { + _fontInfo = XQueryFont( _display, _fontId ); + } + + void freeFontInfo() + { + XFreeFontInfo( NULL, _fontInfo, 1 ); + } + + void initializeEvents() + { + _eventMask = ExposureMask; + XSelectInput( _display, _window, _eventMask ); + } + + void initializeBar( const WorldDescription &wd ) + { + getTotalTests( wd ); + _testsDone = 0; + _barColor = getColor( _greenName ); + } + + void getTotalTests() + { + getTotalTests( tracker().world() ); + } + + void getTotalTests( const WorldDescription &wd ) + { + _numTotalTests = wd.numTotalTests(); + wd.strTotalTests( _strTotalTests ); + } + + void centerWindow() + { + XMapWindow( _display, _window ); + + Screen *screen = XDefaultScreenOfDisplay( _display ); + int screenWidth = WidthOfScreen( screen ); + int screenHeight = HeightOfScreen( screen ); + int xCenter = screenWidth / 2; + int yCenter = screenHeight / 2; + + _width = (screenWidth * 4) / 5; + _height = screenHeight / 14; + + XMoveResizeWindow( _display, _window, xCenter - (_width / 2), yCenter - (_height / 2), _width, _height ); + } + + void processEvents() + { + redraw(); + + XEvent event; + while( XCheckMaskEvent( _display, _eventMask, &event ) ) + redraw(); + } + + void setWindowName( const char *suiteName, const char *testName ) + { + unsigned length = strlen( _programName ) + strlen( suiteName ) + strlen( testName ) + sizeof( " - ::()" ); + char *name = (char *)malloc( length ); + sprintf( name, "%s - %s::%s()", _programName, suiteName, testName ); + XSetStandardProperties( _display, _window, name, 0, 0, 0, 0, 0 ); + free( name ); + } + + void redraw() + { + getWindowSize(); + drawSolidBar(); + drawDividers(); + drawPercentage(); + flush(); + } + + void getWindowSize() + { + XWindowAttributes attributes; + XGetWindowAttributes( _display, _window, &attributes ); + _width = attributes.width; + _height = attributes.height; + } + + void drawSolidBar() + { + unsigned barWidth = (_width * _testsDone) / _numTotalTests; + + XSetForeground( _display, _gc, _barColor ); + XFillRectangle( _display, _window, _gc, 0, 0, barWidth, _height ); + + XSetForeground( _display, _gc, _background ); + XFillRectangle( _display, _window, _gc, barWidth, 0, _width + 1 - barWidth, _height ); + } + + void drawDividers() + { + if(_width / _numTotalTests < 5) + return; + for ( unsigned i = 1; i < _testsDone; ++ i ) { + int x = (_width * i) / _numTotalTests; + XDrawLine( _display, _window, _gc, x, 0, x, _height); + } + } + + void drawPercentage() + { + XSetForeground( _display, _gc, _foreground ); + + char str[sizeof("1000000000 of ") + sizeof(_strTotalTests) + sizeof(" (100%)")]; + sprintf( str, "%u of %s (%u%%)", _testsDone, _strTotalTests, (_testsDone * 100) / _numTotalTests ); + unsigned len = strlen( str ); + + int textWidth = XTextWidth( _fontInfo, str, len ); + + XDrawString( _display, _window, _gc, + (_width - textWidth) / 2, ((_height + _textHeight) / 2) - _textDescent, + str, len ); + } + + void flush() + { + XFlush( _display ); + } + + void destroyGc() + { + XFreeGC( _display, _gc ); + } + + void destroyWindow() + { + XDestroyWindow( _display, _window ); + } + + void closeDisplay() + { + XCloseDisplay( _display ); + } + }; +}; + +#endif //__cxxtest__X11Gui_h__ diff --git a/cxxtest/YesNoRunner.h b/cxxtest/YesNoRunner.h new file mode 100644 index 0000000..e7b83b6 --- /dev/null +++ b/cxxtest/YesNoRunner.h @@ -0,0 +1,29 @@ +#ifndef __cxxtest__YesNoRunner_h__ +#define __cxxtest__YesNoRunner_h__ + +// +// The YesNoRunner is a simple TestListener that +// just returns true iff all tests passed. +// + +#include <cxxtest/TestRunner.h> +#include <cxxtest/TestListener.h> + +namespace CxxTest +{ + class YesNoRunner : public TestListener + { + public: + YesNoRunner() + { + } + + int run() + { + TestRunner::runAllTests( *this ); + return tracker().failedTests(); + } + }; +} + +#endif // __cxxtest__YesNoRunner_h__ diff --git a/cxxtestgen b/cxxtestgen new file mode 100755 index 0000000..9eb4c58 --- /dev/null +++ b/cxxtestgen @@ -0,0 +1,597 @@ +#!/usr/bin/python +'''Usage: %s [OPTIONS] <input file(s)> +Generate test source file for CxxTest. + + -v, --version Write CxxTest version + -o, --output=NAME Write output to file NAME + --runner=CLASS Create a main() function that runs CxxTest::CLASS + --gui=CLASS Like --runner, with GUI component + --error-printer Same as --runner=ErrorPrinter + --abort-on-fail Abort tests on failed asserts (like xUnit) + --have-std Use standard library (even if not found in tests) + --no-std Don\'t use standard library (even if found in tests) + --have-eh Use exception handling (even if not found in tests) + --no-eh Don\'t use exception handling (even if found in tests) + --longlong=[TYPE] Use TYPE (default: long long) as long long + --template=TEMPLATE Use TEMPLATE file to generate the test runner + --include=HEADER Include HEADER in test runner before other headers + --root Write CxxTest globals + --part Don\'t write CxxTest globals + --no-static-init Don\'t rely on static initialization +''' + +import re +import sys +import getopt +import glob +import string + +# Global variables +suites = [] +suite = None +inBlock = 0 + +outputFileName = None +runner = None +gui = None +root = None +part = None +noStaticInit = None +templateFileName = None +headers = [] + +haveExceptionHandling = 0 +noExceptionHandling = 0 +haveStandardLibrary = 0 +noStandardLibrary = 0 +abortOnFail = 0 +factor = 0 +longlong = 0 + +def main(): + '''The main program''' + files = parseCommandline() + scanInputFiles( files ) + writeOutput() + +def usage( problem = None ): + '''Print usage info and exit''' + if problem is None: + print usageString() + sys.exit(0) + else: + sys.stderr.write( usageString() ) + abort( problem ) + +def usageString(): + '''Construct program usage string''' + return __doc__ % sys.argv[0] + +def abort( problem ): + '''Print error message and exit''' + sys.stderr.write( '\n' ) + sys.stderr.write( problem ) + sys.stderr.write( '\n\n' ) + sys.exit(2) + +def parseCommandline(): + '''Analyze command line arguments''' + try: + options, patterns = getopt.getopt( sys.argv[1:], 'o:r:', + ['version', 'output=', 'runner=', 'gui=', + 'error-printer', 'abort-on-fail', 'have-std', 'no-std', + 'have-eh', 'no-eh', 'template=', 'include=', + 'root', 'part', 'no-static-init', 'factor', 'longlong='] ) + except getopt.error, problem: + usage( problem ) + setOptions( options ) + return setFiles( patterns ) + +def setOptions( options ): + '''Set options specified on command line''' + global outputFileName, templateFileName, runner, gui, haveStandardLibrary, factor, longlong + global haveExceptionHandling, noExceptionHandling, abortOnFail, headers, root, part, noStaticInit + for o, a in options: + if o in ('-v', '--version'): + printVersion() + elif o in ('-o', '--output'): + outputFileName = a + elif o == '--template': + templateFileName = a + elif o == '--runner': + runner = a + elif o == '--gui': + gui = a + elif o == '--include': + if not re.match( r'^["<].*[>"]$', a ): + a = ('"%s"' % a) + headers.append( a ) + elif o == '--error-printer': + runner = 'ErrorPrinter' + haveStandardLibrary = 1 + elif o == '--abort-on-fail': + abortOnFail = 1 + elif o == '--have-std': + haveStandardLibrary = 1 + elif o == '--no-std': + noStandardLibrary = 1 + elif o == '--have-eh': + haveExceptionHandling = 1 + elif o == '--no-eh': + noExceptionHandling = 1 + elif o == '--root': + root = 1 + elif o == '--part': + part = 1 + elif o == '--no-static-init': + noStaticInit = 1 + elif o == '--factor': + factor = 1 + elif o == '--longlong': + if a: + longlong = a + else: + longlong = 'long long' + + if noStaticInit and (root or part): + abort( '--no-static-init cannot be used with --root/--part' ) + + if gui and not runner: + runner = 'StdioPrinter' + +def printVersion(): + '''Print CxxTest version and exit''' + sys.stdout.write( "This is CxxTest version 3.10.1.\n" ) + sys.exit(0) + +def setFiles( patterns ): + '''Set input files specified on command line''' + files = expandWildcards( patterns ) + if len(files) is 0 and not root: + usage( "No input files found" ) + return files + +def expandWildcards( patterns ): + '''Expand all wildcards in an array (glob)''' + fileNames = [] + for pathName in patterns: + patternFiles = glob.glob( pathName ) + for fileName in patternFiles: + fileNames.append( fixBackslashes( fileName ) ) + return fileNames + +def fixBackslashes( fileName ): + '''Convert backslashes to slashes in file name''' + return re.sub( r'\\', '/', fileName, 0 ) + +def scanInputFiles(files): + '''Scan all input files for test suites''' + for file in files: + scanInputFile(file) + global suites + if len(suites) is 0 and not root: + sys.stderr.write( 'Warning: No tests defined' ) + if len(files) is 1: + sys.stderr.write( ' in ' + files[0] + '\n') + else: + sys.stderr.write( '\n' ) + +def scanInputFile(fileName): + '''Scan single input file for test suites''' + file = open(fileName) + lineNo = 0 + while 1: + line = file.readline() + if not line: + break + lineNo = lineNo + 1 + + scanInputLine( fileName, lineNo, line ) + closeSuite() + file.close() + +def scanInputLine( fileName, lineNo, line ): + '''Scan single input line for interesting stuff''' + scanLineForExceptionHandling( line ) + scanLineForStandardLibrary( line ) + + scanLineForSuiteStart( fileName, lineNo, line ) + + global suite + if suite: + scanLineInsideSuite( suite, lineNo, line ) + +def scanLineInsideSuite( suite, lineNo, line ): + '''Analyze line which is part of a suite''' + global inBlock + if lineBelongsToSuite( suite, lineNo, line ): + scanLineForTest( suite, lineNo, line ) + scanLineForCreate( suite, lineNo, line ) + scanLineForDestroy( suite, lineNo, line ) + +def lineBelongsToSuite( suite, lineNo, line ): + '''Returns whether current line is part of the current suite. + This can be false when we are in a generated suite outside of CXXTEST_CODE() blocks + If the suite is generated, adds the line to the list of lines''' + if not suite['generated']: + return 1 + + global inBlock + if not inBlock: + inBlock = lineStartsBlock( line ) + if inBlock: + inBlock = addLineToBlock( suite, lineNo, line ) + return inBlock + + +std_re = re.compile( r"\b(std\s*::|CXXTEST_STD|using\s+namespace\s+std\b|^\s*\#\s*include\s+<[a-z0-9]+>)" ) +def scanLineForStandardLibrary( line ): + '''Check if current line uses standard library''' + global haveStandardLibrary, noStandardLibrary + if not haveStandardLibrary and std_re.search(line): + if not noStandardLibrary: + haveStandardLibrary = 1 + +exception_re = re.compile( r"\b(throw|try|catch|TSM?_ASSERT_THROWS[A-Z_]*)\b" ) +def scanLineForExceptionHandling( line ): + '''Check if current line uses exception handling''' + global haveExceptionHandling, noExceptionHandling + if not haveExceptionHandling and exception_re.search(line): + if not noExceptionHandling: + haveExceptionHandling = 1 + +suite_re = re.compile( r'\bclass\s+(\w+)\s*:\s*public\s+((::)?\s*CxxTest\s*::\s*)?TestSuite\b' ) +generatedSuite_re = re.compile( r'\bCXXTEST_SUITE\s*\(\s*(\w*)\s*\)' ) +def scanLineForSuiteStart( fileName, lineNo, line ): + '''Check if current line starts a new test suite''' + m = suite_re.search( line ) + if m: + startSuite( m.group(1), fileName, lineNo, 0 ) + m = generatedSuite_re.search( line ) + if m: + sys.stdout.write( "%s:%s: Warning: Inline test suites are deprecated.\n" % (fileName, lineNo) ) + startSuite( m.group(1), fileName, lineNo, 1 ) + +def startSuite( name, file, line, generated ): + '''Start scanning a new suite''' + global suite + closeSuite() + suite = { 'name' : name, + 'file' : file, + 'cfile' : cstr(file), + 'line' : line, + 'generated' : generated, + 'object' : 'suite_%s' % name, + 'dobject' : 'suiteDescription_%s' % name, + 'tlist' : 'Tests_%s' % name, + 'tests' : [], + 'lines' : [] } + +def lineStartsBlock( line ): + '''Check if current line starts a new CXXTEST_CODE() block''' + return re.search( r'\bCXXTEST_CODE\s*\(', line ) is not None + +test_re = re.compile( r'^([^/]|/[^/])*\bvoid\s+(([Tt]est|[Ss]hould)\w+)\s*\(\s*(void)?\s*\)' ) +def scanLineForTest( suite, lineNo, line ): + '''Check if current line starts a test''' + m = test_re.search( line ) + if m: + addTest( suite, m.group(2), lineNo ) + +def addTest( suite, name, line ): + '''Add a test function to the current suite''' + test = { 'name' : name, + 'suite' : suite, + 'class' : 'TestDescription_%s_%s' % (suite['name'], name), + 'object' : 'testDescription_%s_%s' % (suite['name'], name), + 'line' : line, + } + suite['tests'].append( test ) + +def addLineToBlock( suite, lineNo, line ): + '''Append the line to the current CXXTEST_CODE() block''' + line = fixBlockLine( suite, lineNo, line ) + line = re.sub( r'^.*\{\{', '', line ) + + e = re.search( r'\}\}', line ) + if e: + line = line[:e.start()] + suite['lines'].append( line ) + return e is None + +def fixBlockLine( suite, lineNo, line): + '''Change all [E]TS_ macros used in a line to _[E]TS_ macros with the correct file/line''' + return re.sub( r'\b(E?TSM?_(ASSERT[A-Z_]*|FAIL))\s*\(', + r'_\1(%s,%s,' % (suite['cfile'], lineNo), + line, 0 ) + +create_re = re.compile( r'\bstatic\s+\w+\s*\*\s*createSuite\s*\(\s*(void)?\s*\)' ) +def scanLineForCreate( suite, lineNo, line ): + '''Check if current line defines a createSuite() function''' + if create_re.search( line ): + addSuiteCreateDestroy( suite, 'create', lineNo ) + +destroy_re = re.compile( r'\bstatic\s+void\s+destroySuite\s*\(\s*\w+\s*\*\s*\w*\s*\)' ) +def scanLineForDestroy( suite, lineNo, line ): + '''Check if current line defines a destroySuite() function''' + if destroy_re.search( line ): + addSuiteCreateDestroy( suite, 'destroy', lineNo ) + +def cstr( str ): + '''Convert a string to its C representation''' + return '"' + string.replace( str, '\\', '\\\\' ) + '"' + + +def addSuiteCreateDestroy( suite, which, line ): + '''Add createSuite()/destroySuite() to current suite''' + if suite.has_key(which): + abort( '%s:%s: %sSuite() already declared' % ( suite['file'], str(line), which ) ) + suite[which] = line + +def closeSuite(): + '''Close current suite and add it to the list if valid''' + global suite + if suite is not None: + if len(suite['tests']) is not 0: + verifySuite(suite) + rememberSuite(suite) + suite = None + +def verifySuite(suite): + '''Verify current suite is legal''' + if suite.has_key('create') and not suite.has_key('destroy'): + abort( '%s:%s: Suite %s has createSuite() but no destroySuite()' % + (suite['file'], suite['create'], suite['name']) ) + if suite.has_key('destroy') and not suite.has_key('create'): + abort( '%s:%s: Suite %s has destroySuite() but no createSuite()' % + (suite['file'], suite['destroy'], suite['name']) ) + +def rememberSuite(suite): + '''Add current suite to list''' + global suites + suites.append( suite ) + +def writeOutput(): + '''Create output file''' + if templateFileName: + writeTemplateOutput() + else: + writeSimpleOutput() + +def writeSimpleOutput(): + '''Create output not based on template''' + output = startOutputFile() + writePreamble( output ) + writeMain( output ) + writeWorld( output ) + output.close() + +include_re = re.compile( r"\s*\#\s*include\s+<cxxtest/" ) +preamble_re = re.compile( r"^\s*<CxxTest\s+preamble>\s*$" ) +world_re = re.compile( r"^\s*<CxxTest\s+world>\s*$" ) +def writeTemplateOutput(): + '''Create output based on template file''' + template = open(templateFileName) + output = startOutputFile() + while 1: + line = template.readline() + if not line: + break; + if include_re.search( line ): + writePreamble( output ) + output.write( line ) + elif preamble_re.search( line ): + writePreamble( output ) + elif world_re.search( line ): + writeWorld( output ) + else: + output.write( line ) + template.close() + output.close() + +def startOutputFile(): + '''Create output file and write header''' + if outputFileName is not None: + output = open( outputFileName, 'w' ) + else: + output = sys.stdout + output.write( "/* Generated file, do not edit */\n\n" ) + return output + +wrotePreamble = 0 +def writePreamble( output ): + '''Write the CxxTest header (#includes and #defines)''' + global wrotePreamble, headers, longlong + if wrotePreamble: return + output.write( "#ifndef CXXTEST_RUNNING\n" ) + output.write( "#define CXXTEST_RUNNING\n" ) + output.write( "#endif\n" ) + output.write( "\n" ) + if haveStandardLibrary: + output.write( "#define _CXXTEST_HAVE_STD\n" ) + if haveExceptionHandling: + output.write( "#define _CXXTEST_HAVE_EH\n" ) + if abortOnFail: + output.write( "#define _CXXTEST_ABORT_TEST_ON_FAIL\n" ) + if longlong: + output.write( "#define _CXXTEST_LONGLONG %s\n" % longlong ) + if factor: + output.write( "#define _CXXTEST_FACTOR\n" ) + for header in headers: + output.write( "#include %s\n" % header ) + output.write( "#include <cxxtest/TestListener.h>\n" ) + output.write( "#include <cxxtest/TestTracker.h>\n" ) + output.write( "#include <cxxtest/TestRunner.h>\n" ) + output.write( "#include <cxxtest/RealDescriptions.h>\n" ) + if runner: + output.write( "#include <cxxtest/%s.h>\n" % runner ) + if gui: + output.write( "#include <cxxtest/%s.h>\n" % gui ) + output.write( "\n" ) + wrotePreamble = 1 + +def writeMain( output ): + '''Write the main() function for the test runner''' + if gui: + output.write( 'int main( int argc, char *argv[] ) {\n' ) + if noStaticInit: + output.write( ' CxxTest::initialize();\n' ) + output.write( ' return CxxTest::GuiTuiRunner<CxxTest::%s, CxxTest::%s>( argc, argv ).run();\n' % (gui, runner) ) + output.write( '}\n' ) + elif runner: + output.write( 'int main() {\n' ) + if noStaticInit: + output.write( ' CxxTest::initialize();\n' ) + output.write( ' return CxxTest::%s().run();\n' % runner ) + output.write( '}\n' ) + +wroteWorld = 0 +def writeWorld( output ): + '''Write the world definitions''' + global wroteWorld, part + if wroteWorld: return + writePreamble( output ) + writeSuites( output ) + if root or not part: + writeRoot( output ) + if noStaticInit: + writeInitialize( output ) + wroteWorld = 1 + +def writeSuites(output): + '''Write all TestDescriptions and SuiteDescriptions''' + for suite in suites: + writeInclude( output, suite['file'] ) + if isGenerated(suite): + generateSuite( output, suite ) + if isDynamic(suite): + writeSuitePointer( output, suite ) + else: + writeSuiteObject( output, suite ) + writeTestList( output, suite ) + writeSuiteDescription( output, suite ) + writeTestDescriptions( output, suite ) + +def isGenerated(suite): + '''Checks whether a suite class should be created''' + return suite['generated'] + +def isDynamic(suite): + '''Checks whether a suite is dynamic''' + return suite.has_key('create') + +lastIncluded = '' +def writeInclude(output, file): + '''Add #include "file" statement''' + global lastIncluded + if file == lastIncluded: return + output.writelines( [ '#include "', file, '"\n\n' ] ) + lastIncluded = file + +def generateSuite( output, suite ): + '''Write a suite declared with CXXTEST_SUITE()''' + output.write( 'class %s : public CxxTest::TestSuite {\n' % suite['name'] ) + output.write( 'public:\n' ) + for line in suite['lines']: + output.write(line) + output.write( '};\n\n' ) + +def writeSuitePointer( output, suite ): + '''Create static suite pointer object for dynamic suites''' + if noStaticInit: + output.write( 'static %s *%s;\n\n' % (suite['name'], suite['object']) ) + else: + output.write( 'static %s *%s = 0;\n\n' % (suite['name'], suite['object']) ) + +def writeSuiteObject( output, suite ): + '''Create static suite object for non-dynamic suites''' + output.writelines( [ "static ", suite['name'], " ", suite['object'], ";\n\n" ] ) + +def writeTestList( output, suite ): + '''Write the head of the test linked list for a suite''' + if noStaticInit: + output.write( 'static CxxTest::List %s;\n' % suite['tlist'] ) + else: + output.write( 'static CxxTest::List %s = { 0, 0 };\n' % suite['tlist'] ) + +def writeTestDescriptions( output, suite ): + '''Write all test descriptions for a suite''' + for test in suite['tests']: + writeTestDescription( output, suite, test ) + +def writeTestDescription( output, suite, test ): + '''Write test description object''' + output.write( 'static class %s : public CxxTest::RealTestDescription {\n' % test['class'] ) + output.write( 'public:\n' ) + if not noStaticInit: + output.write( ' %s() : CxxTest::RealTestDescription( %s, %s, %s, "%s" ) {}\n' % + (test['class'], suite['tlist'], suite['dobject'], test['line'], test['name']) ) + output.write( ' void runTest() { %s }\n' % runBody( suite, test ) ) + output.write( '} %s;\n\n' % test['object'] ) + +def runBody( suite, test ): + '''Body of TestDescription::run()''' + if isDynamic(suite): return dynamicRun( suite, test ) + else: return staticRun( suite, test ) + +def dynamicRun( suite, test ): + '''Body of TestDescription::run() for test in a dynamic suite''' + return 'if ( ' + suite['object'] + ' ) ' + suite['object'] + '->' + test['name'] + '();' + +def staticRun( suite, test ): + '''Body of TestDescription::run() for test in a non-dynamic suite''' + return suite['object'] + '.' + test['name'] + '();' + +def writeSuiteDescription( output, suite ): + '''Write SuiteDescription object''' + if isDynamic( suite ): + writeDynamicDescription( output, suite ) + else: + writeStaticDescription( output, suite ) + +def writeDynamicDescription( output, suite ): + '''Write SuiteDescription for a dynamic suite''' + output.write( 'CxxTest::DynamicSuiteDescription<%s> %s' % (suite['name'], suite['dobject']) ) + if not noStaticInit: + output.write( '( %s, %s, "%s", %s, %s, %s, %s )' % + (suite['cfile'], suite['line'], suite['name'], suite['tlist'], + suite['object'], suite['create'], suite['destroy']) ) + output.write( ';\n\n' ) + +def writeStaticDescription( output, suite ): + '''Write SuiteDescription for a static suite''' + output.write( 'CxxTest::StaticSuiteDescription %s' % suite['dobject'] ) + if not noStaticInit: + output.write( '( %s, %s, "%s", %s, %s )' % + (suite['cfile'], suite['line'], suite['name'], suite['object'], suite['tlist']) ) + output.write( ';\n\n' ) + +def writeRoot(output): + '''Write static members of CxxTest classes''' + output.write( '#include <cxxtest/Root.cpp>\n' ) + +def writeInitialize(output): + '''Write CxxTest::initialize(), which replaces static initialization''' + output.write( 'namespace CxxTest {\n' ) + output.write( ' void initialize()\n' ) + output.write( ' {\n' ) + for suite in suites: + output.write( ' %s.initialize();\n' % suite['tlist'] ) + if isDynamic(suite): + output.write( ' %s = 0;\n' % suite['object'] ) + output.write( ' %s.initialize( %s, %s, "%s", %s, %s, %s, %s );\n' % + (suite['dobject'], suite['cfile'], suite['line'], suite['name'], + suite['tlist'], suite['object'], suite['create'], suite['destroy']) ) + else: + output.write( ' %s.initialize( %s, %s, "%s", %s, %s );\n' % + (suite['dobject'], suite['cfile'], suite['line'], suite['name'], + suite['object'], suite['tlist']) ) + + for test in suite['tests']: + output.write( ' %s.initialize( %s, %s, %s, "%s" );\n' % + (test['object'], suite['tlist'], suite['dobject'], test['line'], test['name']) ) + + output.write( ' }\n' ) + output.write( '}\n' ) + +main() diff --git a/examples/vodcatchersources.conf b/examples/vodcatchersources.conf new file mode 100644 index 0000000..778d65a --- /dev/null +++ b/examples/vodcatchersources.conf @@ -0,0 +1,21 @@ +# +# Example sources for vdr-vodcatcher +# + +# videocast.srtwist.net +FEED_URL=http://videocast.srtwist.net/ + +# Galacticast +FEED_URL=http://feeds.feedburner.com/galacticast-wmv + +# Probot World of Amazing Action-Figure Adventure! +FEED_URL=http://www.probotproductions.com/vidcasts/ProbotVodcast.xml + +# Adventures of Thomas Brin +FEED_URL=http://podcasts.thomasbrin.com/BrinRSS + +# Quarks & Co +FEED_URL=http://podcast.wdr.de/quarks.xml + +# Computer Club 2 +FEED_URL=http://www.cczwei.de/rss_tvissues.php diff --git a/po/de_DE.po b/po/de_DE.po new file mode 100644 index 0000000..400f9e7 --- /dev/null +++ b/po/de_DE.po @@ -0,0 +1,60 @@ +# German translations for PACKAGE package +# German messages for PACKAGE. +# Copyright (C) 2007 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Tobias <tg@e-tobi.net>, 2007. +# +msgid "" +msgstr "" +"Project-Id-Version: vdr-vodcatche 0.1.0\n" +"Report-Msgid-Bugs-To: <tg@e-tobi.net>\n" +"POT-Creation-Date: 2008-08-06 20:37+0200\n" +"PO-Revision-Date: 2007-11-25 13:50+0100\n" +"Last-Translator: Tobias Grimm <tg@e-tobi.net>\n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-15\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Max. download cache age (days)" +msgstr "Max. Haltezeit Cache (Tage)" + +msgid "Media Player" +msgstr "Media Player" + +msgid "Video Podcasts" +msgstr "Video Podcasts" + +msgid "Starting playback, please wait..." +msgstr "Starte Wiedergabe, bitte warten...." + +msgid "Playback failed!" +msgstr "Wiedergabe fehlgeschlagen!" + +msgid "Downloading...Please wait!" +msgstr "Download in Arbeit... Bitte warten!" + +msgid "No entries!" +msgstr "Keine Einträge!" + +msgid "Play" +msgstr "Abspielen" + +msgid "Record" +msgstr "Aufnehmen" + +msgid "Browse and play video podcasts" +msgstr "Video Podcasts anzeigen und abspielen" + +msgid "Vodcatcher" +msgstr "Vodcatcher" + +msgid "Low" +msgstr "Niedrig" + +msgid "Medium" +msgstr "Mittel" + +msgid "High" +msgstr "Hoch" diff --git a/po/it_IT.po b/po/it_IT.po new file mode 100644 index 0000000..7872d9c --- /dev/null +++ b/po/it_IT.po @@ -0,0 +1,61 @@ +# German translations for PACKAGE package +# German messages for PACKAGE. +# Copyright (C) 2007 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Tobias <tg@e-tobi.net>, 2007. +# +msgid "" +msgstr "" +"Project-Id-Version: vdr-vodcatche 0.1.0\n" +"Report-Msgid-Bugs-To: <tg@e-tobi.net>\n" +"POT-Creation-Date: 2008-08-06 20:37+0200\n" +"PO-Revision-Date: 2008-08-10 01:46+0100\n" +"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-15\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Max. download cache age (days)" +msgstr "Età max. cache download (gg)" + +msgid "Media Player" +msgstr "Lettore multimediale" + +msgid "Video Podcasts" +msgstr "Podcast video" + +msgid "Starting playback, please wait..." +msgstr "Avvio riproduzione, attendere prego..." + +msgid "Playback failed!" +msgstr "Riproduzione fallita!" + +msgid "Downloading...Please wait!" +msgstr "Scaricamento...Attendere prego!" + +msgid "No entries!" +msgstr "Nessun valore!" + +msgid "Play" +msgstr "Riproduci" + +msgid "Record" +msgstr "Registra" + +msgid "Browse and play video podcasts" +msgstr "Sfoglia e riproduci podcast video" + +msgid "Vodcatcher" +msgstr "Vodcatcher" + +msgid "Low" +msgstr "Bassa" + +msgid "Medium" +msgstr "Media" + +msgid "High" +msgstr "Alta" + diff --git a/po/vodcatcher.pot b/po/vodcatcher.pot new file mode 100644 index 0000000..3c40cc1 --- /dev/null +++ b/po/vodcatcher.pot @@ -0,0 +1,59 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: <tg@e-tobi.net>\n" +"POT-Creation-Date: 2008-08-09 22:56+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Max. download cache age (days)" +msgstr "" + +msgid "Media Player" +msgstr "" + +msgid "Video Podcasts" +msgstr "" + +msgid "Starting playback, please wait..." +msgstr "" + +msgid "Playback failed!" +msgstr "" + +msgid "Downloading...Please wait!" +msgstr "" + +msgid "No entries!" +msgstr "" + +msgid "Play" +msgstr "" + +msgid "Record" +msgstr "" + +msgid "Browse and play video podcasts" +msgstr "" + +msgid "Vodcatcher" +msgstr "" + +msgid "Low" +msgstr "" + +msgid "Medium" +msgstr "" + +msgid "High" +msgstr "" diff --git a/sources.mk b/sources.mk new file mode 100644 index 0000000..51d3c22 --- /dev/null +++ b/sources.mk @@ -0,0 +1,59 @@ +SRCS = \ + src/MplayerPlugin.cc \ + src/OsdItemView.cc \ + src/OsdListMenu.cc \ + src/OsdSetupMenu.cc \ + src/PluginCreator.cc \ + src/ServiceLocatorImpl.cc \ + src/SyslogErrorLogger.cc \ + src/VdrCriticalSection.cc \ + src/VdrInterface.cc \ + src/VdrSleeper.cc \ + src/VdrThread.cc \ + src/XineliboutputPlayer.cc \ + +SRCS_TESTABLE = \ + src/FeedRepository.cc \ + src/CurlDownloader.cc \ + src/Download.cc \ + src/DownloadAction.cc \ + src/DownloadQueue.cc \ + src/Feed.cc \ + src/FeedMenuController.cc \ + src/FeedsConfigFile.cc \ + src/FeedUpdaterImpl.cc \ + src/HtmlToText.cc \ + src/Item.cc \ + src/ItemMenuPresenter.cc \ + src/ItemViewPresenter.cc \ + src/LocalFileCache.cc \ + src/VodcatcherPlugin.cc \ + src/Rfc822DateTime.cc \ + src/RssFeedParser.cc \ + src/SdbmHashCalculator.cc \ + src/StreamType.cc \ + src/SynchedDownloadPool.cc \ + src/ThreadsafeDownloadPool.cc \ + tinyxml/tinystr.cpp \ + tinyxml/tinyxml.cpp \ + tinyxml/tinyxmlerror.cpp \ + tinyxml/tinyxmlparser.cpp \ + + +SRCS_TESTONLY = \ + src/DownloadCacheMock.cc \ + src/DownloadPoolMock.cc \ + src/FeedUpdaterMock.cc \ + src/ListMenuMock.cc \ + src/ServiceLocatorStub.cc \ + src/SleeperMock.cc \ + src/StderrMock.cc \ + src/StringMessageMock.cc \ + src/ThreadMock.cc \ + src/vdr-stub/ccontrolstub.cc \ + src/vdr-stub/i18n.cc \ + src/vdr-stub/menuitems.cc \ + src/vdr-stub/osdbase.cc \ + src/vdr-stub/plugin.cc \ + src/vdr-stub/pluginhooks.cc \ + src/vdr-stub/tools.cc \ diff --git a/src/ConfigurationStub.h b/src/ConfigurationStub.h new file mode 100644 index 0000000..578c4c4 --- /dev/null +++ b/src/ConfigurationStub.h @@ -0,0 +1,68 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ConfigurationStub.h 7656 2008-08-09 21:07:45Z svntobi $ + * + */ + +#ifndef CONFIGURATIONSTUB_H_ +#define CONFIGURATIONSTUB_H_ + +#include "IConfiguration.h" + +class ConfigurationStub : public IConfiguration +{ +private: + StreamType _playBackQuality; + +public: + const std::string GetCacheDirName() const + { + return ""; + } + const std::string GetSourcesFileName() const + { + return ""; + } + int GetMaxCacheAge() const + { + return 0; + } + void SetMaxCacheAge(const int age) + { + } + void SetPlayBackQuality(const StreamType quality) + { + _playBackQuality = quality; + } + StreamType GetPlayBackQuality() const + { + return _playBackQuality; + } + + MediaPlayerType GetMediaPlayerType() const + { + return Mplayer; + } + + void SetMediaPlayerType(MediaPlayerType) + { + } +}; + +#endif diff --git a/src/CriticalSection.h b/src/CriticalSection.h new file mode 100644 index 0000000..ee60747 --- /dev/null +++ b/src/CriticalSection.h @@ -0,0 +1,36 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: CriticalSection.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___CRITICALSECTION_H +#define ___CRITICALSECTION_H + +class ICriticalSection +{ +public: + virtual ~ICriticalSection() + { + } + virtual void Enter() = 0; + virtual void Leave() = 0; +}; + +#endif diff --git a/src/CurlDownloader.cc b/src/CurlDownloader.cc new file mode 100644 index 0000000..29a0dfd --- /dev/null +++ b/src/CurlDownloader.cc @@ -0,0 +1,82 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: CurlDownloader.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <sstream> +#include <curl/curl.h> +#include "CurlDownloader.h" +#include "IDownloadCache.h" +#include "Download.h" + +using namespace std; + +CurlDownloader::CurlDownloader(IDownloadCache& downloadCache) : + downloadCache(downloadCache) +{ +} + +bool CurlDownloader::PerformDownload(Download& download) +{ + stringstream stream; + CURL *curl; + CURLcode res; + + curl = curl_easy_init(); + if (curl) + { + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlDownloader::WriteDataToStream); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream); + curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &download); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); +#ifdef DEBUG + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); +#endif + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "vdr-vodcatcher/0.1"); + curl_easy_setopt(curl, CURLOPT_URL, download.GetUrl().c_str()); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + if (CURLE_OK == res) + { + downloadCache.Put(stream, download.GetUrl()); + return true; + } + } + return false; +} + +size_t CurlDownloader::WriteDataToStream(const void *buffer, size_t sizeOfMemblock, size_t numberOfMemblocks, + void *streamPointer) +{ + ostream* stream = (ostream*) streamPointer; + + try + { + int bufferSize = sizeOfMemblock * numberOfMemblocks; + stream->write((const char*) buffer, bufferSize); + return bufferSize; + } + catch(...) + { + } + + return 0; +} diff --git a/src/CurlDownloader.h b/src/CurlDownloader.h new file mode 100644 index 0000000..1a4f0f3 --- /dev/null +++ b/src/CurlDownloader.h @@ -0,0 +1,44 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: CurlDownloader.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___CURLDOWNLOADER_H +#define ___CURLDOWNLOADER_H + +#include "IDownloader.h" + +class IDownloadCache; + +class CurlDownloader : public IDownloader +{ +private: + IDownloadCache& downloadCache; + +public: + CurlDownloader(IDownloadCache& downloadCache); + static size_t WriteDataToStream(const void *buffer, size_t sizeOfMemblock, size_t numberOfMemblocks, + void *streamPointer); + // <IDownloader> + bool PerformDownload(Download& download); + // </IDownloader> +}; + +#endif diff --git a/src/CurlDownloader_test.cc b/src/CurlDownloader_test.cc new file mode 100644 index 0000000..f81c37e --- /dev/null +++ b/src/CurlDownloader_test.cc @@ -0,0 +1,97 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: CurlDownloader_test.cc 7656 2008-08-09 21:07:45Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "CurlDownloader.h" +#include <fstream> +#include <sstream> +#include "StringMessageMock.h" +#include "Download.h" +#include "DownloadCacheMock.h" +#include <stdlib.h> + +using namespace std; + +namespace +{ + +class CurlDownloaderTest : public CxxTest::TestSuite +{ +public: + void tearDown() + { + unlink("test.download"); + } + + void TestWriteData() + { + std::stringstream testStream; + const char* buffer = "MemBlock1MemBlock2MemBlock3"; + + TS_ASSERT_EQUALS(9*2, (int) CurlDownloader::WriteDataToStream( + buffer, 9, 2, &testStream)); + + TS_ASSERT_EQUALS("MemBlock1MemBlock2", testStream.str()); + } + + void TestWriteDataCausingWriteException() + { + std::filebuf streamBuf; + std::ostream testStream(&streamBuf); + testStream.exceptions(ofstream::badbit); + + const char* buffer = "X"; + + TS_ASSERT_EQUALS(0, (int) CurlDownloader::WriteDataToStream( + buffer, 1, 1, &testStream)); + } + + void TestDownload() + { + DownloadCacheMock downloadCache; + CurlDownloader downloader(downloadCache); + Download download("file://test.download"); + + system("echo -n foo >test.download"); + + downloadCache.ExpectMethod("Put", "foo", download.GetUrl()); + + TS_ASSERT(downloader.PerformDownload(download)); + + downloadCache.Verify(); + } + + void TestDownloadFailure() + { + DownloadCacheMock downloadCache; + CurlDownloader downloader(downloadCache); + Download download("file://test.download"); + + unlink("test.download"); + + TS_ASSERT(!downloader.PerformDownload(download)); + + downloadCache.Verify(); + } +}; + +}; diff --git a/src/Download.cc b/src/Download.cc new file mode 100644 index 0000000..68ca273 --- /dev/null +++ b/src/Download.cc @@ -0,0 +1,35 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Download.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "Download.h" + +using namespace std; + +Download::Download(string url) : + url(url) +{ +} + +string Download::GetUrl() +{ + return url; +} diff --git a/src/Download.h b/src/Download.h new file mode 100644 index 0000000..77e4845 --- /dev/null +++ b/src/Download.h @@ -0,0 +1,40 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Download.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___DOWNLOAD_H +#define ___DOWNLOAD_H + +#include <string> +#include "RefPtr.h" + +// TODO Download may be removed and replaced by std::string +class Download +{ +private: + std::string url; + +public: + Download(std::string url); + std::string GetUrl(); +}; + +#endif diff --git a/src/DownloadAction.cc b/src/DownloadAction.cc new file mode 100644 index 0000000..17c882a --- /dev/null +++ b/src/DownloadAction.cc @@ -0,0 +1,48 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadAction.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "DownloadAction.h" + +#include "IDownloadPool.h" +#include "IDownloader.h" +#include "Download.h" +#include "Thread.h" +#include "Sleeper.h" + +using namespace std; + +DownloadAction::DownloadAction(IDownloadPool& downloadPool, IDownloader& downloader) : + downloadPool(downloadPool), downloader(downloader) +{ +} + +bool DownloadAction::Action() +{ + RefPtr<Download> download = downloadPool.GetNextDownload(); + if (download.get()) + { + downloader.PerformDownload(*download); + downloadPool.RemoveDownload(download); + } + + return true; +} diff --git a/src/DownloadAction.h b/src/DownloadAction.h new file mode 100644 index 0000000..68d8f6e --- /dev/null +++ b/src/DownloadAction.h @@ -0,0 +1,44 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadAction.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___DOWNLOADACTION_H +#define ___DOWNLOADACTION_H + +#include "ThreadAction.h" + +class IDownloadPool; +class IDownloader; + +class DownloadAction : public IThreadAction +{ +private: + IDownloadPool& downloadPool; + IDownloader& downloader; + +public: + DownloadAction(IDownloadPool& downloadPool, IDownloader& downloader); + // <IThreadAction> + bool Action(); + // </IThreadAction> +}; + +#endif diff --git a/src/DownloadAction_test.cc b/src/DownloadAction_test.cc new file mode 100644 index 0000000..daab6f5 --- /dev/null +++ b/src/DownloadAction_test.cc @@ -0,0 +1,98 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadAction_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "DownloadAction.h" +#include "StringMessageMock.h" +#include "DownloadQueue.h" +#include "IDownloader.h" +#include "Download.h" + +using namespace std; + +namespace +{ + +class MockDownloader : public StringMessageMock, public IDownloader +{ +public: + bool PerformDownload(Download& download) + { + AddMethod("PerformDownload", download.GetUrl()); + return true; + } +}; + +class DownloadActionTest : public CxxTest::TestSuite +{ +private: + MockDownloader downloader; + DownloadQueue* downloadQueue; + DownloadAction* downloadAction; + +public: + void setUp() + { + downloader.ResetMock(); + downloadQueue = new DownloadQueue(); + downloadAction = new DownloadAction(*downloadQueue, downloader); + } + + void tearDown() + { + delete downloadAction; + delete downloadQueue; + } + + void TestEmptyQueue() + { + TS_ASSERT(downloadAction->Action()); + downloader.Verify(); + } + + void TestOneItemInQueue() + { + RefPtr<Download> download = downloadQueue->AddDownload("url"); + + downloader.ExpectMethod("PerformDownload", "url"); + TS_ASSERT(downloadAction->Action()); + + downloader.Verify(); + TS_ASSERT(NULL == downloadQueue->GetNextDownload().get()); + } + + void TestTwoItemsInQueue() + { + RefPtr<Download> download1 = downloadQueue->AddDownload("url1"); + RefPtr<Download> download2 = downloadQueue->AddDownload("url2"); + + downloader.ExpectMethod("PerformDownload", "url1"); + TS_ASSERT(downloadAction->Action()); + downloader.Verify(); + + downloader.ExpectMethod("PerformDownload", "url2"); + TS_ASSERT(downloadAction->Action()); + downloader.Verify(); + } +}; + +}; diff --git a/src/DownloadCacheMock.cc b/src/DownloadCacheMock.cc new file mode 100644 index 0000000..dc092be --- /dev/null +++ b/src/DownloadCacheMock.cc @@ -0,0 +1,55 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadCacheMock.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "DownloadCacheMock.h" + +using namespace std; + +DownloadCacheMock::DownloadCacheMock() : + filesAreCached(false), fileAge(0) +{ +} + +RefPtr<istream> DownloadCacheMock::CreateStreamByUrl(const string& url) const +{ + return RefPtr<istream>(streamReturnedByCache); +} + +void DownloadCacheMock::Put(istream& document, const string& url) +{ + AddMethod("Put", MakeString(document.rdbuf()), url); +} + +long DownloadCacheMock::GetAgeInMinutes(const string& url) const +{ + return fileAge; +} + +bool DownloadCacheMock::IsCached(const string& url) const +{ + return filesAreCached; +} + +void DownloadCacheMock::CleanUp(const long maxAge) +{ + AddMethod("CleanUp", MakeString(maxAge)); +} diff --git a/src/DownloadCacheMock.h b/src/DownloadCacheMock.h new file mode 100644 index 0000000..2636ba2 --- /dev/null +++ b/src/DownloadCacheMock.h @@ -0,0 +1,47 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadCacheMock.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___DOWNLOADCACHEMOCK_H +#define ___DOWNLOADCACHEMOCK_H + +#include "StringMessageMock.h" +#include "IDownloadCache.h" + +class DownloadCacheMock : public StringMessageMock, public IDownloadCache +{ +public: + bool filesAreCached; + int fileAge; + std::istream* streamReturnedByCache; + +public: + DownloadCacheMock(); + // <IDownloadCache> + void Put(std::istream& document, const std::string& url); + RefPtr<std::istream> CreateStreamByUrl(const std::string& url) const; + long GetAgeInMinutes(const std::string& url) const; + bool IsCached(const std::string& url) const; + void CleanUp(const long maxAge); + // </IDownloadCache> +}; + +#endif // ___DOWNLOADCACHEMOCK_H diff --git a/src/DownloadObserver.h b/src/DownloadObserver.h new file mode 100644 index 0000000..9024f74 --- /dev/null +++ b/src/DownloadObserver.h @@ -0,0 +1,35 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadObserver.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___DOWNLOADOBSERVER_H +#define ___DOWNLOADOBSERVER_H + +class IDownloadObserver +{ +public: + virtual ~IDownloadObserver() + { + } + virtual void DownloadFinished() = 0; +}; + +#endif diff --git a/src/DownloadPoolMock.cc b/src/DownloadPoolMock.cc new file mode 100644 index 0000000..d8b4f06 --- /dev/null +++ b/src/DownloadPoolMock.cc @@ -0,0 +1,67 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadPoolMock.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "DownloadPoolMock.h" +#include "Download.h" + +using namespace std; + +DownloadPoolMock::DownloadPoolMock() : + StringMessageMock() +{ +} + +DownloadPoolMock::DownloadPoolMock(StringMessageMock& parent) : + StringMessageMock(parent) +{ +} + +RefPtr<Download> DownloadPoolMock::AddDownload(const std::string url) +{ + AddMethod("AddDownload", url); + lastAddedDownload = RefPtr<Download>(new Download(url)); + return lastAddedDownload; +} + +void DownloadPoolMock::RemoveDownload(RefPtr<Download> download) +{ + AddMethod("RemoveDownload", download->GetUrl()); +} + +RefPtr<Download> DownloadPoolMock::GetNextDownload() +{ + AddMethod("GetNextDownload"); + if (!returnedDownload.get()) + { + return RefPtr<Download>(); + } + else + { + return returnedDownload; + } +} + +RefPtr<Download> DownloadPoolMock::GetDownloadByUrl(string url) +{ + AddMethod("GetDownloadByUrl", url); + return returnedDownload; +} diff --git a/src/DownloadPoolMock.h b/src/DownloadPoolMock.h new file mode 100644 index 0000000..cd5db60 --- /dev/null +++ b/src/DownloadPoolMock.h @@ -0,0 +1,48 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadPoolMock.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___DOWNLOADPOOLMOCK_H +#define ___DOWNLOADPOOLMOCK_H + +#include "StringMessageMock.h" +#include "IDownloadPool.h" + +class DownloadPoolMock : public StringMessageMock, public IDownloadPool +{ +public: + RefPtr<Download> returnedDownload; + RefPtr<Download> lastAddedDownload; + +public: + DownloadPoolMock(); + DownloadPoolMock(StringMessageMock& parent); + + // <IDownloadPool> + //void AddDownload(RefPtr<Download> download); + RefPtr<Download> AddDownload(const std::string url); + void RemoveDownload(RefPtr<Download> download); + RefPtr<Download> GetNextDownload(); + RefPtr<Download> GetDownloadByUrl(std::string url); + // </IDownloadPool> +}; + +#endif // ___DOWNLOADPOOLMOCK_H diff --git a/src/DownloadQueue.cc b/src/DownloadQueue.cc new file mode 100644 index 0000000..0718806 --- /dev/null +++ b/src/DownloadQueue.cc @@ -0,0 +1,91 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadQueue.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "DownloadQueue.h" +#include "Download.h" + +using namespace std; + +RefPtr<Download> DownloadQueue::GetNextDownload() +{ + if (downloads.empty()) + { + return RefPtr<Download>(); + } + else + { + return downloads.back(); + } +} + +RefPtr<Download> DownloadQueue::GetDownloadByUrl(string url) +{ + DownloadDeque::iterator i = LocateDownload(url); + + if (i != downloads.end()) + { + return *LocateDownload(url); + } + else + { + return RefPtr<Download>(); + } +} + +void DownloadQueue::RemoveDownload(RefPtr<Download> download) +{ + DownloadDeque::iterator i = LocateDownload(download->GetUrl()); + + if (i != downloads.end()) + { + downloads.erase(i); + } +} + +DownloadQueue::DownloadDeque::iterator DownloadQueue::LocateDownload(string url) +{ + DownloadDeque::iterator i; + + for (i = downloads.begin(); i != downloads.end(); i++) + { + if ((*i)->GetUrl() == url) + { + break; + } + } + + return i; +} + +RefPtr<Download> DownloadQueue::AddDownload(const string url) +{ + DownloadDeque::iterator i = LocateDownload(url); + + if (i == downloads.end()) + { + RefPtr<Download> download(new Download(url)); + downloads.push_front(download); + return download; + } + + return *i; +} diff --git a/src/DownloadQueue.h b/src/DownloadQueue.h new file mode 100644 index 0000000..8c6b2cc --- /dev/null +++ b/src/DownloadQueue.h @@ -0,0 +1,48 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadQueue.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___DOWNLOADQUEUE_H +#define ___DOWNLOADQUEUE_H + +#include <deque> +#include "IDownloadPool.h" + +class DownloadQueue : public IDownloadPool +{ + typedef std::deque< RefPtr<Download> > DownloadDeque; + +private: + DownloadDeque downloads; + +private: + DownloadDeque::iterator LocateDownload(std:: string url); + +public: + // <IDownloadPool> + RefPtr<Download> AddDownload(const std::string url); + virtual void RemoveDownload(RefPtr<Download> download); + RefPtr<Download> GetNextDownload(); + RefPtr<Download> GetDownloadByUrl(std::string url); + // </IDownloadPool> +}; + +#endif // ___DOWNLOADQUEUE_H diff --git a/src/DownloadQueue_test.cc b/src/DownloadQueue_test.cc new file mode 100644 index 0000000..fc6b33a --- /dev/null +++ b/src/DownloadQueue_test.cc @@ -0,0 +1,105 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: DownloadQueue_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "DownloadQueue.h" +#include "Download.h" + +namespace +{ + +class DownloadQueueTest : public CxxTest::TestSuite +{ +private: + DownloadQueue* queue; + +public: + void setUp() + { + queue = new DownloadQueue(); + } + + void tearDown() + { + delete queue; + } + + void TestEmptyQueue() + { + TS_ASSERT(NULL == queue->GetNextDownload().get()); + } + + void TestAddDownload() + { + RefPtr<Download> download = queue->AddDownload("url"); + TS_ASSERT(NULL != queue->GetNextDownload().get()); + TS_ASSERT_EQUALS("url", queue->GetNextDownload()->GetUrl()); + } + + void TestRemoveDownloadNotInQueue() + { + RefPtr<Download> download1 = queue->AddDownload("url1"); + RefPtr<Download> download2 = queue->AddDownload("url2"); + + TS_ASSERT(NULL != queue->GetNextDownload().get()); + + queue->RemoveDownload(download2); + + TS_ASSERT(NULL != queue->GetNextDownload().get()); + } + + void TestRemoveDownload() + { + RefPtr<Download> download1 = queue->AddDownload("url1"); + RefPtr<Download> download2 = queue->AddDownload("url2"); + + queue->RemoveDownload(download1); + TS_ASSERT_EQUALS("url2", queue->GetNextDownload()->GetUrl()); + + queue->RemoveDownload(download2); + TS_ASSERT(NULL == queue->GetNextDownload().get()); + } + + void TestDontAddDuplicates() + { + RefPtr<Download> download1 = queue->AddDownload("url"); + RefPtr<Download> download2 = queue->AddDownload("url"); + + queue->RemoveDownload(download1); + + TS_ASSERT(NULL == queue->GetNextDownload().get()); + } + + void TestFifo() + { + RefPtr<Download> download1 = queue->AddDownload("url1"); + RefPtr<Download> download2 = queue->AddDownload("url2"); + + TS_ASSERT_EQUALS("url1", queue->GetNextDownload()->GetUrl()); + + queue->RemoveDownload(download1); + + TS_ASSERT_EQUALS("url2", queue->GetNextDownload()->GetUrl()); + } +}; + +}; diff --git a/src/Download_test.cc b/src/Download_test.cc new file mode 100644 index 0000000..cfe087e --- /dev/null +++ b/src/Download_test.cc @@ -0,0 +1,41 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Download_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "Download.h" +#include "StringMessageMock.h" + +namespace +{ + +class DownloadTest : public CxxTest::TestSuite +{ +public: + void TestInitialization() + { + Download download("foo"); + TS_ASSERT_EQUALS("foo", download.GetUrl()); + } +}; + +} +; diff --git a/src/Feed.cc b/src/Feed.cc new file mode 100644 index 0000000..b284d65 --- /dev/null +++ b/src/Feed.cc @@ -0,0 +1,66 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Feed.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "Feed.h" + +using namespace std; + +Feed::Feed(const string& url) : + url(url), _timeToLive(720) +{ + title = "unknown"; +} + +const string Feed::GetTitle() const +{ + return title; +} + +void Feed::SetTitle(const string& title) +{ + (*this).title = title; +} + +const string Feed::GetUrl() const +{ + return url; +} + +const ItemList Feed::GetItems() const +{ + return items; +} + +void Feed::SetTimeToLive(const int timeToLive) +{ + _timeToLive = timeToLive; +} + +int Feed::GetTimeToLive() const +{ + return _timeToLive; +} + +void Feed::AddItem(const Item& item) +{ + items.push_back(item); +} diff --git a/src/Feed.h b/src/Feed.h new file mode 100644 index 0000000..8da43af --- /dev/null +++ b/src/Feed.h @@ -0,0 +1,51 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Feed.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___FEED_H +#define ___FEED_H + +#include <string> +#include <vector> +#include "Item.h" + +class Feed +{ +private: + std::string url; + std::string title; + ItemList items; + int _timeToLive; + +public: + Feed(const std::string& url); + const std::string GetTitle() const; + void SetTitle(const std::string& title); + const std::string GetUrl() const; + void AddItem(const Item& item); + const ItemList GetItems() const; + void SetTimeToLive(const int _timeToLive); + int GetTimeToLive() const; +}; + +typedef std::vector<Feed> FeedList; + +#endif // ___FEED_H diff --git a/src/FeedMenuController.cc b/src/FeedMenuController.cc new file mode 100644 index 0000000..8511686 --- /dev/null +++ b/src/FeedMenuController.cc @@ -0,0 +1,72 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedMenuController.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "FeedMenuController.h" +#include "IServiceLocator.h" +#include "IListMenu.h" +#include "IMenuFactory.h" +#include "IFeedRepository.h" + +FeedMenuController::FeedMenuController(IMenuFactory& menuFactory, const IFeedRepository& feedRepository) : + _menuFactory(menuFactory), _feedRepository(feedRepository) +{ +} + +void FeedMenuController::Initialize(IListMenu* listMenu) +{ + _menu = listMenu; + + _menu->SetTitle(tr("Video Podcasts")); + + _feeds = _feedRepository.GetRootFeeds(); + + for (FeedList::iterator i = _feeds.begin(); i != _feeds.end(); i++) + { + _menu->AddItem(i->GetTitle()); + } +} + +const eOSState FeedMenuController::ProcessKey(const int selectedFeed, const eKeys key, const eOSState state) +{ + if (!state == osUnknown) + { + return state; + } + + switch (key) + { + case kOk: + { + if ((selectedFeed >= 0) && ((unsigned int) selectedFeed < _feeds.size())) + { + cOsdMenu* itemMenu = _menuFactory.CreateItemMenu(_feeds[selectedFeed]); + _menu->ShowSubMenu(itemMenu); + } + return osContinue; + } + + default: + { + return state; + } + } +} diff --git a/src/FeedMenuController.h b/src/FeedMenuController.h new file mode 100644 index 0000000..e311452 --- /dev/null +++ b/src/FeedMenuController.h @@ -0,0 +1,51 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedMenuController.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___FEEDMENUPRESENTER_H +#define ___FEEDMENUPRESENTER_H + +#include <vdr/osdbase.h> +#include "Feed.h" +#include "IListMenuPresenter.h" +#include "Download.h" + +class IDownloadPool; +class IMenuFactory; +class IFeedRepository; + +class FeedMenuController : public IListMenuPresenter +{ +private: + IMenuFactory& _menuFactory; + const IFeedRepository& _feedRepository; + FeedList _feeds; + IListMenu* _menu; + +public: + FeedMenuController(IMenuFactory& menuFactory, const IFeedRepository& feedRepository); + // <IListMenuPresenter> + void Initialize(IListMenu* listMenu); + const eOSState ProcessKey(const int selectedFeed, const eKeys Key, const eOSState state); + // </IListMenuPresenter> +}; + +#endif // ___FEEDMENUPRESENTER_H diff --git a/src/FeedMenuController_test.cc b/src/FeedMenuController_test.cc new file mode 100644 index 0000000..9013a78 --- /dev/null +++ b/src/FeedMenuController_test.cc @@ -0,0 +1,145 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedMenuController_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <vector> +#include <sstream> +#include <cxxtest/TestSuite.h> +#include "FeedMenuController.h" +#include "ServiceLocatorStub.h" +#include "DownloadPoolMock.h" +#include "IListMenu.h" +#include "ListMenuMock.h" +#include "IMenuFactory.h" +#include "IFeedRepository.h" + +using namespace std; + +namespace +{ + +class FeedRepositoryStub: public IFeedRepository +{ +public: + FeedList Feeds; + +public: + // <IFeedRepository> + Feed GetFeed(std::string url) + { + for (FeedList::iterator i = Feeds.begin(); i < Feeds.end(); i++) + { + if ((*i).GetUrl() == url) + { + return *i; + } + } + return Feed(url); + } + + const FeedList GetRootFeeds() const + { + return Feeds; + } + // </IFeedRepository> +}; + +class MenuFactoryStub: public IMenuFactory +{ +public: + RefPtr<cOsdMenu> Menu; + +public: + cOsdMenu* CreateItemMenu(const Feed& feed) + { + return Menu.get(); + } +}; + +class A_FeedMenuController: public CxxTest::TestSuite +{ +private: + RefPtr<FeedMenuController> _feedMenuController; + RefPtr<MenuFactoryStub> _menuFactory; + RefPtr<FeedRepositoryStub> _feedRepository; + +private: + void setUp() + { + _feedRepository = new FeedRepositoryStub(); + _menuFactory = new MenuFactoryStub(); + _feedMenuController = new FeedMenuController(*_menuFactory, *_feedRepository); + } + +public: + void Should_set_the_menu_title_on_initialization() + { + ListMenuMock menu; + menu.ExpectMethod("SetTitle", "i18n:Video Podcasts"); + _feedMenuController->Initialize(&menu); + VERIFY_EXPECTATIONS(menu); + } + + void Should_add_the_feed_titles_to_the_menu() + { + Feed feed1("feed1Url"); feed1.SetTitle("feed1"); + Feed feed2("feed2Url"); feed2.SetTitle("feed2"); + _feedRepository->Feeds.push_back(feed1); + _feedRepository->Feeds.push_back(feed2); + ListMenuMock menu; + + menu.ExpectMethod("AddItem", "feed1"); + menu.ExpectMethod("AddItem", "feed2"); + + _feedMenuController->Initialize(&menu); + + VERIFY_EXPECTATIONS(menu); + } + + void Should_do_nothing_when_pressing_ok_while_no_feeds_are_present() + { + _menuFactory->Menu = new cOsdMenu(""); + ListMenuMock menu; + _feedMenuController->Initialize(&menu); + menu.ResetMock(); + TS_ASSERT_EQUALS(osContinue, _feedMenuController->ProcessKey(0, kOk, osUnknown)); + VERIFY(menu); + } + + void Should_open_a_sub_menu_when_selecting_a_feed_with_OK() + { + Feed feed1("feed1Url"); feed1.SetTitle("feed1"); + Feed feed2("feed2Url"); feed2.SetTitle("feed2"); + _feedRepository->Feeds.push_back(feed1); + _feedRepository->Feeds.push_back(feed2); + _menuFactory->Menu = new cOsdMenu(""); + ListMenuMock menu; + + menu.ExpectMethod("ShowSubMenu", menu.MakeString(_menuFactory->Menu.get())); + + _feedMenuController->Initialize(&menu); + TS_ASSERT_EQUALS(osContinue, _feedMenuController->ProcessKey(1, kOk, osUnknown)); + + VERIFY_EXPECTATIONS(menu); + } +}; + +}; diff --git a/src/FeedRepository.cc b/src/FeedRepository.cc new file mode 100644 index 0000000..76ba0b4 --- /dev/null +++ b/src/FeedRepository.cc @@ -0,0 +1,79 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedRepository.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "FeedRepository.h" + +#include "IFeedParser.h" +#include "IDownloader.h" +#include "IDownloadCache.h" +#include "Download.h" +#include "IFeedSources.h" +#include "IDownloadPool.h" + +using namespace std; + +FeedRepository::FeedRepository(IDownloadCache& downloadCache, IDownloadPool& downloadPool, IDownloader& downloader, + IFeedParser& feedParser, IFeedSources& feedSources) : + _downloadCache(downloadCache), _downloadPool(downloadPool), _downloader(downloader), _feedParser(feedParser), + _feedSources(feedSources) +{ +} + +Feed FeedRepository::GetFeed(string url) +{ + Feed feed(url); + + if (_downloadCache.IsCached(url)) + { + _feedParser.Parse(feed); + } + + if (!_downloadCache.IsCached(url) || _downloadCache.GetAgeInMinutes(url) >= feed.GetTimeToLive()) + { + RefPtr<Download> download(new Download(url)); + _downloader.PerformDownload(*download); + _feedParser.Parse(feed); + } + + return feed; +} + +const FeedList FeedRepository::GetRootFeeds() const +{ + FeedList feeds; + vector<string> urls = _feedSources.GetFeedUrls(); + + for (vector<string>::iterator i = urls.begin(); i != urls.end(); i++) + { + Feed feed(*i); + _feedParser.Parse(feed); + + if (!_downloadCache.IsCached(*i) || _downloadCache.GetAgeInMinutes(*i) >= feed.GetTimeToLive()) + { + _downloadPool.AddDownload(*i); + } + + feeds.push_back(feed); + } + + return feeds; +} diff --git a/src/FeedRepository.h b/src/FeedRepository.h new file mode 100644 index 0000000..340b700 --- /dev/null +++ b/src/FeedRepository.h @@ -0,0 +1,54 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedRepository.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___FEEDREPOSITORY_H_ +#define ___FEEDREPOSITORY_H_ + +#include "IFeedRepository.h" +#include "RefPtr.h" + +class IDownloadCache; +class IDownloader; +class IFeedParser; +class IFeedSources; +class IDownloadPool; + +class FeedRepository : public IFeedRepository +{ +private: + IDownloadCache& _downloadCache; + IDownloadPool& _downloadPool; + IDownloader& _downloader; + IFeedParser& _feedParser; + IFeedSources& _feedSources; + +public: + FeedRepository(IDownloadCache& downloadCache, IDownloadPool& downloadPool, IDownloader& downloader, + IFeedParser& feedParser, IFeedSources& feedSources); + //<IFeedRepository> + Feed GetFeed(std::string url); + const FeedList GetRootFeeds() const; + //</IFeedRepository> + +}; + +#endif diff --git a/src/FeedRepository_test.cc b/src/FeedRepository_test.cc new file mode 100644 index 0000000..f7086e0 --- /dev/null +++ b/src/FeedRepository_test.cc @@ -0,0 +1,213 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedRepository_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> + +#include "RefPtr.h" +#include "FeedRepository.h" +#include "DownloadCacheMock.h" +#include "DownloadPoolMock.h" +#include "IFeedSources.h" +#include "IDownloader.h" +#include "IFeedParser.h" +#include "Download.h" + +using namespace std; + +namespace +{ + +class FeedSourcesStub : public IFeedSources +{ + void GetFeeds(FeedList& feeds) const + { + } + + const vector<string> GetFeedUrls() const + { + vector<string> urls; + urls.push_back("feedUrl1"); + urls.push_back("feedUrl2"); + return urls; + } +}; + +class FeedParserStub : public IFeedParser +{ + bool Parse(Feed& feed) const + { + feed.SetTitle("Parsed"); + feed.SetTimeToLive(60); + return true; + } +}; + +class DownloaderMock : public IDownloader, public StringMessageMock +{ +public: + bool PerformDownload(Download& download) + { + AddMethod("PerformDownload", download.GetUrl()); + return true; + } +}; + +class A_FeedRepository_with_an_empty_cache : public CxxTest::TestSuite +{ +private: + RefPtr<FeedRepository> _feedRepository; + RefPtr<DownloadCacheMock> _downloadCache; + RefPtr<DownloaderMock> _downloader; + RefPtr<DownloadPoolMock> _downloadPool; + RefPtr<FeedParserStub> _feedParser; + RefPtr<IFeedSources> _feedSources; + +public: + void setUp() + { + _downloadCache = new DownloadCacheMock(); + _downloadCache->filesAreCached = false; + _downloadPool = new DownloadPoolMock(); + _feedSources = new FeedSourcesStub(); + _feedParser = new FeedParserStub(); + _downloader = new DownloaderMock(); + + _feedRepository = new FeedRepository(*_downloadCache, *_downloadPool, *_downloader, *_feedParser, *_feedSources); + } + + void Should_add_the_root_feeds_to_the_download_queue() + { + _downloadPool->ExpectMethod("AddDownload", "feedUrl1"); + _downloadPool->ExpectMethod("AddDownload", "feedUrl2"); + + _feedRepository->GetRootFeeds(); + + VERIFY_EXPECTATIONS(*_downloadPool); + } + + void Should_return_the_root_feeds() + { + FeedList feeds = _feedRepository->GetRootFeeds(); + TS_ASSERT_EQUALS((unsigned int) 2, feeds.size()); + TS_ASSERT_EQUALS("feedUrl1", feeds[0].GetUrl()); + TS_ASSERT_EQUALS("feedUrl2", feeds[1].GetUrl()); + } + + void Should_immediately_download_and_parse_a_feed_requested_by_url() + { + _downloader->ExpectMethod("PerformDownload", "url"); + + Feed feed = _feedRepository->GetFeed("url"); + + VERIFY_EXPECTATIONS(*_downloader); + TS_ASSERT_EQUALS("Parsed", feed.GetTitle()); + } +}; + +class A_FeedRepository_with_uptodate_feeds_in_the_cache : public CxxTest::TestSuite +{ +private: + RefPtr<FeedRepository> _feedRepository; + RefPtr<DownloadCacheMock> _downloadCache; + RefPtr<DownloaderMock> _downloader; + RefPtr<DownloadPoolMock> _downloadPool; + RefPtr<FeedParserStub> _feedParser; + RefPtr<IFeedSources> _feedSources; + +public: + void setUp() + { + _downloadCache = new DownloadCacheMock(); + _downloadCache->filesAreCached = true; + _downloadCache->fileAge = 10; + _downloadPool = new DownloadPoolMock(); + _feedSources = new FeedSourcesStub(); + _feedParser = new FeedParserStub(); + _downloader = new DownloaderMock(); + + _feedRepository = new FeedRepository(*_downloadCache, *_downloadPool, *_downloader, *_feedParser, *_feedSources); + } + + void Should_not_download_but_only_parse_the_root_feeds() + { + FeedList feeds = _feedRepository->GetRootFeeds(); + + VERIFY(*_downloadPool); + VERIFY(*_downloader); + TS_ASSERT_EQUALS("Parsed", feeds[0].GetTitle()); + TS_ASSERT_EQUALS("Parsed", feeds[1].GetTitle()); + } + + void Should_not_download_but_only_parse_the_feed_requested_by_url() + { + Feed feed = _feedRepository->GetFeed("feedUrl"); + + VERIFY(*_downloadPool); + VERIFY(*_downloader); + TS_ASSERT_EQUALS("Parsed", feed.GetTitle()); + } +}; + +class A_FeedRepository_with_expired_feeds_in_the_cache : public CxxTest::TestSuite +{ +private: + RefPtr<FeedRepository> _feedRepository; + RefPtr<DownloadCacheMock> _downloadCache; + RefPtr<DownloaderMock> _downloader; + RefPtr<DownloadPoolMock> _downloadPool; + RefPtr<FeedParserStub> _feedParser; + RefPtr<IFeedSources> _feedSources; + +public: + void setUp() + { + _downloadCache = new DownloadCacheMock(); + _downloadCache->filesAreCached = true; + _downloadCache->fileAge = 100; + _downloadPool = new DownloadPoolMock(); + _feedSources = new FeedSourcesStub(); + _feedParser = new FeedParserStub(); + _downloader = new DownloaderMock(); + + _feedRepository = new FeedRepository(*_downloadCache, *_downloadPool, *_downloader, *_feedParser, *_feedSources); + } + + void Should_add_the_root_feeds_to_the_download_queue() + { + _downloadPool->ExpectMethod("AddDownload", "feedUrl1"); + _downloadPool->ExpectMethod("AddDownload", "feedUrl2"); + + _feedRepository->GetRootFeeds(); + + VERIFY_EXPECTATIONS(*_downloadPool); + } + + void Should_return_the_parsed_root_feeds() + { + FeedList feeds = _feedRepository->GetRootFeeds(); + TS_ASSERT_EQUALS((unsigned int) 2, feeds.size()); + TS_ASSERT_EQUALS("Parsed", feeds[0].GetTitle()); + TS_ASSERT_EQUALS("Parsed", feeds[1].GetTitle()); + } +}; + +}; diff --git a/src/FeedUpdaterImpl.cc b/src/FeedUpdaterImpl.cc new file mode 100644 index 0000000..bd1c142 --- /dev/null +++ b/src/FeedUpdaterImpl.cc @@ -0,0 +1,50 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedUpdaterImpl.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "FeedUpdaterImpl.h" +#include "IFeedSources.h" +#include "IDownloadPool.h" +#include "IDownloadCache.h" +#include "Download.h" + +using namespace std; + +FeedUpdaterImpl::FeedUpdaterImpl(IFeedSources& feedSources, IDownloadPool& downloadPool, IDownloadCache& downloadCache) : + feedSources(feedSources), downloadPool(downloadPool), downloadCache(downloadCache) +{ +} + +void FeedUpdaterImpl::Update() +{ + FeedList feeds; + feedSources.GetFeeds(feeds); + + for (FeedList::iterator i = feeds.begin(); i != feeds.end(); i++) + { + string url = (*i).GetUrl(); + + if (!downloadCache.IsCached(url) || downloadCache.GetAgeInMinutes(url) >= maxAge) + { + RefPtr<Download> download = downloadPool.AddDownload(i->GetUrl()); + } + } +} diff --git a/src/FeedUpdaterImpl.h b/src/FeedUpdaterImpl.h new file mode 100644 index 0000000..b00ea2a --- /dev/null +++ b/src/FeedUpdaterImpl.h @@ -0,0 +1,48 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedUpdaterImpl.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___FEEDUPDATERIMPL_H +#define ___FEEDUPDATERIMPL_H + +#include "RefPtr.h" +#include "IFeedUpdater.h" + +class IFeedSources; +class IDownloadPool; +class IDownloadCache; + +class FeedUpdaterImpl : public IFeedUpdater +{ +private: + static const int maxAge = 2; + IFeedSources& feedSources; + IDownloadPool& downloadPool; + IDownloadCache& downloadCache; + +public: + FeedUpdaterImpl(IFeedSources& feedSources, IDownloadPool& downloadPool, IDownloadCache& downloadCache); + // <IFeedUpdater> + void Update(); + // </IFeedUpdater> +}; + +#endif // ___FEEDUPDATERIMPL_H diff --git a/src/FeedUpdaterImpl_test.cc b/src/FeedUpdaterImpl_test.cc new file mode 100644 index 0000000..c3f8ff3 --- /dev/null +++ b/src/FeedUpdaterImpl_test.cc @@ -0,0 +1,110 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedUpdaterImpl_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "FeedUpdaterImpl.h" +#include "DownloadPoolMock.h" +#include "IFeedSources.h" +#include "Download.h" +#include "DownloadCacheMock.h" + +using namespace std; + +namespace +{ + +class FeedSourcesStub : public IFeedSources +{ +public: + // <IFeedSources> + void GetFeeds(FeedList& feeds) const + { + Feed feed1("foo"); + Feed feed2("bar"); + feeds.push_back(feed1); + feeds.push_back(feed2); + } + + const vector<string> GetFeedUrls() const + { + return vector<string>(); + } + // </IFeedSources> +}; + +class FeedUpdaterImplTest : public CxxTest::TestSuite +{ +private: + FeedSourcesStub feedSources; + DownloadPoolMock downloadPool; + DownloadCacheMock downloadCache; + FeedUpdaterImpl* feedUpdater; + +public: + void setUp() + { + downloadPool.ResetMock(); + feedUpdater = new FeedUpdaterImpl(feedSources, downloadPool, + downloadCache); + } + + void tearDown() + { + delete feedUpdater; + } + + void TestDownloadNewFeeds() + { + downloadCache.filesAreCached = false; + downloadCache.fileAge = 99; + downloadPool.ExpectMethod("AddDownload", "foo"); + downloadPool.ExpectMethod("AddDownload", "bar"); + + feedUpdater->Update(); + + downloadPool.Verify(); + } + + void TestDontDownloadUptodateFeeds() + { + downloadCache.filesAreCached = true; + downloadCache.fileAge = 1; + + feedUpdater->Update(); + + downloadPool.Verify(); + } + + void TestRefreshOldFeeds() + { + downloadCache.filesAreCached = true; + downloadCache.fileAge = 3; + downloadPool.ExpectMethod("AddDownload", "foo"); + downloadPool.ExpectMethod("AddDownload", "bar"); + + feedUpdater->Update(); + + downloadPool.Verify(); + } +}; + +}; diff --git a/src/FeedUpdaterMock.cc b/src/FeedUpdaterMock.cc new file mode 100644 index 0000000..09094f9 --- /dev/null +++ b/src/FeedUpdaterMock.cc @@ -0,0 +1,28 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedUpdaterMock.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "FeedUpdaterMock.h" + +void FeedUpdaterMock::Update() +{ + AddMethod("Update"); +} diff --git a/src/FeedUpdaterMock.h b/src/FeedUpdaterMock.h new file mode 100644 index 0000000..d503603 --- /dev/null +++ b/src/FeedUpdaterMock.h @@ -0,0 +1,37 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedUpdaterMock.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___FEEDUPDATERMOCK +#define ___FEEDUPDATERMOCK + +#include "IFeedUpdater.h" +#include "StringMessageMock.h" + +class FeedUpdaterMock : public StringMessageMock, public IFeedUpdater +{ +public: + // <IFeedUpdater> + void Update(); + // </IFeedUpdater> +}; + +#endif diff --git a/src/Feed_test.cc b/src/Feed_test.cc new file mode 100644 index 0000000..1bb34be --- /dev/null +++ b/src/Feed_test.cc @@ -0,0 +1,65 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Feed_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "Feed.h" + +class A_Feed : public CxxTest::TestSuite +{ +public: + void Should_have_an_url() + { + Feed feed("url"); + TS_ASSERT_EQUALS("url", feed.GetUrl()); + } + + void Should_have_unknown_as_default_title() + { + Feed feed("foo"); + TS_ASSERT_EQUALS("unknown", feed.GetTitle()); + } + + void Should_have_an_assignable_title() + { + Feed feed(""); + feed.SetTitle("bar"); + TS_ASSERT_EQUALS("bar", feed.GetTitle()); + } + + void Should_allow_to_add_items() + { + Feed feed(""); + Item item1 = Item("item1", Rfc822DateTime(""), ""); + Item item2 = Item("item2", Rfc822DateTime(""), ""); + feed.AddItem(item1); + feed.AddItem(item2); + TS_ASSERT_EQUALS(2, (int) feed.GetItems().size()); + TS_ASSERT_EQUALS("item1", feed.GetItems()[0].GetTitle()); + TS_ASSERT_EQUALS("item2", feed.GetItems()[1].GetTitle()); + } + + void Should_have_a_default_time_to_live_of_720_minutes() + { + Feed feed(""); + TS_ASSERT_EQUALS(720, feed.GetTimeToLive()); + } +}; diff --git a/src/FeedsConfigFile.cc b/src/FeedsConfigFile.cc new file mode 100644 index 0000000..df7d3a3 --- /dev/null +++ b/src/FeedsConfigFile.cc @@ -0,0 +1,60 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedsConfigFile.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <fstream> +#include "FeedsConfigFile.h" + +using namespace std; + +FeedsConfigFile::FeedsConfigFile(string sourcesFileName) : + sourcesFileName(sourcesFileName) +{ +} + +void FeedsConfigFile::GetFeeds(FeedList& feeds) const +{ + vector<string> urls = GetFeedUrls(); + for (vector<string>::iterator i = urls.begin(); i != urls.end(); i++) + { + Feed feed(*i); + feeds.push_back(feed); + } +} + +const vector<string> FeedsConfigFile::GetFeedUrls() const +{ + vector<string> urls; + ifstream sourcesFile(sourcesFileName.c_str()); + + string line; + while (getline(sourcesFile, line)) + { + if (line.find("FEED_URL=") == 0) + { + string url = line.substr(9); + + urls.push_back(url); + } + } + + return urls; +} diff --git a/src/FeedsConfigFile.h b/src/FeedsConfigFile.h new file mode 100644 index 0000000..e4cab74 --- /dev/null +++ b/src/FeedsConfigFile.h @@ -0,0 +1,41 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedsConfigFile.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___FEEDSCONFIGFILE_H +#define ___FEEDSCONFIGFILE_H + +#include "IFeedSources.h" + +class FeedsConfigFile : public IFeedSources +{ +private: + std::string sourcesFileName; + +public: + FeedsConfigFile(std::string sourcesFileName); + // <IFeedSources> + void GetFeeds(FeedList& feeds) const; + const std::vector<std::string> GetFeedUrls() const; + // </IFeedSources> +}; + +#endif // ___FEEDSCONFIGFILE_H diff --git a/src/FeedsConfigFile_test.cc b/src/FeedsConfigFile_test.cc new file mode 100644 index 0000000..27b8d0c --- /dev/null +++ b/src/FeedsConfigFile_test.cc @@ -0,0 +1,88 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: FeedsConfigFile_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <fstream> +#include <cxxtest/TestSuite.h> +#include "FeedsConfigFile.h" + +using namespace std; + +namespace +{ + +class FeedsConfigFileTest : public CxxTest::TestSuite +{ +private: + void WriteConfigData(const char* data) + { + ofstream configFile("test.config", ios_base::app); + configFile << data << endl; + configFile.close(); + } + +public: + void setUp() + { + unlink("test.config"); + } + + void tearDown() + { + unlink("test.config"); + } + + void TestEmptyConfigFile() + { + FeedsConfigFile feedSources("test.config"); + FeedList feeds; + feedSources.GetFeeds(feeds); + TS_ASSERT_EQUALS(0, (int) feeds.size()); + } + + void TestSingelFeed() + { + WriteConfigData("FEED_URL=foo\n"); + + FeedsConfigFile feedSources("test.config"); + FeedList feeds; + feedSources.GetFeeds(feeds); + TS_ASSERT_EQUALS(1, (int) feeds.size()); + TS_ASSERT_EQUALS("foo", feeds[0].GetUrl()); + } + + void TestMultipleFeedsWithComments() + { + WriteConfigData("FEED_URL=foo\n"); + WriteConfigData("# Comment\n"); + WriteConfigData("# FEED_URL=abc\n"); + WriteConfigData("FEED_URL=bar\n"); + + FeedsConfigFile feedSources("test.config"); + FeedList feeds; + feedSources.GetFeeds(feeds); + TS_ASSERT_EQUALS(2, (int) feeds.size()); + TS_ASSERT_EQUALS("foo", feeds[0].GetUrl()); + TS_ASSERT_EQUALS("bar", feeds[1].GetUrl()); + } +}; + +}; diff --git a/src/HtmlToText.cc b/src/HtmlToText.cc new file mode 100644 index 0000000..6d24235 --- /dev/null +++ b/src/HtmlToText.cc @@ -0,0 +1,63 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: HtmlToText.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "HtmlToText.h" + +using namespace std; + +string HtmlToText::Convert(std::string html) +{ + string result = ""; + bool isTag = false; + + string::const_iterator htmlIterator = html.begin(); + + while (htmlIterator != html.end()) + { + switch (*htmlIterator) + { + case '<': + { + isTag = true; + break; + } + + case '>': + { + isTag = false; + break; + } + + default: + { + if (!isTag) + { + result = result + *htmlIterator; + } + } + + } + htmlIterator++; + } + + return result; +} diff --git a/src/HtmlToText.h b/src/HtmlToText.h new file mode 100644 index 0000000..2328cf2 --- /dev/null +++ b/src/HtmlToText.h @@ -0,0 +1,34 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: HtmlToText.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___HTMLTOTEXT_H +#define ___HTMLTOTEXT_H + +#include <string> + +class HtmlToText +{ +public: + static std::string Convert(std::string html); +}; + +#endif diff --git a/src/HtmlToText_test.cc b/src/HtmlToText_test.cc new file mode 100644 index 0000000..80affe8 --- /dev/null +++ b/src/HtmlToText_test.cc @@ -0,0 +1,50 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: HtmlToText_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "HtmlToText.h" + +namespace +{ + +class HtmlToTextTest : public CxxTest::TestSuite +{ +public: + void TestEmpty() + { + TS_ASSERT_EQUALS("", HtmlToText::Convert("")); + } + + void TestNoHtml() + { + TS_ASSERT_EQUALS("test", HtmlToText::Convert("test")); + } + + void TestStripHtmlTags() + { + TS_ASSERT_EQUALS("text text text", + HtmlToText::Convert("<foo>text</foo> text <bar>text")); + } + ; +}; + +}; diff --git a/src/IConfiguration.h b/src/IConfiguration.h new file mode 100644 index 0000000..437e437 --- /dev/null +++ b/src/IConfiguration.h @@ -0,0 +1,51 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IConfiguration.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___ICONFIGURATION_H +#define ___ICONFIGURATION_H + +#include <string> +#include "Item.h" + +enum MediaPlayerType +{ + Mplayer, + Xineliboutput +}; + +class IConfiguration +{ +public: + virtual ~IConfiguration() + { + } + virtual const std::string GetCacheDirName() const = 0; + virtual const std::string GetSourcesFileName() const = 0; + virtual int GetMaxCacheAge() const = 0; + virtual void SetMaxCacheAge(const int age) = 0; + virtual void SetPlayBackQuality(const StreamType quality) = 0; + virtual StreamType GetPlayBackQuality() const = 0; + virtual void SetMediaPlayerType(MediaPlayerType mediaplayerType) = 0; + virtual MediaPlayerType GetMediaPlayerType() const = 0; +}; + +#endif diff --git a/src/IDownloadCache.h b/src/IDownloadCache.h new file mode 100644 index 0000000..7ff150d --- /dev/null +++ b/src/IDownloadCache.h @@ -0,0 +1,43 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IDownloadCache.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___IDOWNLOADCACHE_H +#define ___IDOWNLOADCACHE_H + +#include <string> +#include <iostream> +#include "RefPtr.h" + +class IDownloadCache +{ +public: + virtual ~IDownloadCache() + { + } + virtual RefPtr<std::istream> CreateStreamByUrl(const std::string& url) const = 0; + virtual void Put(std::istream& document, const std::string& url) = 0; + virtual long GetAgeInMinutes(const std::string& url) const = 0; + virtual bool IsCached(const std::string& url) const = 0; + virtual void CleanUp(const long maxAge) = 0; +}; + +#endif diff --git a/src/IDownloadPool.h b/src/IDownloadPool.h new file mode 100644 index 0000000..2f0e626 --- /dev/null +++ b/src/IDownloadPool.h @@ -0,0 +1,44 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IDownloadPool.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___IDOWNLOADPOOL_H +#define ___IDOWNLOADPOOL_H + +#include "RefPtr.h" +#include <string> + +class Download; + +class IDownloadPool +{ +public: + virtual ~IDownloadPool() + { + } + //virtual void AddDownload(RefPtr<Download> download) = 0; + virtual RefPtr<Download> AddDownload(const std::string url) = 0; + virtual void RemoveDownload(RefPtr<Download> download) = 0; + virtual RefPtr<Download> GetNextDownload() = 0; + virtual RefPtr<Download> GetDownloadByUrl(std::string url) = 0; +}; + +#endif diff --git a/src/IDownloader.h b/src/IDownloader.h new file mode 100644 index 0000000..cc1161b --- /dev/null +++ b/src/IDownloader.h @@ -0,0 +1,37 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IDownloader.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___IDOWNLOADER_H +#define ___IDOWNLOADER_H + +class Download; + +class IDownloader +{ +public: + virtual ~IDownloader() + { + } + virtual bool PerformDownload(Download& download) = 0; +}; + +#endif diff --git a/src/IErrorLogger.h b/src/IErrorLogger.h new file mode 100644 index 0000000..cb7c94a --- /dev/null +++ b/src/IErrorLogger.h @@ -0,0 +1,38 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IErrorLogger.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___IERRORLOGGER_H +#define ___IERRORLOGGER_H + +#include <string> + +class IErrorLogger +{ +public: + virtual ~IErrorLogger() + { + } + virtual void LogError(const std::string& Message) = 0; + virtual void LogDebug(const std::string& Message) = 0; +}; + +#endif diff --git a/src/IFeedParser.h b/src/IFeedParser.h new file mode 100644 index 0000000..6b7f832 --- /dev/null +++ b/src/IFeedParser.h @@ -0,0 +1,37 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IFeedParser.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___IFEEDPARSER_H +#define ___IFEEDPARSER_H + +class Feed; + +class IFeedParser +{ +public: + virtual ~IFeedParser() + { + } + virtual bool Parse(Feed& feed) const = 0; +}; + +#endif diff --git a/src/IFeedRepository.h b/src/IFeedRepository.h new file mode 100644 index 0000000..d0bd5e6 --- /dev/null +++ b/src/IFeedRepository.h @@ -0,0 +1,38 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IFeedRepository.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef IFEEDREPOSITORY_H_ +#define IFEEDREPOSITORY_H_ + +#include "Feed.h" + +class IFeedRepository +{ +public: + virtual ~IFeedRepository() + { + } + virtual Feed GetFeed(std::string url) = 0; + virtual const FeedList GetRootFeeds() const = 0; +}; + +#endif diff --git a/src/IFeedSources.h b/src/IFeedSources.h new file mode 100644 index 0000000..7af8b03 --- /dev/null +++ b/src/IFeedSources.h @@ -0,0 +1,41 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IFeedSources.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___FEEDSOURCES_H +#define ___FEEDSOURCES_H + +#include <vector> +#include <string> + +#include "Feed.h" + +class IFeedSources +{ +public: + virtual ~IFeedSources() + { + } + virtual void GetFeeds(FeedList& feeds) const = 0; + virtual const std::vector<std::string> GetFeedUrls() const = 0; +}; + +#endif diff --git a/src/IFeedUpdater.h b/src/IFeedUpdater.h new file mode 100644 index 0000000..5e7e565 --- /dev/null +++ b/src/IFeedUpdater.h @@ -0,0 +1,35 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IFeedUpdater.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___IFEEDUPDATER_H +#define ___IFEEDUPDATER_H + +class IFeedUpdater +{ +public: + virtual ~IFeedUpdater() + { + } + virtual void Update() = 0; +}; + +#endif diff --git a/src/IItemHelpButtonsController.h b/src/IItemHelpButtonsController.h new file mode 100644 index 0000000..82af38e --- /dev/null +++ b/src/IItemHelpButtonsController.h @@ -0,0 +1,34 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IItemHelpButtonsController.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef IITEMHELPBUTTONSCONTROLLER_H_ +#define IITEMHELPBUTTONSCONTROLLER_H_ + +class IItemHelpButtonsController +{ +public: + virtual void Initialize() = 0; + virtual void ProcessYellowKey() = 0; + virtual void ProcessGreenKey() = 0; +}; + +#endif diff --git a/src/IListMenu.h b/src/IListMenu.h new file mode 100644 index 0000000..8d3e119 --- /dev/null +++ b/src/IListMenu.h @@ -0,0 +1,44 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IListMenu.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___ILISTMENU_H +#define ___ILISTMENU_H + +#include <string> +#include <vdr/osdbase.h> +#include "IMenu.h" + +class IListMenu : public IMenu +{ +public: + virtual ~IListMenu() + { + } + virtual void SetTitle(const std::string title) = 0; + virtual void AddItem(const std::string item) = 0; + virtual eOSState ShowSubMenu(cOsdMenu* menu) = 0; + virtual void Clear() = 0; + virtual void Refresh() = 0; + virtual bool IsActiveMenu() = 0; +}; + +#endif diff --git a/src/IListMenuPresenter.h b/src/IListMenuPresenter.h new file mode 100644 index 0000000..c0ef578 --- /dev/null +++ b/src/IListMenuPresenter.h @@ -0,0 +1,40 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IListMenuPresenter.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___ILISTMENUPRESENTER_H +#define ___ILISTMENUPRESENTER_H + +#include <vdr/osdbase.h> + +class IListMenu; + +class IListMenuPresenter +{ +public: + virtual ~IListMenuPresenter() + { + } + virtual void Initialize(IListMenu* listMenu) = 0; + virtual const eOSState ProcessKey(const int selectedItem, const eKeys Key, const eOSState state) = 0; +}; + +#endif diff --git a/src/IMediaPlayer.h b/src/IMediaPlayer.h new file mode 100644 index 0000000..70522ad --- /dev/null +++ b/src/IMediaPlayer.h @@ -0,0 +1,35 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IMediaPlayer.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef IMEDIAPLAYER_H_ +#define IMEDIAPLAYER_H_ + +#include <string> + +class IMediaPlayer +{ +public: + virtual ~IMediaPlayer() {}; + virtual bool Play(std::string url) = 0; +}; + +#endif /*IMEDIAPLAYER_H_*/ diff --git a/src/IMenu.h b/src/IMenu.h new file mode 100644 index 0000000..83d9ed5 --- /dev/null +++ b/src/IMenu.h @@ -0,0 +1,40 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IMenu.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___IMENU_H +#define ___IMENU_H + +#include <string> + +class IMenu +{ +public: + virtual ~IMenu() + { + } + virtual void SetRedHelp(std::string text) = 0; + virtual void SetGreenHelp(std::string text) = 0; + virtual void SetYellowHelp(std::string text) = 0; + virtual void SetBlueHelp(std::string text) = 0; +}; + +#endif diff --git a/src/IMenuFactory.h b/src/IMenuFactory.h new file mode 100644 index 0000000..da67e3e --- /dev/null +++ b/src/IMenuFactory.h @@ -0,0 +1,38 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IMenuFactory.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef IMENUFACTORY_H_ +#define IMENUFACTORY_H_ + +class cOsdMenu; +class Feed; + +class IMenuFactory +{ +public: + virtual ~IMenuFactory() + { + } + virtual cOsdMenu* CreateItemMenu(const Feed& feed)= 0; +}; + +#endif /*IMENUFACTORY_H_*/ diff --git a/src/IServiceLocator.h b/src/IServiceLocator.h new file mode 100644 index 0000000..67e9603 --- /dev/null +++ b/src/IServiceLocator.h @@ -0,0 +1,65 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IServiceLocator.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___ISERVICELOCATOR_H +#define ___ISERVICELOCATOR_H + +#include "Feed.h" +#include "Item.h" +#include "RefPtr.h" + +class IFeedSources; +class IFeedParser; +class IDownloadPool; +class IDownloadCache; +class IFeedUpdater; +class IThreadAction; +class IPesPlayer; +class IMp3Decoder; +class IThread; +class IErrorLogger; +class cControl; +class cOsdMenu; +class cMenuSetupPage; +class IMenuFactory; + +class IServiceLocator +{ +public: + virtual ~IServiceLocator() + { + } + virtual IFeedSources* GetFeedSources() = 0; + virtual IFeedParser* GetFeedParser() = 0; + virtual IDownloadPool* GetDownloadPool() = 0; + virtual IDownloadCache* GetDownloadCache() = 0; + virtual IThread* GetDownloadThread() = 0; + virtual IFeedUpdater* GetFeedUpdater() = 0; + virtual RefPtr<IThreadAction> CreateDownloadAction() = 0; + virtual cOsdMenu* CreateFeedMenu() = 0; + virtual cOsdMenu* CreateItemView(Feed feed, Item item) = 0; + virtual IErrorLogger* GetErrorLogger() = 0; + virtual cMenuSetupPage* CreateSetupMenu() = 0; + virtual IMenuFactory* GetMenuFactory() = 0; +}; + +#endif // ___ISERVICELOCATOR_H diff --git a/src/IVdrInterface.h b/src/IVdrInterface.h new file mode 100644 index 0000000..7997d67 --- /dev/null +++ b/src/IVdrInterface.h @@ -0,0 +1,38 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: IVdrInterface.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___IVDRINTERFACE_H_ +#define ___IVDRINTERFACE_H_ + +#include <string> + +class IVdrInterface +{ +public: + virtual ~IVdrInterface() + { + } + virtual void ShowMessage(const std::string& message) = 0; + virtual void ShowErrorMessage(const std::string& message) = 0; +}; + +#endif diff --git a/src/Item.cc b/src/Item.cc new file mode 100644 index 0000000..aaa705d --- /dev/null +++ b/src/Item.cc @@ -0,0 +1,92 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Item.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "Item.h" + +using namespace std; + +Item::Item(const string& title, const Rfc822DateTime& date, const string& description) +:_title(title), _date(date), _description(description) +{ +} + +const string Item::GetTitle() const +{ + return _title; +} + +const Rfc822DateTime Item::GetDate() const +{ + return _date; +} + +const string Item::GetDescription() const +{ + return _description; +} + +void Item::SetSubFeedUrl(const string& url) +{ + _subFeedUrl = url; +} + +const string Item::GetSubFeedUrl() const +{ + return _subFeedUrl; +} + +void Item::SetVideoCastStream(const StreamType quality, const std::string& url) +{ + _streams[quality] = url; +} + +const string Item::GetVideoCastStream(const StreamType quality) const +{ + if ((_streams[quality] == "") && (quality > Low)) + { + return GetVideoCastStream(StreamType(quality - 1)); + } + else + { + return _streams[quality]; + } +} + +void Item::SetStreamLength(const string& streamLength) +{ + _streamLength = streamLength; +} + +const string Item::GetStreamLength() const +{ + return _streamLength; +} + +bool Item::IsVideocast() const +{ + return (Item::GetVideoCastStream(High) != ""); +} + +bool Item::IsCategory() const +{ + return (_subFeedUrl != ""); +} diff --git a/src/Item.h b/src/Item.h new file mode 100644 index 0000000..5da0336 --- /dev/null +++ b/src/Item.h @@ -0,0 +1,62 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Item.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___ITEM_H +#define ___ITEM_H + +#include <string> +#include <vector> +#include "Rfc822DateTime.h" +#include "StreamType.h" + +class Item +{ +private: + std::string _title; + Rfc822DateTime _date; + std::string _description; + std::string _subFeedUrl; + std::string _streams[3]; + std::string _streamLength; + +public: + Item(const std::string& title, const Rfc822DateTime& date, const std::string& description); + const std::string GetTitle() const; + const Rfc822DateTime GetDate() const; + const std::string GetDescription() const; + + void SetSubFeedUrl(const std::string& url); + const std::string GetSubFeedUrl() const; + + void SetVideoCastStream(const StreamType quality, const std::string& url); + const std::string GetVideoCastStream(const StreamType quality) const; + + void SetStreamLength(const std::string& streamLength); + const std::string GetStreamLength() const; + + bool IsVideocast() const; + bool IsCategory() const; +}; + +typedef std::vector<Item> ItemList; + +#endif // ___ITEM_H diff --git a/src/ItemMenuPresenter.cc b/src/ItemMenuPresenter.cc new file mode 100644 index 0000000..866c504 --- /dev/null +++ b/src/ItemMenuPresenter.cc @@ -0,0 +1,218 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ItemMenuPresenter.cc 7654 2008-08-06 18:40:41Z svntobi $ + * + */ + +#include <vdr/player.h> +#include "ItemMenuPresenter.h" +#include "IServiceLocator.h" +#include "IDownloadPool.h" +#include "IDownloadCache.h" +#include "Download.h" +#include <sstream> +#include "MplayerPlugin.h" +#include "RssFeedParser.h" +#include "IFeedRepository.h" +#include "IMenuFactory.h" +#include "IConfiguration.h" +#include "IVdrInterface.h" + +using namespace std; + +ItemMenuPresenter::ItemMenuPresenter(IServiceLocator& serviceLocator, Feed feed, + RefPtr<IFeedRepository> feedRepository, IConfiguration& configuration, RefPtr<IMediaPlayer> mplayerPlugin, + RefPtr<IVdrInterface> vdrInterface) : + serviceLocator(serviceLocator), feed(feed), _feedRepository(feedRepository), _configuration(configuration), + _mplayerPlugin(mplayerPlugin), _vdrInterface(vdrInterface) +{ +} + +void ItemMenuPresenter::Initialize(IListMenu* listMenu) +{ + menu = listMenu; + menu->SetTitle(feed.GetTitle()); + UpdateMenu(); +} + +const eOSState ItemMenuPresenter::ProcessKey(const int selectedItem, const eKeys key, const eOSState state) +{ + if (state == osUnknown) + { + if (IsValidItemSelection(selectedItem)) + { + return ProcessItemAction(key, displayedItems[selectedItem]); + } + } + + if (menu->IsActiveMenu()) + { + if ((key == kDown) || (key == kUp) || (key == kRight) || (key == kLeft) || (key == kNone)) + { + UpdateDownloadAndPlayButton(selectedItem); + } + } + + return state; +} + +bool ItemMenuPresenter::IsValidItemSelection(int selectedItem) +{ + int maxItems = displayedItems.size(); + return ((selectedItem < maxItems) && (selectedItem >= 0)); +} + +eOSState ItemMenuPresenter::ProcessItemAction(eKeys key, Item item) +{ + switch (key) + { + case kOk: + if (item.IsCategory()) + { + ShowSubCategory(item); + } + else + { + ShowItemView(item); + } + break; + + case kGreen: + if (PlayItem(item)) + { + return osEnd; + } + else + { + return osContinue; + } + + case kYellow: + switch (_configuration.GetPlayBackQuality()) + { + case Low: + _configuration.SetPlayBackQuality(High); + break; + case Medium: + _configuration.SetPlayBackQuality(Low); + break; + case High: + _configuration.SetPlayBackQuality(Medium); + break; + } + UpdateYellowButton(); + break; + default: + break; + } + return osContinue; +} + +void ItemMenuPresenter::UpdateYellowButton() const +{ + menu->SetYellowHelp(StreamTypeToString(_configuration.GetPlayBackQuality())); +} + +void ItemMenuPresenter::ShowItemView(Item item) +{ + cOsdMenu* itemView = serviceLocator.CreateItemView(feed, item); + menu->ShowSubMenu(itemView); +} + +void ItemMenuPresenter::ShowSubCategory(Item item) +{ + Feed feed = _feedRepository->GetFeed(item.GetSubFeedUrl()); + cOsdMenu* itemMenu = serviceLocator.GetMenuFactory()->CreateItemMenu(feed); + menu->ShowSubMenu(itemMenu); +} + +bool ItemMenuPresenter::PlayItem(Item item) +{ + if (item.IsVideocast()) + { + if (_mplayerPlugin->Play(item.GetVideoCastStream(_configuration.GetPlayBackQuality()))) + { + _vdrInterface->ShowMessage(tr("Starting playback, please wait...")); + return true; + } + else + { + _vdrInterface->ShowErrorMessage(tr("Playback failed!")); + } + } + return false; +} + +void ItemMenuPresenter::UpdateMenu() +{ + UpdateItemList(); + UpdateDownloadAndPlayButton(0); + + menu->Refresh(); +} + +void ItemMenuPresenter::UpdateItemList() +{ + displayedItems.clear(); + menu->Clear(); + + RefPtr<Download> download=serviceLocator.GetDownloadPool()->GetDownloadByUrl(feed.GetUrl()); + if (download.get()) + { + menu->AddItem(tr("Downloading...Please wait!")); + } + else + { + ItemList items = feed.GetItems(); + + if (items.empty()) + { + menu->AddItem(tr("No entries!")); + + } + else + { + for (ItemList::iterator i = items.begin(); i != items.end(); i++) + { + string menuEntry = (*i).GetDate().ToShortString(); + menuEntry += "\t" + (*i).GetTitle(); + if ((*i).GetStreamLength() != "") + { + menuEntry += " (" + (*i).GetStreamLength() + ")"; + } + menu->AddItem(menuEntry); + displayedItems.push_back(*i); + } + } + } +} + +void ItemMenuPresenter::UpdateDownloadAndPlayButton(int selectedItem) +{ + if (IsValidItemSelection(selectedItem)) + { + Item item = displayedItems[selectedItem]; + if (item.IsVideocast()) + { + menu->SetGreenHelp(tr("Play")); + menu->SetRedHelp(tr("Record")); + UpdateYellowButton(); + } + } +} diff --git a/src/ItemMenuPresenter.h b/src/ItemMenuPresenter.h new file mode 100644 index 0000000..b08a8b1 --- /dev/null +++ b/src/ItemMenuPresenter.h @@ -0,0 +1,72 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ItemMenuPresenter.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___ITEMMENUPRESENTER_H +#define ___ITEMMENUPRESENTER_H + +#include <vdr/osdbase.h> +#include "IListMenuPresenter.h" +#include "IListMenu.h" +#include "Feed.h" +#include "RefPtr.h" + +class IServiceLocator; +class IItemMenu; +class IMediaPlayer; +class IFeedRepository; +class IConfiguration; +class IVdrInterface; + +class ItemMenuPresenter : public IListMenuPresenter +{ +private: + IServiceLocator& serviceLocator; + Feed feed; + ItemList displayedItems; + IListMenu* menu; + bool _downloading; + RefPtr<IFeedRepository> _feedRepository; + IConfiguration& _configuration; + RefPtr<IMediaPlayer> _mplayerPlugin; + RefPtr<IVdrInterface> _vdrInterface; + +private: + void UpdateMenu(); + void UpdateDownloadAndPlayButton(int selectedItem); + void UpdateItemList(); + void ShowItemView(Item item); + void ShowSubCategory(Item item); + bool PlayItem(Item item); + bool IsValidItemSelection(int selectedItem); + eOSState ProcessItemAction(eKeys key, Item item); + void UpdateYellowButton() const; + +public: + ItemMenuPresenter(IServiceLocator& serviceLocator, Feed feed, RefPtr<IFeedRepository> feedRepository, + IConfiguration& configuration, RefPtr<IMediaPlayer> mplayerPlugin, RefPtr<IVdrInterface> vdrInterface); + // <IListMenuPresenter> + void Initialize(IListMenu* listMenu); + const eOSState ProcessKey(const int selectedItem, const eKeys Key, const eOSState state); + // </IListMenuPresenter> +}; + +#endif // ___ILISTMENUPRESENTER_H diff --git a/src/ItemMenuPresenter_test.cc b/src/ItemMenuPresenter_test.cc new file mode 100644 index 0000000..48386f4 --- /dev/null +++ b/src/ItemMenuPresenter_test.cc @@ -0,0 +1,312 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ItemMenuPresenter_test.cc 7656 2008-08-09 21:07:45Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "ItemMenuPresenter.h" +#include "ServiceLocatorStub.h" +#include "ListMenuMock.h" +#include "DownloadPoolMock.h" +#include "DownloadCacheMock.h" +#include "vdr-stub/ccontrolstub.h" +#include "IMediaPlayer.h" +#include "IFeedRepository.h" +#include "ConfigurationStub.h" +#include "IVdrInterface.h" + +using namespace std; + +namespace +{ + +class An_ItemMenuPresenter_for_an_empty_feed: public CxxTest::TestSuite +{ +private: + ServiceLocatorStub _serviceLocator; + DownloadCacheMock _downloadCache; + DownloadPoolMock _downloadPool; + RefPtr<IMediaPlayer> _mplayerPlugin; + RefPtr<ListMenuMock> _menu; + RefPtr<Feed> _feed; + RefPtr<ItemMenuPresenter> _presenter; + RefPtr<IFeedRepository> _feedRepository; + RefPtr<IConfiguration> _configuration; + RefPtr<IVdrInterface> _vdrInterface; + +public: + void setUp() + { + _menu = new ListMenuMock(); + _feed = new Feed(""); + _feed->SetTitle("dummy title"); + _presenter = new ItemMenuPresenter(_serviceLocator, *_feed, _feedRepository, *_configuration, _mplayerPlugin, _vdrInterface); + _serviceLocator.downloadPool = &_downloadPool; + } + + void Should_do_nothing() + { + _presenter->Initialize(_menu.get()); + _menu->isActiveMenu = true; + + TS_ASSERT_EQUALS(osUnknown, _presenter->ProcessKey(-1, kOk, osUnknown)); + } + + void Should_display_the_feed_title_as_the_menu_title() + { + _menu->ExpectMethod("SetTitle", "dummy title"); + + _presenter->Initialize(_menu.get()); + + VERIFY_EXPECTATIONS(*_menu); + } + + void Should_display_a_no_entries_message_as_the_first_menu_entry() + { + _menu->ExpectMethod("AddItem", "i18n:No entries!"); + + _presenter->Initialize(_menu.get()); + + VERIFY_EXPECTATIONS(*_menu); + } +}; + +class FeedRepositoryMock: public IFeedRepository +{ +public: + Feed GetFeed(std::string url) { return Feed(""); } + const FeedList GetRootFeeds() const { return FeedList(); } +}; + +class MplayerPluginMock: public IMediaPlayer, public StringMessageMock +{ +public: + bool PlayResult; + + MplayerPluginMock() : + PlayResult(true) + { + } + + bool Play(string url) + { + AddMethod("Play", url); + return PlayResult; + } +}; + +class VdrInterfaceMock: public IVdrInterface, public StringMessageMock +{ +public: + void ShowMessage(const string& message) + { + AddMethod("ShowMessage", message); + } + + void ShowErrorMessage(const string& message) + { + AddMethod("ShowErrorMessage", message); + } +}; + +class An_ItemMenuPresenter_for_a_nonempty_feed : public CxxTest::TestSuite +{ +private: + ServiceLocatorStub _serviceLocator; + DownloadCacheMock _downloadCache; + DownloadPoolMock _downloadPool; + RefPtr<MplayerPluginMock> _mplayerPlugin; + RefPtr<ListMenuMock> _menu; + RefPtr<Feed> _feed; + RefPtr<ItemMenuPresenter> _presenter; + RefPtr<IFeedRepository> _feedRepository; + RefPtr<ConfigurationStub> _configuration; + RefPtr<VdrInterfaceMock> _vdrInterface; + +public: + void setUp() + { + _menu = new ListMenuMock(); + _menu->isActiveMenu = true; + _feed = new Feed(""); + Item item1("item1", Rfc822DateTime("Tue, 10 Jun 2003 04:00:00 GMT"), ""); + Item item2("item2", Rfc822DateTime("Tue, 11 Jun 2003 04:00:00 GMT"), ""); + Item item3("item2", Rfc822DateTime("Tue, 11 Jun 2003 04:00:00 GMT"), ""); + item2.SetVideoCastStream(High, "streamUrlHigh"); + item2.SetVideoCastStream(Medium, "streamUrlMedium"); + item2.SetVideoCastStream(Low, "streamUrlLow"); + item2.SetStreamLength("12 min"); + item3.SetSubFeedUrl("linkUrl"); + _feed->AddItem(item1); + _feed->AddItem(item2); + _feed->AddItem(item3); + _mplayerPlugin = new MplayerPluginMock(); + _configuration = new ConfigurationStub(); + _configuration->SetPlayBackQuality(High); + _vdrInterface = new VdrInterfaceMock(); + _presenter = new ItemMenuPresenter(_serviceLocator, *_feed, _feedRepository, *_configuration, _mplayerPlugin, _vdrInterface); + _feedRepository = new FeedRepositoryMock(); + _serviceLocator.downloadPool = &_downloadPool; + } + + void Should_display_the_item_dates_titels_and_lengths_as_menu_entries() + { + _menu->ExpectMethod("AddItem", "10.06.03\titem1"); + _menu->ExpectMethod("AddItem", "11.06.03\titem2 (12 min)"); + + _presenter->Initialize(_menu.get()); + + VERIFY_EXPECTATIONS(*_menu); + } + + void Should_set_the_green_help_button_to_play_when_moving_the_selection_to_a_videocast_item() + { + _menu->ExpectMethod("SetGreenHelp", "i18n:Play"); + + _presenter->Initialize(_menu.get()); + _presenter->ProcessKey(1, kDown, osContinue); + + VERIFY_EXPECTATIONS(*_menu); + } + + void Should_set_the_red_help_button_to_record_when_moving_the_selection_to_a_videocast_item() + { + _menu->ExpectMethod("SetRedHelp", "i18n:Record"); + + _presenter->Initialize(_menu.get()); + _presenter->ProcessKey(1, kDown, osContinue); + + VERIFY_EXPECTATIONS(*_menu); + } + + void Should_set_the_yellow_help_button_to_the_last_selected_quality_when_moving_the_selection_to_a_videocast_item() + { + _configuration->SetPlayBackQuality(Medium); + + _menu->ExpectMethod("SetYellowHelp", "i18n:Medium"); + + _presenter->Initialize(_menu.get()); + _presenter->ProcessKey(1, kDown, osContinue); + + VERIFY_EXPECTATIONS(*_menu); + } + + void Should_open_a_submenu_when_selecting_a_category_item() + { + cOsdMenu itemMenu(""); + _serviceLocator.itemMenu = &itemMenu; + + _menu->ExpectMethod("ShowSubMenu", _menu->MakeString(&itemMenu)); + + _presenter->Initialize(_menu.get()); + TS_ASSERT_EQUALS(osContinue, _presenter->ProcessKey(2, kOk, osUnknown)); + + VERIFY_EXPECTATIONS(*_menu); + } + + void Should_play_a_stream_in_the_selected_quality_when_pressing_green() + { + cOsdMenu itemMenu(""); + _serviceLocator.itemMenu = &itemMenu; + _configuration->SetPlayBackQuality(Medium); + + _mplayerPlugin->ExpectMethod("Play", "streamUrlMedium"); + + _presenter->Initialize(_menu.get()); + _presenter->ProcessKey(1, kGreen, osUnknown); + + VERIFY_EXPECTATIONS(*_mplayerPlugin); + } + + void Should_leave_the_osd_menu_when_playing_a_stream() + { + cOsdMenu itemMenu(""); + _serviceLocator.itemMenu = &itemMenu; + _configuration->SetPlayBackQuality(Medium); + + _presenter->Initialize(_menu.get()); + TS_ASSERT_EQUALS(osEnd, _presenter->ProcessKey(1, kGreen, osUnknown)); + } + + void Should_display_a_message_when_playing_a_stream() + { + cOsdMenu itemMenu(""); + _serviceLocator.itemMenu = &itemMenu; + _configuration->SetPlayBackQuality(Medium); + _presenter->Initialize(_menu.get()); + + _vdrInterface->ExpectMethod("ShowMessage", "i18n:Starting playback, please wait..."); + + _presenter->ProcessKey(1, kGreen, osUnknown); + + VERIFY(*_vdrInterface); + } + + void Should_display_a_message_when_playing_a_stream_failed() + { + cOsdMenu itemMenu(""); + _serviceLocator.itemMenu = &itemMenu; + _configuration->SetPlayBackQuality(Medium); + _presenter->Initialize(_menu.get()); + _mplayerPlugin->PlayResult = false; + + _vdrInterface->ExpectMethod("ShowErrorMessage", "i18n:Playback failed!"); + + _presenter->ProcessKey(1, kGreen, osUnknown); + + VERIFY(*_vdrInterface); + } + + void Should_not_leave_the_menu_when_playing_a_stream_failed() + { + cOsdMenu itemMenu(""); + _serviceLocator.itemMenu = &itemMenu; + _configuration->SetPlayBackQuality(Medium); + _presenter->Initialize(_menu.get()); + _mplayerPlugin->PlayResult = false; + + TS_ASSERT_EQUALS(osContinue, _presenter->ProcessKey(1, kGreen, osUnknown)); + } + + void Should_toggle_the_quality_when_pressing_the_yellow_key() + { + cOsdMenu itemMenu(""); + _serviceLocator.itemMenu = &itemMenu; + _configuration->SetPlayBackQuality(Low); + _presenter->Initialize(_menu.get()); + + _menu->ResetMock(); + _menu->ExpectMethod("SetYellowHelp", "i18n:High"); + _menu->ExpectMethod("SetYellowHelp", "i18n:Medium"); + _menu->ExpectMethod("SetYellowHelp", "i18n:Low"); + + TS_ASSERT_EQUALS(osContinue, _presenter->ProcessKey(1, kYellow, osUnknown)); + TS_ASSERT_EQUALS(High, _configuration->GetPlayBackQuality()); + + TS_ASSERT_EQUALS(osContinue, _presenter->ProcessKey(1, kYellow, osUnknown)); + TS_ASSERT_EQUALS(Medium, _configuration->GetPlayBackQuality()); + + TS_ASSERT_EQUALS(osContinue, _presenter->ProcessKey(1, kYellow, osUnknown)); + TS_ASSERT_EQUALS(Low, _configuration->GetPlayBackQuality()); + + VERIFY_EXPECTATIONS(*_menu); + } +}; +}; diff --git a/src/ItemView.h b/src/ItemView.h new file mode 100644 index 0000000..1e20908 --- /dev/null +++ b/src/ItemView.h @@ -0,0 +1,39 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ItemView.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___ITEMVIEW_H +#define ___ITEMVIEW_H + +#include <string> +#include "IMenu.h" + +class IItemView : public IMenu +{ +public: + virtual ~IItemView() + { + } + virtual void SetDescription(std::string description) = 0; + virtual void SetTitle(std::string title) = 0; +}; + +#endif diff --git a/src/ItemViewPresenter.cc b/src/ItemViewPresenter.cc new file mode 100644 index 0000000..1bde958 --- /dev/null +++ b/src/ItemViewPresenter.cc @@ -0,0 +1,87 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ItemViewPresenter.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "ItemViewPresenter.h" +#include <vdr/i18n.h> +#include "ItemView.h" +#include "IConfiguration.h" + +using namespace std; + +ItemViewPresenter::ItemViewPresenter(const string feedTitle, const Item item, IConfiguration& configuration) : + feedTitle(feedTitle), item(item), _configuration(configuration) +{ +} + +void ItemViewPresenter::Initialize(IItemView* view) +{ + this->view = view; + view->SetTitle(feedTitle); + + view->SetDescription(item.GetDate().ToLongString() + "\n\n" + item.GetTitle() + "\n\n" + item.GetDescription()); + + if (item.IsVideocast()) + { + view->SetRedHelp(tr("Record")); + view->SetGreenHelp(tr("Play")); + UpdateYellowHelp(); + } + else + { + view->SetRedHelp(""); + view->SetGreenHelp(""); + view->SetYellowHelp(""); + } +} + +eOSState ItemViewPresenter::ProcessKey(eOSState state, eKeys key) +{ + switch (key) + { + case kYellow: + switch (_configuration.GetPlayBackQuality()) + { + case High: + _configuration.SetPlayBackQuality(Medium); + break; + case Medium: + _configuration.SetPlayBackQuality(Low); + break; + case Low: + _configuration.SetPlayBackQuality(High); + break; + } + UpdateYellowHelp(); + return osContinue; + case kRed: + case kGreen: + // indicates the parent, that it can process these keys + return osUnknown; + default: + return state; + } +} + +void ItemViewPresenter::UpdateYellowHelp() +{ + view->SetYellowHelp(StreamTypeToString(_configuration.GetPlayBackQuality())); +} diff --git a/src/ItemViewPresenter.h b/src/ItemViewPresenter.h new file mode 100644 index 0000000..6dd8e4c --- /dev/null +++ b/src/ItemViewPresenter.h @@ -0,0 +1,49 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ItemViewPresenter.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___ITEMVIEWPRESENTER_H +#define ___ITEMVIEWPRESENTER_H + +#include <vdr/osdbase.h> +#include "Item.h" + +class IItemView; +class IConfiguration; + +class ItemViewPresenter +{ +private: + const std::string feedTitle; + const Item item; + IConfiguration& _configuration; + IItemView* view; + +public: + ItemViewPresenter(const std::string feedTitle, const Item item, IConfiguration& configuration); + void Initialize(IItemView* view); + eOSState ProcessKey(eOSState state, eKeys Key); + +private: + void UpdateYellowHelp(); +}; + +#endif diff --git a/src/ItemViewPresenter_test.cc b/src/ItemViewPresenter_test.cc new file mode 100644 index 0000000..5d18723 --- /dev/null +++ b/src/ItemViewPresenter_test.cc @@ -0,0 +1,202 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ItemViewPresenter_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include <string> +#include "Item.h" +#include "ItemView.h" +#include "ItemViewPresenter.h" +#include "ConfigurationStub.h" +#include "RefPtr.h" + +using namespace std; + +namespace +{ + +class ItemViewStub : public IItemView +{ +public: + string description; + string title; + string redHelp; + string greenHelp; + string yellowHelp; + +public: + // <IItemView> + void SetDescription(string description) + { + this->description = description; + } + + void SetTitle(string title) + { + this->title = title; + } + + void SetRedHelp(string text) + { + redHelp = text; + } + + void SetGreenHelp(string text) + { + greenHelp = text; + } + + void SetYellowHelp(string text) + { + yellowHelp = text; + } + void SetBlueHelp(string text) + { + } + // </IItemView> +}; + +class An_ItemViewPresenter_for_any_item : public CxxTest::TestSuite +{ +private: + ItemViewStub view; + RefPtr<ItemViewPresenter> _presenter; + RefPtr<ConfigurationStub> _configuration; + +public: + void setUp() + { + view.title = ""; + view.description = ""; + Item item("title", Rfc822DateTime("long_date"), "description"); + _configuration = new ConfigurationStub(); + _presenter = new ItemViewPresenter("FeedTitle", item, *_configuration); + } + + void Should_show_the_item_description() + { + _presenter->Initialize(&view); + + TS_ASSERT_EQUALS("long_date\n\ntitle\n\ndescription", view.description); + } + + void Should_display_the_feed_title_as_menu_title() + { + _presenter->Initialize(&view); + + TS_ASSERT_EQUALS("FeedTitle", view.title); + } + + void Should_ignore_already_processed_keys() + { + _presenter->Initialize(&view); + + TS_ASSERT_EQUALS(osBack, _presenter->ProcessKey(osBack, kOk)); + } +}; + +class An_ItemViewPresenter_for_a_non_videocast_item : public CxxTest::TestSuite +{ +private: + ItemViewStub view; + RefPtr<ItemViewPresenter> _presenter; + RefPtr<ConfigurationStub> _configuration; + +public: + void setUp() + { + view.title = ""; + view.description = ""; + Item item("title", Rfc822DateTime("long_date"), "description"); + _configuration = new ConfigurationStub(); + _configuration->SetPlayBackQuality(High); + _presenter = new ItemViewPresenter("", item, *_configuration); + } + + void Should_not_display_the_Play_and_Record_help_for_a_non_videocast_item() + { + view.redHelp = view.greenHelp = view.yellowHelp = "dummy"; + + _presenter->Initialize(&view); + + TS_ASSERT_EQUALS("", view.greenHelp); + TS_ASSERT_EQUALS("", view.redHelp); + TS_ASSERT_EQUALS("", view.yellowHelp); + } +}; + +class An_ItemViewPresenter_for_a_videocast_item : public CxxTest::TestSuite +{ +private: + ItemViewStub view; + RefPtr<ItemViewPresenter> _presenter; + RefPtr<ConfigurationStub> _configuration; + +public: + void setUp() + { + view.title = ""; + view.description = ""; + Item item("title", Rfc822DateTime("long_date"), "description"); + item.SetVideoCastStream(High, "streamUrl"); + _configuration = new ConfigurationStub(); + _configuration->SetPlayBackQuality(High); + _presenter = new ItemViewPresenter("", item, *_configuration); + } + + void Should_display_the_Play_Record_and_quality_help_for_a_videocast_item() + { + view.redHelp = view.greenHelp = view.yellowHelp = "dummy"; + _configuration->SetPlayBackQuality(High); + + _presenter->Initialize(&view); + + TS_ASSERT_EQUALS("i18n:Play", view.greenHelp); + TS_ASSERT_EQUALS("i18n:Record", view.redHelp); + TS_ASSERT_EQUALS("i18n:High", view.yellowHelp); + } + + void Should_let_the_parent_menu_handle_play_and_record() + { + _presenter->Initialize(&view); + + TS_ASSERT_EQUALS(osUnknown, _presenter->ProcessKey(osContinue, kRed)); + TS_ASSERT_EQUALS(osUnknown, _presenter->ProcessKey(osContinue, kGreen)); + } + + void Should_toggle_the_quality_when_pressing_the_yellow_key() + { + _presenter->Initialize(&view); + + TS_ASSERT_EQUALS("i18n:High", view.yellowHelp); + + _presenter->ProcessKey(osContinue, kYellow); + TS_ASSERT_EQUALS("i18n:Medium", view.yellowHelp); + + _presenter->ProcessKey(osContinue, kYellow); + TS_ASSERT_EQUALS("i18n:Low", view.yellowHelp); + + _presenter->ProcessKey(osContinue, kYellow); + TS_ASSERT_EQUALS("i18n:High", view.yellowHelp); + } +}; + +}; diff --git a/src/Item_test.cc b/src/Item_test.cc new file mode 100644 index 0000000..08b8a55 --- /dev/null +++ b/src/Item_test.cc @@ -0,0 +1,169 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Item_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "Item.h" +#include "RefPtr.h" + +namespace +{ + +class Any_item : public CxxTest::TestSuite +{ +private: + RefPtr<Item> _item; + +public: + void setUp() + { + _item = new Item("title", Rfc822DateTime("01 Jan 2002 00:00::00 GMT"), "description"); + } + + void Should_have_a_title() + { + TS_ASSERT_EQUALS("title", _item->GetTitle()); + } + + void Should_have_a_date() + { + TS_ASSERT_EQUALS("01.01.02", _item->GetDate().ToShortString()); + } + + void Should_have_a_description() + { + TS_ASSERT_EQUALS("description", _item->GetDescription()); + } + + void Should_not_have_videocast_streams() + { + TS_ASSERT_EQUALS("", _item->GetVideoCastStream(High)); + } + + void Should_not_be_a_videocast_item() + { + TS_ASSERT(!_item->IsVideocast()); + } + + void Should_not_be_a_category_item() + { + TS_ASSERT(!_item->IsCategory()); + } + + void Should_have_an_empty_default_stream_length() + { + TS_ASSERT_EQUALS("", _item->GetStreamLength()); + } +}; + +class An_item_with_a_sub_feed_url : public CxxTest::TestSuite +{ +private: + RefPtr<Item> _item; + +public: + void setUp() + { + _item = new Item("title", Rfc822DateTime("01 Jan 2002 00:00::00 GMT"), "description"); + _item->SetSubFeedUrl("linkUrl"); + } + + void Should_have_a_SubFeedUrl() + { + TS_ASSERT_EQUALS("linkUrl", _item->GetSubFeedUrl()); + } + + void Should_be_a_category_item() + { + TS_ASSERT(_item->IsCategory()); + } +}; + +class An_item_with_videocast_streams_in_different_qualities : public CxxTest::TestSuite +{ +private: + RefPtr<Item> _item; + +public: + void setUp() + { + _item = new Item("title", Rfc822DateTime("01 Jan 2002 00:00::00 GMT"), "description"); + _item->SetVideoCastStream(High, "hqStreamUrl"); + _item->SetVideoCastStream(Medium, "mqStreamUrl"); + _item->SetVideoCastStream(Low, "lqStreamUrl"); + _item->SetStreamLength("12 min"); + } + + void Should_have_a_high_quality_stream() + { + TS_ASSERT_EQUALS("hqStreamUrl", _item->GetVideoCastStream(High)); + } + + void Should_have_a_medium_quality_stream() + { + TS_ASSERT_EQUALS("mqStreamUrl", _item->GetVideoCastStream(Medium)); + } + + void Should_have_a_low_quality_stream() + { + TS_ASSERT_EQUALS("lqStreamUrl", _item->GetVideoCastStream(Low)); + } + + void Should_be_a_videocast_item() + { + TS_ASSERT(_item->IsVideocast()); + } + + void Should_have_a_stream_length() + { + TS_ASSERT_EQUALS("12 min", _item->GetStreamLength()); + } +}; + +class An_item_with_only_a_low_quality_videocast_stream : public CxxTest::TestSuite +{ +private: + RefPtr<Item> _item; + +public: + void setUp() + { + _item = new Item("title", Rfc822DateTime("01 Jan 2002 00:00::00 GMT"), "description"); + _item->SetVideoCastStream(Low, "lqStreamUrl"); + } + + void Should_return_the_low_quality_stream_when_requesting_a_high_quality_stream() + { + TS_ASSERT_EQUALS("lqStreamUrl", _item->GetVideoCastStream(High)); + } + + void Should_return_the_low_quality_stream_when_requesting_a_medium_quality_stream() + { + TS_ASSERT_EQUALS("lqStreamUrl", _item->GetVideoCastStream(Medium)); + } + + void Should_be_a_videocast_item() + { + TS_ASSERT(_item->IsVideocast()); + } +}; + +}; diff --git a/src/ListMenuMock.cc b/src/ListMenuMock.cc new file mode 100644 index 0000000..9af2060 --- /dev/null +++ b/src/ListMenuMock.cc @@ -0,0 +1,90 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ListMenuMock.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "ListMenuMock.h" + +using namespace std; + +ListMenuMock::ListMenuMock() +{ + red = ""; + green = ""; + yellow = ""; + blue = ""; + isActiveMenu = true; +} + + +void ListMenuMock::SetTitle(const string title) +{ + AddMethod("SetTitle", title); +} + +void ListMenuMock::AddItem(const string item) +{ + AddMethod("AddItem", item); +} + +eOSState ListMenuMock::ShowSubMenu(cOsdMenu* menu) +{ + AddMethod("ShowSubMenu", MakeString(menu)); + return osContinue; +} + +void ListMenuMock::Clear() +{ + AddMethod("Clear"); +} + +void ListMenuMock::Refresh() +{ + AddMethod("Refresh"); +} + +void ListMenuMock::SetRedHelp(const string text) +{ + AddMethod("SetRedHelp", text); + red = text; +} + +void ListMenuMock::SetGreenHelp(const string text) +{ + AddMethod("SetGreenHelp", text); + green = text; +} + +void ListMenuMock::SetYellowHelp(const string text) +{ + AddMethod("SetYellowHelp", text); + yellow = text; +} + +void ListMenuMock::SetBlueHelp(const string text) +{ + AddMethod("SetBlueHelp", text); + blue = text; +} + +bool ListMenuMock::IsActiveMenu() +{ + return isActiveMenu; +} diff --git a/src/ListMenuMock.h b/src/ListMenuMock.h new file mode 100644 index 0000000..92eb7c5 --- /dev/null +++ b/src/ListMenuMock.h @@ -0,0 +1,58 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ListMenuMock.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___LISTMENUMOCK_H +#define ___LISTMENUMOCK_H + +#include <string> +#include "StringMessageMock.h" +#include "IListMenu.h" + +class ListMenuMock : public StringMessageMock, public IListMenu +{ +public: + std::string red; + std::string green; + std::string yellow; + std::string blue; + bool isActiveMenu; + +private: + void SetHelpString(std::string& str, const char* text); + +public: + ListMenuMock(); + // <IListMenu> + void SetTitle(const std::string title); + void AddItem(const std::string item); + void SetRedHelp(const std::string text); + void SetGreenHelp(const std::string text); + void SetYellowHelp(const std::string text); + void SetBlueHelp(const std::string text); + eOSState ShowSubMenu(cOsdMenu* menu); + void Clear(); + void Refresh(); + bool IsActiveMenu(); + // </IListMenu> +}; + +#endif diff --git a/src/LocalFileCache.cc b/src/LocalFileCache.cc new file mode 100644 index 0000000..b46ec66 --- /dev/null +++ b/src/LocalFileCache.cc @@ -0,0 +1,121 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: LocalFileCache.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <sys/stat.h> +#include <fstream> +#include <dirent.h> +#include "LocalFileCache.h" +#include "SdbmHashCalculator.h" +#include <values.h> + +using namespace std; + +LocalFileCache::LocalFileCache(const string& cacheDirName) : + _cacheDirName(cacheDirName) +{ +} + +RefPtr<istream> LocalFileCache::CreateStreamByUrl(const string& url) const +{ + ifstream* localFile = new ifstream(); + localFile->open(UrlToLocalFileName(url).c_str()); + if (!localFile->fail()) + { + return RefPtr<istream>(localFile); + } + else + { + delete localFile; + return RefPtr<istream>(NULL); + } +} + +void LocalFileCache::Put(istream& document, const string& url) +{ + ofstream localFile(UrlToLocalFileName(url).c_str()); + localFile << document.rdbuf(); + localFile.close(); + if (!localFile.good()) + { + unlink(UrlToLocalFileName(url).c_str()); + } +} + +string LocalFileCache::UrlToLocalFileName(const string& url) const +{ + return _cacheDirName + "/" + SdbmHashCalculator::Calculate(url); +} + +long LocalFileCache::GetAgeInMinutes(const string& url) const +{ + return GetLocalFileAge(UrlToLocalFileName(url)); +} + +bool LocalFileCache::IsCached(const string& url) const +{ + struct stat buf; + if (0 == stat(UrlToLocalFileName(url).c_str(), &buf)) + { + return true; + } + else + { + return false; + } +} + +void LocalFileCache::CleanUp(const long maxAgeInDays) +{ + DIR* directory = opendir(_cacheDirName.c_str()); + + if (directory) + { + struct dirent* entry; + while ((entry = readdir(directory))) + { + string fileName = entry->d_name; + if ((fileName != ".") & (fileName != "..")) + { + fileName = string(_cacheDirName) + "/" + fileName; + if (GetLocalFileAge(fileName) >= maxAgeInDays * 60 * 24) + { + remove(fileName.c_str()); + } + } + } + } + + closedir(directory); +} + +long LocalFileCache::GetLocalFileAge(const string& localFileName) const +{ + struct stat buf; + time_t tm = time(NULL); + + if (0 == stat(localFileName.c_str(), &buf)) + { + return (tm - buf.st_mtime) / 60 ; + } + + return MAXLONG; +} diff --git a/src/LocalFileCache.h b/src/LocalFileCache.h new file mode 100644 index 0000000..6c31128 --- /dev/null +++ b/src/LocalFileCache.h @@ -0,0 +1,50 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: LocalFileCache.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___LOCALFILECACHE_H +#define ___LOCALFILECACHE_H + +#include <string> +#include <iostream> +#include "IDownloadCache.h" + +class LocalFileCache : public IDownloadCache +{ +private: + std::string _cacheDirName; + +private: + std::string UrlToLocalFileName(const std::string& url) const; + long GetLocalFileAge(const std::string& localFileName) const; + +public: + LocalFileCache(const std::string& cacheDirName); + // <IDownloadCache> + RefPtr<std::istream> CreateStreamByUrl(const std::string& url) const; + void Put(std::istream& document, const std::string& url); + long GetAgeInMinutes(const std::string& url) const; + bool IsCached(const std::string& url) const; + void CleanUp(const long maxAgeInDays); + // </IDownloadCache> +}; + +#endif // ___LOCALFILECACHE_H diff --git a/src/LocalFileCache_test.cc b/src/LocalFileCache_test.cc new file mode 100644 index 0000000..6bd8379 --- /dev/null +++ b/src/LocalFileCache_test.cc @@ -0,0 +1,160 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: LocalFileCache_test.cc 7656 2008-08-09 21:07:45Z svntobi $ + * + */ + +#include <time.h> +#include <sstream> +#include <cxxtest/TestSuite.h> +#include "LocalFileCache.h" +#include "SdbmHashCalculator.h" +#include "values.h" +#include <stdlib.h> + +using namespace std; + +namespace +{ + +const string CacheDir("test.cache"); + +class An_empty_LocalFileCache: public CxxTest::TestSuite +{ +private: + RefPtr<LocalFileCache> _localFileCache; + +private: + void CreateCachedFile(string fileName, long age) + { + string cachedFileName = SdbmHashCalculator::Calculate(fileName); + + time_t fileTime = time(NULL) - age * 60; + struct tm *timeComponents = localtime(&fileTime); + + char formattedTime[11]; + strftime(formattedTime, 11, "%y%m%d%H%M", timeComponents); + + string touchCommand = string("touch -t ") + formattedTime + " " + + CacheDir + "/" + cachedFileName; + + system(touchCommand.c_str()); + } + +public: + void setUp() + { + system(("rm -rf " + CacheDir).c_str()); + system(("mkdir " + CacheDir).c_str()); + _localFileCache = new LocalFileCache(CacheDir); + } + + void tearDown() + { + system(("rm -rf " + CacheDir).c_str()); + } + + void Should_not_return_any_data() + { + TS_ASSERT(NULL == _localFileCache->CreateStreamByUrl("foo://bar").get()); + } + + void Should_return_the_data_put_into_the_cache() + { + stringstream input("foo"); + _localFileCache->Put(input, "http://foo"); + + RefPtr<istream> output = _localFileCache->CreateStreamByUrl("http://foo"); + TS_ASSERT(NULL != output.get()); + + stringstream outputStr; + outputStr << output->rdbuf(); + TS_ASSERT_EQUALS(input.str(), outputStr.str()); + } + + void Should_return_different_data_by_different_urls() + { + stringstream input1("foo"); + stringstream input2("bar"); + + _localFileCache->Put(input1, "http://foo/baz"); + _localFileCache->Put(input2, "http://bar/baz"); + + stringstream output1; + stringstream output2; + + output1 << _localFileCache->CreateStreamByUrl("http://foo/baz")->rdbuf(); + output2 << _localFileCache->CreateStreamByUrl("http://bar/baz")->rdbuf(); + + TS_ASSERT_EQUALS(input1.str(), output1.str()); + TS_ASSERT_EQUALS(input2.str(), output2.str()); + } + + void Should_return_the_age_of_an_existing_entry_in_minutes() + { + CreateCachedFile("testFile", 5); + TS_ASSERT_EQUALS(5, _localFileCache->GetAgeInMinutes("testFile")); + } + + void Should_return_the_maximum_age_for_a_non_existing_entry_in_minutes() + { + TS_ASSERT_EQUALS(MAXLONG, _localFileCache->GetAgeInMinutes("testFile")); + } + + void Should_tell_if_a_file_is_cached() + { + TS_ASSERT(!_localFileCache->IsCached("testFile")); + + CreateCachedFile("testFile", 0); + TS_ASSERT(_localFileCache->IsCached("testFile")); + } + + void Should_not_clean_up_younger_files() + { + CreateCachedFile("testFile1", 5); // 5 minutes + CreateCachedFile("testFile2", 5 * 60 * 24); // 5 days + TS_ASSERT(_localFileCache->IsCached("testFile1")); + TS_ASSERT(_localFileCache->IsCached("testFile2")); + + _localFileCache->CleanUp(10); + + TS_ASSERT(_localFileCache->IsCached("testFile1")); + TS_ASSERT(_localFileCache->IsCached("testFile2")); + } + + void Should_clean_up_older_files() + { + CreateCachedFile("testFile1", 5 * 60 * 24); // 5 days + CreateCachedFile("testFile2", 10 * 60 * 24); // 10 days + TS_ASSERT(_localFileCache->IsCached("testFile1")); + TS_ASSERT(_localFileCache->IsCached("testFile2")); + + _localFileCache->CleanUp(10); + + TS_ASSERT(_localFileCache->IsCached("testFile1")); + TS_ASSERT(!_localFileCache->IsCached("testFile2")); + } + + void Should_delete_a_file_if_it_can_not_be_written_successfully() + { + // TODO: Any ideas how to test this? + } +}; + +}; diff --git a/src/Menu.h b/src/Menu.h new file mode 100644 index 0000000..34a24ae --- /dev/null +++ b/src/Menu.h @@ -0,0 +1,38 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Menu.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___MENU_H +#define ___MENU_H + +class IMenu +{ +public: + virtual ~IMenu() + { + } + virtual void SetRedHelp(std::string text) = 0; + virtual void SetGreenHelp(std::string text) = 0; + virtual void SetYellowHelp(std::string text) = 0; + virtual void SetBlueHelp(std::string text) = 0; +}; + +#endif diff --git a/src/MplayerPlugin.cc b/src/MplayerPlugin.cc new file mode 100644 index 0000000..1ce94b0 --- /dev/null +++ b/src/MplayerPlugin.cc @@ -0,0 +1,65 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: MplayerPlugin.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "MplayerPlugin.h" +#include <vdr/plugin.h> +#include <iostream> +#include <fstream> +#include "IErrorLogger.h" + +using namespace std; + +const char* MplayerPlugin::ServiceId = "MPlayer-Play-v1"; + +MplayerPlugin::MplayerPlugin(IErrorLogger& errorLogger) : + _errorLogger(errorLogger) +{ + +} + +bool MplayerPlugin::Play(string url) +{ + _errorLogger.LogDebug("Starting to play " + url); + + MPlayerServiceData data; + const char* const tmpPlayListFileName = "/tmp/vodcatcher.pls"; + + ofstream tmpPlayList(tmpPlayListFileName); + tmpPlayList << url; + tmpPlayList.close(); + + data.data.filename = tmpPlayListFileName; + + if (!cPluginManager::CallFirstService(ServiceId, &data)) + { + _errorLogger.LogDebug((string)"Failed to locate Mplayer service '" + ServiceId + "'"); + return false; + } + + if (!data.result) + { + _errorLogger.LogDebug("Mplayer service failed"); + return false; + } + + return true; +} diff --git a/src/MplayerPlugin.h b/src/MplayerPlugin.h new file mode 100644 index 0000000..e10ef4c --- /dev/null +++ b/src/MplayerPlugin.h @@ -0,0 +1,50 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: MplayerPlugin.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___MPLAYERPLUGIN_H +#define ___MPLAYERPLUGIN_H + +#include "IMediaPlayer.h" + +class IErrorLogger; + +class MplayerPlugin : public IMediaPlayer +{ +private: + static const char* ServiceId; + IErrorLogger& _errorLogger; + +public: + MplayerPlugin(IErrorLogger& errorLogger); + virtual bool Play(std::string url); +}; + +struct MPlayerServiceData +{ + int result; + union + { + const char *filename; + } data; +}; + +#endif diff --git a/src/OsdItemView.cc b/src/OsdItemView.cc new file mode 100644 index 0000000..c41a39f --- /dev/null +++ b/src/OsdItemView.cc @@ -0,0 +1,92 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: OsdItemView.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "OsdItemView.h" +#include "ItemViewPresenter.h" + +using namespace std; + +OsdItemView::OsdItemView(RefPtr<ItemViewPresenter> presenter) +:cMenuText("", ""), presenter(presenter) +{ + this->presenter->Initialize(this); +} + +void OsdItemView::SetTitle(string title) +{ + cMenuText::SetTitle(title.c_str()); +} + +void OsdItemView::SetDescription(string description) +{ + SetText(description.c_str()); +} + +eOSState OsdItemView::ProcessKey(eKeys Key) +{ + eOSState state = cMenuText::ProcessKey(Key); + + return presenter->ProcessKey(state, Key); +} + +void OsdItemView::SetRedHelp(string text) +{ + redHelp = text; + UpdateHelpButtons(); +} + +void OsdItemView::SetGreenHelp(string text) +{ + greenHelp = text; + UpdateHelpButtons(); +} + +void OsdItemView::SetYellowHelp(string text) +{ + yellowHelp = text; + UpdateHelpButtons(); +} + +void OsdItemView::SetBlueHelp(string text) +{ + blueHelp = text; + UpdateHelpButtons(); +} + +void OsdItemView::UpdateHelpButtons() +{ + cMenuText::SetHelp(EmptyToNull(redHelp.c_str()), + EmptyToNull(greenHelp.c_str()), EmptyToNull(yellowHelp.c_str()), + EmptyToNull(blueHelp.c_str())); +} + +const char* OsdItemView::EmptyToNull(const char* str) +{ + if (*str) + { + return str; + } + else + { + return NULL; + } +} diff --git a/src/OsdItemView.h b/src/OsdItemView.h new file mode 100644 index 0000000..d106b57 --- /dev/null +++ b/src/OsdItemView.h @@ -0,0 +1,58 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: OsdItemView.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___OSDITEMVIEW_H +#define ___OSDITEMVIEW_H + +#include <vdr/menu.h> +#include "ItemView.h" +#include "RefPtr.h" + +class ItemViewPresenter; + +class OsdItemView : public cMenuText, public IItemView +{ +private: + RefPtr<ItemViewPresenter> presenter; + std::string redHelp; + std::string greenHelp; + std::string yellowHelp; + std::string blueHelp; + +private: + void UpdateHelpButtons(); + const char* EmptyToNull(const char* str); + +public: + OsdItemView(RefPtr<ItemViewPresenter> presenter); + eOSState ProcessKey(eKeys Key); + // <IItemView> + void SetDescription(std::string description); + void SetTitle(std::string title); + void SetRedHelp(std::string text); + void SetGreenHelp(std::string text); + void SetYellowHelp(std::string text); + void SetBlueHelp(std::string text); + // </IItemView> +}; + +#endif diff --git a/src/OsdListMenu.cc b/src/OsdListMenu.cc new file mode 100644 index 0000000..1a995b7 --- /dev/null +++ b/src/OsdListMenu.cc @@ -0,0 +1,112 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: OsdListMenu.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "OsdListMenu.h" +#include "IListMenuPresenter.h" + +using namespace std; + +OsdListMenu::OsdListMenu(RefPtr<IListMenuPresenter> presenter, int tabStop1) +:cOsdMenu("", tabStop1), presenter(presenter) +{ + this->presenter->Initialize(this); +} + +void OsdListMenu::SetTitle(const string title) +{ + cOsdMenu::SetTitle(title.c_str()); +} + +eOSState OsdListMenu::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + return presenter->ProcessKey(Current(), Key, state); +} + +void OsdListMenu::AddItem(const std::string item) +{ + Add(new cOsdItem(item.c_str())); +} + +eOSState OsdListMenu::ShowSubMenu(cOsdMenu* menu) +{ + return AddSubMenu(menu); +} + +void OsdListMenu::Clear() +{ + cOsdMenu::Clear(); +} + +void OsdListMenu::Refresh() +{ + cOsdMenu::Display(); +} + +void OsdListMenu::SetRedHelp(const string text) +{ + redHelp = text; + UpdateHelpButtons(); +} + +void OsdListMenu::SetGreenHelp(const string text) +{ + greenHelp = text; + UpdateHelpButtons(); +} + +void OsdListMenu::SetYellowHelp(string text) +{ + yellowHelp = text; + UpdateHelpButtons(); +} + +void OsdListMenu::SetBlueHelp(const string text) +{ + blueHelp = text; + UpdateHelpButtons(); +} + +void OsdListMenu::UpdateHelpButtons() +{ + cOsdMenu::SetHelp(EmptyToNull(redHelp.c_str()), + EmptyToNull(greenHelp.c_str()), EmptyToNull(yellowHelp.c_str()), + EmptyToNull(blueHelp.c_str())); +} + +const char* OsdListMenu::EmptyToNull(const char* str) const +{ + if (*str) + { + return str; + } + else + { + return NULL; + } +} + +bool OsdListMenu::IsActiveMenu() +{ + return !HasSubMenu(); +} diff --git a/src/OsdListMenu.h b/src/OsdListMenu.h new file mode 100644 index 0000000..a3a20c9 --- /dev/null +++ b/src/OsdListMenu.h @@ -0,0 +1,66 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: OsdListMenu.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___OSDLISTMENU_H +#define ___OSDLISTMENU_H + +#include <string> +#include <vdr/osdbase.h> +#include "IListMenu.h" +#include "RefPtr.h" + +class IListMenuPresenter; + +class OsdListMenu : public cOsdMenu, public IListMenu +{ +private: + RefPtr<IListMenuPresenter> presenter; + std::string redHelp; + std::string greenHelp; + std::string yellowHelp; + std::string blueHelp; + +private: + void UpdateHelpButtons(); + const char* EmptyToNull(const char* str) const; + +public: + OsdListMenu(RefPtr<IListMenuPresenter> presenter, int tabStop1 = 0); + // <cOsdMenu> + eOSState ProcessKey(eKeys Key); + // <cOsdMenu> + + // <IListMenu> + void SetTitle(const std::string title); + void SetRedHelp(const std::string text); + void SetGreenHelp(const std::string text); + void SetYellowHelp(const std::string text); + void SetBlueHelp(const std::string text); + void AddItem(const std::string item); + eOSState ShowSubMenu(cOsdMenu* menu); + void Clear(); + void Refresh(); + bool IsActiveMenu(); + // </IListMenu> +}; + +#endif // ___OSDLISTMENU_H diff --git a/src/OsdSetupMenu.cc b/src/OsdSetupMenu.cc new file mode 100644 index 0000000..1858560 --- /dev/null +++ b/src/OsdSetupMenu.cc @@ -0,0 +1,43 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: OsdSetupMenu.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "OsdSetupMenu.h" +#include "IConfiguration.h" + +const char* OsdSetupMenu::AvailableMediaPlayers[2] = {"MPlayer", "Xineliboutput"}; + +OsdSetupMenu::OsdSetupMenu(IConfiguration& configuration) +:configuration(configuration) +{ + maxCacheAge = configuration.GetMaxCacheAge(); + mediaPlayer = configuration.GetMediaPlayerType(); + Add(new cMenuEditIntItem(tr("Max. download cache age (days)"), &maxCacheAge)); + Add(new cMenuEditStraItem(tr("Media Player"), &mediaPlayer, 2, AvailableMediaPlayers)); +} + +void OsdSetupMenu::Store() +{ + configuration.SetMaxCacheAge(maxCacheAge); + configuration.SetMediaPlayerType((MediaPlayerType) mediaPlayer); + SetupStore("MaxCacheAge", maxCacheAge); + SetupStore("MediaPlayerType", mediaPlayer); +} diff --git a/src/OsdSetupMenu.h b/src/OsdSetupMenu.h new file mode 100644 index 0000000..ab92a3a --- /dev/null +++ b/src/OsdSetupMenu.h @@ -0,0 +1,45 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: OsdSetupMenu.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___OSDSETUPMENU_H +#define ___OSDSETUPMENU_H + +#include <vdr/menuitems.h> + +class IConfiguration; + +class OsdSetupMenu : public cMenuSetupPage +{ + static const char* AvailableMediaPlayers[2]; +public: + IConfiguration& configuration; + int maxCacheAge; + int mediaPlayer; + +protected: + void Store(); + +public: + OsdSetupMenu(IConfiguration& configuration); +}; + +#endif diff --git a/src/PluginCreator.cc b/src/PluginCreator.cc new file mode 100644 index 0000000..77a039b --- /dev/null +++ b/src/PluginCreator.cc @@ -0,0 +1,34 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: PluginCreator.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "VodcatcherPlugin.h" +#include "ServiceLocatorImpl.h" + +using namespace std; + +extern "C" void *VDRPluginCreator() +{ + VodcatcherPlugin* plugin = new VodcatcherPlugin; + RefPtr<IServiceLocator> serviceLocator(new ServiceLocatorImpl(*plugin)); + plugin->SetServiceLocator(serviceLocator); + return plugin; +} diff --git a/src/Queue_test.cc b/src/Queue_test.cc new file mode 100644 index 0000000..2700cce --- /dev/null +++ b/src/Queue_test.cc @@ -0,0 +1,51 @@ +#include <cxxtest/TestSuite.h> + +#include <deque> +#include <string> +#include "RefPtr.h" + +using namespace std; + +template<class T> class Queue +{ +private: + std::deque< RefPtr<T> > _queue; + +public: + void Add(RefPtr<T> item) + { + _queue.push_front(item); + } + + //void Remove(T item); + RefPtr<T> GetNext() + { + if (_queue.empty()) + { + return RefPtr<T>(); + } + else + { + return _queue.back(); + } + } + //T GetByString(std::string url); +}; + +class A_queue : public CxxTest::TestSuite +{ +public: + void Should_by_empty_by_default() + { + Queue<string> queue; + TS_ASSERT(!queue.GetNext().IsAssigned()); + } + + void Should_allow_to_add_items() + { + Queue<string> queue; + queue.Add(RefPtr<string>(new string("item"))); + TS_ASSERT(queue.GetNext().IsAssigned()); + TS_ASSERT_EQUALS("item", *queue.GetNext()); + } +}; diff --git a/src/RefPtr.h b/src/RefPtr.h new file mode 100644 index 0000000..c52af5b --- /dev/null +++ b/src/RefPtr.h @@ -0,0 +1,144 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: RefPtr.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___REFPTR__H +#define ___REFPTR__H + +#include <stddef.h> + +// +// A small note: RefPtr can cope with incomplete types as described in +// http://www.octopull.demon.co.uk/arglib/TheGrin.html by Alan Griffiths +// + +template<class X> class RefPtr +{ + typedef void (*DeleteFunction)(X* pointer); + +private: + X* managedPointer; + int* refCounter; + DeleteFunction deleteFunction; + +public: + explicit RefPtr(X* ptr = 0) : + deleteFunction(DeleteManagedPointer) + { + ManagePointer(ptr); + } + + RefPtr(const RefPtr& refPtr) : + deleteFunction(DeleteManagedPointer) + { + AttachToForeignRefPtr(refPtr.get(), refPtr.refCounter); + } + + ~RefPtr() + { + ReleasePointer(); + } + + X* get() const + { + return managedPointer; + } + + X* operator->() const + { + return managedPointer; + } + + X& operator*() const + { + return *managedPointer; + } + + RefPtr& operator=(const RefPtr& refPtr) + { + if (this != &refPtr) + { + ReleasePointer(); + + AttachToForeignRefPtr(refPtr.get(), refPtr.refCounter); + } + + return *this; + } + + RefPtr operator=(X* ptr) + { + if (this->managedPointer != ptr) + { + ReleasePointer(); + ManagePointer(ptr); + } + + return *this; + } + + template<class Y> RefPtr(const RefPtr<Y> &refPtr) : + deleteFunction(DeleteManagedPointer) + { + AttachToForeignRefPtr(refPtr.get(), refPtr.refCounter); + } + + bool IsAssigned() const + { + return (managedPointer != NULL); + } + +private: + template<class Y> friend class RefPtr; + + void ReleasePointer() + { + if (*refCounter > 0) + { + (*refCounter)--; + } + else + { + deleteFunction(managedPointer); + delete refCounter; + } + } + + void AttachToForeignRefPtr(X* foreignPtr, int* foreignRefCounter) + { + refCounter = foreignRefCounter; + (*refCounter)++; + managedPointer = foreignPtr; + } + + void ManagePointer(X* ptr) + { + this->managedPointer = ptr; + refCounter = new int(0); + } + + static void DeleteManagedPointer(X* pointer) + { + delete pointer; + } +}; + +#endif diff --git a/src/RefPtr_test.cc b/src/RefPtr_test.cc new file mode 100644 index 0000000..9966c38 --- /dev/null +++ b/src/RefPtr_test.cc @@ -0,0 +1,227 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: RefPtr_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "RefPtr.h" +#include "StringMessageMock.h" + +using namespace std; + +class Foo : public StringMessageMock +{ +public: + Foo() + { + } + + Foo(StringMessageMock& parent, string messagePrefix = "") : + StringMessageMock(parent, messagePrefix) + { + } + + ~Foo() + { + AddMethod("dtor"); + } + + int Bar(int arg) + { + return arg; + } +}; + +class RefPtrTest : public CxxTest::TestSuite +{ +public: + void TestEmptyPtr() + { + RefPtr<Foo> ptr; + } + + void TestSingleReference() + { + StringMessageMock mock; + Foo* foo = new Foo(mock); + + mock.ExpectMethod("dtor"); + + RefPtr<Foo>* ref = new RefPtr<Foo>(foo); + delete ref; + + mock.Verify(); + } + + void TestGet() + { + Foo* foo = new Foo(); + RefPtr<Foo> ref(foo); + TS_ASSERT_EQUALS(foo, ref.get()); + } + + void TestPointerOperator() + { + Foo* foo = new Foo(); + RefPtr<Foo> ref(foo); + TS_ASSERT_EQUALS(10, ref->Bar(10)); + } + + void TestAstersikOperator() + { + Foo* foo = new Foo(); + RefPtr<Foo> ref(foo); + TS_ASSERT_EQUALS(10, (*ref).Bar(10)); + } + + void TestMultipleReferences() + { + StringMessageMock mock; + Foo* foo = new Foo(mock); + + RefPtr<Foo>* ref1 = new RefPtr<Foo>(foo); + RefPtr<Foo>* ref2 = new RefPtr<Foo>(*ref1); + RefPtr<Foo>* ref3 = new RefPtr<Foo>(*ref2); + + delete ref1; + mock.Verify(); + + delete ref3; + mock.Verify(); + + mock.ExpectMethod("dtor"); + delete ref2; + mock.Verify(); + } + + void TestAssignmentOperator() + { + StringMessageMock mock; + Foo* foo1 = new Foo(mock, "foo1"); + Foo* foo2 = new Foo(mock, "foo2"); + + RefPtr<Foo>* ref1 = new RefPtr<Foo>(foo1); + RefPtr<Foo>* ref2 = new RefPtr<Foo>(foo2); + + mock.ExpectMethod("foo2:dtor"); + + *ref2 = *ref1; + + mock.Verify(); + TS_ASSERT_EQUALS(foo1, ref2->get()); + + mock.ResetMock(); + mock.ExpectMethod("foo1:dtor"); + + delete ref1; + delete ref2; + + mock.Verify(); + } + + void TestConstAssignment() + { + StringMessageMock mock; + Foo* foo1 = new Foo(mock, "foo1"); + Foo* foo2 = new Foo(mock, "foo2"); + + const RefPtr<Foo>* ref1 = new RefPtr<Foo>(foo1); + RefPtr<Foo>* ref2 = new RefPtr<Foo>(foo2); + + mock.ExpectMethod("foo2:dtor"); + + *ref2 = *ref1; + + mock.Verify(); + TS_ASSERT_EQUALS(foo1, ref2->get()); + + mock.ResetMock(); + mock.ExpectMethod("foo1:dtor"); + + delete ref1; + delete ref2; + + mock.Verify(); + } + + void TestSelfAssignment() + { + StringMessageMock mock; + + RefPtr<Foo> ref1(new Foo(mock)); + + ref1 = ref1; + + mock.Verify(); + } + + void TestPointerAssignment() + { + StringMessageMock mock; + + Foo* foo1 = new Foo(mock, "foo1"); + RefPtr<Foo> ref(foo1); + + mock.ExpectMethod("foo1:dtor"); + + Foo* foo2 = new Foo(mock, "foo2"); + ref = foo2; + + mock.Verify(); + TS_ASSERT_EQUALS(foo2, ref.get()); + } + + void TestPointerselfAssignment() + { + StringMessageMock mock; + Foo* foo = new Foo(mock); + + RefPtr<Foo> ref(foo); + + ref = foo; + + mock.Verify(); + } + + void TestIncompletType() + { + // To some extend, imcomplete types can be handled as Alan Griffiths + // dscribes in: http://www.octopull.demon.co.uk/arglib/TheGrin.html + // + // Unfortunately, I have absolutely no idea, how to write a test + // case for this. So just watch the compiler warnings about + // not beeing able to delete an incomplete type! + } + + void TestConversionToConst() + { + RefPtr<int> nonConstContent(new int(99)); + RefPtr<const int> constContent(nonConstContent); + TS_ASSERT_EQUALS(*nonConstContent, *constContent); + } + + void TestIsAssigned() + { + RefPtr<int> ptr; + TS_ASSERT(!ptr.IsAssigned()); + ptr = new int(3); + TS_ASSERT(ptr.IsAssigned()); + } +}; diff --git a/src/Rfc822DateTime.cc b/src/Rfc822DateTime.cc new file mode 100644 index 0000000..8b7e2a0 --- /dev/null +++ b/src/Rfc822DateTime.cc @@ -0,0 +1,121 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Rfc822DateTime.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "Rfc822DateTime.h" +#include <stdlib.h> + +using namespace std; + +Rfc822DateTime::Rfc822DateTime(string rfc822DateTimeString) +{ + this->rfc822DateTimeString = rfc822DateTimeString; + ParseRfc822DateTimeString(rfc822DateTimeString); +} + +const string Rfc822DateTime::ToShortString() const +{ + if ((year.size() != 4) || (month.size() != 2) || (day.size() != 2)) + { + return "??.??.??"; + } + else + { + return day + '.' + month + '.' + year.substr(2, 2); + } +} + +const string Rfc822DateTime::ToLongString() const +{ + return rfc822DateTimeString; +} + +void Rfc822DateTime::ParseRfc822DateTimeString(string rfc822DateTimeString) +{ + string zone = ExtractLastWord(rfc822DateTimeString); + string time = ExtractLastWord(rfc822DateTimeString); + + year = ExtractLastWord(rfc822DateTimeString); + month = ExtractLastWord(rfc822DateTimeString); + day = ExtractLastWord(rfc822DateTimeString); + + month = MonthNameToNumber(month); + + if (year.length() == 2) + { + year = "20" + year; + } + + if (day.length() == 1) + { + day = "0" + day; + } +} + +string Rfc822DateTime::ExtractLastWord(string& line) +{ + string lastWord; + + if (line != "") + { + unsigned int delimiterPos = line.rfind(" "); + unsigned int wordPos; + + if (delimiterPos == string::npos) + { + delimiterPos = 0; + wordPos = 0; + } + else + { + wordPos = delimiterPos + 1; + } + + lastWord = line.substr(wordPos); + line = line.substr(0, delimiterPos); + line.erase(line.find_last_not_of(" ") + 1); + } + + return lastWord; +} + +string Rfc822DateTime::MonthNameToNumber(string monthName) +{ + static const string months("JanFebMarAprMayJunJulAugSepOctNovDec"); + + monthName = monthName.substr(0, 3); + + unsigned int monthIndex = months.find(monthName); + + if (monthIndex == string::npos) + { + return ""; + } + else + { + char* formattedMonth; + asprintf(&formattedMonth, "%.2d", 1 + monthIndex / 3); + string monthNumber = formattedMonth; + free(formattedMonth); + + return monthNumber; + } +} diff --git a/src/Rfc822DateTime.h b/src/Rfc822DateTime.h new file mode 100644 index 0000000..d774830 --- /dev/null +++ b/src/Rfc822DateTime.h @@ -0,0 +1,47 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Rfc822DateTime.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___RFC822DATETIME_H +#define ___RFC822DATETIME_H + +#include <string> + +class Rfc822DateTime +{ +private: + std::string rfc822DateTimeString; + std::string year; + std::string month; + std::string day; + +private: + void ParseRfc822DateTimeString(std::string rfc822DateTimeString); + static std::string ExtractLastWord(std::string& line); + static std::string MonthNameToNumber(std::string monthName); + +public: + Rfc822DateTime(std::string rfc822DateTimeString); + const std::string ToShortString() const; + const std::string ToLongString() const; +}; + +#endif diff --git a/src/Rfc822DateTime_test.cc b/src/Rfc822DateTime_test.cc new file mode 100644 index 0000000..7af517e --- /dev/null +++ b/src/Rfc822DateTime_test.cc @@ -0,0 +1,149 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Rfc822DateTime_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "Rfc822DateTime.h" + +namespace +{ + +class Rfc822DateTimeTest : public CxxTest::TestSuite +{ +public: + void TestInvalidRfc822DateTime() + { + Rfc822DateTime dateTime("invalid"); + TS_ASSERT_EQUALS("??.??.??", dateTime.ToShortString()); + TS_ASSERT_EQUALS("invalid", dateTime.ToLongString()); + } + + void TestFullDate() + { + Rfc822DateTime dateTime("Tue, 10 Jun 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.06.03", dateTime.ToShortString()); + TS_ASSERT_EQUALS("Tue, 10 Jun 2003 04:00:00 GMT", dateTime.ToLongString()); + } + + void TestNoWeekday() + { + Rfc822DateTime dateTime("10 Jun 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.06.03", dateTime.ToShortString()); + } + + void TestSingleDigitDay() + { + Rfc822DateTime dateTime("1 Jun 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("01.06.03", dateTime.ToShortString()); + } + + void TestTwoDigitYear() + { + Rfc822DateTime dateTime("1 Jun 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("01.06.03", dateTime.ToShortString()); + } + + void TestAllMonths() + { + Rfc822DateTime dateTime1("10 Jan 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.01.03", dateTime1.ToShortString()); + + Rfc822DateTime dateTime2("10 Feb 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.02.03", dateTime2.ToShortString()); + + Rfc822DateTime dateTime3("10 Mar 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.03.03", dateTime3.ToShortString()); + + Rfc822DateTime dateTime4("10 Apr 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.04.03", dateTime4.ToShortString()); + + Rfc822DateTime dateTime5("10 May 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.05.03", dateTime5.ToShortString()); + + Rfc822DateTime dateTime6("10 Jun 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.06.03", dateTime6.ToShortString()); + + Rfc822DateTime dateTime7("10 Jul 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.07.03", dateTime7.ToShortString()); + + Rfc822DateTime dateTime8("10 Aug 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.08.03", dateTime8.ToShortString()); + + Rfc822DateTime dateTime9("10 Sep 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.09.03", dateTime9.ToShortString()); + + Rfc822DateTime dateTime10("10 Oct 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.10.03", dateTime10.ToShortString()); + + Rfc822DateTime dateTime11("10 Nov 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.11.03", dateTime11.ToShortString()); + + Rfc822DateTime dateTime12("10 Dec 03 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.12.03", dateTime12.ToShortString()); + } + + void TestExtraSpacesInDate() + { + Rfc822DateTime dateTime("Wed, 21 Dec 2005 11:28 CET"); + TS_ASSERT_EQUALS("21.12.05", dateTime.ToShortString()); + } + + void TestLongMonthNames() + { + Rfc822DateTime dateTime1("Tue, 10 January 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.01.03", dateTime1.ToShortString()); + + Rfc822DateTime dateTime2("Tue, 10 February 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.02.03", dateTime2.ToShortString()); + + Rfc822DateTime dateTime3("Tue, 10 March 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.03.03", dateTime3.ToShortString()); + + Rfc822DateTime dateTime4("Tue, 10 April 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.04.03", dateTime4.ToShortString()); + + Rfc822DateTime dateTime5("Tue, 10 May 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.05.03", dateTime5.ToShortString()); + + Rfc822DateTime dateTime6("Tue, 10 June 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.06.03", dateTime6.ToShortString()); + + Rfc822DateTime dateTime7("Tue, 10 July 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.07.03", dateTime7.ToShortString()); + + Rfc822DateTime dateTime8("Tue, 10 August 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.08.03", dateTime8.ToShortString()); + + Rfc822DateTime dateTime9("Tue, 10 September 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.09.03", dateTime9.ToShortString()); + + Rfc822DateTime dateTime10("Tue, 10 October 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.10.03", dateTime10.ToShortString()); + + Rfc822DateTime dateTime11("Tue, 10 November 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.11.03", dateTime11.ToShortString()); + + Rfc822DateTime dateTime12("Tue, 10 December 2003 04:00:00 GMT"); + TS_ASSERT_EQUALS("10.12.03", dateTime12.ToShortString()); + } +}; + +}; diff --git a/src/RssFeedParser.cc b/src/RssFeedParser.cc new file mode 100644 index 0000000..db5558e --- /dev/null +++ b/src/RssFeedParser.cc @@ -0,0 +1,180 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: RssFeedParser.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "RssFeedParser.h" +#include "HtmlToText.h" +#include "Feed.h" +#include "IDownloadCache.h" +#include "tinyxml.h" +#include "Item.h" + +using namespace std; + +RssFeedParser::RssFeedParser(const IDownloadCache& downloadCache) : + _downloadCache(downloadCache) +{ +} + +bool RssFeedParser::Parse(Feed& feed) const +{ + RefPtr<istream> source(_downloadCache.CreateStreamByUrl(feed.GetUrl())); + + if (source.IsAssigned()) + { + RefPtr<TiXmlDocument> rssDoc = CreateRssDocument(*source); + + TiXmlHandle docHandle(rssDoc.get()); + const TiXmlNode* channelNode = docHandle.FirstChild("rss").FirstChild("channel").Node(); + if (channelNode) + { + feed.SetTitle(GetChildValue(*channelNode, "title")); + + feed.SetTimeToLive(StringToIntDefault(GetChildValue(*channelNode, "ttl"), feed.GetTimeToLive())); + + for (const TiXmlNode* itemNode = channelNode->FirstChild("item"); itemNode; itemNode + = itemNode->NextSibling("item")) + { + Item item = ParseItem(*itemNode); + feed.AddItem(item); + } + + return true; + } + } + return false; +} + +RefPtr<TiXmlDocument> RssFeedParser::CreateRssDocument(istream& rssStream) const +{ + RefPtr<TiXmlDocument> rssDoc(new TiXmlDocument()); + rssStream >> *rssDoc; + if (!rssDoc->Error()) + { + return rssDoc; + } + + return RefPtr<TiXmlDocument>(); +} + +string RssFeedParser::GetValue(const TiXmlNode& node) const +{ + const TiXmlNode* textNode = node.FirstChild(); + if (textNode) + { + return textNode->Value(); + } + return ""; +} + +const string RssFeedParser::GetAttribute(const TiXmlElement& element, string name) const +{ + const string* value = element.Attribute(name); + if (value != NULL) + { + return *value; + } + else + { + return ""; + } +} + +string RssFeedParser::GetChildValue(const TiXmlNode& root, const string& childName) const +{ + const TiXmlNode* childNode = root.FirstChild(childName); + if (childNode) + { + return GetValue(*childNode); + } + + return ""; +} + +Item RssFeedParser::ParseItem(const TiXmlNode& itemNode) const +{ + string title = GetChildValue(itemNode, "title"); + string description = GetChildValue(itemNode, "description"); + string date = GetChildValue(itemNode, "pubDate"); + + Item item(title, Rfc822DateTime(date), HtmlToText::Convert(description)); + + const TiXmlElement* subFeedElement = itemNode.FirstChildElement("videocast:subfeed"); + if (subFeedElement) + { + item.SetSubFeedUrl(GetAttribute(*subFeedElement, "url")); + } + + const TiXmlElement* enclosureElement = itemNode.FirstChildElement("enclosure"); + if (enclosureElement) + { + string type = GetAttribute(*enclosureElement, "type"); + if (type.find("video/") == 0) + { + item.SetVideoCastStream(Low, GetAttribute(*enclosureElement, "url")); + } + } + + const TiXmlElement* streamElement = itemNode.FirstChildElement("videocast:stream"); + while (streamElement) + { + ParseStreamElement(*streamElement, item); + streamElement = streamElement->NextSiblingElement("videocast:stream"); + } + + const TiXmlElement* streamInfoElement = itemNode.FirstChildElement("videocast:streaminfo"); + if (streamInfoElement) + { + item.SetStreamLength(GetAttribute(*streamInfoElement, "length")); + } + + return item; +} + +void RssFeedParser::ParseStreamElement(const TiXmlElement& streamElement, Item& item) const +{ + string quality = GetAttribute(streamElement, "quality"); + string url = GetAttribute(streamElement, "url"); + + if (quality == "high") + { + item.SetVideoCastStream(High, url); + } + else if (quality == "medium") + { + item.SetVideoCastStream(Medium, url); + } + else if (quality == "low") + { + item.SetVideoCastStream(Low, url); + } +} + +int RssFeedParser::StringToIntDefault(const string& str, const int defaultValue) const +{ + istringstream tempStream(str); + int value; + if (!(tempStream >> value)) + { + value = defaultValue; + } + return value; +} diff --git a/src/RssFeedParser.h b/src/RssFeedParser.h new file mode 100644 index 0000000..c5dc38e --- /dev/null +++ b/src/RssFeedParser.h @@ -0,0 +1,58 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: RssFeedParser.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___RSSFEEDPARSER_H +#define ___RSSFEEDPARSER_H + +#include <string> +#include "IFeedParser.h" +#include "Item.h" +#include "RefPtr.h" + +class IDownloadCache; +class TiXmlDocument; +class TiXmlNode; +class TiXmlElement; + +class RssFeedParser : public IFeedParser +{ +private: + const IDownloadCache& _downloadCache; + +private: + RefPtr<TiXmlDocument> CreateRssDocument() const; + RefPtr<TiXmlDocument> CreateRssDocument(std::istream& rssStream) const; + std::string GetChildValue(const TiXmlNode& root, const std::string& childName) const; + std::string GetValue(const TiXmlNode& node) const; + const std::string GetAttribute(const TiXmlElement& element, const std::string name) const; + Item ParseItem(const TiXmlNode& itemNode) const; + void ParseStreamElement(const TiXmlElement& streamElement, Item& item) const; + int StringToIntDefault(const std::string& str, const int defaultValue) const; + +public: + RssFeedParser(const IDownloadCache& downloadCache); + // <IFeedParser> + bool Parse(Feed& feed) const; + // </IFeedParser> +}; + +#endif //___RSSFEEDPARSER_H diff --git a/src/RssFeedParser_test.cc b/src/RssFeedParser_test.cc new file mode 100644 index 0000000..ec491f9 --- /dev/null +++ b/src/RssFeedParser_test.cc @@ -0,0 +1,338 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: RssFeedParser_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <sstream> +#include <cxxtest/TestSuite.h> +#include "RssFeedParser.h" +#include "Feed.h" +#include "DownloadCacheMock.h" + +using namespace std; + +namespace +{ + +class FeedBuilder +{ +public: + + static string CreateRssFeed(string title, string items) + { + string result; + + result = "<?xml version='1.0'?>"; + result += "<rss version='2.0'><channel>"; + result += CreateXmlTag("title", title); + result += items; + result += "</channel></rss>"; + + return result; + } + + static string CreateRssItem(string title, string pubDate, string description, string link="", string videoCasts="") + { + string result; + + result = "<item>"; + result += CreateXmlTag("title", title); + result += CreateXmlTag("pubDate", pubDate); + result += CreateXmlTag("description", description); + result += link; + result += videoCasts; + result += "</item>"; + + return result; + } + + static string CreateSubFeed(string url) + { + return CreateXmlTag("videocast:subfeed", "", CreateAttribute("url", url)); + } + + static string CreateVideoCast(string url, string quality) + { + return CreateXmlTag("videocast:stream", "", CreateAttribute("quality", quality) + CreateAttribute("url", url)); + } + + static string CreateEnclosure(string url, string type) + { + return CreateXmlTag("enclosure", "", CreateAttribute("url", url) + CreateAttribute("type", type)); + } + + static string CreateTimeToLive(string timeToLive) + { + return CreateXmlTag("ttl", timeToLive); + } + + static string CreateVideoCastStreamInfo(string streamLength) + { + return CreateXmlTag("videocast:streaminfo", "", CreateAttribute("length", streamLength)); + } + +private: + static string CreateXmlTag(string name, string value, string attributes="") + { + return "<" + name + " " + attributes + ">" + value + "</" + name + ">"; + } + + static string CreateAttribute(string name, string value) + { + if (value != "") + { + return name + "='" + value + "' "; + } + else + { + return ""; + } + } +}; + +class A_RssFeedParser : public CxxTest::TestSuite +{ +private: + RefPtr<Feed> _feed; + RefPtr<RssFeedParser> _parser; + RefPtr<DownloadCacheMock> _downloadCache; + +public: + void setUp() + { + _feed = new Feed(""); + _downloadCache = new DownloadCacheMock(); + _parser = new RssFeedParser(*_downloadCache); + } + + void Should_fail_to_parse_a_feed_that_does_not_exist_in_the_download_cache() + { + _downloadCache->streamReturnedByCache = NULL; + + TS_ASSERT(!_parser->Parse(*_feed)); + } + + void Should_fail_to_parse_an_invalid_feed() + { + _downloadCache->streamReturnedByCache = new stringstream("<invalidXml>"); + TS_ASSERT(!_parser->Parse(*_feed)); + } + + void Should_parse_the_feed_title() + { + _downloadCache->streamReturnedByCache = new stringstream(FeedBuilder::CreateRssFeed("FooTitle", "")); + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(0, (int)_feed->GetItems().size()); + TS_ASSERT_EQUALS("FooTitle", _feed->GetTitle()); + } + + void Should_parse_a_single_item() + { + string item = FeedBuilder::CreateRssItem("ItemTitle", "3 Jun 2005 04:00:00 GMT", "ItemDescription", "", ""); + _downloadCache->streamReturnedByCache = new stringstream(FeedBuilder::CreateRssFeed("FooTitle", item)); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("ItemTitle", _feed->GetItems()[0].GetTitle()); + TS_ASSERT_EQUALS("ItemDescription", _feed->GetItems()[0].GetDescription()); + TS_ASSERT_EQUALS("03.06.05", _feed->GetItems()[0].GetDate().ToShortString()); + } + + void Should_parse_two_items() + { + string item1 = FeedBuilder::CreateRssItem("ItemTitle1", "", "", "", ""); + string item2 = FeedBuilder::CreateRssItem("ItemTitle2", "", "", "", ""); + + _downloadCache->streamReturnedByCache = new stringstream(FeedBuilder::CreateRssFeed("FooTitle", item1 + item2)); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(2, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("ItemTitle1", _feed->GetItems()[0].GetTitle()); + TS_ASSERT_EQUALS("ItemTitle2", _feed->GetItems()[1].GetTitle()); + } + + void Should_ignore_tags_after_the_last_item() + { + string item = FeedBuilder::CreateRssItem("ItemTitle1", "", "", "", ""); + + _downloadCache->streamReturnedByCache = new stringstream(FeedBuilder::CreateRssFeed("FooTitle", item + "<tag/>")); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + } + + void Should_parse_an_item_with_a_sub_feed() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "", + FeedBuilder::CreateSubFeed("linkUrl"))); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("linkUrl", _feed->GetItems()[0].GetSubFeedUrl()); + } + + void Should_parse_the_different_streams_of_different_quality() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "", "", + FeedBuilder::CreateVideoCast("urlHigh", "high") + FeedBuilder::CreateVideoCast("urlMedium", "medium") + + FeedBuilder::CreateVideoCast("urlLow", "low"))); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("urlHigh", _feed->GetItems()[0].GetVideoCastStream(High)); + TS_ASSERT_EQUALS("urlMedium", _feed->GetItems()[0].GetVideoCastStream(Medium)); + TS_ASSERT_EQUALS("urlLow", _feed->GetItems()[0].GetVideoCastStream(Low)); + } + + void Should_strip_all_html_from_the_description() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "<![CDATA[<br>test<br>]]>")); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("test", _feed->GetItems()[0].GetDescription()); + } + + void Should_ignore_a_videocast_stream_without_an_url() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "", "", + FeedBuilder::CreateVideoCast("", "high"))); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("", _feed->GetItems()[0].GetVideoCastStream(High)); + } + + void Should_ignore_a_videocast_stream_without_a_quality() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "", "", + FeedBuilder::CreateVideoCast("url", ""))); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("", _feed->GetItems()[0].GetVideoCastStream(High)); + TS_ASSERT_EQUALS("", _feed->GetItems()[0].GetVideoCastStream(Low)); + TS_ASSERT_EQUALS("", _feed->GetItems()[0].GetVideoCastStream(Medium)); + } + + void Should_parse_the_streaminfo() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "", "", + FeedBuilder::CreateVideoCast("urlHigh", "high") + FeedBuilder::CreateVideoCastStreamInfo("12 min"))); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("12 min", _feed->GetItems()[0].GetStreamLength()); + } + + void Should_parse_the_feeds_time_to_live() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateTimeToLive("333")); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(333, _feed->GetTimeToLive()); + } + + void Should_ignore_an_invalid_time_to_live() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateTimeToLive("invalid")); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(720, _feed->GetTimeToLive()); + } + + void Should_add_a_video_encloser_as_low_type_stream() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "", "", + FeedBuilder::CreateEnclosure("enclosureUrl", "video/dont-care"))); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("enclosureUrl", _feed->GetItems()[0].GetVideoCastStream(Low)); + } + + void Should_ignore_non_video_enclosures() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "", "", + FeedBuilder::CreateEnclosure("enclosureUrl", "audio/mp3"))); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("", _feed->GetItems()[0].GetVideoCastStream(Low)); + } + + void Should_prefer_a_low_quality_videocast_stream_over_a_video_enclosure() + { + string feedXml = FeedBuilder::CreateRssFeed("", FeedBuilder::CreateRssItem("", "", "", "", + FeedBuilder::CreateVideoCast("streamUrl", "low") + FeedBuilder::CreateEnclosure("enclosureUrl", + "video/dont-care"))); + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + TS_ASSERT_EQUALS(1, (int)_feed->GetItems().size()); + + TS_ASSERT_EQUALS("streamUrl", _feed->GetItems()[0].GetVideoCastStream(Low)); + } + + void Should_convert_text_to_current_locale() + { + string feedXml = "<?xml version='1.0' encoding='UTF-8'?><rss><channel><title>äää</title></channel></rss>"; + + _downloadCache->streamReturnedByCache = new stringstream(feedXml); + + TS_ASSERT(_parser->Parse(*_feed)); + + // TODO + // TS_ASSERT_EQUALS("äää", _feed->GetTitle()); + } +}; + +}; diff --git a/src/SdbmHashCalculator.cc b/src/SdbmHashCalculator.cc new file mode 100644 index 0000000..2785f6a --- /dev/null +++ b/src/SdbmHashCalculator.cc @@ -0,0 +1,43 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SdbmHashCalculator.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "SdbmHashCalculator.h" +#include <sstream> +#include <iostream> + +using namespace std; + +string SdbmHashCalculator::Calculate(string str) +{ + unsigned long hash = 0; + + for (unsigned int i=0; i < str.length(); i++) + { + int character = str[i]; + hash = character + (hash << 6) + (hash << 16) - hash; + } + + ostringstream hashString; + hashString << hash; + + return hashString.str(); +} diff --git a/src/SdbmHashCalculator.h b/src/SdbmHashCalculator.h new file mode 100644 index 0000000..7f61498 --- /dev/null +++ b/src/SdbmHashCalculator.h @@ -0,0 +1,34 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SdbmHashCalculator.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___SDBMHASHCALCULATOR_H +#define ___SDBMHASHCALCULATOR_H + +#include <string> + +class SdbmHashCalculator +{ +public: + static std::string Calculate(std::string str); +}; + +#endif diff --git a/src/SdbmHashCalculator_test.cc b/src/SdbmHashCalculator_test.cc new file mode 100644 index 0000000..dc28816 --- /dev/null +++ b/src/SdbmHashCalculator_test.cc @@ -0,0 +1,43 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SdbmHashCalculator_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "SdbmHashCalculator.h" + +namespace +{ + +class SdbmHashCalculatorTest : public CxxTest::TestSuite +{ +public: + void TestHashing() + { + TS_ASSERT_EQUALS("0", SdbmHashCalculator::Calculate("")); + TS_ASSERT_EQUALS("0", SdbmHashCalculator::Calculate("\0x00")); + TS_ASSERT_EQUALS("32", SdbmHashCalculator::Calculate(" ")); + TS_ASSERT_EQUALS("65", SdbmHashCalculator::Calculate("A")); + TS_ASSERT_EQUALS("4264001", SdbmHashCalculator::Calculate("AB")); + } +}; + +} +; diff --git a/src/ServiceLocatorImpl.cc b/src/ServiceLocatorImpl.cc new file mode 100644 index 0000000..820c075 --- /dev/null +++ b/src/ServiceLocatorImpl.cc @@ -0,0 +1,199 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ServiceLocatorImpl.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "ServiceLocatorImpl.h" +#include "FeedsConfigFile.h" +#include "IConfiguration.h" +#include "RssFeedParser.h" +#include "VdrThread.h" +#include "VdrSleeper.h" +#include "VdrCriticalSection.h" +#include "DownloadQueue.h" +#include "ThreadsafeDownloadPool.h" +#include "SynchedDownloadPool.h" +#include "LocalFileCache.h" +#include "CurlDownloader.h" +#include "FeedUpdaterImpl.h" +#include "DownloadAction.h" +#include "OsdListMenu.h" +#include "FeedMenuController.h" +#include "ItemMenuPresenter.h" +#include "OsdItemView.h" +#include "ItemViewPresenter.h" +#include "SyslogErrorLogger.h" +#include "OsdSetupMenu.h" +#include "MplayerPlugin.h" +#include "XineliboutputPlayer.h" +#include "FeedRepository.h" +#include "IMenuFactory.h" +#include "VdrInterface.h" + +using namespace std; + +ServiceLocatorImpl::ServiceLocatorImpl(IConfiguration& configuration) : + configuration(configuration) +{ +} + +IFeedSources* ServiceLocatorImpl::GetFeedSources() +{ + if (!feedSources.get()) + { + feedSources = RefPtr<IFeedSources>(new FeedsConfigFile(configuration.GetSourcesFileName())); + } + + return feedSources.get(); +} + +IFeedParser* ServiceLocatorImpl::GetFeedParser() +{ + if (!feedParser.get()) + { + feedParser = RefPtr<IFeedParser>(new RssFeedParser(*GetDownloadCache())); + } + + return feedParser.get(); +} + +IDownloadPool* ServiceLocatorImpl::GetDownloadPool() +{ + if (!downloadPool.get()) + { + RefPtr<ISleeper> downloadSleeper(new VdrSleeper()); + RefPtr<ICriticalSection> criticalSection(new VdrCriticalSection()); + + // Download pool decorated with thread safety and thread synchronization + RefPtr<IDownloadPool> pool(new DownloadQueue()); + RefPtr<IDownloadPool> threadSafePool(new ThreadSafeDownloadPool(pool, + criticalSection)); + RefPtr<IDownloadPool> synchedPool(new SynchedDownloadPool( + threadSafePool, downloadSleeper)); + + downloadPool = RefPtr<IDownloadPool>(synchedPool); + } + + return downloadPool.get(); +} + +IDownloadCache* ServiceLocatorImpl::GetDownloadCache() +{ + if (!downloadCache.get()) + { + downloadCache = RefPtr<IDownloadCache>(new LocalFileCache(configuration.GetCacheDirName())); + } + + return downloadCache.get(); +} + +IDownloader* ServiceLocatorImpl::GetDownloader() +{ + if (!downloader.get()) + { + downloader = RefPtr<IDownloader>(new CurlDownloader(*GetDownloadCache())); + } + + return downloader.get(); +} + +IThread* ServiceLocatorImpl::GetDownloadThread() +{ + if (!downloadThread.get()) + { + downloadThread = RefPtr<IThread>(new VdrThread(CreateDownloadAction())); + } + + return downloadThread.get(); +} + +IFeedUpdater* ServiceLocatorImpl::GetFeedUpdater() +{ + if (!feedUpdater.get()) + { + feedUpdater = RefPtr<IFeedUpdater>(new FeedUpdaterImpl(*GetFeedSources(), *GetDownloadPool(), + *GetDownloadCache())); + } + + return feedUpdater.get(); +} + +RefPtr<IThreadAction> ServiceLocatorImpl::CreateDownloadAction() +{ + return RefPtr<IThreadAction>(new DownloadAction(*GetDownloadPool(), + *GetDownloader())); +} + +IMenuFactory* ServiceLocatorImpl::GetMenuFactory() +{ + return this; +} + +cOsdMenu* ServiceLocatorImpl::CreateFeedMenu() +{ + return new OsdListMenu( + RefPtr<IListMenuPresenter>(new FeedMenuController(*GetMenuFactory(), *CreateFeedRepository()))); +} + +RefPtr<IFeedRepository> ServiceLocatorImpl::CreateFeedRepository() +{ + RefPtr<IFeedRepository> feedRepository(new FeedRepository(*GetDownloadCache(), *GetDownloadPool(), *GetDownloader(), *GetFeedParser(), *GetFeedSources())); + return feedRepository; +} + +cOsdMenu* ServiceLocatorImpl::CreateItemMenu(const Feed& feed) +{ + RefPtr<IMediaPlayer> mplayerPlugin; + + switch(configuration.GetMediaPlayerType()) + { + case Mplayer: + mplayerPlugin = new MplayerPlugin(*GetErrorLogger()); + break; + default: + mplayerPlugin = new XineliboutputPlayer(*GetErrorLogger()); + break; + } + RefPtr<IVdrInterface> vdrInterface(new VdrInterface()); + return new OsdListMenu( + RefPtr<IListMenuPresenter>(new ItemMenuPresenter(*this, feed, CreateFeedRepository(), configuration, mplayerPlugin, vdrInterface)), 9); +} + +cOsdMenu* ServiceLocatorImpl::CreateItemView(Feed feed, Item item) +{ + RefPtr<ItemViewPresenter> itemViewPresenter(new ItemViewPresenter(feed.GetTitle(), item, configuration)); + + return new OsdItemView(itemViewPresenter); +} + +IErrorLogger* ServiceLocatorImpl::GetErrorLogger() +{ + if (!errorLogger.get()) + { + errorLogger = RefPtr<IErrorLogger>(new SyslogErrorLogger()); + } + + return errorLogger.get(); +} + +cMenuSetupPage* ServiceLocatorImpl::CreateSetupMenu() +{ + return new OsdSetupMenu(configuration); +} diff --git a/src/ServiceLocatorImpl.h b/src/ServiceLocatorImpl.h new file mode 100644 index 0000000..160f98a --- /dev/null +++ b/src/ServiceLocatorImpl.h @@ -0,0 +1,75 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ServiceLocatorImpl.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___SERVICELOCATORIMPL_H +#define ___SERVICELOCATORIMPL_H + +#include "IServiceLocator.h" +#include "RefPtr.h" +#include "IMenuFactory.h" + +class IDownloader; +class IPesPacketBuffer; +class IConfiguration; +class IFeedRepository; + +class ServiceLocatorImpl : public IServiceLocator, IMenuFactory +{ +private: + IConfiguration& configuration; + RefPtr<IFeedSources> feedSources; + RefPtr<IFeedParser> feedParser; + RefPtr<IDownloadPool> downloadPool; + RefPtr<IDownloadCache> downloadCache; + RefPtr<IThread> downloadThread; + RefPtr<IDownloader> downloader; + RefPtr<IFeedUpdater> feedUpdater; + RefPtr<IErrorLogger> errorLogger; + RefPtr<IMenuFactory> menuFactory; + +private: + IDownloader* GetDownloader(); + RefPtr<IPesPacketBuffer> CreateSynchedPcmBuffer(); + +public: + ServiceLocatorImpl(IConfiguration& configuration); + // <IServiceLocator> + IFeedSources* GetFeedSources(); + IFeedParser* GetFeedParser(); + IDownloadPool* GetDownloadPool(); + IDownloadCache* GetDownloadCache(); + IThread* GetDownloadThread(); + IFeedUpdater* GetFeedUpdater(); + RefPtr<IThreadAction> CreateDownloadAction(); + cOsdMenu* CreateFeedMenu(); + cOsdMenu* CreateItemView(Feed feed, Item item); + IErrorLogger* GetErrorLogger(); + cMenuSetupPage* CreateSetupMenu(); + // </IServiceLocator> + IMenuFactory* GetMenuFactory(); + RefPtr<IFeedRepository> CreateFeedRepository(); + // <IMenuFactory> + cOsdMenu* CreateItemMenu(const Feed& feed); + // </IMenuFactory> +}; + +#endif // ___SERVICELOCATORIMPL_H diff --git a/src/ServiceLocatorStub.cc b/src/ServiceLocatorStub.cc new file mode 100644 index 0000000..c4daa16 --- /dev/null +++ b/src/ServiceLocatorStub.cc @@ -0,0 +1,103 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ServiceLocatorStub.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "ServiceLocatorStub.h" +#include "ThreadAction.h" + +using namespace std; + +ServiceLocatorStub::ServiceLocatorStub() +{ + feedSources = NULL; + feedParser = NULL; + downloadPool = NULL; + downloadCache = NULL; + feedMenu = NULL; + downloadThread = NULL; + setupMenu = NULL; +} + +IFeedSources* ServiceLocatorStub::GetFeedSources() +{ + return feedSources; +} + +IFeedParser* ServiceLocatorStub::GetFeedParser() +{ + return feedParser; +} + +IDownloadPool* ServiceLocatorStub::GetDownloadPool() +{ + return downloadPool; +} + +IDownloadCache* ServiceLocatorStub::GetDownloadCache() +{ + return downloadCache; +} + +IThread* ServiceLocatorStub::GetDownloadThread() +{ + return downloadThread; +} + +IFeedUpdater* ServiceLocatorStub::GetFeedUpdater() +{ + return feedUpdater; +} + +RefPtr<IThreadAction> ServiceLocatorStub::CreateDownloadAction() +{ + return RefPtr<IThreadAction>(NULL); +} + +cOsdMenu* ServiceLocatorStub::CreateFeedMenu() +{ + return feedMenu; +} + +cOsdMenu* ServiceLocatorStub::CreateItemMenu(const Feed& feed) +{ + return itemMenu; +} + +cOsdMenu* ServiceLocatorStub::CreateItemView(Feed feed, Item item) +{ + itemViewWasCreatedFor = item.GetTitle(); + return itemView; +} + +IErrorLogger* ServiceLocatorStub::GetErrorLogger() +{ + return errorLogger; +} + +cMenuSetupPage* ServiceLocatorStub::CreateSetupMenu() +{ + return setupMenu; +} + +IMenuFactory* ServiceLocatorStub::GetMenuFactory() +{ + return this; +} diff --git a/src/ServiceLocatorStub.h b/src/ServiceLocatorStub.h new file mode 100644 index 0000000..6e2713c --- /dev/null +++ b/src/ServiceLocatorStub.h @@ -0,0 +1,69 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ServiceLocatorStub.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___SERVICELOCATORSTUB_H +#define ___SERVICELOCATORSTUB_H + +#include "IServiceLocator.h" +#include "IMenuFactory.h" + +class ServiceLocatorStub : public IServiceLocator, IMenuFactory +{ +public: + IFeedSources* feedSources; + IFeedParser* feedParser; + IDownloadPool* downloadPool; + IDownloadCache* downloadCache; + IThread* downloadThread; + IFeedUpdater* feedUpdater; + cOsdMenu* feedMenu; + cOsdMenu* itemMenu; + cOsdMenu* itemView; + std::string itemAssignedToPlayerControl; + std::string itemViewWasCreatedFor; + cControl* playerControl; + IErrorLogger* errorLogger; + cMenuSetupPage* setupMenu; + +public: + ServiceLocatorStub(); + // <IServiceLocator> + IFeedSources* GetFeedSources(); + IFeedParser* GetFeedParser(); + IDownloadPool* GetDownloadPool(); + IDownloadCache* GetDownloadCache(); + IThread* GetDownloadThread(); + IFeedUpdater* GetFeedUpdater(); + RefPtr<IThreadAction> CreateDownloadAction(); + cOsdMenu* CreateFeedMenu(); + cOsdMenu* CreateItemView(Feed feed, Item item); + cControl* CreatePlayerControl(Item item); + IErrorLogger* GetErrorLogger(); + cMenuSetupPage* CreateSetupMenu(); + IMenuFactory* GetMenuFactory(); + // </IServiceLocator> + // <IMenuFactory> + cOsdMenu* CreateItemMenu(const Feed& feed); + // </IMenuFactory> +}; + +#endif diff --git a/src/Sleeper.h b/src/Sleeper.h new file mode 100644 index 0000000..b047891 --- /dev/null +++ b/src/Sleeper.h @@ -0,0 +1,36 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Sleeper.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___SLEEPER_H +#define ___SLEEPER_H + +class ISleeper +{ +public: + virtual ~ISleeper() + { + } + virtual void Sleep() = 0; + virtual void WakeUp() = 0; +}; + +#endif diff --git a/src/SleeperMock.cc b/src/SleeperMock.cc new file mode 100644 index 0000000..e1e0ae0 --- /dev/null +++ b/src/SleeperMock.cc @@ -0,0 +1,44 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SleeperMock.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "SleeperMock.h" + +using namespace std; + +SleeperMock::SleeperMock() +{ +} + +SleeperMock::SleeperMock(StringMessageMock& parent, string messagePrefix) : + StringMessageMock(parent, messagePrefix) +{ +} + +void SleeperMock::Sleep() +{ + AddMethod("Sleep"); +} + +void SleeperMock::WakeUp() +{ + AddMethod("WakeUp"); +} diff --git a/src/SleeperMock.h b/src/SleeperMock.h new file mode 100644 index 0000000..63aabe7 --- /dev/null +++ b/src/SleeperMock.h @@ -0,0 +1,40 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SleeperMock.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___SLEEPERMOCK_H +#define ___SLEEPERMOCK_H + +#include "Sleeper.h" +#include "StringMessageMock.h" + +class SleeperMock : public StringMessageMock, public ISleeper +{ +public: + SleeperMock(); + SleeperMock(StringMessageMock& parent, std::string messagePrefix = ""); + // <ISleeper> + void Sleep(); + void WakeUp(); + // </ISleeper> +}; + +#endif diff --git a/src/StderrMock.cc b/src/StderrMock.cc new file mode 100644 index 0000000..a216acc --- /dev/null +++ b/src/StderrMock.cc @@ -0,0 +1,49 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: StderrMock.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "StderrMock.h" + +using namespace std; + +StdErrMock::StdErrMock() +{ + backupCerrStreamBuffer = cerr.rdbuf(); + cerr.rdbuf(stdErrStringStream.rdbuf()); + stdErrStringStream.str(""); +} + +StdErrMock::~StdErrMock() +{ + cerr.rdbuf(backupCerrStreamBuffer); + backupCerrStreamBuffer = NULL; +} + +void StdErrMock::Expect(string expectation) +{ + expected = expectation; +} + +void StdErrMock::Verify() +{ + TS_ASSERT_EQUALS(expected, stdErrStringStream.str()); +} diff --git a/src/StderrMock.h b/src/StderrMock.h new file mode 100644 index 0000000..34215ae --- /dev/null +++ b/src/StderrMock.h @@ -0,0 +1,44 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: StderrMock.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___STDERRMOCK_H +#define ___STDERRMOCK_H + +#include <string> +#include <iostream> +#include <sstream> + +class StdErrMock +{ +private: + std::string expected; + std::streambuf* backupCerrStreamBuffer; + std::stringstream stdErrStringStream; + +public: + StdErrMock(); + ~StdErrMock(); + void Expect(std::string expectation); + void Verify(); +}; + +#endif diff --git a/src/StreamType.cc b/src/StreamType.cc new file mode 100644 index 0000000..44f772e --- /dev/null +++ b/src/StreamType.cc @@ -0,0 +1,17 @@ +#include "StreamType.h" + +using namespace std; + +string StreamTypeToString(StreamType type) +{ + switch (type) + { + case Low: + return tr("Low"); + case Medium: + return tr("Medium"); + case High: + return tr("High"); + } + return ""; +} diff --git a/src/StreamType.h b/src/StreamType.h new file mode 100644 index 0000000..bcd29dd --- /dev/null +++ b/src/StreamType.h @@ -0,0 +1,38 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: StreamType.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___STREAMTYPE_H_ +#define ___STREAMTYPE_H_ + +#include <string> +#include <vdr/i18n.h> + +enum StreamType +{ + Low=0, + Medium=1, + High=2, +}; + +std::string StreamTypeToString(StreamType type); + +#endif diff --git a/src/StreamType_test.cc b/src/StreamType_test.cc new file mode 100644 index 0000000..291d77f --- /dev/null +++ b/src/StreamType_test.cc @@ -0,0 +1,15 @@ +#include <cxxtest/TestSuite.h> + +#include "StreamType.h" + +class A_StreamType : public CxxTest::TestSuite +{ +public: + void Should_have_a_string_representation() + { + TS_ASSERT_EQUALS(std::string("i18n:Low"), StreamTypeToString(Low)); + TS_ASSERT_EQUALS(std::string("i18n:Medium"), StreamTypeToString(Medium)); + TS_ASSERT_EQUALS(std::string("i18n:High"), StreamTypeToString(High)); + } +}; + diff --git a/src/StringMessageMock.cc b/src/StringMessageMock.cc new file mode 100644 index 0000000..b2d76bd --- /dev/null +++ b/src/StringMessageMock.cc @@ -0,0 +1,154 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: StringMessageMock.cc 7656 2008-08-09 21:07:45Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "StringMessageMock.h" +#include <algorithm> + +using namespace std; + +StringMessageMock::StringMessageMock() : + parent(NULL) +{ +} + +StringMessageMock::StringMessageMock(StringMessageMock& parent, string messagePrefix) : + parent(&parent), messagePrefix(messagePrefix) +{ +} + +void StringMessageMock::AddMessage(string message) +{ + if (parent) + { + if (messagePrefix != "") + { + message = messagePrefix + ":" + message; + } + parent->AddMessage(message); + } + current.push_back(message); +} + +void StringMessageMock::AddMethod(string methodName, string argument1, string argument2) +{ + AddMessage(MakeMethodMessage(methodName, argument1, argument2)); +} + +void StringMessageMock::Expect(string expectation) +{ + if (parent) + { + parent->Expect(expectation); + } + expectations.push_back(expectation); +} + +void StringMessageMock::ExpectMethod(string methodName, string argument1, string argument2) +{ + Expect(MakeMethodMessage(methodName, argument1, argument2)); +} + +void StringMessageMock::VerifyExpectations(const char* file, unsigned int line, bool ordered) +{ + vector<string>::iterator expectation = expectations.begin(); + vector<string>::iterator found = current.begin(); + + while (expectation != expectations.end()) + { + found = find(ordered ? found : current.begin(), current.end(), *expectation); + if (found == current.end()) + { + _TS_FAIL(file, line, *expectation + " expected"); + } + expectation++; + } +} + +void StringMessageMock::Verify(const char* file, unsigned int line) +{ + vector<string>::iterator i = expectations.begin(); + vector<string>::iterator j = current.begin(); + + while (i != expectations.end() && j != current.end()) + { + _TS_ASSERT_EQUALS(file, line, *i++, *j++) + } + + if (i != expectations.end()) + { + _TS_FAIL(file, line, (*i + " expected").c_str()); + } + + if (j != current.end()) + { + _TS_FAIL(file, line, (*j + " was not expected").c_str()); + } +} + +void StringMessageMock::Verify() +{ + vector<string>::iterator i = expectations.begin(); + vector<string>::iterator j = current.begin(); + + while (i != expectations.end() && j != current.end()) + { + TS_ASSERT_EQUALS(*i++, *j++) + } + + if (i != expectations.end()) + { + TS_FAIL((*i + " expected").c_str()); + } + + if (j != current.end()) + { + TS_FAIL((*j + " was not expected").c_str()); + } +} + +void StringMessageMock::ResetMock() +{ + expectations.clear(); + current.clear(); +} + +string StringMessageMock::MakeMethodMessage(string methodName, string argument1, string argument2) +{ + string message; + + message = methodName + "("; + + if (argument1 != "") + { + message = message + argument1; + } + + if (argument2 != "") + { + message = message + ", " + argument2; + } + + message = message + ")"; + + return message; +} diff --git a/src/StringMessageMock.h b/src/StringMessageMock.h new file mode 100644 index 0000000..10369ab --- /dev/null +++ b/src/StringMessageMock.h @@ -0,0 +1,68 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: StringMessageMock.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___STRINGMESSAGEMOCK_H +#define ___STRINGMESSAGEMOCK_H + +#include <string> +#include <vector> +#include <sstream> + +class StringMessageMock +{ +private: + std::vector<std::string> expectations; + std::vector<std::string> current; + StringMessageMock* parent; + std::string messagePrefix; + +private: + std::string MakeMethodMessage(std::string methodName, std::string argument1, std::string argument2); + +protected: + void AddMessage(std::string message); + void AddMethod(std::string methodName, std::string argument1 = "", std::string argument2 = ""); + +public: + StringMessageMock(); + StringMessageMock(StringMessageMock& parent, std::string messagePrefix = ""); + + template<class T> std::string MakeString(const T& subject) + { + std::stringstream sstream; + sstream << subject; + return sstream.str(); + } + + void Expect(std::string expectation); + void ExpectMethod(std::string methodName, std::string argument1 = "", std::string argument2 = ""); + void Verify(); + void Verify(const char* file, unsigned int line); + void VerifyExpectations(const char* file, unsigned int line, bool ordered=true); + void ResetMock(); +}; + +#define VERIFY_EXPECTATIONS(mock) (mock).VerifyExpectations(__FILE__, __LINE__) +#define VERIFY_EXPECTATIONS_UNORDERED(mock) (mock).VerifyExpectations(__FILE__, __LINE__, false) +#define VERIFY(mock) (mock).Verify(__FILE__, __LINE__) + +#endif // ___STRINGMESSAGEMOCK_H diff --git a/src/SynchedDownloadPool.cc b/src/SynchedDownloadPool.cc new file mode 100644 index 0000000..04d5564 --- /dev/null +++ b/src/SynchedDownloadPool.cc @@ -0,0 +1,61 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SynchedDownloadPool.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "SynchedDownloadPool.h" +#include "Sleeper.h" +#include "Download.h" + +using namespace std; + +SynchedDownloadPool::SynchedDownloadPool(RefPtr<IDownloadPool> downloadPool, RefPtr<ISleeper> sleeper) : + downloadPool(downloadPool), sleeper(sleeper) +{ +} + +void SynchedDownloadPool::RemoveDownload(RefPtr<Download> download) +{ + downloadPool->RemoveDownload(download); +} + +RefPtr <Download> SynchedDownloadPool::GetNextDownload() +{ + RefPtr<Download> download = downloadPool->GetNextDownload(); + + if (!download.get()) + { + sleeper->Sleep(); + } + + return download; +} + +RefPtr <Download> SynchedDownloadPool::GetDownloadByUrl(string url) +{ + return downloadPool->GetDownloadByUrl(url); +} + +RefPtr<Download> SynchedDownloadPool::AddDownload(const string url) +{ + RefPtr<Download> download = downloadPool->AddDownload(url); + sleeper->WakeUp(); + return download; +} diff --git a/src/SynchedDownloadPool.h b/src/SynchedDownloadPool.h new file mode 100644 index 0000000..d17c864 --- /dev/null +++ b/src/SynchedDownloadPool.h @@ -0,0 +1,47 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SynchedDownloadPool.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___SYNCHEDDOWNLOADPOOL_H +#define ___SYNCHEDDOWNLOADPOOL_H + +#include "IDownloadPool.h" + +class ISleeper; + +class SynchedDownloadPool : public IDownloadPool +{ +private: + RefPtr<IDownloadPool> downloadPool; + RefPtr<ISleeper> sleeper; + +public: + SynchedDownloadPool(RefPtr<IDownloadPool> downloadPool, RefPtr<ISleeper> sleeper); + // <IDownloadPool> + //void AddDownload(RefPtr<Download> download); + RefPtr<Download> AddDownload(const std::string url); + void RemoveDownload(RefPtr<Download> download); + RefPtr<Download> GetNextDownload(); + RefPtr<Download> GetDownloadByUrl(std::string url); + // </IDownloadPool> +}; + +#endif // ___SYNCHEDDOWNLOADPOOL_H diff --git a/src/SynchedDownloadPool_test.cc b/src/SynchedDownloadPool_test.cc new file mode 100644 index 0000000..8ea9042 --- /dev/null +++ b/src/SynchedDownloadPool_test.cc @@ -0,0 +1,101 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SynchedDownloadPool_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "SynchedDownloadPool.h" +#include "DownloadPoolMock.h" +#include "SleeperMock.h" +#include "Download.h" + +using namespace std; + +namespace +{ + +class SynchedDownloadPoolTest : public CxxTest::TestSuite +{ +public: + void TestWrappedDownloadPool() + { + DownloadPoolMock* wrappedDownloadPool = new DownloadPoolMock(); + RefPtr<IDownloadPool> wrappedDownloadPoolPtr(wrappedDownloadPool); + SynchedDownloadPool downloadPool(wrappedDownloadPoolPtr, RefPtr<ISleeper>(new SleeperMock())); + + wrappedDownloadPool->ExpectMethod("AddDownload", "foo"); + wrappedDownloadPool->ExpectMethod("GetNextDownload"); + wrappedDownloadPool->ExpectMethod("RemoveDownload", "foo"); + + RefPtr<Download> download = downloadPool.AddDownload("foo"); + wrappedDownloadPool->returnedDownload = download; + TS_ASSERT(NULL != downloadPool.GetNextDownload().get()); + downloadPool.RemoveDownload(download); + wrappedDownloadPool->Verify(); + } + + void TestSleepIfPoolIsEmpty() + { + StringMessageMock mock; + DownloadPoolMock* wrappedDownloadPool = new DownloadPoolMock(mock); + RefPtr<IDownloadPool> wrappedDownloadPoolPtr(wrappedDownloadPool); + SynchedDownloadPool downloadPool(wrappedDownloadPoolPtr, RefPtr<ISleeper>(new SleeperMock(mock))); + + mock.ExpectMethod("GetNextDownload"); + mock.ExpectMethod("Sleep"); + + TS_ASSERT(NULL == downloadPool.GetNextDownload().get()); + + mock.Verify(); + } + + void TestDoNotSleepIfPoolContainsDownloads() + { + StringMessageMock mock; + DownloadPoolMock* wrappedDownloadPool = new DownloadPoolMock(mock); + RefPtr<IDownloadPool> wrappedDownloadPoolPtr(wrappedDownloadPool); + SynchedDownloadPool downloadPool(wrappedDownloadPoolPtr, RefPtr<ISleeper>(new SleeperMock(mock))); + RefPtr<Download> download(new Download("foo")); + wrappedDownloadPool->returnedDownload = download; + + mock.ExpectMethod("GetNextDownload"); + + TS_ASSERT(NULL != downloadPool.GetNextDownload().get()); + + mock.Verify(); + } + + void TestWakeUpWhenAddingNewDownload() + { + StringMessageMock mock; + DownloadPoolMock* wrappedDownloadPool = new DownloadPoolMock(mock); + RefPtr<IDownloadPool> wrappedDownloadPoolPtr(wrappedDownloadPool); + SynchedDownloadPool downloadPool(wrappedDownloadPoolPtr, RefPtr<ISleeper>(new SleeperMock(mock))); + + mock.ExpectMethod("AddDownload", "foo"); + mock.ExpectMethod("WakeUp"); + + RefPtr<Download> download = downloadPool.AddDownload("foo"); + + mock.Verify(); + } +}; + +}; diff --git a/src/SyslogErrorLogger.cc b/src/SyslogErrorLogger.cc new file mode 100644 index 0000000..faeca21 --- /dev/null +++ b/src/SyslogErrorLogger.cc @@ -0,0 +1,36 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SyslogErrorLogger.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "SyslogErrorLogger.h" +#include <vdr/tools.h> + +using namespace std; + +void SyslogErrorLogger::LogError(const string& Message) +{ + esyslog(("ERROR (vdr-vodcatcher): " + Message).c_str()); +} + +void SyslogErrorLogger::LogDebug(const string& Message) +{ + dsyslog(("DEBUG (vdr-vodcatcher): " + Message).c_str()); +} diff --git a/src/SyslogErrorLogger.h b/src/SyslogErrorLogger.h new file mode 100644 index 0000000..f568915 --- /dev/null +++ b/src/SyslogErrorLogger.h @@ -0,0 +1,37 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: SyslogErrorLogger.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___SYSLOGERRORLOGGER_H +#define ___SYSLOGERRORLOGGER_H + +#include "IErrorLogger.h" + +class SyslogErrorLogger : public IErrorLogger +{ +public: + // <IErrorLogger> + void LogError(const std::string& Message); + void LogDebug(const std::string& Message); + // </IErrorLogger> +}; + +#endif diff --git a/src/Thread.h b/src/Thread.h new file mode 100644 index 0000000..51b50e1 --- /dev/null +++ b/src/Thread.h @@ -0,0 +1,37 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Thread.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___THREAD_H +#define ___THREAD_H + +class IThread +{ +public: + virtual ~IThread() + { + } + virtual void Start() = 0; + virtual void Stop() = 0; + virtual bool IsRunning() = 0; +}; + +#endif diff --git a/src/ThreadAction.h b/src/ThreadAction.h new file mode 100644 index 0000000..02816a3 --- /dev/null +++ b/src/ThreadAction.h @@ -0,0 +1,35 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ThreadAction.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___THREADACTION_H +#define ___THREADACTION_H + +class IThreadAction +{ +public: + virtual ~IThreadAction() + { + } + virtual bool Action() = 0; +}; + +#endif diff --git a/src/ThreadMock.cc b/src/ThreadMock.cc new file mode 100644 index 0000000..a7cdfd7 --- /dev/null +++ b/src/ThreadMock.cc @@ -0,0 +1,52 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ThreadMock.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "ThreadMock.h" + +using namespace std; + +ThreadMock::ThreadMock() +:StringMessageMock(), isRunning(false) +{ +} + +ThreadMock::ThreadMock(StringMessageMock& parent, string messagePrefix) +:StringMessageMock(parent, messagePrefix) +{ +} + +void ThreadMock::Start() +{ + AddMethod("Start"); + isRunning = true; +} + +void ThreadMock::Stop() +{ + AddMethod("Stop"); + isRunning = false; +} + +bool ThreadMock::IsRunning() +{ + return isRunning; +} diff --git a/src/ThreadMock.h b/src/ThreadMock.h new file mode 100644 index 0000000..47f656f --- /dev/null +++ b/src/ThreadMock.h @@ -0,0 +1,44 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ThreadMock.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___THREADMOCK_H +#define ___THREADMOCK_H + +#include "StringMessageMock.h" +#include "Thread.h" + +class ThreadMock : public StringMessageMock, public IThread +{ +private: + bool isRunning; + +public: + ThreadMock(); + ThreadMock(StringMessageMock& parent, std::string messagePrefix = ""); + // <IThread> + void Start(); + void Stop(); + bool IsRunning(); + // </IThread> +}; + +#endif diff --git a/src/ThreadsafeDownloadPool.cc b/src/ThreadsafeDownloadPool.cc new file mode 100644 index 0000000..7338a27 --- /dev/null +++ b/src/ThreadsafeDownloadPool.cc @@ -0,0 +1,66 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ThreadsafeDownloadPool.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "ThreadsafeDownloadPool.h" +#include "Thread.h" +#include "Sleeper.h" +#include "CriticalSection.h" +#include "Download.h" + +using namespace std; + +ThreadSafeDownloadPool::ThreadSafeDownloadPool(RefPtr<IDownloadPool> downloadPool, + RefPtr<ICriticalSection> criticalSection) : + downloadPool(downloadPool), criticalSection(criticalSection) +{ +} + +void ThreadSafeDownloadPool::RemoveDownload(RefPtr<Download> download) +{ + criticalSection->Enter(); + downloadPool->RemoveDownload(download); + criticalSection->Leave(); +} + +RefPtr<Download> ThreadSafeDownloadPool::GetNextDownload() +{ + criticalSection->Enter(); + RefPtr<Download> nextDownload = downloadPool->GetNextDownload(); + criticalSection->Leave(); + return nextDownload; +} + +RefPtr<Download> ThreadSafeDownloadPool::GetDownloadByUrl(string url) +{ + criticalSection->Enter(); + RefPtr<Download> download = downloadPool->GetDownloadByUrl(url); + criticalSection->Leave(); + return download; +} + +RefPtr<Download> ThreadSafeDownloadPool::AddDownload(const string url) +{ + criticalSection->Enter(); + RefPtr<Download> download = downloadPool->AddDownload(url); + criticalSection->Leave(); + return download; +} diff --git a/src/ThreadsafeDownloadPool.h b/src/ThreadsafeDownloadPool.h new file mode 100644 index 0000000..efc905b --- /dev/null +++ b/src/ThreadsafeDownloadPool.h @@ -0,0 +1,47 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ThreadsafeDownloadPool.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___THREADSAFEDOWNLOADPOOL_H +#define ___THREADSAFEDOWNLOADPOOL_H + +#include "IDownloadPool.h" + +class ICriticalSection; + +class ThreadSafeDownloadPool : public IDownloadPool +{ +private: + RefPtr<IDownloadPool> downloadPool; + RefPtr<ICriticalSection> criticalSection; + +public: + ThreadSafeDownloadPool(RefPtr<IDownloadPool> downloadPool, RefPtr<ICriticalSection> criticalSection); + // <IDownloadPool> + //void AddDownload(RefPtr<Download> download); + RefPtr<Download> AddDownload(const std::string url); + void RemoveDownload(RefPtr<Download> download); + RefPtr<Download> GetNextDownload(); + RefPtr<Download> GetDownloadByUrl(std::string url); + // </IDownloadPool> +}; + +#endif // ___THREADSAFEDOWNLOADPOOL_H diff --git a/src/ThreadsafeDownloadPool_test.cc b/src/ThreadsafeDownloadPool_test.cc new file mode 100644 index 0000000..ed5e420 --- /dev/null +++ b/src/ThreadsafeDownloadPool_test.cc @@ -0,0 +1,133 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ThreadsafeDownloadPool_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "StringMessageMock.h" +#include "ThreadsafeDownloadPool.h" +#include "DownloadPoolMock.h" +#include "Download.h" +#include "CriticalSection.h" + +using namespace std; + +namespace +{ + +class CriticalSectionMock : public StringMessageMock, public ICriticalSection +{ +public: + CriticalSectionMock(StringMessageMock& parent) : + StringMessageMock(parent) + { + } + + // <ICriticalSection> + void Enter() + { + AddMethod("Enter"); + } + + void Leave() + { + AddMethod("Leave"); + } + // </ICriticalSection> +}; + +class ThreadSafeDownloadPoolTest : public CxxTest::TestSuite +{ +private: + StringMessageMock combinedMock; + DownloadPoolMock* downloadPool; + ICriticalSection* criticalSection; + ThreadSafeDownloadPool* queue; + +public: + void setUp() + { + combinedMock.ResetMock(); + downloadPool = new DownloadPoolMock(combinedMock); + criticalSection = new CriticalSectionMock(combinedMock); + queue = new ThreadSafeDownloadPool(RefPtr<IDownloadPool>(downloadPool), + RefPtr<ICriticalSection>(criticalSection)); + } + + void tearDown() + { + delete queue; + } + + void TestAddDownload() + { + combinedMock.ExpectMethod("Enter"); + combinedMock.ExpectMethod("AddDownload", "url"); + combinedMock.ExpectMethod("Leave"); + + RefPtr<Download> download = queue->AddDownload("url"); + + combinedMock.Verify(); + } + + void TestGetNextDownload() + { + RefPtr<Download> download(new Download("url")); + downloadPool->returnedDownload = download; + + combinedMock.ExpectMethod("Enter"); + combinedMock.ExpectMethod("GetNextDownload"); + combinedMock.ExpectMethod("Leave"); + + TS_ASSERT_EQUALS("url", queue->GetNextDownload()->GetUrl()); + + combinedMock.Verify(); + } + + void TestRemoveDownload() + { + RefPtr<Download> download(new Download("foo")); + + combinedMock.ExpectMethod("Enter"); + combinedMock.ExpectMethod("RemoveDownload", "foo"); + combinedMock.ExpectMethod("Leave"); + + queue->RemoveDownload(download); + + combinedMock.Verify(); + } + + void TestGetDownloadByUrl() + { + RefPtr<Download> download(new Download("url")); + downloadPool->returnedDownload = download; + + combinedMock.ExpectMethod("Enter"); + combinedMock.ExpectMethod("GetDownloadByUrl", "url"); + combinedMock.ExpectMethod("Leave"); + + TS_ASSERT_EQUALS("url", + queue->GetDownloadByUrl("url")->GetUrl()); + + combinedMock.Verify(); + } +}; + +}; diff --git a/src/VdrCriticalSection.cc b/src/VdrCriticalSection.cc new file mode 100644 index 0000000..25d154f --- /dev/null +++ b/src/VdrCriticalSection.cc @@ -0,0 +1,33 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VdrCriticalSection.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "VdrCriticalSection.h" + +void VdrCriticalSection::Enter() +{ + mutex.Lock(); +} + +void VdrCriticalSection::Leave() +{ + mutex.Unlock(); +} diff --git a/src/VdrCriticalSection.h b/src/VdrCriticalSection.h new file mode 100644 index 0000000..63db989 --- /dev/null +++ b/src/VdrCriticalSection.h @@ -0,0 +1,39 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VdrCriticalSection.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___VDRCRITICALSECTION_H +#define ___VDRCRITICALSECTION_H + +#include <vdr/thread.h> +#include "CriticalSection.h" + +class VdrCriticalSection : public ICriticalSection +{ +private: + cMutex mutex; + +public: + void Enter(); + void Leave(); +}; + +#endif diff --git a/src/VdrInterface.cc b/src/VdrInterface.cc new file mode 100644 index 0000000..8b50d11 --- /dev/null +++ b/src/VdrInterface.cc @@ -0,0 +1,36 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VdrInterface.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "VdrInterface.h" +#include <vdr/skins.h> + +using namespace std; + +void VdrInterface::ShowMessage(const string& message) +{ + Skins.QueueMessage(mtInfo, message.c_str()); +} + +void VdrInterface::ShowErrorMessage(const string& message) +{ + Skins.Message(mtError, message.c_str()); +} diff --git a/src/VdrInterface.h b/src/VdrInterface.h new file mode 100644 index 0000000..ede6ad9 --- /dev/null +++ b/src/VdrInterface.h @@ -0,0 +1,35 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VdrInterface.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___VDRINTERFACE_H_ +#define ___VDRINTERFACE_H_ + +#include "IVdrInterface.h" + +class VdrInterface: public IVdrInterface +{ +public: + void ShowMessage(const std::string& message); + void ShowErrorMessage(const std::string& message); +}; + +#endif /*VDRINTERFACE_H_*/ diff --git a/src/VdrSleeper.cc b/src/VdrSleeper.cc new file mode 100644 index 0000000..9f0c8cc --- /dev/null +++ b/src/VdrSleeper.cc @@ -0,0 +1,33 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VdrSleeper.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "VdrSleeper.h" + +void VdrSleeper::Sleep() +{ + waitMutex.Wait(1000); +} + +void VdrSleeper::WakeUp() +{ + waitMutex.Signal(); +} diff --git a/src/VdrSleeper.h b/src/VdrSleeper.h new file mode 100644 index 0000000..251d8fc --- /dev/null +++ b/src/VdrSleeper.h @@ -0,0 +1,41 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VdrSleeper.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___VDRSLEEPER_H +#define ___VDRSLEEPER_H + +#include <vdr/thread.h> +#include "Sleeper.h" + +class VdrSleeper : public ISleeper +{ +private: + cCondWait waitMutex; + +public: + // <ISleeper> + void Sleep(); + void WakeUp(); + // </ISleeper> +}; + +#endif diff --git a/src/VdrThread.cc b/src/VdrThread.cc new file mode 100644 index 0000000..f6145e7 --- /dev/null +++ b/src/VdrThread.cc @@ -0,0 +1,60 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VdrThread.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "VdrThread.h" +#include "ThreadAction.h" + +VdrThread::VdrThread(RefPtr<IThreadAction> threadAction) : + threadAction(threadAction) +{ +} + +VdrThread::~VdrThread() +{ + Stop(); +} + +void VdrThread::Action() +{ + while (Running()) + { + if (!threadAction->Action()) + { + return; + } + } +} + +void VdrThread::Start() +{ + cThread::Start(); +} + +void VdrThread::Stop() +{ + cThread::Cancel(2000); +} + +bool VdrThread::IsRunning() +{ + return Running(); +} diff --git a/src/VdrThread.h b/src/VdrThread.h new file mode 100644 index 0000000..0ac3f20 --- /dev/null +++ b/src/VdrThread.h @@ -0,0 +1,52 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VdrThread.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___VDRTHREAD_H +#define ___VDRTHREAD_H + +#include "RefPtr.h" +#include <vdr/thread.h> +#include "Thread.h" + +class IThreadAction; + +class VdrThread : public cThread, public IThread +{ +private: + RefPtr<IThreadAction> threadAction; + +protected: + // <cThread> + void Action(); + // </cThread> + +public: + VdrThread(RefPtr<IThreadAction> threadAction); + ~VdrThread(); + // <IThread> + void Start(); + void Stop(); + bool IsRunning(); + // <IThread> +}; + +#endif diff --git a/src/Version.h b/src/Version.h new file mode 100644 index 0000000..cb9d3b7 --- /dev/null +++ b/src/Version.h @@ -0,0 +1,28 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: Version.h 7664 2008-08-10 09:53:34Z svntobi $ + * + */ + +#ifndef ___VERSION_H +#define ___VERSION_H + +static const char VERSION[] = "0.2.1"; + +#endif diff --git a/src/VodcatcherPlugin.cc b/src/VodcatcherPlugin.cc new file mode 100644 index 0000000..3fa0fa4 --- /dev/null +++ b/src/VodcatcherPlugin.cc @@ -0,0 +1,212 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VodcatcherPlugin.cc 7654 2008-08-06 18:40:41Z svntobi $ + * + */ + +#include "VodcatcherPlugin.h" +#include <iostream> +#include <getopt.h> +#include "Version.h" +#include "IFeedUpdater.h" +#include "IErrorLogger.h" +#include "IDownloadCache.h" +#include "IServiceLocator.h" +#include "Thread.h" + +using namespace std; + +VodcatcherPlugin::VodcatcherPlugin() : + cacheDirName(DEFAULT_CACHE_DIR), _maxCacheAge(30), _playBackQuality(High) +{ +} + +void VodcatcherPlugin::SetServiceLocator(RefPtr<IServiceLocator> serviceLocator) +{ + this->serviceLocator = serviceLocator; +} + +const char* VodcatcherPlugin::Version(void) +{ + return VERSION; +} + +const char* VodcatcherPlugin::Description(void) +{ + return tr("Browse and play video podcasts"); +} + +const char* VodcatcherPlugin::MainMenuEntry(void) +{ + return tr("Vodcatcher"); +} + +bool VodcatcherPlugin::Initialize(void) +{ + const char* configDir = ConfigDirectory(); + + if (configDir) + { + sourcesFileName = string(configDir) + "/vodcatchersources.conf"; + } + + if (CacheDirIsAccessible()) + { + return true; + } + else + { + serviceLocator->GetErrorLogger()->LogError("Unable to access cache directory `" + GetCacheDirName() + "`"); + return false; + } +} + +const char* VodcatcherPlugin::CommandLineHelp(void) +{ + return " -c, --cache=CACHE_DIR specify cache dir, defaults to " + "/var/cache/vdr-plugin-vodcatcher"; +} + +bool VodcatcherPlugin::ProcessArgs(int argc, char* argv[]) +{ + static struct option longOptions[] = + { + { "cache", required_argument, NULL, 'c' }, + { NULL } }; + + optind = 0; + opterr = 0; + + int optionChar; + int optionIndex = 0; + while ((optionChar = getopt_long(argc, argv, "c:", longOptions, &optionIndex)) != -1) + { + switch (optionChar) + { + case 'c': + { + cacheDirName = optarg; + break; + } + default: + { + cerr << argv[0] << ": " << "invalid option " << argv[optind-1] << endl; + return false; + } + } + } + return true; +} + +const string VodcatcherPlugin::GetCacheDirName() const +{ + return cacheDirName; +} + +const string VodcatcherPlugin::GetSourcesFileName() const +{ + return sourcesFileName; +} + +int VodcatcherPlugin::GetMaxCacheAge() const +{ + return _maxCacheAge; +} + +void VodcatcherPlugin::SetMaxCacheAge(const int age) +{ + _maxCacheAge = age; +} + +void VodcatcherPlugin::SetPlayBackQuality(const StreamType quality) +{ + _playBackQuality = quality; +} + +StreamType VodcatcherPlugin::GetPlayBackQuality() const +{ + return _playBackQuality; +} + +void VodcatcherPlugin::SetMediaPlayerType(const MediaPlayerType mediaPlayerType) +{ + _mediaPlayerType = mediaPlayerType; +} + +MediaPlayerType VodcatcherPlugin::GetMediaPlayerType() const +{ + return _mediaPlayerType; +} + +cOsdObject* VodcatcherPlugin::MainMenuAction() +{ + return serviceLocator->CreateFeedMenu(); +} + +bool VodcatcherPlugin::Start() +{ + serviceLocator->GetDownloadThread()->Start(); + serviceLocator->GetFeedUpdater()->Update(); + return true; +} + +void VodcatcherPlugin::Stop() +{ + serviceLocator->GetDownloadThread()->Stop(); +} + +void VodcatcherPlugin::Housekeeping() +{ + serviceLocator->GetFeedUpdater()->Update(); + serviceLocator->GetDownloadCache()->CleanUp(24 * GetMaxCacheAge()); +} + +bool VodcatcherPlugin::CacheDirIsAccessible() +{ + if (0 == access((GetCacheDirName()+"/.").c_str(), W_OK)) + { + return true; + } + else + { + return false; + } +} + +bool VodcatcherPlugin::SetupParse(const char* Name, const char* Value) +{ + if (string(Name) == "MaxCacheAge") + { + _maxCacheAge = atoi(Value); + return true; + } + + if (string(Name) == "MediaPlayerType") + { + _mediaPlayerType = (MediaPlayerType) atoi(Value); + return true; + } + + return false; +} + +cMenuSetupPage* VodcatcherPlugin::SetupMenu() +{ + return serviceLocator->CreateSetupMenu(); +} diff --git a/src/VodcatcherPlugin.h b/src/VodcatcherPlugin.h new file mode 100644 index 0000000..2aff88c --- /dev/null +++ b/src/VodcatcherPlugin.h @@ -0,0 +1,76 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VodcatcherPlugin.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___VODCATCHERPLUGIN_H +#define ___VODCATCHERPLUGIN_H + +#include <vdr/plugin.h> +#include "RefPtr.h" +#include "IConfiguration.h" + +class IServiceLocator; + +class VodcatcherPlugin : public cPlugin, public IConfiguration +{ +private: + std::string cacheDirName; + std::string sourcesFileName; + RefPtr<IServiceLocator> serviceLocator; + unsigned int _maxCacheAge; + StreamType _playBackQuality; + MediaPlayerType _mediaPlayerType; + +private: + bool CacheDirIsAccessible(); + +public: + VodcatcherPlugin(); + void SetServiceLocator(RefPtr<IServiceLocator> serviceLocator); + // <cPlugin> + const char* Version(); + const char* Description(); + const char* MainMenuEntry(); + bool Initialize(); + bool Start(); + void Stop(); + void Housekeeping(); + const char* CommandLineHelp(); + bool ProcessArgs(int argc, char* argv[]); + cOsdObject* MainMenuAction(); + cMenuSetupPage* SetupMenu(); + bool SetupParse(const char* Name, const char* Value); + // </cPlugin> + // <IConfiguration> + const std::string GetCacheDirName() const; + const std::string GetSourcesFileName() const; + int GetMaxCacheAge() const; + void SetMaxCacheAge(const int age); + void SetPlayBackQuality(const StreamType quality); + StreamType GetPlayBackQuality() const; + void SetMediaPlayerType(MediaPlayerType mediaplayerType); + MediaPlayerType GetMediaPlayerType() const; + // </IConfiguration> +}; + +extern "C" void* VDRPluginCreator(); + +#endif // ___VODCATCHERPLUGIN_H diff --git a/src/VodcatcherPluginCommandline_test.cc b/src/VodcatcherPluginCommandline_test.cc new file mode 100644 index 0000000..442c33e --- /dev/null +++ b/src/VodcatcherPluginCommandline_test.cc @@ -0,0 +1,112 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VodcatcherPluginCommandline_test.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <string> +#include <iostream> +#include <cxxtest/TestSuite.h> +#include "VodcatcherPlugin.h" +#include "StderrMock.h" + +using namespace std; + +namespace +{ + +class VodcatcherPluginCommandLineTest : public CxxTest::TestSuite +{ +private: + VodcatcherPlugin* plugin; + StdErrMock* stdErrMock; + +private: + void RedirectStdErr() + { + stdErrMock = new StdErrMock(); + } + + void RestoreStdErr() + { + delete stdErrMock; + } + +public: + void TestCommandLineHelp() + { + VodcatcherPlugin plugin; + TS_ASSERT_EQUALS(string(" -c, --cache=CACHE_DIR specify cache " + "dir, defaults to /var/cache/vdr-plugin-vodcatcher"), + plugin.CommandLineHelp()); + } + + void TestDefaultCommandLineParameters() + { + VodcatcherPlugin plugin; + char first[] = "dummy"; + char* argv[1] = + { first }; + + TS_ASSERT(plugin.ProcessArgs(0, argv)); + TS_ASSERT_EQUALS("/var/cache/vdr-plugin-vodcatcher", plugin.GetCacheDirName()); + } + + void TestWrongCommandLineParameters() + { + VodcatcherPlugin plugin; + StdErrMock stdErrMock; + char first[] = "dummy"; + char second[] = "--foo"; + char* argv[2] = + { first, second }; + + stdErrMock.Expect("dummy: invalid option --foo\n"); + TS_ASSERT(!plugin.ProcessArgs(2, argv)); + stdErrMock.Verify(); + } + + void TestShortCacheDirParameter() + { + VodcatcherPlugin plugin; + char first[] = "dummy"; + char second[] = "-c"; + char third[] = "/foo"; + char* argv[3] = + { first, second, third }; + + TS_ASSERT(plugin.ProcessArgs(3, argv)); + TS_ASSERT_EQUALS("/foo", plugin.GetCacheDirName()); + } + + void TestLongCacheDirParameter() + { + VodcatcherPlugin plugin; + char first[] = "dummy"; + char second[] = "--cache=/foo"; + char* argv[2] = + { first, second }; + + TS_ASSERT(plugin.ProcessArgs(2, argv)); + TS_ASSERT_EQUALS("/foo", plugin.GetCacheDirName()); + } +}; + +} +; diff --git a/src/VodcatcherPlugin_test.cc b/src/VodcatcherPlugin_test.cc new file mode 100644 index 0000000..94a0cd4 --- /dev/null +++ b/src/VodcatcherPlugin_test.cc @@ -0,0 +1,235 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: VodcatcherPlugin_test.cc 7664 2008-08-10 09:53:34Z svntobi $ + * + */ + +#include <cxxtest/TestSuite.h> +#include "vdr-stub/pluginhooks.h" +#include "VodcatcherPlugin.h" +#include "ThreadMock.h" +#include "ServiceLocatorStub.h" +#include "FeedUpdaterMock.h" +#include "IErrorLogger.h" +#include "DownloadCacheMock.h" +#include "vdr-stub/menusetuppagestub.h" + +using namespace std; + +namespace +{ + +class ErrorLoggerStub : public IErrorLogger +{ +public: + string lastLogMessage; + string lastDebugLogMessage; + +public: + // <IErrorLogger> + void LogError(const string& Message) + { + lastLogMessage = Message; + } + + void LogDebug(const string& Message) + { + lastDebugLogMessage = Message; + } + // </IErrorLogger> +}; + +class VodcatcherPluginTest : public CxxTest::TestSuite +{ +private: + VodcatcherPlugin* plugin; + ServiceLocatorStub* serviceLocator; + ErrorLoggerStub errorLoggerStub; + +public: + void setUp() + { + serviceLocator = new ServiceLocatorStub(); + plugin = new VodcatcherPlugin; + plugin->SetServiceLocator(RefPtr<IServiceLocator>(serviceLocator)); + serviceLocator->errorLogger = &errorLoggerStub; + } + + void tearDown() + { + system("rmdir ./notWritable 2>/dev/null"); + delete plugin; + } + + void TestMainMenuEntry() + { + TS_ASSERT_EQUALS(string("i18n:Vodcatcher"), plugin->MainMenuEntry()); + } + + void TestVersion() + { + TS_ASSERT_EQUALS(string("0.2.1"), plugin->Version()); + } + + void TestDescription() + { + TS_ASSERT_EQUALS(string("i18n:Browse and play video podcasts"), + plugin->Description()); + } + + void TestSourcesFileName() + { + char first[] = "dummy"; + char second[] = "--cache=."; + char* argv[2] = + { first, second }; + TS_ASSERT(plugin->ProcessArgs(2, argv)); + TS_ASSERT(plugin->Initialize()); + TS_ASSERT_EQUALS("/vdrstub/vodcatchersources.conf", + plugin->GetSourcesFileName()); + } + + void TestMainMenuAction() + { + cOsdMenu menu(""); + serviceLocator->feedMenu = &menu; + TS_ASSERT_EQUALS(&menu, plugin->MainMenuAction()); + } + + void TestSetupMenuAction() + { + MenuSetupPageStub setupMenu; + serviceLocator->setupMenu = &setupMenu; + TS_ASSERT_EQUALS(&setupMenu, plugin->SetupMenu()); + } + + void TestStartRunsDownloadThreadAndUpdatesFeeds() + { + ThreadMock downloadThread; + FeedUpdaterMock feedUpdater; + + serviceLocator->downloadThread = &downloadThread; + serviceLocator->feedUpdater = &feedUpdater; + + downloadThread.ExpectMethod("Start"); + feedUpdater.ExpectMethod("Update"); + + TS_ASSERT(plugin->Start()); + + downloadThread.Verify(); + feedUpdater.Verify(); + } + + void TestStopStopsDownloadThread() + { + ThreadMock downloadThread; + serviceLocator->downloadThread = &downloadThread; + + downloadThread.ExpectMethod("Stop"); + + plugin->Stop(); + + downloadThread.Verify(); + } + + void TestHousekeepingUpdatesFeeds() + { + FeedUpdaterMock feedUpdater; + serviceLocator->feedUpdater = &feedUpdater; + DownloadCacheMock downloadCache; + serviceLocator->downloadCache = &downloadCache; + + feedUpdater.ExpectMethod("Update"); + plugin->Housekeeping(); + feedUpdater.Verify(); + } + + void TestHousekeepingCleansCache() + { + FeedUpdaterMock feedUpdater; + serviceLocator->feedUpdater = &feedUpdater; + DownloadCacheMock downloadCache; + serviceLocator->downloadCache = &downloadCache; + plugin->SetupParse("MaxCacheAge", "10"); + + downloadCache.ExpectMethod("CleanUp", downloadCache.MakeString(10 * 24)); + plugin->Housekeeping(); + downloadCache.Verify(); + } + + void TestInitializeFailsWithErrorMessageIfCacheDirNotExisting() + { + char first[] = "dummy"; + char second[] = "--cache=/foo/notExisting"; + char* argv[2] = + { first, second }; + errorLoggerStub.lastLogMessage = ""; + + TS_ASSERT(plugin->ProcessArgs(2, argv)); + TS_ASSERT(!plugin->Initialize()); + TS_ASSERT_EQUALS("Unable to access cache directory " + "`/foo/notExisting`", errorLoggerStub.lastLogMessage); + } + + void TestInitializeFailsWithErrorMessageIfCacheDirNotWritable() + { + char first[] = "dummy"; + char second[] = "--cache=./notWritable"; + char* argv[2] = + { first, second }; + system("mkdir ./notWritable"); + system("chmod a-w ./notWritable"); + errorLoggerStub.lastLogMessage = ""; + + TS_ASSERT(plugin->ProcessArgs(2, argv)); + TS_ASSERT(!plugin->Initialize()); + TS_ASSERT_EQUALS("Unable to access cache directory " + "`./notWritable`", errorLoggerStub.lastLogMessage); + } + + void TestSetupParseFailsForUnknownSetting() + { + TS_ASSERT(!plugin->SetupParse("unknown setting", "value")); + } + + void TestDefaultMaxCacheAge() + { + TS_ASSERT_EQUALS(30, plugin->GetMaxCacheAge()); + } + + void TestSetupParseMaxCacheAge() + { + TS_ASSERT(plugin->SetupParse("MaxCacheAge", "10")); + TS_ASSERT_EQUALS(10, plugin->GetMaxCacheAge()); + } + + void TestSetMaxCacheAge() + { + plugin->SetMaxCacheAge(22); + TS_ASSERT_EQUALS(22, plugin->GetMaxCacheAge()); + } + + void TestDefaultPlayBackQuality() + { + TS_ASSERT_EQUALS(High, plugin->GetPlayBackQuality()); + } +}; + +} +; diff --git a/src/XineliboutputPlayer.cc b/src/XineliboutputPlayer.cc new file mode 100644 index 0000000..1c44e13 --- /dev/null +++ b/src/XineliboutputPlayer.cc @@ -0,0 +1,46 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: XineliboutputPlayer.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +using namespace std; + +#include "XineliboutputPlayer.h" + +const char* XineliboutputPlayer::ServiceId = "MediaPlayer-1.0"; + +XineliboutputPlayer::XineliboutputPlayer(IErrorLogger& errorLogger) : + _errorLogger(errorLogger) +{ + +} + +bool XineliboutputPlayer::Play(string url) +{ + _errorLogger.LogDebug("Starting to play " + url); + + if (!cPluginManager::CallFirstService(ServiceId, (void*) url.c_str())) + { + _errorLogger.LogDebug((string)"Failed to locate Xineliboutput MediaPlayer service '" + ServiceId + "'"); + return false; + } + + return true; +} diff --git a/src/XineliboutputPlayer.h b/src/XineliboutputPlayer.h new file mode 100644 index 0000000..2086fbe --- /dev/null +++ b/src/XineliboutputPlayer.h @@ -0,0 +1,43 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: XineliboutputPlayer.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef XINELIBOUTPUTPLAYER_H_ +#define XINELIBOUTPUTPLAYER_H_ + +#include "IMediaPlayer.h" +#include "IErrorLogger.h" +#include <vdr/plugin.h> + +class IErrorLogger; + +class XineliboutputPlayer : public IMediaPlayer +{ +private: + static const char* ServiceId; + IErrorLogger& _errorLogger; + +public: + XineliboutputPlayer(IErrorLogger& errorLogger); + virtual bool Play(std::string url); +}; + +#endif /*XINELIBOUTPUTPLAYER_H_*/ diff --git a/src/vdr-stub/ccontrolstub.cc b/src/vdr-stub/ccontrolstub.cc new file mode 100644 index 0000000..1e53f87 --- /dev/null +++ b/src/vdr-stub/ccontrolstub.cc @@ -0,0 +1,55 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ccontrolstub.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <vdr/player.h> +#include "ccontrolstub.h" +#include <vdr/osdbase.h> + +cControl::cControl(cPlayer *Player, bool Hidden) +{ +} + +cControl::~cControl() +{ +} + +cControlStub::cControlStub() +:cControl(NULL, false) +{ +} + +cControlStub::~cControlStub() +{ +} + +void cControl::Launch(cControl *control) +{ + cControlStub::LaunchedPlayerControl = control; +} + +cOsdObject* cControl::GetInfo(void) +{ + return NULL; +} + +cControl* cControlStub::LaunchedPlayerControl; + diff --git a/src/vdr-stub/ccontrolstub.h b/src/vdr-stub/ccontrolstub.h new file mode 100644 index 0000000..9218cf6 --- /dev/null +++ b/src/vdr-stub/ccontrolstub.h @@ -0,0 +1,39 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: ccontrolstub.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___CCONTROLSTUB_H +#define ___CCONTROLSTUB_H + +#include <vdr/player.h> + +class cControlStub: public cControl +{ + public: + static cControl* LaunchedPlayerControl; + + public: + cControlStub(); + ~cControlStub(); + void Hide() {}; +}; + +#endif diff --git a/src/vdr-stub/i18n.cc b/src/vdr-stub/i18n.cc new file mode 100644 index 0000000..6bab61a --- /dev/null +++ b/src/vdr-stub/i18n.cc @@ -0,0 +1,35 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: i18n.cc 7656 2008-08-09 21:07:45Z svntobi $ + * + */ + +#include <string> +#include <vector> +#include <iostream> + +using namespace std; + +const char *I18nTranslate(const char* s, const char* Plugin) +{ + static vector<string> translations; + translations.push_back(string("i18n:") + s); + + return translations[translations.size()-1].c_str(); +} diff --git a/src/vdr-stub/menuitems.cc b/src/vdr-stub/menuitems.cc new file mode 100644 index 0000000..2550ea5 --- /dev/null +++ b/src/vdr-stub/menuitems.cc @@ -0,0 +1,34 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: menuitems.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <vdr/menuitems.h> + +cMenuSetupPage::cMenuSetupPage() +:cOsdMenu("") +{ +} + +eOSState cMenuSetupPage::ProcessKey(eKeys Key) +{ + return osUnknown; +} + diff --git a/src/vdr-stub/menusetuppagestub.h b/src/vdr-stub/menusetuppagestub.h new file mode 100644 index 0000000..711c470 --- /dev/null +++ b/src/vdr-stub/menusetuppagestub.h @@ -0,0 +1,34 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: menusetuppagestub.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___MENUSETUPPAGESTUB_H +#define ___MENUSETUPPAGESTUB_H + +#include <vdr/menuitems.h> + +class MenuSetupPageStub: public cMenuSetupPage +{ + protected: + void Store() {}; +}; + +#endif diff --git a/src/vdr-stub/osdbase.cc b/src/vdr-stub/osdbase.cc new file mode 100644 index 0000000..31b991d --- /dev/null +++ b/src/vdr-stub/osdbase.cc @@ -0,0 +1,68 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: osdbase.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <vdr/osdbase.h> + +cListBase::cListBase() +{ +} + +cListBase::~cListBase() +{ +} + +void cListBase::Clear() +{ +} + +void cListBase::Move(int a, int b) +{ +} + +cOsdMenu::~cOsdMenu() +{ +} + +cOsdMenu::cOsdMenu(const char* a, int b, int c, int d, int e, int f) +{ +} + +void cOsdMenu::Clear() +{ +} + +void cOsdMenu::Del(int Index) +{ +} + +void cOsdMenu::Display(void) +{ +} + +eOSState cOsdMenu::ProcessKey(eKeys Key) +{ + return osUnknown; +} + +void cOsdObject::Show() +{ +} diff --git a/src/vdr-stub/plugin.cc b/src/vdr-stub/plugin.cc new file mode 100644 index 0000000..f1c481b --- /dev/null +++ b/src/vdr-stub/plugin.cc @@ -0,0 +1,130 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: plugin.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <string> +#include <vdr/plugin.h> +#include "pluginhooks.h" + +using namespace std; + +cPlugin::cPlugin(void) +{ +} + +cPlugin::~cPlugin() +{ +} + +const char* cPlugin::CommandLineHelp(void) +{ + return ""; +} + +bool cPlugin::ProcessArgs(int argCount, char* argValues[]) +{ + return false; +} + +bool cPlugin::Start(void) +{ + return false; +} + +void cPlugin::Stop(void) +{ +} + +void cPlugin::Housekeeping(void) +{ +} + +const char* cPlugin::MainMenuEntry(void) +{ + return ""; +} + +bool cPlugin::Initialize(void) +{ + return false; +} + +cOsdObject* cPlugin::MainMenuAction(void) +{ + return NULL; +} + +cMenuSetupPage* cPlugin::SetupMenu(void) +{ + return NULL; +} + +bool cPlugin::SetupParse(const char* Name, const char* Value) +{ + return false; +} + +bool cPlugin::Service(const char* Id, void* Data) +{ + return false; +} + +const char** cPlugin::SVDRPHelpPages(void) +{ + return NULL; +} + +cString cPlugin::SVDRPCommand(const char* Command, const char* Option, int &ReplyCode) +{ + return ""; +} + +#if APIVERSNUM >= 10507 +void cPlugin::RegisterI18n(const void *) +{ +} +#else +void cPlugin::RegisterI18n(const tI18nPhrase * const Phrases) +{ + PluginHooks::Phrases = Phrases; +} +#endif + +const char* cPlugin::ConfigDirectory(const char* PluginName) +{ + return "/vdrstub"; +} + +void cPlugin::MainThreadHook(void) +{ +} + +cString cPlugin::Active(void) +{ + return NULL; +} + +#if APIVERSNUM >= 10501 +time_t cPlugin::WakeupTime(void) +{ + return 0; +} +#endif diff --git a/src/vdr-stub/pluginhooks.cc b/src/vdr-stub/pluginhooks.cc new file mode 100644 index 0000000..719de81 --- /dev/null +++ b/src/vdr-stub/pluginhooks.cc @@ -0,0 +1,25 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: pluginhooks.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include "pluginhooks.h" + +const tI18nPhrase* PluginHooks::Phrases = NULL; diff --git a/src/vdr-stub/pluginhooks.h b/src/vdr-stub/pluginhooks.h new file mode 100644 index 0000000..4ac8cdd --- /dev/null +++ b/src/vdr-stub/pluginhooks.h @@ -0,0 +1,34 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: pluginhooks.h 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#ifndef ___PLUGINHOOKS_H +#define ___PLUGINHOOKS_H + +#include <vdr/i18n.h> + +class PluginHooks +{ + public: + static const tI18nPhrase* Phrases; +}; + +#endif diff --git a/src/vdr-stub/tools.cc b/src/vdr-stub/tools.cc new file mode 100644 index 0000000..4975303 --- /dev/null +++ b/src/vdr-stub/tools.cc @@ -0,0 +1,42 @@ +/* + * vdr-vodcatcher - A plugin for the Linux Video Disk Recorder + * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: tools.cc 7652 2008-08-05 21:37:57Z svntobi $ + * + */ + +#include <vdr/tools.h> +#include <vdr/config.h> + +cString::cString(const char* S, bool TakePointer) +{ +} + +cString::~cString() +{ +} + +#if APIVERSNUM < 10500 +cTimeMs::cTimeMs() +{ +} +#else +cTimeMs::cTimeMs(int Ms) +{ +} +#endif diff --git a/tinyxml/COPYING b/tinyxml/COPYING new file mode 100644 index 0000000..a6959ca --- /dev/null +++ b/tinyxml/COPYING @@ -0,0 +1,20 @@ +TinyXML is released under the zlib license: + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. diff --git a/tinyxml/tinystr.cpp b/tinyxml/tinystr.cpp new file mode 100644 index 0000000..6812507 --- /dev/null +++ b/tinyxml/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/tinyxml/tinystr.h b/tinyxml/tinystr.h new file mode 100644 index 0000000..3c2aa9d --- /dev/null +++ b/tinyxml/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include <assert.h> +#include <string.h> + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast<size_type>( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast<size_type>( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast<int*>( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/tinyxml/tinyxml.cpp b/tinyxml/tinyxml.cpp new file mode 100644 index 0000000..5de21f6 --- /dev/null +++ b/tinyxml/tinyxml.cpp @@ -0,0 +1,1888 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> + +#ifdef TIXML_USE_STL +#include <sstream> +#include <iostream> +#endif + +#include "tinyxml.h" + + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + + fprintf( cfile, "<%s", value.c_str() ); + + const TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a <foo /> node + // 2) An element with only a text child is printed as <foo> text </foo> + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "</%s>", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i<depth; ++i ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "</%s>", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // <snip> + // <quote> + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // </quote> + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i<depth; i++ ) + { + fprintf( cfile, " " ); + } + fprintf( cfile, "<!--%s-->", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "<?xml " ); + if ( str ) (*str) += "<?xml "; + + if ( !version.empty() ) { + if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ()); + if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; } + } + if ( !encoding.empty() ) { + if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ()); + if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; } + } + if ( !standalone.empty() ) { + if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ()); + if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; } + } + if ( cfile ) fprintf( cfile, "?>" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i<depth; i++ ) + fprintf( cfile, " " ); + fprintf( cfile, "<%s>", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && i<count; + child = child->NextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && i<count; + child = child->NextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && i<count; + child = child->NextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && i<count; + child = child->NextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += "</"; + buffer += element.Value(); + buffer += ">"; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += "<![CDATA["; + buffer += text.Value(); + buffer += "]]>"; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += "<!--"; + buffer += comment.Value(); + buffer += "-->"; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/tinyxml/tinyxml.h b/tinyxml/tinyxml.h new file mode 100644 index 0000000..c6f40cc --- /dev/null +++ b/tinyxml/tinyxml.h @@ -0,0 +1,1802 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include <string> + #include <iostream> + #include <sstream> + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, <b>no children of this node or its sibilings</b> will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + <foo>This is text</foo> + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + <foo><b>This is text</b></foo> + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + <foo>This is <b>text</b></foo> + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + <?xml version="1.0" standalone="yes"?> + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + <Document> + <Element attributeA = "valueA"> + <Child attributeB = "value1" /> + <Child attributeB = "value2" /> + </Element> + <Document> + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i<depth; ++i ) + buffer += indent; + } + void DoLineBreak() { + buffer += lineBreak; + } + + int depth; + bool simpleTextPrint; + TIXML_STRING buffer; + TIXML_STRING indent; + TIXML_STRING lineBreak; +}; + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/tinyxml/tinyxmlerror.cpp b/tinyxml/tinyxmlerror.cpp new file mode 100644 index 0000000..d24f63b --- /dev/null +++ b/tinyxml/tinyxmlerror.cpp @@ -0,0 +1,53 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + +// The goal of the seperate error file is to make the first +// step towards localization. tinyxml (currently) only supports +// english error messages, but the could now be translated. +// +// It also cleans up the code a bit. +// + +const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] = +{ + "No error", + "Error", + "Failed to open file", + "Memory allocation failed.", + "Error parsing Element.", + "Failed to read Element name", + "Error reading Element value.", + "Error reading Attributes.", + "Error: empty tag.", + "Error reading end tag.", + "Error parsing Unknown.", + "Error parsing Comment.", + "Error parsing Declaration.", + "Error document empty.", + "Error null (0) or unexpected EOF found in input stream.", + "Error parsing CDATA.", + "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", +}; diff --git a/tinyxml/tinyxmlparser.cpp b/tinyxml/tinyxmlparser.cpp new file mode 100644 index 0000000..cf123c1 --- /dev/null +++ b/tinyxml/tinyxmlparser.cpp @@ -0,0 +1,1638 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> +#include <stddef.h> + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include <windows.h> +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && (IsWhiteSpace( *p ) || *p == '\n' || *p =='\r') ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; i<NUM_ENTITY; ++i ) + { + if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 ) + { + assert( strlen( entity[i].str ) == entity[i].strLength ); + *value = entity[i].chr; + *length = 1; + return ( p + entity[i].strLength ); + } + } + + // So it wasn't an entity, its unrecognized, or something like that. + *value = *p; // Don't put back the last one, since we return it! + //*length = 1; // Leave unrecognized entities - this doesn't really work. + // Just writes strange XML. + return p+1; +} + + +bool TiXmlBase::StringEqual( const char* p, + const char* tag, + bool ignoreCase, + TiXmlEncoding encoding ) +{ + assert( p ); + assert( tag ); + if ( !p || !*p ) + { + assert( 0 ); + return false; + } + + const char* q = p; + + if ( ignoreCase ) + { + while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) + return true; + } + else + { + while ( *q && *tag && *q == *tag ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) // Have we found the end of the tag, and everything equal? + return true; + } + return false; +} + +const char* TiXmlBase::ReadText( const char* p, + TIXML_STRING * text, + bool trimWhiteSpace, + const char* endTag, + bool caseInsensitive, + TiXmlEncoding encoding ) +{ + *text = ""; + if ( !trimWhiteSpace // certain tags always keep whitespace + || !condenseWhiteSpace ) // if true, whitespace is always kept + { + // Keep all the white space. + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) + ) + { + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + text->append( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: <!-- + // - Decleration: <?xml + // - Everthing else is unknown to tinyxml. + // + + const char* xmlHeader = { "<?xml" }; + const char* commentHeader = { "<!--" }; + const char* dtdHeader = { "<!" }; + const char* cdataHeader = { "<![CDATA[" }; + + if ( StringEqual( p, xmlHeader, true, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Declaration\n" ); + #endif + returnNode = new TiXmlDeclaration(); + } + else if ( StringEqual( p, commentHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Comment\n" ); + #endif + returnNode = new TiXmlComment(); + } + else if ( StringEqual( p, cdataHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing CDATA\n" ); + #endif + TiXmlText* text = new TiXmlText( "" ); + text->SetCDATA( true ); + returnNode = text; + } + else if ( StringEqual( p, dtdHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(1)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + else if ( IsAlpha( *(p+1), encoding ) + || *(p+1) == '_' ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Element\n" ); + #endif + returnNode = new TiXmlElement( "" ); + } + else + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(2)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + + if ( returnNode ) + { + // Set the parent, so it can report errors + returnNode->parent = this; + } + else + { + if ( doc ) + doc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } + return returnNode; +} + +#ifdef TIXML_USE_STL + +void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag) +{ + // We're called with some amount of pre-parsing. That is, some of "this" + // element is in "tag". Go ahead and stream to the closing ">" + while( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c ; + + if ( c == '>' ) + break; + } + + if ( tag->length() < 3 ) return; + + // Okay...if we are a "/>" tag, then we're done. We've read a complete tag. + // If not, identify and stream. + + if ( tag->at( tag->length() - 1 ) == '>' + && tag->at( tag->length() - 2 ) == '/' ) + { + // All good! + return; + } + else if ( tag->at( tag->length() - 1 ) == '>' ) + { + // There is more. Could be: + // text + // cdata text (which looks like another node) + // closing tag + // another node. + for ( ;; ) + { + StreamWhiteSpace( in, tag ); + + // Do we have text? + if ( in->good() && in->peek() != '<' ) + { + // Yep, text. + TiXmlText text( "" ); + text.StreamIn( in, tag ); + + // What follows text is a closing tag or another node. + // Go around again and figure it out. + continue; + } + + // We now have either a closing tag...or another node. + // We should be at a "<", regardless. + if ( !in->good() ) return; + assert( in->peek() == '<' ); + int tagIndex = (int) tag->length(); + + bool closingTag = false; + bool firstCharFound = false; + + for( ;; ) + { + if ( !in->good() ) + return; + + int c = in->peek(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + if ( c == '>' ) + break; + + *tag += (char) c; + in->get(); + + // Early out if we find the CDATA id. + if ( c == '[' && tag->size() >= 9 ) + { + size_t len = tag->size(); + const char* start = tag->c_str() + len - 9; + if ( strcmp( start, "<![CDATA[" ) == 0 ) { + assert( !closingTag ); + break; + } + } + + if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) ) + { + firstCharFound = true; + if ( c == '/' ) + closingTag = true; + } + } + // If it was a closing tag, then read in the closing '>' to clean up the input stream. + // If it was not, the streaming will be done by the tag. + if ( closingTag ) + { + if ( !in->good() ) + return; + + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + assert( c == '>' ); + *tag += (char) c; + + // We are done, once we've found our closing tag. + return; + } + else + { + // If not a closing tag, id it, and stream. + const char* tagloc = tag->c_str() + tagIndex; + TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING ); + if ( !node ) + return; + node->StreamIn( in, tag ); + delete node; + node = 0; + + // No return: go around from the beginning: text, closing tag, or node. + } + } + } +} +#endif + +const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + TiXmlDocument* document = GetDocument(); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding ); + return 0; + } + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + if ( *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding ); + return 0; + } + + p = SkipWhiteSpace( p+1, encoding ); + + // Read the name. + const char* pErr = p; + + p = ReadName( p, &value, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding ); + return 0; + } + + TIXML_STRING endTag ("</"); + endTag += value; + endTag += ">"; + + // Check for and read attributes. Also look for an empty + // tag or an end tag. + while ( p && *p ) + { + pErr = p; + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + if ( *p == '/' ) + { + ++p; + // Empty tag. + if ( *p != '>' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding ); + return 0; + } + return (p+1); + } + else if ( *p == '>' ) + { + // Done with attributes (if there were any.) + // Read the value -- which can include other + // elements -- read the end tag, and return. + ++p; + p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens. + if ( !p || !*p ) { + // We were looking for the end tag, but found nothing. + // Fix for [ 1663758 ] Failure to report error on bad XML + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + + // We should find the end tag now + if ( StringEqual( p, endTag.c_str(), false, encoding ) ) + { + p += endTag.length(); + return p; + } + else + { + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + } + else + { + // Try to read an attribute: + TiXmlAttribute* attrib = new TiXmlAttribute(); + if ( !attrib ) + { + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding ); + return 0; + } + + attrib->SetDocument( document ); + pErr = p; + p = attrib->Parse( p, data, encoding ); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + // Handle the strange case of double attributes: + #ifdef TIXML_USE_STL + TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() ); + #else + TiXmlAttribute* node = attributeSet.Find( attrib->Name() ); + #endif + if ( node ) + { + node->SetValue( attrib->Value() ); + delete attrib; + return 0; + } + + attributeSet.Add( attrib ); + } + } + return p; +} + + +const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + + // Read in text and elements in any order. + const char* pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + + while ( p && *p ) + { + if ( *p != '<' ) + { + // Take what we have, make a text element. + TiXmlText* textNode = new TiXmlText( "" ); + + if ( !textNode ) + { + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding ); + return 0; + } + + if ( TiXmlBase::IsWhiteSpaceCondensed() ) + { + p = textNode->Parse( p, data, encoding ); + } + else + { + // Special case: we want to keep the white space + // so that leading spaces aren't removed. + p = textNode->Parse( pWithWhiteSpace, data, encoding ); + } + + if ( !textNode->Blank() ) + LinkEndChild( textNode ); + else + delete textNode; + } + else + { + // We hit a '<' + // Have we hit a new element or an end tag? This could also be + // a TiXmlText in the "CDATA" style. + if ( StringEqual( p, "</", false, encoding ) ) + { + return p; + } + else + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, data, encoding ); + LinkEndChild( node ); + } + else + { + return 0; + } + } + } + pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + } + + if ( !p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding ); + } + return p; +} + + +#ifdef TIXML_USE_STL +void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + if ( !p || !*p || *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding ); + return 0; + } + ++p; + value = ""; + + while ( p && *p && *p != '>' ) + { + value += *p; + ++p; + } + + if ( !p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding ); + } + if ( *p == '>' ) + return p+1; + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + + if ( c == '>' + && tag->at( tag->length() - 2 ) == '-' + && tag->at( tag->length() - 3 ) == '-' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + value = ""; + + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + const char* startTag = "<!--"; + const char* endTag = "-->"; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + <!-- declarations for <head> & <body> --> + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = "<![CDATA["; + const char* const endTag = "]]>"; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i<value.length(); i++ ) + if ( !IsWhiteSpace( value[i] ) ) + return false; + return true; +} + |