summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoretobi <git@e-tobi.net>2009-11-03 19:02:47 +0100
committeretobi <git@e-tobi.net>2009-11-03 19:02:47 +0100
commit90b872ed164d8cf004b40f0128fd7a9b3ca7d27e (patch)
treeb558cc69a5f750c98b90d4aac228d9d031d2316a
downloadvdr-plugin-vodcatcher-90b872ed164d8cf004b40f0128fd7a9b3ca7d27e.tar.gz
vdr-plugin-vodcatcher-90b872ed164d8cf004b40f0128fd7a9b3ca7d27e.tar.bz2
initial commit
-rw-r--r--COPYING339
-rw-r--r--CREDITS21
-rw-r--r--HISTORY25
-rw-r--r--Makefile175
-rw-r--r--README135
-rw-r--r--cxxtest/Descriptions.cpp58
-rw-r--r--cxxtest/Descriptions.h74
-rw-r--r--cxxtest/DummyDescriptions.cpp49
-rw-r--r--cxxtest/DummyDescriptions.h76
-rw-r--r--cxxtest/ErrorFormatter.h281
-rw-r--r--cxxtest/ErrorPrinter.h55
-rw-r--r--cxxtest/Flags.h121
-rw-r--r--cxxtest/GlobalFixture.cpp23
-rw-r--r--cxxtest/GlobalFixture.h30
-rw-r--r--cxxtest/Gui.h178
-rw-r--r--cxxtest/LinkedList.cpp172
-rw-r--r--cxxtest/LinkedList.h65
-rw-r--r--cxxtest/Mock.h350
-rw-r--r--cxxtest/ParenPrinter.h21
-rw-r--r--cxxtest/QtGui.h271
-rw-r--r--cxxtest/RealDescriptions.cpp311
-rw-r--r--cxxtest/RealDescriptions.h223
-rw-r--r--cxxtest/Root.cpp18
-rw-r--r--cxxtest/SelfTest.h7
-rw-r--r--cxxtest/StdHeaders.h25
-rw-r--r--cxxtest/StdValueTraits.h229
-rw-r--r--cxxtest/StdioFilePrinter.h41
-rw-r--r--cxxtest/StdioPrinter.h22
-rw-r--r--cxxtest/TeeListener.h182
-rw-r--r--cxxtest/TestListener.h70
-rw-r--r--cxxtest/TestRunner.h125
-rw-r--r--cxxtest/TestSuite.cpp138
-rw-r--r--cxxtest/TestSuite.h512
-rw-r--r--cxxtest/TestTracker.cpp248
-rw-r--r--cxxtest/TestTracker.h114
-rw-r--r--cxxtest/ValueTraits.cpp140
-rw-r--r--cxxtest/ValueTraits.h377
-rw-r--r--cxxtest/Win32Gui.h531
-rw-r--r--cxxtest/X11Gui.h327
-rw-r--r--cxxtest/YesNoRunner.h29
-rwxr-xr-xcxxtestgen597
-rw-r--r--examples/vodcatchersources.conf21
-rw-r--r--po/de_DE.po60
-rw-r--r--po/it_IT.po61
-rw-r--r--po/vodcatcher.pot59
-rw-r--r--sources.mk59
-rw-r--r--src/ConfigurationStub.h68
-rw-r--r--src/CriticalSection.h36
-rw-r--r--src/CurlDownloader.cc82
-rw-r--r--src/CurlDownloader.h44
-rw-r--r--src/CurlDownloader_test.cc97
-rw-r--r--src/Download.cc35
-rw-r--r--src/Download.h40
-rw-r--r--src/DownloadAction.cc48
-rw-r--r--src/DownloadAction.h44
-rw-r--r--src/DownloadAction_test.cc98
-rw-r--r--src/DownloadCacheMock.cc55
-rw-r--r--src/DownloadCacheMock.h47
-rw-r--r--src/DownloadObserver.h35
-rw-r--r--src/DownloadPoolMock.cc67
-rw-r--r--src/DownloadPoolMock.h48
-rw-r--r--src/DownloadQueue.cc91
-rw-r--r--src/DownloadQueue.h48
-rw-r--r--src/DownloadQueue_test.cc105
-rw-r--r--src/Download_test.cc41
-rw-r--r--src/Feed.cc66
-rw-r--r--src/Feed.h51
-rw-r--r--src/FeedMenuController.cc72
-rw-r--r--src/FeedMenuController.h51
-rw-r--r--src/FeedMenuController_test.cc145
-rw-r--r--src/FeedRepository.cc79
-rw-r--r--src/FeedRepository.h54
-rw-r--r--src/FeedRepository_test.cc213
-rw-r--r--src/FeedUpdaterImpl.cc50
-rw-r--r--src/FeedUpdaterImpl.h48
-rw-r--r--src/FeedUpdaterImpl_test.cc110
-rw-r--r--src/FeedUpdaterMock.cc28
-rw-r--r--src/FeedUpdaterMock.h37
-rw-r--r--src/Feed_test.cc65
-rw-r--r--src/FeedsConfigFile.cc60
-rw-r--r--src/FeedsConfigFile.h41
-rw-r--r--src/FeedsConfigFile_test.cc88
-rw-r--r--src/HtmlToText.cc63
-rw-r--r--src/HtmlToText.h34
-rw-r--r--src/HtmlToText_test.cc50
-rw-r--r--src/IConfiguration.h51
-rw-r--r--src/IDownloadCache.h43
-rw-r--r--src/IDownloadPool.h44
-rw-r--r--src/IDownloader.h37
-rw-r--r--src/IErrorLogger.h38
-rw-r--r--src/IFeedParser.h37
-rw-r--r--src/IFeedRepository.h38
-rw-r--r--src/IFeedSources.h41
-rw-r--r--src/IFeedUpdater.h35
-rw-r--r--src/IItemHelpButtonsController.h34
-rw-r--r--src/IListMenu.h44
-rw-r--r--src/IListMenuPresenter.h40
-rw-r--r--src/IMediaPlayer.h35
-rw-r--r--src/IMenu.h40
-rw-r--r--src/IMenuFactory.h38
-rw-r--r--src/IServiceLocator.h65
-rw-r--r--src/IVdrInterface.h38
-rw-r--r--src/Item.cc92
-rw-r--r--src/Item.h62
-rw-r--r--src/ItemMenuPresenter.cc218
-rw-r--r--src/ItemMenuPresenter.h72
-rw-r--r--src/ItemMenuPresenter_test.cc312
-rw-r--r--src/ItemView.h39
-rw-r--r--src/ItemViewPresenter.cc87
-rw-r--r--src/ItemViewPresenter.h49
-rw-r--r--src/ItemViewPresenter_test.cc202
-rw-r--r--src/Item_test.cc169
-rw-r--r--src/ListMenuMock.cc90
-rw-r--r--src/ListMenuMock.h58
-rw-r--r--src/LocalFileCache.cc121
-rw-r--r--src/LocalFileCache.h50
-rw-r--r--src/LocalFileCache_test.cc160
-rw-r--r--src/Menu.h38
-rw-r--r--src/MplayerPlugin.cc65
-rw-r--r--src/MplayerPlugin.h50
-rw-r--r--src/OsdItemView.cc92
-rw-r--r--src/OsdItemView.h58
-rw-r--r--src/OsdListMenu.cc112
-rw-r--r--src/OsdListMenu.h66
-rw-r--r--src/OsdSetupMenu.cc43
-rw-r--r--src/OsdSetupMenu.h45
-rw-r--r--src/PluginCreator.cc34
-rw-r--r--src/Queue_test.cc51
-rw-r--r--src/RefPtr.h144
-rw-r--r--src/RefPtr_test.cc227
-rw-r--r--src/Rfc822DateTime.cc121
-rw-r--r--src/Rfc822DateTime.h47
-rw-r--r--src/Rfc822DateTime_test.cc149
-rw-r--r--src/RssFeedParser.cc180
-rw-r--r--src/RssFeedParser.h58
-rw-r--r--src/RssFeedParser_test.cc338
-rw-r--r--src/SdbmHashCalculator.cc43
-rw-r--r--src/SdbmHashCalculator.h34
-rw-r--r--src/SdbmHashCalculator_test.cc43
-rw-r--r--src/ServiceLocatorImpl.cc199
-rw-r--r--src/ServiceLocatorImpl.h75
-rw-r--r--src/ServiceLocatorStub.cc103
-rw-r--r--src/ServiceLocatorStub.h69
-rw-r--r--src/Sleeper.h36
-rw-r--r--src/SleeperMock.cc44
-rw-r--r--src/SleeperMock.h40
-rw-r--r--src/StderrMock.cc49
-rw-r--r--src/StderrMock.h44
-rw-r--r--src/StreamType.cc17
-rw-r--r--src/StreamType.h38
-rw-r--r--src/StreamType_test.cc15
-rw-r--r--src/StringMessageMock.cc154
-rw-r--r--src/StringMessageMock.h68
-rw-r--r--src/SynchedDownloadPool.cc61
-rw-r--r--src/SynchedDownloadPool.h47
-rw-r--r--src/SynchedDownloadPool_test.cc101
-rw-r--r--src/SyslogErrorLogger.cc36
-rw-r--r--src/SyslogErrorLogger.h37
-rw-r--r--src/Thread.h37
-rw-r--r--src/ThreadAction.h35
-rw-r--r--src/ThreadMock.cc52
-rw-r--r--src/ThreadMock.h44
-rw-r--r--src/ThreadsafeDownloadPool.cc66
-rw-r--r--src/ThreadsafeDownloadPool.h47
-rw-r--r--src/ThreadsafeDownloadPool_test.cc133
-rw-r--r--src/VdrCriticalSection.cc33
-rw-r--r--src/VdrCriticalSection.h39
-rw-r--r--src/VdrInterface.cc36
-rw-r--r--src/VdrInterface.h35
-rw-r--r--src/VdrSleeper.cc33
-rw-r--r--src/VdrSleeper.h41
-rw-r--r--src/VdrThread.cc60
-rw-r--r--src/VdrThread.h52
-rw-r--r--src/Version.h28
-rw-r--r--src/VodcatcherPlugin.cc212
-rw-r--r--src/VodcatcherPlugin.h76
-rw-r--r--src/VodcatcherPluginCommandline_test.cc112
-rw-r--r--src/VodcatcherPlugin_test.cc235
-rw-r--r--src/XineliboutputPlayer.cc46
-rw-r--r--src/XineliboutputPlayer.h43
-rw-r--r--src/vdr-stub/ccontrolstub.cc55
-rw-r--r--src/vdr-stub/ccontrolstub.h39
-rw-r--r--src/vdr-stub/i18n.cc35
-rw-r--r--src/vdr-stub/menuitems.cc34
-rw-r--r--src/vdr-stub/menusetuppagestub.h34
-rw-r--r--src/vdr-stub/osdbase.cc68
-rw-r--r--src/vdr-stub/plugin.cc130
-rw-r--r--src/vdr-stub/pluginhooks.cc25
-rw-r--r--src/vdr-stub/pluginhooks.h34
-rw-r--r--src/vdr-stub/tools.cc42
-rw-r--r--tinyxml/COPYING20
-rw-r--r--tinyxml/tinystr.cpp116
-rw-r--r--tinyxml/tinystr.h319
-rw-r--r--tinyxml/tinyxml.cpp1888
-rw-r--r--tinyxml/tinyxml.h1802
-rw-r--r--tinyxml/tinyxmlerror.cpp53
-rw-r--r--tinyxml/tinyxmlparser.cpp1638
197 files changed, 23156 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..3420205
--- /dev/null
+++ b/CREDITS
@@ -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
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..c461e36
--- /dev/null
+++ b/HISTORY
@@ -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
diff --git a/README b/README
new file mode 100644
index 0000000..c2d6773
--- /dev/null
+++ b/README
@@ -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 &current(); \
+ \
+ 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 &current(); \
+ \
+ 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.
+ // &#xA9; -- 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 ] =
+{
+ { "&amp;", 5, '&' },
+ { "&lt;", 4, '<' },
+ { "&gt;", 4, '>' },
+ { "&quot;", 6, '\"' },
+ { "&apos;", 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;
+}
+