diff options
author | Shevek <[email protected]> | 2008-03-21 23:05:04 +0000 |
---|---|---|
committer | Shevek <[email protected]> | 2008-03-21 23:05:04 +0000 |
commit | 5ff55648127c8a8e1b9829775045af986e37647c (patch) | |
tree | b28209b1efe12824fbdcabd4ee9073e93ca30636 | |
parent | fca34200881fcaf7b84b4210f7a5f40c8925c4d1 (diff) |
move stuff into trunk
93 files changed, 8553 insertions, 0 deletions
@@ -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 » + </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 » + </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 » + </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 = ''"><none></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 = ''"><none></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 > 0">Error</xsl:when> + <xsl:when test="$failureCount > 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) > 0">Error</xsl:when> + <xsl:when test="sum($insamepackage/@failures) > 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 = ''"><none></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) > 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[.> 0]">Error</xsl:when> + <xsl:when test="@failures[.> 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),"'","\'")"/> + <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),"
",$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 > 0">Failure</xsl:when> + <xsl:when test="$errorCount > 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 » + </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 > 0">Failure</xsl:when> + <xsl:when test="$errorCount > 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[.> 0]">Failure</xsl:when> + <xsl:when test="@errors[.> 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),"'","\'")"/> + <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),"
",$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 © 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 Binary files differnew file mode 100644 index 0000000..6e04e84 --- /dev/null +++ b/lib/ant/ant-contrib.jar diff --git a/lib/checkstyle/checkstyle-all-4.1.jar b/lib/checkstyle/checkstyle-all-4.1.jar Binary files differnew file mode 100644 index 0000000..ea018b6 --- /dev/null +++ b/lib/checkstyle/checkstyle-all-4.1.jar diff --git a/lib/checkstyle/commons-beanutils-core.jar b/lib/checkstyle/commons-beanutils-core.jar Binary files differnew file mode 100644 index 0000000..ce79cbe --- /dev/null +++ b/lib/checkstyle/commons-beanutils-core.jar diff --git a/lib/checkstyle/commons-cli.jar b/lib/checkstyle/commons-cli.jar Binary files differnew file mode 100644 index 0000000..22a004e --- /dev/null +++ b/lib/checkstyle/commons-cli.jar diff --git a/lib/checkstyle/commons-collections.jar b/lib/checkstyle/commons-collections.jar Binary files differnew file mode 100644 index 0000000..f66c6d2 --- /dev/null +++ b/lib/checkstyle/commons-collections.jar diff --git a/lib/checkstyle/commons-logging.jar b/lib/checkstyle/commons-logging.jar Binary files differnew file mode 100644 index 0000000..b99c937 --- /dev/null +++ b/lib/checkstyle/commons-logging.jar diff --git a/lib/cobertura/asm-2.2.1.jar b/lib/cobertura/asm-2.2.1.jar Binary files differnew file mode 100644 index 0000000..297690f --- /dev/null +++ b/lib/cobertura/asm-2.2.1.jar diff --git a/lib/cobertura/cobertura.jar b/lib/cobertura/cobertura.jar Binary files differnew file mode 100644 index 0000000..03baf65 --- /dev/null +++ b/lib/cobertura/cobertura.jar diff --git a/lib/cobertura/jakarta-oro-2.0.8.jar b/lib/cobertura/jakarta-oro-2.0.8.jar Binary files differnew file mode 100644 index 0000000..23488d2 --- /dev/null +++ b/lib/cobertura/jakarta-oro-2.0.8.jar diff --git a/lib/findbugs/lib/annotations.jar b/lib/findbugs/lib/annotations.jar Binary files differnew file mode 100644 index 0000000..238c87e --- /dev/null +++ b/lib/findbugs/lib/annotations.jar diff --git a/lib/findbugs/lib/asm-3.0_RC1.jar b/lib/findbugs/lib/asm-3.0_RC1.jar Binary files differnew file mode 100644 index 0000000..68a44ed --- /dev/null +++ b/lib/findbugs/lib/asm-3.0_RC1.jar diff --git a/lib/findbugs/lib/asm-analysis-3.0_RC1.jar b/lib/findbugs/lib/asm-analysis-3.0_RC1.jar Binary files differnew file mode 100644 index 0000000..0f56a17 --- /dev/null +++ b/lib/findbugs/lib/asm-analysis-3.0_RC1.jar diff --git a/lib/findbugs/lib/asm-commons-3.0_RC1.jar b/lib/findbugs/lib/asm-commons-3.0_RC1.jar Binary files differnew file mode 100644 index 0000000..4e1bac2 --- /dev/null +++ b/lib/findbugs/lib/asm-commons-3.0_RC1.jar diff --git a/lib/findbugs/lib/asm-tree-3.0_RC1.jar b/lib/findbugs/lib/asm-tree-3.0_RC1.jar Binary files differnew file mode 100644 index 0000000..97f17d0 --- /dev/null +++ b/lib/findbugs/lib/asm-tree-3.0_RC1.jar diff --git a/lib/findbugs/lib/asm-util-3.0_RC1.jar b/lib/findbugs/lib/asm-util-3.0_RC1.jar Binary files differnew file mode 100644 index 0000000..86e20a6 --- /dev/null +++ b/lib/findbugs/lib/asm-util-3.0_RC1.jar diff --git a/lib/findbugs/lib/asm-xml-3.0_RC1.jar b/lib/findbugs/lib/asm-xml-3.0_RC1.jar Binary files differnew file mode 100644 index 0000000..32a3b3c --- /dev/null +++ b/lib/findbugs/lib/asm-xml-3.0_RC1.jar diff --git a/lib/findbugs/lib/bcel.jar b/lib/findbugs/lib/bcel.jar Binary files differnew file mode 100644 index 0000000..b422c1e --- /dev/null +++ b/lib/findbugs/lib/bcel.jar diff --git a/lib/findbugs/lib/dom4j-full.jar b/lib/findbugs/lib/dom4j-full.jar Binary files differnew file mode 100644 index 0000000..1efbf7e --- /dev/null +++ b/lib/findbugs/lib/dom4j-full.jar diff --git a/lib/findbugs/lib/findbugs-ant.jar b/lib/findbugs/lib/findbugs-ant.jar Binary files differnew file mode 100644 index 0000000..66129af --- /dev/null +++ b/lib/findbugs/lib/findbugs-ant.jar diff --git a/lib/findbugs/lib/findbugs.jar b/lib/findbugs/lib/findbugs.jar Binary files differnew file mode 100644 index 0000000..04a7856 --- /dev/null +++ b/lib/findbugs/lib/findbugs.jar diff --git a/lib/findbugs/lib/findbugsGUI.jar b/lib/findbugs/lib/findbugsGUI.jar Binary files differnew file mode 100644 index 0000000..cbe77b0 --- /dev/null +++ b/lib/findbugs/lib/findbugsGUI.jar diff --git a/lib/findbugs/plugin/coreplugin.jar b/lib/findbugs/plugin/coreplugin.jar Binary files differnew file mode 100644 index 0000000..813d2b8 --- /dev/null +++ b/lib/findbugs/plugin/coreplugin.jar 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">&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>
+   
+ </span> P1
+ <span>
+ <xsl:attribute name="class">bug-priority-2</xsl:attribute>
+   
+ </span> P2
+ <span>
+ <xsl:attribute name="class">bug-priority-3</xsl:attribute>
+   
+ </span> P3
+ <span>
+ <xsl:attribute name="class">bug-priority-4</xsl:attribute>
+   
+ </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>
+   
+ </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" />  
+ (<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" />  
+ (<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) > 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 > 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 > 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 > 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>       </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 Binary files differnew file mode 100644 index 0000000..9c8de92 --- /dev/null +++ b/lib/jrat/bcel-326809.jar diff --git a/lib/jrat/jmxri.jar b/lib/jrat/jmxri.jar Binary files differnew file mode 100644 index 0000000..5e519fd --- /dev/null +++ b/lib/jrat/jmxri.jar diff --git a/lib/jrat/shiftone-arbor.jar b/lib/jrat/shiftone-arbor.jar Binary files differnew file mode 100644 index 0000000..f16528e --- /dev/null +++ b/lib/jrat/shiftone-arbor.jar diff --git a/lib/jrat/shiftone-jrat.jar b/lib/jrat/shiftone-jrat.jar Binary files differnew file mode 100644 index 0000000..3c1c5bf --- /dev/null +++ b/lib/jrat/shiftone-jrat.jar diff --git a/lib/junit/junit.jar b/lib/junit/junit.jar Binary files differnew file mode 100644 index 0000000..717cd08 --- /dev/null +++ b/lib/junit/junit.jar diff --git a/lib/log4j/log4j-1.2.13.jar b/lib/log4j/log4j-1.2.13.jar Binary files differnew file mode 100644 index 0000000..dde9972 --- /dev/null +++ b/lib/log4j/log4j-1.2.13.jar diff --git a/lib/svn/commons-lang-2.0.jar b/lib/svn/commons-lang-2.0.jar Binary files differnew file mode 100644 index 0000000..c8a2870 --- /dev/null +++ b/lib/svn/commons-lang-2.0.jar diff --git a/lib/svn/jakarta-regexp-1.3.jar b/lib/svn/jakarta-regexp-1.3.jar Binary files differnew file mode 100644 index 0000000..d653a38 --- /dev/null +++ b/lib/svn/jakarta-regexp-1.3.jar diff --git a/lib/svn/svnClientAdapter.jar b/lib/svn/svnClientAdapter.jar Binary files differnew file mode 100644 index 0000000..502a53f --- /dev/null +++ b/lib/svn/svnClientAdapter.jar diff --git a/lib/svn/svnant.jar b/lib/svn/svnant.jar Binary files differnew file mode 100644 index 0000000..fc5e2b9 --- /dev/null +++ b/lib/svn/svnant.jar diff --git a/lib/svn/svnjavahl.jar b/lib/svn/svnjavahl.jar Binary files differnew file mode 100644 index 0000000..4208165 --- /dev/null +++ b/lib/svn/svnjavahl.jar diff --git a/lib/vpp/commons-collections-3.1.jar b/lib/vpp/commons-collections-3.1.jar Binary files differnew file mode 100644 index 0000000..41e230f --- /dev/null +++ b/lib/vpp/commons-collections-3.1.jar diff --git a/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar b/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar Binary files differnew file mode 100644 index 0000000..3956a43 --- /dev/null +++ b/lib/vpp/foundrylogic-vpp-2.2.1-nodeps.jar diff --git a/lib/vpp/velocity-1.4.jar b/lib/vpp/velocity-1.4.jar Binary files differnew file mode 100644 index 0000000..04ec9d2 --- /dev/null +++ b/lib/vpp/velocity-1.4.jar 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()); + } + } +} |