summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE340
-rw-r--r--build.xml19
-rw-r--r--etc/MANIFEST2
-rw-r--r--etc/build.properties64
-rw-r--r--etc/checkstyle/config.xml178
-rw-r--r--etc/global.xml107
-rw-r--r--etc/junit/xsl/junit-frames.xsl716
-rw-r--r--etc/junit/xsl/junit-noframes.xsl461
-rw-r--r--etc/targets/global-checkstyle.xml23
-rw-r--r--etc/targets/global-clean.xml8
-rw-r--r--etc/targets/global-cobertura.xml35
-rw-r--r--etc/targets/global-compile.xml35
-rw-r--r--etc/targets/global-findbugs.xml47
-rw-r--r--etc/targets/global-inject.xml20
-rw-r--r--etc/targets/global-jar.xml35
-rw-r--r--etc/targets/global-javadoc.xml53
-rw-r--r--etc/targets/global-junit.xml99
-rw-r--r--etc/targets/global-tar.xml84
-rw-r--r--etc/targets/global-taskdefs.xml65
-rw-r--r--etc/targets/global-verify.xml23
-rw-r--r--etc/targets/global-vpp.xml32
-rw-r--r--lib/ant/ant-contrib.jarbin0 -> 196135 bytes
-rw-r--r--lib/checkstyle/checkstyle-all-4.1.jarbin0 -> 1261097 bytes
-rw-r--r--lib/checkstyle/commons-beanutils-core.jarbin0 -> 168760 bytes
-rw-r--r--lib/checkstyle/commons-cli.jarbin0 -> 30117 bytes
-rw-r--r--lib/checkstyle/commons-collections.jarbin0 -> 165119 bytes
-rw-r--r--lib/checkstyle/commons-logging.jarbin0 -> 31605 bytes
-rw-r--r--lib/cobertura/asm-2.2.1.jarbin0 -> 34783 bytes
-rw-r--r--lib/cobertura/cobertura.jarbin0 -> 176075 bytes
-rw-r--r--lib/cobertura/jakarta-oro-2.0.8.jarbin0 -> 65261 bytes
-rw-r--r--lib/findbugs/lib/annotations.jarbin0 -> 10626 bytes
-rw-r--r--lib/findbugs/lib/asm-3.0_RC1.jarbin0 -> 42077 bytes
-rw-r--r--lib/findbugs/lib/asm-analysis-3.0_RC1.jarbin0 -> 17321 bytes
-rw-r--r--lib/findbugs/lib/asm-commons-3.0_RC1.jarbin0 -> 26053 bytes
-rw-r--r--lib/findbugs/lib/asm-tree-3.0_RC1.jarbin0 -> 21011 bytes
-rw-r--r--lib/findbugs/lib/asm-util-3.0_RC1.jarbin0 -> 32049 bytes
-rw-r--r--lib/findbugs/lib/asm-xml-3.0_RC1.jarbin0 -> 51245 bytes
-rw-r--r--lib/findbugs/lib/bcel.jarbin0 -> 646419 bytes
-rw-r--r--lib/findbugs/lib/dom4j-full.jarbin0 -> 513409 bytes
-rw-r--r--lib/findbugs/lib/findbugs-ant.jarbin0 -> 10839 bytes
-rw-r--r--lib/findbugs/lib/findbugs.jarbin0 -> 1234001 bytes
-rw-r--r--lib/findbugs/lib/findbugsGUI.jarbin0 -> 517946 bytes
-rw-r--r--lib/findbugs/plugin/coreplugin.jarbin0 -> 529856 bytes
-rw-r--r--lib/findbugs/xsl/default.xsl284
-rw-r--r--lib/findbugs/xsl/fancy.xsl743
-rw-r--r--lib/findbugs/xsl/plain.xsl202
-rw-r--r--lib/findbugs/xsl/summary.xsl240
-rw-r--r--lib/jrat/bcel-326809.jarbin0 -> 532700 bytes
-rw-r--r--lib/jrat/jmxri.jarbin0 -> 309048 bytes
-rw-r--r--lib/jrat/shiftone-arbor.jarbin0 -> 45630 bytes
-rw-r--r--lib/jrat/shiftone-jrat.jarbin0 -> 421539 bytes
-rw-r--r--lib/junit/junit.jarbin0 -> 187676 bytes
-rw-r--r--lib/log4j/log4j-1.2.13.jarbin0 -> 358180 bytes
-rw-r--r--lib/svn/commons-lang-2.0.jarbin0 -> 169763 bytes
-rw-r--r--lib/svn/jakarta-regexp-1.3.jarbin0 -> 25429 bytes
-rw-r--r--lib/svn/svnClientAdapter.jarbin0 -> 175111 bytes
-rw-r--r--lib/svn/svnant.jarbin0 -> 44028 bytes
-rw-r--r--lib/svn/svnjavahl.jarbin0 -> 42210 bytes
-rw-r--r--lib/vpp/commons-collections-3.1.jarbin0 -> 559366 bytes
-rw-r--r--lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jarbin0 -> 26463 bytes
-rw-r--r--lib/vpp/velocity-1.4.jarbin0 -> 361173 bytes
-rw-r--r--src/input/test.c61
-rw-r--r--src/input/test0.h7
-rw-r--r--src/input/test1.h7
-rw-r--r--src/java/org/anarres/cpp/Argument.java79
-rw-r--r--src/java/org/anarres/cpp/CppReader.java147
-rw-r--r--src/java/org/anarres/cpp/FileLexerSource.java74
-rw-r--r--src/java/org/anarres/cpp/FixedTokenSource.java67
-rw-r--r--src/java/org/anarres/cpp/InternalException.java33
-rw-r--r--src/java/org/anarres/cpp/JoinReader.java168
-rw-r--r--src/java/org/anarres/cpp/LexerException.java35
-rw-r--r--src/java/org/anarres/cpp/LexerSource.java677
-rw-r--r--src/java/org/anarres/cpp/Macro.java157
-rw-r--r--src/java/org/anarres/cpp/MacroTokenSource.java191
-rw-r--r--src/java/org/anarres/cpp/Main.java111
-rw-r--r--src/java/org/anarres/cpp/Preprocessor.java1511
-rw-r--r--src/java/org/anarres/cpp/PreprocessorListener.java83
-rw-r--r--src/java/org/anarres/cpp/Source.java226
-rw-r--r--src/java/org/anarres/cpp/SourceIterator.java94
-rw-r--r--src/java/org/anarres/cpp/State.java69
-rw-r--r--src/java/org/anarres/cpp/StringLexerSource.java64
-rw-r--r--src/java/org/anarres/cpp/Token.java215
-rw-r--r--src/java/org/anarres/cpp/TokenSnifferSource.java56
-rw-r--r--src/resources/log4j.properties22
-rw-r--r--src/scripts/cpp.sh32
-rw-r--r--src/scripts/release.sh4
-rw-r--r--src/tests/AutoTestSuite.java121
-rw-r--r--src/tests/org/anarres/cpp/BaseTestCase.java6
-rw-r--r--src/tests/org/anarres/cpp/CppReaderTestCase.java34
-rw-r--r--src/tests/org/anarres/cpp/ErrorTestCase.java50
-rw-r--r--src/tests/org/anarres/cpp/JoinReaderTestCase.java40
-rw-r--r--src/tests/org/anarres/cpp/LexerSourceTestCase.java43
-rw-r--r--src/tests/org/anarres/cpp/PreprocessorTestCase.java154
93 files changed, 8553 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, 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 Library 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 St, 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 Library General
+Public License instead of this License.
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..728c55e
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="jcpp" default="all" basedir=".">
+ <dirname property="global.dir.root" file="${ant.file}"/>
+ <property name="global.dir.etc" value="${global.dir.root}/etc"/>
+ <import file="${global.dir.etc}/global.xml" />
+
+ <target name="clean" depends="global-clean" />
+ <target name="compile" depends="global-compile" />
+ <target name="all" depends="global-tar" />
+
+
+ <target name="junit" depends="global-junit" />
+ <target name="test" depends="global-junit" />
+ <!-- global-findbugs" -->
+
+ <target name="findbugs" depends="global-findbugs,global-findbugs-gui" />
+
+</project>
diff --git a/etc/MANIFEST b/etc/MANIFEST
new file mode 100644
index 0000000..95dd104
--- /dev/null
+++ b/etc/MANIFEST
@@ -0,0 +1,2 @@
+Class-Path: log4j.jar dnsjava-2.0.0.jar
+Main-Class: org.anarres.iengine.Main
diff --git a/etc/build.properties b/etc/build.properties
new file mode 100644
index 0000000..5b54165
--- /dev/null
+++ b/etc/build.properties
@@ -0,0 +1,64 @@
+global.version = 1.0.8
+global.name = anarres-cpp
+
+global.dir.arch = ${global.dir.root}/arch
+global.dir.etc = ${global.dir.root}/etc
+global.dir.lib = ${global.dir.root}/lib
+global.dir.tmp = ${global.dir.root}/build/tmp
+
+global.dir.licenses = ${global.dir.etc}/licenses
+
+global.dir.src = ${global.dir.root}/src
+global.dir.src.docs = ${global.dir.root}/src/docs
+global.dir.src.java = ${global.dir.root}/src/java
+global.dir.src.resources = ${global.dir.root}/src/resources
+global.dir.src.scripts = ${global.dir.root}/src/scripts
+global.dir.src.sql = ${global.dir.root}/src/sql
+global.dir.src.tests = ${global.dir.root}/src/tests
+global.dir.src.tools = ${global.dir.root}/src/tools
+
+# XXX Add hacks.
+
+global.dir.build = ${global.dir.root}/build
+global.dir.build.classes = ${global.dir.root}/build/classes
+global.dir.build.cobertura = ${global.dir.root}/build/cobertura
+global.dir.build.depcache = ${global.dir.root}/build/depcache
+global.dir.build.dist = ${global.dir.root}/build/dist
+global.dir.build.docs = ${global.dir.root}/build/docs
+global.dir.build.hbm = ${global.dir.root}/build/hbm
+global.dir.build.jar = ${global.dir.root}/build/jar
+global.dir.build.java = ${global.dir.root}/build/java
+global.dir.build.javadoc = ${global.dir.root}/build/javadoc
+global.dir.build.lib = ${global.dir.root}/build/lib
+global.dir.build.reports = ${global.dir.root}/build/reports
+global.dir.build.sql = ${global.dir.root}/build/sql
+global.dir.build.tar = ${global.dir.root}/build/tar
+global.dir.build.test-xml = ${global.dir.root}/build/test-xml
+global.dir.build.tests = ${global.dir.root}/build/tests
+global.dir.build.tools = ${global.dir.root}/build/tools
+global.dir.build.wsdd = ${global.dir.root}/build/wsdd
+global.dir.build.wsdl = ${global.dir.root}/build/wsdl
+global.dir.build.xml = ${global.dir.root}/build/xml
+
+global.file.jar.name = ${global.name}.jar
+global.file.jar = ${global.dir.build.tar}/lib/${global.file.jar.name}
+global.file.bintar.name = ${global.name}-bin-${global.version}.tar.gz
+global.file.bintar = ${global.dir.build.dist}/${global.file.bintar.name}
+global.file.srctar.name = ${global.name}-src-${global.version}.tar.gz
+global.file.srctar = ${global.dir.build.dist}/${global.file.srctar.name}
+
+build.compiler = modern
+system.javac.optimize = true
+system.javac.debug = true
+system.javac.source = 1.5
+system.javac.target = ${system.javac.source}
+system.javac.deprecation = true
+
+# system.javadoc.access = package
+system.javadoc.access = protected
+system.javadoc.source = ${system.javac.source}
+system.javadoc.offline = false
+
+system.jar.compress = true
+
+wsdl.server.http.url = http://localhost:8080/axis
diff --git a/etc/checkstyle/config.xml b/etc/checkstyle/config.xml
new file mode 100644
index 0000000..07ff52a
--- /dev/null
+++ b/etc/checkstyle/config.xml
@@ -0,0 +1,178 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+ "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
+ "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
+
+<!--
+
+ Checkstyle configuration that checks the sun coding conventions from:
+
+ - the Java Language Specification at
+ http://java.sun.com/docs/books/jls/second_edition/html/index.html
+
+ - the Sun Code Conventions at http://java.sun.com/docs/codeconv/
+
+ - the Javadoc guidelines at
+ http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
+
+ - the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
+
+ - some best practices
+
+ Checkstyle is very configurable. Be sure to read the documentation at
+ http://checkstyle.sf.net (or in your downloaded distribution).
+
+ Most Checks are configurable, be sure to consult the documentation.
+
+ To completely disable a check, just comment it out or delete it from the file.
+
+ Finally, it is worth reading the documentation.
+
+-->
+<!--
+ Modified for karmaphere use
+-->
+
+<module name="Checker">
+
+ <!-- Checks that a package.html file exists for each package. -->
+ <!-- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -->
+ <!-- Currently too numerous, disabled -->
+ <!--module name="PackageHtml"/-->
+
+ <!-- Checks whether files end with a new line. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
+ <module name="NewlineAtEndOfFile"/>
+
+ <!-- Checks that property files contain the same keys. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
+ <module name="Translation"/>
+
+
+ <module name="TreeWalker">
+
+ <!-- Checks for Javadoc comments. -->
+ <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+ <!-- Currently too numerous -->
+ <!--module name="JavadocMethod"/-->
+ <!--module name="JavadocType"/-->
+ <!--module name="JavadocVariable"/-->
+ <!--module name="JavadocStyle"/-->
+
+
+ <!-- Checks for Naming Conventions. -->
+ <!-- See http://checkstyle.sf.net/config_naming.html -->
+ <module name="ConstantName"/>
+ <module name="LocalFinalVariableName"/>
+ <module name="LocalVariableName"/>
+ <module name="MemberName"/>
+ <module name="MethodName"/>
+ <module name="PackageName"/>
+ <module name="ParameterName"/>
+ <module name="StaticVariableName"/>
+ <module name="TypeName"/>
+
+
+ <!-- Checks for Headers -->
+ <!-- See http://checkstyle.sf.net/config_header.html -->
+ <!-- <module name="Header"> -->
+ <!-- The follow property value demonstrates the ability -->
+ <!-- to have access to ANT properties. In this case it uses -->
+ <!-- the ${basedir} property to allow Checkstyle to be run -->
+ <!-- from any directory within a project. See property -->
+ <!-- expansion, -->
+ <!-- http://checkstyle.sf.net/config.html#properties -->
+ <!-- <property -->
+ <!-- name="headerFile" -->
+ <!-- value="${basedir}/java.header"/> -->
+ <!-- </module> -->
+
+ <!-- Following interprets the header file as regular expressions. -->
+ <!-- <module name="RegexpHeader"/> -->
+
+
+ <!-- Checks for imports -->
+ <!-- See http://checkstyle.sf.net/config_import.html -->
+ <module name="AvoidStarImport"/>
+ <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+ <module name="RedundantImport"/>
+ <module name="UnusedImports"/>
+
+
+ <!-- Checks for Size Violations. -->
+ <!-- See http://checkstyle.sf.net/config_sizes.html -->
+ <module name="FileLength"/>
+ <module name="LineLength"/>
+ <module name="MethodLength"/>
+ <module name="ParameterNumber"/>
+
+
+ <!-- Checks for whitespace -->
+ <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+ <module name="EmptyForIteratorPad"/>
+ <module name="MethodParamPad"/>
+ <module name="NoWhitespaceAfter"/>
+ <module name="NoWhitespaceBefore"/>
+ <module name="OperatorWrap"/>
+ <module name="ParenPad"/>
+ <module name="TypecastParenPad"/>
+ <!--
+ <module name="TabCharacter"/>
+ <module name="WhitespaceAfter"/>
+ <module name="WhitespaceAround"/>
+ -->
+
+
+ <!-- Modifier Checks -->
+ <!-- See http://checkstyle.sf.net/config_modifiers.html -->
+ <module name="ModifierOrder"/>
+ <module name="RedundantModifier"/>
+
+
+ <!-- Checks for blocks. You know, those {}'s -->
+ <!-- See http://checkstyle.sf.net/config_blocks.html -->
+ <module name="AvoidNestedBlocks"/>
+ <module name="EmptyBlock"/>
+ <module name="LeftCurly"/>
+ <!--module name="NeedBraces"/-->
+ <module name="RightCurly"/>
+
+
+ <!-- Checks for common coding problems -->
+ <!-- See http://checkstyle.sf.net/config_coding.html -->
+ <module name="AvoidInlineConditionals"/>
+ <module name="DoubleCheckedLocking"/> <!-- MY FAVOURITE -->
+ <module name="EmptyStatement"/>
+ <module name="EqualsHashCode"/>
+ <module name="HiddenField"/>
+ <module name="IllegalInstantiation"/>
+ <module name="InnerAssignment"/>
+ <module name="MagicNumber"/>
+ <module name="MissingSwitchDefault"/>
+ <module name="RedundantThrows"/>
+ <module name="SimplifyBooleanExpression"/>
+ <module name="SimplifyBooleanReturn"/>
+
+ <!-- Checks for class design -->
+ <!-- See http://checkstyle.sf.net/config_design.html -->
+ <module name="DesignForExtension"/>
+ <module name="FinalClass"/>
+ <module name="HideUtilityClassConstructor"/>
+ <module name="InterfaceIsType"/>
+ <module name="VisibilityModifier"/>
+
+
+ <!-- Miscellaneous other checks. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html -->
+ <module name="ArrayTypeStyle"/>
+ <module name="FinalParameters"/>
+ <module name="GenericIllegalRegexp">
+ <property name="format" value="\s+$"/>
+ <property name="message" value="Line has trailing spaces."/>
+ </module>
+ <module name="TodoComment"/>
+ <module name="UpperEll"/>
+
+ </module>
+
+</module>
diff --git a/etc/global.xml b/etc/global.xml
new file mode 100644
index 0000000..052ea6b
--- /dev/null
+++ b/etc/global.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global" default="all" basedir=".">
+
+ <dirname property="global.dir.etc" file="${ant.file}"/>
+ <dirname property="global.dir.root" file="${global.dir.etc}"/>
+ <property file="${global.dir.etc}/custom.properties" />
+ <property file="${global.dir.etc}/build.properties" />
+ <property file="${global.dir.etc}/log4j.properties" />
+
+ <path id="compile-classpath">
+ <!--
+ <fileset dir="${global.dir.lib}">
+ <include name="**/*.jar"/>
+ </fileset>
+ -->
+ </path>
+
+ <path id="runtime-classpath">
+ <pathelement path="${global.dir.build.classes}" />
+ <path refid="compile-classpath" />
+ </path>
+
+ <path id="antcontrib-classpath">
+ <fileset dir="${global.dir.lib}/ant">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <path id="checkstyle-classpath">
+ <fileset dir="${global.dir.lib}/checkstyle">
+ <include name="**/*.jar"/>
+ </fileset>
+ </path>
+
+ <path id="vpp-classpath">
+ <fileset dir="${global.dir.lib}/vpp">
+ <include name="**/*.jar" />
+ </fileset>
+ </path>
+
+ <path id="findbugs-classpath">
+ <fileset dir="${global.dir.lib}/findbugs">
+ <include name="**/*.jar" />
+ </fileset>
+ </path>
+
+ <path id="jrat-classpath">
+ <fileset dir="${global.dir.lib}/jrat">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <path id="cobertura-classpath">
+ <fileset dir="${global.dir.lib}/log4j">
+ <include name="log4j-*.jar"/>
+ </fileset>
+ <fileset dir="${global.dir.lib}/cobertura">
+ <include name="**/*.jar" />
+ </fileset>
+ </path>
+
+ <path id="svn-classpath">
+ <fileset dir="${global.dir.lib}/svn">
+ <include name="**/*.jar" />
+ </fileset>
+ </path>
+
+ <!-- classpath for compiling junit tasks -->
+ <path id="junit-compile-classpath">
+ <fileset dir="${global.dir.lib}/junit">
+ <include name="**/*.jar" />
+ </fileset>
+ <path refid="runtime-classpath" />
+ </path>
+
+ <!-- classpath for running junit tasks -->
+ <path id="junit-run-classpath">
+ <pathelement path="${global.dir.build.tests}"/>
+ <pathelement path="${global.dir.build.cobertura}"/>
+ <pathelement path="${global.dir.src.resources}" />
+ <pathelement path="${global.dir.src.tests}" />
+ <path refid="junit-compile-classpath"/>
+ <path refid="cobertura-classpath"/>
+ <path refid="jrat-classpath"/>
+ </path>
+
+
+
+
+
+
+ <import file="${global.dir.etc}/targets/global-checkstyle.xml" />
+ <import file="${global.dir.etc}/targets/global-clean.xml" />
+ <import file="${global.dir.etc}/targets/global-cobertura.xml" />
+ <import file="${global.dir.etc}/targets/global-compile.xml" />
+ <import file="${global.dir.etc}/targets/global-findbugs.xml" />
+ <import file="${global.dir.etc}/targets/global-inject.xml" />
+ <import file="${global.dir.etc}/targets/global-jar.xml" />
+ <import file="${global.dir.etc}/targets/global-javadoc.xml" />
+ <import file="${global.dir.etc}/targets/global-junit.xml" />
+ <import file="${global.dir.etc}/targets/global-tar.xml" />
+ <import file="${global.dir.etc}/targets/global-taskdefs.xml" />
+ <import file="${global.dir.etc}/targets/global-verify.xml" />
+ <import file="${global.dir.etc}/targets/global-vpp.xml" />
+
+</project>
diff --git a/etc/junit/xsl/junit-frames.xsl b/etc/junit/xsl/junit-frames.xsl
new file mode 100644
index 0000000..a9d37a5
--- /dev/null
+++ b/etc/junit/xsl/junit-frames.xsl
@@ -0,0 +1,716 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+ xmlns:lxslt="http://xml.apache.org/xslt"
+ xmlns:redirect="http://xml.apache.org/xalan/redirect"
+ xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils"
+ extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator=","/>
+<!--
+ Copyright 2001-2004 The Apache Software Foundation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!--
+
+ Sample stylesheet to be used with Ant JUnitReport output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+ @author Stephane Bailliez <a href="mailto:[email protected]"/>
+ @author Erik Hatcher <a href="mailto:[email protected]"/>
+ @author Martijn Kruithof <a href="mailto:[email protected]"/>
+
+-->
+<xsl:param name="output.dir" select="'.'"/>
+
+
+<xsl:template match="testsuites">
+ <!-- create the index.html -->
+ <redirect:write file="{$output.dir}/index.html">
+ <xsl:call-template name="index.html"/>
+ </redirect:write>
+
+ <!-- create the stylesheet.css -->
+ <redirect:write file="{$output.dir}/stylesheet.css">
+ <xsl:call-template name="stylesheet.css"/>
+ </redirect:write>
+
+ <!-- create the overview-packages.html at the root -->
+ <redirect:write file="{$output.dir}/overview-summary.html">
+ <xsl:apply-templates select="." mode="overview.packages"/>
+ </redirect:write>
+
+ <!-- create the all-packages.html at the root -->
+ <redirect:write file="{$output.dir}/overview-frame.html">
+ <xsl:apply-templates select="." mode="all.packages"/>
+ </redirect:write>
+
+ <!-- create the all-classes.html at the root -->
+ <redirect:write file="{$output.dir}/allclasses-frame.html">
+ <xsl:apply-templates select="." mode="all.classes"/>
+ </redirect:write>
+
+ <!-- process all packages -->
+ <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+ <xsl:call-template name="package">
+ <xsl:with-param name="name" select="@package"/>
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="package">
+ <xsl:param name="name"/>
+ <xsl:variable name="package.dir">
+ <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
+ <xsl:if test="$name = ''">.</xsl:if>
+ </xsl:variable>
+ <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
+ <!-- create a classes-list.html in the package directory -->
+ <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+ <xsl:call-template name="classes.list">
+ <xsl:with-param name="name" select="$name"/>
+ </xsl:call-template>
+ </redirect:write>
+
+ <!-- create a package-summary.html in the package directory -->
+ <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+ <xsl:call-template name="package.summary">
+ <xsl:with-param name="name" select="$name"/>
+ </xsl:call-template>
+ </redirect:write>
+
+ <!-- for each class, creates a @name.html -->
+ <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+ <xsl:for-each select="/testsuites/testsuite[@package = $name]">
+ <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+ <xsl:apply-templates select="." mode="class.details"/>
+ </redirect:write>
+ <xsl:if test="string-length(./system-out)!=0">
+ <redirect:write file="{$output.dir}/{$package.dir}/{@name}-out.txt">
+ <xsl:value-of select="./system-out" />
+ </redirect:write>
+ </xsl:if>
+ <xsl:if test="string-length(./system-err)!=0">
+ <redirect:write file="{$output.dir}/{$package.dir}/{@name}-err.txt">
+ <xsl:value-of select="./system-err" />
+ </redirect:write>
+ </xsl:if>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="index.html">
+<html>
+ <head>
+ <title>Unit Test Results.</title>
+ </head>
+ <frameset cols="20%,80%">
+ <frameset rows="30%,70%">
+ <frame src="overview-frame.html" name="packageListFrame"/>
+ <frame src="allclasses-frame.html" name="classListFrame"/>
+ </frameset>
+ <frame src="overview-summary.html" name="classFrame"/>
+ <noframes>
+ <h2>Frame Alert</h2>
+ <p>
+ This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+ </p>
+ </noframes>
+ </frameset>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+body {
+ font:normal 68% verdana,arial,helvetica;
+ color:#000000;
+}
+table tr td, table tr th {
+ font-size: 68%;
+}
+table.details tr th{
+ font-weight: bold;
+ text-align:left;
+ background:#a6caf0;
+}
+table.details tr td{
+ background:#eeeee0;
+}
+
+p {
+ line-height:1.5em;
+ margin-top:0.5em; margin-bottom:1.0em;
+}
+h1 {
+ margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+}
+h2 {
+ margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+}
+h3 {
+ margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+}
+h4 {
+ margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h5 {
+ margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h6 {
+ margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+.Error {
+ font-weight:bold; color:red;
+}
+.Failure {
+ font-weight:bold; color:purple;
+}
+.Properties {
+ text-align:right;
+}
+</xsl:template>
+
+
+<!-- ======================================================================
+ This page is created for every testsuite class.
+ It prints a summary of the testsuite and detailed information about
+ testcase methods.
+ ====================================================================== -->
+<xsl:template match="testsuite" mode="class.details">
+ <xsl:variable name="package.name" select="@package"/>
+ <xsl:variable name="class.name"><xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></xsl:variable>
+ <html>
+ <head>
+ <title>Unit Test Results: <xsl:value-of select="$class.name"/></title>
+ <xsl:call-template name="create.stylesheet.link">
+ <xsl:with-param name="package.name" select="$package.name"/>
+ </xsl:call-template>
+ <script type="text/javascript" language="JavaScript">
+ var TestCases = new Array();
+ var cur;
+ <xsl:apply-templates select="properties"/>
+ </script>
+ <script type="text/javascript" language="JavaScript"><![CDATA[
+ function displayProperties (name) {
+ var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+ var doc = win.document.open();
+ doc.write("<html><head><title>Properties of " + name + "</title>");
+ doc.write("<style type=\"text/css\">");
+ doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+ doc.write("table tr td, table tr th { font-size: 68%; }");
+ doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+ doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+ doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+ doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+ doc.write("</style>");
+ doc.write("</head><body>");
+ doc.write("<h3>Properties of " + name + "</h3>");
+ doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+ doc.write("<table class='properties'>");
+ doc.write("<tr><th>Name</th><th>Value</th></tr>");
+ for (prop in TestCases[name]) {
+ doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+ }
+ doc.write("</table>");
+ doc.write("</body></html>");
+ doc.close();
+ win.focus();
+ }
+ ]]>
+ </script>
+ </head>
+ <body>
+ <xsl:call-template name="pageHeader"/>
+ <h3>Class <xsl:value-of select="$class.name"/></h3>
+
+
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <xsl:call-template name="testsuite.test.header"/>
+ <xsl:apply-templates select="." mode="print.test"/>
+ </table>
+
+ <h2>Tests</h2>
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <xsl:call-template name="testcase.test.header"/>
+ <!--
+ test can even not be started at all (failure to load the class)
+ so report the error directly
+ -->
+ <xsl:if test="./error">
+ <tr class="Error">
+ <td colspan="4"><xsl:apply-templates select="./error"/></td>
+ </tr>
+ </xsl:if>
+ <xsl:apply-templates select="./testcase" mode="print.test"/>
+ </table>
+ <div class="Properties">
+ <a>
+ <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+ Properties &#187;
+ </a>
+ </div>
+ <xsl:if test="string-length(./system-out)!=0">
+ <div class="Properties">
+ <a>
+ <xsl:attribute name="href">./<xsl:value-of select="@name"/>-out.txt</xsl:attribute>
+ System.out &#187;
+ </a>
+ </div>
+ </xsl:if>
+ <xsl:if test="string-length(./system-err)!=0">
+ <div class="Properties">
+ <a>
+ <xsl:attribute name="href">./<xsl:value-of select="@name"/>-err.txt</xsl:attribute>
+ System.err &#187;
+ </a>
+ </div>
+ </xsl:if>
+ </body>
+ </html>
+</xsl:template>
+
+ <!--
+ Write properties into a JavaScript data structure.
+ This is based on the original idea by Erik Hatcher ([email protected])
+ -->
+ <xsl:template match="properties">
+ cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+ <xsl:for-each select="property">
+ <xsl:sort select="@name"/>
+ cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+ </xsl:for-each>
+ </xsl:template>
+
+
+<!-- ======================================================================
+ This page is created for every package.
+ It prints the name of all classes that belongs to this package.
+ @param name the package name to print classes.
+ ====================================================================== -->
+<!-- list of classes in a package -->
+<xsl:template name="classes.list">
+ <xsl:param name="name"/>
+ <html>
+ <head>
+ <title>Unit Test Classes: <xsl:value-of select="$name"/></title>
+ <xsl:call-template name="create.stylesheet.link">
+ <xsl:with-param name="package.name" select="$name"/>
+ </xsl:call-template>
+ </head>
+ <body>
+ <table width="100%">
+ <tr>
+ <td nowrap="nowrap">
+ <h2><a href="package-summary.html" target="classFrame">
+ <xsl:value-of select="$name"/>
+ <xsl:if test="$name = ''">&lt;none&gt;</xsl:if>
+ </a></h2>
+ </td>
+ </tr>
+ </table>
+
+ <h2>Classes</h2>
+ <table width="100%">
+ <xsl:for-each select="/testsuites/testsuite[./@package = $name]">
+ <xsl:sort select="@name"/>
+ <tr>
+ <td nowrap="nowrap">
+ <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </body>
+ </html>
+</xsl:template>
+
+
+<!--
+ Creates an all-classes.html file that contains a link to all package-summary.html
+ on each class.
+-->
+<xsl:template match="testsuites" mode="all.classes">
+ <html>
+ <head>
+ <title>All Unit Test Classes</title>
+ <xsl:call-template name="create.stylesheet.link">
+ <xsl:with-param name="package.name"/>
+ </xsl:call-template>
+ </head>
+ <body>
+ <h2>Classes</h2>
+ <table width="100%">
+ <xsl:apply-templates select="testsuite" mode="all.classes">
+ <xsl:sort select="@name"/>
+ </xsl:apply-templates>
+ </table>
+ </body>
+ </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.classes">
+ <xsl:variable name="package.name" select="@package"/>
+ <tr>
+ <td nowrap="nowrap">
+ <a target="classFrame">
+ <xsl:attribute name="href">
+ <xsl:if test="not($package.name='')">
+ <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+ </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+ </xsl:attribute>
+ <xsl:value-of select="@name"/>
+ </a>
+ </td>
+ </tr>
+</xsl:template>
+
+
+<!--
+ Creates an html file that contains a link to all package-summary.html files on
+ each package existing on testsuites.
+ @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="testsuites" mode="all.packages">
+ <html>
+ <head>
+ <title>All Unit Test Packages</title>
+ <xsl:call-template name="create.stylesheet.link">
+ <xsl:with-param name="package.name"/>
+ </xsl:call-template>
+ </head>
+ <body>
+ <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+ <h2>Packages</h2>
+ <table width="100%">
+ <xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]" mode="all.packages">
+ <xsl:sort select="@package"/>
+ </xsl:apply-templates>
+ </table>
+ </body>
+ </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.packages">
+ <tr>
+ <td nowrap="nowrap">
+ <a href="./{translate(@package,'.','/')}/package-summary.html" target="classFrame">
+ <xsl:value-of select="@package"/>
+ <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+ </a>
+ </td>
+ </tr>
+</xsl:template>
+
+
+<xsl:template match="testsuites" mode="overview.packages">
+ <html>
+ <head>
+ <title>Unit Test Results: Summary</title>
+ <xsl:call-template name="create.stylesheet.link">
+ <xsl:with-param name="package.name"/>
+ </xsl:call-template>
+ </head>
+ <body>
+ <xsl:attribute name="onload">open('allclasses-frame.html','classListFrame')</xsl:attribute>
+ <xsl:call-template name="pageHeader"/>
+ <h2>Summary</h2>
+ <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+ <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+ <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+ <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+ <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <tr valign="top">
+ <th>Tests</th>
+ <th>Failures</th>
+ <th>Errors</th>
+ <th>Success rate</th>
+ <th>Time</th>
+ </tr>
+ <tr valign="top">
+ <xsl:attribute name="class">
+ <xsl:choose>
+ <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+ <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+ <xsl:otherwise>Pass</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <td><xsl:value-of select="$testCount"/></td>
+ <td><xsl:value-of select="$failureCount"/></td>
+ <td><xsl:value-of select="$errorCount"/></td>
+ <td>
+ <xsl:call-template name="display-percent">
+ <xsl:with-param name="value" select="$successRate"/>
+ </xsl:call-template>
+ </td>
+ <td>
+ <xsl:call-template name="display-time">
+ <xsl:with-param name="value" select="$timeCount"/>
+ </xsl:call-template>
+ </td>
+
+ </tr>
+ </table>
+ <table border="0" width="95%">
+ <tr>
+ <td style="text-align: justify;">
+ Note: <em>failures</em> are anticipated and checked for with assertions while <em>errors</em> are unanticipated.
+ </td>
+ </tr>
+ </table>
+
+ <h2>Packages</h2>
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <xsl:call-template name="testsuite.test.header"/>
+ <xsl:for-each select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+ <xsl:sort select="@package" order="ascending"/>
+ <!-- get the node set containing all testsuites that have the same package -->
+ <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = current()/@package]"/>
+ <tr valign="top">
+ <!-- display a failure if there is any failure/error in the package -->
+ <xsl:attribute name="class">
+ <xsl:choose>
+ <xsl:when test="sum($insamepackage/@errors) &gt; 0">Error</xsl:when>
+ <xsl:when test="sum($insamepackage/@failures) &gt; 0">Failure</xsl:when>
+ <xsl:otherwise>Pass</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <td><a href="./{translate(@package,'.','/')}/package-summary.html">
+ <xsl:value-of select="@package"/>
+ <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+ </a></td>
+ <td><xsl:value-of select="sum($insamepackage/@tests)"/></td>
+ <td><xsl:value-of select="sum($insamepackage/@errors)"/></td>
+ <td><xsl:value-of select="sum($insamepackage/@failures)"/></td>
+ <td>
+ <xsl:call-template name="display-time">
+ <xsl:with-param name="value" select="sum($insamepackage/@time)"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </body>
+ </html>
+</xsl:template>
+
+
+<xsl:template name="package.summary">
+ <xsl:param name="name"/>
+ <html>
+ <head>
+ <xsl:call-template name="create.stylesheet.link">
+ <xsl:with-param name="package.name" select="$name"/>
+ </xsl:call-template>
+ </head>
+ <body>
+ <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
+ <xsl:call-template name="pageHeader"/>
+ <h3>Package <xsl:value-of select="$name"/></h3>
+
+ <!--table border="0" cellpadding="5" cellspacing="2" width="95%">
+ <xsl:call-template name="class.metrics.header"/>
+ <xsl:apply-templates select="." mode="print.metrics"/>
+ </table-->
+
+ <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = $name]"/>
+ <xsl:if test="count($insamepackage) &gt; 0">
+ <h2>Classes</h2>
+ <p>
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <xsl:call-template name="testsuite.test.header"/>
+ <xsl:apply-templates select="$insamepackage" mode="print.test">
+ <xsl:sort select="@name"/>
+ </xsl:apply-templates>
+ </table>
+ </p>
+ </xsl:if>
+ </body>
+ </html>
+</xsl:template>
+
+
+<!--
+ transform string like a.b.c to ../../../
+ @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+ <xsl:param name="path"/>
+ <xsl:if test="contains($path,'.')">
+ <xsl:text>../</xsl:text>
+ <xsl:call-template name="path">
+ <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:if test="not(contains($path,'.')) and not($path = '')">
+ <xsl:text>../</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+ <xsl:param name="package.name"/>
+ <link rel="stylesheet" type="text/css" title="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></link>
+</xsl:template>
+
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+ <h1>Unit Test Results</h1>
+ <table width="100%">
+ <tr>
+ <td align="left"></td>
+ <td align="right">Designed for use with <a href="http://www.junit.org/">JUnit</a> and <a href="http://jakarta.apache.org/">Ant</a>.</td>
+ </tr>
+ </table>
+ <hr size="1"/>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+ <tr valign="top">
+ <th width="80%">Name</th>
+ <th>Tests</th>
+ <th>Errors</th>
+ <th>Failures</th>
+ <th nowrap="nowrap">Time(s)</th>
+ </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+ <tr valign="top">
+ <th>Name</th>
+ <th>Status</th>
+ <th width="80%">Type</th>
+ <th nowrap="nowrap">Time(s)</th>
+ </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+ <tr valign="top">
+ <xsl:attribute name="class">
+ <xsl:choose>
+ <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+ <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+ <xsl:otherwise>Pass</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
+ <td><xsl:apply-templates select="@tests"/></td>
+ <td><xsl:apply-templates select="@errors"/></td>
+ <td><xsl:apply-templates select="@failures"/></td>
+ <td><xsl:call-template name="display-time">
+ <xsl:with-param name="value" select="@time"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+ <tr valign="top">
+ <xsl:attribute name="class">
+ <xsl:choose>
+ <xsl:when test="error">Error</xsl:when>
+ <xsl:when test="failure">Failure</xsl:when>
+ <xsl:otherwise>TableRowColor</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <td><xsl:value-of select="concat( @classname, '.', @name)"/></td>
+ <xsl:choose>
+ <xsl:when test="failure">
+ <td>Failure</td>
+ <td><xsl:apply-templates select="failure"/></td>
+ </xsl:when>
+ <xsl:when test="error">
+ <td>Error</td>
+ <td><xsl:apply-templates select="error"/></td>
+ </xsl:when>
+ <xsl:otherwise>
+ <td>Success</td>
+ <td></td>
+ </xsl:otherwise>
+ </xsl:choose>
+ <td>
+ <xsl:call-template name="display-time">
+ <xsl:with-param name="value" select="@time"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+</xsl:template>
+
+
+<!-- Note : the below template error and failure are the same style
+ so just call the same style store in the toolkit template -->
+<xsl:template match="failure">
+ <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+ <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the testcase template -->
+<xsl:template name="display-failures">
+ <xsl:choose>
+ <xsl:when test="not(@message)">N/A</xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@message"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- display the stacktrace -->
+ <br/><br/>
+ <code>
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="word" select="."/>
+ </xsl:call-template>
+ </code>
+ <!-- the latter is better but might be problematic for non-21" monitors... -->
+ <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+ <xsl:param name="string"/>
+ <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+ <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+ <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+ template that will convert a carriage return into a br tag
+ @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+ <xsl:param name="word"/>
+ <xsl:param name="br"><br/></xsl:param>
+ <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+ <xsl:param name="value"/>
+ <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+ <xsl:param name="value"/>
+ <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+</xsl:stylesheet>
+
diff --git a/etc/junit/xsl/junit-noframes.xsl b/etc/junit/xsl/junit-noframes.xsl
new file mode 100644
index 0000000..25487f8
--- /dev/null
+++ b/etc/junit/xsl/junit-noframes.xsl
@@ -0,0 +1,461 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+ xmlns:lxslt="http://xml.apache.org/xslt"
+ xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"
+ doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+ Copyright 2001-2004 The Apache Software Foundation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!--
+
+ Sample stylesheet to be used with Ant JUnitReport output.
+
+ It creates a non-framed report that can be useful to send via
+ e-mail or such.
+
+ @author Stephane Bailliez <a href="mailto:[email protected]"/>
+ @author Erik Hatcher <a href="mailto:[email protected]"/>
+
+-->
+<xsl:template match="testsuites">
+ <html>
+ <head>
+ <title>Unit Test Results</title>
+ <style type="text/css">
+ body {
+ font:normal 68% verdana,arial,helvetica;
+ color:#000000;
+ }
+ table tr td, table tr th {
+ font-size: 68%;
+ }
+ table.details tr th{
+ font-weight: bold;
+ text-align:left;
+ background:#a6caf0;
+ }
+ table.details tr td{
+ background:#eeeee0;
+ }
+
+ p {
+ line-height:1.5em;
+ margin-top:0.5em; margin-bottom:1.0em;
+ }
+ h1 {
+ margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+ }
+ h2 {
+ margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+ }
+ h3 {
+ margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+ }
+ h4 {
+ margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+ }
+ h5 {
+ margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+ }
+ h6 {
+ margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+ }
+ .Error {
+ font-weight:bold; color:red;
+ }
+ .Failure {
+ font-weight:bold; color:purple;
+ }
+ .Properties {
+ text-align:right;
+ }
+ </style>
+ <script type="text/javascript" language="JavaScript">
+ var TestCases = new Array();
+ var cur;
+ <xsl:for-each select="./testsuite">
+ <xsl:apply-templates select="properties"/>
+ </xsl:for-each>
+
+ </script>
+ <script type="text/javascript" language="JavaScript"><![CDATA[
+ function displayProperties (name) {
+ var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+ var doc = win.document.open();
+ doc.write("<html><head><title>Properties of " + name + "</title>");
+ doc.write("<style>")
+ doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+ doc.write("table tr td, table tr th { font-size: 68%; }");
+ doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+ doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+ doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+ doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+ doc.write("</style>");
+ doc.write("</head><body>");
+ doc.write("<h3>Properties of " + name + "</h3>");
+ doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+ doc.write("<table class='properties'>");
+ doc.write("<tr><th>Name</th><th>Value</th></tr>");
+ for (prop in TestCases[name]) {
+ doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+ }
+ doc.write("</table>");
+ doc.write("</body></html>");
+ doc.close();
+ win.focus();
+ }
+ ]]>
+ </script>
+ </head>
+ <body>
+ <a name="top"></a>
+ <xsl:call-template name="pageHeader"/>
+
+ <!-- Summary part -->
+ <xsl:call-template name="summary"/>
+ <hr size="1" width="95%" align="left"/>
+
+ <!-- Package List part -->
+ <xsl:call-template name="packagelist"/>
+ <hr size="1" width="95%" align="left"/>
+
+ <!-- For each package create its part -->
+ <xsl:call-template name="packages"/>
+ <hr size="1" width="95%" align="left"/>
+
+ <!-- For each class create the part -->
+ <xsl:call-template name="classes"/>
+
+ </body>
+ </html>
+</xsl:template>
+
+
+
+ <!-- ================================================================== -->
+ <!-- Write a list of all packages with an hyperlink to the anchor of -->
+ <!-- of the package name. -->
+ <!-- ================================================================== -->
+ <xsl:template name="packagelist">
+ <h2>Packages</h2>
+ Note: package statistics are not computed recursively, they only sum up all of its testsuites numbers.
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <xsl:call-template name="testsuite.test.header"/>
+ <!-- list all packages recursively -->
+ <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+ <xsl:sort select="@package"/>
+ <xsl:variable name="testsuites-in-package" select="/testsuites/testsuite[./@package = current()/@package]"/>
+ <xsl:variable name="testCount" select="sum($testsuites-in-package/@tests)"/>
+ <xsl:variable name="errorCount" select="sum($testsuites-in-package/@errors)"/>
+ <xsl:variable name="failureCount" select="sum($testsuites-in-package/@failures)"/>
+ <xsl:variable name="timeCount" select="sum($testsuites-in-package/@time)"/>
+
+ <!-- write a summary for the package -->
+ <tr valign="top">
+ <!-- set a nice color depending if there is an error/failure -->
+ <xsl:attribute name="class">
+ <xsl:choose>
+ <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+ <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+ </xsl:choose>
+ </xsl:attribute>
+ <td><a href="#{@package}"><xsl:value-of select="@package"/></a></td>
+ <td><xsl:value-of select="$testCount"/></td>
+ <td><xsl:value-of select="$errorCount"/></td>
+ <td><xsl:value-of select="$failureCount"/></td>
+ <td>
+ <xsl:call-template name="display-time">
+ <xsl:with-param name="value" select="$timeCount"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:template>
+
+
+ <!-- ================================================================== -->
+ <!-- Write a package level report -->
+ <!-- It creates a table with values from the document: -->
+ <!-- Name | Tests | Errors | Failures | Time -->
+ <!-- ================================================================== -->
+ <xsl:template name="packages">
+ <!-- create an anchor to this package name -->
+ <xsl:for-each select="/testsuites/testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+ <xsl:sort select="@package"/>
+ <a name="{@package}"></a>
+ <h3>Package <xsl:value-of select="@package"/></h3>
+
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <xsl:call-template name="testsuite.test.header"/>
+
+ <!-- match the testsuites of this package -->
+ <xsl:apply-templates select="/testsuites/testsuite[./@package = current()/@package]" mode="print.test"/>
+ </table>
+ <a href="#top">Back to top</a>
+ <p/>
+ <p/>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template name="classes">
+ <xsl:for-each select="testsuite">
+ <xsl:sort select="@name"/>
+ <!-- create an anchor to this class name -->
+ <a name="{@name}"></a>
+ <h3>TestCase <xsl:value-of select="@name"/></h3>
+
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <xsl:call-template name="testcase.test.header"/>
+ <!--
+ test can even not be started at all (failure to load the class)
+ so report the error directly
+ -->
+ <xsl:if test="./error">
+ <tr class="Error">
+ <td colspan="4"><xsl:apply-templates select="./error"/></td>
+ </tr>
+ </xsl:if>
+ <xsl:apply-templates select="./testcase" mode="print.test"/>
+ </table>
+ <div class="Properties">
+ <a>
+ <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+ Properties &#187;
+ </a>
+ </div>
+ <p/>
+
+ <a href="#top">Back to top</a>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template name="summary">
+ <h2>Summary</h2>
+ <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+ <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+ <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+ <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+ <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+ <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+ <tr valign="top">
+ <th>Tests</th>
+ <th>Failures</th>
+ <th>Errors</th>
+ <th>Success rate</th>
+ <th>Time</th>
+ </tr>
+ <tr valign="top">
+ <xsl:attribute name="class">
+ <xsl:choose>
+ <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+ <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+ </xsl:choose>
+ </xsl:attribute>
+ <td><xsl:value-of select="$testCount"/></td>
+ <td><xsl:value-of select="$failureCount"/></td>
+ <td><xsl:value-of select="$errorCount"/></td>
+ <td>
+ <xsl:call-template name="display-percent">
+ <xsl:with-param name="value" select="$successRate"/>
+ </xsl:call-template>
+ </td>
+ <td>
+ <xsl:call-template name="display-time">
+ <xsl:with-param name="value" select="$timeCount"/>
+ </xsl:call-template>
+ </td>
+
+ </tr>
+ </table>
+ <table border="0" width="95%">
+ <tr>
+ <td style="text-align: justify;">
+ Note: <i>failures</i> are anticipated and checked for with assertions while <i>errors</i> are unanticipated.
+ </td>
+ </tr>
+ </table>
+ </xsl:template>
+
+ <!--
+ Write properties into a JavaScript data structure.
+ This is based on the original idea by Erik Hatcher ([email protected])
+ -->
+ <xsl:template match="properties">
+ cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+ <xsl:for-each select="property">
+ <xsl:sort select="@name"/>
+ cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+ </xsl:for-each>
+ </xsl:template>
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+ <h1>Unit Test Results</h1>
+ <table width="100%">
+ <tr>
+ <td align="left"></td>
+ <td align="right">Designed for use with <a href='http://www.junit.org'>JUnit</a> and <a href='http://jakarta.apache.org/ant'>Ant</a>.</td>
+ </tr>
+ </table>
+ <hr size="1"/>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="header">
+ <tr valign="top">
+ <th width="80%">Name</th>
+ <th>Tests</th>
+ <th>Errors</th>
+ <th>Failures</th>
+ <th nowrap="nowrap">Time(s)</th>
+ </tr>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+ <tr valign="top">
+ <th width="80%">Name</th>
+ <th>Tests</th>
+ <th>Errors</th>
+ <th>Failures</th>
+ <th nowrap="nowrap">Time(s)</th>
+ </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+ <tr valign="top">
+ <th>Name</th>
+ <th>Status</th>
+ <th width="80%">Type</th>
+ <th nowrap="nowrap">Time(s)</th>
+ </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+ <tr valign="top">
+ <!-- set a nice color depending if there is an error/failure -->
+ <xsl:attribute name="class">
+ <xsl:choose>
+ <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+ <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+ </xsl:choose>
+ </xsl:attribute>
+
+ <!-- print testsuite information -->
+ <td><a href="#{@name}"><xsl:value-of select="@name"/></a></td>
+ <td><xsl:value-of select="@tests"/></td>
+ <td><xsl:value-of select="@errors"/></td>
+ <td><xsl:value-of select="@failures"/></td>
+ <td>
+ <xsl:call-template name="display-time">
+ <xsl:with-param name="value" select="@time"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+ <tr valign="top">
+ <xsl:attribute name="class">
+ <xsl:choose>
+ <xsl:when test="failure | error">Error</xsl:when>
+ </xsl:choose>
+ </xsl:attribute>
+ <td><xsl:value-of select="@name"/></td>
+ <xsl:choose>
+ <xsl:when test="failure">
+ <td>Failure</td>
+ <td><xsl:apply-templates select="failure"/></td>
+ </xsl:when>
+ <xsl:when test="error">
+ <td>Error</td>
+ <td><xsl:apply-templates select="error"/></td>
+ </xsl:when>
+ <xsl:otherwise>
+ <td>Success</td>
+ <td></td>
+ </xsl:otherwise>
+ </xsl:choose>
+ <td>
+ <xsl:call-template name="display-time">
+ <xsl:with-param name="value" select="@time"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+</xsl:template>
+
+
+<xsl:template match="failure">
+ <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+ <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the tescase template -->
+<xsl:template name="display-failures">
+ <xsl:choose>
+ <xsl:when test="not(@message)">N/A</xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@message"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- display the stacktrace -->
+ <code>
+ <br/><br/>
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="word" select="."/>
+ </xsl:call-template>
+ </code>
+ <!-- the later is better but might be problematic for non-21" monitors... -->
+ <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+ <xsl:param name="string"/>
+ <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+ <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+ <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+ template that will convert a carriage return into a br tag
+ @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+ <xsl:param name="word"/>
+ <xsl:param name="br"><br/></xsl:param>
+ <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+ <xsl:param name="value"/>
+ <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+ <xsl:param name="value"/>
+ <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/etc/targets/global-checkstyle.xml b/etc/targets/global-checkstyle.xml
new file mode 100644
index 0000000..5c15352
--- /dev/null
+++ b/etc/targets/global-checkstyle.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-checkstyle" basedir=".">
+
+ <target name="global-checkstyle" depends="global-vpp"
+ unless="test.no.checkstyle">
+
+ <mkdir dir="${global.dir.build.reports}" />
+
+ <!-- XXX: Stupid checkstyle task doesn't expand it's parameters -->
+ <checkstyle config="etc/checkstyle/config.xml"
+ failOnViolation="false">
+ <classpath refid="checkstyle-classpath" />
+ <fileset dir="${global.dir.build.java}" />
+ <formatter type="xml"
+ toFile="${global.dir.build.reports}/checkstyle.xml" />
+ <formatter type="plain"
+ toFile="${global.dir.build.reports}/checkstyle.txt" />
+ </checkstyle>
+
+ </target>
+
+</project>
diff --git a/etc/targets/global-clean.xml b/etc/targets/global-clean.xml
new file mode 100644
index 0000000..246a2d0
--- /dev/null
+++ b/etc/targets/global-clean.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-clean" basedir=".">
+ <target name="global-clean">
+ <delete dir="${global.dir.root}/build" />
+ <delete dir="${global.dir.root}/webroot" />
+ </target>
+</project>
diff --git a/etc/targets/global-cobertura.xml b/etc/targets/global-cobertura.xml
new file mode 100644
index 0000000..425a9fd
--- /dev/null
+++ b/etc/targets/global-cobertura.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-cobertura" basedir=".">
+
+ <target name="global-cobertura-instrument" depends="global-taskdefs"
+ unless="test.no.cobertura">
+ <delete dir="${global.dir.build.cobertura}" />
+ <mkdir dir="${global.dir.build.cobertura}" />
+
+ <cobertura-instrument
+ todir="${global.dir.build.cobertura}"
+ datafile="${global.dir.build.cobertura}/cobertura.ser">
+ <fileset dir="${global.dir.build.classes}">
+ <include name="**/*.class" />
+ <exclude name="**/Main.class" />
+ <exclude name="**/cmd/**" />
+ <exclude name="**/soap/port/*/*PortSoapBindingStub.class" />
+ <exclude name="**/soap/port/*/*PortServiceLocator.class" />
+ <exclude name="**/soap/port/*/*PortService.class" />
+ </fileset>
+ </cobertura-instrument>
+
+ </target>
+
+ <target name="global-cobertura-report" depends="global-taskdefs"
+ unless="test.no.cobertura">
+ <cobertura-report
+ format="html"
+ datafile="${global.dir.build.cobertura}/cobertura.ser"
+ destdir="${global.dir.build.reports}/cobertura"
+ srcdir="${global.dir.build.java}" />
+ </target>
+
+
+</project>
diff --git a/etc/targets/global-compile.xml b/etc/targets/global-compile.xml
new file mode 100644
index 0000000..b226041
--- /dev/null
+++ b/etc/targets/global-compile.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-compile" basedir=".">
+
+ <target name="global-compile" depends="global-vpp">
+ <mkdir dir="${global.dir.build.classes}" />
+ <mkdir dir="${global.dir.build.depcache}" />
+ <mkdir dir="${global.dir.build.java}" />
+
+ <depend
+ srcdir="${global.dir.build.java}"
+ destdir="${global.dir.build.classes}"
+ cache="${global.dir.build.depcache}"
+ closure="yes">
+ </depend>
+
+ <javac
+ srcdir="${global.dir.build.java}"
+ destdir="${global.dir.build.classes}"
+ optimize="${system.javac.optimize}"
+ debug="${system.javac.debug}"
+ source="${system.javac.source}"
+ target="${system.javac.target}"
+ deprecation="${system.javac.deprecation}"
+ >
+ <classpath refid="compile-classpath" />
+ <!--
+ <compilerarg compiler="modern" value="-Xlint:unchecked" />
+ -->
+ <compilerarg compiler="modern" value="-Xlint:deprecation" />
+ <compilerarg compiler="modern" value="-Xlint:finally" />
+ </javac>
+ </target>
+
+</project>
diff --git a/etc/targets/global-findbugs.xml b/etc/targets/global-findbugs.xml
new file mode 100644
index 0000000..9a06996
--- /dev/null
+++ b/etc/targets/global-findbugs.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-findbugs" basedir=".">
+
+ <!-- Used to depend on compile. -->
+ <target name="global-findbugs" depends="global-taskdefs">
+ <delete dir="${global.dir.build.reports}/findbugs-output" />
+ <mkdir dir="${global.dir.build.reports}/findbugs-output" />
+ <findbugs
+ home="${global.dir.lib}/findbugs"
+ output="xml"
+ outputFile="${global.dir.build.reports}/findbugs-output/findbugs.xml" >
+ <sourcePath path="${global.dir.build.java}" />
+ <class location="${global.dir.build.classes}" />
+ <auxClasspath refid="compile-classpath" />
+ </findbugs>
+
+ </target>
+
+ <target name="global-findbugs-report" depends="global-taskdefs">
+ <xslt
+ basedir="${global.dir.build.reports}/findbugs-output"
+ destdir="${global.dir.build.reports}/findbugs"
+ extension=".html"
+ style="${global.dir.lib}/findbugs/xsl/plain.xsl">
+ </xslt>
+ </target>
+
+ <target name="global-findbugs-gui" depends="global-taskdefs">
+ <java
+ fork="true"
+ spawn="true"
+ classname="edu.umd.cs.findbugs.gui.FindBugsFrame">
+ <classpath>
+ <fileset dir="${global.dir.lib}/findbugs/lib" />
+ </classpath>
+ <sysproperty
+ key="findbugs.home"
+ value="${global.dir.lib}/findbugs" />
+ <jvmarg value="-Xmx768m" />
+ <arg value="-loadbugs" />
+ <arg value="${global.dir.build.reports}/findbugs-output/findbugs.xml" />
+ </java>
+ </target>
+
+
+</project>
diff --git a/etc/targets/global-inject.xml b/etc/targets/global-inject.xml
new file mode 100644
index 0000000..e707b04
--- /dev/null
+++ b/etc/targets/global-inject.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-inject" basedir=".">
+
+ <target name="global-inject">
+ <inject>
+ <fileset dir="${global.dir.build.classes}">
+ <include name="**/*.class" />
+ </fileset>
+ <criteria>
+ <!-- <includeClass class="org.jboss.util.stream.*"/> -->
+ <!-- <excludeClass class="java.lang.reflect.*"/> -->
+ <!-- <excludeMethod method="get*"/> -->
+ <!-- <excludeMethod method="set*"/> -->
+ </criteria>
+ </inject>
+
+ </target>
+
+</project>
diff --git a/etc/targets/global-jar.xml b/etc/targets/global-jar.xml
new file mode 100644
index 0000000..22b6cd4
--- /dev/null
+++ b/etc/targets/global-jar.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-jar" basedir=".">
+
+ <target name="global-jar" depends="global-compile">
+ <delete dir="${global.dir.build.jar}" />
+ <mkdir dir="${global.dir.build.jar}" />
+ <mkdir dir="${global.dir.build.tar}/lib" />
+ <mkdir dir="${global.dir.src.resources}" />
+
+ <copy todir="${global.dir.build.jar}">
+ <fileset dir="${global.dir.build.java}">
+ <include name="**/*.dat" />
+ </fileset>
+ <fileset dir="${global.dir.build.classes}">
+ <include name="**" />
+ </fileset>
+ <fileset dir="${global.dir.src.resources}">
+ <include name="log4j.properties" />
+ </fileset>
+ </copy>
+
+ <jar
+ destfile="${global.file.jar}"
+ manifest="etc/MANIFEST"
+ index="true"
+ compress="${system.jar.compress}">
+ <fileset dir="${global.dir.build.jar}" />
+ <manifest>
+ <attribute name="Built-By" value="${user.name}" />
+ </manifest>
+ </jar>
+ </target>
+
+</project>
diff --git a/etc/targets/global-javadoc.xml b/etc/targets/global-javadoc.xml
new file mode 100644
index 0000000..db48b03
--- /dev/null
+++ b/etc/targets/global-javadoc.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-javadoc" basedir=".">
+
+ <target name="global-javadoc" depends="global-vpp" unless="build.no.javadoc">
+ <javadoc
+ sourcepath="${global.dir.build.java}:${global.dir.src.docs}/javadoc"
+ destdir="${global.dir.build.javadoc}"
+ classpathref="compile-classpath"
+ source="${system.javac.source}"
+ access="${system.javadoc.access}"
+ use="true"
+ splitindex="false"
+ failonerror="true"
+ windowtitle="Anarres C Preprocessor version ${global.version}"
+ >
+
+ <package name="org.anarres.cpp" />
+ <packageset dir="${global.dir.build.java}" />
+
+ <link
+ href="http://java.sun.com/j2se/1.5.0/docs/api/"
+ offline="${system.javadoc.offline}" />
+ <link
+ href="http://java.sun.com/j2ee/1.4/docs/api/"
+ offline="${system.javadoc.offline}" />
+ <link
+ href="http://java.sun.com/products/servlet/2.3/javadoc/"
+ offline="${system.javadoc.offline}" />
+ <link
+ href="http://ws.apache.org/axis/java/apiDocs/"
+ offline="${system.javadoc.offline}" />
+ <link
+ href="http://www.jdom.org/docs/apidocs/"
+ offline="${system.javadoc.offline}" />
+ <link
+ href="http://www.junit.org/junit/javadoc/3.8.1/"
+ offline="${system.javadoc.offline}" />
+ <link
+ href="http://jakarta.apache.org/commons/configuration/apidocs/"
+ offline="${system.javadoc.offline}" />
+ <link
+ href="http://www.jajakarta.org/ant/ant-1.6.1/docs/ja/manual/api/"
+ offline="${system.javadoc.offline}" />
+<!-- href="http://www.cs.bris.ac.uk/Teaching/Resources/General/ant/docs/manual/api/" -->
+ <tag dir="${global.dir.etc}/javadoc" />
+
+ <bottom><![CDATA[Copyright &#169; 2007 <a href="http://www.anarres.org/">Shevek, Anarres</a>. All Rights Reserved.]]></bottom>
+ </javadoc>
+
+ </target>
+
+</project>
diff --git a/etc/targets/global-junit.xml b/etc/targets/global-junit.xml
new file mode 100644
index 0000000..e53c5a7
--- /dev/null
+++ b/etc/targets/global-junit.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-junit" basedir=".">
+
+ <target name="global-junit" depends="global-taskdefs">
+ <runtarget target="global-junit-compile" />
+ <runtarget target="global-cobertura-instrument" />
+
+ <mkdir dir="${global.dir.build.reports}/junit-output" />
+ <!-- This only sets the properties if they aren't set
+ - already. -->
+ <property name="test.package" value="" />
+ <property name="test.case" value="" />
+ <property name="test.all" value="" />
+ <property name="test.root" value="${global.dir.build.tests}"/>
+ <property name="jrat.configuration" value="${global.dir.src.resources}/jrat.xml" />
+
+ <runtarget target="global-junit-cleanup" />
+
+ <junit
+ printsummary="yes"
+ showoutput="yes"
+ haltonfailure="no"
+ fork="yes"
+ forkmode="once"
+ >
+ <jvmarg value="-Xbootclasspath/p:${global.dir.build}/hacks/lib/boot.jar"/>
+ <classpath refid="junit-run-classpath"/>
+ <formatter type="plain" />
+ <formatter type="xml" />
+ <sysproperty key="net.sourceforge.cobertura.datafile"
+ file="${global.dir.build.cobertura}/cobertura.ser" />
+ <sysproperty key="test.package" value="${test.package}" />
+ <sysproperty key="test.case" value="${test.case}" />
+ <sysproperty key="test.root" value="${test.root}" />
+ <sysproperty key="test.all" value="${test.all}" />
+ <sysproperty key="global.dir.build.classes"
+ value="${global.dir.build.classes}" />
+ <sysproperty key="jrat.configuration" value="${jrat.configuration}" />
+
+ <assertions>
+ <enable/>
+ </assertions>
+
+ <batchtest fork="yes" todir="${global.dir.build.reports}/junit-output">
+ <fileset dir="${global.dir.build.tests}">
+ <include name="AutoTestSuite.class" />
+ </fileset>
+ </batchtest>
+ </junit>
+
+ <delete dir="${global.dir.root}/webroot" />
+ <delete dir="${global.dir.src.resources}/webapp/WEB-INF" />
+
+
+ <runtarget target="global-junit-report" />
+ <runtarget target="global-cobertura-report" />
+ </target>
+
+ <target name="global-junit-cleanup">
+ <delete dir="${global.dir.root}/webroot" />
+ </target>
+
+ <target name="global-junit-compile">
+ <delete dir="${global.dir.build.tests}" />
+ <mkdir dir="${global.dir.src.tests}" />
+ <mkdir dir="${global.dir.build.tests}" />
+
+ <javac
+ srcdir="${global.dir.src.tests}"
+ destdir="${global.dir.build.tests}"
+ optimize="false"
+ debug="true"
+ source="${system.javac.source}"
+ target="${system.javac.target}"
+ deprecation="${system.javac.deprecation}"
+ classpathref="junit-compile-classpath"
+ >
+ <!--
+ <exclude name="**/master/**" />
+ <exclude name="**/slave/**" />
+ <exclude name="**/old/**" />
+ -->
+ </javac>
+ </target>
+
+ <target name="global-junit-report" depends="global-taskdefs">
+ <junitreport
+ todir="${global.dir.build.reports}/junit-output">
+ <fileset dir="${global.dir.build.reports}/junit-output">
+ <include name="*.xml" />
+ <exclude name="TESTS-TestSuites.xml" />
+ </fileset>
+ <report format="frames" todir="${global.dir.build.reports}/junit"
+ styledir="${global.dir.etc}/junit/xsl"/>
+ </junitreport>
+ </target>
+
+</project>
diff --git a/etc/targets/global-tar.xml b/etc/targets/global-tar.xml
new file mode 100644
index 0000000..1b053ae
--- /dev/null
+++ b/etc/targets/global-tar.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-tar" basedir=".">
+
+ <target name="global-tar" depends="global-jar,global-javadoc">
+ <mkdir dir="${global.dir.build.dist}" />
+ <mkdir dir="${global.dir.build.tar}/lib" />
+ <mkdir dir="${global.dir.build.tar}/bin" />
+ <mkdir dir="${global.dir.build.javadoc}" />
+
+ <copy todir="${global.dir.build.tar}/lib">
+ <fileset dir="${global.dir.lib}/log4j">
+ <include name="**/*.jar" />
+ </fileset>
+ <mapper type="flatten" />
+ </copy>
+
+ <copy todir="${global.dir.build.tar}">
+ <fileset dir="${global.dir.src.scripts}">
+ <include name="cpp.sh" />
+ </fileset>
+ <fileset dir="${global.dir.root}">
+ <include name="LICENSE" />
+ </fileset>
+ </copy>
+
+ <copy todir="${global.dir.build.tar}/docs/api">
+ <fileset dir="${global.dir.build.javadoc}" />
+ </copy>
+
+<!--
+ <copy todir="${global.dir.build.tar}/docs/examples">
+ <fileset
+ dir="${global.dir.build.java}/org/anarres/cpp/examples">
+ <include name="**" />
+ </fileset>
+ </copy>
+-->
+
+ <chmod perm="a+x">
+ <fileset dir="${global.dir.build.tar}/bin">
+ <include name="**/*.pl"/>
+ </fileset>
+ <fileset dir="${global.dir.build.tar}">
+ <include name="*.sh"/>
+ </fileset>
+ </chmod>
+
+ <!-- <runtarget target="global-inject" /> -->
+
+ <tar
+ destfile="${global.file.bintar}"
+ compression="gzip"
+ longfile="gnu">
+ <tarfileset
+ dir="${global.dir.build.tar}"
+ prefix="${global.name}-bin-${global.version}"
+ username="root" group="root"
+ uid="0" gid="0"
+ >
+ <include name="**" />
+ </tarfileset>
+ </tar>
+
+ <tar
+ destfile="${global.file.srctar}"
+ compression="gzip"
+ longfile="gnu">
+ <tarfileset
+ dir="${global.dir.root}"
+ prefix="${global.name}-src-${global.version}"
+ username="root" group="root"
+ uid="0" gid="0"
+ >
+ <include name="**" />
+ <exclude name="build/**" />
+ <exclude name="**/.*.swp" />
+ <exclude name="src/scripts/release.sh" />
+ </tarfileset>
+ </tar>
+
+ </target>
+
+</project>
diff --git a/etc/targets/global-taskdefs.xml b/etc/targets/global-taskdefs.xml
new file mode 100644
index 0000000..8218c91
--- /dev/null
+++ b/etc/targets/global-taskdefs.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-taskdefs">
+
+ <target name="global-taskdefs" unless="global.taskdefs.done">
+
+ <taskdef
+ resource="net/sf/antcontrib/antlib.xml"
+ classpathref="antcontrib-classpath"
+ loaderref="ant" />
+
+ <taskdef
+ resource="net/sf/antcontrib/antcontrib.properties"
+ classpathref="antcontrib-classpath"
+ loaderref="ant" />
+
+ <taskdef
+ resource="checkstyletask.properties"
+ classpathref="checkstyle-classpath"
+ loaderref="checkstyle"
+ />
+
+ <taskdef
+ resource="foundrylogic/vpp/taskdef.properties"
+ classpathref="vpp-classpath"
+ loaderref="vpp" />
+
+ <!--
+ velocity-tools-generic-1.4.jar
+ jtidy-r8-21122004.jar
+ -->
+
+ <taskdef name="injectcopy"
+ classname="org.shiftone.jrat.inject.ant.InjectCopyTask"
+ classpathref="jrat-classpath"
+ loaderref="jrat" />
+
+ <taskdef name="inject"
+ classname="org.shiftone.jrat.inject.ant.InjectTask"
+ classpathref="jrat-classpath"
+ loaderref="jrat" />
+
+ <taskdef
+ classpathref="cobertura-classpath"
+ resource="tasks.properties"
+ loaderref="cobratura"
+ />
+
+ <taskdef name="findbugs"
+ classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
+ classpathref="findbugs-classpath"
+ loaderref="findbugs" />
+
+ <taskdef
+ name="svn"
+ classname="org.tigris.subversion.svnant.SvnTask"
+ classpathref="svn-classpath"
+ loaderref="svn"
+ />
+
+ <property name="global.taskdefs.done" value="true" />
+
+ </target>
+
+</project>
diff --git a/etc/targets/global-verify.xml b/etc/targets/global-verify.xml
new file mode 100644
index 0000000..11c8674
--- /dev/null
+++ b/etc/targets/global-verify.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-verify" basedir=".">
+
+ <target name="global-verify" depends="global-taskdefs">
+ <!-- "master-jar,slave-jar,client-jar" -->
+ <mkdir dir="${global.dir.build.reports}" />
+
+ <verifydesign
+ design="${global.dir.etc}/design.xml"
+ jar="${master.file.jar}" />
+
+ <verifydesign
+ design="${global.dir.etc}/design.xml"
+ jar="${slave.file.jar}" />
+
+ <verifydesign
+ design="${global.dir.etc}/design.xml"
+ jar="${client.file.jar}" />
+
+ </target>
+
+</project>
diff --git a/etc/targets/global-vpp.xml b/etc/targets/global-vpp.xml
new file mode 100644
index 0000000..6ffc23d
--- /dev/null
+++ b/etc/targets/global-vpp.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="global-vpp">
+
+ <target name="global-vpp" depends="global-taskdefs">
+ <mkdir dir="${global.dir.build.java}" />
+
+ <vppcopy todir="${global.dir.build.java}">
+ <fileset dir="${global.dir.src.java}">
+ <include name="**/*.java" />
+ </fileset>
+ <config>
+ <context>
+ <!-- Avoid subversion and RCS tags. -->
+ <property key="Revision" value="\$Revision" />
+ <property key="Id" value="\$Id" />
+ <property key="Author" value="\$Author" />
+ <property key="Date" value="\$Date" />
+ <property key="URL" value="\$URL" />
+ <property key="Rev" value="\$Rev" />
+ <property key="SOA" value="\$SOA" />
+ <property key="TTL" value="\$TTL" />
+ <property key="DATASET" value="$DATASET" />
+ <property key="_" value="\$_" />
+
+ <property key="version" value="${global.version}" />
+ </context>
+ </config>
+ </vppcopy>
+ </target>
+
+</project>
diff --git a/lib/ant/ant-contrib.jar b/lib/ant/ant-contrib.jar
new file mode 100644
index 0000000..6e04e84
--- /dev/null
+++ b/lib/ant/ant-contrib.jar
Binary files differ
diff --git a/lib/checkstyle/checkstyle-all-4.1.jar b/lib/checkstyle/checkstyle-all-4.1.jar
new file mode 100644
index 0000000..ea018b6
--- /dev/null
+++ b/lib/checkstyle/checkstyle-all-4.1.jar
Binary files differ
diff --git a/lib/checkstyle/commons-beanutils-core.jar b/lib/checkstyle/commons-beanutils-core.jar
new file mode 100644
index 0000000..ce79cbe
--- /dev/null
+++ b/lib/checkstyle/commons-beanutils-core.jar
Binary files differ
diff --git a/lib/checkstyle/commons-cli.jar b/lib/checkstyle/commons-cli.jar
new file mode 100644
index 0000000..22a004e
--- /dev/null
+++ b/lib/checkstyle/commons-cli.jar
Binary files differ
diff --git a/lib/checkstyle/commons-collections.jar b/lib/checkstyle/commons-collections.jar
new file mode 100644
index 0000000..f66c6d2
--- /dev/null
+++ b/lib/checkstyle/commons-collections.jar
Binary files differ
diff --git a/lib/checkstyle/commons-logging.jar b/lib/checkstyle/commons-logging.jar
new file mode 100644
index 0000000..b99c937
--- /dev/null
+++ b/lib/checkstyle/commons-logging.jar
Binary files differ
diff --git a/lib/cobertura/asm-2.2.1.jar b/lib/cobertura/asm-2.2.1.jar
new file mode 100644
index 0000000..297690f
--- /dev/null
+++ b/lib/cobertura/asm-2.2.1.jar
Binary files differ
diff --git a/lib/cobertura/cobertura.jar b/lib/cobertura/cobertura.jar
new file mode 100644
index 0000000..03baf65
--- /dev/null
+++ b/lib/cobertura/cobertura.jar
Binary files differ
diff --git a/lib/cobertura/jakarta-oro-2.0.8.jar b/lib/cobertura/jakarta-oro-2.0.8.jar
new file mode 100644
index 0000000..23488d2
--- /dev/null
+++ b/lib/cobertura/jakarta-oro-2.0.8.jar
Binary files differ
diff --git a/lib/findbugs/lib/annotations.jar b/lib/findbugs/lib/annotations.jar
new file mode 100644
index 0000000..238c87e
--- /dev/null
+++ b/lib/findbugs/lib/annotations.jar
Binary files differ
diff --git a/lib/findbugs/lib/asm-3.0_RC1.jar b/lib/findbugs/lib/asm-3.0_RC1.jar
new file mode 100644
index 0000000..68a44ed
--- /dev/null
+++ b/lib/findbugs/lib/asm-3.0_RC1.jar
Binary files differ
diff --git a/lib/findbugs/lib/asm-analysis-3.0_RC1.jar b/lib/findbugs/lib/asm-analysis-3.0_RC1.jar
new file mode 100644
index 0000000..0f56a17
--- /dev/null
+++ b/lib/findbugs/lib/asm-analysis-3.0_RC1.jar
Binary files differ
diff --git a/lib/findbugs/lib/asm-commons-3.0_RC1.jar b/lib/findbugs/lib/asm-commons-3.0_RC1.jar
new file mode 100644
index 0000000..4e1bac2
--- /dev/null
+++ b/lib/findbugs/lib/asm-commons-3.0_RC1.jar
Binary files differ
diff --git a/lib/findbugs/lib/asm-tree-3.0_RC1.jar b/lib/findbugs/lib/asm-tree-3.0_RC1.jar
new file mode 100644
index 0000000..97f17d0
--- /dev/null
+++ b/lib/findbugs/lib/asm-tree-3.0_RC1.jar
Binary files differ
diff --git a/lib/findbugs/lib/asm-util-3.0_RC1.jar b/lib/findbugs/lib/asm-util-3.0_RC1.jar
new file mode 100644
index 0000000..86e20a6
--- /dev/null
+++ b/lib/findbugs/lib/asm-util-3.0_RC1.jar
Binary files differ
diff --git a/lib/findbugs/lib/asm-xml-3.0_RC1.jar b/lib/findbugs/lib/asm-xml-3.0_RC1.jar
new file mode 100644
index 0000000..32a3b3c
--- /dev/null
+++ b/lib/findbugs/lib/asm-xml-3.0_RC1.jar
Binary files differ
diff --git a/lib/findbugs/lib/bcel.jar b/lib/findbugs/lib/bcel.jar
new file mode 100644
index 0000000..b422c1e
--- /dev/null
+++ b/lib/findbugs/lib/bcel.jar
Binary files differ
diff --git a/lib/findbugs/lib/dom4j-full.jar b/lib/findbugs/lib/dom4j-full.jar
new file mode 100644
index 0000000..1efbf7e
--- /dev/null
+++ b/lib/findbugs/lib/dom4j-full.jar
Binary files differ
diff --git a/lib/findbugs/lib/findbugs-ant.jar b/lib/findbugs/lib/findbugs-ant.jar
new file mode 100644
index 0000000..66129af
--- /dev/null
+++ b/lib/findbugs/lib/findbugs-ant.jar
Binary files differ
diff --git a/lib/findbugs/lib/findbugs.jar b/lib/findbugs/lib/findbugs.jar
new file mode 100644
index 0000000..04a7856
--- /dev/null
+++ b/lib/findbugs/lib/findbugs.jar
Binary files differ
diff --git a/lib/findbugs/lib/findbugsGUI.jar b/lib/findbugs/lib/findbugsGUI.jar
new file mode 100644
index 0000000..cbe77b0
--- /dev/null
+++ b/lib/findbugs/lib/findbugsGUI.jar
Binary files differ
diff --git a/lib/findbugs/plugin/coreplugin.jar b/lib/findbugs/plugin/coreplugin.jar
new file mode 100644
index 0000000..813d2b8
--- /dev/null
+++ b/lib/findbugs/plugin/coreplugin.jar
Binary files differ
diff --git a/lib/findbugs/xsl/default.xsl b/lib/findbugs/xsl/default.xsl
new file mode 100644
index 0000000..efa2307
--- /dev/null
+++ b/lib/findbugs/xsl/default.xsl
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ FindBugs - Find bugs in Java programs
+ Copyright (C) 2004,2005 University of Maryland
+ Copyright (C) 2005, Chris Nappin
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-->
+
+<!--
+ A simple XSLT stylesheet to transform FindBugs XML results
+ annotated with messages into HTML.
+
+ If you want to experiment with modifying this stylesheet,
+ or write your own, you need to generate XML output from FindBugs
+ using a special option which lets it know to include
+ human-readable messages in the XML. Invoke the findbugs script
+ as follows:
+
+ findbugs -textui -xml:withMessages -project myProject.fb > results.xml
+
+ Then you can use your favorite XSLT implementation to transform
+ the XML output into HTML. (But don't use xsltproc. It generates well-nigh
+ unreadable output, and generates incorrect output for the
+ <script> element.)
+
+ Authors:
+ David Hovemeyer
+ Chris Nappin (summary table)
+-->
+
+<xsl:stylesheet
+ version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:output
+ method="xml"
+ omit-xml-declaration="yes"
+ standalone="yes"
+ doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
+ doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
+ indent="yes"
+ encoding="UTF-8"/>
+
+<xsl:variable name="literalNbsp">&amp;nbsp;</xsl:variable>
+
+<!--xsl:key name="bug-category-key" match="/BugCollection/BugInstance" use="@category"/-->
+
+<xsl:variable name="bugTableHeader">
+ <tr class="tableheader">
+ <th align="left">Code<xsl:value-of select="$literalNbsp" disable-output-escaping="yes"/></th>
+ <th align="left">Warning</th>
+ </tr>
+</xsl:variable>
+
+<xsl:template match="/">
+ <html>
+ <head>
+ <title>FindBugs Report</title>
+ <style type="text/css">
+ .tablerow0 {
+ background: #EEEEEE;
+ }
+
+ .tablerow1 {
+ background: white;
+ }
+
+ .detailrow0 {
+ background: #EEEEEE;
+ }
+
+ .detailrow1 {
+ background: white;
+ }
+
+ .tableheader {
+ background: #b9b9fe;
+ font-size: larger;
+ }
+
+ .tablerow0:hover, .tablerow1:hover {
+ background: #aaffaa;
+ }
+
+ .priority-1 {
+ color: red;
+ font-weight: bold;
+ }
+ .priority-2 {
+ color: orange;
+ font-weight: bold;
+ }
+ .priority-3 {
+ color: green;
+ font-weight: bold;
+ }
+ .priority-4 {
+ color: blue;
+ font-weight: bold;
+ }
+ </style>
+ <script type="text/javascript">
+ function toggleRow(elid) {
+ if (document.getElementById) {
+ element = document.getElementById(elid);
+ if (element) {
+ if (element.style.display == 'none') {
+ element.style.display = 'block';
+ //window.status = 'Toggle on!';
+ } else {
+ element.style.display = 'none';
+ //window.status = 'Toggle off!';
+ }
+ }
+ }
+ }
+ </script>
+ </head>
+
+ <xsl:variable name="unique-catkey" select="/BugCollection/BugCategory/@category"/>
+ <!--xsl:variable name="unique-catkey" select="/BugCollection/BugInstance[generate-id() = generate-id(key('bug-category-key',@category))]/@category"/-->
+
+ <body>
+
+ <h1>FindBugs Report</h1>
+
+ <h2>Project Information</h2>
+ <xsl:apply-templates select="/BugCollection/Project"/>
+
+ <h2>Contents</h2>
+ <ul>
+ <xsl:for-each select="$unique-catkey">
+ <xsl:sort select="." order="ascending"/>
+ <xsl:variable name="catkey" select="."/>
+ <xsl:variable name="catdesc" select="/BugCollection/BugCategory[@category=$catkey]/Description"/>
+
+ <li><a href="#Warnings_{$catkey}"><xsl:value-of select="$catdesc"/> Warnings</a></li>
+ </xsl:for-each>
+
+ <li><a href="#Details">Details</a></li>
+ </ul>
+
+ <h1>Summary</h1>
+ <table width="500" cellpadding="5" cellspacing="2">
+ <tr class="tableheader">
+ <th align="left">Warning Type</th>
+ <th align="right">Number</th>
+ </tr>
+
+ <xsl:for-each select="$unique-catkey">
+ <xsl:sort select="." order="ascending"/>
+ <xsl:variable name="catkey" select="."/>
+ <xsl:variable name="catdesc" select="/BugCollection/BugCategory[@category=$catkey]/Description"/>
+ <xsl:variable name="styleclass">
+ <xsl:choose><xsl:when test="position() mod 2 = 1">tablerow0</xsl:when>
+ <xsl:otherwise>tablerow1</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <tr class="{$styleclass}">
+ <td><a href="#Warnings_{$catkey}"><xsl:value-of select="$catdesc"/> Warnings</a></td>
+ <td align="right"><xsl:value-of select="count(/BugCollection/BugInstance[@category=$catkey])"/></td>
+ </tr>
+ </xsl:for-each>
+
+ <xsl:variable name="styleclass">
+ <xsl:choose><xsl:when test="count($unique-catkey) mod 2 = 0">tablerow0</xsl:when>
+ <xsl:otherwise>tablerow1</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$styleclass}">
+ <td><b>Total</b></td>
+ <td align="right"><b><xsl:value-of select="count(/BugCollection/BugInstance)"/></b></td>
+ </tr>
+ </table>
+
+ <h1>Warnings</h1>
+
+ <p>Click on a warning row to see full context information.</p>
+
+ <xsl:for-each select="$unique-catkey">
+ <xsl:sort select="." order="ascending"/>
+ <xsl:variable name="catkey" select="."/>
+ <xsl:variable name="catdesc" select="/BugCollection/BugCategory[@category=$catkey]/Description"/>
+
+ <xsl:call-template name="generateWarningTable">
+ <xsl:with-param name="warningSet" select="/BugCollection/BugInstance[@category=$catkey]"/>
+ <xsl:with-param name="sectionTitle"><xsl:value-of select="$catdesc"/> Warnings</xsl:with-param>
+ <xsl:with-param name="sectionId">Warnings_<xsl:value-of select="$catkey"/></xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <h1><a name="Details">Details</a></h1>
+
+ <xsl:apply-templates select="/BugCollection/BugPattern">
+ <xsl:sort select="@abbrev"/>
+ <xsl:sort select="ShortDescription"/>
+ </xsl:apply-templates>
+
+ </body>
+ </html>
+</xsl:template>
+
+<xsl:template match="Project">
+ <p>Project: <xsl:value-of select="@filename"/></p>
+ <p>FindBugs version: <xsl:value-of select="/BugCollection/@version"/></p>
+
+ <p>Code analyzed:</p>
+ <ul>
+ <xsl:for-each select="./Jar">
+ <li><xsl:value-of select="text()"/></li>
+ </xsl:for-each>
+ </ul>
+</xsl:template>
+
+<xsl:template match="BugInstance">
+ <xsl:variable name="warningId"><xsl:value-of select="generate-id()"/></xsl:variable>
+
+ <tr class="tablerow{position() mod 2}" onclick="toggleRow('{$warningId}');">
+
+ <td>
+ <span><xsl:attribute name="class">priority-<xsl:value-of select="@priority"/></xsl:attribute>
+ <xsl:value-of select="@abbrev"/>
+ </span>
+ </td>
+
+ <td>
+ <xsl:value-of select="LongMessage"/>
+ </td>
+
+ </tr>
+
+ <!-- Add bug annotation elements: Class, Method, Field, SourceLine, Field -->
+ <tr class="detailrow{position() mod 2}">
+ <td/>
+ <td>
+ <p id="{$warningId}" style="display: none;">
+ <a href="#{@type}">Bug type <xsl:value-of select="@type"/> (click for details)</a>
+ <xsl:for-each select="./*/Message">
+ <br/><xsl:value-of select="text()"/>
+ </xsl:for-each>
+ </p>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template match="BugPattern">
+ <h2><a name="{@type}"><xsl:value-of select="@type"/>: <xsl:value-of select="ShortDescription"/></a></h2>
+ <xsl:value-of select="Details" disable-output-escaping="yes"/>
+</xsl:template>
+
+<xsl:template name="generateWarningTable">
+ <xsl:param name="warningSet"/>
+ <xsl:param name="sectionTitle"/>
+ <xsl:param name="sectionId"/>
+
+ <h2><a name="{$sectionId}"><xsl:value-of select="$sectionTitle"/></a></h2>
+ <table class="warningtable" width="100%" cellspacing="0">
+ <xsl:copy-of select="$bugTableHeader"/>
+ <xsl:apply-templates select="$warningSet">
+ <xsl:sort select="@abbrev"/>
+ <xsl:sort select="Class/@classname"/>
+ </xsl:apply-templates>
+ </table>
+</xsl:template>
+
+</xsl:stylesheet>
+
+<!-- vim:set ts=4: -->
diff --git a/lib/findbugs/xsl/fancy.xsl b/lib/findbugs/xsl/fancy.xsl
new file mode 100644
index 0000000..59092a1
--- /dev/null
+++ b/lib/findbugs/xsl/fancy.xsl
@@ -0,0 +1,743 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Copyright (C) 2005, Etienne Giraudy, InStranet Inc
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-->
+
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
+ <xsl:output
+ method="xml" indent="yes"
+ doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+ doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
+ encoding="UTF-8"/>
+
+ <!--xsl:key name="lbc-category-key" match="/BugCollection/BugInstance" use="@category" /-->
+ <xsl:key name="lbc-code-key" match="/BugCollection/BugInstance" use="concat(@category,@abbrev)" />
+ <xsl:key name="lbc-bug-key" match="/BugCollection/BugInstance" use="concat(@category,@abbrev,@type)" />
+
+ <xsl:key name="lbp-class-bug-type" match="/BugCollection/BugInstance" use="concat(Class/@classname,@type)" />
+
+
+<xsl:template match="/" >
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>FindBugs (<xsl:value-of select="/BugCollection/@version" />) Analysis for <xsl:value-of select="/BugCollection/Project/@filename" /></title>
+ <script type="text/javascript">
+ function show(foo) {
+ document.getElementById(foo).style.display="block";
+ }
+ function hide(foo) {
+ document.getElementById(foo).style.display="none";
+ }
+ function toggle(foo) {
+ if( document.getElementById(foo).style.display == "none") {
+ show(foo);
+ } else {
+ if( document.getElementById(foo).style.display == "block") {
+ hide(foo);
+ } else {
+ show(foo);
+ }
+ }
+ }
+ function showmenu(foo) {
+ if( document.getElementById(foo).style.display == "none") {
+ hide("bug-summary");
+ document.getElementById("bug-summary-tab").className="menu-tab";
+ hide("analysis-data");
+ document.getElementById("analysis-data-tab").className="menu-tab";
+ //hide("list-by-bug-type");
+ //document.getElementById("list-by-bug-type-tab").className="menu-tab";
+ hide("list-by-package");
+ document.getElementById("list-by-package-tab").className="menu-tab";
+ hide("list-by-category");
+ document.getElementById("list-by-category-tab").className="menu-tab";
+ document.getElementById(foo+"-tab").className="menu-tab-selected";
+ show(foo);
+ }
+ // else menu already selected!
+ }
+ </script>
+ <style type='text/css'>
+ html, body {
+ background-color: #ffffff;
+ }
+ a, a:link , a:active, a:visited, a:hover {
+ text-decoration: none; color: black;
+ }
+ div, span {
+ vertical-align: top;
+ }
+ p {
+ margin: 0px;
+ }
+ #header {
+ width: 100%;
+ text-align: center;
+ margin-bottom: 5px;
+ font-size: 14pt;
+ color: red;
+ }
+ #menu {
+ margin-bottom: 10px;
+ }
+ #menu ul {
+ margin-left: 0;
+ padding-left: 0;
+ display: inline;
+ }
+ #menu ul li {
+ margin-left: 0;
+ margin-bottom: 0;
+ padding: 2px 15px 5px;
+ border: 1px solid #000;
+ list-style: none;
+ display: inline;
+ }
+ #menu ul li.here {
+ border-bottom: 1px solid #ffc;
+ list-style: none;
+ display: inline;
+ }
+ .menu-tab {
+ background: white;
+ }
+ .menu-tab:hover {
+ background: grey;
+ }
+ .menu-tab-selected {
+ background: #aaaaaa;
+ }
+ #analysis-data ul {
+ margin-left: 15px;
+ }
+ #analyzed-files, #used-libraries, #analysis-error {
+ float: left;
+ margin: 2px;
+ border: 1px black solid;
+ padding: 2px;
+ overflow:auto;
+ }
+ #analyzed-files {
+ width: 25%;
+ }
+ #used-libraries {
+ width: 25%;
+ }
+ #analysis-error {
+ width: 40%;
+ }
+ div.summary {
+ width:100%;
+ text-align:center;
+ }
+ .summary table {
+ border:1px solid black;
+ }
+ .summary th {
+ background: #aaaaaa;
+ color: white;
+ }
+ .summary th, .summary td {
+ padding: 2px 4px 2px 4px;
+ }
+ .summary-name {
+ background: #eeeeee;
+ text-align:left;
+ }
+ .summary-size {
+ background: #eeeeee;
+ text-align:center;
+ }
+ .summary-ratio {
+ background: #eeeeee;
+ text-align:center;
+ }
+ .summary-priority-all {
+ background: #dddddd;
+ text-align:center;
+ }
+ .summary-priority-1 {
+ background: red;
+ text-align:center;
+ }
+ .summary-priority-2 {
+ background: orange;
+ text-align:center;
+ }
+ .summary-priority-3 {
+ background: green;
+ text-align:center;
+ }
+ .summary-priority-4 {
+ background: blue;
+ text-align:center;
+ }
+ .outerbox {
+ border: 1px solid black;
+ margin: 10px;
+ }
+ .outerbox-title {
+ border-bottom: 1px solid #000000; font-size: 12pt; font-weight: bold;
+ background: #cccccc; margin: 0; padding: 0 5px 0 5px;
+ }
+ .title-help {
+ font-weight: normal;
+ }
+ .innerbox-1, .innerbox-2 {
+ margin: 0 0 0 10px;
+ }
+ .innerbox-1-title, .innerbox-2-title {
+ border-bottom: 1px solid #000000; border-left: 1px solid #000000;
+ margin: 0; padding: 0 5px 0 5px;
+ font-size: 12pt; font-weight: bold; background: #cccccc;
+ }
+ .bug-box {
+ border-bottom: 1px solid #000000; border-left: 1px solid #000000;
+ }
+ .bug-priority-1 {
+ background: red; height: 0.5em; width: 1em;
+ margin-right: 0.5em;
+ }
+ .bug-priority-2 {
+ background: orange; height: 0.5em; width: 1em;
+ margin-right: 0.5em;
+ }
+ .bug-priority-3 {
+ background: green; height: 0.5em; width: 1em;
+ margin-right: 0.5em;
+ }
+ .bug-priority-4 {
+ background: blue; height: 0.5em; width: 1em;
+ margin-right: 0.5em;
+ }
+ .bug-type {
+ }
+ .bug-ref {
+ font-size: 10pt; font-weight: bold; padding: 0 0 0 60px;
+ }
+ .bug-descr {
+ font-weight: normal; background: #eeeee0;
+ padding: 0 5px 0 5px; border-bottom: 1px dashed black; margin: 0px;
+ }
+ .bug-details {
+ font-weight: normal; background: #eeeee0;
+ padding: 0 5px 0 5px; margin: 0px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="header">
+ FindBugs (<xsl:value-of select="/BugCollection/@version" />) Analysis for <xsl:value-of select="/BugCollection/Project/@filename" />
+ </div>
+ <div id="menu">
+ <ul>
+ <li id='bug-summary-tab' class='menu-tab-selected'>
+ <xsl:attribute name="onclick">showmenu('bug-summary');return false;</xsl:attribute>
+ <a href='' onclick='return false;'>Bug Summary</a>
+ </li>
+ <li id='analysis-data-tab' class='menu-tab'>
+ <xsl:attribute name="onclick">showmenu('analysis-data');return false;</xsl:attribute>
+ <a href='' onclick='return false;'>Analysis Information</a>
+ </li>
+ <li id='list-by-category-tab' class='menu-tab'>
+ <xsl:attribute name="onclick">showmenu('list-by-category');return false;</xsl:attribute>
+ <a href='' onclick='return false;'>List bugs by bug category</a>
+ </li>
+ <li id='list-by-package-tab' class='menu-tab'>
+ <xsl:attribute name="onclick">showmenu('list-by-package');return false;</xsl:attribute>
+ <a href='' onclick='return false;'>List bugs by package</a>
+ </li>
+ </ul>
+ </div>
+ <xsl:call-template name="generateSummary" />
+ <xsl:call-template name="analysis-data" />
+ <xsl:call-template name="list-by-category" />
+ <xsl:call-template name="list-by-package" />
+ </body>
+</html>
+</xsl:template>
+
+<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+<!-- generate summary report from stats -->
+<xsl:template name="generateSummary" >
+<div class='summary' id='bug-summary'>
+ <h2>FindBugs Analysis generated at: <xsl:value-of select="/BugCollection/FindBugsSummary/@timestamp" /></h2>
+ <table>
+ <tr>
+ <th>Package</th>
+ <th>Code Size</th>
+ <th>Bugs</th>
+ <th>Bugs p1</th>
+ <th>Bugs p2</th>
+ <th>Bugs p3</th>
+ <th>Bugs Exp.</th>
+ <th>Ratio</th>
+ </tr>
+ <tr>
+ <td class='summary-name'>
+ Overall
+ (<xsl:value-of select="/BugCollection/FindBugsSummary/@num_packages" /> packages),
+ (<xsl:value-of select="/BugCollection/FindBugsSummary/@total_classes" /> classes)
+ </td>
+ <td class='summary-size'><xsl:value-of select="/BugCollection/FindBugsSummary/@total_size" /></td>
+ <td class='summary-priority-all'><xsl:value-of select="/BugCollection/FindBugsSummary/@total_bugs" /></td>
+ <td class='summary-priority-1'><xsl:value-of select="/BugCollection/FindBugsSummary/@priority_1" /></td>
+ <td class='summary-priority-2'><xsl:value-of select="/BugCollection/FindBugsSummary/@priority_2" /></td>
+ <td class='summary-priority-3'><xsl:value-of select="/BugCollection/FindBugsSummary/@priority_3" /></td>
+ <td class='summary-priority-4'><xsl:value-of select="/BugCollection/FindBugsSummary/@priority_4" /></td>
+ <td class='summary-ratio'></td>
+ </tr>
+ <xsl:for-each select="/BugCollection/FindBugsSummary/PackageStats">
+ <xsl:sort select="@package" />
+ <xsl:if test="@total_bugs!='0'" >
+ <tr>
+ <td class='summary-name'><xsl:value-of select="@package" /></td>
+ <td class='summary-size'><xsl:value-of select="@total_size" /></td>
+ <td class='summary-priority-all'><xsl:value-of select="@total_bugs" /></td>
+ <td class='summary-priority-1'><xsl:value-of select="@priority_1" /></td>
+ <td class='summary-priority-2'><xsl:value-of select="@priority_2" /></td>
+ <td class='summary-priority-3'><xsl:value-of select="@priority_3" /></td>
+ <td class='summary-priority-4'><xsl:value-of select="@priority_4" /></td>
+ <td class='summary-ratio'></td>
+<!--
+ <xsl:for-each select="ClassStats">
+ <xsl:if test="@bugs!='0'" >
+ <li>
+ <xsl:value-of select="@class" /> - total: <xsl:value-of select="@bugs" />
+ </li>
+ </xsl:if>
+ </xsl:for-each>
+-->
+ </tr>
+ </xsl:if>
+ </xsl:for-each>
+ </table>
+</div>
+</xsl:template>
+
+<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+<!-- display analysis info -->
+<xsl:template name="analysis-data">
+ <div id='analysis-data' style='display:none;'>
+ <div id='analyzed-files'>
+ Analyzed Files:
+ <ul>
+ <xsl:for-each select="/BugCollection/Project/Jar">
+ <li><xsl:apply-templates /></li>
+ </xsl:for-each>
+ </ul>
+ </div>
+ <div id='used-libraries'>
+ Used Libraries:
+ <ul>
+ <xsl:for-each select="/BugCollection/Project/AuxClasspathEntry">
+ <li><xsl:apply-templates /></li>
+ </xsl:for-each>
+ <xsl:if test="count(/BugCollection/Project/AuxClasspathEntry)=0" >
+ <li>None</li>
+ </xsl:if>
+ </ul>
+ </div>
+ <div id='analysis-error'>
+ Analysis Errors:
+ <ul>
+ <xsl:variable name="error-count"
+ select="count(/BugCollection/Errors/MissingClass)" />
+ <xsl:if test="$error-count=0" >
+ <li>None</li>
+ </xsl:if>
+ <xsl:if test="$error-count>0" >
+ <li>Missing ref classes for analysis:
+ <ul>
+ <xsl:for-each select="/BugCollection/Errors/MissingClass">
+ <li><xsl:apply-templates /></li>
+ </xsl:for-each>
+ </ul>
+ </li>
+ </xsl:if>
+ </ul>
+ </div>
+ </div>
+</xsl:template>
+
+<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+<!-- show priorities helper -->
+<xsl:template name="helpPriorities">
+ <span>
+ <xsl:attribute name="class">bug-priority-1</xsl:attribute>
+ &#160;&#160;
+ </span> P1
+ <span>
+ <xsl:attribute name="class">bug-priority-2</xsl:attribute>
+ &#160;&#160;
+ </span> P2
+ <span>
+ <xsl:attribute name="class">bug-priority-3</xsl:attribute>
+ &#160;&#160;
+ </span> P3
+ <span>
+ <xsl:attribute name="class">bug-priority-4</xsl:attribute>
+ &#160;&#160;
+ </span> Exp.
+</xsl:template>
+
+<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+<!-- display the details of a bug -->
+<xsl:template name="display-bug" >
+ <xsl:param name="bug-type" select="''" />
+ <xsl:param name="bug-id" select="''" />
+ <xsl:param name="which-list" select="''" />
+ <div class="bug-box">
+ <a>
+ <xsl:attribute name="href"></xsl:attribute>
+ <xsl:attribute name="onclick">toggle('<xsl:value-of select="$which-list" />-<xsl:value-of select="@uid" />');return false;</xsl:attribute>
+ <span>
+ <xsl:attribute name="class">bug-priority-<xsl:value-of select="@priority"/></xsl:attribute>
+ &#160;&#160;
+ </span>
+ <span class="bug-type"><xsl:value-of select="@abbrev" />: </span> <xsl:value-of select="Class/Message" />
+ </a>
+ <div style="display:none;">
+ <xsl:attribute name="id"><xsl:value-of select="$which-list" />-<xsl:value-of select="@uid" /></xsl:attribute>
+ <xsl:for-each select="*/Message">
+ <div class="bug-ref"><xsl:apply-templates /></div>
+ </xsl:for-each>
+ <div class="bug-descr">
+ <xsl:value-of select="LongMessage" disable-output-escaping="no" />
+ </div>
+ <div class="bug-details"><xsl:value-of select="/BugCollection/BugPattern[@type=$bug-type]/Details" disable-output-escaping="yes" /></div>
+ </div>
+ </div>
+</xsl:template>
+
+<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+<!-- main template for the list by category -->
+<xsl:template name="list-by-category" >
+ <div id='list-by-category' class='data-box' style='display:none;'>
+ <xsl:call-template name="helpPriorities" />
+ <xsl:variable name="unique-category" select="/BugCollection/BugCategory/@category"/>
+ <!--xsl:variable name="unique-category" select="/BugCollection/BugInstance[generate-id() = generate-id(key('lbc-category-key',@category))]/@category" /-->
+ <xsl:for-each select="$unique-category">
+ <xsl:sort select="." order="ascending" />
+ <xsl:call-template name="categories">
+ <xsl:with-param name="category" select="." />
+ </xsl:call-template>
+ </xsl:for-each>
+ </div>
+</xsl:template>
+
+<xsl:template name="categories" >
+ <xsl:param name="category" select="''" />
+ <xsl:variable name="category-count"
+ select="count(/BugCollection/BugInstance[@category=$category])" />
+ <xsl:variable name="category-count-p1"
+ select="count(/BugCollection/BugInstance[@category=$category and @priority='1'])" />
+ <xsl:variable name="category-count-p2"
+ select="count(/BugCollection/BugInstance[@category=$category and @priority='2'])" />
+ <xsl:variable name="category-count-p3"
+ select="count(/BugCollection/BugInstance[@category=$category and @priority='3'])" />
+ <xsl:variable name="category-count-p4"
+ select="count(/BugCollection/BugInstance[@category=$category and @priority='4'])" />
+ <div class='outerbox'>
+ <div class='outerbox-title'>
+ <a>
+ <xsl:attribute name="href"></xsl:attribute>
+ <xsl:attribute name="onclick">toggle('category-<xsl:value-of select="$category" />');return false;</xsl:attribute>
+ <xsl:value-of select="/BugCollection/BugCategory[@category=$category]/Description" />
+ (<xsl:value-of select="$category-count" />:
+ <span class='title-help'><xsl:value-of select="$category-count-p1" />/<xsl:value-of select="$category-count-p2" />/<xsl:value-of select="$category-count-p3" />/<xsl:value-of select="$category-count-p4" /></span>)
+ </a>
+ </div>
+ <div style="display:none;">
+ <xsl:attribute name="id">category-<xsl:value-of select="$category" /></xsl:attribute>
+ <xsl:call-template name="list-by-category-and-code">
+ <xsl:with-param name="category" select="$category" />
+ </xsl:call-template>
+ </div>
+ </div>
+</xsl:template>
+
+<xsl:template name="list-by-category-and-code" >
+ <xsl:param name="category" select="''" />
+ <xsl:variable name="unique-code" select="/BugCollection/BugInstance[@category=$category and generate-id()= generate-id(key('lbc-code-key',concat(@category,@abbrev)))]/@abbrev" />
+ <xsl:for-each select="$unique-code">
+ <xsl:sort select="." order="ascending" />
+ <xsl:call-template name="codes">
+ <xsl:with-param name="category" select="$category" />
+ <xsl:with-param name="code" select="." />
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="codes" >
+ <xsl:param name="category" select="''" />
+ <xsl:param name="code" select="''" />
+ <xsl:variable name="code-count"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code])" />
+ <xsl:variable name="code-count-p1"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @priority='1'])" />
+ <xsl:variable name="code-count-p2"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @priority='2'])" />
+ <xsl:variable name="code-count-p3"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @priority='3'])" />
+ <xsl:variable name="code-count-p4"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @priority='4'])" />
+ <div class='innerbox-1'>
+ <div class="innerbox-1-title">
+ <a>
+ <xsl:attribute name="href"></xsl:attribute>
+ <xsl:attribute name="onclick">toggle('category-<xsl:value-of select="$category" />-and-code-<xsl:value-of select="$code" />');return false;</xsl:attribute>
+ <xsl:value-of select="$code" />: <xsl:value-of select="/BugCollection/BugCode[@abbrev=$code]/Description" />
+ (<xsl:value-of select="$code-count" />:
+ <span class='title-help'><xsl:value-of select="$code-count-p1" />/<xsl:value-of select="$code-count-p2" />/<xsl:value-of select="$code-count-p3" />/<xsl:value-of select="$code-count-p4" /></span>)
+ </a>
+ </div>
+ <div style="display:none;">
+ <xsl:attribute name="id">category-<xsl:value-of select="$category" />-and-code-<xsl:value-of select="$code" /></xsl:attribute>
+ <xsl:call-template name="list-by-category-and-code-and-bug">
+ <xsl:with-param name="category" select="$category" />
+ <xsl:with-param name="code" select="$code" />
+ </xsl:call-template>
+ </div>
+ </div>
+</xsl:template>
+
+<xsl:template name="list-by-category-and-code-and-bug" >
+ <xsl:param name="category" select="''" />
+ <xsl:param name="code" select="''" />
+ <xsl:variable name="unique-bug" select="/BugCollection/BugInstance[@category=$category and @abbrev=$code and generate-id()= generate-id(key('lbc-bug-key',concat(@category,@abbrev,@type)))]/@type" />
+ <xsl:for-each select="$unique-bug">
+ <xsl:sort select="." order="ascending" />
+ <xsl:call-template name="bugs">
+ <xsl:with-param name="category" select="$category" />
+ <xsl:with-param name="code" select="$code" />
+ <xsl:with-param name="bug" select="." />
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="bugs" >
+ <xsl:param name="category" select="''" />
+ <xsl:param name="code" select="''" />
+ <xsl:param name="bug" select="''" />
+ <xsl:variable name="bug-count"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @type=$bug])" />
+ <xsl:variable name="bug-count-p1"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @type=$bug and @priority='1'])" />
+ <xsl:variable name="bug-count-p2"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @type=$bug and @priority='2'])" />
+ <xsl:variable name="bug-count-p3"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @type=$bug and @priority='3'])" />
+ <xsl:variable name="bug-count-p4"
+ select="count(/BugCollection/BugInstance[@category=$category and @abbrev=$code and @type=$bug and @priority='4'])" />
+ <div class='innerbox-2'>
+ <div class='innerbox-2-title'>
+ <a>
+ <xsl:attribute name="href"></xsl:attribute>
+ <xsl:attribute name="onclick">toggle('category-<xsl:value-of select="$category" />-and-code-<xsl:value-of select="$code" />-and-bug-<xsl:value-of select="$bug" />');return false;</xsl:attribute>
+ <xsl:attribute name="title"><xsl:value-of select="$bug" /></xsl:attribute>
+ <xsl:value-of select="/BugCollection/BugPattern[@category=$category and @abbrev=$code and @type=$bug]/ShortDescription" />&#160;&#160;
+ (<xsl:value-of select="$bug-count" />:
+ <span class='title-help'><xsl:value-of select="$bug-count-p1" />/<xsl:value-of select="$bug-count-p2" />/<xsl:value-of select="$bug-count-p3" />/<xsl:value-of select="$bug-count-p4" /></span>)
+ </a>
+ </div>
+ <div style="display:none;">
+ <xsl:attribute name="id">category-<xsl:value-of select="$category" />-and-code-<xsl:value-of select="$code" />-and-bug-<xsl:value-of select="$bug" /></xsl:attribute>
+ <xsl:variable name="cat-code-type">category-<xsl:value-of select="$category" />-and-code-<xsl:value-of select="$code" />-and-bug-<xsl:value-of select="$bug" /></xsl:variable>
+ <xsl:for-each select="/BugCollection/BugInstance[@category=$category and @abbrev=$code and @type=$bug]">
+ <xsl:call-template name="display-bug">
+ <xsl:with-param name="bug-type" select="@type" />
+ <xsl:with-param name="bug-id" select="@uid" />
+ <xsl:with-param name="which-list" select="$cat-code-type" />
+ </xsl:call-template>
+ </xsl:for-each>
+ </div>
+ </div>
+</xsl:template>
+
+<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+<!-- main template for the list by package -->
+<xsl:template name="list-by-package" >
+ <div id='list-by-package' class='data-box' style='display:none;'>
+ <xsl:call-template name="helpPriorities" />
+ <xsl:for-each select="/BugCollection/FindBugsSummary/PackageStats[@total_bugs != '0']/@package">
+ <xsl:sort select="." order="ascending" />
+ <xsl:call-template name="packages">
+ <xsl:with-param name="package" select="." />
+ </xsl:call-template>
+ </xsl:for-each>
+ </div>
+</xsl:template>
+
+<xsl:template name="packages" >
+ <xsl:param name="package" select="''" />
+ <xsl:variable name="package-count-p1">
+ <xsl:if test="not(/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_1 != '')">0</xsl:if>
+ <xsl:if test="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_1 != ''">
+ <xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_1" />
+ </xsl:if>
+ </xsl:variable>
+ <xsl:variable name="package-count-p2">
+ <xsl:if test="not(/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_2 != '')">0</xsl:if>
+ <xsl:if test="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_2 != ''">
+ <xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_2" />
+ </xsl:if>
+ </xsl:variable>
+ <xsl:variable name="package-count-p3">
+ <xsl:if test="not(/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_3 != '')">0</xsl:if>
+ <xsl:if test="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_3 != ''">
+ <xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_3" />
+ </xsl:if>
+ </xsl:variable>
+ <xsl:variable name="package-count-p4">
+ <xsl:if test="not(/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_4 != '')">0</xsl:if>
+ <xsl:if test="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_4 != ''">
+ <xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@priority_4" />
+ </xsl:if>
+ </xsl:variable>
+
+ <div class='outerbox'>
+ <div class='outerbox-title'>
+ <a>
+ <xsl:attribute name="href"></xsl:attribute>
+ <xsl:attribute name="onclick">toggle('package-<xsl:value-of select="$package" />');return false;</xsl:attribute>
+ <xsl:value-of select="$package" />
+ (<xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/@total_bugs" />:
+ <span class='title-help'><xsl:value-of select="$package-count-p1" />/<xsl:value-of select="$package-count-p2" />/<xsl:value-of select="$package-count-p3" />/<xsl:value-of select="$package-count-p4" /></span>)
+ </a>
+ </div>
+ <div style="display:none;">
+ <xsl:attribute name="id">package-<xsl:value-of select="$package" /></xsl:attribute>
+ <xsl:call-template name="list-by-package-and-class">
+ <xsl:with-param name="package" select="$package" />
+ </xsl:call-template>
+ </div>
+ </div>
+</xsl:template>
+
+<xsl:template name="list-by-package-and-class" >
+ <xsl:param name="package" select="''" />
+ <xsl:for-each select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@bugs != '0']/@class">
+ <xsl:sort select="." order="ascending" />
+ <xsl:call-template name="classes">
+ <xsl:with-param name="package" select="$package" />
+ <xsl:with-param name="class" select="." />
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="classes" >
+ <xsl:param name="package" select="''" />
+ <xsl:param name="class" select="''" />
+ <xsl:variable name="class-count"
+ select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@bugs" />
+
+ <xsl:variable name="class-count-p1">
+ <xsl:if test="not(/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_1 != '')">0</xsl:if>
+ <xsl:if test="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_1 != ''">
+ <xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_1" />
+ </xsl:if>
+ </xsl:variable>
+ <xsl:variable name="class-count-p2">
+ <xsl:if test="not(/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_2 != '')">0</xsl:if>
+ <xsl:if test="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_2 != ''">
+ <xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_2" />
+ </xsl:if>
+ </xsl:variable>
+ <xsl:variable name="class-count-p3">
+ <xsl:if test="not(/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_3 != '')">0</xsl:if>
+ <xsl:if test="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_3 != ''">
+ <xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_3" />
+ </xsl:if>
+ </xsl:variable>
+ <xsl:variable name="class-count-p4">
+ <xsl:if test="not(/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_4 != '')">0</xsl:if>
+ <xsl:if test="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_4 != ''">
+ <xsl:value-of select="/BugCollection/FindBugsSummary/PackageStats[@package=$package]/ClassStats[@class=$class and @bugs != '0']/@priority_4" />
+ </xsl:if>
+ </xsl:variable>
+
+
+ <div class='innerbox-1'>
+ <div class="innerbox-1-title">
+ <a>
+ <xsl:attribute name="href"></xsl:attribute>
+ <xsl:attribute name="onclick">toggle('package-<xsl:value-of select="$package" />-and-class-<xsl:value-of select="$class" />');return false;</xsl:attribute>
+ <xsl:value-of select="$class" /> (<xsl:value-of select="$class-count" />:
+ <span class='title-help'><xsl:value-of select="$class-count-p1" />/<xsl:value-of select="$class-count-p2" />/<xsl:value-of select="$class-count-p3" />/<xsl:value-of select="$class-count-p4" /></span>)
+ </a>
+ </div>
+ <div style="display:none;">
+ <xsl:attribute name="id">package-<xsl:value-of select="$package" />-and-class-<xsl:value-of select="$class" /></xsl:attribute>
+ <xsl:call-template name="list-by-package-and-class-and-bug">
+ <xsl:with-param name="package" select="$package" />
+ <xsl:with-param name="class" select="$class" />
+ </xsl:call-template>
+ </div>
+ </div>
+</xsl:template>
+
+
+<xsl:template name="list-by-package-and-class-and-bug" >
+ <xsl:param name="package" select="''" />
+ <xsl:param name="class" select="''" />
+ <xsl:variable name="unique-class-bugs" select="/BugCollection/BugInstance[Class[position()=1 and @classname=$class] and generate-id() = generate-id(key('lbp-class-bug-type',concat(Class/@classname,@type)))]/@type" />
+
+ <xsl:for-each select="$unique-class-bugs">
+ <xsl:sort select="." order="ascending" />
+ <xsl:call-template name="class-bugs">
+ <xsl:with-param name="package" select="$package" />
+ <xsl:with-param name="class" select="$class" />
+ <xsl:with-param name="type" select="." />
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="class-bugs" >
+ <xsl:param name="package" select="''" />
+ <xsl:param name="class" select="''" />
+ <xsl:param name="type" select="''" />
+ <xsl:variable name="bug-count"
+ select="count(/BugCollection/BugInstance[@type=$type and Class[position()=1 and @classname=$class]])" />
+ <div class='innerbox-2'>
+ <div class='innerbox-2-title'>
+ <a>
+ <xsl:attribute name="href"></xsl:attribute>
+ <xsl:attribute name="onclick">toggle('package-<xsl:value-of select="$package" />-and-class-<xsl:value-of select="$class" />-and-type-<xsl:value-of select="$type" />');return false;</xsl:attribute>
+ <xsl:attribute name="title"><xsl:value-of select="$type" /></xsl:attribute>
+ <xsl:value-of select="/BugCollection/BugPattern[@type=$type]/ShortDescription" />&#160;&#160;
+ (<xsl:value-of select="$bug-count" />)
+ </a>
+ </div>
+ <div style="display:none;">
+ <xsl:attribute name="id">package-<xsl:value-of select="$package" />-and-class-<xsl:value-of select="$class" />-and-type-<xsl:value-of select="$type" /></xsl:attribute>
+ <xsl:variable name="package-class-type">package-<xsl:value-of select="$package" />-and-class-<xsl:value-of select="$class" />-and-type-<xsl:value-of select="$type" /></xsl:variable>
+ <xsl:for-each select="/BugCollection/BugInstance[@type=$type and Class[position()=1 and @classname=$class]]">
+ <xsl:call-template name="display-bug">
+ <xsl:with-param name="bug-type" select="@type" />
+ <xsl:with-param name="bug-id" select="@uid" />
+ <xsl:with-param name="which-list" select="$package-class-type" />
+ </xsl:call-template>
+ </xsl:for-each>
+ </div>
+ </div>
+</xsl:template>
+
+
+
+</xsl:transform> \ No newline at end of file
diff --git a/lib/findbugs/xsl/plain.xsl b/lib/findbugs/xsl/plain.xsl
new file mode 100644
index 0000000..d9dfaa3
--- /dev/null
+++ b/lib/findbugs/xsl/plain.xsl
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ FindBugs - Find bugs in Java programs
+ Copyright (C) 2004,2005 University of Maryland
+ Copyright (C) 2005, Chris Nappin
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-->
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output
+ method="html"
+ omit-xml-declaration="yes"
+ standalone="yes"
+ doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
+ doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
+ indent="yes"
+ encoding="UTF-8"/>
+
+<!--xsl:key name="bug-category-key" match="/BugCollection/BugInstance" use="@category"/-->
+
+<xsl:variable name="bugTableHeader">
+ <tr class="tableheader">
+ <th align="left">Warning</th>
+ <th align="left">Details</th>
+ </tr>
+</xsl:variable>
+
+<xsl:template match="/">
+ <html>
+ <head>
+ <title>FindBugs Report</title>
+ <style type="text/css">
+ .tablerow0 {
+ background: #EEEEEE;
+ }
+
+ .tablerow1 {
+ background: white;
+ }
+
+ .detailrow0 {
+ background: #EEEEEE;
+ }
+
+ .detailrow1 {
+ background: white;
+ }
+
+ .tableheader {
+ background: #b9b9fe;
+ font-size: larger;
+ }
+ </style>
+ </head>
+
+ <xsl:variable name="unique-catkey" select="/BugCollection/BugCategory/@category"/>
+ <!--xsl:variable name="unique-catkey" select="/BugCollection/BugInstance[generate-id() = generate-id(key('bug-category-key',@category))]/@category"/-->
+
+ <body>
+
+ <h1>FindBugs Report</h1>
+
+ <h2>Summary</h2>
+ <table width="500" cellpadding="5" cellspacing="2">
+ <tr class="tableheader">
+ <th align="left">Warning Type</th>
+ <th align="right">Number</th>
+ </tr>
+
+ <xsl:for-each select="$unique-catkey">
+ <xsl:sort select="." order="ascending"/>
+ <xsl:variable name="catkey" select="."/>
+ <xsl:variable name="catdesc" select="/BugCollection/BugCategory[@category=$catkey]/Description"/>
+ <xsl:variable name="styleclass">
+ <xsl:choose><xsl:when test="position() mod 2 = 1">tablerow0</xsl:when>
+ <xsl:otherwise>tablerow1</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <tr class="{$styleclass}">
+ <td><a href="#Warnings_{$catkey}"><xsl:value-of select="$catdesc"/> Warnings</a></td>
+ <td align="right"><xsl:value-of select="count(/BugCollection/BugInstance[@category=$catkey])"/></td>
+ </tr>
+ </xsl:for-each>
+
+ <xsl:variable name="styleclass">
+ <xsl:choose><xsl:when test="count($unique-catkey) mod 2 = 0">tablerow0</xsl:when>
+ <xsl:otherwise>tablerow1</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$styleclass}">
+ <td><b>Total</b></td>
+ <td align="right"><b><xsl:value-of select="count(/BugCollection/BugInstance)"/></b></td>
+ </tr>
+ </table>
+ <p><br/><br/></p>
+
+ <h1>Warnings</h1>
+
+ <p>Click on each warning link to see a full description of the issue, and
+ details of how to resolve it.</p>
+
+ <xsl:for-each select="$unique-catkey">
+ <xsl:sort select="." order="ascending"/>
+ <xsl:variable name="catkey" select="."/>
+ <xsl:variable name="catdesc" select="/BugCollection/BugCategory[@category=$catkey]/Description"/>
+
+ <xsl:call-template name="generateWarningTable">
+ <xsl:with-param name="warningSet" select="/BugCollection/BugInstance[@category=$catkey]"/>
+ <xsl:with-param name="sectionTitle"><xsl:value-of select="$catdesc"/> Warnings</xsl:with-param>
+ <xsl:with-param name="sectionId">Warnings_<xsl:value-of select="$catkey"/></xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <p><br/><br/></p>
+ <h1><a name="Details">Warning Types</a></h1>
+
+ <xsl:apply-templates select="/BugCollection/BugPattern">
+ <xsl:sort select="@abbrev"/>
+ <xsl:sort select="ShortDescription"/>
+ </xsl:apply-templates>
+
+ </body>
+ </html>
+</xsl:template>
+
+<xsl:template match="BugInstance">
+ <xsl:variable name="warningId"><xsl:value-of select="generate-id()"/></xsl:variable>
+
+ <tr class="tablerow{position() mod 2}">
+ <td width="20%" valign="top">
+ <a href="#{@type}"><xsl:value-of select="ShortMessage"/></a>
+ </td>
+ <td width="80%">
+ <p><xsl:value-of select="LongMessage"/><br/><br/>
+
+ <!-- add source filename and line number(s), if any -->
+ <xsl:if test="SourceLine">
+ <br/>In file <xsl:value-of select="SourceLine/@sourcefile"/>,
+ <xsl:choose>
+ <xsl:when test="SourceLine/@start = SourceLine/@end">
+ line <xsl:value-of select="SourceLine/@start"/>
+ </xsl:when>
+ <xsl:otherwise>
+ lines <xsl:value-of select="SourceLine/@start"/>
+ to <xsl:value-of select="SourceLine/@end"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+ <xsl:for-each select="./*/Message">
+ <br/><xsl:value-of select="text()"/>
+ </xsl:for-each>
+ </p>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template match="BugPattern">
+ <h2><a name="{@type}"><xsl:value-of select="ShortDescription"/></a></h2>
+ <xsl:value-of select="Details" disable-output-escaping="yes"/>
+ <p><br/><br/></p>
+</xsl:template>
+
+<xsl:template name="generateWarningTable">
+ <xsl:param name="warningSet"/>
+ <xsl:param name="sectionTitle"/>
+ <xsl:param name="sectionId"/>
+
+ <h2><a name="{$sectionId}"><xsl:value-of select="$sectionTitle"/></a></h2>
+ <table class="warningtable" width="100%" cellspacing="2" cellpadding="5">
+ <xsl:copy-of select="$bugTableHeader"/>
+ <xsl:choose>
+ <xsl:when test="count($warningSet) &gt; 0">
+ <xsl:apply-templates select="$warningSet">
+ <xsl:sort select="@abbrev"/>
+ <xsl:sort select="Class/@classname"/>
+ </xsl:apply-templates>
+ </xsl:when>
+ <xsl:otherwise>
+ <tr><td colspan="2"><p><i>None</i></p></td></tr>
+ </xsl:otherwise>
+ </xsl:choose>
+ </table>
+ <p><br/><br/></p>
+</xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/lib/findbugs/xsl/summary.xsl b/lib/findbugs/xsl/summary.xsl
new file mode 100644
index 0000000..6bbe950
--- /dev/null
+++ b/lib/findbugs/xsl/summary.xsl
@@ -0,0 +1,240 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
+
+<xsl:output method="html"/>
+
+<xsl:param name="PAGE.TITLE" select="'Findbugs Summary Statistics'" />
+<xsl:param name="PAGE.FONT" select="'Arial'" />
+<xsl:param name="SUMMARY.HEADER" select="'Findbugs Summary Report'" />
+<xsl:param name="SUMMARY.LABEL" select="'Summary Analysis Generated at: '" />
+<xsl:param name="PACKAGE.HEADER" select="'Bugs By Package'" />
+<xsl:param name="PACKAGE.SORT.LABEL" select="'Sorted by Total Bugs'" />
+<xsl:param name="PACKAGE.LABEL" select="'Analysis of Package: '" />
+<xsl:param name="DEFAULT.PACKAGE.NAME" select="'default package'" />
+<xsl:param name="PACKAGE.BUGCLASS.LABEL" select="'Most Buggy Class in Package with #1 $1:'" />
+<xsl:param name="TOTAL.PACKAGES.LABEL" select="'#1 $1 Analyzed'" />
+
+<xsl:param name="BUGS.SINGLE.LABEL" select="'Bug'" />
+<xsl:param name="BUGS.PULURAL.LABEL" select="'Bugs'" />
+<xsl:param name="PACKAGE.SINGLE.LABEL" select="'Package'" />
+<xsl:param name="PACKAGE.PULURAL.LABEL" select="'Packages'" />
+
+
+<xsl:param name="TABLE.HEADING.TYPE" select="'Type Checked'" />
+<xsl:param name="TABLE.HEADING.COUNT" select="'Count'" />
+<xsl:param name="TABLE.HEADING.BUGS" select="'Bugs'" />
+<xsl:param name="TABLE.HEADING.PERCENT" select="'Percentage'" />
+<xsl:param name="TABLE.ROW.OUTER" select="'Outer Classes'" />
+<xsl:param name="TABLE.ROW.INNER" select="'Inner Classes'" />
+<xsl:param name="TABLE.ROW.INTERFACE" select="'Interfaces'" />
+<xsl:param name="TABLE.ROW.TOTAL" select="'Total'" />
+<xsl:param name="TABLE.WIDTH" select="'90%'" />
+
+<xsl:param name="PERCENTAGE.FORMAT" select="'#0.00%'" />
+
+<!-- This template drives the rest of the output -->
+<xsl:template match="/" >
+ <html>
+ <!-- JEditorPane gets really angry if it sees this -->
+ <!--<head><title><xsl:value-of select="$PAGE.TITLE" /></title></head> -->
+ <body>
+ <h1><center><xsl:value-of select="$SUMMARY.HEADER" /></center></h1>
+ <h2><center><xsl:value-of select="$SUMMARY.LABEL" />
+ <i><xsl:value-of select="//FindBugsSummary/@timestamp" /></i></center></h2>
+ <xsl:apply-templates select="//FindBugsSummary" />
+ <br/>
+ <center>
+ <font face="{$PAGE.FONT}" size="6"><xsl:value-of select="$PACKAGE.HEADER" /></font>
+ <br/><font face="{$PAGE.FONT}" size="4"><i>(<xsl:value-of select="$PACKAGE.SORT.LABEL"/>)</i></font>
+ </center>
+ <xsl:for-each select="//FindBugsSummary/PackageStats">
+ <xsl:sort select="@total_bugs" data-type="number" order="descending" />
+ <xsl:apply-templates select="." />
+ </xsl:for-each>
+ </body>
+ </html>
+</xsl:template>
+
+<xsl:template name="status_table_row" >
+ <xsl:param name="LABEL" select="''" />
+ <xsl:param name="COUNT" select="1" />
+ <xsl:param name="BUGS" select="0" />
+ <xsl:param name="FONT_SIZE" select="4" />
+ <tr>
+ <td align="left"><font face="{$PAGE.FONT}" size="{$FONT_SIZE}"><xsl:value-of select="$LABEL" /></font></td>
+ <td align="center"><font face="{$PAGE.FONT}" color="green" size="{$FONT_SIZE}"><xsl:value-of select="$COUNT" /></font></td>
+ <td align="center"><font face="{$PAGE.FONT}" color="red" size="{$FONT_SIZE}"><xsl:value-of select="$BUGS" /></font></td>
+ <td align="center"><font face="{$PAGE.FONT}" color="blue" size="{$FONT_SIZE}">
+ <xsl:choose>
+ <xsl:when test="$COUNT &gt; 0">
+ <xsl:value-of select="format-number(number($BUGS div $COUNT), $PERCENTAGE.FORMAT)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="format-number(0, $PERCENTAGE.FORMAT)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </font>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template name="table_header" >
+ <tr>
+ <th><font face="{$PAGE.FONT}" size="4"><xsl:value-of select="$TABLE.HEADING.TYPE"/></font></th>
+ <th><font face="{$PAGE.FONT}" size="4"><xsl:value-of select="$TABLE.HEADING.COUNT"/></font></th>
+ <th><font face="{$PAGE.FONT}" size="4"><xsl:value-of select="$TABLE.HEADING.BUGS"/></font></th>
+ <th><font face="{$PAGE.FONT}" size="4"><xsl:value-of select="$TABLE.HEADING.PERCENT"/></font></th>
+ </tr>
+</xsl:template>
+
+<xsl:template match="FindBugsSummary" >
+ <center><table width="{$TABLE.WIDTH}" border="1">
+ <xsl:call-template name="table_header" />
+
+ <xsl:call-template name="status_table_row">
+ <xsl:with-param name="LABEL" select="$TABLE.ROW.OUTER" />
+ <xsl:with-param name="COUNT" select="count(PackageStats/ClassStats[@interface='false' and substring-after(@class,'$')=''])" />
+ <xsl:with-param name="BUGS" select="sum(PackageStats/ClassStats[@interface='false' and substring-after(@class,'$')='']/@bugs)" />
+ </xsl:call-template>
+
+ <xsl:call-template name="status_table_row">
+ <xsl:with-param name="LABEL" select="$TABLE.ROW.INNER" />
+ <xsl:with-param name="COUNT" select="count(PackageStats/ClassStats[@interface='false' and substring-after(@class,'$')!=''])" />
+ <xsl:with-param name="BUGS" select="sum(PackageStats/ClassStats[@interface='false' and substring-after(@class,'$')!='']/@bugs)" />
+ </xsl:call-template>
+
+ <xsl:call-template name="status_table_row">
+ <xsl:with-param name="LABEL" select="$TABLE.ROW.INTERFACE" />
+ <xsl:with-param name="COUNT" select="count(PackageStats/ClassStats[@interface='true'])" />
+ <xsl:with-param name="BUGS" select="sum(PackageStats/ClassStats[@interface='true']/@bugs)" />
+ </xsl:call-template>
+
+ <xsl:call-template name="status_table_row">
+ <xsl:with-param name="LABEL" select="$TABLE.ROW.TOTAL" />
+ <xsl:with-param name="COUNT" select="@total_classes" />
+ <xsl:with-param name="BUGS" select="@total_bugs"/>
+ <xsl:with-param name="FONT_SIZE" select="5"/>
+ </xsl:call-template>
+ <xsl:variable name="num_packages" select="count(PackageStats)" />
+ <tr><td align="center" colspan="4"><font face="{$PAGE.FONT}" size="4">
+ <xsl:call-template name='string_format'>
+ <xsl:with-param name="COUNT" select="$num_packages"/>
+ <xsl:with-param name="STRING" select="$TOTAL.PACKAGES.LABEL"/>
+ <xsl:with-param name="SINGLE" select="$PACKAGE.SINGLE.LABEL"/>
+ <xsl:with-param name="PULURAL" select="$PACKAGE.PULURAL.LABEL"/>
+ </xsl:call-template>
+ </font></td>
+ </tr>
+ </table></center>
+</xsl:template>
+
+
+<xsl:template name='string_format'>
+ <xsl:param name="COUNT" select="1"/>
+ <xsl:param name="STRING" select="''"/>
+ <xsl:param name="SINGLE" select="''"/>
+ <xsl:param name="PULURAL" select="''"/>
+ <xsl:variable name="count_str" select="concat(substring-before($STRING,'#1'), $COUNT, substring-after($STRING,'#1'))" />
+
+ <xsl:choose>
+ <xsl:when test="$COUNT &gt; 1">
+ <xsl:value-of select="concat(substring-before($count_str,'$1'), $PULURAL, substring-after($count_str,'$1'))" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(substring-before($count_str,'$1'), $SINGLE, substring-after($count_str,'$1'))" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="PackageStats" >
+ <xsl:variable name="package-name">
+ <xsl:choose>
+ <xsl:when test="@package = ''">
+ <xsl:value-of select="$DEFAULT.PACKAGE.NAME"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@package"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="package-prefix">
+ <xsl:choose>
+ <xsl:when test="@package = ''">
+ <xsl:text></xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(@package,'.')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <center><h2><xsl:value-of select="$PACKAGE.LABEL"/><i><font color='green'><xsl:value-of select="$package-name" /></font></i></h2></center>
+ <center><table width="{$TABLE.WIDTH}" border="1">
+ <xsl:call-template name="table_header" />
+
+ <xsl:call-template name="status_table_row">
+ <xsl:with-param name="LABEL" select="$TABLE.ROW.OUTER" />
+ <xsl:with-param name="COUNT" select="count(ClassStats[@interface='false' and substring-after(@class,'$')=''])" />
+ <xsl:with-param name="BUGS" select="sum(ClassStats[@interface='false' and substring-after(@class,'$')='']/@bugs)" />
+ </xsl:call-template>
+
+ <xsl:call-template name="status_table_row">
+ <xsl:with-param name="LABEL" select="$TABLE.ROW.INNER" />
+ <xsl:with-param name="COUNT" select="count(ClassStats[@interface='false' and substring-after(@class,'$')!=''])" />
+ <xsl:with-param name="BUGS" select="sum(ClassStats[@interface='false' and substring-after(@class,'$')!='']/@bugs)" />
+ </xsl:call-template>
+
+ <xsl:call-template name="status_table_row">
+ <xsl:with-param name="LABEL" select="$TABLE.ROW.INTERFACE" />
+ <xsl:with-param name="COUNT" select="count(ClassStats[@interface='true'])" />
+ <xsl:with-param name="BUGS" select="sum(ClassStats[@interface='true']/@bugs)" />
+ </xsl:call-template>
+
+ <xsl:call-template name="status_table_row">
+ <xsl:with-param name="LABEL" select="$TABLE.ROW.TOTAL" />
+ <xsl:with-param name="COUNT" select="@total_types" />
+ <xsl:with-param name="BUGS" select="@total_bugs" />
+ <xsl:with-param name="FONT_SIZE" select="5"/>
+ </xsl:call-template>
+
+ </table>
+ <xsl:if test="@total_bugs &gt; 0">
+ <table width="{$TABLE.WIDTH}" border="0">
+ <xsl:variable name="max_bugs">
+ <xsl:for-each select="ClassStats">
+ <xsl:sort select="@bugs" data-type="number" order="descending"/>
+ <xsl:if test="position()=1">
+ <xsl:value-of select="@bugs"/>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:variable>
+
+ <tr>
+ <td align="left" colspan="2">
+ <font face="{$PAGE.FONT}" size="4">
+ <xsl:call-template name='string_format'>
+ <xsl:with-param name="COUNT" select="$max_bugs"/>
+ <xsl:with-param name="STRING" select="$PACKAGE.BUGCLASS.LABEL"/>
+ <xsl:with-param name="SINGLE" select="$BUGS.SINGLE.LABEL"/>
+ <xsl:with-param name="PULURAL" select="$BUGS.PULURAL.LABEL"/>
+ </xsl:call-template>
+ </font>
+ </td>
+ </tr>
+
+ <xsl:for-each select="ClassStats">
+ <xsl:if test="@bugs = $max_bugs">
+ <tr>
+ <td>&#160;&#160;&#160;&#160;&#160;&#160;&#160;</td>
+ <td align="left"><font face="{$PAGE.FONT}" color="red" size="4"><i><xsl:value-of select="$package-prefix"/><xsl:value-of select="@class" /></i></font></td>
+ </tr>
+ </xsl:if>
+ </xsl:for-each>
+
+ </table>
+ </xsl:if>
+ </center>
+ <br/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/lib/jrat/bcel-326809.jar b/lib/jrat/bcel-326809.jar
new file mode 100644
index 0000000..9c8de92
--- /dev/null
+++ b/lib/jrat/bcel-326809.jar
Binary files differ
diff --git a/lib/jrat/jmxri.jar b/lib/jrat/jmxri.jar
new file mode 100644
index 0000000..5e519fd
--- /dev/null
+++ b/lib/jrat/jmxri.jar
Binary files differ
diff --git a/lib/jrat/shiftone-arbor.jar b/lib/jrat/shiftone-arbor.jar
new file mode 100644
index 0000000..f16528e
--- /dev/null
+++ b/lib/jrat/shiftone-arbor.jar
Binary files differ
diff --git a/lib/jrat/shiftone-jrat.jar b/lib/jrat/shiftone-jrat.jar
new file mode 100644
index 0000000..3c1c5bf
--- /dev/null
+++ b/lib/jrat/shiftone-jrat.jar
Binary files differ
diff --git a/lib/junit/junit.jar b/lib/junit/junit.jar
new file mode 100644
index 0000000..717cd08
--- /dev/null
+++ b/lib/junit/junit.jar
Binary files differ
diff --git a/lib/log4j/log4j-1.2.13.jar b/lib/log4j/log4j-1.2.13.jar
new file mode 100644
index 0000000..dde9972
--- /dev/null
+++ b/lib/log4j/log4j-1.2.13.jar
Binary files differ
diff --git a/lib/svn/commons-lang-2.0.jar b/lib/svn/commons-lang-2.0.jar
new file mode 100644
index 0000000..c8a2870
--- /dev/null
+++ b/lib/svn/commons-lang-2.0.jar
Binary files differ
diff --git a/lib/svn/jakarta-regexp-1.3.jar b/lib/svn/jakarta-regexp-1.3.jar
new file mode 100644
index 0000000..d653a38
--- /dev/null
+++ b/lib/svn/jakarta-regexp-1.3.jar
Binary files differ
diff --git a/lib/svn/svnClientAdapter.jar b/lib/svn/svnClientAdapter.jar
new file mode 100644
index 0000000..502a53f
--- /dev/null
+++ b/lib/svn/svnClientAdapter.jar
Binary files differ
diff --git a/lib/svn/svnant.jar b/lib/svn/svnant.jar
new file mode 100644
index 0000000..fc5e2b9
--- /dev/null
+++ b/lib/svn/svnant.jar
Binary files differ
diff --git a/lib/svn/svnjavahl.jar b/lib/svn/svnjavahl.jar
new file mode 100644
index 0000000..4208165
--- /dev/null
+++ b/lib/svn/svnjavahl.jar
Binary files differ
diff --git a/lib/vpp/commons-collections-3.1.jar b/lib/vpp/commons-collections-3.1.jar
new file mode 100644
index 0000000..41e230f
--- /dev/null
+++ b/lib/vpp/commons-collections-3.1.jar
Binary files differ
diff --git a/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar b/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar
new file mode 100644
index 0000000..3956a43
--- /dev/null
+++ b/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar
Binary files differ
diff --git a/lib/vpp/velocity-1.4.jar b/lib/vpp/velocity-1.4.jar
new file mode 100644
index 0000000..04ec9d2
--- /dev/null
+++ b/lib/vpp/velocity-1.4.jar
Binary files differ
diff --git a/src/input/test.c b/src/input/test.c
new file mode 100644
index 0000000..150b759
--- /dev/null
+++ b/src/input/test.c
@@ -0,0 +1,61 @@
+line = __LINE__
+file = __FILE__
+
+#define A a /* a defined */
+#define B b /* b defined */
+#define C c /* c defined */
+
+#define EXPAND(x) x
+EXPAND(a) -> a
+EXPAND(A) -> a
+
+#define _STRINGIFY(x) #x
+_STRINGIFY(A) -> "A"
+
+#define STRINGIFY(x) _STRINGIFY(x)
+STRINGIFY(b) -> "b"
+STRINGIFY(A) -> "a"
+
+#define _CONCAT(x, y) x ## y
+_CONCAT(A, B) -> AB
+
+#define A_CONCAT done_a_concat
+_CONCAT(A, _CONCAT(B, C)) -> done_a_concat(b, c)
+
+#define CONCAT(x, y) _CONCAT(x, y)
+CONCAT(A, CONCAT(B, C)) -> abc
+
+#define _CONCAT3(x, y, z) x ## y ## z
+_CONCAT3(a, b, c) -> abc
+_CONCAT3(A, B, C) -> ABC
+_CONCAT3(A, EXPAND(B), C) -> AEXPAND(b)C
+
+Line is __LINE__
+File is __FILE__
+
+#define two three
+one /* one */
+#define one two
+one /* three */
+#undef two
+#define two five
+one /* five */
+#undef two
+one /* two */
+#undef one
+#define one four
+one /* four */
+#undef one
+#define one one
+one /* one */
+
+/* warning line 57 column 0 */
+#warning arse
+
+#define foo(x) foo(x, b)
+foo(1) -> _foo(1, b) without the _
+foo(foo(2)) -> _foo(_foo(2, b), b) without the _
+foo(y, z)
+
+#define var(x...) a x b
+var(e, f, g) -> a e, f, g b
diff --git a/src/input/test0.h b/src/input/test0.h
new file mode 100644
index 0000000..72db7b7
--- /dev/null
+++ b/src/input/test0.h
@@ -0,0 +1,7 @@
+
+test0start_2
+
+#include <test1.h>
+
+test0end___6
+
diff --git a/src/input/test1.h b/src/input/test1.h
new file mode 100644
index 0000000..0b690f7
--- /dev/null
+++ b/src/input/test1.h
@@ -0,0 +1,7 @@
+
+test1start_2
+
+test1mid___4
+
+test1end___6
+
diff --git a/src/java/org/anarres/cpp/Argument.java b/src/java/org/anarres/cpp/Argument.java
new file mode 100644
index 0000000..da87d70
--- /dev/null
+++ b/src/java/org/anarres/cpp/Argument.java
@@ -0,0 +1,79 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * A macro argument.
+ *
+ * This encapsulates a raw and preprocessed token stream.
+ */
+/* pp */ class Argument extends ArrayList<Token> {
+ public static final int NO_ARGS = -1;
+
+ private List<Token> expansion;
+
+ public Argument() {
+ this.expansion = null;
+ }
+
+ public void addToken(Token tok) {
+ add(tok);
+ }
+
+ /* pp */ void expand(Preprocessor p)
+ throws IOException,
+ LexerException {
+ /* Cache expansion. */
+ if (expansion == null) {
+ this.expansion = p.expand(this);
+ // System.out.println("Expanded arg " + this);
+ }
+ }
+
+ public Iterator<Token> expansion() {
+ return expansion.iterator();
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("Argument(");
+ // buf.append(super.toString());
+ buf.append("raw=[ ");
+ for (int i = 0; i < size(); i++)
+ buf.append(get(i).getText());
+ buf.append(" ];expansion=[ ");
+ if (expansion == null)
+ buf.append("null");
+ else
+ for (int i = 0; i < expansion.size(); i++)
+ buf.append(expansion.get(i).getText());
+ buf.append(" ])");
+ return buf.toString();
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/CppReader.java b/src/java/org/anarres/cpp/CppReader.java
new file mode 100644
index 0000000..0aa6788
--- /dev/null
+++ b/src/java/org/anarres/cpp/CppReader.java
@@ -0,0 +1,147 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * A Reader wrapper around the Preprocessor.
+ *
+ * This is a utility class to provide a transparent {@link Reader}
+ * which preprocesses the input text.
+ *
+ * @see Preprocessor
+ * @see Reader
+ */
+public class CppReader extends Reader {
+
+ private Preprocessor cpp;
+ private String token;
+ private int idx;
+
+ public CppReader(final Reader r) {
+ cpp = new Preprocessor(new LexerSource(r, true) {
+ @Override
+ public String getName() {
+ return "<CppReader Input@" +
+ System.identityHashCode(r) + ">";
+ }
+ });
+ token = "";
+ idx = 0;
+ }
+
+ public CppReader(Preprocessor p) {
+ cpp = p;
+ token = "";
+ idx = 0;
+ }
+
+ /**
+ * Returns the Preprocessor used by this CppReader.
+ */
+ public Preprocessor getPreprocessor() {
+ return cpp;
+ }
+
+ /**
+ * Defines the given name as a macro.
+ *
+ * This is a convnience method.
+ */
+ public void addMacro(String name)
+ throws LexerException {
+ cpp.addMacro(name);
+ }
+
+ /**
+ * Defines the given name as a macro.
+ *
+ * This is a convnience method.
+ */
+ public void addMacro(String name, String value)
+ throws LexerException {
+ cpp.addMacro(name, value);
+ }
+
+ private boolean refill()
+ throws IOException {
+ try {
+ assert cpp != null : "cpp is null : was it closed?";
+ if (token == null)
+ return false;
+ while (idx >= token.length()) {
+ Token tok = cpp.token();
+ switch (tok.getType()) {
+ case EOF:
+ token = null;
+ return false;
+ case COMMENT:
+ if (false) {
+ token = " ";
+ break;
+ }
+ default:
+ token = tok.getText();
+ break;
+ }
+ idx = 0;
+ }
+ return true;
+ }
+ catch (LexerException e) {
+ IOException ie = new IOException(String.valueOf(e));
+ ie.initCause(e);
+ throw ie;
+ }
+ }
+
+ public int read()
+ throws IOException {
+ if (!refill())
+ return -1;
+ return token.charAt(idx++);
+ }
+
+ /* XXX Very slow and inefficient. */
+ public int read(char cbuf[], int off, int len)
+ throws IOException {
+ if (token == null)
+ return -1;
+ for (int i = 0; i < len; i++) {
+ int ch = read();
+ if (ch == -1)
+ return i;
+ cbuf[off + i] = (char)ch;
+ }
+ return len;
+ }
+
+ public void close()
+ throws IOException {
+ cpp = null;
+ token = null;
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/FileLexerSource.java b/src/java/org/anarres/cpp/FileLexerSource.java
new file mode 100644
index 0000000..9f574a0
--- /dev/null
+++ b/src/java/org/anarres/cpp/FileLexerSource.java
@@ -0,0 +1,74 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import java.util.List;
+import java.util.Iterator;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * A {@link Source} which lexes a file.
+ *
+ * The input is buffered.
+ *
+ * @see Source
+ */
+public class FileLexerSource extends LexerSource {
+ private File file;
+
+ /**
+ * Creates a new Source for lexing the given File.
+ *
+ * Preprocessor directives are honoured within the file.
+ */
+ public FileLexerSource(File file)
+ throws IOException {
+ super(
+ new BufferedReader(
+ new FileReader(
+ file
+ )
+ ),
+ true
+ );
+
+ this.file = file;
+ }
+
+ @Override
+ /* pp */ File getFile() {
+ return file;
+ }
+
+ @Override
+ /* pp */ String getName() {
+ return String.valueOf(file);
+ }
+
+ public String toString() {
+ return "file " + file;
+ }
+}
diff --git a/src/java/org/anarres/cpp/FixedTokenSource.java b/src/java/org/anarres/cpp/FixedTokenSource.java
new file mode 100644
index 0000000..d123f89
--- /dev/null
+++ b/src/java/org/anarres/cpp/FixedTokenSource.java
@@ -0,0 +1,67 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.io.StringReader;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Iterator;
+
+/* pp */ class FixedTokenSource extends Source {
+ private static final Token EOF =
+ new Token(Token.EOF, "<ts-eof>");
+
+ private List<Token> tokens;
+ private int idx;
+
+ /* pp */ FixedTokenSource(Token... tokens) {
+ this.tokens = Arrays.asList(tokens);
+ this.idx = 0;
+ }
+
+ /* pp */ FixedTokenSource(List<Token> tokens) {
+ this.tokens = tokens;
+ this.idx = 0;
+ }
+
+ public Token token()
+ throws IOException,
+ LexerException {
+ if (idx >= tokens.size())
+ return EOF;
+ return tokens.get(idx++);
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("constant token stream " + tokens);
+ Source parent = getParent();
+ if (parent != null)
+ buf.append(" in ").append(String.valueOf(parent));
+ return buf.toString();
+ }
+}
diff --git a/src/java/org/anarres/cpp/InternalException.java b/src/java/org/anarres/cpp/InternalException.java
new file mode 100644
index 0000000..d228710
--- /dev/null
+++ b/src/java/org/anarres/cpp/InternalException.java
@@ -0,0 +1,33 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+/**
+ * An internal exception.
+ *
+ * This exception is thrown when an internal state violation is
+ * encountered. This should never happen. If it ever happens, please
+ * report it as a bug.
+ */
+public class InternalException extends RuntimeException {
+ public InternalException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/java/org/anarres/cpp/JoinReader.java b/src/java/org/anarres/cpp/JoinReader.java
new file mode 100644
index 0000000..10ec535
--- /dev/null
+++ b/src/java/org/anarres/cpp/JoinReader.java
@@ -0,0 +1,168 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.Reader;
+import java.io.PushbackReader;
+import java.io.IOException;
+
+/* pp */ class JoinReader extends Reader {
+ private Reader in;
+
+ private boolean trigraphs;
+
+ private int newlines;
+ private boolean flushnl;
+ private int[] unget;
+ private int uptr;
+
+ public JoinReader(Reader in, boolean trigraphs) {
+ this.in = in;
+ this.trigraphs = trigraphs;
+ this.newlines = 0;
+ this.flushnl = false;
+ this.unget = new int[2];
+ this.uptr = 0;
+ }
+
+ public JoinReader(Reader in) {
+ this(in, false);
+ }
+
+ private int __read() throws IOException {
+ if (uptr > 0)
+ return unget[--uptr];
+ return in.read();
+ }
+
+ private void _unread(int c) {
+ if (c != -1)
+ unget[uptr++] = c;
+ }
+
+ private int _read() throws IOException {
+ int c = __read();
+ if (c == '?' && trigraphs) {
+ int d = __read();
+ if (d == '?') {
+ int e = __read();
+ switch (e) {
+ case '(': return '[';
+ case ')': return ']';
+ case '<': return '{';
+ case '>': return '}';
+ case '=': return '#';
+ case '/': return '\\';
+ case '\'': return '^';
+ case '!': return '|';
+ case '-': return '~';
+ }
+ _unread(e);
+ }
+ _unread(d);
+ }
+ return c;
+ }
+
+ public int read() throws IOException {
+ if (flushnl) {
+ if (newlines > 0) {
+ newlines--;
+ return '\n';
+ }
+ flushnl = false;
+ }
+
+ for (;;) {
+ int c = _read();
+ switch (c) {
+ case '\\':
+ int d = _read();
+ switch (d) {
+ case '\n':
+ newlines++;
+ continue;
+ case '\r':
+ newlines++;
+ int e = _read();
+ if (e != '\n')
+ _unread(e);
+ continue;
+ default:
+ _unread(d);
+ return c;
+ }
+ case '\r':
+ case '\n':
+ case '\u2028':
+ case '\u2029':
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ flushnl = true;
+ return c;
+ case -1:
+ if (newlines > 0) {
+ newlines--;
+ return '\n';
+ }
+ default:
+ return c;
+ }
+ }
+ }
+
+ public int read(char cbuf[], int off, int len)
+ throws IOException {
+ for (int i = 0; i < len; i++) {
+ int ch = read();
+ if (ch == -1)
+ return i;
+ cbuf[off + i] = (char)ch;
+ }
+ return len;
+ }
+
+ public void close()
+ throws IOException {
+ in.close();
+ }
+
+ public String toString() {
+ return "JoinReader(nl=" + newlines + ")";
+ }
+
+/*
+ public static void main(String[] args) throws IOException {
+ FileReader f = new FileReader(new File(args[0]));
+ BufferedReader b = new BufferedReader(f);
+ JoinReader r = new JoinReader(b);
+ BufferedWriter w = new BufferedWriter(
+ new java.io.OutputStreamWriter(System.out)
+ );
+ int c;
+ while ((c = r.read()) != -1) {
+ w.write((char)c);
+ }
+ w.close();
+ }
+*/
+
+}
diff --git a/src/java/org/anarres/cpp/LexerException.java b/src/java/org/anarres/cpp/LexerException.java
new file mode 100644
index 0000000..a4b5e2e
--- /dev/null
+++ b/src/java/org/anarres/cpp/LexerException.java
@@ -0,0 +1,35 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+/**
+ * A preprocessor exception.
+ *
+ * Note to users: I don't really like the name of this class. S.
+ */
+public class LexerException extends Exception {
+ public LexerException(String msg) {
+ super(msg);
+ }
+
+ public LexerException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/java/org/anarres/cpp/LexerSource.java b/src/java/org/anarres/cpp/LexerSource.java
new file mode 100644
index 0000000..a291bff
--- /dev/null
+++ b/src/java/org/anarres/cpp/LexerSource.java
@@ -0,0 +1,677 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.util.Stack;
+
+import static org.anarres.cpp.Token.*;
+
+/** Does not handle digraphs. */
+public class LexerSource extends Source {
+ private static final boolean DEBUG = false;
+
+ private PushbackReader reader;
+ private boolean ppvalid;
+ private boolean bol;
+ private boolean include;
+
+ private int line;
+ private int column;
+ private int lastcolumn;
+ private boolean cr;
+
+ /* ppvalid is:
+ * false in StringLexerSource,
+ * true in FileLexerSource */
+ public LexerSource(Reader r, boolean ppvalid) {
+ this.reader = new PushbackReader(new JoinReader(r), 5);
+ this.ppvalid = ppvalid;
+ this.bol = true;
+ this.include = false;
+
+ this.line = 1;
+ this.column = 0;
+ this.lastcolumn = -1;
+ this.cr = false;
+ }
+
+ @Override
+ public int getLine() {
+ return line;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ /* pp */ boolean isNumbered() {
+ return true;
+ }
+
+/* Error handling - this lot is barely worth it. */
+
+ private final void _error(String msg, boolean error)
+ throws LexerException {
+ int _l = line;
+ int _c = column;
+ if (_c == 0) {
+ _c = lastcolumn;
+ _l--;
+ }
+ else {
+ _c--;
+ }
+ if (error)
+ super.error(_l, _c, msg);
+ else
+ super.warning(_l, _c, msg);
+ }
+
+ private final void error(String msg)
+ throws LexerException {
+ _error(msg, true);
+ }
+
+ private final void warning(String msg)
+ throws LexerException {
+ _error(msg, false);
+ }
+
+/* A flag for string handling. */
+
+ /* pp */ void setInclude(boolean b) {
+ this.include = b;
+ }
+
+/*
+ private boolean _isLineSeparator(int c) {
+ return Character.getType(c) == Character.LINE_SEPARATOR
+ || c == -1;
+ }
+*/
+
+ /* XXX Move to JoinReader and canonicalise newlines. */
+ private static final boolean isLineSeparator(int c) {
+ switch ((char)c) {
+ case '\r':
+ case '\n':
+ case '\u2028':
+ case '\u2029':
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ return true;
+ default:
+ return (c == -1);
+ }
+ }
+
+
+ private int read() throws IOException {
+ int c = reader.read();
+ switch (c) {
+ case '\r':
+ cr = true;
+ line++;
+ lastcolumn = column;
+ column = 0;
+ break;
+ case '\n':
+ if (cr) {
+ cr = false;
+ break;
+ }
+ /* fallthrough */
+ case '\u2028':
+ case '\u2029':
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ cr = false;
+ line++;
+ lastcolumn = column;
+ column = 0;
+ break;
+ default:
+ cr = false;
+ column++;
+ break;
+ }
+
+/*
+ if (isLineSeparator(c)) {
+ line++;
+ lastcolumn = column;
+ column = 0;
+ }
+ else {
+ column++;
+ }
+*/
+
+ return c;
+ }
+
+ /* You can unget AT MOST one newline. */
+ private void unread(int c)
+ throws IOException {
+ if (c != -1) {
+ if (isLineSeparator(c)) {
+ line--;
+ column = lastcolumn;
+ cr = false;
+ }
+ else {
+ column--;
+ }
+ reader.unread(c);
+ }
+ }
+
+ private Token ccomment()
+ throws IOException {
+ StringBuilder text = new StringBuilder("/*");
+ int d;
+ do {
+ do {
+ d = read();
+ text.append((char)d);
+ } while (d != '*');
+ do {
+ d = read();
+ text.append((char)d);
+ } while (d == '*');
+ } while (d != '/');
+ return new Token(COMMENT, text.toString());
+ }
+
+ private Token cppcomment()
+ throws IOException {
+ StringBuilder text = new StringBuilder("//");
+ int d = read();
+ while (!isLineSeparator(d)) {
+ text.append((char)d);
+ d = read();
+ }
+ unread(d);
+ return new Token(COMMENT, text.toString());
+ }
+
+ private int escape(StringBuilder text)
+ throws IOException,
+ LexerException {
+ int d = read();
+ switch (d) {
+ case 'a': text.append('a'); return 0x0a;
+ case 'b': text.append('b'); return '\b';
+ case 'f': text.append('f'); return '\f';
+ case 'n': text.append('n'); return '\n';
+ case 'r': text.append('r'); return '\r';
+ case 't': text.append('t'); return '\t';
+ case 'v': text.append('v'); return 0x0b;
+ case '\\': text.append('\\'); return '\\';
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ int len = 0;
+ int val = 0;
+ do {
+ val = (val << 3) + Character.digit(d, 8);
+ text.append((char)d);
+ d = read();
+ } while (++len < 3 && Character.digit(d, 8) != -1);
+ unread(d);
+ return val;
+
+ case 'x':
+ len = 0;
+ val = 0;
+ do {
+ val = (val << 4) + Character.digit(d, 16);
+ text.append((char)d);
+ d = read();
+ } while (++len < 2 && Character.digit(d, 16) != -1);
+ unread(d);
+ return val;
+
+ /* Exclude two cases from the warning. */
+ case '"': text.append('"'); return '"';
+ case '\'': text.append('\''); return '\'';
+
+ default:
+ warning("Unnecessary escape character " + (char)d);
+ text.append((char)d);
+ return d;
+ }
+ }
+
+ private Token character()
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder("'");
+ int d = read();
+ if (d == '\\') {
+ text.append('\\');
+ d = escape(text);
+ }
+ else if (isLineSeparator(d)) {
+ unread(d);
+ error("Unterminated character literal");
+ return new Token(ERROR, text.toString(), null);
+ }
+ else if (d == '\'') {
+ text.append('\'');
+ error("Empty character literal");
+ return new Token(ERROR, text.toString(), null);
+ }
+ else if (!Character.isDefined(d)) {
+ text.append('?');
+ error("Illegal unicode character literal");
+ }
+ else {
+ text.append((char)d);
+ }
+
+ int e = read();
+ if (e != '\'') {
+ unread(e);
+ error("Illegal character constant");
+ /* XXX We could do some patching up here? */
+ return new Token(ERROR, text.toString(), null);
+ }
+ text.append('\'');
+ /* XXX Bad cast. */
+ return new Token(CHARACTER,
+ text.toString(), Character.valueOf((char)d));
+ }
+
+ /* XXX This strips the enclosing quotes from the
+ * returned value. */
+ private Token string(char open, char close)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder();
+ text.append(open);
+
+ StringBuilder buf = new StringBuilder();
+
+ for (;;) {
+ int c = read();
+ if (c == close) {
+ break;
+ }
+ else if (c == '\\') {
+ text.append('\\');
+ if (!include) {
+ char d = (char)escape(text);
+ buf.append(d);
+ }
+ }
+ else if (c == -1) {
+ unread(c);
+ error("End of file in string literal after " + buf);
+ return new Token(ERROR, text.toString(), null);
+ }
+ else if (isLineSeparator(c)) {
+ unread(c);
+ error("Unterminated string literal after " + buf);
+ return new Token(ERROR, text.toString(), null);
+ }
+ else {
+ text.append((char)c);
+ buf.append((char)c);
+ }
+ }
+ text.append(close);
+ return new Token(close == '>' ? HEADER : STRING,
+ text.toString(), buf.toString());
+ }
+
+ private void number_suffix(StringBuilder text, int d)
+ throws IOException {
+ if (d == 'U') {
+ text.append((char)d);
+ d = read();
+ }
+ if (d == 'L') {
+ text.append((char)d);
+ }
+ else if (d == 'I') {
+ text.append((char)d);
+ }
+ else {
+ unread(d);
+ }
+ }
+
+ /* We already chewed a zero, so empty is fine. */
+ private Token number_octal()
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder("0");
+ int d = read();
+ long val = 0;
+ while (Character.digit(d, 8) != -1) {
+ val = (val << 3) + Character.digit(d, 8);
+ text.append((char)d);
+ d = read();
+ }
+ number_suffix(text, d);
+ return new Token(INTEGER,
+ text.toString(), Long.valueOf(val));
+ }
+
+ /* We do not know whether know the first digit is valid. */
+ private Token number_hex(char x)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder("0");
+ text.append(x);
+ int d = read();
+ if (Character.digit(d, 16) == -1) {
+ unread(d);
+ error("Illegal hexadecimal constant " + (char)d);
+ return new Token(ERROR, text.toString(), null);
+ }
+ long val = 0;
+ do {
+ val = (val << 4) + Character.digit(d, 16);
+ text.append((char)d);
+ d = read();
+ } while (Character.digit(d, 16) != -1);
+ number_suffix(text, d);
+ return new Token(INTEGER,
+ text.toString(), Long.valueOf(val));
+ }
+
+ /* We know we have at least one valid digit, but empty is not
+ * fine. */
+ /* XXX This needs a complete rewrite. */
+ private Token number_decimal(int c)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder((char)c);
+ int d = c;
+ long val = 0;
+ do {
+ val = val * 10 + Character.digit(d, 10);
+ text.append((char)d);
+ d = read();
+ } while (Character.digit(d, 10) != -1);
+ number_suffix(text, d);
+ return new Token(INTEGER,
+ text.toString(), Long.valueOf(val));
+ }
+
+ private Token identifier(int c)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder();
+ int d;
+ text.append((char)c);
+ for (;;) {
+ d = read();
+ if (Character.isIdentifierIgnorable(d))
+ ;
+ else if (Character.isJavaIdentifierPart(d))
+ text.append((char)d);
+ else
+ break;
+ }
+ unread(d);
+ return new Token(IDENTIFIER, text.toString());
+ }
+
+ private Token whitespace(int c)
+ throws IOException,
+ LexerException {
+ StringBuilder text = new StringBuilder();
+ int d;
+ text.append((char)c);
+ for (;;) {
+ d = read();
+ if (ppvalid && isLineSeparator(d)) /* XXX Ugly. */
+ break;
+ if (Character.isWhitespace(d))
+ text.append((char)d);
+ else
+ break;
+ }
+ unread(d);
+ return new Token(WHITESPACE, text.toString());
+ }
+
+ /* No token processed by cond() contains a newline. */
+ private Token cond(char c, int yes, int no)
+ throws IOException {
+ int d = read();
+ if (c == d)
+ return new Token(yes);
+ unread(d);
+ return new Token(no);
+ }
+
+ public Token token()
+ throws IOException,
+ LexerException {
+ Token tok = null;
+
+ int _l = line;
+ int _c = column;
+
+ int c = read();
+ int d, e;
+
+ switch (c) {
+ case '\n':
+ if (ppvalid) {
+ bol = true;
+ if (include) {
+ tok = new Token(NL, _l, _c, new String("\n"));
+ }
+ else {
+ int nls = 0;
+ do {
+ d = read();
+ nls++;
+ } while (d == '\n');
+ unread(d);
+ char[] text = new char[nls];
+ for (int i = 0; i < text.length; i++)
+ text[i] = '\n';
+ // Skip the bol = false below.
+ tok = new Token(NL, _l, _c, new String(text));
+ }
+ if (DEBUG)
+ System.out.println("lx: Returning NL: " + tok);
+ return tok;
+ }
+ /* Let it be handled as whitespace. */
+ break;
+
+ case '!':
+ tok = cond('=', NE, '!');
+ break;
+
+ case '#':
+ if (bol)
+ tok = new Token(HASH);
+ else
+ tok = cond('#', PASTE, '#');
+ break;
+
+ case '+':
+ d = read();
+ if (d == '+')
+ tok = new Token(INC);
+ else if (d == '=')
+ tok = new Token(PLUS_EQ);
+ else
+ unread(d);
+ break;
+ case '-':
+ d = read();
+ if (d == '-')
+ tok = new Token(DEC);
+ else if (d == '=')
+ tok = new Token(SUB_EQ);
+ else if (d == '>')
+ tok = new Token(ARROW);
+ else
+ unread(d);
+ break;
+
+ case '*':
+ tok = cond('=', MULT_EQ, '*');
+ break;
+ case '/':
+ d = read();
+ if (d == '*')
+ tok = ccomment();
+ else if (d == '/')
+ tok = cppcomment();
+ else if (d == '=')
+ tok = new Token(DIV_EQ);
+ else
+ unread(d);
+ break;
+
+ case '%':
+ tok = cond('=', MOD_EQ, '%');
+ break;
+
+ case ':':
+ /* :: */
+ break;
+
+ case '<':
+ if (include) {
+ tok = string('<', '>');
+ }
+ else {
+ d = read();
+ if (d == '=')
+ tok = new Token(LE);
+ else if (d == '<')
+ tok = cond('=', LSH_EQ, LSH);
+ else
+ unread(d);
+ }
+ break;
+
+ case '=':
+ tok = cond('=', EQ, '=');
+ break;
+
+ case '>':
+ d = read();
+ if (d == '=')
+ tok = new Token(GE);
+ else if (d == '>')
+ tok = cond('=', RSH_EQ, RSH);
+ else
+ unread(d);
+ break;
+
+ case '^':
+ tok = cond('=', XOR_EQ, '^');
+ break;
+
+ case '|':
+ d = read();
+ if (d == '=')
+ tok = new Token(OR_EQ);
+ else if (d == '|')
+ tok = cond('=', LOR_EQ, LOR);
+ else
+ unread(d);
+ break;
+ case '&':
+ d = read();
+ if (d == '&')
+ tok = cond('=', LAND_EQ, LAND);
+ else if (d == '=')
+ tok = new Token(AND_EQ);
+ else
+ unread(d);
+ break;
+
+ case '.':
+ d = read();
+ if (d == '.')
+ tok = cond('.', ELLIPSIS, RANGE);
+ else
+ unread(d);
+ /* XXX decimal fraction */
+ break;
+
+ case '0':
+ /* octal or hex */
+ d = read();
+ if (d == 'x' || d == 'X')
+ tok = number_hex((char)d);
+ else {
+ unread(d);
+ tok = number_octal();
+ }
+ break;
+
+ case '\'':
+ tok = character();
+ break;
+
+ case '"':
+ tok = string('"', '"');
+ break;
+
+ case -1:
+ tok = new Token(EOF, _l, _c, "<eof>");
+ break;
+ }
+
+ if (tok == null) {
+ if (Character.isWhitespace(c)) {
+ tok = whitespace(c);
+ }
+ else if (Character.isDigit(c)) {
+ tok = number_decimal(c);
+ }
+ else if (Character.isJavaIdentifierStart(c)) {
+ tok = identifier(c);
+ }
+ else {
+ tok = new Token(c);
+ }
+ }
+
+ bol = false;
+
+ tok.setLocation(_l, _c);
+ if (DEBUG)
+ System.out.println("lx: Returning " + tok);
+ // (new Exception("here")).printStackTrace(System.out);
+ return tok;
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/Macro.java b/src/java/org/anarres/cpp/Macro.java
new file mode 100644
index 0000000..0d0ae55
--- /dev/null
+++ b/src/java/org/anarres/cpp/Macro.java
@@ -0,0 +1,157 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A macro object.
+ *
+ * This encapsulates a name, an argument count, and a token stream
+ * for replacement. The replacement token stream may contain the
+ * extra tokens {@link Token#M_ARG} and {@link Token#M_STRING}.
+ */
+public class Macro {
+ private String name;
+ /* It's an explicit decision to keep these around here. We don't
+ * need to; the argument token type is M_ARG and the value
+ * is the index. The strings themselves are only used in
+ * stringification of the macro, for debugging. */
+ private List<String> args;
+ private boolean variadic;
+ private List<Token> tokens;
+
+ public Macro(String name) {
+ this.name = name;
+ this.args = null;
+ this.variadic = false;
+ this.tokens = new ArrayList<Token>();
+ }
+
+ /**
+ * Returns the name of this macro.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the arguments to this macro.
+ */
+ public void setArgs(List<String> args) {
+ this.args = args;
+ }
+
+ /**
+ * Returns true if this is a function-like macro.
+ */
+ public boolean isFunctionLike() {
+ return args != null;
+ }
+
+ /**
+ * Returns the number of arguments to this macro.
+ */
+ public int getArgs() {
+ return args.size();
+ }
+
+ /**
+ * Sets the variadic flag on this Macro.
+ */
+ public void setVariadic(boolean b) {
+ this.variadic = b;
+ }
+
+ /**
+ * Returns true if this is a variadic function-like macro.
+ */
+ public boolean isVariadic() {
+ return variadic;
+ }
+
+ /**
+ * Adds a token to the expansion of this macro.
+ */
+ public void addToken(Token tok) {
+ this.tokens.add(tok);
+ }
+
+ /**
+ * Adds a "paste" operator to the expansion of this macro.
+ *
+ * A paste operator causes the next token added to be pasted
+ * to the previous token when the macro is expanded.
+ * It is an error for a macro to end with a paste token.
+ */
+ public void addPaste(Token tok) {
+ /*
+ * Given: tok0 ## tok1
+ * We generate: M_PASTE, tok0, tok1
+ * This extends as per a stack language:
+ * tok0 ## tok1 ## tok2 ->
+ * M_PASTE, tok0, M_PASTE, tok1, tok2
+ */
+ this.tokens.add(tokens.size() - 1, tok);
+ }
+
+ /* pp */ List<Token> getTokens() {
+ return tokens;
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder(name);
+ if (args != null) {
+ buf.append('(');
+ Iterator<String> it = args.iterator();
+ while (it.hasNext()) {
+ buf.append(it.next());
+ if (it.hasNext())
+ buf.append(", ");
+ else if (isVariadic())
+ buf.append("...");
+ }
+ buf.append(')');
+ }
+ if (!tokens.isEmpty()) {
+ boolean paste = false;
+ buf.append(" => ");
+ for (int i = 0; i < tokens.size(); i++) {
+ Token tok = tokens.get(i);
+ if (tok.getType() == Token.M_PASTE) {
+ paste = true;
+ continue;
+ }
+ else {
+ buf.append(tok.getText());
+ }
+ if (paste) {
+ buf.append(" #" + "# ");
+ paste = false;
+ }
+ // buf.append(tokens.get(i));
+ }
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/MacroTokenSource.java b/src/java/org/anarres/cpp/MacroTokenSource.java
new file mode 100644
index 0000000..249afdf
--- /dev/null
+++ b/src/java/org/anarres/cpp/MacroTokenSource.java
@@ -0,0 +1,191 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.io.StringReader;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import static org.anarres.cpp.Token.*;
+
+/* pp */ class MacroTokenSource extends Source {
+ private Macro macro;
+ private Iterator<Token> tokens; /* Pointer into the macro. */
+ private List<Argument> args; /* { unexpanded, expanded } */
+ private Iterator<Token> arg; /* "current expansion" */
+
+ /* pp */ MacroTokenSource(Macro m, List<Argument> args) {
+ this.macro = m;
+ this.tokens = m.getTokens().iterator();
+ this.args = args;
+ this.arg = null;
+ }
+
+ @Override
+ /* pp */ boolean isExpanding(Macro m) {
+ /* When we are expanding an arg, 'this' macro is not
+ * being expanded, and thus we may re-expand it. */
+ if (/* XXX this.arg == null && */ this.macro == m)
+ return true;
+ return super.isExpanding(m);
+ }
+
+ private static void escape(StringBuilder buf, CharSequence cs) {
+ for (int i = 0; i < cs.length(); i++) {
+ char c = cs.charAt(i);
+ switch (c) {
+ case '\\':
+ buf.append("\\\\");
+ break;
+ case '"':
+ buf.append("\\\"");
+ break;
+ case '\n':
+ buf.append("\\n");
+ break;
+ case '\r':
+ buf.append("\\r");
+ break;
+ default:
+ buf.append(c);
+ }
+ }
+ }
+
+ private void concat(StringBuilder buf, Argument arg) {
+ Iterator<Token> it = arg.iterator();
+ while (it.hasNext()) {
+ Token tok = it.next();
+ buf.append(tok.getText());
+ }
+ }
+
+ private Token stringify(Token pos, Argument arg) {
+ StringBuilder buf = new StringBuilder();
+ concat(buf, arg);
+ StringBuilder str = new StringBuilder("\"");
+ escape(str, buf);
+ str.append('\"');
+ return new Token(STRING,
+ pos.getLine(), pos.getColumn(),
+ str.toString(), buf.toString());
+ }
+
+
+ /* At this point, we have consumed the first M_PASTE.
+ * @see Macro#addPaste(Token) */
+ private void paste(Token ptok)
+ throws IOException,
+ LexerException {
+ StringBuilder buf = new StringBuilder();
+ /* We know here that arg is null or expired,
+ * since we cannot paste an expanded arg. */
+
+ int count = 2;
+ for (int i = 0; i < count; i++) {
+ if (!tokens.hasNext())
+ error(ptok.getLine(), ptok.getColumn(),
+ "Paste at end of expansion");
+ Token tok = tokens.next();
+ switch (tok.getType()) {
+ case M_PASTE:
+ /* One extra to paste, plus one because the
+ * paste token didn't count. */
+ count += 2;
+ ptok = tok;
+ break;
+ case M_ARG:
+ int idx = ((Integer)tok.getValue()).intValue();
+ concat(buf, args.get(idx));
+ break;
+ /* XXX Test this. */
+ case COMMENT:
+ break;
+ default:
+ buf.append(tok.getText());
+ break;
+ }
+ }
+
+ /* XXX Somewhere here, need to check that concatenation
+ * produces a valid token. */
+
+ /* Push and re-lex. */
+ StringBuilder src = new StringBuilder();
+ escape(src, buf);
+ StringLexerSource sl = new StringLexerSource(src.toString());
+
+ arg = new SourceIterator(sl);
+ }
+
+ public Token token()
+ throws IOException,
+ LexerException {
+ for (;;) {
+ /* Deal with lexed tokens first. */
+
+ if (arg != null) {
+ if (arg.hasNext())
+ return arg.next();
+ arg = null;
+ }
+
+ if (!tokens.hasNext())
+ return new Token(EOF, -1, -1, ""); /* End of macro. */
+ Token tok = tokens.next();
+ int idx;
+ switch (tok.getType()) {
+ case M_STRING:
+ /* Use the nonexpanded arg. */
+ idx = ((Integer)tok.getValue()).intValue();
+ return stringify(tok, args.get(idx));
+ case M_ARG:
+ /* Expand the arg. */
+ idx = ((Integer)tok.getValue()).intValue();
+ // System.out.println("Pushing arg " + args.get(idx));
+ arg = args.get(idx).expansion();
+ break;
+ case M_PASTE:
+ paste(tok);
+ break;
+ default:
+ return tok;
+ }
+ } /* for */
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("expansion of ").append(macro.getName());
+ Source parent = getParent();
+ if (parent != null)
+ buf.append(" in ").append(String.valueOf(parent));
+ return buf.toString();
+ }
+}
diff --git a/src/java/org/anarres/cpp/Main.java b/src/java/org/anarres/cpp/Main.java
new file mode 100644
index 0000000..cec7a37
--- /dev/null
+++ b/src/java/org/anarres/cpp/Main.java
@@ -0,0 +1,111 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * (Currently a simple test class).
+ */
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ List<String> path = new ArrayList<String>();
+ path.add("/usr/include");
+ path.add("/usr/local/include");
+ path.add("/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include");
+
+ Source source = new FileLexerSource(new File(args[0]));
+ Preprocessor pp = new Preprocessor(source);
+ pp.setIncludePath(path);
+
+ for (int i = 1; i < args.length; i++) {
+ pp.push_source(new FileLexerSource(new File(args[i])),true);
+ }
+
+ Macro m = new Macro("__WORDSIZE");
+ m.addToken(new Token(INTEGER, -1, -1, "32", Integer.valueOf(32)));
+ pp.addMacro(m);
+
+ m = new Macro("__STDC__");
+ m.addToken(new Token(INTEGER, -1, -1, "1", Integer.valueOf(1)));
+ pp.addMacro(m);
+
+ try {
+ for (;;) {
+ Token tok = pp.token();
+ if (tok != null && tok.getType() == Token.EOF)
+ break;
+ switch (2) {
+ case 0:
+ System.out.print(tok);
+ break;
+ case 1:
+ System.out.print("[" + tok.getText() + "]");
+ break;
+ case 2:
+ System.out.print(tok.getText());
+ break;
+ }
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ Source s = pp.getSource();
+ while (s != null) {
+ System.out.println(" -> " + s);
+ s = s.getParent();
+ }
+
+ /*
+ Iterator<State> it = pp.states.iterator();
+ while (it.hasNext()) {
+ System.out.println(" -? " + it.next());
+ }
+ */
+
+ }
+
+ Map<String,Macro> macros = pp.getMacros();
+ List<String> keys = new ArrayList<String>(
+ macros.keySet()
+ );
+ Collections.sort(keys);
+ Iterator<String> mt = keys.iterator();
+ while (mt.hasNext()) {
+ String key = mt.next();
+ Macro macro = macros.get(key);
+ System.out.println("#" + "macro " + macro);
+ }
+
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/Preprocessor.java b/src/java/org/anarres/cpp/Preprocessor.java
new file mode 100644
index 0000000..c1b87d7
--- /dev/null
+++ b/src/java/org/anarres/cpp/Preprocessor.java
@@ -0,0 +1,1511 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * A C Preprocessor.
+ * The Preprocessor outputs a token stream which does not need
+ * re-lexing for C or C++. Alternatively, the output text may be
+ * reconstructed by concatenating the {@link Token#getText() text}
+ * values of the returned {@link Token Tokens}. (See
+ * {@link CppReader}, which does this.)
+ */
+public class Preprocessor {
+ private static final boolean DEBUG = false;
+
+ public static final int FL_LINEMARKER = 1;
+
+ private static final Macro __LINE__ = new Macro("__LINE__");
+ private static final Macro __FILE__ = new Macro("__FILE__");
+
+ private Map<String,Macro> macros;
+ private Stack<State> states;
+ private Source source;
+
+ private List<String> path;
+ private PreprocessorListener listener;
+
+ private int flags;
+
+ public Preprocessor(Source initial, int flags) {
+ this.macros = new HashMap<String,Macro>();
+ macros.put(__LINE__.getName(), __LINE__);
+ macros.put(__FILE__.getName(), __FILE__);
+ this.states = new Stack<State>();
+ states.push(new State());
+ this.source = null;
+ this.path = null;
+ setListener(new PreprocessorListener());
+ setFlags(flags);
+
+ push_source(initial, false);
+ /* We need to get a \n onto the end of this somehow. */
+ if ((flags & FL_LINEMARKER) != 0)
+ source_untoken(line_token(1, source.getName(), "\n"));
+ }
+
+ public Preprocessor(Source initial) {
+ this(initial, 0);
+ }
+
+ /** Equivalent to
+ * 'new Preprocessor(new {@link FileLexerSource}(file))'
+ */
+ public Preprocessor(File file)
+ throws IOException {
+ this(new FileLexerSource(file), 0);
+ }
+
+ public void setListener(PreprocessorListener listener) {
+ this.listener = listener;
+ Source s = source;
+ while (s != null) {
+ s.setListener(listener);
+ s = s.getParent();
+ }
+ }
+
+ public void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Handles an error.
+ *
+ * If a PreprocessorListener is installed, it receives the
+ * error. Otherwise, it is ignored.
+ */
+ protected void error(Token tok, String msg)
+ throws LexerException {
+ if (listener != null)
+ listener.handleError(source,
+ tok.getLine(), tok.getColumn(),
+ msg);
+ }
+
+ /**
+ * Handles a warning.
+ *
+ * If a PreprocessorListener is installed, it receives the
+ * warning. Otherwise, it is ignored.
+ */
+ protected void warning(Token tok, String msg)
+ throws LexerException {
+ if (listener != null)
+ listener.handleError(source,
+ tok.getLine(), tok.getColumn(),
+ msg);
+ }
+
+/*
+ public void setSource(Source source) {
+ this.source = source;
+ }
+*/
+
+ public void addMacro(Macro m) throws LexerException {
+ String name = m.getName();
+ /* Already handled as a source error in macro(). */
+ if ("defined".equals(name))
+ throw new LexerException("Cannot redefine name 'defined'");
+ macros.put(m.getName(), m);
+ }
+
+ /**
+ * Defines the given name as a macro.
+ *
+ * This is a convnience method.
+ */
+ public void addMacro(String name, String value)
+ throws LexerException {
+ try {
+ Macro m = new Macro(name);
+ StringLexerSource s = new StringLexerSource(value);
+ for (;;) {
+ Token tok = s.token();
+ if (tok.getType() == EOF)
+ break;
+ m.addToken(tok);
+ }
+ addMacro(m);
+ }
+ catch (IOException e) {
+ throw new LexerException(e);
+ }
+ }
+
+ /**
+ * Defines the given name as a macro.
+ *
+ * This is a convnience method.
+ */
+ public void addMacro(String name)
+ throws LexerException {
+ addMacro(name, "1");
+ }
+
+ /**
+ * Sets the include path used by this Preprocessor.
+ */
+ /* Note for future: Create an IncludeHandler? */
+ public void setIncludePath(List<String> path) {
+ this.path = path;
+ }
+
+ /**
+ * Returns the Map of Macros parsed during the run of this
+ * Preprocessor.
+ */
+ protected Map<String,Macro> getMacros() {
+ return macros;
+ }
+
+
+/* States */
+
+ private void push_state() {
+ State top = states.peek();
+ states.push(new State(top));
+ }
+
+ private void pop_state()
+ throws LexerException {
+ State s = states.pop();
+ if (states.isEmpty()) {
+ if (listener != null)
+ listener.handleError(getSource(), 0, 0,
+ "#" + "endif without #" + "if");
+ states.push(s);
+ }
+ }
+
+ private boolean isActive() {
+ State state = states.peek();
+ return state.isParentActive() && state.isActive();
+ }
+
+
+/* Sources */
+
+ /**
+ * Returns the top Source on the input stack.
+ *
+ * @see Source
+ * @see #push_source(Source,boolean)
+ * @see #pop_source()
+ */
+ protected Source getSource() {
+ return source;
+ }
+
+ /**
+ * Pushes a Source onto the input stack.
+ *
+ * @see #getSource()
+ * @see #pop_source()
+ */
+ protected void push_source(Source source, boolean autopop) {
+ source.setParent(this.source, autopop);
+ source.setListener(listener);
+ this.source = source;
+ if (listener != null)
+ listener.handleSourceChange(this.source, "push");
+ }
+
+ /**
+ * Pops a Source from the input stack.
+ *
+ * @see #getSource()
+ * @see #push_source(Source,boolean)
+ */
+ protected void pop_source() {
+ this.source = this.source.getParent();
+ if (listener != null)
+ listener.handleSourceChange(this.source, "pop");
+ }
+
+
+/* Source tokens */
+
+ private Token source_token;
+
+ private Token line_token(int line, String name, String extra) {
+ return new Token(P_LINE, line, 0,
+ "#line " + line + " \"" + name + "\"" + extra,
+ null
+ );
+ }
+
+ private Token source_token()
+ throws IOException,
+ LexerException {
+ if (source_token != null) {
+ Token tok = source_token;
+ source_token = null;
+ return tok;
+ }
+
+ for (;;) {
+ Token tok = source.token();
+ if (tok.getType() == EOF && source.isAutopop()) {
+ // System.out.println("Autopop " + source);
+ Source s = source;
+ pop_source();
+ if ((flags & FL_LINEMARKER) != 0 && s.isNumbered()) {
+ /* Not perfect, but ... */
+ source_untoken(new Token(NL, source.getLine(), 0, "\n"));
+ return line_token(source.getLine(), source.getName(), "");
+ }
+ else {
+ continue;
+ }
+ }
+ return tok;
+ }
+ }
+
+ private void source_untoken(Token tok) {
+ if (this.source_token != null)
+ throw new IllegalStateException("Cannot return two tokens");
+ this.source_token = tok;
+ }
+
+ private boolean isWhite(Token tok) {
+ int type = tok.getType();
+ return (type == WHITESPACE) || (type == COMMENT);
+ }
+
+ private Token source_token_nonwhite()
+ throws IOException,
+ LexerException {
+ Token tok;
+ do {
+ tok = source_token();
+ } while (isWhite(tok));
+ return tok;
+ }
+
+ /**
+ * Returns an NL or an EOF token.
+ *
+ * The metadata on the token will be correct, which is better
+ * than generating a new one.
+ */
+ private Token source_skipline(boolean white)
+ throws IOException,
+ LexerException {
+ // (new Exception("skipping line")).printStackTrace(System.out);
+ return source.skipline(white);
+ }
+
+ /* processes and expands a macro. */
+ private boolean macro(Macro m, Token orig)
+ throws IOException,
+ LexerException {
+ Token tok;
+ List<Argument> args;
+
+ // System.out.println("pp: expanding " + m);
+
+ if (m.isFunctionLike()) {
+ OPEN: for (;;) {
+ tok = source_token();
+ // System.out.println("pp: open: token is " + tok);
+ switch (tok.getType()) {
+ case WHITESPACE: /* XXX Really? */
+ case COMMENT:
+ case NL:
+ break; /* continue */
+ case '(':
+ break OPEN;
+ default:
+ source_untoken(tok);
+ return false;
+ }
+ }
+
+ // tok = expanded_token_nonwhite();
+ tok = source_token_nonwhite();
+
+ /* We either have, or we should have args.
+ * This deals elegantly with the case that we have
+ * one empty arg. */
+ if (tok.getType() != ')' || m.getArgs() > 0) {
+ args = new ArrayList<Argument>();
+
+ Argument arg = new Argument();
+ int depth = 0;
+ boolean space = false;
+
+ ARGS: for (;;) {
+ // System.out.println("pp: arg: token is " + tok);
+ switch (tok.getType()) {
+ case EOF:
+ error(tok, "EOF in macro args");
+ return false;
+
+ case ',':
+ if (depth == 0) {
+ if (m.isVariadic() &&
+ /* We are building the last arg. */
+ args.size() == m.getArgs() - 1) {
+ /* Just add the comma. */
+ arg.addToken(tok);
+ }
+ else {
+ args.add(arg);
+ arg = new Argument();
+ }
+ }
+ else {
+ arg.addToken(tok);
+ }
+ space = false;
+ break;
+ case ')':
+ if (depth == 0) {
+ args.add(arg);
+ break ARGS;
+ }
+ else {
+ depth--;
+ arg.addToken(tok);
+ }
+ space = false;
+ break;
+ case '(':
+ depth++;
+ arg.addToken(tok);
+ space = false;
+ break;
+
+ case WHITESPACE:
+ case COMMENT:
+ /* Avoid duplicating spaces. */
+ space = true;
+ break;
+
+ default:
+ /* Do not put space on the beginning of
+ * an argument token. */
+ if (space && ! arg.isEmpty())
+ arg.addToken(Token.space);
+ arg.addToken(tok);
+ space = false;
+ break;
+
+ }
+ // tok = expanded_token();
+ tok = source_token();
+ }
+ /* space may still be true here, thus trailing space
+ * is stripped from arguments. */
+
+ if (args.size() != m.getArgs()) {
+ error(tok,
+ "macro " + m.getName() +
+ " has " + m.getArgs() + " parameters " +
+ "but given " + args.size() + " args");
+ /* We could replay the arg tokens, but I
+ * note that GNU cpp does exactly what we do,
+ * i.e. output the macro name and chew the args.
+ */
+ return false;
+ }
+
+ for (int i = 0; i < args.size(); i++) {
+ args.get(i).expand(this);
+ }
+
+ // System.out.println("Macro " + m + " args " + args);
+ }
+ else {
+ /* nargs == 0 and we (correctly) got () */
+ args = null;
+ }
+
+ }
+ else {
+ /* Macro without args. */
+ args = null;
+ }
+
+ if (m == __LINE__) {
+ push_source(new FixedTokenSource(
+ new Token[] { new Token(INTEGER,
+ orig.getLine(), orig.getColumn(),
+ String.valueOf(orig.getLine()),
+ Integer.valueOf(orig.getLine())) }
+ ), true);
+ }
+ else if (m == __FILE__) {
+ File file = source.getFile();
+ push_source(new FixedTokenSource(
+ new Token[] { new Token(STRING,
+ orig.getLine(), orig.getColumn(),
+ '"'+ String.valueOf(file) +'"',
+ file) }
+ ), true);
+ }
+ else {
+ push_source(new MacroTokenSource(m, args), true);
+ }
+
+ return true;
+ }
+
+ /**
+ * Expands an argument.
+ */
+ /* I'd rather this were done lazily. */
+ /* pp */ List<Token> expand(List<Token> arg)
+ throws IOException,
+ LexerException {
+ List<Token> expansion = new ArrayList<Token>();
+ boolean space = false;
+
+ push_source(new FixedTokenSource(arg), false);
+ EXPANSION: for (;;) {
+ Token tok = expanded_token();
+ switch (tok.getType()) {
+ case EOF:
+ break EXPANSION;
+
+ case WHITESPACE:
+ case COMMENT:
+ space = true;
+ break;
+
+ default:
+ if (space && ! expansion.isEmpty())
+ expansion.add(Token.space);
+ expansion.add(tok);
+ space = false;
+ break;
+ }
+ }
+
+ pop_source();
+
+ return expansion;
+ }
+
+ /* processes a #define directive */
+ private Token define()
+ throws IOException,
+ LexerException {
+ Token tok = source_token_nonwhite();
+ if (tok.getType() != IDENTIFIER) {
+ error(tok, "Expected identifier");
+ return source_skipline(false);
+ }
+ /* if predefined */
+
+ String name = tok.getText();
+ if ("defined".equals(name)) {
+ error(tok, "Cannot redefine name 'defined'");
+ return source_skipline(false);
+ }
+
+ Macro m = new Macro(name);
+ List<String> args;
+
+ tok = source_token();
+ if (tok.getType() == '(') {
+ tok = source_token_nonwhite();
+ if (tok.getType() != ')') {
+ args = new ArrayList<String>();
+ ARGS: for (;;) {
+ switch (tok.getType()) {
+ case IDENTIFIER:
+ args.add(tok.getText());
+ break;
+ // case ELLIPSIS:
+ case NL:
+ case EOF:
+ error(tok,
+ "Unterminated macro parameter list");
+ break ARGS;
+ default:
+ source_skipline(false);
+ error(tok,
+ "error in macro parameters: " +
+ tok.getText());
+ /* XXX return? */
+ break ARGS;
+ }
+ tok = source_token_nonwhite();
+ switch (tok.getType()) {
+ case ',':
+ break;
+ case ')':
+ tok = source_token_nonwhite();
+ break ARGS;
+ case ELLIPSIS:
+ tok = source_token_nonwhite();
+ if (tok.getType() != ')')
+ error(tok,
+ "ellipsis must be on last argument");
+ m.setVariadic(true);
+ tok = source_token_nonwhite();
+ break ARGS;
+
+ case NL:
+ case EOF:
+ /* Do not skip line. */
+ error(tok,
+ "Unterminated macro definition");
+ break ARGS;
+ default:
+ source_skipline(false);
+ error(tok,
+ "bad token in macro parameters: " +
+ tok.getText());
+ /* XXX return? */
+ break ARGS;
+ }
+ tok = source_token_nonwhite();
+ }
+ }
+ else {
+ tok = source_token_nonwhite(); /* Lose the ')' */
+ args = Collections.emptyList();
+ }
+
+ m.setArgs(args);
+ }
+ else {
+ /* For searching. */
+ args = Collections.emptyList();
+ if (tok.getType() == COMMENT ||
+ tok.getType() == WHITESPACE) {
+ tok = source_token_nonwhite();
+ }
+ }
+
+ /* Get an expansion for the macro, using indexOf. */
+ boolean space = false;
+ boolean paste = false;
+ /* XXX UGLY: Ensure no space at start.
+ * Careful not to break EOF/LF from above. */
+ if (isWhite(tok)) /* XXX Not sure this can ever happen now. */
+ tok = source_token_nonwhite();
+ int idx;
+
+ EXPANSION: for (;;) {
+ switch (tok.getType()) {
+ case EOF:
+ break EXPANSION;
+ case NL:
+ break EXPANSION;
+
+ case COMMENT:
+ // break;
+ case WHITESPACE:
+ if (!paste)
+ space = true;
+ break;
+
+ case PASTE:
+ space = false;
+ paste = true;
+ m.addPaste(new Token(M_PASTE,
+ tok.getLine(), tok.getColumn(),
+ "#" + "#", null));
+ break;
+
+ case '#':
+ if (space)
+ m.addToken(Token.space);
+ space = false;
+ Token la = source_token_nonwhite();
+ if (la.getType() == IDENTIFIER &&
+ ((idx = args.indexOf(la.getText())) != -1)) {
+ m.addToken(new Token(M_STRING,
+ la.getLine(), la.getColumn(),
+ "#" + la.getText(),
+ Integer.valueOf(idx)));
+ }
+ else {
+ m.addToken(tok);
+ /* Allow for special processing. */
+ source_untoken(la);
+ }
+ break;
+
+ case IDENTIFIER:
+ if (space)
+ m.addToken(Token.space);
+ space = false;
+ paste = false;
+ idx = args.indexOf(tok.getText());
+ if (idx == -1)
+ m.addToken(tok);
+ else
+ m.addToken(new Token(M_ARG,
+ tok.getLine(), tok.getColumn(),
+ tok.getText(),
+ Integer.valueOf(idx)));
+ break;
+
+ default:
+ if (space)
+ m.addToken(Token.space);
+ space = false;
+ paste = false;
+ m.addToken(tok);
+ break;
+ }
+ tok = source_token();
+ }
+
+ // if (DEBUG)
+ // System.out.println("Defined macro " + m);
+ addMacro(m);
+
+ return tok; /* NL or EOF. */
+ }
+
+ private Token undef()
+ throws IOException,
+ LexerException {
+ Token tok = source_token_nonwhite();
+ if (tok.getType() != IDENTIFIER) {
+ error(tok,
+ "Expected identifier, not " + tok.getText());
+ if (tok.getType() == NL || tok.getType() == EOF)
+ return tok;
+ }
+ else {
+ Macro m = macros.get(tok.getText());
+ if (m != null) {
+ /* XXX error if predefined */
+ macros.remove(m.getName());
+ }
+ }
+ return source_skipline(true);
+ }
+
+ /**
+ * Handles a include directive.
+ *
+ * The user may override this to provide alternate semantics
+ * for the include directive, for example, creating a Source
+ * based on a virtual file system.
+ */
+ protected void include(File parent, int line,
+ String name, boolean quoted)
+ throws IOException,
+ LexerException {
+ if (quoted) {
+ File dir = parent.getParentFile();
+ if (dir == null)
+ dir = new File("/");
+ File file = new File(dir, name);
+ // System.err.println("Include: " + file);
+ if (file.exists()) {
+ push_source(new FileLexerSource(file), true);
+ return;
+ }
+ }
+
+ if (path != null) {
+ for (int i = 0; i < path.size(); i++) {
+ File file = new File(
+ path.get(i) + File.separator + name
+ );
+ if (file.exists()) {
+ // System.err.println("Include: " + file);
+ push_source(new FileLexerSource(file), true);
+ return;
+ }
+ }
+ }
+
+ if (listener != null)
+ listener.handleError(getSource(),
+ line, 0,
+ "Header not found: " + name + " in " + path
+ );
+ }
+
+ private Token include()
+ throws IOException,
+ LexerException {
+ LexerSource lexer = (LexerSource)source;
+ try {
+ lexer.setInclude(true);
+ Token tok = token_nonwhite();
+
+ String name;
+ boolean quoted;
+
+ if (tok.getType() == STRING) {
+ /* XXX Use the original text, not the value.
+ * Backslashes must not be treated as escapes here. */
+ StringBuilder buf = new StringBuilder((String)tok.getValue());
+ HEADER: for (;;) {
+ tok = _token(); /* Do macros but nothing else. */
+ switch (tok.getType()) {
+ case WHITESPACE:
+ case COMMENT:
+ continue;
+ case STRING:
+ buf.append((String)tok.getValue());
+ break;
+ case NL:
+ case EOF:
+ break HEADER;
+ default:
+ warning(tok,
+ "Unexpected token on #"+"include line");
+ return source_skipline(false);
+ }
+ }
+ name = buf.toString();
+ quoted = true;
+ }
+ else if (tok.getType() == HEADER) {
+ name = (String)tok.getValue();
+ quoted = false;
+ tok = source_skipline(true);
+ }
+ else {
+ error(tok,
+ "Expected string or header, not " + tok.getText());
+ switch (tok.getType()) {
+ case NL:
+ case EOF:
+ return tok;
+ default:
+ /* Only if not a NL or EOF already. */
+ return source_skipline(false);
+ }
+ }
+
+ /* Do the inclusion. */
+ include(source.getFile(), tok.getLine(), name, quoted);
+
+ /* 'tok' is the 'nl' after the include. We use it after the
+ * #line directive. */
+ if ((flags & FL_LINEMARKER) != 0) {
+ source_untoken(tok);
+ return line_token(1, name, "");
+ }
+ return tok;
+ }
+ finally {
+ lexer.setInclude(false);
+ }
+ }
+
+ /* For #error and #warning. */
+ private void error(Token pptok, boolean is_error)
+ throws IOException,
+ LexerException {
+ StringBuilder buf = new StringBuilder();
+ buf.append('#').append(pptok.getText()).append(' ');
+ /* Peculiar construction to ditch first whitespace. */
+ Token tok = source_token_nonwhite();
+ ERROR: for (;;) {
+ switch (tok.getType()) {
+ case NL:
+ case EOF:
+ break ERROR;
+ default:
+ buf.append(tok.getText());
+ break;
+ }
+ tok = source_token();
+ }
+ if (is_error)
+ error(pptok, buf.toString());
+ else
+ warning(pptok, buf.toString());
+ }
+
+
+
+
+ /* This bypasses token() for #elif expressions.
+ * If we don't do this, then isActive() == false
+ * causes token() to simply chew the entire input line. */
+ private Token expanded_token()
+ throws IOException,
+ LexerException {
+ for (;;) {
+ Token tok = source_token();
+ // System.out.println("Source token is " + tok);
+ if (tok.getType() == IDENTIFIER) {
+ Macro m = macros.get(tok.getText());
+ if (m == null)
+ return tok;
+ if (source.isExpanding(m))
+ return tok;
+ if (macro(m, tok))
+ continue;
+ }
+ return tok;
+ }
+ }
+
+ private Token expanded_token_nonwhite()
+ throws IOException,
+ LexerException {
+ Token tok;
+ do {
+ tok = expanded_token();
+ // System.out.println("expanded token is " + tok);
+ } while (isWhite(tok));
+ return tok;
+ }
+
+
+ private Token expr_token = null;
+
+ private Token expr_token()
+ throws IOException,
+ LexerException {
+ Token tok = expr_token;
+
+ if (tok != null) {
+ // System.out.println("ungetting");
+ expr_token = null;
+ }
+ else {
+ tok = expanded_token_nonwhite();
+ // System.out.println("expt is " + tok);
+
+ if (tok.getType() == IDENTIFIER &&
+ tok.getText().equals("defined")) {
+ Token la = source_token_nonwhite();
+ boolean paren = false;
+ if (la.getType() == '(') {
+ paren = true;
+ la = source_token_nonwhite();
+ }
+
+ // System.out.println("Core token is " + la);
+
+ if (la.getType() != IDENTIFIER) {
+ error(la,
+ "defined() needs identifier, not " +
+ la.getText());
+ tok = new Token(INTEGER,
+ la.getLine(), la.getColumn(),
+ "0", Integer.valueOf(0));
+ }
+ else if (macros.containsKey(la.getText())) {
+ // System.out.println("Found macro");
+ tok = new Token(INTEGER,
+ la.getLine(), la.getColumn(),
+ "1", Integer.valueOf(1));
+ }
+ else {
+ // System.out.println("Not found macro");
+ tok = new Token(INTEGER,
+ la.getLine(), la.getColumn(),
+ "0", Integer.valueOf(0));
+ }
+
+ if (paren) {
+ la = source_token_nonwhite();
+ if (la.getType() != ')') {
+ expr_untoken(la);
+ error(la, "Missing ) in defined()");
+ }
+ }
+ }
+ }
+
+ // System.out.println("expr_token returns " + tok);
+
+ return tok;
+ }
+
+ private void expr_untoken(Token tok)
+ throws LexerException {
+ if (expr_token != null)
+ throw new InternalException(
+ "Cannot unget two expression tokens."
+ );
+ expr_token = tok;
+ }
+
+ private int expr_priority(Token op) {
+ switch (op.getType()) {
+ case '/': return 11;
+ case '%': return 11;
+ case '*': return 11;
+ case '+': return 10;
+ case '-': return 10;
+ case LSH: return 9;
+ case RSH: return 9;
+ case '<': return 8;
+ case '>': return 8;
+ case LE: return 8;
+ case GE: return 8;
+ case EQ: return 7;
+ case NE: return 7;
+ case '&': return 6;
+ case '^': return 5;
+ case '|': return 4;
+ case LAND: return 3;
+ case LOR: return 2;
+ case '?': return 1;
+ default:
+ // System.out.println("Unrecognised operator " + op);
+ return 0;
+ }
+ }
+
+ private long expr(int priority)
+ throws IOException,
+ LexerException {
+ /*
+ System.out.flush();
+ (new Exception("expr(" + priority + ") called")).printStackTrace();
+ System.err.flush();
+ */
+
+ Token tok = expr_token();
+ long lhs, rhs;
+
+ // System.out.println("Expr lhs token is " + tok);
+
+ switch (tok.getType()) {
+ case '(':
+ lhs = expr(0);
+ tok = expr_token();
+ if (tok.getType() != ')') {
+ expr_untoken(tok);
+ error(tok, "missing ) in expression");
+ return 0;
+ }
+ break;
+
+ case '~': lhs = ~expr(11); break;
+ case '!': lhs = expr(11) == 0 ? 1 : 0; break;
+ case '-': lhs = -expr(11); break;
+ case INTEGER:
+ lhs = ((Number)tok.getValue()).longValue();
+ break;
+ case CHARACTER:
+ lhs = (long)((Character)tok.getValue()).charValue();
+ break;
+ case IDENTIFIER:
+ /* XXX warn */
+ lhs = 0;
+ break;
+
+ default:
+ expr_untoken(tok);
+ error(tok,
+ "Bad token in expression: " + tok.getText());
+ return 0;
+ }
+
+ EXPR: for (;;) {
+ // System.out.println("expr: lhs is " + lhs + ", pri = " + priority);
+ Token op = expr_token();
+ int pri = expr_priority(op); /* 0 if not a binop. */
+ if (pri == 0 || priority >= pri) {
+ expr_untoken(op);
+ break EXPR;
+ }
+ rhs = expr(pri);
+ // System.out.println("rhs token is " + rhs);
+ switch (op.getType()) {
+ case '/':
+ if (rhs == 0) {
+ error(op, "Division by zero");
+ lhs = 0;
+ }
+ else {
+ lhs = lhs / rhs;
+ }
+ break;
+ case '%':
+ if (rhs == 0) {
+ error(op, "Modulus by zero");
+ lhs = 0;
+ }
+ else {
+ lhs = lhs % rhs;
+ }
+ break;
+ case '*': lhs = lhs * rhs; break;
+ case '+': lhs = lhs + rhs; break;
+ case '-': lhs = lhs - rhs; break;
+ case '<': lhs = lhs < rhs ? 1 : 0; break;
+ case '>': lhs = lhs > rhs ? 1 : 0; break;
+ case '&': lhs = lhs & rhs; break;
+ case '^': lhs = lhs ^ rhs; break;
+ case '|': lhs = lhs | rhs; break;
+
+ case LSH: lhs = lhs << rhs; break;
+ case RSH: lhs = lhs >> rhs; break;
+ case LE: lhs = lhs <= rhs ? 1 : 0; break;
+ case GE: lhs = lhs >= rhs ? 1 : 0; break;
+ case EQ: lhs = lhs == rhs ? 1 : 0; break;
+ case NE: lhs = lhs != rhs ? 1 : 0; break;
+ case LAND: lhs = (lhs != 0) && (rhs != 0) ? 1 : 0; break;
+ case LOR: lhs = (lhs != 0) || (rhs != 0) ? 1 : 0; break;
+
+ case '?':
+ /* XXX Handle this? */
+
+ default:
+ error(op,
+ "Unexpected operator " + op.getText());
+ return 0;
+
+ }
+ }
+
+ /*
+ System.out.flush();
+ (new Exception("expr returning " + lhs)).printStackTrace();
+ System.err.flush();
+ */
+ // System.out.println("expr returning " + lhs);
+
+ return lhs;
+ }
+
+ private Token toWhitespace(Token tok) {
+ String text = tok.getText();
+ int len = text.length();
+ boolean cr = false;
+ int nls = 0;
+
+ for (int i = 0; i < len; i++) {
+ char c = text.charAt(i);
+
+ switch (c) {
+ case '\r':
+ cr = true;
+ nls++;
+ break;
+ case '\n':
+ if (cr) {
+ cr = false;
+ break;
+ }
+ /* fallthrough */
+ case '\u2028':
+ case '\u2029':
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ cr = false;
+ nls++;
+ break;
+ }
+ }
+
+ char[] cbuf = new char[nls];
+ Arrays.fill(cbuf, '\n');
+ return new Token(WHITESPACE,
+ tok.getLine(), tok.getColumn(),
+ new String(cbuf));
+ }
+
+ private final Token _token()
+ throws IOException,
+ LexerException {
+
+ Token tok;
+ for (;;) {
+ if (!isActive()) {
+ /* Tell lexer to ignore warnings. */
+ tok = source_token();
+ /* Tell lexer to stop ignoring warnings. */
+ switch (tok.getType()) {
+ case HASH:
+ case NL:
+ case EOF:
+ /* The preprocessor has to take action here. */
+ break;
+ case WHITESPACE:
+ case COMMENT:
+ // Patch up to preserve whitespace.
+ /* XXX We might want to return tok here in C */
+ return toWhitespace(tok);
+ default:
+ // Return NL to preserve whitespace.
+ return source_skipline(false);
+ }
+ }
+ else {
+ tok = source_token();
+ }
+
+ LEX: switch (tok.getType()) {
+ case EOF:
+ /* Pop the stacks. */
+ return tok;
+
+ case WHITESPACE:
+ case NL:
+ return tok;
+
+ case COMMENT:
+ return tok;
+
+ case '!': case '%': case '&':
+ case '(': case ')': case '*':
+ case '+': case ',': case '-':
+ case '/': case ':': case ';':
+ case '<': case '=': case '>':
+ case '?': case '[': case ']':
+ case '^': case '{': case '|':
+ case '}': case '~': case '.':
+
+ // case '#':
+
+ case AND_EQ:
+ case ARROW:
+ case CHARACTER:
+ case DEC:
+ case DIV_EQ:
+ case ELLIPSIS:
+ case EQ:
+ case GE:
+ case HEADER: /* Should only arise from include() */
+ case INC:
+ case LAND:
+ case LE:
+ case LOR:
+ case LSH:
+ case LSH_EQ:
+ case SUB_EQ:
+ case MOD_EQ:
+ case MULT_EQ:
+ case NE:
+ case OR_EQ:
+ case PLUS_EQ:
+ case RANGE:
+ case RSH:
+ case RSH_EQ:
+ case STRING:
+ case XOR_EQ:
+ return tok;
+
+ case INTEGER:
+ return tok;
+
+ case IDENTIFIER:
+ Macro m = macros.get(tok.getText());
+ if (m == null)
+ return tok;
+ if (source.isExpanding(m))
+ return tok;
+ if (macro(m, tok))
+ break;
+ return tok;
+
+ case P_LINE:
+ if ((flags & FL_LINEMARKER) != 0)
+ return tok;
+ break;
+
+ case ERROR:
+ return tok;
+
+ default:
+ throw new InternalException("Bad token " + tok);
+ // break;
+
+ case HASH:
+ tok = source_token_nonwhite();
+ // (new Exception("here")).printStackTrace();
+ switch (tok.getType()) {
+ case NL:
+ break LEX; /* Some code has #\n */
+ case IDENTIFIER:
+ break;
+ default:
+ error(tok,
+ "Preprocessor directive not a word " +
+ tok.getText());
+ return source_skipline(false);
+ }
+ Integer _ppcmd = ppcmds.get(tok.getText());
+ if (_ppcmd == null) {
+ error(tok,
+ "Unknown preprocessor directive " +
+ tok.getText());
+ return source_skipline(false);
+ }
+ int ppcmd = _ppcmd.intValue();
+
+ switch (ppcmd) {
+
+ case PP_DEFINE:
+ if (!isActive())
+ return source_skipline(false);
+ else
+ return define();
+ // break;
+
+ case PP_UNDEF:
+ if (!isActive())
+ return source_skipline(false);
+ else
+ return undef();
+ // break;
+
+ case PP_INCLUDE:
+ if (!isActive())
+ return source_skipline(false);
+ else
+ return include();
+ // break;
+
+ case PP_WARNING:
+ case PP_ERROR:
+ if (!isActive())
+ return source_skipline(false);
+ else
+ error(tok, ppcmd == PP_ERROR);
+ break;
+
+ case PP_IF:
+ push_state();
+ if (!isActive()) {
+ return source_skipline(false);
+ }
+ expr_token = null;
+ states.peek().setActive(expr(0) != 0);
+ tok = expr_token(); /* unget */
+ if (tok.getType() == NL)
+ return tok;
+ return source_skipline(true);
+ // break;
+
+ case PP_ELIF:
+ State state = states.peek();
+ if (false) {
+ /* Check for 'if' */ ;
+ }
+ else if (state.sawElse()) {
+ error(tok,
+ "#elif after #" + "else");
+ return source_skipline(false);
+ }
+ else if (!state.isParentActive()) {
+ /* Nested in skipped 'if' */
+ return source_skipline(false);
+ }
+ else if (state.isActive()) {
+ /* The 'if' part got executed. */
+ state.setParentActive(false);
+ /* This is like # else # if but with
+ * only one # end. */
+ state.setActive(false);
+ return source_skipline(false);
+ }
+ else {
+ expr_token = null;
+ state.setActive(expr(0) != 0);
+ tok = expr_token(); /* unget */
+ if (tok.getType() == NL)
+ return tok;
+ return source_skipline(true);
+ }
+ // break;
+
+ case PP_ELSE:
+ state = states.peek();
+ if (false)
+ /* Check for 'if' */ ;
+ else if (state.sawElse()) {
+ error(tok,
+ "#" + "else after #" + "else");
+ return source_skipline(false);
+ }
+ else {
+ state.setSawElse();
+ state.setActive(! state.isActive());
+ return source_skipline(true);
+ }
+ // break;
+
+ case PP_IFDEF:
+ push_state();
+ if (!isActive()) {
+ return source_skipline(false);
+ }
+ else {
+ tok = source_token_nonwhite();
+ // System.out.println("ifdef " + tok);
+ if (tok.getType() != IDENTIFIER) {
+ error(tok,
+ "Expected identifier, not " +
+ tok.getText());
+ return source_skipline(false);
+ }
+ else {
+ String text = tok.getText();
+ boolean exists =
+ macros.containsKey(text);
+ states.peek().setActive(exists);
+ return source_skipline(true);
+ }
+ }
+ // break;
+
+ case PP_IFNDEF:
+ push_state();
+ if (!isActive()) {
+ return source_skipline(false);
+ }
+ else {
+ tok = source_token_nonwhite();
+ if (tok.getType() != IDENTIFIER) {
+ error(tok,
+ "Expected identifier, not " +
+ tok.getText());
+ return source_skipline(false);
+ }
+ else {
+ String text = tok.getText();
+ boolean exists =
+ macros.containsKey(text);
+ states.peek().setActive(!exists);
+ return source_skipline(true);
+ }
+ }
+ // break;
+
+ case PP_ENDIF:
+ pop_state();
+ return source_skipline(true);
+ // break;
+
+ case PP_LINE:
+ return source_skipline(false);
+ // break;
+
+ case PP_PRAGMA:
+ return source_skipline(false);
+ // break;
+
+ default:
+ /* Actual unknown directives are
+ * processed above. If we get here,
+ * we succeeded the map lookup but
+ * failed to handle it. Therefore,
+ * this is (unconditionally?) fatal. */
+ // if (isActive()) /* XXX Could be warning. */
+ throw new InternalException(
+ "Internal error: Unknown directive "
+ + tok);
+ // return source_skipline(false);
+ }
+
+
+ }
+ }
+ }
+
+ private Token token_nonwhite()
+ throws IOException,
+ LexerException {
+ Token tok;
+ do {
+ tok = _token();
+ } while (isWhite(tok));
+ return tok;
+ }
+
+ /**
+ * Returns the next preprocessor token.
+ *
+ * @see Token
+ * @throws LexerException if a preprocessing error occurs.
+ * @throws InternalException if an unexpected error condition arises.
+ */
+ public Token token()
+ throws IOException,
+ LexerException {
+ Token tok = _token();
+ if (DEBUG)
+ System.out.println("pp: Returning " + tok);
+ return tok;
+ }
+
+#set ($i = 1) /* First ppcmd is 1, not 0. */
+#set ($ppcmds = [ "define", "elif", "else", "endif", "error", "if", "ifdef", "ifndef", "include", "line", "pragma", "undef", "warning" ])
+#foreach ($ppcmd in $ppcmds)
+ private static final int PP_$ppcmd.toUpperCase() = $i;
+#set ($i = $i + 1)
+#end
+
+ private static final Map<String,Integer> ppcmds =
+ new HashMap<String,Integer>();
+
+ static {
+#foreach ($ppcmd in $ppcmds)
+ ppcmds.put("$ppcmd", Integer.valueOf(PP_$ppcmd.toUpperCase()));
+#end
+ }
+
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+
+ Source s = getSource();
+ while (s != null) {
+ buf.append(" -> ").append(String.valueOf(s)).append("\n");
+ s = s.getParent();
+ }
+
+ Map<String,Macro> macros = getMacros();
+ List<String> keys = new ArrayList<String>(
+ macros.keySet()
+ );
+ Collections.sort(keys);
+ Iterator<String> mt = keys.iterator();
+ while (mt.hasNext()) {
+ String key = mt.next();
+ Macro macro = macros.get(key);
+ buf.append("#").append("macro ").append(macro).append("\n");
+ }
+
+ return buf.toString();
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/PreprocessorListener.java b/src/java/org/anarres/cpp/PreprocessorListener.java
new file mode 100644
index 0000000..84a105d
--- /dev/null
+++ b/src/java/org/anarres/cpp/PreprocessorListener.java
@@ -0,0 +1,83 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.File;
+
+public class PreprocessorListener {
+
+ private int errors;
+ private int warnings;
+
+ public PreprocessorListener() {
+ clear();
+ }
+
+ public void clear() {
+ errors = 0;
+ warnings = 0;
+ }
+
+ public int getErrors() {
+ return errors;
+ }
+
+ public int getWarnings() {
+ return warnings;
+ }
+
+ protected void print(String msg) {
+ System.err.println(msg);
+ }
+
+ /**
+ * Handles a warning.
+ *
+ * The behaviour of this method is defined by the
+ * implementation. It may simply record the error message, or
+ * it may throw an exception.
+ */
+ public void handleWarning(Source source, int line, int column,
+ String msg)
+ throws LexerException {
+ warnings++;
+ print(source.getName() + ":" + line + ":" + column +
+ ": warning: " + msg);
+ }
+
+ /**
+ * Handles an error.
+ *
+ * The behaviour of this method is defined by the
+ * implementation. It may simply record the error message, or
+ * it may throw an exception.
+ */
+ public void handleError(Source source, int line, int column,
+ String msg)
+ throws LexerException {
+ errors++;
+ print(source.getName() + ":" + line + ":" + column +
+ ": error: " + msg);
+ }
+
+ public void handleSourceChange(Source source, String event) {
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/Source.java b/src/java/org/anarres/cpp/Source.java
new file mode 100644
index 0000000..2999418
--- /dev/null
+++ b/src/java/org/anarres/cpp/Source.java
@@ -0,0 +1,226 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.io.StringReader;
+
+import java.util.List;
+import java.util.Iterator;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * An input to the Preprocessor.
+ *
+ * Inputs may come from Files, Strings or other sources. The
+ * preprocessor maintains a stack of Sources. Operations such as
+ * file inclusion or token pasting will push a new source onto
+ * the Preprocessor stack. Sources pop from the stack when they
+ * are exhausted; this may be transparent or explicit.
+ *
+ * BUG: Error messages are not handled properly.
+ */
+public abstract class Source implements Iterable<Token> {
+ private Source parent;
+ private boolean autopop;
+ private PreprocessorListener listener;
+
+ /* LineNumberReader */
+
+/*
+ // We can't do this, since we would lose the LexerException
+ private class Itr implements Iterator {
+ private Token next = null;
+ private void advance() {
+ try {
+ if (next != null)
+ next = token();
+ }
+ catch (IOException e) {
+ throw new UnsupportedOperationException(
+ "Failed to advance token iterator: " +
+ e.getMessage()
+ );
+ }
+ }
+ public boolean hasNext() {
+ return next.getType() != EOF;
+ }
+ public Token next() {
+ advance();
+ Token t = next;
+ next = null;
+ return t;
+ }
+ public void remove() {
+ throw new UnsupportedOperationException(
+ "Cannot remove tokens from a Source."
+ );
+ }
+ }
+*/
+
+ public Source() {
+ this.parent = null;
+ this.autopop = false;
+ }
+
+ /* pp */ void setParent(Source parent, boolean autopop) {
+ this.parent = parent;
+ this.autopop = autopop;
+ }
+
+ /* pp */ final Source getParent() {
+ return parent;
+ }
+
+ /* pp */ void setListener(PreprocessorListener listener) {
+ this.listener = listener;
+ }
+
+ /**
+ * Returns the File currently being lexed.
+ *
+ * If this Source is not a {@link FileLexerSource}, then
+ * it will ask the parent Source, and so forth recursively.
+ * If no Source on the stack is a FileLexerSource, returns null.
+ */
+ /* pp */ File getFile() {
+ Source parent = getParent();
+ while (parent != null) {
+ File file = parent.getFile();
+ if (file != null)
+ return file;
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
+ /* pp */ String getName() {
+ Source parent = getParent();
+ while (parent != null) {
+ String name = parent.getName();
+ if (name != null)
+ return name;
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
+ public int getLine() {
+ Source parent = getParent();
+ if (parent == null)
+ return 0;
+ return parent.getLine();
+ }
+
+ /* pp */ boolean isExpanding(Macro m) {
+ Source parent = getParent();
+ if (parent != null)
+ return parent.isExpanding(m);
+ return false;
+ }
+
+ /**
+ * Returns true if this Source should be transparently popped
+ * from the input stack.
+ *
+ * Examples of such sources are macro expansions.
+ */
+ /* pp */ boolean isAutopop() {
+ return autopop;
+ }
+
+ /* pp */ boolean isNumbered() {
+ return false;
+ }
+
+ /**
+ * Returns the next Token parsed from this input stream.
+ *
+ * @see Token
+ */
+ public abstract Token token()
+ throws IOException,
+ LexerException;
+
+ public Iterator<Token> iterator() {
+ return new SourceIterator(this);
+ }
+
+ /**
+ * Skips tokens until the end of line.
+ *
+ * @param white true if only whitespace is permitted on the
+ * remainder of the line.
+ * @return the NL token.
+ */
+ public Token skipline(boolean white)
+ throws IOException,
+ LexerException {
+ for (;;) {
+ Token tok = token();
+ switch (tok.getType()) {
+ case EOF:
+ /* There ought to be a newline before EOF.
+ * At least, in any skipline context. */
+ /* XXX Are we sure about this? */
+ warning(tok.getLine(), tok.getColumn(),
+ "No newline before end of file");
+ return tok;
+ case NL:
+ /* This may contain one or more newlines. */
+ return tok;
+ case COMMENT:
+ case WHITESPACE:
+ break;
+ default:
+ /* XXX Check white, if required. */
+ if (white)
+ warning(tok.getLine(), tok.getColumn(),
+ "Unexpected nonwhite token");
+ break;
+ }
+ }
+ }
+
+ protected void error(int line, int column, String msg)
+ throws LexerException {
+ if (listener != null)
+ listener.handleError(this, line, column, msg);
+ else
+ throw new LexerException("No handler for error at " + line + ":" + column + ": " + msg);
+ }
+
+ protected void warning(int line, int column, String msg)
+ throws LexerException {
+ if (listener != null)
+ listener.handleWarning(this, line, column, msg);
+ else
+ throw new LexerException("No handler for warning at " + line + ":" + column + ": " + msg);
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/SourceIterator.java b/src/java/org/anarres/cpp/SourceIterator.java
new file mode 100644
index 0000000..ac2bc24
--- /dev/null
+++ b/src/java/org/anarres/cpp/SourceIterator.java
@@ -0,0 +1,94 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.IOException;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * An Iterator for {@link Source Sources},
+ * returning {@link Token Tokens}.
+ */
+public class SourceIterator implements Iterator<Token> {
+ private Source source;
+ private Token tok;
+
+ public SourceIterator(Source s) {
+ this.source = s;
+ this.tok = null;
+ }
+
+ /**
+ * Rethrows IOException inside IllegalStateException.
+ */
+ private void advance() {
+ try {
+ if (tok == null)
+ tok = source.token();
+ }
+ catch (LexerException e) {
+ throw new IllegalStateException(e);
+ }
+ catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Returns true if the enclosed Source has more tokens.
+ *
+ * The EOF token is never returned by the iterator.
+ * @throws IllegalStateException if the Source
+ * throws a LexerException or IOException
+ */
+ public boolean hasNext() {
+ advance();
+ return tok.getType() != EOF;
+ }
+
+ /**
+ * Returns the next token from the enclosed Source.
+ *
+ * The EOF token is never returned by the iterator.
+ * @throws IllegalStateException if the Source
+ * throws a LexerException or IOException
+ */
+ public Token next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ Token t = this.tok;
+ this.tok = null;
+ return t;
+ }
+
+ /**
+ * Not supported.
+ *
+ * @throws UnsupportedOperationException.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/src/java/org/anarres/cpp/State.java b/src/java/org/anarres/cpp/State.java
new file mode 100644
index 0000000..441e71e
--- /dev/null
+++ b/src/java/org/anarres/cpp/State.java
@@ -0,0 +1,69 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+/* pp */ class State {
+ boolean parent;
+ boolean active;
+ boolean sawElse;
+
+ /* pp */ State() {
+ this.parent = true;
+ this.active = true;
+ this.sawElse = false;
+ }
+
+ /* pp */ State(State parent) {
+ this.parent = parent.isParentActive() && parent.isActive();
+ this.active = true;
+ this.sawElse = false;
+ }
+
+ /* Required for #elif */
+ /* pp */ void setParentActive(boolean b) {
+ this.parent = b;
+ }
+
+ /* pp */ boolean isParentActive() {
+ return parent;
+ }
+
+ /* pp */ void setActive(boolean b) {
+ this.active = b;
+ }
+
+ /* pp */ boolean isActive() {
+ return active;
+ }
+
+ /* pp */ void setSawElse() {
+ sawElse = true;
+ }
+
+ /* pp */ boolean sawElse() {
+ return sawElse;
+ }
+
+ public String toString() {
+ return "parent=" + parent +
+ ", active=" + active +
+ ", sawelse=" + sawElse;
+ }
+}
diff --git a/src/java/org/anarres/cpp/StringLexerSource.java b/src/java/org/anarres/cpp/StringLexerSource.java
new file mode 100644
index 0000000..7e7df75
--- /dev/null
+++ b/src/java/org/anarres/cpp/StringLexerSource.java
@@ -0,0 +1,64 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import java.util.List;
+import java.util.Iterator;
+
+import static org.anarres.cpp.Token.*;
+
+/**
+ * A Source for lexing a String.
+ *
+ * This class is used by token pasting, but can be used by user
+ * code.
+ */
+public class StringLexerSource extends LexerSource {
+
+ /**
+ * Creates a new Source for lexing the given String.
+ *
+ * @param ppvalid true if preprocessor directives are to be
+ * honoured within the string.
+ */
+ public StringLexerSource(String string, boolean ppvalid)
+ throws IOException {
+ super(new StringReader(string), ppvalid);
+ }
+
+ /**
+ * Creates a new Source for lexing the given String.
+ *
+ * By default, preprocessor directives are not honoured within
+ * the string.
+ */
+ public StringLexerSource(String string)
+ throws IOException {
+ this(string, false);
+ }
+
+ public String toString() {
+ return "string literal";
+ }
+}
diff --git a/src/java/org/anarres/cpp/Token.java b/src/java/org/anarres/cpp/Token.java
new file mode 100644
index 0000000..e5c1319
--- /dev/null
+++ b/src/java/org/anarres/cpp/Token.java
@@ -0,0 +1,215 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+/**
+ * A Preprocessor token.
+ *
+ * @see Preprocessor
+ */
+public final class Token {
+
+ // public static final int EOF = -1;
+
+ private int type;
+ private int line;
+ private int column;
+ private Object value;
+ private String text;
+
+ public Token(int type, int line, int column,
+ String text, Object value) {
+ this.type = type;
+ this.line = line;
+ this.column = column;
+ this.text = text;
+ this.value = value;
+ }
+
+ public Token(int type, int line, int column, String text) {
+ this(type, line, column, text, null);
+ }
+
+ /* pp */ Token(int type, String text, Object value) {
+ this(type, -1, -1, text, value);
+ }
+
+ /* pp */ Token(int type, String text) {
+ this(type, text, null);
+ }
+
+ /* pp */ Token(int type) {
+ this(type, texts[type]);
+ }
+
+ /**
+ * Returns the semantic type of this token.
+ */
+ public int getType() {
+ return type;
+ }
+
+ /* pp */ void setLocation(int line, int column) {
+ this.line = line;
+ this.column = column;
+ }
+
+ /**
+ * Returns the line at which this token started.
+ *
+ * Lines are numbered from zero.
+ */
+ public int getLine() {
+ return line;
+ }
+
+ /**
+ * Returns the column at which this token started.
+ *
+ * Columns are numbered from zero.
+ */
+ public int getColumn() {
+ return column;
+ }
+
+ /**
+ * Returns the original or generated text of this token.
+ *
+ * This is distinct from the semantic value of the token.
+ *
+ * @see #getValue()
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Returns the semantic value of this token.
+ *
+ * For strings, this is the parsed String.
+ * For integers, this is an Integer object.
+ * For other token types, as appropriate.
+ *
+ * @see #getText()
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Returns a description of this token, for debugging purposes.
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+
+ buf.append('[').append(getTokenName(type));
+ if (line != -1) {
+ buf.append('@').append(line);
+ if (column != -1)
+ buf.append(',').append(column);
+ }
+ buf.append("]:");
+ if (text != null)
+ buf.append('"').append(text).append('"');
+ else if (type > 3 && type < 256)
+ buf.append( (char)type );
+ else
+ buf.append('<').append(type).append('>');
+ if (value != null)
+ buf.append('=').append(value);
+ return buf.toString();
+ }
+
+ /**
+ * Returns the descriptive name of the given token type.
+ *
+ * This is mostly used for stringification and debugging.
+ */
+ public static final String getTokenName(int type) {
+ if (type < 0)
+ return "Invalid" + type;
+ if (type >= names.length)
+ return "Invalid" + type;
+ if (names[type] == null)
+ return "Unknown" + type;
+ return names[type];
+ }
+
+#set ($i = 257)
+#set ($tokens = [ "AND_EQ", "ARROW", "CHARACTER", "COMMENT", "DEC", "DIV_EQ", "ELLIPSIS", "EOF", "EQ", "GE", "HASH", "HEADER", "IDENTIFIER", "INC", "INTEGER", "LAND", "LAND_EQ", "LE", "LITERAL", "LOR", "LOR_EQ", "LSH", "LSH_EQ", "MOD_EQ", "MULT_EQ", "NE", "NL", "OR_EQ", "PASTE", "PLUS_EQ", "RANGE", "RSH", "RSH_EQ", "STRING", "SUB_EQ", "WHITESPACE", "XOR_EQ", "M_ARG", "M_PASTE", "M_STRING", "P_LINE", "ERROR" ])
+#foreach ($token in $tokens)
+ /** The token type $token. */
+ public static final int $token = $i;
+#set ($i = $i + 1)
+#end
+ /**
+ * The number of possible semantic token types.
+ *
+ * Please note that not all token types below 255 are used.
+ */
+ public static final int _TOKENS = $i;
+
+ /** The position-less space token. */
+ /* pp */ static final Token space = new Token(WHITESPACE, -1, -1, " ");
+
+ private static final String[] names = new String[_TOKENS];
+ private static final String[] texts = new String[_TOKENS];
+ static {
+ for (int i = 0; i < 255; i++) {
+ texts[i] = String.valueOf(new char[] { (char)i });
+ names[i] = texts[i];
+ }
+
+ texts[AND_EQ] = "&=";
+ texts[ARROW] = "->";
+ texts[DEC] = "--";
+ texts[DIV_EQ] = "/=";
+ texts[ELLIPSIS] = "...";
+ texts[EQ] = "==";
+ texts[GE] = ">=";
+ texts[HASH] = "#";
+ texts[INC] = "++";
+ texts[LAND] = "&&";
+ texts[LAND_EQ] = "&&=";
+ texts[LE] = "<=";
+ texts[LOR] = "||";
+ texts[LOR_EQ] = "||=";
+ texts[LSH] = "<<";
+ texts[LSH_EQ] = "<<=";
+ texts[MOD_EQ] = "%=";
+ texts[MULT_EQ] = "*=";
+ texts[NE] = "!=";
+ texts[NL] = "\n";
+ texts[OR_EQ] = "|=";
+ /* We have to split the two hashes or Velocity eats them. */
+ texts[PASTE] = "#" + "#";
+ texts[PLUS_EQ] = "+=";
+ texts[RANGE] = "..";
+ texts[RSH] = ">>";
+ texts[RSH_EQ] = ">>=";
+ texts[SUB_EQ] = "-=";
+ texts[XOR_EQ] = "^=";
+
+#foreach ($token in $tokens)
+ names[$token] = "$token";
+#end
+ }
+
+}
diff --git a/src/java/org/anarres/cpp/TokenSnifferSource.java b/src/java/org/anarres/cpp/TokenSnifferSource.java
new file mode 100644
index 0000000..55b53d7
--- /dev/null
+++ b/src/java/org/anarres/cpp/TokenSnifferSource.java
@@ -0,0 +1,56 @@
+/*
+ * Anarres C Preprocessor
+ * Copyright (C) 2007 Shevek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.anarres.cpp;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.io.StringReader;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+import static org.anarres.cpp.Token.*;
+
+@Deprecated
+/* pp */ class TokenSnifferSource extends Source {
+ private List<Token> target;
+
+ /* pp */ TokenSnifferSource(List<Token> target) {
+ this.target = target;
+ }
+
+ public Token token()
+ throws IOException,
+ LexerException {
+ Token tok = getParent().token();
+ if (tok.getType() != EOF)
+ target.add(tok);
+ return tok;
+ }
+
+ public String toString() {
+ return getParent().toString();
+ }
+}
diff --git a/src/resources/log4j.properties b/src/resources/log4j.properties
new file mode 100644
index 0000000..901854c
--- /dev/null
+++ b/src/resources/log4j.properties
@@ -0,0 +1,22 @@
+# Set root logger level to DEBUG and its only appender to stdout.
+log4j.rootLogger=INFO, stdout
+
+# stdout is set to be a ConsoleAppender.
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+
+# stdout uses PatternLayout.
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+# log4j.appender.stdout.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+# log4j.appender.stdout.layout.ConversionPattern=%-4r [%t] %p %c{1} - %m%n
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t %x] %p %c - %m%n
+# Make log4j honour other programs changing System.out
+log4j.appender.stdout.follow=true
+
+# Turn on Axis exception logging
+log4j.logger.org.apache.axis.EXCEPTIONS=DEBUG
+
+# log4j.logger.org.apache.axis=WARN
+# log4j.logger.org.mortbay=WARN
+# log4j.logger.com.mchange=WARN
+
+# log4j.logger.org.anarres.iengine=DEBUG
diff --git a/src/scripts/cpp.sh b/src/scripts/cpp.sh
new file mode 100644
index 0000000..ed167c5
--- /dev/null
+++ b/src/scripts/cpp.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+CPP_JAR=anarres-cpp.jar
+
+if [ -n "$CPP_ROOT" ] ; then
+ CPP_ROOT="$CPP_ROOT"
+elif [ -f lib/$CPP_JAR ] ; then
+ CPP_ROOT="."
+elif [ -f ../lib/$CPP_JAR ] ; then
+ CPP_ROOT=".."
+elif [ -f $(dirname $0)/lib/$CPP_JAR ] ; then
+ CPP_ROOT=$(dirname $0)
+else
+ echo "Could not find $CPP_JAR. Please set CPP_ROOT."
+ exit 1
+fi
+
+if [ -z "$CPP_LIB" ] ; then
+ CPP_LIB=$CPP_ROOT/lib
+fi
+
+if [ -z "$CPP_CLASSPATH" ] ; then
+ CPP_CLASSPATH="$(ls $CPP_LIB/*.jar | tr '\n' ':')"
+fi
+
+if [ -z "$CPP_MAINCLASS" ] ; then
+ CPP_MAINCLASS=org.anarres.cpp.Main
+fi
+
+CPP_JFLAGS="-Xmx128M"
+
+exec java $CPP_JFLAGS -cp "$CPP_CLASSPATH" $CPP_MAINCLASS "$@"
diff --git a/src/scripts/release.sh b/src/scripts/release.sh
new file mode 100644
index 0000000..6393a95
--- /dev/null
+++ b/src/scripts/release.sh
@@ -0,0 +1,4 @@
+scp build/dist/anarres-cpp-*.tar.gz [email protected]:public_html/projects/jcpp
+scp -r build/javadoc/ [email protected]:public_html/projects/jcpp
+cp build/tar/lib/anarres-cpp.jar /home/shevek/java/iengine/lib/jcpp
+cp build/tar/lib/anarres-cpp.jar /home/shevek/java/karma/trunk/lib/dp
diff --git a/src/tests/AutoTestSuite.java b/src/tests/AutoTestSuite.java
new file mode 100644
index 0000000..894a365
--- /dev/null
+++ b/src/tests/AutoTestSuite.java
@@ -0,0 +1,121 @@
+import java.lang.reflect.Modifier;
+
+import java.io.File;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import junit.framework.TestSuite;
+import junit.framework.TestCase;
+import junit.framework.Test;
+
+public class AutoTestSuite extends TestSuite {
+ private String testPackage;
+ private Set<String> testCases;
+ private boolean testAll;
+ private File root;
+
+ public AutoTestSuite() {
+ this.testPackage = System.getProperty("test.package");
+ String tcase = System.getProperty("test.case");
+ if (tcase != null && tcase.length() > 0) {
+ this.testCases = new HashSet(Arrays.asList(
+ tcase.split("[,:]")
+ ));
+ }
+ else {
+ this.testCases = null;
+ }
+ this.testAll = System.getProperty("test.all") != null;
+ this.root = new File(System.getProperty("test.root"));
+
+ Set<Class> tests = new HashSet();
+
+ findClasses("", root, tests);
+
+ Iterator<Class> i = tests.iterator();
+
+ while(i.hasNext()) {
+ addTestSuite(i.next());
+ }
+ }
+
+ public void addTestSuite(Class clazz) {
+ if (testPackage != null) {
+ String name = clazz.getPackage().getName();
+ if (!name.startsWith(testPackage)) {
+ /*
+ System.out.println("Skipping test in package '" +
+ name + "' - does not start with '" +
+ testPackage + "'");
+ */
+ return;
+ }
+ }
+ if (testCases != null) {
+ String name = clazz.getName();
+ name = name.substring(name.lastIndexOf('.') + 1);
+ if (!testCases.contains(name)) {
+ /*
+ System.out.println("Skipping test in class '" +
+ name + "' - does not start with '" +
+ testCases + "'");
+ */
+ return;
+ }
+ }
+ /*
+ if (
+ testCases == null &&
+ testPackage == null &&
+ !testAll &&
+ Optional.class.isAssignableFrom(clazz)
+ )
+ {
+ return;
+ }
+ */
+ System.out.println("Adding test class '" + clazz + "'");
+ super.addTestSuite(clazz);
+ }
+
+ public static Test suite() {
+ return new AutoTestSuite();
+ }
+
+ private final void findClasses(String pkg, File root, Set<Class> result) {
+ File[] children = root.listFiles();
+ for(int i = 0; i<children.length; i++) {
+ File child = children[i];
+ if(child.isDirectory()) {
+ findClasses(
+ pkg + child.getName() + ".",
+ child,
+ result
+ );
+ } else if(child.isFile()) {
+ String name = child.getName();
+ // System.out.println("Checking: " + pkg + name);
+ if(name.endsWith(".class") && name.indexOf('$') == -1) {
+ try {
+ Class test = Class.forName(pkg +
+ name.substring(0,name.length() - 6));
+ int modifiers = test.getModifiers();
+ if(
+ (modifiers & Modifier.ABSTRACT) > 0 ||
+ (modifiers & Modifier.INTERFACE) > 0 ||
+ !TestCase.class.isAssignableFrom(test) ||
+ TestSuite.class.isAssignableFrom(test)
+ )
+ continue;
+ result.add(test);
+ } catch (ClassNotFoundException cnfe) {
+ cnfe.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/tests/org/anarres/cpp/BaseTestCase.java b/src/tests/org/anarres/cpp/BaseTestCase.java
new file mode 100644
index 0000000..ad6ae6a
--- /dev/null
+++ b/src/tests/org/anarres/cpp/BaseTestCase.java
@@ -0,0 +1,6 @@
+package org.anarres.cpp;
+
+import junit.framework.TestCase;
+
+public abstract class BaseTestCase extends TestCase {
+}
diff --git a/src/tests/org/anarres/cpp/CppReaderTestCase.java b/src/tests/org/anarres/cpp/CppReaderTestCase.java
new file mode 100644
index 0000000..5aeee06
--- /dev/null
+++ b/src/tests/org/anarres/cpp/CppReaderTestCase.java
@@ -0,0 +1,34 @@
+package org.anarres.cpp;
+
+import java.util.Collections;
+
+import java.io.StringReader;
+import java.io.BufferedReader;
+
+import junit.framework.Test;
+
+public class CppReaderTestCase extends BaseTestCase implements Test {
+
+ private void testCppReader(String in, String out)
+ throws Exception {
+ System.out.println("Testing " + in + " => " + out);
+ StringReader r = new StringReader(in);
+ CppReader p = new CppReader(r);
+ p.getPreprocessor().setIncludePath(
+ Collections.singletonList("src/input")
+ );
+ p.getPreprocessor().setFlags(Preprocessor.FL_LINEMARKER);
+ BufferedReader b = new BufferedReader(p);
+
+ String line;
+ while ((line = b.readLine()) != null) {
+ System.out.println(" >> " + line);
+ }
+ }
+
+ public void testJoinReader()
+ throws Exception {
+ testCppReader("#include <test0.h>\n", "ab");
+ }
+
+}
diff --git a/src/tests/org/anarres/cpp/ErrorTestCase.java b/src/tests/org/anarres/cpp/ErrorTestCase.java
new file mode 100644
index 0000000..d5d44a3
--- /dev/null
+++ b/src/tests/org/anarres/cpp/ErrorTestCase.java
@@ -0,0 +1,50 @@
+package org.anarres.cpp;
+
+import java.io.*;
+
+import junit.framework.Test;
+
+import static org.anarres.cpp.Token.*;
+
+public class ErrorTestCase extends BaseTestCase {
+
+ private void testError(Source source)
+ throws LexerException,
+ IOException {
+ for (;;) {
+ Token tok = source.token();
+ if (tok.getType() == EOF)
+ break;
+ }
+
+ }
+
+ private void testError(String input) throws Exception {
+ StringLexerSource sl;
+ PreprocessorListener pl;
+
+ /* Without a PreprocessorListener, throws an exception. */
+ sl = new StringLexerSource(input, true);
+ try {
+ testError(sl);
+ fail("Lexing succeeded");
+ }
+ catch (LexerException e) {
+ /* ignored */
+ }
+
+ /* With a PreprocessorListener, records the error. */
+ sl = new StringLexerSource(input, true);
+ pl = new PreprocessorListener();
+ sl.setListener(pl);
+ testError(sl);
+ assertTrue(pl.getErrors() > 0);
+ }
+
+ public void testErrors() throws Exception {
+ testError("\"");
+ testError("'");
+ testError("''");
+ }
+
+}
diff --git a/src/tests/org/anarres/cpp/JoinReaderTestCase.java b/src/tests/org/anarres/cpp/JoinReaderTestCase.java
new file mode 100644
index 0000000..2b99c2f
--- /dev/null
+++ b/src/tests/org/anarres/cpp/JoinReaderTestCase.java
@@ -0,0 +1,40 @@
+package org.anarres.cpp;
+
+import java.io.StringReader;
+
+import junit.framework.Test;
+
+public class JoinReaderTestCase extends BaseTestCase implements Test {
+
+ private void testJoinReader(String in, String out, boolean tg)
+ throws Exception {
+ System.out.println("Testing " + in + " => " + out);
+ StringReader r = new StringReader(in);
+ JoinReader j = new JoinReader(r, tg);
+
+ for (int i = 0; i < out.length(); i++) {
+ int c = j.read();
+ // System.out.println("At offset " + i + ": " + (char)c);
+ assertEquals((char)out.charAt(i), c);
+ }
+ assertEquals(-1, j.read());
+ assertEquals(-1, j.read());
+ }
+
+ private void testJoinReader(String in, String out)
+ throws Exception {
+ testJoinReader(in, out, true);
+ testJoinReader(in, out, false);
+ }
+
+ public void testJoinReader()
+ throws Exception {
+ testJoinReader("ab", "ab");
+ testJoinReader("a\\b", "a\\b");
+ testJoinReader("a\nb", "a\nb");
+ testJoinReader("a\\\nb", "ab\n");
+ testJoinReader("foo??(bar", "foo[bar", true);
+ testJoinReader("foo??/\nbar", "foobar\n", true);
+ }
+
+}
diff --git a/src/tests/org/anarres/cpp/LexerSourceTestCase.java b/src/tests/org/anarres/cpp/LexerSourceTestCase.java
new file mode 100644
index 0000000..e8fb410
--- /dev/null
+++ b/src/tests/org/anarres/cpp/LexerSourceTestCase.java
@@ -0,0 +1,43 @@
+package org.anarres.cpp;
+
+import java.io.StringReader;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import static org.anarres.cpp.Token.*;
+
+public class LexerSourceTestCase extends BaseTestCase implements Test {
+
+ private void testLexerSource(String in, int[] out)
+ throws Exception {
+ System.out.println("Testing '" + in + "' => " +
+ Arrays.toString(out));
+ StringLexerSource s = new StringLexerSource(in);
+
+ for (int i = 0; i < out.length; i++) {
+ Token tok = s.token();
+ System.out.println("Token is " + tok);
+ assertEquals(out[i], tok.getType());
+ }
+ assertEquals(EOF, s.token().getType());
+ }
+
+ public void testJoinReader()
+ throws Exception {
+
+ testLexerSource("int a = 5;", new int[] {
+ IDENTIFIER, WHITESPACE, IDENTIFIER, WHITESPACE,
+ '=', WHITESPACE, INTEGER, ';', EOF
+ });
+
+ testLexerSource("# # foo", new int[] {
+ HASH, WHITESPACE, '#', WHITESPACE, IDENTIFIER
+ });
+
+ }
+
+}
diff --git a/src/tests/org/anarres/cpp/PreprocessorTestCase.java b/src/tests/org/anarres/cpp/PreprocessorTestCase.java
new file mode 100644
index 0000000..ea6aab7
--- /dev/null
+++ b/src/tests/org/anarres/cpp/PreprocessorTestCase.java
@@ -0,0 +1,154 @@
+package org.anarres.cpp;
+
+import java.io.*;
+
+import junit.framework.Test;
+
+import static org.anarres.cpp.Token.*;
+
+public class PreprocessorTestCase extends BaseTestCase {
+ private OutputStreamWriter writer;
+ private Preprocessor p;
+
+ public void setUp() throws Exception {
+ final PipedOutputStream po = new PipedOutputStream();
+ writer = new OutputStreamWriter(po);
+
+ p = new Preprocessor(
+ new LexerSource(
+ new InputStreamReader(
+ new PipedInputStream(po)
+ ),
+ true
+ ) {
+ public File getFile() {
+ return new File("test-input");
+ }
+ }
+ ) {
+ @Override
+ protected void include(File parent, int line,
+ String name, boolean quoted) {
+ /* XXX Perform a useful assertion. */
+ }
+ };
+ }
+
+ private static class I {
+ private String t;
+ public I(String t) {
+ this.t = t;
+ }
+ public String getText() {
+ return t;
+ }
+ }
+
+ private static I I(String t) {
+ return new I(t);
+ }
+
+ public void testPreprocessor() throws Exception {
+ /* Magic macros */
+ testInput("line = __LINE__\n",
+ I("line"), WHITESPACE, '=', WHITESPACE, INTEGER
+ /*, NL - all nls deferred so as not to block the reader */
+ );
+ testInput("file = __FILE__\n", NL, /* from before, etc */
+ I("file"), WHITESPACE, '=', WHITESPACE, STRING
+ );
+
+ /* Simple definitions */
+ testInput("#define A a /* a defined */\n", NL);
+ testInput("#define B b /* b defined */\n", NL);
+ testInput("#define C c /* c defined */\n", NL);
+
+ /* Expansion of arguments */
+ testInput("#define EXPAND(x) x\n", NL);
+ testInput("EXPAND(a)\n", NL, I("a"));
+ testInput("EXPAND(A)\n", NL, I("a"));
+
+ /* Stringification */
+ testInput("#define _STRINGIFY(x) #x\n", NL);
+ testInput("_STRINGIFY(A)\n", NL, "A");
+ testInput("#define STRINGIFY(x) _STRINGIFY(x)\n", NL);
+ testInput("STRINGIFY(b)\n", NL, "b");
+ testInput("STRINGIFY(A)\n", NL, "a");
+
+ /* Concatenation */
+ testInput("#define _CONCAT(x, y) x ## y\n", NL);
+ testInput("_CONCAT(A, B)\n", NL, I("AB"));
+ testInput("#define A_CONCAT done_a_concat\n", NL);
+ testInput("_CONCAT(A, _CONCAT(B, C))\n", NL,
+ I("done_a_concat"), '(', I("b"), ',', WHITESPACE, I("c"), ')'
+ );
+ testInput("#define CONCAT(x, y) _CONCAT(x, y)\n", NL);
+ testInput("CONCAT(A, CONCAT(B, C))\n", NL, I("abc"));
+ testInput("#define _CONCAT3(x, y, z) x ## y ## z\n", NL);
+ testInput("_CONCAT3(a, b, c)\n", NL, I("abc"));
+ testInput("_CONCAT3(A, B, C)\n", NL, I("ABC"));
+
+/* Redefinitions, undefinitions. */
+testInput("#define two three\n", NL);
+testInput("one /* one */\n", NL, I("one"), WHITESPACE, COMMENT);
+testInput("#define one two\n", NL);
+testInput("one /* three */\n", NL, I("three"), WHITESPACE, COMMENT);
+testInput("#undef two\n", NL);
+testInput("#define two five\n", NL);
+testInput("one /* five */\n", NL, I("five"), WHITESPACE, COMMENT);
+testInput("#undef two\n", NL);
+testInput("one /* two */\n", NL, I("two"), WHITESPACE, COMMENT);
+testInput("#undef one\n", NL);
+testInput("#define one four\n", NL);
+testInput("one /* four */\n", NL, I("four"), WHITESPACE, COMMENT);
+testInput("#undef one\n", NL);
+testInput("#define one one\n", NL);
+testInput("one /* one */\n", NL, I("one"), WHITESPACE, COMMENT);
+
+ /* Variadic macros. */
+ testInput("#define var(x...) a x b\n", NL);
+ testInput("var(e, f, g)", NL,
+ I("a"), WHITESPACE,
+ I("e"), ',', WHITESPACE,
+ I("f"), ',', WHITESPACE,
+ I("g"), WHITESPACE,
+ I("b")
+ );
+
+ writer.close();
+
+ Token t;
+ do {
+ t = p.token();
+ System.out.println("Remaining token " + t);
+ } while(t.getType() != EOF);
+ }
+
+ private void testInput(String in, Object... out)
+ throws Exception {
+ System.out.print("Input: " + in);
+ writer.write(in);
+ writer.flush();
+ for (int i = 0; i < out.length; i++) {
+ Token t = p.token();
+ System.out.println(t);
+ Object v = out[i];
+ if (v instanceof String) {
+ if (t.getType() != STRING)
+ fail("Expected STRING, but got " + t);
+ assertEquals((String)v, (String)t.getValue());
+ }
+ else if (v instanceof I) {
+ if (t.getType() != IDENTIFIER)
+ fail("Expected IDENTIFIER, but got " + t);
+ assertEquals( ((I)v).getText(), (String)t.getText());
+ }
+ else if (v instanceof Character)
+ assertEquals( (int)((Character)v).charValue(), t.getType());
+ else if (v instanceof Integer)
+ assertEquals( ((Integer)v).intValue(), t.getType());
+ else
+ fail("Bad object " + v.getClass());
+ }
+ }
+}