From bafa4ca8c9f6cc47028abbaf24716a893e39ef70 Mon Sep 17 00:00:00 2001 From: stfm Date: Tue, 19 Jun 2012 01:17:56 +0200 Subject: [PATCH] initial code + examples commit --- README.md | 77 +- .../example1_complete_report/createDocs.sh | 24 + .../example2_complete_report/createDocs.sh | 31 + .../texdoclet_include/finish.tex | 16 + .../texdoclet_include/intro.tex | 41 + .../texdoclet_include/setup.tex | 11 + .../example3_complete_article/createDocs.sh | 25 + .../texdoclet_include/finish.tex | 18 + .../texdoclet_include/intro.tex | 17 + .../texdoclet_include/preamble.tex | 15 + .../texdoclet_include/setup.tex | 14 + examples/example4_include/MyDocument.tex | 36 + examples/example4_include/createDocs.sh | 31 + pom.xml | 54 + src/main/java/com/keypoint/PngEncoder.java | 621 +++++ src/main/java/com/keypoint/PngEncoderB.java | 415 +++ .../java/org/stfm/texdoclet/ClassFilter.java | 20 + .../org/stfm/texdoclet/ClassHierachy.java | 79 + .../stfm/texdoclet/HTMLtoLaTeXBackEnd.java | 610 +++++ .../java/org/stfm/texdoclet/HelpOutput.java | 84 + .../org/stfm/texdoclet/InterfaceHierachy.java | 80 + src/main/java/org/stfm/texdoclet/Package.java | 98 + .../java/org/stfm/texdoclet/TableInfo.java | 338 +++ .../java/org/stfm/texdoclet/TeXDoclet.java | 2321 +++++++++++++++++ .../java/org/stfm/texdoclet/TestFilter.java | 20 + .../stfm/texdoclet/doc-files/texdoclet.gif | Bin 0 -> 14643 bytes .../stfm/texdoclet/doc-files/texdoclet.jpg | Bin 0 -> 5948 bytes .../stfm/texdoclet/doc-files/texdoclet.png | Bin 0 -> 30478 bytes src/main/java/org/stfm/texdoclet/package.html | 6 + 29 files changed, 5100 insertions(+), 2 deletions(-) create mode 100755 examples/example1_complete_report/createDocs.sh create mode 100755 examples/example2_complete_report/createDocs.sh create mode 100644 examples/example2_complete_report/texdoclet_include/finish.tex create mode 100644 examples/example2_complete_report/texdoclet_include/intro.tex create mode 100644 examples/example2_complete_report/texdoclet_include/setup.tex create mode 100755 examples/example3_complete_article/createDocs.sh create mode 100644 examples/example3_complete_article/texdoclet_include/finish.tex create mode 100644 examples/example3_complete_article/texdoclet_include/intro.tex create mode 100644 examples/example3_complete_article/texdoclet_include/preamble.tex create mode 100644 examples/example3_complete_article/texdoclet_include/setup.tex create mode 100644 examples/example4_include/MyDocument.tex create mode 100755 examples/example4_include/createDocs.sh create mode 100644 pom.xml create mode 100755 src/main/java/com/keypoint/PngEncoder.java create mode 100755 src/main/java/com/keypoint/PngEncoderB.java create mode 100755 src/main/java/org/stfm/texdoclet/ClassFilter.java create mode 100755 src/main/java/org/stfm/texdoclet/ClassHierachy.java create mode 100755 src/main/java/org/stfm/texdoclet/HTMLtoLaTeXBackEnd.java create mode 100644 src/main/java/org/stfm/texdoclet/HelpOutput.java create mode 100755 src/main/java/org/stfm/texdoclet/InterfaceHierachy.java create mode 100755 src/main/java/org/stfm/texdoclet/Package.java create mode 100755 src/main/java/org/stfm/texdoclet/TableInfo.java create mode 100755 src/main/java/org/stfm/texdoclet/TeXDoclet.java create mode 100755 src/main/java/org/stfm/texdoclet/TestFilter.java create mode 100644 src/main/java/org/stfm/texdoclet/doc-files/texdoclet.gif create mode 100755 src/main/java/org/stfm/texdoclet/doc-files/texdoclet.jpg create mode 100644 src/main/java/org/stfm/texdoclet/doc-files/texdoclet.png create mode 100755 src/main/java/org/stfm/texdoclet/package.html diff --git a/README.md b/README.md index dfc8493..1fec60f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,75 @@ -texdoclet -========= \ No newline at end of file +TeXDoclet +========= + +TeXDoclet is a Java doclet implementation that generates a LaTeX file from you Java code documentation. + +The doclet is based on the doclet originally created by Greg Wonderly of +[C2 technologies Inc.](http://www.c2-tech.com>) and its revision by Soeren Caspersen of +[XO Software](http://www.xosoftware.dk). The project of Greg Wonderly is still available here: +. + +Build +----- + + mvn clean install + +Run +--- + +Example : + + javadoc -docletpath target/TeXDoclet.jar \ + -doclet org.stfm.texdoclet.TeXDoclet \ + -noindex \ + -tree \ + -hyperref \ + -texinit texdoclet_include/preamble.tex \ + -imagespath ".." \ + -output texdoclet_output/TeXDoclet.tex \ + -title "TeXDoclet Java Documentation" \ + -author "Greg Wonderly \and S\"oren Caspersen \and Stefan Marx" \ + -sourcepath src/main/java \ + -subpackages org:com + +See createDocs.sh script in /examples dir how to run. + +Print help : + + javadoc -docletpath target/TeXDoclet.jar -doclet org.stfm.texdoclet.TeXDoclet + +TeXDoclet usage : + + -title A title to use for the generated output document. + -subtitle <title> A subtitle for the output document. No -title will result in no title page. + -output <outfile> Specifies the output file to write to. If none specified, the default is docs.tex in the current directory. + -docclass <class> LaTeX2e document class, `report' is the default. + -doctype <type> LaTeX2e document style, `headings' is the default. + -classfilter <name> The name of a class implementing the ClassFilter interface. + -date <date string> The value to use for the document date. + -author <author> Specifies string to use for document Author. + -texinit <file> LaTeX2e statements included before \begin{document}. + -texsetup <file> LaTeX2e statements included after \begin{document} \maketitle (if title was specified). + -texintro <file> LaTeX2e statements included after table of contents + -texfinish <file> LaTeX2e statements included before \end{document}. + -texpackage <file> LaTeX2e statements included before packages' \chapter. + -setup <file> A setup file included before \begin{document}. + -twosided Print twosided. + -serial Do print Serializable information. + -nosummaries Do print summaries of fiels, constructors and methods. + -nofieldsummary Do not print field summaries + -noconstructorsummary Do not print constructor summaries + -noinherited Do not include inherited API information in output. + -shortinherited Prints a short inheritance, only the member name (not the whole signature) + -noindex Do not create index. + -tree Create a class tree. + -treeindent <float> Indent <float>cm i the class tree. Default is 1cm. + -hyperref Use the hyperref package. + -pdfhyperref Use the hyperref package with pdf. Overrides -hypertex + -version Includes version-tags + -hr Prints horizontal rows in the output (to get a better? view) + -include Creates output without latex initiation (writes it in initdocsinclude.tex), titlepage, contents + -sectionlevel <level> Specifies the highest level of sections (either "subsection", "section" or "chapter") + -imagespath Path to the texdoclet_images dir (absolute or relative to the output document .tex file). + + + diff --git a/examples/example1_complete_report/createDocs.sh b/examples/example1_complete_report/createDocs.sh new file mode 100755 index 0000000..2f35e13 --- /dev/null +++ b/examples/example1_complete_report/createDocs.sh @@ -0,0 +1,24 @@ +mkdir texdoclet_output + +rm texdoclet_output/TeXDoclet.aux + +javadoc -docletpath ../../target/TeXDoclet.jar \ + -doclet org.stfm.texdoclet.TeXDoclet \ + -noindex \ + -tree \ + -hyperref \ + -texinit texdoclet_include/preamble.tex \ + -imagespath ".." \ + -output texdoclet_output/TeXDoclet.tex \ + -title "TeXDoclet Java Documentation" \ + -subtitle "Created with Javadoc TeXDoclet Doclet" \ + -author "Greg Wonderly \and S\"oren Caspersen \and Stefan Marx" \ + -sourcepath ../../src/main/java \ + -subpackages org:com \ + -shortinherited + +cd texdoclet_output +pdflatex TeXDoclet.tex +pdflatex TeXDoclet.tex +mkdir ../javadoc +cp TeXDoclet.pdf ../javadoc \ No newline at end of file diff --git a/examples/example2_complete_report/createDocs.sh b/examples/example2_complete_report/createDocs.sh new file mode 100755 index 0000000..535dd55 --- /dev/null +++ b/examples/example2_complete_report/createDocs.sh @@ -0,0 +1,31 @@ +# +# this example demonstrates the use of different .tex inlude files +# + +mkdir texdoclet_output + +rm texdoclet_output/TeXDoclet.aux + +javadoc -docletpath ../../target/TeXDoclet.jar \ + -doclet org.stfm.texdoclet.TeXDoclet \ + -noindex \ + -tree \ + -hyperref \ + -texsetup texdoclet_include/setup.tex \ + -texintro texdoclet_include/intro.tex \ + -texfinish texdoclet_include/finish.tex \ + -texinit texdoclet_include/preamble.tex \ + -imagespath ".." \ + -output texdoclet_output/TeXDoclet.tex \ + -title "TeXDoclet Java Documentation" \ + -subtitle "Created with Javadoc TeXDoclet Doclet" \ + -author "Greg Wonderly \and S\"oren Caspersen \and Stefan Marx" \ + -sourcepath ../../src/main/java \ + -subpackages org:com \ + -sectionlevel section + +cd texdoclet_output +pdflatex TeXDoclet.tex +pdflatex TeXDoclet.tex +mkdir ../javadoc +cp TeXDoclet.pdf ../javadoc diff --git a/examples/example2_complete_report/texdoclet_include/finish.tex b/examples/example2_complete_report/texdoclet_include/finish.tex new file mode 100644 index 0000000..627348e --- /dev/null +++ b/examples/example2_complete_report/texdoclet_include/finish.tex @@ -0,0 +1,16 @@ +% ------- textdoclet_include/finish.tex + +% add something here + +% closing for \chapter{TeXDoclet Java Documentation} { +} + +\chapter{Finish}{ + +(content from file finish.tex) +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + +} +% ------- textdoclet_include/finish.tex end diff --git a/examples/example2_complete_report/texdoclet_include/intro.tex b/examples/example2_complete_report/texdoclet_include/intro.tex new file mode 100644 index 0000000..f5641f3 --- /dev/null +++ b/examples/example2_complete_report/texdoclet_include/intro.tex @@ -0,0 +1,41 @@ +% ------- textdoclet_include/intro.tex + +% add something here + +\chapter{Introduction} { + +(content from file intro.tex) +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. +} +\chapter{Foo} { + +(content from file intro.tex) +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. +} + +\chapter{Bar} { + +(content from file intro.tex) +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + +} + +\chapter{TeXDoclet Java Documentation} { + +% ------- textdoclet_include/intro.tex end diff --git a/examples/example2_complete_report/texdoclet_include/setup.tex b/examples/example2_complete_report/texdoclet_include/setup.tex new file mode 100644 index 0000000..f30b848 --- /dev/null +++ b/examples/example2_complete_report/texdoclet_include/setup.tex @@ -0,0 +1,11 @@ +% ------- textdoclet_include/setup.tex + +% add something here +\begin{abstract} +(content from file setup.tex) +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. +\end{abstract} + +% ------- textdoclet_include/setup.tex end diff --git a/examples/example3_complete_article/createDocs.sh b/examples/example3_complete_article/createDocs.sh new file mode 100755 index 0000000..fcf79ea --- /dev/null +++ b/examples/example3_complete_article/createDocs.sh @@ -0,0 +1,25 @@ +mkdir texdoclet_output + +rm texdoclet_output/TeXDoclet.aux + +javadoc -docletpath ../../target/TeXDoclet.jar \ + -doclet org.stfm.texdoclet.TeXDoclet \ + -noindex \ + -hyperref \ + -texinit texdoclet_include/preamble.tex \ + -texsetup texdoclet_include/setup.tex \ + -texintro texdoclet_include/intro.tex \ + -texfinish texdoclet_include/finish.tex \ + -imagespath ".." \ + -output texdoclet_output/TeXDoclet.tex \ + -sourcepath ../../src/main/java \ + -subpackages org:com \ + -sectionlevel section \ + -docclass article \ + -nosummaries + +cd texdoclet_output +pdflatex TeXDoclet.tex +pdflatex TeXDoclet.tex +mkdir ../javadoc +cp TeXDoclet.pdf ../javadoc diff --git a/examples/example3_complete_article/texdoclet_include/finish.tex b/examples/example3_complete_article/texdoclet_include/finish.tex new file mode 100644 index 0000000..f629c31 --- /dev/null +++ b/examples/example3_complete_article/texdoclet_include/finish.tex @@ -0,0 +1,18 @@ +% ------- textdoclet_include/finish.tex + +% add something here + +%\chapter{Finish}{ +\section{Finish}{ +% +% or without numbering : +% +%\section*{Finish} { +%\addcontentsline{toc}{section}{Finish} +(content from file finish.tex) +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + +} +% ------- textdoclet_include/finish.tex end diff --git a/examples/example3_complete_article/texdoclet_include/intro.tex b/examples/example3_complete_article/texdoclet_include/intro.tex new file mode 100644 index 0000000..5d7f7bd --- /dev/null +++ b/examples/example3_complete_article/texdoclet_include/intro.tex @@ -0,0 +1,17 @@ +% ------- textdoclet_include/intro.tex + +% add something here + +\section{Introduction} { +% +% or without numbering : +% +%\section*{Introduction} { +%\addcontentsline{toc}{section}{Introduction} +(content from file intro.tex) +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. +} + +% ------- textdoclet_include/intro.tex end diff --git a/examples/example3_complete_article/texdoclet_include/preamble.tex b/examples/example3_complete_article/texdoclet_include/preamble.tex new file mode 100644 index 0000000..3e6018b --- /dev/null +++ b/examples/example3_complete_article/texdoclet_include/preamble.tex @@ -0,0 +1,15 @@ +% ------- textdoclet_include/preamble.tex + +% add something here + +\date{\today} +\title{TeXDoclet Java Documentation\bigskip\\ \Large Created with Javadoc TeXDoclet Doclet} +\author{Greg Wonderly \and S\"oren Caspersen \and Stefan Marx} + +%\usepackage{titlesec} +%\titlespacing*{\chapter}{0pt}{10pt}{5pt} +%\titlespacing*{\section}{0pt}{10pt}{5pt} +%\titlespacing*{\subsection}{0pt}{5pt}{3pt} +%\titlespacing*{\subsubsection}{0pt}{5pt}{3pt} + +% ------- textdoclet_include/preamble.tex end diff --git a/examples/example3_complete_article/texdoclet_include/setup.tex b/examples/example3_complete_article/texdoclet_include/setup.tex new file mode 100644 index 0000000..e4c2092 --- /dev/null +++ b/examples/example3_complete_article/texdoclet_include/setup.tex @@ -0,0 +1,14 @@ +% ------- textdoclet_include/setup.tex + +% we have to call maketitle here because no title was given in the javadoc -docletpath ../../target/TeXDoclet.jar execution +\maketitle + +% add something here +\begin{abstract} +(content from file setup.tex) +\newline +\newline +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. +\end{abstract} + +% ------- textdoclet_include/setup.tex end diff --git a/examples/example4_include/MyDocument.tex b/examples/example4_include/MyDocument.tex new file mode 100644 index 0000000..7947c7b --- /dev/null +++ b/examples/example4_include/MyDocument.tex @@ -0,0 +1,36 @@ +\documentclass[11pt,a4paper]{report} +\usepackage[english]{babel} +\usepackage{blindtext} + +% 1. TeXDoclet output include +\input{texdoclet_output/TeXDoclet_preamble} + +% use behind of TeXDoclet_preamble.tex because of option clash with pdftex +\usepackage{fullpage} +\usepackage{hyperref} +\hypersetup{ + colorlinks=true,allcolors=blue,pdfpagemode=UseOutlines,pdfpagelayout=TwoPageRight +} +\usepackage{hypcap} + +\begin{document} + +\date{\today} +\title{TeXDoclet Java Documentation\bigskip\\ \Large Created with Javadoc TeXDoclet Doclet} +\author{Greg Wonderly \and S\"oren Caspersen \and Stefan Marx} +\maketitle + +\tableofcontents + +\chapter{Foo} {\section{Foo1} {\blindtext}} + +\chapter{Bar} {\section{Bar1} {\blindtext \newline \newline \blindtext }\section{Bar2} {\blindtext}} + +\chapter{TeXdoclet Java Documentation} { +% 2. TeXDoclet output include +\input{texdoclet_output/TeXDoclet} +} + +\chapter{FooBar} {\section{FooBar1} {\blindtext}} + +\end{document} diff --git a/examples/example4_include/createDocs.sh b/examples/example4_include/createDocs.sh new file mode 100755 index 0000000..7985385 --- /dev/null +++ b/examples/example4_include/createDocs.sh @@ -0,0 +1,31 @@ +# +# - this example shows how to use the -include option +# - TeXDoclet generated output (TeXDoclet.tex and TeXDoclet_preamble.tex) +# is included in another Latex document (MyDocument.tex) here +# + +mkdir texdoclet_output + +rm MyDocument.aux +rm MyDocument.idx +rm MyDocument.out +rm MyDocument.toc +rm MyDocument.pdf +rm texdoclet_output/TeXDoclet.aux +rm texdoclet_output/TeXDoclet_preamble.aux + +# -imagespath ".." not needed here because path texdoclet_images is in subdir of MyDocument.tex + +javadoc -docletpath ../../target/TeXDoclet.jar \ + -doclet org.stfm.texdoclet.TeXDoclet \ + -tree \ + -noindex \ + -hyperref \ + -output texdoclet_output/TeXDoclet.tex \ + -sourcepath ../../src/main/java \ + -subpackages com:org \ + -include \ + -sectionlevel section + +pdflatex MyDocument.tex +pdflatex MyDocument.tex diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d4b54b7 --- /dev/null +++ b/pom.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.stfm</groupId> + <artifactId>texdoclet</artifactId> + <version>0.9-SNAPSHOT</version> + <name>TexDoclet</name> + <packaging>jar</packaging> + + <properties> + + <maven.compiler.source>1.6</maven.compiler.source> + <maven.compiler.target>1.6</maven.compiler.target> + + <maven-shade-plugin.version>1.4</maven-shade-plugin.version> + <maven-war-plugin.version>2.1.1</maven-war-plugin.version> + + <finalJarName>TeXdoclet</finalJarName> + + </properties> + + <build> + + <finalName>${finalJarName}</finalName> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>${maven-shade-plugin.version}</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <manifestEntries> + <Main-Class>org.stfm.texdoclet.TeXDoclet</Main-Class> + </manifestEntries> + </transformer> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/src/main/java/com/keypoint/PngEncoder.java b/src/main/java/com/keypoint/PngEncoder.java new file mode 100755 index 0000000..d3e1888 --- /dev/null +++ b/src/main/java/com/keypoint/PngEncoder.java @@ -0,0 +1,621 @@ +package com.keypoint; + +/** + * PngEncoder takes a Java Image object and creates a byte string which can be saved as a PNG file. + * The Image is presumed to use the DirectColorModel. + * + * Thanks to Jay Denny at KeyPoint Software + * http://www.keypoint.com/ + * who let me develop this code on company time. + * + * You may contact me with (probably very-much-needed) improvements, + * comments, and bug fixes at: + * + * david@catcode.com + * + * 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 copy of the GNU LGPL may be found at + * http://www.gnu.org/copyleft/lesser.html, + * + * @author J. David Eisenberg + * @version 1.4, 31 March 2000 + */ + +import java.awt.*; +import java.awt.image.*; +import java.util.*; +import java.util.zip.*; +import java.io.*; + +public class PngEncoder extends Object +{ + /** Constant specifying that alpha channel should be encoded. */ + public static final boolean ENCODE_ALPHA=true; + /** Constant specifying that alpha channel should not be encoded. */ + public static final boolean NO_ALPHA=false; + /** Constants for filters */ + public static final int FILTER_NONE = 0; + public static final int FILTER_SUB = 1; + public static final int FILTER_UP = 2; + public static final int FILTER_LAST = 2; + + protected byte[] pngBytes; + protected byte[] priorRow; + protected byte[] leftBytes; + protected Image image; + protected int width, height; + protected int bytePos, maxPos; + protected int hdrPos, dataPos, endPos; + protected CRC32 crc = new CRC32(); + protected long crcValue; + protected boolean encodeAlpha; + protected int filter; + protected int bytesPerPixel; + protected int compressionLevel; + + /** + * Class constructor + * + */ + public PngEncoder() + { + this( null, false, FILTER_NONE, 0 ); + } + + /** + * Class constructor specifying Image to encode, with no alpha channel encoding. + * + * @param image A Java Image object which uses the DirectColorModel + * @see java.awt.Image + */ + public PngEncoder( Image image ) + { + this(image, false, FILTER_NONE, 0); + } + + /** + * Class constructor specifying Image to encode, and whether to encode alpha. + * + * @param image A Java Image object which uses the DirectColorModel + * @param encodeAlpha Encode the alpha channel? false=no; true=yes + * @see java.awt.Image + */ + public PngEncoder( Image image, boolean encodeAlpha ) + { + this(image, encodeAlpha, FILTER_NONE, 0); + } + + /** + * Class constructor specifying Image to encode, whether to encode alpha, and filter to use. + * + * @param image A Java Image object which uses the DirectColorModel + * @param encodeAlpha Encode the alpha channel? false=no; true=yes + * @param whichFilter 0=none, 1=sub, 2=up + * @see java.awt.Image + */ + public PngEncoder( Image image, boolean encodeAlpha, int whichFilter ) + { + this( image, encodeAlpha, whichFilter, 0 ); + } + + + /** + * Class constructor specifying Image source to encode, whether to encode alpha, filter to use, and compression level. + * + * @param image A Java Image object + * @param encodeAlpha Encode the alpha channel? false=no; true=yes + * @param whichFilter 0=none, 1=sub, 2=up + * @param compLevel 0..9 + * @see java.awt.Image + */ + public PngEncoder( Image image, boolean encodeAlpha, int whichFilter, + int compLevel) + { + this.image = image; + this.encodeAlpha = encodeAlpha; + setFilter( whichFilter ); + if (compLevel >=0 && compLevel <=9) + { + this.compressionLevel = compLevel; + } + } + + /** + * Set the image to be encoded + * + * @param image A Java Image object which uses the DirectColorModel + * @see java.awt.Image + * @see java.awt.image.DirectColorModel + */ + public void setImage( Image image ) + { + this.image = image; + pngBytes = null; + } + + /** + * Creates an array of bytes that is the PNG equivalent of the current image, specifying whether to encode alpha or not. + * + * @param encodeAlpha boolean false=no alpha, true=encode alpha + * @return an array of bytes, or null if there was a problem + */ + public byte[] pngEncode( boolean encodeAlpha ) + { + byte[] pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10 }; + int i; + + if (image == null) + { + return null; + } + width = image.getWidth( null ); + height = image.getHeight( null ); + this.image = image; + + /* + * start with an array that is big enough to hold all the pixels + * (plus filter bytes), and an extra 200 bytes for header info + */ + pngBytes = new byte[((width+1) * height * 3) + 200]; + + /* + * keep track of largest byte written to the array + */ + maxPos = 0; + + bytePos = writeBytes( pngIdBytes, 0 ); + hdrPos = bytePos; + writeHeader(); + dataPos = bytePos; + if (writeImageData()) + { + writeEnd(); + pngBytes = resizeByteArray( pngBytes, maxPos ); + } + else + { + pngBytes = null; + } + return pngBytes; + } + + /** + * Creates an array of bytes that is the PNG equivalent of the current image. + * Alpha encoding is determined by its setting in the constructor. + * + * @return an array of bytes, or null if there was a problem + */ + public byte[] pngEncode() + { + return pngEncode( encodeAlpha ); + } + + /** + * Set the alpha encoding on or off. + * + * @param encodeAlpha false=no, true=yes + */ + public void setEncodeAlpha( boolean encodeAlpha ) + { + this.encodeAlpha = encodeAlpha; + } + + /** + * Retrieve alpha encoding status. + * + * @return boolean false=no, true=yes + */ + public boolean getEncodeAlpha() + { + return encodeAlpha; + } + + /** + * Set the filter to use + * + * @param whichFilter from constant list + */ + public void setFilter( int whichFilter ) + { + this.filter = FILTER_NONE; + if ( whichFilter <= FILTER_LAST ) + { + this.filter = whichFilter; + } + } + + /** + * Retrieve filtering scheme + * + * @return int (see constant list) + */ + public int getFilter() + { + return filter; + } + + /** + * Set the compression level to use + * + * @param level 0 through 9 + */ + public void setCompressionLevel( int level ) + { + if ( level >= 0 && level <= 9) + { + this.compressionLevel = level; + } + } + + /** + * Retrieve compression level + * + * @return int in range 0-9 + */ + public int getCompressionLevel() + { + return compressionLevel; + } + + /** + * Increase or decrease the length of a byte array. + * + * @param array The original array. + * @param newLength The length you wish the new array to have. + * @return Array of newly desired length. If shorter than the + * original, the trailing elements are truncated. + */ + protected byte[] resizeByteArray( byte[] array, int newLength ) + { + byte[] newArray = new byte[newLength]; + int oldLength = array.length; + + System.arraycopy( array, 0, newArray, 0, + Math.min( oldLength, newLength ) ); + return newArray; + } + + /** + * Write an array of bytes into the pngBytes array. + * Note: This routine has the side effect of updating + * maxPos, the largest element written in the array. + * The array is resized by 1000 bytes or the length + * of the data to be written, whichever is larger. + * + * @param data The data to be written into pngBytes. + * @param offset The starting point to write to. + * @return The next place to be written to in the pngBytes array. + */ + protected int writeBytes( byte[] data, int offset ) + { + maxPos = Math.max( maxPos, offset + data.length ); + if (data.length + offset > pngBytes.length) + { + pngBytes = resizeByteArray( pngBytes, pngBytes.length + + Math.max( 1000, data.length ) ); + } + System.arraycopy( data, 0, pngBytes, offset, data.length ); + return offset + data.length; + } + + /** + * Write an array of bytes into the pngBytes array, specifying number of bytes to write. + * Note: This routine has the side effect of updating + * maxPos, the largest element written in the array. + * The array is resized by 1000 bytes or the length + * of the data to be written, whichever is larger. + * + * @param data The data to be written into pngBytes. + * @param nBytes The number of bytes to be written. + * @param offset The starting point to write to. + * @return The next place to be written to in the pngBytes array. + */ + protected int writeBytes( byte[] data, int nBytes, int offset ) + { + maxPos = Math.max( maxPos, offset + nBytes ); + if (nBytes + offset > pngBytes.length) + { + pngBytes = resizeByteArray( pngBytes, pngBytes.length + + Math.max( 1000, nBytes ) ); + } + System.arraycopy( data, 0, pngBytes, offset, nBytes ); + return offset + nBytes; + } + + /** + * Write a two-byte integer into the pngBytes array at a given position. + * + * @param n The integer to be written into pngBytes. + * @param offset The starting point to write to. + * @return The next place to be written to in the pngBytes array. + */ + protected int writeInt2( int n, int offset ) + { + byte[] temp = { (byte)((n >> 8) & 0xff), + (byte) (n & 0xff) }; + return writeBytes( temp, offset ); + } + + /** + * Write a four-byte integer into the pngBytes array at a given position. + * + * @param n The integer to be written into pngBytes. + * @param offset The starting point to write to. + * @return The next place to be written to in the pngBytes array. + */ + protected int writeInt4( int n, int offset ) + { + byte[] temp = { (byte)((n >> 24) & 0xff), + (byte) ((n >> 16) & 0xff ), + (byte) ((n >> 8) & 0xff ), + (byte) ( n & 0xff ) }; + return writeBytes( temp, offset ); + } + + /** + * Write a single byte into the pngBytes array at a given position. + * + * @param n The integer to be written into pngBytes. + * @param offset The starting point to write to. + * @return The next place to be written to in the pngBytes array. + */ + protected int writeByte( int b, int offset ) + { + byte[] temp = { (byte) b }; + return writeBytes( temp, offset ); + } + + /** + * Write a string into the pngBytes array at a given position. + * This uses the getBytes method, so the encoding used will + * be its default. + * + * @param n The integer to be written into pngBytes. + * @param offset The starting point to write to. + * @return The next place to be written to in the pngBytes array. + * @see java.lang.String#getBytes() + */ + protected int writeString( String s, int offset ) + { + return writeBytes( s.getBytes(), offset ); + } + + /** + * Write a PNG "IHDR" chunk into the pngBytes array. + */ + protected void writeHeader() + { + int startPos; + + startPos = bytePos = writeInt4( 13, bytePos ); + bytePos = writeString( "IHDR", bytePos ); + width = image.getWidth( null ); + height = image.getHeight( null ); + bytePos = writeInt4( width, bytePos ); + bytePos = writeInt4( height, bytePos ); + bytePos = writeByte( 8, bytePos ); // bit depth + bytePos = writeByte( (encodeAlpha) ? 6 : 2, bytePos ); // direct model + bytePos = writeByte( 0, bytePos ); // compression method + bytePos = writeByte( 0, bytePos ); // filter method + bytePos = writeByte( 0, bytePos ); // no interlace + crc.reset(); + crc.update( pngBytes, startPos, bytePos-startPos ); + crcValue = crc.getValue(); + bytePos = writeInt4( (int) crcValue, bytePos ); + } + + /** + * Perform "sub" filtering on the given row. + * Uses temporary array leftBytes to store the original values + * of the previous pixels. The array is 16 bytes long, which + * will easily hold two-byte samples plus two-byte alpha. + * + * @param pixels The array holding the scan lines being built + * @param startPos Starting position within pixels of bytes to be filtered. + * @param width Width of a scanline in pixels. + */ + protected void filterSub( byte[] pixels, int startPos, int width ) + { + int i; + int offset = bytesPerPixel; + int actualStart = startPos + offset; + int nBytes = width * bytesPerPixel; + int leftInsert = offset; + int leftExtract = 0; + byte current_byte; + + for (i=actualStart; i < startPos + nBytes; i++) + { + leftBytes[leftInsert] = pixels[i]; + pixels[i] = (byte) ((pixels[i] - leftBytes[leftExtract]) % 256); + leftInsert = (leftInsert+1) % 0x0f; + leftExtract = (leftExtract + 1) % 0x0f; + } + } + + /** + * Perform "up" filtering on the given row. + * Side effect: refills the prior row with current row + * + * @param pixels The array holding the scan lines being built + * @param startPos Starting position within pixels of bytes to be filtered. + * @param width Width of a scanline in pixels. + */ + protected void filterUp( byte[] pixels, int startPos, int width ) + { + int i, nBytes; + byte current_byte; + + nBytes = width * bytesPerPixel; + + for (i=0; i < nBytes; i++) + { + current_byte = pixels[startPos + i]; + pixels[startPos + i] = (byte) ((pixels[startPos + i] - priorRow[i]) % 256); + priorRow[i] = current_byte; + } + } + + /** + * Write the image data into the pngBytes array. + * This will write one or more PNG "IDAT" chunks. In order + * to conserve memory, this method grabs as many rows as will + * fit into 32K bytes, or the whole image; whichever is less. + * + * + * @return true if no errors; false if error grabbing pixels + */ + protected boolean writeImageData() + { + int rowsLeft = height; // number of rows remaining to write + int startRow = 0; // starting row to process this time through + int nRows; // how many rows to grab at a time + + byte[] scanLines; // the scan lines to be compressed + int scanPos; // where we are in the scan lines + int startPos; // where this line's actual pixels start (used for filtering) + + byte[] compressedLines; // the resultant compressed lines + int nCompressed; // how big is the compressed area? + + int depth; // color depth ( handle only 8 or 32 ) + + PixelGrabber pg; + + bytesPerPixel = (encodeAlpha) ? 4 : 3; + + Deflater scrunch = new Deflater( compressionLevel ); + ByteArrayOutputStream outBytes = + new ByteArrayOutputStream(1024); + + DeflaterOutputStream compBytes = + new DeflaterOutputStream( outBytes, scrunch ); + try + { + while (rowsLeft > 0) + { + nRows = Math.min( 32767 / (width*(bytesPerPixel+1)), rowsLeft ); + // nRows = rowsLeft; + + int[] pixels = new int[width * nRows]; + + pg = new PixelGrabber(image, 0, startRow, + width, nRows, pixels, 0, width); + try { + pg.grabPixels(); + } + catch (Exception e) { + System.err.println("interrupted waiting for pixels!"); + return false; + } + if ((pg.getStatus() & ImageObserver.ABORT) != 0) { + System.err.println("image fetch aborted or errored"); + return false; + } + + /* + * Create a data chunk. scanLines adds "nRows" for + * the filter bytes. + */ + scanLines = new byte[width * nRows * bytesPerPixel + nRows]; + + if (filter == FILTER_SUB) + { + leftBytes = new byte[16]; + } + if (filter == FILTER_UP) + { + priorRow = new byte[width*bytesPerPixel]; + } + + scanPos = 0; + startPos = 1; + for (int i=0; i<width*nRows; i++) + { + if (i % width == 0) + { + scanLines[scanPos++] = (byte) filter; + startPos = scanPos; + } + scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff); + scanLines[scanPos++] = (byte) ((pixels[i] >> 8) & 0xff); + scanLines[scanPos++] = (byte) ((pixels[i] ) & 0xff); + if (encodeAlpha) + { + scanLines[scanPos++] = (byte) ((pixels[i] >> 24) & 0xff ); + } + if ((i % width == width-1) && (filter != FILTER_NONE)) + { + if (filter == FILTER_SUB) + { + filterSub( scanLines, startPos, width ); + } + if (filter == FILTER_UP) + { + filterUp( scanLines, startPos, width ); + } + } + } + + /* + * Write these lines to the output area + */ + compBytes.write( scanLines, 0, scanPos ); + + + startRow += nRows; + rowsLeft -= nRows; + } + compBytes.close(); + + /* + * Write the compressed bytes + */ + compressedLines = outBytes.toByteArray(); + nCompressed = compressedLines.length; + + crc.reset(); + bytePos = writeInt4( nCompressed, bytePos ); + bytePos = writeString("IDAT", bytePos ); + crc.update("IDAT".getBytes()); + bytePos = writeBytes( compressedLines, nCompressed, bytePos ); + crc.update( compressedLines, 0, nCompressed ); + + crcValue = crc.getValue(); + bytePos = writeInt4( (int) crcValue, bytePos ); + scrunch.finish(); + return true; + } + catch (IOException e) + { + System.err.println( e.toString()); + return false; + } + } + + /** + * Write a PNG "IEND" chunk into the pngBytes array. + */ + protected void writeEnd() + { + bytePos = writeInt4( 0, bytePos ); + bytePos = writeString( "IEND", bytePos ); + crc.reset(); + crc.update("IEND".getBytes()); + crcValue = crc.getValue(); + bytePos = writeInt4( (int) crcValue, bytePos ); + } +} + diff --git a/src/main/java/com/keypoint/PngEncoderB.java b/src/main/java/com/keypoint/PngEncoderB.java new file mode 100755 index 0000000..6d127bb --- /dev/null +++ b/src/main/java/com/keypoint/PngEncoderB.java @@ -0,0 +1,415 @@ +package com.keypoint; + +/** + * PngEncoderB takes a Java BufferedImage object and creates a byte string which can be saved as a PNG file. + * The encoder will accept BufferedImages with eight-bit samples + * or 4-byte ARGB samples. + * + * There is also code to handle 4-byte samples returned as + * one int per pixel, but that has not been tested. + * + * Thanks to Jay Denny at KeyPoint Software + * http://www.keypoint.com/ + * who let me develop this code on company time. + * + * You may contact me with (probably very-much-needed) improvements, + * comments, and bug fixes at: + * + * david@catcode.com + * + * 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 copy of the GNU LGPL may be found at + * http://www.gnu.org/copyleft/lesser.html, + * + * @author J. David Eisenberg + * @version 1.4, 31 March 2000 + */ + +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +public class PngEncoderB extends PngEncoder { + protected BufferedImage image; + protected WritableRaster wRaster; + protected int tType; + + /** + * Class constructor + * + */ + public PngEncoderB() { + this(null, false, FILTER_NONE, 0); + } + + /** + * Class constructor specifying BufferedImage to encode, with no alpha + * channel encoding. + * + * @param image + * A Java BufferedImage object + */ + public PngEncoderB(BufferedImage image) { + this(image, false, FILTER_NONE, 0); + } + + /** + * Class constructor specifying BufferedImage to encode, and whether to + * encode alpha. + * + * @param image + * A Java BufferedImage object + * @param encodeAlpha + * Encode the alpha channel? false=no; true=yes + */ + public PngEncoderB(BufferedImage image, boolean encodeAlpha) { + this(image, encodeAlpha, FILTER_NONE, 0); + } + + /** + * Class constructor specifying BufferedImage to encode, whether to encode + * alpha, and filter to use. + * + * @param image + * A Java BufferedImage object + * @param encodeAlpha + * Encode the alpha channel? false=no; true=yes + * @param whichFilter + * 0=none, 1=sub, 2=up + */ + public PngEncoderB(BufferedImage image, boolean encodeAlpha, int whichFilter) { + this(image, encodeAlpha, whichFilter, 0); + } + + /** + * Class constructor specifying BufferedImage source to encode, whether to + * encode alpha, filter to use, and compression level + * + * @param image + * A Java BufferedImage object + * @param encodeAlpha + * Encode the alpha channel? false=no; true=yes + * @param whichFilter + * 0=none, 1=sub, 2=up + * @param compLevel + * 0..9 + */ + public PngEncoderB(BufferedImage image, boolean encodeAlpha, + int whichFilter, int compLevel) { + this.image = image; + this.encodeAlpha = encodeAlpha; + setFilter(whichFilter); + if (compLevel >= 0 && compLevel <= 9) { + this.compressionLevel = compLevel; + } + } + + /** + * Set the BufferedImage to be encoded + * + * @param BufferedImage + * A Java BufferedImage object + */ + public void setImage(BufferedImage image) { + this.image = image; + pngBytes = null; + } + + /** + * Creates an array of bytes that is the PNG equivalent of the current + * image, specifying whether to encode alpha or not. + * + * @param encodeAlpha + * boolean false=no alpha, true=encode alpha + * @return an array of bytes, or null if there was a problem + */ + @Override + public byte[] pngEncode(boolean encodeAlpha) { + byte[] pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10 }; + int i; + + if (image == null) { + return null; + } + width = image.getWidth(null); + height = image.getHeight(null); + this.image = image; + + if (!establishStorageInfo()) { + return null; + } + + /* + * start with an array that is big enough to hold all the pixels (plus + * filter bytes), and an extra 200 bytes for header info + */ + pngBytes = new byte[((width + 1) * height * 3) + 200]; + + /* + * keep track of largest byte written to the array + */ + maxPos = 0; + + bytePos = writeBytes(pngIdBytes, 0); + hdrPos = bytePos; + writeHeader(); + dataPos = bytePos; + if (writeImageData()) { + writeEnd(); + pngBytes = resizeByteArray(pngBytes, maxPos); + } else { + pngBytes = null; + } + return pngBytes; + } + + /** + * Creates an array of bytes that is the PNG equivalent of the current + * image. Alpha encoding is determined by its setting in the constructor. + * + * @return an array of bytes, or null if there was a problem + */ + @Override + public byte[] pngEncode() { + return pngEncode(encodeAlpha); + } + + /** + * + * Get and set variables that determine how picture is stored. + * + * Retrieves the writable raster of the buffered image, as well its transfer + * type. + * + * Sets number of output bytes per pixel, and, if only eight-bit bytes, + * turns off alpha encoding. + * + * @return true if 1-byte or 4-byte data, false otherwise + */ + protected boolean establishStorageInfo() { + int dataBytes; + + wRaster = image.getRaster(); + dataBytes = wRaster.getNumDataElements(); + tType = wRaster.getTransferType(); + + if (((tType == DataBuffer.TYPE_BYTE) && (dataBytes == 4)) + || ((tType == DataBuffer.TYPE_INT) && (dataBytes == 1))) { + bytesPerPixel = (encodeAlpha) ? 4 : 3; + } else if ((tType == DataBuffer.TYPE_BYTE) && (dataBytes == 1)) { + bytesPerPixel = 1; + encodeAlpha = false; // one-byte samples + } else { + return false; + } + return true; + } + + /** + * Write a PNG "IHDR" chunk into the pngBytes array. + */ + @Override + protected void writeHeader() { + int startPos; + + startPos = bytePos = writeInt4(13, bytePos); + bytePos = writeString("IHDR", bytePos); + width = image.getWidth(null); + height = image.getHeight(null); + bytePos = writeInt4(width, bytePos); + bytePos = writeInt4(height, bytePos); + bytePos = writeByte(8, bytePos); // bit depth + if (bytesPerPixel != 1) { + bytePos = writeByte((encodeAlpha) ? 6 : 2, bytePos); // direct model + } else { + bytePos = writeByte(3, bytePos); // indexed + } + bytePos = writeByte(0, bytePos); // compression method + bytePos = writeByte(0, bytePos); // filter method + bytePos = writeByte(0, bytePos); // no interlace + crc.reset(); + crc.update(pngBytes, startPos, bytePos - startPos); + crcValue = crc.getValue(); + bytePos = writeInt4((int) crcValue, bytePos); + } + + protected void writePalette(IndexColorModel icm) { + byte[] redPal = new byte[256]; + byte[] greenPal = new byte[256]; + byte[] bluePal = new byte[256]; + byte[] allPal = new byte[768]; + int i; + + icm.getReds(redPal); + icm.getGreens(greenPal); + icm.getBlues(bluePal); + for (i = 0; i < 256; i++) { + allPal[i * 3] = redPal[i]; + allPal[i * 3 + 1] = greenPal[i]; + allPal[i * 3 + 2] = bluePal[i]; + } + bytePos = writeInt4(768, bytePos); + bytePos = writeString("PLTE", bytePos); + crc.reset(); + crc.update("PLTE".getBytes()); + bytePos = writeBytes(allPal, bytePos); + crc.update(allPal); + crcValue = crc.getValue(); + bytePos = writeInt4((int) crcValue, bytePos); + } + + /** + * Write the image data into the pngBytes array. This will write one or more + * PNG "IDAT" chunks. In order to conserve memory, this method grabs as many + * rows as will fit into 32K bytes, or the whole image; whichever is less. + * + * + * @return true if no errors; false if error grabbing pixels + */ + @Override + protected boolean writeImageData() { + int rowsLeft = height; // number of rows remaining to write + int startRow = 0; // starting row to process this time through + int nRows; // how many rows to grab at a time + + byte[] scanLines; // the scan lines to be compressed + int scanPos; // where we are in the scan lines + int startPos; // where this line's actual pixels start (used for + // filtering) + int readPos; // position from which source pixels are read + + byte[] compressedLines; // the resultant compressed lines + int nCompressed; // how big is the compressed area? + + byte[] pixels; // storage area for byte-sized pixels + int[] iPixels; // storage area for int-sized pixels + + Deflater scrunch = new Deflater(compressionLevel); + ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024); + + DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes, + scrunch); + + if (bytesPerPixel == 1) { + writePalette((IndexColorModel) image.getColorModel()); + } + + try { + while (rowsLeft > 0) { + nRows = Math.min(32767 / (width * (bytesPerPixel + 1)), + rowsLeft); + // nRows = rowsLeft; + + /* + * Create a data chunk. scanLines adds "nRows" for the filter + * bytes. + */ + scanLines = new byte[width * nRows * bytesPerPixel + nRows]; + + if (filter == FILTER_SUB) { + leftBytes = new byte[16]; + } + if (filter == FILTER_UP) { + priorRow = new byte[width * bytesPerPixel]; + } + + if (tType == DataBuffer.TYPE_BYTE) { + pixels = (byte[]) wRaster.getDataElements(0, startRow, + width, nRows, null); + iPixels = null; + } else { + iPixels = (int[]) wRaster.getDataElements(0, startRow, + width, nRows, null); + pixels = null; + } + + scanPos = 0; + readPos = 0; + startPos = 1; + for (int i = 0; i < width * nRows; i++) { + if (i % width == 0) { + scanLines[scanPos++] = (byte) filter; + startPos = scanPos; + } + + if (bytesPerPixel == 1) { + scanLines[scanPos++] = pixels[readPos++]; + } else if (tType == DataBuffer.TYPE_BYTE) { + scanLines[scanPos++] = pixels[readPos++]; + scanLines[scanPos++] = pixels[readPos++]; + scanLines[scanPos++] = pixels[readPos++]; + if (encodeAlpha) { + scanLines[scanPos++] = pixels[readPos++]; + } else { + readPos++; + } + } else { + scanLines[scanPos++] = (byte) ((iPixels[readPos] >> 16) & 0xff); + scanLines[scanPos++] = (byte) ((iPixels[readPos] >> 8) & 0xff); + scanLines[scanPos++] = (byte) ((iPixels[readPos]) & 0xff); + if (encodeAlpha) { + scanLines[scanPos++] = (byte) ((iPixels[readPos] >> 24) & 0xff); + } + readPos++; + } + if ((i % width == width - 1) && (filter != FILTER_NONE)) { + if (filter == FILTER_SUB) { + filterSub(scanLines, startPos, width); + } + if (filter == FILTER_UP) { + filterUp(scanLines, startPos, width); + } + } + } + + /* + * Write these lines to the output area + */ + compBytes.write(scanLines, 0, scanPos); + + startRow += nRows; + rowsLeft -= nRows; + } + compBytes.close(); + + /* + * Write the compressed bytes + */ + compressedLines = outBytes.toByteArray(); + nCompressed = compressedLines.length; + + crc.reset(); + bytePos = writeInt4(nCompressed, bytePos); + bytePos = writeString("IDAT", bytePos); + crc.update("IDAT".getBytes()); + bytePos = writeBytes(compressedLines, nCompressed, bytePos); + crc.update(compressedLines, 0, nCompressed); + + crcValue = crc.getValue(); + bytePos = writeInt4((int) crcValue, bytePos); + scrunch.finish(); + return true; + } catch (IOException e) { + System.err.println(e.toString()); + return false; + } + } + +} diff --git a/src/main/java/org/stfm/texdoclet/ClassFilter.java b/src/main/java/org/stfm/texdoclet/ClassFilter.java new file mode 100755 index 0000000..4c8f9f5 --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/ClassFilter.java @@ -0,0 +1,20 @@ +package org.stfm.texdoclet; + +import com.sun.javadoc.ClassDoc; + +/** + * This interface can be implemented and a class name provided to the Doclet to + * filter which classes are and are not included in the output document. + * + * @version $Revision: 1.1 $ + * @author Gregg Wonderly - C2 Technologies Inc. + */ +public interface ClassFilter { + + /** + * Filters the ClassDoc passed. If true is returned, the passed class will + * be included into the output. If false is returned, this document will not + * be included. + */ + public boolean includeClass(ClassDoc cd); +} \ No newline at end of file diff --git a/src/main/java/org/stfm/texdoclet/ClassHierachy.java b/src/main/java/org/stfm/texdoclet/ClassHierachy.java new file mode 100755 index 0000000..91ba13e --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/ClassHierachy.java @@ -0,0 +1,79 @@ +package org.stfm.texdoclet; + +import java.util.Iterator; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.sun.javadoc.ClassDoc; +import com.sun.javadoc.RootDoc; + +/** + * Manages and prints a class hierarchy. Use <CODE>add</CODE> to add another + * class to the hierarchy. Use <CODE>printTree</CODE> to print the corresponding + * <TEX txt="\LaTeX{}">LaTeX</TEX>. + * + * @version $Revision: 1.1 $ + * @author Soeren Caspersen - XO Software + */ +public class ClassHierachy extends java.lang.Object { + + public SortedMap root = new TreeMap(); + + /** + * Creates new ClassHierachy + */ + public ClassHierachy() { + } + + /** + * Adds another class to the hierachy + */ + protected SortedMap add(ClassDoc cls) { + SortedMap temp; + if (cls.superclass() != null) { + temp = add(cls.superclass()); + } else { + temp = root; + } + + SortedMap result = (SortedMap) temp.get(cls.qualifiedName()); + if (result == null) { + result = new TreeMap(); + temp.put(cls.qualifiedName(), result); + } + return result; + } + + /** + * Prints the <TEX txt="\LaTeX{}">LaTeX</TEX> corresponding to the tree. The + * tree is printed using <CODE>TeXDoclet.os</CODE>. + */ + public void printTree(RootDoc rootDoc, double overviewindent) { + printBranch(rootDoc, root, 0, overviewindent); + } + + /** + * Prints a branch of the tree. The branch is printed using + * <CODE>TeXDoclet.os</CODE>. + */ + protected void printBranch(RootDoc rootDoc, SortedMap map, double indent, + double overviewindent) { + Set set = map.keySet(); + Iterator it = set.iterator(); + while (it.hasNext()) { + String qualifName = (String) it.next(); + ClassDoc cls = rootDoc.classNamed(qualifName); + TeXDoclet.os.print("\\hspace{" + Double.toString(indent) + + "cm} $\\bullet$ " + + HTMLtoLaTeXBackEnd.fixText(qualifName) + " {\\tiny "); + // (S.M. modification) only for resolved classes + if (cls != null) { + TeXDoclet.printRef(cls.containingPackage(), cls.name(), ""); + } + TeXDoclet.os.println("} \\\\"); + printBranch(rootDoc, (SortedMap) map.get(qualifName), indent + + overviewindent, overviewindent); + } + } +} diff --git a/src/main/java/org/stfm/texdoclet/HTMLtoLaTeXBackEnd.java b/src/main/java/org/stfm/texdoclet/HTMLtoLaTeXBackEnd.java new file mode 100755 index 0000000..2ac140a --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/HTMLtoLaTeXBackEnd.java @@ -0,0 +1,610 @@ +package org.stfm.texdoclet; + +import java.awt.Color; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.image.MemoryImageSource; +import java.awt.image.PixelGrabber; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.Hashtable; +import java.util.Stack; + +import javax.swing.ImageIcon; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.parser.ParserDelegator; + +import com.keypoint.PngEncoder; + +/** + * This class implements a <CODE>ParserCallback</CODE> that translates HTML to + * the corresponding <TEX txt="\LaTeX{}">LaTeX</TEX>. Not all tags a processed + * but the most common are. + * + * @see javax.swing.text.html.parser.ParserDelegator + * @author Soeren Caspersen + */ +public class HTMLtoLaTeXBackEnd extends HTMLEditorKit.ParserCallback { + + private static final String IMAGES_DIR = "texdoclet_images"; + + /** + * Buffer containing the translated HTML. + */ + StringBuffer ret; + + Stack tblstk = new Stack(); + TableInfo tblinfo; + int verbat = 0; + int colIdx = 0; + Hashtable colors = new Hashtable(10); + String block = ""; + String refurl = null; + // S.M. modification : doNotPrintURL changed to doPrintURL + String doPrintURL = null; + String refname = null; + String refimg = null; + boolean notex = false; + int imageindex = 0; + + /** + * Constructs a new instance. + * + * @param StringBuffer + * The <CODE>StringBuffer</CODE> where the translated HTML is + * appended. + */ + public HTMLtoLaTeXBackEnd(StringBuffer ret) { + this.ret = ret; + } + + /** + * This method handles simple HTML tags (e.g. <CODE><HR></CODE>-tags). + * It is called by the parser whenever such a tag is encountered. + */ + @Override + public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attrSet, + int pos) { + String str = null; + int i = 0; + if (tag.toString().equalsIgnoreCase("tex")) { + if (attrSet.containsAttribute(HTML.Attribute.ENDTAG, "true")) { + notex = false; + } else { + String tex = (String) attrSet.getAttribute("txt"); + ret.append(tex); + notex = true; + } + } else if (notex) { + return; + } else if (tag == HTML.Tag.META) { + } else if (tag == HTML.Tag.HR) { + String sz = (String) attrSet.getAttribute(HTML.Attribute.SIZE); + int size = 1; + if (sz != null) { + size = Integer.parseInt(sz); + } + ret.append("\\mbox{}\\newline\\rule[2mm]{\\hsize}{" + + (1 * size * .5) + "mm}\\newline\n"); + } else if (tag == HTML.Tag.BR) { + ret.append("\\mbox{}\\newline "); + } else if (tag == HTML.Tag.IMG) { + String refimg = (String) attrSet.getAttribute(HTML.Attribute.SRC); + + // (S.M. modification) include images not in java source as link + if (refimg.indexOf("://") != -1) { + + // if (refimg.indexOf("http://") == 0) { + // make link + ret.append("(see image at " + + fixText("<a href=\"" + refimg + "\">" + refimg + + "</a>") + ")"); + // } else { + // skip it + // } + + } else { + + new File(IMAGES_DIR).mkdir(); + + double scale = 1.0; + File imgF = new File(TeXDoclet.packageDir, refimg); + if (!imgF.exists()) { + ret.append("(image file not found)"); + return; + } + + String imgfile = new File(TeXDoclet.packageDir, refimg) + .getAbsolutePath(); + + ImageIcon icn = new ImageIcon(imgfile); + + int width = icn.getIconWidth(); + int height = icn.getIconHeight(); + + String sw = (String) attrSet.getAttribute(HTML.Attribute.WIDTH); + String sh = (String) attrSet + .getAttribute(HTML.Attribute.HEIGHT); + + try { + if (sw != null) { + scale = NumberFormat.getPercentInstance().parse(sw) + .doubleValue(); + } else if (sh != null) { + scale = NumberFormat.getPercentInstance().parse(sh) + .doubleValue(); + } + } catch (ParseException er) { + er.printStackTrace(); + } + + Image img = icn.getImage(); + + PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, + true); + try { + pg.grabPixels(); + } catch (InterruptedException e) { + throw new RuntimeException( + "interrupted waiting for pixels!"); + } + int[] pixels = (int[]) pg.getPixels(); + img = Toolkit.getDefaultToolkit().createImage( + new MemoryImageSource(width, height, pixels, 0, width)); + byte[] pngbytes; + PngEncoder png = new PngEncoder(img, true); + + String filnavn = IMAGES_DIR + "/pngimage" + imageindex++ + + ".png"; + try { + FileOutputStream outfile = new FileOutputStream(filnavn); + pngbytes = png.pngEncode(); + if (pngbytes != null) { + outfile.write(pngbytes); + } + outfile.flush(); + outfile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + if (width * scale <= 800) { + width *= scale * 0.5; + height *= scale * 0.5; + } else { + scale = width * scale / 800; + width *= 1.0 / scale * 0.5; + height *= 1.0 / scale * 0.5; + } + + String fs = System.getProperty("file.separator"); + String filnavnFinal = (TeXDoclet.imagesPath == null ? "" + : TeXDoclet.imagesPath + + (TeXDoclet.imagesPath.endsWith(fs) ? "" : fs)) + + filnavn; + + ret.append("\\mbox{\\includegraphics[width=" + width + + "pt, height=" + height + "pt]{" + filnavnFinal + "}}"); + + } + + } + } + + /** + * This method handles HTML tags that mark a beginning (e.g. + * <CODE><P></CODE>-tags). It is called by the parser whenever such a + * tag is encountered. + */ + @Override + public void handleStartTag(HTML.Tag tag, MutableAttributeSet attrSet, + int pos) { + String str = null; + int i = 0; + if (notex) { + return; + } else if (tag == HTML.Tag.PRE) { + ret.append(TeXDoclet.TRUETYPE + "\\small\n\\mbox{}\\newline "); + verbat++; + } else if (tag == HTML.Tag.H1) { + ret.append("\\chapter*{"); + } else if (tag == HTML.Tag.H2) { + ret.append("\\section*{"); + } else if (tag == HTML.Tag.H3) { + ret.append("\\subsection*{"); + } else if (tag == HTML.Tag.H4) { + ret.append("\\subsubsection*{"); + } else if (tag == HTML.Tag.H5) { + ret.append("\\subsubsection*{"); + } else if (tag == HTML.Tag.H6) { + ret.append("\\subsubsection*{"); + } else if (tag == HTML.Tag.SUB) { + ret.append("$_{"); + } else if (tag == HTML.Tag.SUP) { + ret.append("$^{"); + // } else if (tag == HTML.Tag.HTML) { + } else if (tag == HTML.Tag.HEAD) { + } else if (tag == HTML.Tag.CENTER) { + ret.append("\\makebox[\\hsize]{ "); + } else if (tag == HTML.Tag.TITLE) { + ret.append("\\chapter{"); + } else if (tag == HTML.Tag.FORM) { + } else if (tag == HTML.Tag.INPUT) { + } else if (tag == HTML.Tag.BODY) { + } else if (tag == HTML.Tag.CODE) { + ret.append(TeXDoclet.TRUETYPE + "\\small "); + } else if (tag == HTML.Tag.TT) { + ret.append(TeXDoclet.TRUETYPE + " "); + } else if (tag == HTML.Tag.P) { + ret.append("\n\n"); + } else if (tag == HTML.Tag.B) { + ret.append("{\\bf "); + } else if (tag == HTML.Tag.STRONG) { + ret.append("{\\bf "); + } else if (tag == HTML.Tag.A) { + refurl = (String) attrSet.getAttribute(HTML.Attribute.HREF); + // (S.M. modification) + doPrintURL = (String) attrSet.getAttribute("doprinturl"); + if (refurl != null) { + if (TeXDoclet.hyperref) { + // if (refurl.toLowerCase().startsWith("doc-files")) { + // File file = new File(TeXDoclet.packageDir, refurl); + // if (file.exists()) { + // if (TeXDoclet.appendencies.contains(file.getPath())) { + // refurl = (String) TeXDoclet.appendencies + // .get(file.getPath()); + // } else { + // refurl = "appendix" + // + new Integer( + // TeXDoclet.appendencies.size() + 1); + // TeXDoclet.appendencies.put(file.getPath(), + // refurl); + // } + // ret.append("\\hyperref{}{" + refurl + "}{}{"); + // return; + // } + // } + + String sharp = ""; + if (refurl.indexOf("#") >= 0) { + sharp = refurl.substring(refurl.indexOf("#") + 1, + refurl.length()); + if (sharp.indexOf("%") >= 0) { + sharp = ""; // Don't know what to do with '%' + } + refurl = refurl.substring(0, refurl.indexOf("#")); + } + ret.append("\\hyperref{" + refurl + "}{" + sharp + "}{}{"); + // ret.append("\\href{" + refurl + "}{"); + } else { + ret.append("{\\bf "); + } + } else { + refname = (String) attrSet.getAttribute(HTML.Attribute.NAME); + if (refname != null && TeXDoclet.hyperref) { + ret.append("\\hyperdef{" + refname + "}{"); + } + } + + } else if (tag == HTML.Tag.OL) { + ret.append("\n\\begin{enumerate}"); + } else if (tag == HTML.Tag.DL) { + ret.append("\n\\begin{itemize}"); + } else if (tag == HTML.Tag.LI) { + ret.append("\n\\item{\\vskip -.8ex "); + } else if (tag == HTML.Tag.DT) { + ret.append("\\item["); + } else if (tag == HTML.Tag.DD) { + ret.append("{"); + } else if (tag == HTML.Tag.UL) { + ret.append("\\begin{itemize}"); + } else if (tag == HTML.Tag.I) { + ret.append(TeXDoclet.ITALIC + " "); + } else if (tag == HTML.Tag.TABLE) { + tblstk.push(tblinfo); + tblinfo = new TableInfo(); + ret = tblinfo.startTable(ret, attrSet); + } else if (tag == HTML.Tag.TH) { + tblinfo.startHeadCol(attrSet); + } else if (tag == HTML.Tag.TD) { + tblinfo.startCol(attrSet); + } else if (tag == HTML.Tag.TR) { + tblinfo.startRow(attrSet); + } else if (tag == HTML.Tag.FONT) { + String sz = (String) attrSet.getAttribute(HTML.Attribute.SIZE); + String col = (String) attrSet.getAttribute(HTML.Attribute.COLOR); + ret.append("{"); + if (col != null) { + if ("redgreenbluewhiteyellowblackcyanmagenta".indexOf(col) != -1) { + ret.append("\\color{" + col + "}"); + } else { + if ("abcdefABCDEF0123456789".indexOf(col.charAt(0)) != -1) { + Color cc = new Color((int) Long.parseLong(col, 16)); + String name = (String) colors + .get("color" + cc.getRGB()); + if (name == null) { + ret.append("\\definecolor{color" + colIdx + + "}[rgb]{" + (cc.getRed() / 255.0) + "," + + (cc.getBlue() / 255.0) + "," + + (cc.getGreen() / 255.0) + "}"); + name = "color" + colIdx; + colIdx++; + colors.put("color" + cc.getRGB(), name); + } + ret.append("\\color{" + name + "}"); + ++colIdx; + } + } + } + } + } + + /** + * This method handles HTML tags that mark an ending (e.g. + * <CODE></P></CODE>-tags). It is called by the parser whenever such a + * tag is encountered. + */ + @Override + public void handleEndTag(HTML.Tag tag, int pos) { + int i = 0; + + if (notex) { + return; + } else if (tag == HTML.Tag.PRE) { + verbat--; + ret.append("}\n"); + } else if (tag == HTML.Tag.H1) { + ret.append("}"); + } else if (tag == HTML.Tag.H2) { + ret.append("}"); + } else if (tag == HTML.Tag.H3) { + ret.append("}"); + } else if (tag == HTML.Tag.H4) { + ret.append("}"); + } else if (tag == HTML.Tag.H5) { + ret.append("}"); + } else if (tag == HTML.Tag.H6) { + ret.append("}"); + } else if (tag == HTML.Tag.SUB) { + ret.append("}$"); + } else if (tag == HTML.Tag.SUP) { + ret.append("}$"); + // } else if (tag == HTML.Tag.HTML) { + } else if (tag == HTML.Tag.HEAD) { + } else if (tag == HTML.Tag.CENTER) { + ret.append("}"); + } else if (tag == HTML.Tag.TITLE) { + ret.append("}{"); + } else if (tag == HTML.Tag.FORM) { + } else if (tag == HTML.Tag.INPUT) { + } else if (tag == HTML.Tag.BODY) { + } else if (tag == HTML.Tag.CODE) { + ret.append("}"); + } else if (tag == HTML.Tag.TT) { + ret.append("}"); + } else if (tag == HTML.Tag.P) { + } else if (tag == HTML.Tag.B) { + ret.append("}"); + } else if (tag == HTML.Tag.STRONG) { + ret.append("}"); + } else if (tag == HTML.Tag.A) { + if (refurl != null) { + ret.append("}"); + // S.M. modification : doPrintURL must be set instead of + // doNotPrintUrl + if (doPrintURL != null) { + if (!refurl.equals("")) { + ret.append("(at "); + ret.append(fixText(refurl)); + ret.append(")"); + } + } + } else if (refname != null) { + ret.append("}"); + } + + } else if (tag == HTML.Tag.LI) { + ret.append("}"); + } else if (tag == HTML.Tag.DT) { + ret.append("]"); + } else if (tag == HTML.Tag.DD) { + ret.append("}"); + } else if (tag == HTML.Tag.DL) {// / + ret.append("\n\\end{itemize}\n"); + } else if (tag == HTML.Tag.OL) { + ret.append("\n\\end{enumerate}\n"); + } else if (tag == HTML.Tag.UL) { + ret.append("\n\\end{itemize}\n"); + } else if (tag == HTML.Tag.I) { + ret.append("}"); + } else if (tag == HTML.Tag.TABLE) { + ret = tblinfo.endTable(); + tblinfo = (TableInfo) tblstk.pop(); + } else if (tag == HTML.Tag.TH) { + tblinfo.endCol(); + } else if (tag == HTML.Tag.TD) { + tblinfo.endCol(); + } else if (tag == HTML.Tag.TR) { + tblinfo.endRow(); + } else if (tag == HTML.Tag.FONT) { + ret.append("}"); + } + } + + /** + * This method handles all other text. + */ + @Override + public void handleText(char[] data, int pos) { + String str = new String(data); + for (int i = 0; i < str.length(); ++i) { + int c = str.charAt(i); + if (notex) { + continue; + } + switch (c) { + case 160: //   + ret.append("\\phantom{ }"); + break; + case ' ': + if (verbat > 0) { + ret.append("\\phantom{ }"); + } else { + ret.append(' '); + } + break; + case '[': + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\lbrack\\ "); + i++; + } else { + ret.append("\\lbrack "); + } + break; + case ']': + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\rbrack\\ "); + i++; + } else { + ret.append("\\rbrack "); + } + break; + case '_': + case '%': + case '$': + case '#': + case '}': + case '{': + case '&': + ret.append('\\'); + ret.append((char) c); + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\ "); + i++; + } + break; + // case 'æ': + case '¾': + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\ae\\ "); + i++; + } else { + ret.append("\\ae "); + } + break; + // case 'Æ': + case '®': + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\AE\\ "); + i++; + } else { + ret.append("\\AE "); + } + break; + // case 'å': + case 'Œ': + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\aa\\ "); + i++; + } else { + ret.append("\\aa "); + } + break; + // case 'Å': + case '': + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\AA\\ "); + i++; + } else { + ret.append("\\AA "); + } + break; + // case 'ø': + case '¿': + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\o\\ "); + i++; + } else { + ret.append("\\o "); + } + break; + // case 'Ø': + case '¯': + if (i < str.length() - 1 && str.charAt(i + 1) == ' ') { + ret.append("\\O\\ "); + i++; + } else { + ret.append("\\O "); + } + break; + case '^': + ret.append("$\\wedge$"); + break; + case '<': + ret.append("\\textless "); + break; + case '\r': + case '\n': + if (tblstk.size() > 0) { + // Swallow new lines while tables are in progress, + // <tr> controls new line emission. + if (verbat > 0) { + ret.append("}\\mbox{}\\newline\n" + TeXDoclet.TRUETYPE + + "\\small "); + } else { + ret.append(" "); + } + } else { + if (verbat > 0) { + ret.append("}\\mbox{}\\newline\n" + TeXDoclet.TRUETYPE + + "\\small "); + } else if ((i + 1) < str.length() + && str.charAt(i + 1) == 10) { + ret.append("\\bl "); + ++i; + } else { + ret.append((char) c); + } + } + break; + case '/': + ret.append("/"); + break; + case '>': + ret.append("\\textgreater "); + break; + case '\\': + ret.append("\\textbackslash "); + break; + default: + ret.append((char) c); + break; + } + } + } + + /** + * Converts a HTML string into <TEX txt="\LaTeX{}">LaTeX</TEX> using an + * instance of <CODE>HTMLtoLaTeXBackEnd</CODE>. + * + * @returns The converted string. + */ + public static String fixText(String str) { + // System.out.println("fixText: " + str); + StringBuffer result = new StringBuffer(str.length()); + HTMLtoLaTeXBackEnd b = new HTMLtoLaTeXBackEnd(result); + Reader reader = new StringReader(str); + try { + new ParserDelegator().parse(reader, b, false); + } catch (IOException e) { + } + return new String(result); + } + +} diff --git a/src/main/java/org/stfm/texdoclet/HelpOutput.java b/src/main/java/org/stfm/texdoclet/HelpOutput.java new file mode 100644 index 0000000..50dd24f --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/HelpOutput.java @@ -0,0 +1,84 @@ +package org.stfm.texdoclet; + +public class HelpOutput { + + protected static void printHelp() { + System.err.println("TeXDoclet Usage:"); + System.err + .println("-title <title> A title to use for the generated output document."); + // (S.M. modification) + System.err + .println("-subtitle <title> A subtitle for the output document."); + System.err + .println(" No -title will result in no title page."); + System.err + .println("-output <outfile> Specifies the output file to write to. If none"); + System.err + .println(" specified, the default is docs.tex in the current"); + System.err.println(" directory."); + System.err + .println("-docclass <class> LaTeX2e document class, `report' is the default."); + System.err + .println("-doctype <type> LaTeX2e document style, `headings' is the default."); + System.err + .println("-classfilter <name> The name of a class implementing the ClassFilter interface."); + System.err + .println("-date <date string> The value to use for the document date."); + System.err + .println("-author <author> Specifies string to use for document Author."); + System.err + .println("-texinit <file> LaTeX2e statements included before \\begin{document}."); + System.err + .println("-texsetup <file> LaTeX2e statements included after \\begin{document} \\maketitle (if title was specified)."); + System.err + .println("-texintro <file> LaTeX2e statements included after table of contents"); + System.err + .println("-texfinish <file> LaTeX2e statements included before \\end{document}."); + System.err + .println("-texpackage <file> LaTeX2e statements included before packages' \\chapter."); + System.err + .println("-setup <file> A setup file included before \\begin{document}."); + System.err.println("-twosided Print twosided."); + System.err + .println("-serial Do print Serializable information."); + System.err + .println("-nosummaries Do print summaries of fiels, constructors and methods."); + // (S.M. modification) + System.err + .println("-nofieldsummary Do not print field summaries"); + // (S.M. modification) + System.err + .println("-noconstructorsummary Do not print constructor summaries"); + System.err + .println("-noinherited Do not include inherited API information in output."); + // (S.M. modification) + System.err + .println("-shortinherited Prints a short inheritance, only the member name (not the whole signature)"); + System.err.println("-noindex Do not create index."); + // (S.M. modification) System.err.println( + // "-notree Do not create a class tree." ); + // no class tree is default + System.err.println("-tree Create a class tree."); + System.err + .println("-treeindent <float> Indent <float>cm i the class tree. Default is 1cm."); + System.err + .println("-hyperref Use the hyperref package."); + System.err + .println("-pdfhyperref Use the hyperref package with pdf. Overrides -hypertex "); + System.err.println("-version Includes version-tags "); + // ----- (S.M. modification) + + System.err + .println("-hr Prints horizontal rows in the output (to get a better? view)"); + System.err + .println("-include Creates output without latex initiation (writes it in initdocsinclude.tex), titlepage, contents "); + System.err + .println("-sectionlevel <level> Specifies the highest level of sections (either \"subsection\", \"section\" or \"chapter\")"); + System.err + .println("-imagespath Path to the texdoclet_images dir (absolute or relative to the output document .tex file)."); + + // ----- (S.M. modification end) + + } + +} diff --git a/src/main/java/org/stfm/texdoclet/InterfaceHierachy.java b/src/main/java/org/stfm/texdoclet/InterfaceHierachy.java new file mode 100755 index 0000000..160eaf2 --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/InterfaceHierachy.java @@ -0,0 +1,80 @@ +package org.stfm.texdoclet; + +import java.util.Iterator; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.sun.javadoc.ClassDoc; +import com.sun.javadoc.RootDoc; + +/** + * Manages and prints a interface hierarchy. Use <CODE>add</CODE> to add another + * interface to the hierarchy. Use <CODE>printTree</CODE> to print the + * corresponding <TEX txt="\LaTeX{}">LaTeX</TEX>. + * + * @version $Revision: 1.1 $ + * @author Soeren Caspersen - XO Software + */ +public class InterfaceHierachy extends java.lang.Object { + + public SortedMap root = new TreeMap(); + + /** + * Creates new InterfaceHierachy + */ + public InterfaceHierachy() { + } + + /** + * Adds another interface to the hierachy + */ + protected SortedMap add(ClassDoc cls) { + SortedMap temp; + if (cls.interfaces().length > 0) { + temp = add(cls.interfaces()[0]); + } else { + temp = root; + } + + SortedMap result = (SortedMap) temp.get(cls.qualifiedName()); + if (result == null) { + result = new TreeMap(); + temp.put(cls.qualifiedName(), result); + } + return result; + } + + /** + * Prints the <TEX txt="\LaTeX{}">LaTeX</TEX> corresponding to the tree. The + * tree is printed using <CODE>TeXDoclet.os</CODE>. + */ + public void printTree(RootDoc rootDoc, double overviewindent) { + printBranch(rootDoc, root, 0, overviewindent); + } + + /** + * Prints a branch of the tree. The branch is printed using + * <CODE>TeXDoclet.os</CODE>. + */ + protected void printBranch(RootDoc rootDoc, SortedMap map, double indent, + double overviewindent) { + Set set = map.keySet(); + Iterator it = set.iterator(); + while (it.hasNext()) { + String qualifName = (String) it.next(); + ClassDoc cls = rootDoc.classNamed(qualifName); + TeXDoclet.os.print("\\hspace{" + Double.toString(indent) + + "cm} $\\bullet$ " + + HTMLtoLaTeXBackEnd.fixText(qualifName) + " {\\tiny "); + // (S.M. modification) only for resolved classes + if (cls != null) { + TeXDoclet.printRef(cls.containingPackage(), cls.name(), ""); + } + TeXDoclet.os.println("} \\\\"); + printBranch(rootDoc, (SortedMap) map.get(qualifName), indent + + overviewindent, overviewindent); + + } + } +} diff --git a/src/main/java/org/stfm/texdoclet/Package.java b/src/main/java/org/stfm/texdoclet/Package.java new file mode 100755 index 0000000..9afba0e --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/Package.java @@ -0,0 +1,98 @@ +package org.stfm.texdoclet; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Vector; + +import com.sun.javadoc.ClassDoc; +import com.sun.javadoc.PackageDoc; + +/** + * This class is used to manage the contents of a Java package. It accepts + * ClassDoc objects and examines them and groups them according to whether they + * are classes, interfaces, exceptions or errors. The accumulated Vectors can + * then be processed to get to all of the elements of the package that fall into + * each category. If needed the classes, interfaces, exceptions and errors can + * be sorted using the <CODE>sort</CODE> method. + * + * @see #sort + * @version $Revision: 1.1 $ + * @author Gregg Wonderly - C2 Technologies Inc. + */ +public class Package { + + protected PackageDoc pkgDoc; + /** The name of the package this object is for */ + protected String pkg; + /** The classes this package has in it */ + protected Vector classes; + /** The interfaces this package has in it */ + protected Vector interfaces; + /** The exceptions this package has in it */ + protected Vector exceptions; + /** The errors this package has in it */ + protected Vector errors; + + /** + * Construct a new object corresponding to the passed package name. + * + * @param pkg + * the package name to use + */ + public Package(String pkg, PackageDoc doc) { + pkgDoc = doc; + this.pkg = pkg; + if (pkg.equals("")) { + this.pkg = "<none>"; + } + classes = new Vector(); + interfaces = new Vector(); + exceptions = new Vector(); + errors = new Vector(); + } + + /** + * Adds a ClassDoc element to this package. + * + * @param cd + * the object to add to this package + */ + public void addElement(ClassDoc cd) { + if (cd.isInterface()) { + interfaces.addElement(cd); + } else if (cd.isClass()) { + if (cd.isException()) { + exceptions.addElement(cd); + } else if (cd.isError()) { + errors.addElement(cd); + } else { + classes.addElement(cd); + } + } + } + + /** + * Sorts the vectors of classes, interfaces exceptions and errors. + */ + public void sort() { + + Comparator comp = new Comparator() { + @Override + public int compare(Object o1, Object o2) { + ClassDoc cls1 = (ClassDoc) o1; + ClassDoc cls2 = (ClassDoc) o2; + return cls1.name().compareToIgnoreCase(cls2.name()); + } + + @Override + public boolean equals(Object obj) { + return false; + } + }; + + Collections.sort(classes, comp); + Collections.sort(interfaces, comp); + Collections.sort(exceptions, comp); + Collections.sort(errors, comp); + } +} \ No newline at end of file diff --git a/src/main/java/org/stfm/texdoclet/TableInfo.java b/src/main/java/org/stfm/texdoclet/TableInfo.java new file mode 100755 index 0000000..15b2cd2 --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/TableInfo.java @@ -0,0 +1,338 @@ +package org.stfm.texdoclet; + +import java.util.Properties; + +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.html.HTML; + +/** + * This class provides support for converting HTML tables into <TEX + * txt="\LaTeX{}">LaTeX</TEX> tables. Some of the things <b>NOT</b> implemented + * include the following: + * <ul> + * <li>valign attributes are not processed, but align= is. + * <li>rowspan attributes are not processed, but colspan= is. + * <li>the argument to border= in the table tag is not used to control line size + * </ul> + * <br> + * Here is an example table. + * <p> + * <table border bgcolor="#AAAAAA"> + * <tr> + * <th>Column 1 Heading + * <th>Column two heading + * <th>Column three heading + * <tr> + * <td>data + * <td colspan=2>Span two columns + * <tr> + * <td><i>more data</i> + * <td align=right>right + * <td align=left>left + * <tr> + * <td colspan=3> + * <table border=5 bgcolor="#CCCCCC"> + * <tr> + * <th colspan=3>A nested table example + * <tr> + * <th>Column 1 Heading</th> + * <th>Coludliadfuapfd a fia fopia foipapio dupoau foapoifd pdpfiu apsd + * oipoioaofiduaopiufopiiiiiiiiiimn two heading</th> + * <th>Column three heading</th> + * <tr> + * <td>data</td> + * <td colspan=2>Span two columns</td> + * <tr> + * <td><i>more data</i></td> + * <td align=right>right</td> + * <td align=left>left</td> + * <tr> + * <td> + * + * <pre> + * 1 + * 2 + * 3 + * 4 + * </pre> + * + * </td> + * <td> + * + * <pre> + * first line + * second line + * third line + * fourth line + * </pre> + * + * </td> + * </table> + * </table> + * + * @version $Revision: 1.2 $ + * @author Gregg Wonderly - C2 Technologies Inc. + */ +public class TableInfo { + + private StringBuffer originalBuffer; + private StringBuffer ret; + + private int colcnt = 0; + private int rowcnt = 0; + private int totalcolcnt = 0; + private boolean border = false; + private Properties props; + private int bordwid; + private boolean parboxed; + private double red = -1.0; + private double blue = -1.0; + private double green = -1.0; + static int tblcnt; + int tblno; + String tc; + + HTML.Tag lastTag; + + int hasNumAttr(HTML.Attribute attr, MutableAttributeSet attrSet) { + String val = (String) attrSet.getAttribute(attr); + if (val == null) { + return -1; + } + try { + return Integer.parseInt(val); + } catch (Exception ex) { + return -1; + } + } + + /** + * Constructs a new table object and starts processing of the table by + * scanning the <code><table></code> passed to count columns. + * + * @param p + * properties found on the <code><table></code> tag + * @param ret + * the result buffer that will contain the output + * @param table + * the input string that has the entire table definition in it. + * @param off + * the offset into <code><table></code> where scanning + * should start + */ + public StringBuffer startTable(StringBuffer org, MutableAttributeSet attrSet) { + originalBuffer = org; + ret = new StringBuffer(); + + tblno = tblcnt++; + tc = "" + (char) ('a' + (tblno / (26 * 26))) + + (char) ((tblno / 26) + 'a') + (char) ((tblno % 26) + 'a'); + + String val = (String) attrSet.getAttribute(HTML.Attribute.BORDER); + border = false; + if (val != null) { + border = true; + bordwid = 2; + if (val.equals("") == false) { + try { + bordwid = Integer.parseInt(val); + } catch (Exception ex) { + } + if (bordwid == 0) { + border = false; + } + } + } + String bgcolor = (String) attrSet.getAttribute(HTML.Attribute.BGCOLOR); + if (bgcolor != null) { + try { + if (bgcolor.length() != 7 && bgcolor.charAt(0) == '#') { + throw new NumberFormatException(); + } + red = Integer.decode("#" + bgcolor.substring(1, 3)) + .doubleValue(); + blue = Integer.decode("#" + bgcolor.substring(3, 5)) + .doubleValue(); + green = Integer.decode("#" + bgcolor.substring(5, 7)) + .doubleValue(); + red /= 255.0; + blue /= 255.0; + green /= 255.0; + } catch (NumberFormatException e) { + red = 1.0; + blue = 1.0; + green = 1.0; + } + } + + return ret; + } + + /** + * Ends the table, closing the last row as needed + * + * @param ret + * The output buffer to put <TEX txt="\LaTeXe{}">LaTeX2e</TEX> + * into. + */ + public StringBuffer endTable() { + originalBuffer.append("\n% Table #" + tblno + "\n"); + int col = totalcolcnt; + if (col == 0) { + col = 1; + } + for (int i = 0; i < col; ++i) { + String cc = "" + (char) ('a' + (i / (26 * 26))) + + (char) ((i / 26) + 'a') + (char) ((i % 26) + 'a'); + originalBuffer.append("\\newlength{\\tbl" + tc + "c" + cc + "w}\n"); + // originalBuffer.append("\\setlength{\\tbl"+tc+"c"+cc+"w}{"+(1.0/col)+"\\hsize}\n"); + originalBuffer.append("\\setlength{\\tbl" + tc + "c" + cc + "w}{" + + (1.0 / col) + "\\linewidth}\n"); + } + if (red != -1.0 && green != -1.0 && blue != -1.0) { + originalBuffer.append("\\colorbox[rgb]{" + Double.toString(red) + + "," + Double.toString(blue) + "," + + Double.toString(green) + "}{"); + } + originalBuffer.append("\\begin{tabular}{"); + if (border) { + originalBuffer.append("|"); + } + for (int i = 0; i < col; ++i) { + String cc = "" + (char) ('a' + (i / (26 * 26))) + + (char) ((i / 26) + 'a') + (char) ((i % 26) + 'a'); + originalBuffer.append("p{\\tbl" + tc + "c" + cc + "w}"); + if (border) { + originalBuffer.append("|"); + } + } + originalBuffer.append("}\n"); + + // Append the cached table + originalBuffer.append(ret); + + originalBuffer.append("\\end{tabular}\n"); + if (red != -1.0 && green != -1.0 && blue != -1.0) { + originalBuffer.append("}\n"); + } + + return originalBuffer; + } + + /** + * Starts a new column, possibly closing the current column if needed + * + * @param ret + * The output buffer to put <TEX txt="\LaTeXe{}">LaTeX2e</TEX> + * into. + * @param p + * the properties from the <code><td></code> tag + */ + public void startCol(MutableAttributeSet attrSet) { + int span = hasNumAttr(HTML.Attribute.COLSPAN, attrSet); + if (colcnt > 0) { + ret.append(" & "); + } + String align = (String) attrSet.getAttribute(HTML.Attribute.ALIGN); + if (align != null && span < 0) { + span = 1; + } + if (span > 0) { + ret.append("\\multicolumn{" + span + "}{"); + if (border && colcnt == 0) { + ret.append("|"); + } + String cc = "" + (char) ('a' + (colcnt / (26 * 26))) + + (char) ((colcnt / 26) + 'a') + + (char) ((colcnt % 26) + 'a'); + if (align != null) { + String h = align.substring(0, 1); + if ("rR".indexOf(h) >= 0) { + ret.append("r"); + } else if ("lL".indexOf(h) >= 0) { + ret.append("p{\\tbl" + tc + "c" + cc + "w}"); + } else if ("cC".indexOf(h) >= 0) { + ret.append("p{\\tbl" + tc + "c" + cc + "w}"); + } + } else { + ret.append("p{\\tbl" + tc + "c" + cc + "w}"); + } + if (border) { + ret.append("|"); + } + ret.append("}"); + } + String wid = (String) attrSet.getAttribute("texwidth"); + ret.append("{"); + if (wid != null) { + ret.append("\\parbox{" + wid + "}{\\vskip 1ex "); + parboxed = true; + } + colcnt++; + totalcolcnt = totalcolcnt > colcnt ? totalcolcnt : colcnt; + } + + /** + * Starts a new Heading column, possibly closing the current column if + * needed. A Heading column has a Bold Face font directive around it. + * + * @param ret + * The output buffer to put <TEX txt="\LaTeXe{}">LaTeX2e</TEX> + * into. + * @param p + * The properties from the <code><th></code> tag + */ + public void startHeadCol(MutableAttributeSet attrSet) { + startCol(attrSet); + ret.append("\\bf "); + } + + /** + * Ends the current column. + * + * @param ret + * The output buffer to put <TEX txt="\LaTeXe{}">LaTeX2e</TEX> + * into. + */ + public void endCol() { + if (parboxed) { + ret.append("\\vskip 1ex}"); + } + parboxed = false; + ret.append("}"); + } + + /** + * Starts a new row, possibly closing the current row if needed + * + * @param ret + * The output buffer to put <TEX txt="\LaTeX{}">LaTeX</TEX> into. + * @param p + * The properties from the <code><tr></code> tag + */ + public void startRow(MutableAttributeSet attrSet) { + if (rowcnt == 0) { + if (border) { + ret.append(" \\hline "); + } + } + colcnt = 0; + ++rowcnt; + } + + /** + * Ends the current row. + * + * @param ret + * The output buffer to put <TEX txt="\LaTeXe{}">LaTeX2e</TEX> + * into. + */ + public void endRow() { + ret.append(" \\\\"); + if (border) { + ret.append(" \\hline"); + } + ret.append("\n"); + } + +} diff --git a/src/main/java/org/stfm/texdoclet/TeXDoclet.java b/src/main/java/org/stfm/texdoclet/TeXDoclet.java new file mode 100755 index 0000000..9486ce5 --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/TeXDoclet.java @@ -0,0 +1,2321 @@ +package org.stfm.texdoclet; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.FilenameFilter; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; +import java.util.Vector; + +import com.sun.javadoc.ClassDoc; +import com.sun.javadoc.DocErrorReporter; +import com.sun.javadoc.Doclet; +import com.sun.javadoc.ExecutableMemberDoc; +import com.sun.javadoc.FieldDoc; +import com.sun.javadoc.MemberDoc; +import com.sun.javadoc.MethodDoc; +import com.sun.javadoc.PackageDoc; +import com.sun.javadoc.ParamTag; +import com.sun.javadoc.Parameter; +import com.sun.javadoc.RootDoc; +import com.sun.javadoc.SeeTag; +import com.sun.javadoc.Tag; +import com.sun.javadoc.ThrowsTag; +import com.sun.javadoc.Type; + +/** + * This class provides a Java <code>javadoc</code> Doclet which generates a <TEX + * txt="\LaTeXe{}">LaTeX2e</TEX> document out of the java classes that it is + * used on. This is convenient for creating printable documentation complete + * with cross reference information. <H3>Supported HTML tags</H3> + * <dl> + * <dt><a> + * <dd>including an additional attribut "doprinturl". Since the output of the + * doclet should be printable, the href attribut of <A> tags is printed in + * parentheses following the link if attribut "doprinturl" is set. Sometimes + * this is undesirable, and omitting "doprinturl" attribut will prevent this. + * <dt><dl> + * <dd>with the associated <dt><dd></dl> tags + * <dt><p> + * <dd>but not align=center...yet + * <dt><br> + * <dd>but not clear=xxx + * <dt><table> + * <dd>including all the associated + * <td><th><tr></td></th></tr> + * <dt><ol> + * <dd>ordered lists + * <dt><ul> + * <dd>unordered lists + * <dt><font> + * <dd>font coloring + * <dt><pre> + * <dd>preformatted text + * <dt><code> + * <dd>fixed point fonts + * <dt><i> + * <dd>italized fonts + * <dt><b> + * <dd>bold fonts + * <dt><sub> + * <dd>subscript + * <dt><sup> + * <dd>superscript + * <dt><center> + * <dd>center + * <dt><img> + * <dd>image located in java sources (<img src="package path/image name">) + * <dl> + * <dt>1. example + * <dd>converted from JPG: <IMG width="100%" src="doc-files/texdoclet.jpg"> + * <dt>2. example + * <dd>converted from GIF: <IMG width="100%" src="doc-files/texdoclet.gif"> + * </dl> + * <dt><img> + * <dd>image located in the www: <IMG + * src="http://upload.wikimedia.org/wikipedia/commons/9/92/LaTeX_logo.svg"> + * </dl> + * + * <H3>Extra tags</H3> A new tag is defined: <CODE><TEX></CODE>. This tag + * is useful for passing <TEX txt="\TeX{}">TeX</TEX> code directly to the <TEX + * txt="\TeX{}">TeX</TEX> compiler. The following code: + * + * <PRE> + * + * <TEX txt="\[ F\left( x \right) = \int_{ - \infty }^x {\frac{1}{{\sqrt {2\pi } + * }}e^{ - \frac{{z^2 }}{2}} dz} \]"> + * <BR><BR><B>This alternative text will appear if the javadoc/HTML is parsed + * by any other doclet/browser</B><BR><BR></TEX> + * </PRE> + * <P> + * will produce the following result: <TEX txt="\[ F\left( x \right) = \int_{ - + * \infty }^x {\frac{1}{{\sqrt {2\pi } }}e^{ - \frac{{z^2 }}{2}} dz} \]"> <BR> + * <BR> + * <B>This alternative text will appear if the javadoc/HTML is parsed by any + * other doclet/browser</B><BR> + * <BR> + * </TEX> The "alternative" text is ignored by the TeXDoclet, but useful if you + * want to use both the TeXDoclet and a regular HTML based doclet. + * + * @see HTMLtoLaTeXBackEnd + * @see #start(RootDoc) start + * @version $Revision: 1.8 $ + * @author Gregg Wonderly - C2 Technologies Inc. + * @author Soeren Caspersen - XO Software. + * @author Stefan Marx + */ +public class TeXDoclet extends Doclet { + + private static final String OUT_FILENAME_DOCS_TEX = "docs.tex"; + private static final String OUT_PREAMBLE_SUFFIX = "_preamble"; + + // (S.M. modification) + public static final String SECTION_LEVEL = "section"; + public static final String CHAPTER_LEVEL = "chapter"; + public static final String SUBSECTION_LEVEL = "subsection"; + + public static final String BOLD = "{\\bf "; + // no bold AND truetype support if using textbf ! + // public static final String BOLD = "\\textbf{"; + // public static final String TRUETYPE = "{\\tt "; + public static final String TRUETYPE = "\\texttt{"; + // public static final String ITALIC = "{\\it "; + public static final String ITALIC = "\\textit{"; + + /** Writer for writing to output file */ + public static PrintWriter os = null; + static boolean inherited = true; + static Hashtable map2; + static Vector map; + static ClassFilter clsFilt; + static RootDoc theroot; + static String docclass = "report"; + static String style = "headings"; + static String title; + static String date; + static String author; + static boolean verbose = false; + static boolean index = true; + static boolean classtree = false; + static boolean serial = false; + static boolean twosided = false; + static double overviewindent = 1.0; + static boolean hyperref = false; + static boolean pdfhyperref = false; + static boolean versioninfo = false; + static boolean summaries = true; + static String outfile = OUT_FILENAME_DOCS_TEX; + static String setupFile = "docsetup.tex"; + static String finishFile = "docfinish.tex"; + static String packageFile = "docpackage.tex"; + static String preambleFile = "docinit.tex"; + static File packageDir = null; + // static Hashtable appendencies = new Hashtable(); + static Hashtable refs = new Hashtable(); + static Hashtable externalrefs = new Hashtable(); + + // ------ (S.M. modification) + static boolean includeTexOutputInOtherTexFile = false; + static String sectionLevelMax = null; + static String[] sectionLevels = new String[3]; + static boolean useFieldSummary = true; + static boolean useConstructorSummary = true; + static boolean useHr = false; + static boolean shortInheritance = false; + /** + * print writer for extra init file + */ + static PrintWriter osPreamble = null; + static String outfilePreamble = null; + static String imagesPath = null; + static String subtitle = null; + static String introFile = null; + + // ------ (S.M. modification end) + + /* + * Testing entry point for testing tables + */ + public static void main(String args[]) { + init(); + os.println("\\documentstyle{book}"); + os.println("\\begin{document}"); + os.println(HTMLtoLaTeXBackEnd.fixText("<table border> " + + "<tr><th>Heading 1<th>Heading 2<th>Heading 3" + + "<tr><td>Column 1<td colspan=2 align=right>2 columns here" + + "<tr><td colspan=2>two here as well<td>just 1" + + "<tr><td align=left>1<td>2<td align=right>3" + "</table>")); + os.println("\\end{document}"); + } + + /** + * Returns how many arguments would be consumed if <code>option</code> is a + * recognized option. + * + * @param option + * the option to check + */ + public static int optionLength(String option) { + if (option.equals("-title")) { + return 2; + } else if (option.equals("-date")) { + return 2; + } else if (option.equals("-docclass")) { + return 2; + } else if (option.equals("-doctype")) { + return 2; + } else if (option.equals("-author")) { + return 2; + } else if (option.equals("-texsetup")) { + return 2; + } else if (option.equals("-texinit")) { + return 2; + } else if (option.equals("-texfinish")) { + return 2; + } else if (option.equals("-texpackage")) { + return 2; + } else if (option.equals("-classfilter")) { + return 2; + } else if (option.equals("-noinherited")) { + return 1; + } else if (option.equals("-serial")) { + return 1; + } else if (option.equals("-nosummaries")) { + return 1; + } else if (option.equals("-noindex")) { + return 1; + } else if (option.equals("-tree")) { + return 1; + } else if (option.equals("-hyperref")) { + return 1; + } else if (option.equals("-twosided")) { + return 1; + } else if (option.equals("-pdfhyperref")) { + return 1; + } else if (option.equals("-version")) { + return 1; + } else if (option.equals("-treeindent")) { + return 2; + } else if (option.equals("-link")) { + return 3; + } else if (option.equals("-nofieldsummary")) { + return 1; + } else if (option.equals("-noconstructorsummary")) { + return 1; + } else if (option.equals("-include")) { + return 1; + } else if (option.equals("-sectionlevel")) { + return 2; + } else if (option.equals("-hr")) { + return 1; + } else if (option.equals("-shortinherited")) { + return 1; + } else if (option.equals("-help")) { + HelpOutput.printHelp(); + return 1; + } else if (option.equals("-output")) { + return 2; + } else if (option.equals("-imagespath")) { + return 2; + } else if (option.equals("-subtitle")) { + return 2; + } else if (option.equals("-texintro")) { + return 2; + } + System.out.println("unknown option " + option); + return Doclet.optionLength(option); + } + + /** + * Checks the passed options and their arguments for validity. + * + * @param args + * the arguments to check + * @param err + * the interface to use for reporting errors + */ + static public boolean validOptions(String[][] args, DocErrorReporter err) { + for (int i = 0; i < args.length; ++i) { + if (args[i][0].equals("-output")) { + outfile = args[i][1]; + } else if (args[i][0].equals("-date")) { + date = args[i][1]; + } else if (args[i][0].equals("-title")) { + title = args[i][1]; + } else if (args[i][0].equals("-author")) { + author = args[i][1]; + } else if (args[i][0].equals("-verbose")) { + verbose = true; + } else if (args[i][0].equals("-docclass")) { + docclass = args[i][1]; + } else if (args[i][0].equals("-classfilter")) { + String fcl = args[i][1]; + try { + clsFilt = (ClassFilter) Class.forName(fcl).newInstance(); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(2); + } + } else if (args[i][0].equals("-doctype")) { + style = args[i][1]; + } else if (args[i][0].equals("-texsetup")) { + setupFile = args[i][1]; + } else if (args[i][0].equals("-texinit")) { + preambleFile = args[i][1]; + } else if (args[i][0].equals("-texfinish")) { + finishFile = args[i][1]; + } else if (args[i][0].equals("-texpackage")) { + packageFile = args[i][1]; + } else if (args[i][0].equals("-noinherited")) { + inherited = false; + } else if (args[i][0].equals("-serial")) { + serial = true; + } else if (args[i][0].equals("-nosummaries")) { + summaries = false; + } else if (args[i][0].equals("-noindex")) { + index = false; + } else if (args[i][0].equals("-twosided")) { + twosided = true; + // (S.M. modification) + // } else if ( args[i][0].equals( "-notree" ) ) { + // overview = false; + } else if (args[i][0].equals("-tree")) { + classtree = true; + } else if (args[i][0].equals("-hyperref")) { + hyperref = true; + } else if (args[i][0].equals("-pdfhyperref")) { + pdfhyperref = true; + hyperref = true; + } else if (args[i][0].equals("-version")) { + versioninfo = true; + } else if (args[i][0].equals("-treeindent")) { + overviewindent = Double.parseDouble(args[i][1]); + } else if (args[i][0].equals("-link")) { + try { + FileInputStream in = new FileInputStream(args[i][2] + + ".map"); + ObjectInputStream p = new ObjectInputStream(in); + Hashtable exref = (Hashtable) p.readObject(); + externalrefs.put(args[i][1], exref); + } catch (Exception e) { + e.printStackTrace(); + } + + // ----------- (S.M. modification) + } else if (args[i][0].equals("-nofieldsummary")) { + useFieldSummary = false; + } else if (args[i][0].equals("-noconstructorsummary")) { + useConstructorSummary = false; + } else if (args[i][0].equals("-include")) { + includeTexOutputInOtherTexFile = true; + } else if (args[i][0].equals("-sectionlevel")) { + sectionLevelMax = args[i][1]; + } else if (args[i][0].equals("-hr")) { + useHr = true; + } else if (args[i][0].equals("-shortinherited")) { + shortInheritance = true; + } else if (args[i][0].equals("-help")) { + // done in optionsLength() + // HelpOutput.printHelp(); + } else if (args[i][0].equals("-imagespath")) { + imagesPath = args[i][1]; + } else if (args[i][0].equals("-subtitle")) { + subtitle = args[i][1]; + } else if (args[i][0].equals("-texintro")) { + introFile = args[i][1]; + } + + if (sectionLevelMax != null + && !sectionLevelMax.equals(CHAPTER_LEVEL) + && !sectionLevelMax.equals(SECTION_LEVEL) + && !sectionLevelMax.equals(SUBSECTION_LEVEL)) { + System.err.println("Invalid option -sectionlevel" + + " (use \"subsection\",\"section\" or \"chapter\")"); + return false; + } + // ----------- (S.M. modification end) + + } + System.out.println("args parsed"); + return true; + } + + public static void init() { + + map2 = new Hashtable(); + map = new Vector(); + try { + os = new PrintWriter(new FileWriter(outfile)); + } catch (Exception ex) { + ex.printStackTrace(); + } + // (S.M. modification) + try { + if (includeTexOutputInOtherTexFile) { + outfilePreamble = outfile + .substring(0, outfile.lastIndexOf(".")) + + OUT_PREAMBLE_SUFFIX + ".tex"; + osPreamble = new PrintWriter(new FileWriter(outfilePreamble)); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + // (S.M. modification) + initSections(); + } + + public static void initSections() { + if (sectionLevelMax == null) { + if (!includeTexOutputInOtherTexFile) { + if (docclass.equals("scrreprt") || docclass.equals("report")) { + sectionLevelMax = CHAPTER_LEVEL; + } else if (docclass.equals("scrartcl") + || docclass.equals("article")) { + sectionLevelMax = SECTION_LEVEL; + } else { + sectionLevelMax = SECTION_LEVEL; + } + } else { + sectionLevelMax = SECTION_LEVEL; + } + } + + if (sectionLevelMax.equals(CHAPTER_LEVEL)) { + sectionLevels[0] = "chapter"; + sectionLevels[1] = "section"; + sectionLevels[2] = "subsection"; + } else if (sectionLevelMax.equals(SECTION_LEVEL)) { + sectionLevels[0] = "section"; + sectionLevels[1] = "subsection"; + sectionLevels[2] = "subsubsection"; + } else if (sectionLevelMax.equals(SUBSECTION_LEVEL)) { + sectionLevels[0] = "subsection"; + sectionLevels[1] = "subsubsection"; + sectionLevels[2] = "subsubsection"; + } + } + + public static void finish() { + if (os != null) { + try { + os.close(); + } catch (Exception ex) { + } + } + // (S.M. modification) + if (osPreamble != null) { + try { + osPreamble.close(); + } catch (Exception ex) { + } + } + try { + FileOutputStream ostream = new FileOutputStream(outfile + ".map"); + ObjectOutputStream p = new ObjectOutputStream(ostream); + p.writeObject(refs); + p.flush(); + ostream.close(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * Called by the framework to format the entire document + * + * @param root + * the root of the starting document + */ + public static boolean start(RootDoc root) { + + theroot = root; + + init(); + + // (S.M. modification) + if (!includeTexOutputInOtherTexFile) { + + printPreamble(os); + + os.println("\\begin{document}"); + + if (title != null) { + os.println("\\maketitle"); + } + + addFile(os, setupFile, false); + + os.println("\\sloppy"); + + os.println("\\addtocontents{toc}{\\protect\\markboth{Contents}{Contents}}"); + os.println("\\tableofcontents"); + + } else { + + printPreamble(osPreamble); + + addFile(osPreamble, setupFile, false); + + } + + addFile(os, introFile, false); + + if (root.inlineTags().length > 0) { + + // (S.M. modification) + os.println("\\" + sectionLevels[0] + "*{Introduction}{"); + os.println(" \\addcontentsline{toc}{" + sectionLevels[0] + + "}{Introduction}"); + + os.println("\\thispagestyle{empty}"); + os.println("\\markboth{Introduction}{Introduction}"); + printTags(root.inlineTags()); + os.println("}"); + + } + + ClassDoc[] cls = root.classes(); + + ExecutableMemberDoc[] mems; + + PackageDoc[] specifiedPackages = root.specifiedPackages(); + System.out.println("specifiedPackages : " + specifiedPackages.length); + for (int i = 0; i < specifiedPackages.length; i++) { + Package P = new Package(specifiedPackages[i].name(), + specifiedPackages[i]); + System.out.println(i + ". " + specifiedPackages[i].name()); + map.add(P); + map2.put(specifiedPackages[i].name(), P); + } + + if (clsFilt != null) { + System.out.println("...Filter Classes with: " + clsFilt); + } + for (int i = 0; i < cls.length; ++i) { + ClassDoc cd = cls[i]; + + if (clsFilt != null && clsFilt.includeClass(cd) == false) { + System.out.println("...Filtering out Class: " + + cd.qualifiedName()); + continue; + } + + Package v; + PackageDoc pkgDoc = cd.containingPackage(); + String pkg = pkgDoc.name(); + if ((v = (Package) map2.get(pkg)) == null) { + v = new Package(pkg, pkgDoc); + map2.put(pkg, v); + map.add(v); + } + v.addElement(cd); + } + + // Sorting + Enumeration h = map.elements(); + while (h.hasMoreElements()) { + final Package pkg = (Package) h.nextElement(); + pkg.sort(); + } + + // Class hierachy + if (classtree) { + + printClassHierarchy(root); + + } + + // os.println("\\part{" + + // HTMLtoLaTeXBackEnd.fixText((String)groupHeader.get(0)) + "}"); + // os.println(HTMLtoLaTeXBackEnd.fixText((String)groupPackages.get(0))); + + // Packages + + // os.println( "\\gdef\\packagename{}" ); + // os.println( "\\gdef\\classname{}" ); + Enumeration e = map.elements(); + while (e.hasMoreElements()) { + final Package pkg = (Package) e.nextElement(); + + // System.out.println("processing Package: " + pkg.pkg); + + // (S.M. modification) + // os.println( "\\newpage" ); + // os.println( + // "\\def\\packagename{"+HTMLtoLaTeXBackEnd.fixText(pkg.pkg)+"}" ); + + addFile(os, packageFile, false); + + // (S.M. modification) + // os.println( + // "\\section"+BOLD+" Package "+HTMLtoLaTeXBackEnd.fixText(pkg.pkg) + // +"}{" ); + os.println("\\" + sectionLevels[0] + "{Package " + + HTMLtoLaTeXBackEnd.fixText(pkg.pkg) + "}{"); + + // (S.M. modification) + // os.println( "\\thispagestyle{empty}" ); + + os.print("\\label{" + refName(makeRefKey(pkg.pkg)) + "}"); + if (hyperref) { + os.println("\\hypertarget{" + refName(makeRefKey(pkg.pkg)) + + "}{}"); + } + + // (S.M. modification) + // os.println( + // "\\markboth{\\protect\\packagename}{\\protect\\packagename}" ); + // os.println( + // "\\markboth{\\protect\\packagename \\hspace{.02in} -- \\protect\\classname}{\\protect\\packagename \\hspace{.02in} -- \\protect\\classname}" + // ); + + // (S.M. modification) + if (ITALIC.indexOf("textit") != -1) { + os.println("\\hskip -.05in"); + } + os.println("\\hbox to \\hsize{" + ITALIC + + " Package Contents\\hfil Page}}"); + if (useHr) { + os.println("\\rule{\\hsize}{.7mm}"); + } + tocForClasses("Interfaces", pkg.interfaces); + tocForClasses("Classes", pkg.classes); + os.println("\\vskip .1in"); + if (useHr) { + os.println("\\rule{\\hsize}{.7mm}"); + } + os.println("\\vskip .1in"); + + // The path relative to which <IMG> will be resolved. + packageDir = findPackageDir(pkg.pkg, root); + + // Package comments + if (pkg.pkgDoc.inlineTags().length > 0) { + printTags(pkg.pkgDoc.inlineTags()); + + // (S.M. modification) + if (useHr) { + os.println("\\mbox{}\\\\ \\rule{\\hsize}{.7mm}"); + } + if (useHr) { + os.println("\\vskip .1in"); + } + } + + // (S.M. modification) + // os.println( "\\newpage"); + // os.println( + // "\\markboth{\\protect\\packagename \\hspace{.02in} -- \\protect\\classname}{\\protect\\packagename \\hspace{.02in} -- \\protect\\classname}" + // ); + + layoutClasses("Interfaces", pkg.interfaces); + layoutClasses("Classes", pkg.classes); + layoutClasses("Exceptions", pkg.exceptions); + layoutClasses("Error", pkg.errors); + + os.println("}"); + } + /* + * if (appendencies.size() > 0) { os.println("\\appendix"); Iterator it + * = appendencies.keySet().iterator(); while (it.hasNext()) { + * //os.println + * (" \\chapter{"+(char)('A'+1)+"}\n\\hyperdef{appendix1}{}sdafafdasd"); + * os.println(" \\hyperdef{appendix1}{}sdafafdasd"); String sfa = + * (String) it.next(); System.out.println(sfa); //addFile(sfa, true); } + * } + */ + + /* + * os.println("\\markboth{}{}"); if (index) { os.println( "\\printindex" + * ); } + */ + + addFile(os, finishFile, false); + + if (!includeTexOutputInOtherTexFile) { + os.println("\\end{document}"); + } + + finish(); + + return true; + } + + static void printPreamble(PrintWriter os) { + + // this is not our job if including in other document + if (!includeTexOutputInOtherTexFile) { + + // (S.M. modification) koma script support (scrreprt, scrartcl) + if (docclass.equals("scrreprt") || docclass.equals("scrartcl")) { + os.println("\\documentclass[11pt,a4paper,titlepage,smallheadings," + + "headinclude,headsepline,DIV13,BCOR12.5mm"); + } else { + os.print("\\documentclass[11pt,a4paper"); + } + + if (twosided) { + os.print(",twoside,openright"); + } + + os.println("]{" + docclass + "}"); + + } + + printPreambleUsePackages(os); + + printPreambleIfPfd(os); + + printPreambleNewCommands(os); + + // os.println("\\definecolor{Light}{gray}{.90}"); + + printPreambleTitle(os); + + // already set in \\usepackage{fullpage} option + // os.println("\\pagestyle{" + style + "}"); + + // (S.M. modification) some stuff uncommented + // os.println("\\addtocontents{toc}{\\protect\\thispagestyle{empty}}"); + // os.println("\\addtocontents{toc}{\\protect\\def\\protect\\packagename{}}"); + // os.println("\\addtocontents{toc}{\\protect\\def\\protect\\classname{}}"); + + // os.println("\\oddsidemargin 0in"); + // os.println("\\evensidemargin 0in"); + // os.println("\\topmargin -.3in"); + os.println("\\chardef\\textbackslash=`\\\\"); + // os.println("\\textheight 9.4in"); + // os.println("\\textwidth 6.5in"); + + if (index) { + os.println("\\makeindex"); + } + + addFile(os, preambleFile, false); + + } + + static void printPreambleUsePackages(PrintWriter os) { + os.println("\\usepackage{color}"); + // (S.M. modification) + // os.println("\\usepackage{graphicx}"); + // os.println("\\usepackage{amsfonts}"); + // os.println("\\usepackage{amsmath}"); + os.println("\\usepackage{ifthen}"); + if (index) { + os.println("\\usepackage{makeidx}"); + } + // (S.M. modification) + os.println("\\usepackage{ifpdf}"); + os.println("\\usepackage[" + style + "]{fullpage}"); + } + + static void printPreambleTitle(PrintWriter os) { + + if (date == null && !includeTexOutputInOtherTexFile) { + os.println("\\date{\\today}"); + } else { + os.println("\\date{" + date + "}"); + } + + if (title != null) { + if (subtitle == null) { + os.println("\\title{" + title + "}"); + } else { + os.println("\\title{" + title + "\\bigskip\\\\ \\Large " + + subtitle + "}"); + } + } + if (author != null) { + os.println("\\author{" + author + "}"); + } + + } + + static void printPreambleIfPfd(PrintWriter os) { + os.println("\\ifpdf \\usepackage[pdftex, pdfpagemode={UseOutlines}," + + "bookmarks,colorlinks,linkcolor={blue},plainpages=false,pdfpagelabels," + + "citecolor={red},breaklinks=true]{hyperref}"); + os.println(" \\usepackage[pdftex]{graphicx}"); + os.println(" \\pdfcompresslevel=9"); + os.println(" \\DeclareGraphicsRule{*}{mps}{*}{}"); + os.println("\\else"); + os.println(" \\usepackage[dvips]{graphicx}"); + os.println("\\fi\n"); + } + + static void printPreambleNewCommands(PrintWriter os) { + os.println("\\newcommand{\\entityintro}[3]{%"); + os.println(" \\hbox to \\hsize{%"); + os.println(" \\vbox{%"); + os.println(" \\hbox to .2in{}%"); + os.println(" }%"); + os.println(" " + BOLD + " #1}%"); + os.println(" \\dotfill\\pageref{#2}%"); + os.println(" }"); + os.println(" \\makebox[\\hsize]{%"); + os.println(" \\parbox{.4in}{}%"); + os.println(" \\parbox[l]{5in}{%"); + // (S.M. modification) no italic for first sentence of class + // description + // os.println(" \\vspace{1mm}\\it%"); + os.println(" \\vspace{1mm}%"); + os.println(" #3%"); + os.println(" \\vspace{1mm}%"); + os.println(" }%"); + os.println(" }%"); + os.println("}"); + + // (S.M. modification) + + // os.println("\\newcommand{\\isep}[0]{%"); + // os.println("\\setlength{\\itemsep}{-.4ex}"); + // os.println("}"); + // + // os.println("\\newcommand{\\sld}[0]{%"); + // os.println("\\setlength{\\topsep}{0em}"); + // os.println("\\setlength{\\partopsep}{0em}"); + // os.println("\\setlength{\\parskip}{0em}"); + // os.println("\\setlength{\\parsep}{-1em}"); + // os.println("}"); + + // os.println("\\newcommand{\\membername}[1]{{\\it #1}\\linebreak}"); + + if (useHr) { + os.println("\\newcommand{\\divideents}[1]{\\vskip -1em\\indent\\rule{2in}{.5mm}}"); + } + + os.println("\\newcommand{\\refdefined}[1]{"); + os.println("\\expandafter\\ifx\\csname r@#1\\endcsname\\relax"); + os.println("\\relax\\else"); + // (S.M. modification) + // os.println("{$($in \\ref{#1}, page \\pageref{#1}$)$}"); + // os.println("\\fi}" ); + os.println("{$($in \\ref{#1}, page \\pageref{#1}$)$}\\fi}"); + } + + /** + * Adds an entire file (HTML or LaTeX). + * + * @param fixText + * Should be true if the file contains HTML. + */ + static boolean addFile(PrintWriter os, String name, boolean fixText) { + + if (name == null) { + return true; + } + + try { + File f = new File(name); + if (f.exists() == false) { + if (verbose) { + System.out.println("Can not open: " + f); + } + return false; + } + BufferedReader rd = new BufferedReader(new FileReader(f)); + try { + StringBuffer buf = new StringBuffer((int) f.length()); + String str; + while ((str = rd.readLine()) != null) { + buf.append(str + "\n"); + } + if (fixText) { + os.println(HTMLtoLaTeXBackEnd.fixText(buf.toString())); + } else { + os.println(buf.toString()); + } + } finally { + rd.close(); + } + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + return true; + } + + /** + * Produces a table-of-contents for classes. + */ + static void tocForClasses(String title, Vector v) { + if (v.size() > 0) { + os.println("\\vskip .13in"); + + // os.println("\\hbox" + BOLD + " " + // + HTMLtoLaTeXBackEnd.fixText(title) + "}"); + + os.println("\\hbox{" + BOLD + " " + + HTMLtoLaTeXBackEnd.fixText(title) + "}}"); + + for (int i = 0; i < v.size(); ++i) { + ClassDoc cd = (ClassDoc) v.elementAt(i); + os.print("\\entityintro{" + + HTMLtoLaTeXBackEnd.fixText(cd.name()) + "}" + "{" + + refName(makeRefKey(cd.qualifiedName())) + "}" + "{"); + printTags(cd.firstSentenceTags()); + os.println("}"); + } + } + } + + /** + * Produces a LaTeX referencekey from a Java identifier. + */ + static String refName(String key) { + return key; + } + + static void printClassHierarchy(RootDoc root) { + + // (S.M. modification) + os.println("\\" + sectionLevels[0] + "*{Class Hierarchy}{"); + + os.println("\\thispagestyle{empty}"); + os.println("\\markboth{Class Hierarchy}{Class Hierarchy}"); + + os.println("\\addcontentsline{toc}{" + sectionLevels[0] + + "}{Class Hierarchy}"); + + // Classes + ClassHierachy classHierachy = new ClassHierachy(); + Enumeration f = map.elements(); + while (f.hasMoreElements()) { + final Package pkg = (Package) f.nextElement(); + for (int i = 0; i < pkg.classes.size(); i++) { + classHierachy.add((ClassDoc) pkg.classes.get(i)); + } + } + if (classHierachy.root.size() != 0) { + + // (S.M. modification) + os.println("\\" + sectionLevels[1] + "*{Classes}"); + + // (S.M. modification) raggedright needed here for correct indent + os.println("{\\raggedright"); + + classHierachy.printTree(root, overviewindent); + + os.println("}"); + } + + // Interfaces + InterfaceHierachy interfaceHierachy = new InterfaceHierachy(); + f = map.elements(); + while (f.hasMoreElements()) { + final Package pkg = (Package) f.nextElement(); + for (int i = 0; i < pkg.interfaces.size(); i++) { + interfaceHierachy.add((ClassDoc) pkg.interfaces.get(i)); + } + } + if (interfaceHierachy.root.size() != 0) { + // (S.M. modification) + // os.println( "\\section*{Interfaces}"); + os.println("\\" + sectionLevels[1] + "*{Interfaces}"); + + interfaceHierachy.printTree(root, overviewindent); + } + + // Exceptions + ClassHierachy exceptionHierachy = new ClassHierachy(); + f = map.elements(); + while (f.hasMoreElements()) { + final Package pkg = (Package) f.nextElement(); + for (int i = 0; i < pkg.exceptions.size(); i++) { + exceptionHierachy.add((ClassDoc) pkg.exceptions.get(i)); + } + } + if (exceptionHierachy.root.size() != 0) { + + // (S.M. modification) + // os.println( "\\section*{Exceptions}"); + os.println("\\" + sectionLevels[1] + "*{Exceptions}"); + + exceptionHierachy.printTree(root, overviewindent); + } + + // Errors + ClassHierachy errorHierachy = new ClassHierachy(); + f = map.elements(); + while (f.hasMoreElements()) { + final Package pkg = (Package) f.nextElement(); + for (int i = 0; i < pkg.errors.size(); i++) { + errorHierachy.add((ClassDoc) pkg.errors.get(i)); + } + } + if (errorHierachy.root.size() != 0) { + + // (S.M. modification) + // os.println( "\\section*{Errors}"); + os.println("\\" + sectionLevels[1] + "*{Errors}"); + + errorHierachy.printTree(root, overviewindent); + } + + os.println("}"); + + } + + /** + * Lays out a list of classes. + * + * @param type + * Title of the section, e.g. "Interfaces", "Exceptions" etc. + * @param classes + * Vector of the classes to be laid out. + */ + static void layoutClasses(String type, Vector classes) { + // (S.M. modification) + // if ( classes.size() > 0 ) { + // os.print("\\section{" ); + // os.print( type ); + // os.println( "}{" ); + // } + for (int i = 0; i < classes.size(); ++i) { + ClassDoc cd = (ClassDoc) classes.elementAt(i); + // os.println( + // "\\gdef\\classname{"+HTMLtoLaTeXBackEnd.fixText(cd.name())+"}" ); + + // (S.M. modification) + os.print("\\" + sectionLevels[1] + "{"); + + String mtype = "Class"; + if (type.equals("Interfaces")) { + mtype = "Interface"; + } + if (type.equals("Exceptions")) { + mtype = "Exception"; + } + os.print("\\label{" + refName(makeRefKey(cd.qualifiedName())) + "}"); + if (index) { + if (cd.isInterface()) { + os.print("\\index{" + HTMLtoLaTeXBackEnd.fixText(cd.name()) + + "@" + ITALIC + " " + + HTMLtoLaTeXBackEnd.fixText(cd.name()) + "}}"); + } else { + os.print("\\index{" + HTMLtoLaTeXBackEnd.fixText(cd.name()) + + "}"); + } + } + // os.println( + // ""+BOLD+" {\\it "+mtype+"} "+HTMLtoLaTeXBackEnd.fixText(cd.name())+"}}{"); + os.println(mtype + " " + HTMLtoLaTeXBackEnd.fixText(cd.name()) + + "}{"); + if (useHr) { + os.println("\\rule[1em]{\\hsize}{4pt}\\vskip -1em"); + } + if (hyperref) { + os.print("\\hypertarget{" + + refName(makeRefKey(cd.qualifiedName())) + "}{}"); + } + + os.println("\\vskip .1in "); + if (cd.inlineTags().length > 0) { + printTags(cd.inlineTags()); + os.println("\\vskip .1in "); + } + + SeeTag[] sees = cd.seeTags(); + if (sees.length > 0) { + os.println("\\" + sectionLevels[2] + "{See also}{}\n"); + os.println(" \\begin{list}{-- }{\\setlength{\\itemsep}{0cm}\\setlength{\\parsep}{0cm}}"); + for (int j = 0; j < sees.length; ++j) { + os.print("\\item{ "); + printSeesTag(sees[j], cd.containingPackage()); + os.println("} "); + } + os.println(" \\end{list}"); + } + + // os.println( "\\"+sectionLevels[2]+"{Declaration}{" ); + // os.println( + // "\\fcolorbox{black}{Light}{\\parbox[s]{.98\\hsize}{"); + // os.print( + // "\\small "+HTMLtoLaTeXBackEnd.fixText(cd.modifiers())+" " ); + os.println("\\" + sectionLevels[2] + "{Declaration}{"); + // os.println( + // "\\fcolorbox{black}{Light}{\\parbox[s]{.98\\hsize}{"); + os.print("\\small " + HTMLtoLaTeXBackEnd.fixText(cd.modifiers()) + + " "); + if (cd.isInterface() == false) { + os.print("class "); + } + os.println(HTMLtoLaTeXBackEnd.fixText(cd.name())); + ClassDoc sc = cd.superclass(); + if (sc != null) { + os.println("\\\\ " + BOLD + " extends} " + + HTMLtoLaTeXBackEnd.fixText(sc.qualifiedName())); + printRef(sc.containingPackage(), sc.name(), null); + } + + ClassDoc intf[] = cd.interfaces(); + if (intf.length > 0) { + // os.println( "\\\\ "+BOLD+" implements} " ); + // ----- (S.M. modification) Bug fixed ! + // interfaces never implement something ! + if (cd.isInterface() == false) { + os.println("\\\\ " + BOLD + " implements} "); + } else { + os.println("\\\\ " + BOLD + " extends} "); + } + // ----- (S.M. modification end) + for (int j = 0; j < intf.length; ++j) { + ClassDoc in = intf[j]; + String nm; + if (in.containingPackage().name() + .equals(cd.containingPackage().name())) { + nm = in.name(); + } else { + nm = in.qualifiedName(); + } + if (j > 0) { + os.print(", "); + } + os.print(HTMLtoLaTeXBackEnd.fixText(nm)); + } + } + // os.println("}}}"); + os.println("}"); + ExecutableMemberDoc[] mems; + FieldDoc[] flds; + + Tag[] verTags = cd.tags("version"); + if (versioninfo && verTags.length > 0) { + os.println("\\" + sectionLevels[2] + "{Version}{" + + HTMLtoLaTeXBackEnd.fixText(verTags[0].text()) + "}"); + } + + String subclasses = ""; + for (int index = 0; index < theroot.classes().length; index++) { + ClassDoc cls = theroot.classes()[index]; + if (cls.subclassOf(cd) && !cls.equals(cd)) { + if (!subclasses.equals("")) { + subclasses += ", "; + } + subclasses += HTMLtoLaTeXBackEnd.fixText(cls.name()); + subclasses += "\\small{\\refdefined{" + + refName(makeRefKey(cls.qualifiedName())) + "}}"; + } + } + + // if (!subclasses.equals("")) { + // os.println("\\"+sectionLevels[2]+"{All known subclasses}{" + + // subclasses + + // "}"); + // } + // ----- (S.M. modification) Bug fixed ! + // interfaces never have subclasses, they have subinterfaces ! + if (cd.isInterface()) { + if (!subclasses.equals("")) { + os.println("\\" + sectionLevels[2] + + "{All known subinterfaces}{" + subclasses + "}"); + } + } else { + if (!subclasses.equals("")) { + os.println("\\" + sectionLevels[2] + + "{All known subclasses}{" + subclasses + "}"); + } + } + // ----- (S.M. modification end) + + String subintf = ""; + String implclasses = ""; + if (cd.isInterface()) { + for (int index = 0; index < theroot.classes().length; index++) { + ClassDoc cls = theroot.classes()[index]; + boolean impls = false; + for (int w = 0; w < cls.interfaces().length; w++) { + ClassDoc intfDoc = cls.interfaces()[w]; + if (intfDoc.equals(cd)) { + impls = true; + } + } + if (impls) { + if (cls.isInterface()) { + if (!subintf.equals("")) { + subintf += ", "; + } + subintf += cls.name(); + subintf += "\\small{\\refdefined{" + + refName(makeRefKey(cls.qualifiedName())) + + "}}"; + } else { + if (!implclasses.equals("")) { + implclasses += ", "; + } + implclasses += cls.name(); + implclasses += "\\small{\\refdefined{" + + refName(makeRefKey(cls.qualifiedName())) + + "}}"; + } + } + } + + // (S.M. modification) + // Because of the correction above, the subinterfaces are + // already printed ! + // if (!subintf.equals("")) + // os.println("\\"+sectionLevels[2]+"{All known subinterfaces}{" + // + + // subintf + "}"); + + if (!implclasses.equals("")) { + os.println("\\" + sectionLevels[2] + + "{All classes known to implement interface}{" + + implclasses + "}"); + } + } + + if (summaries) { + flds = cd.fields(); + if (flds.length > 0) { + printFieldSummary(flds, "Field summary"); + } + + if (useConstructorSummary) { + mems = cd.constructors(); + if (mems.length > 0) { + printMethodSummary(mems, "Constructor summary"); + } + } + + if (useFieldSummary) { + mems = cd.methods(); + if (mems.length > 0) { + printMethodSummary(mems, "Method summary"); + } + } + } + + flds = cd.serializableFields(); + if (flds.length > 0 && serial) { + printFields(cd, flds, "Serializable Fields", false); + } + flds = cd.fields(); + if (flds.length > 0) { + printFields(cd, flds, "Fields", true); + } + mems = cd.constructors(); + if (mems.length > 0) { + os.println("\\" + sectionLevels[2] + "{Constructors}{"); + printMembers(cd, mems, true); + os.println("}"); + } + mems = cd.methods(); + if (mems.length > 0) { + os.println("\\" + sectionLevels[2] + "{Methods}{"); + printMembers(cd, mems, true); + os.println("}"); + } + // (S.M. modification) + // + // if (inherited == false) { + // os.println( "\\hide{inherited}{" ); + // } + if (inherited == true) { + + boolean yet = false; + + // (S.M. modification) cd.superclass() will always return null + // for interfaces + // ??! + // so we handle it specially + if (!cd.isInterface()) { + + ClassDoc par = cd.superclass(); + + while (par != null + && par.qualifiedName().equals("java.lang.Object") == false) { + + printInherited(par); + + // boolean members = false; + // + // MemberDoc[] inheritedmembers = new MemberDoc[par + // .fields().length + par.methods().length]; + // for (int k = 0; k < par.fields().length; k++) { + // inheritedmembers[k] = par.fields()[k]; + // members = true; + // } + // for (int k = 0; k < par.methods().length; k++) { + // inheritedmembers[k + par.fields().length] = par + // .methods()[k]; + // members = true; + // } + // + // // (S.M. modification) print only if members + // available ( + // // for instance in case of a not correct set + // classpath + // // members are missed) + // if (members) { + // os.print("\\" + sectionLevels[2] + "{"); + // // (S.M. modification) do not user true type and do + // // not print full name here + // // in the section heading + // // this produces an ugly view in the Contents ... + // // + // os.print("Members inherited from class "+TRUETYPE+"" + // os.print("Members inherited from class {" + // + HTMLtoLaTeXBackEnd.fixText( + // // par.qualifiedName() + // par.name()) + // + "} \\textnormal{\\small"); + // printRef(par.containingPackage(), par.name(), null); + // os.println("}}{"); + // os.println("{\\small "); + // // (S.M. modification) + // // ... but print here the full name including the + // // package + // os.println("(Fully qualified name: " + // + par.qualifiedName() + ")"); + // + // printinheritedMembers(par, inheritedmembers, false); + // + // os.println("}}"); + // } + par = par.superclass(); + } + + } else { + + List superclasses = new Vector(); + while (getSuperClass(cd, superclasses) != null) { + } + + superclasses = sortSuperclasses(superclasses); + + for (int m = superclasses.size() - 1; m >= 0; m--) { + + ClassDoc par = (ClassDoc) superclasses.get(m); + + printInherited(par); + + // boolean members = false; + // + // MemberDoc[] inheritedmembers = new MemberDoc[par + // .fields().length + par.methods().length]; + // for (int k = 0; k < par.fields().length; k++) { + // inheritedmembers[k] = par.fields()[k]; + // members = true; + // } + // for (int k = 0; k < par.methods().length; k++) { + // inheritedmembers[k + par.fields().length] = par + // .methods()[k]; + // members = true; + // } + // + // // (S.M. modification) print only if members + // available ( + // // if class not found because classpath not correctly + // // set, they would be missed) + // if (members) { + // os.print("\\" + sectionLevels[2] + "{"); + // // + // os.println("Members inherited from class "+TRUETYPE+""+ + // // + // HTMLtoLaTeXBackEnd.fixText(par.qualifiedName())+"}}{"); + // // os.println("\\par{\\small " ); + // // printRef( par.containingPackage(), par.name(), + // // null ); + // os.print("Members inherited from class "+TRUETYPE+"" + // + HTMLtoLaTeXBackEnd.fixText(par + // .qualifiedName()) + // + "} \\textnormal{\\small"); + // printRef(par.containingPackage(), par.name(), null); + // os.println("}}{"); + // os.println("{\\small "); + // + // printinheritedMembers(par, inheritedmembers, false); + // + // os.println("}}"); + // } + } + + } + + } + // (S.M. modification) + // + // if ( inherited == false ) { + // os.println("}"); + // } + + os.println("}"); + // (S.M. modification) + // os.print("\\newpage" ); + } + // (S.M. modification) + // if ( classes.size() > 0 ) { + // os.println("}"); + // } + } + + /** + * searches in all classes of the root doc for a superclass of the given + * subsclass, that is not already in the list of superclasses adn adds it to + * the list if found + */ + static ClassDoc getSuperClass(ClassDoc subclass, List superclasses) { + ClassDoc[] cls = theroot.classes(); + for (int n = 0; n < cls.length; ++n) { + ClassDoc cd2 = cls[n]; + if (subclass.subclassOf(cd2)) { + if (subclass != cd2 && !superclasses.contains(cd2)) { + superclasses.add(cd2); + return cd2; + } + } + } + return null; + } + + static List sortSuperclasses(List superclasses) { + List result = new Vector(); + int count = superclasses.size(); + + for (int k = 0; k < count; k++) { + for (int i = 0; i < superclasses.size(); i++) { + ClassDoc cd = (ClassDoc) superclasses.get(i); + boolean isSubInterface = false; + for (int j = 0; j < superclasses.size(); j++) { + ClassDoc cd2 = (ClassDoc) superclasses.get(j); + if (cd.subclassOf(cd2) && cd != cd2) { + isSubInterface = true; + break; + } + + } + if (!isSubInterface) { + result.add(cd); + superclasses.remove(cd); + break; + } + } + + } + return result; + } + + /** + * Enumerates the fields passed and formats them using Tex statements. + * + * @param flds + * the fields to format + */ + static void printFields(ClassDoc cd, FieldDoc[] flds, String title, + boolean labels) { + boolean yet = false; + for (int i = 0; i < flds.length; ++i) { + FieldDoc f = flds[i]; + if (!yet) { + os.println("\\" + sectionLevels[2] + "{" + title + "}{"); + if (useHr) { + os.println("\\rule[1em]{\\hsize}{2pt}"); + } + os.println("\\begin{itemize}"); + yet = true; + } + os.println("\\item{"); + os.println("\\index{" + HTMLtoLaTeXBackEnd.fixText(f.name()) + "}"); + if (labels) { + os.print("\\label{" + refName(makeRefKey(f.qualifiedName())) + + "}"); + if (hyperref) { + os.print("\\hypertarget{" + + refName(makeRefKey(f.qualifiedName())) + "}{"); + } + } + if (!cd.isInterface()) { + os.print(HTMLtoLaTeXBackEnd.fixText(f.modifiers()) + " "); + } + os.print(HTMLtoLaTeXBackEnd.fixText(packageRelativIdentifier( + f.containingPackage(), f.type().qualifiedTypeName())) + + " "); + os.print("" + BOLD + " " + HTMLtoLaTeXBackEnd.fixText(f.name()) + + "}"); + if (labels && hyperref) { + os.println("}"); + } + + if (f.inlineTags().length > 0 || f.seeTags().length > 0) { + os.println("\\begin{itemize}"); + if (f.inlineTags().length > 0) { + os.println("\\item{\\vskip -.9ex "); + printTags(f.inlineTags()); + os.println("}"); + } + + // See tags + SeeTag[] sees = f.seeTags(); + if (sees.length > 0) { + os.println("\\item{{ See also}"); + os.println(" \\begin{itemize}"); + for (int j = 0; j < sees.length; ++j) { + os.print("\\item{ "); + printSeesTag(sees[j], f.containingPackage()); + os.println("}"); + } + os.println(" \\end{itemize}"); + os.println("}%end item"); + } + + os.println("\\end{itemize}"); + } + os.println("}"); + } + if (yet) { + os.println("\\end{itemize}"); + os.println("}"); + } + } + + /** + * Produces a constructor/method summary. + * + * @param dmems + * The fields to be summarized. + * @param title + * The title of the section. + */ + static void printMethodSummary(ExecutableMemberDoc[] dmems, String title) { + if (dmems.length == 0) { + return; + } + os.println("\\" + sectionLevels[2] + "{" + title + "}{"); + os.println("\\begin{verse}"); + boolean first = true; + List l = Arrays.asList(dmems); + Collections.sort(l); + Iterator itr = l.iterator(); + for (int i = 0; itr.hasNext(); ++i) { + ExecutableMemberDoc mem = (ExecutableMemberDoc) itr.next(); + if (hyperref) { + os.print("\\hyperlink{" + + refName(makeRefKey(mem.qualifiedName() + + mem.signature())) + "}{"); + } + os.print(BOLD + + HTMLtoLaTeXBackEnd.fixText(mem.name() + + mem.flatSignature()) + "}"); + if (hyperref) { + os.print("}"); + } + os.print(" "); + printTags(mem.firstSentenceTags()); + os.println("\\\\"); + } + os.println("\\end{verse}"); + os.println("}"); + } + + /** + * Produces a field summary. + * + * @param dmems + * The fields to be summarized. + * @param title + * The title of the section. + */ + static void printFieldSummary(FieldDoc[] dmems, String title) { + if (dmems.length == 0) { + return; + } + os.println("\\" + sectionLevels[2] + "{" + title + "}{"); + os.println("\\begin{verse}"); + boolean first = true; + List l = Arrays.asList(dmems); + Collections.sort(l); + Iterator itr = l.iterator(); + for (int i = 0; itr.hasNext(); ++i) { + FieldDoc mem = (FieldDoc) itr.next(); + if (hyperref) { + os.print("\\hyperlink{" + + refName(makeRefKey(mem.qualifiedName())) + "}{"); + } + os.print(BOLD + HTMLtoLaTeXBackEnd.fixText(mem.name()) + "}"); + if (hyperref) { + os.print("}"); + } + os.print(" "); + printTags(mem.firstSentenceTags()); + os.println("\\\\"); + } + os.println("\\end{verse}"); + os.println("}"); + } + + /** + * Enumerates the members of a section of the document and formats them + * using Tex statements. + */ + static void printMembers(ClassDoc cd, ExecutableMemberDoc[] dmems, + boolean labels) { + if (dmems.length == 0) { + return; + } + if (useHr) { + os.println("\\rule[1em]{\\hsize}{2pt}\\vskip -2em"); + } + List l = Arrays.asList(dmems); + Collections.sort(l); + Iterator itr = l.iterator(); + os.println("\\vskip -2em"); + os.println("\\begin{itemize}"); + for (int i = 0; itr.hasNext(); ++i) { + ExecutableMemberDoc mem = (ExecutableMemberDoc) itr.next(); + + if (i > 0) { + if (useHr) { + os.println("\\divideents{" + + HTMLtoLaTeXBackEnd.fixText(mem.name()) + "}"); + } + } + + printMember(mem); + } + os.println("\\end{itemize}"); + } + + /** + * Enumerates the members of a section of the document and formats them + * using Tex statements. + */ + static void printMember(ExecutableMemberDoc mem) { + printMember(mem, null); + } + + /** + * Enumerates the members of a section of the document and formats them + * using Tex statements. + */ + static void printMember(ExecutableMemberDoc mem, + ExecutableMemberDoc copiedTo) { + PackageDoc pac = copiedTo == null ? mem.containingPackage() : copiedTo + .containingPackage(); + if (mem instanceof MethodDoc) { + MethodDoc method = (MethodDoc) mem; + if (method.commentText() == "" && method.seeTags().length == 0 + && method.throwsTags().length == 0 + && method.paramTags().length == 0) { + + // No javadoc available for this method. Recurse through + // superclasses + // and implemented interfaces to find javadoc of overridden + // methods. + + boolean found = false; + + ClassDoc doc = method.overriddenClass(); + if (doc != null) { + for (int i = 0; !found && i < doc.methods().length; ++i) { + if (doc.methods()[i].name().equals(mem.name()) + && doc.methods()[i].signature().equals( + mem.signature())) { + printMember(doc.methods()[i], + copiedTo == null ? mem : copiedTo); + found = true; + } + } + } + doc = method.containingClass(); + for (int j = 0; !found && j < doc.interfaces().length; j++) { + ClassDoc inf = doc.interfaces()[j]; + for (int i = 0; !found && i < inf.methods().length; ++i) { + if (inf.methods()[i].name().equals(mem.name()) + && inf.methods()[i].signature().equals( + mem.signature())) { + printMember(inf.methods()[i], + copiedTo == null ? mem : copiedTo); + found = true; + } + } + } + if (found) { + return; + } + } + } + + ParamTag[] params = mem.paramTags(); + + // Some index and hyperref stuff + // os.println("\\item{\\vskip -1.9ex " ); + os.println("\\item{ "); + os.println("\\index{" + + HTMLtoLaTeXBackEnd.fixText(mem.name() + mem.flatSignature()) + + "}"); + + // (S.M. modification) + // If including the output this would result in hundreds of undefull + // boxes ??! + if (hyperref) { + if (copiedTo == null) { + os.print("\\hypertarget{" + + refName(makeRefKey(mem.qualifiedName() + + mem.signature())) + "}{"); + } else { + os.print("\\hypertarget{" + + refName(makeRefKey(copiedTo.qualifiedName() + + copiedTo.signature())) + "}{"); + } + } + // (S.M. modification) + // os.print("\\membername{"+HTMLtoLaTeXBackEnd.fixText(mem.name())+"}"); + os.print(BOLD + " " + HTMLtoLaTeXBackEnd.fixText(mem.name()) + "}\\\\"); + if (hyperref) { + os.print("}"); + } + os.println(); + + // Print signature + os.print("" + TRUETYPE + ""); + if (!mem.containingClass().isInterface()) { + os.print(HTMLtoLaTeXBackEnd.fixText(mem.modifiers())); + } + if (mem instanceof MethodDoc) { + os.print(" " + + HTMLtoLaTeXBackEnd.fixText(packageRelativIdentifier(pac, + ((MethodDoc) mem).returnType().toString()))); + } + os.print("\\ " + BOLD + " " + HTMLtoLaTeXBackEnd.fixText(mem.name()) + + "}("); + Parameter[] parms = mem.parameters(); + int p = 0; + String qparmstr = ""; + String parmstr = ""; + for (; p < parms.length; ++p) { + if (p > 0) { + os.println(","); + } + Type t = parms[p].type(); + os.print(TRUETYPE + + HTMLtoLaTeXBackEnd.fixText(packageRelativIdentifier(pac, + t.qualifiedTypeName()))); + os.print(HTMLtoLaTeXBackEnd.fixText(t.dimension()) + "}"); + if (parms[p].name().equals("") == false) { + os.print(" " + BOLD + " " + + HTMLtoLaTeXBackEnd.fixText(parms[p].name()) + "}"); + } + if (qparmstr.length() != 0) { + qparmstr += ","; + } + qparmstr += t.qualifiedTypeName() + t.dimension(); + if (parmstr.length() != 0) { + parmstr += ","; + } + parmstr += t.typeName() + t.dimension(); + } + os.print(")"); + + // Thrown exceptions + ClassDoc[] thrownExceptions = mem.thrownExceptions(); + if (thrownExceptions != null && thrownExceptions.length > 0) { + os.print(" throws " + thrownExceptions[0].qualifiedName()); + for (int e = 1; e < thrownExceptions.length; e++) { + os.print(", " + thrownExceptions[e].qualifiedName()); + } + } + os.println(); + + if (copiedTo == null) { + os.print("\\label{" + + refName(makeRefKey(mem.qualifiedName() + mem.signature())) + + "}"); + } else { + os.print("\\label{" + + refName(makeRefKey(copiedTo.qualifiedName() + + copiedTo.signature())) + "}"); + } + + os.println("}%end signature"); + boolean yet = false; + + // Description + if (mem.inlineTags().length > 0) { + if (!yet) { + os.println("\\begin{itemize}"); + // os.println("\\sld"); + yet = true; + } + os.println("\\item{"); + // os.println("\\sld"); + if (copiedTo == null) { + // os.println( + // ""+BOLD+" Description}\n \\begin{itemize}\\isep\n \\item{" + // ); + os.println(BOLD + " Description}\n"); + } else { + os.print(BOLD + " Description copied from "); + String classname = mem.containingClass().qualifiedName(); + if (hyperref) { + os.print("\\hyperlink{" + refName(makeRefKey(classname)) + + "}{"); + } + os.print(packageRelativIdentifier(pac, classname)); + if (hyperref) { + os.print("}"); + } + os.print("{\\small "); + os.print("\\refdefined{" + refName(makeRefKey(classname)) + "}"); + // os.println( "} }\n \\begin{itemize}\\isep\n \\item{" ); + os.println("} }\n"); + } + printTags(mem.inlineTags()); + // os.println( "}%end item\n \\end{itemize}\n}" ); + os.println("\n}"); + } + + // Parameter tags + if (params.length > 0) { + if (!yet) { + os.println("\\begin{itemize}"); + // os.println("\\sld"); + yet = true; + } + os.println("\\item{"); + // os.println("\\sld"); + os.println(BOLD + " Parameters}"); + // os.println("\\sld\\isep"); + os.println(" \\begin{itemize}"); + // os.println("\\sld\\isep"); + for (int j = 0; j < params.length; ++j) { + os.println(" \\item{"); + // os.println("\\sld"); + os.print(TRUETYPE + + HTMLtoLaTeXBackEnd.fixText(params[j].parameterName()) + + "}" + " -- "); + printTags(params[j].inlineTags()); + os.println("}"); + } + os.println(" \\end{itemize}"); + os.println("}%end item"); + } + + // Return tag + if (mem instanceof MethodDoc) { + Tag[] ret = mem.tags("return"); + if (ret.length > 0) { + if (!yet) { + os.println("\\begin{itemize}"); + // os.println("\\sld"); + yet = true; + } + os.println("\\item{" + BOLD + " Returns} -- "); + for (int j = 0; j < ret.length; ++j) { + printTags(ret[j].inlineTags()); + os.println(" "); + } + os.println("}%end item"); + } + } + + // Throws or Exceptions tag + if (mem instanceof ExecutableMemberDoc) { + ThrowsTag[] excp = (mem).throwsTags(); + if (excp.length > 0) { + if (!yet) { + os.println("\\begin{itemize}"); + // os.println("\\sld"); + yet = true; + } + os.println("\\item{" + BOLD + " Throws}"); + os.println(" \\begin{itemize}"); + // os.println("\\sld"); + for (int j = 0; j < excp.length; ++j) { + String ename = excp[j].exceptionName(); + ClassDoc cdoc = excp[j].exception(); + if (cdoc != null) { + ename = cdoc.qualifiedName(); + } + os.print(" \\item{\\vskip -.6ex " + TRUETYPE + + HTMLtoLaTeXBackEnd.fixText(ename) + "} -- "); + printTags(excp[j].inlineTags()); + os.println("}"); + } + os.println(" \\end{itemize}"); + os.println("}%end item"); + } + } + + // See tags + SeeTag[] sees = mem.seeTags(); + if (sees.length > 0) { + if (!yet) { + os.println("\\begin{itemize}"); + yet = true; + } + os.println("\\item{" + BOLD + " See also}"); + os.println(" \\begin{itemize}"); + for (int j = 0; j < sees.length; ++j) { + os.print("\\item{ "); + printSeesTag(sees[j], pac); + os.println("}"); + } + os.println(" \\end{itemize}"); + os.println("}%end item"); + } + if (yet) { + os.println("\\end{itemize}"); + } + os.println("}%end item"); + } + + /** + * (S.M. modification) + */ + static void printInherited(ClassDoc par) { + boolean members = false; + + MemberDoc[] inheritedmembers = new MemberDoc[par.fields().length + + par.methods().length]; + for (int k = 0; k < par.fields().length; k++) { + inheritedmembers[k] = par.fields()[k]; + members = true; + } + for (int k = 0; k < par.methods().length; k++) { + inheritedmembers[k + par.fields().length] = par.methods()[k]; + members = true; + } + + // (S.M. modification) print only if members available ( + // for instance in case of a not correct set classpath + // members are missed) + if (members) { + os.print("\\" + sectionLevels[2] + "{"); + // (S.M. modification) do not user true type and do + // not print full name here + // because this produces an ugly view in the Contents section + // os.print("Members inherited from class "+TRUETYPE+"" + os.print("Members inherited from class " + + HTMLtoLaTeXBackEnd.fixText( + // par.qualifiedName() + par.name()) + " "); + // par.name()) + "} \\textnormal{\\small"); + os.println("}{"); + // (S.M. modification) + // ... but print here the full name including the + // package and reference + + os.println(TRUETYPE + par.qualifiedName() + "} {\\small "); + printRef(par.containingPackage(), par.name(), null); + os.println("}"); + + os.println("{\\small "); + + printinheritedMembers(par, inheritedmembers, false); + + os.println("}"); + + } + } + + /** + * Enumerates the members of a section of the document and formats them + * using Tex statements. + * + * @param mems + * the members of this entity + * @see #start + */ + static void printinheritedMembers(ClassDoc cd, MemberDoc[] dmems, + boolean labels) { + if (dmems.length == 0) { + return; + } + os.println(); + if (useHr) { + os.println("\\rule[1em]{\\hsize}{2pt}\\vskip -2em"); + } + List l = Arrays.asList(dmems); + Collections.sort(l); + Iterator itr = l.iterator(); + + // (S.M. modification) + if (shortInheritance) { + + for (int i = 0; itr.hasNext(); ++i) { + MemberDoc mem = (MemberDoc) itr.next(); + + // print only member names + if (i != 0) { + os.print(", "); + } + + os.print(HTMLtoLaTeXBackEnd.fixText(mem.name())); + + } + + } else { + + os.println("\\vskip -2em"); + os.println("\\begin{itemize}"); + for (int i = 0; itr.hasNext(); ++i) { + MemberDoc mem = (MemberDoc) itr.next(); + + // Print signature + + // (S.M. modification) + // os.println("\\item{\\vskip -1.9ex " ); + os.println("\\item{\\vskip -1.5ex "); + os.print(TRUETYPE); + os.print(HTMLtoLaTeXBackEnd.fixText(mem.modifiers())); + if (mem instanceof MethodDoc) { + os.print(" " + + HTMLtoLaTeXBackEnd.fixText(((MethodDoc) mem) + .returnType().typeName())); + } + os.print(" " + BOLD + " " + + HTMLtoLaTeXBackEnd.fixText(mem.name()) + "}"); + if (mem instanceof MethodDoc) { + os.print("("); + Parameter[] parms = ((MethodDoc) mem).parameters(); + int p = 0; + String qparmstr = ""; + String parmstr = ""; + for (; p < parms.length; ++p) { + if (p > 0) { + os.println(","); + } + Type t = parms[p].type(); + os.print(TRUETYPE + + + HTMLtoLaTeXBackEnd + .fixText(packageRelativIdentifier( + mem.containingPackage(), + t.qualifiedTypeName()))); + os.print(HTMLtoLaTeXBackEnd.fixText(t.dimension()) + + "}"); + if (parms[p].name().equals("") == false) { + os.print(" " + + BOLD + + " " + + HTMLtoLaTeXBackEnd.fixText(parms[p] + .name()) + "}"); + } + if (qparmstr.length() != 0) { + qparmstr += ","; + } + qparmstr += t.qualifiedTypeName() + t.dimension(); + if (parmstr.length() != 0) { + parmstr += ","; + } + parmstr += t.typeName() + t.dimension(); + } + os.print(")"); + + // Thrown exceptions + if (mem instanceof ExecutableMemberDoc) { + ClassDoc[] thrownExceptions = ((ExecutableMemberDoc) mem) + .thrownExceptions(); + if (thrownExceptions != null + && thrownExceptions.length > 0) { + os.print(" throws " + + packageRelativIdentifier( + mem.containingPackage(), + thrownExceptions[0].qualifiedName())); + for (int e = 1; e < thrownExceptions.length; e++) { + os.print(", " + + thrownExceptions[e].qualifiedName()); + } + } + } + os.println(); + + if (labels && qparmstr.startsWith("field") == false) { + os.print("\\label{" + + refName(makeRefKey(cd.qualifiedName() + + "." + + mem.name() + + ((qparmstr.length() > 0) ? ("(" + + qparmstr + ")") : ""))) + "}"); + os.print("\\label{" + + refName(makeRefKey(cd.name() + + "." + + mem.name() + + ((parmstr.length() > 0) ? ("(" + + parmstr + ")") : ""))) + "}"); + } + } + + os.println("}%end signature"); + boolean yet = false; + + if (yet) { + os.println("\\end{itemize}"); + } + os.println("}%end item"); + + } // end for + os.println("\\end{itemize}"); + } // shortInheritance + } + + /** + * Prints a sequence of tags obtained from e.g. com.sun.javadoc.Doc.tags(). + */ + static void printTags(Tag[] tags) { + String htmlstr = new String(); + + for (int i = 0; i < tags.length; i++) { + if (tags[i] instanceof SeeTag) { + SeeTag link = (SeeTag) tags[i]; + + String linkstr = ""; + if (link.referencedMember() != null) { + if (link.referencedMember() instanceof ExecutableMemberDoc) { + ExecutableMemberDoc m = (ExecutableMemberDoc) link + .referencedMember(); + linkstr = m.qualifiedName() + m.signature(); + } else { + linkstr = link.referencedMember().qualifiedName(); + } + } else if (link.referencedClass() != null) { + linkstr = link.referencedClass().qualifiedName(); + } else if (link.referencedPackage() != null) { + linkstr = link.referencedPackage().name(); + } + + if (linkstr.equals("")) { + htmlstr += link.text(); + } else { + // Encapsulate the link in a "TEX" tag and let + // HTMLtoLaTeXBackEnd.fixText handle the rest. + htmlstr += "<TEX txt=\""; + if (hyperref) { + htmlstr += "\\hyperlink{" + + refName(makeRefKey(linkstr)) + "}{"; + } + htmlstr += HTMLtoLaTeXBackEnd.fixText(link.label()); + if (hyperref) { + htmlstr += "}"; + } + htmlstr += "{\\small \n"; + htmlstr += "\\refdefined{" + refName(makeRefKey(linkstr)) + + "}"; + htmlstr += "}"; + htmlstr += "\"></TEX>"; + } + } else { + htmlstr += tags[i].text(); + } + } + + os.print(HTMLtoLaTeXBackEnd.fixText(htmlstr)); + } + + /** + * Prints a reference to a package, class or member. + */ + static void printRef(PackageDoc pd, String cls, String mem) { + String pkg = ""; + if (pd != null) { + pkg = pd.name() + "."; + } + + String lbl = pkg + cls; + if (mem != null && mem.equals("") == false) { + lbl += "." + mem; + } + os.print("\\refdefined{" + refName(makeRefKey(lbl)) + "}"); + } + + /** + * Should be removed. + */ + static String makeRefKey(String key) { + return key; + } + + /** + * Returns a package relative identifier. + * + * @param doc + * The package the identifier should be relative to. + * @param str + * The identifier to be made relative. + */ + static String packageRelativIdentifier(PackageDoc doc, String str) { + if (str.startsWith(doc.name() + ".")) { + return str.substring(doc.name().length() + 1); + } else { + return str; + } + } + + /** + * Prints a "see also" tag. + * + * @param tag + * The "see also" tag to print. + * @param relativeTo + * The package to which the see tag should be relative to. + */ + static void printSeesTag(SeeTag tag, PackageDoc relativeTo) { + String memName = ""; + String memText = ""; + if (tag.referencedMember() != null) { + if (tag.referencedMember() instanceof ExecutableMemberDoc) { + ExecutableMemberDoc m = (ExecutableMemberDoc) tag + .referencedMember(); + memName = m.qualifiedName() + m.signature(); + memText = HTMLtoLaTeXBackEnd.fixText(packageRelativIdentifier( + relativeTo, m.qualifiedName())); + memText += "("; + for (int i = 0; i < m.parameters().length; i++) { + memText += HTMLtoLaTeXBackEnd + .fixText(packageRelativIdentifier(relativeTo, + m.parameters()[i].typeName())); + if (i < m.parameters().length - 1) { + memText += ",\\allowbreak "; + } + } + memText += ")"; + } else { + memName = tag.referencedMember().qualifiedName(); + memText = HTMLtoLaTeXBackEnd.fixText(packageRelativIdentifier( + relativeTo, memName)); + } + + } else if (tag.referencedClass() != null) { + memName = tag.referencedClass().qualifiedName(); + memText = HTMLtoLaTeXBackEnd.fixText(packageRelativIdentifier( + relativeTo, memName)); + } else if (tag.referencedPackage() != null) { + memName = tag.referencedPackage().name(); + memText = HTMLtoLaTeXBackEnd.fixText(memName); + } + + if (memName.equals("") == false) { + os.print(TRUETYPE); + if (hyperref) { + os.print("\\hyperlink{" + refName(makeRefKey(memName)) + "}{"); + } + // os.print(HTMLtoLaTeXBackEnd.fixText(memText)); + // System.out.println("see also link : " + memText); + os.print(memText); + if (hyperref) { + os.print("}"); + } + os.println("} {\\small "); + os.print("\\refdefined{" + refName(makeRefKey(memName)) + "}"); + os.println("}%end"); + } else { + os.print(HTMLtoLaTeXBackEnd.fixText(tag.text())); + } + } + + /** + * In my opinion a PackageDoc should be able to tell where the source-files + * defining the package were lifted from. The standard doclet uses this + * directory to locate "doc-files" and copy them. Instead we have to attempt + * to replicate the search procedure doclet API in order to locate the + * appropriate directory. The following is not fool proof... + */ + static File findPackageDir(String pkg, RootDoc root) { + File f = null; + + String sourcepath = null; + for (int i = 0; i < root.options().length; i++) { + if (root.options()[i][0].equalsIgnoreCase("-sourcepath")) { + sourcepath = root.options()[i][1]; + } + } + + if (sourcepath == null) { + sourcepath = "."; + } else if (sourcepath.startsWith("\"")) { + sourcepath = sourcepath.substring(1); + if (sourcepath.endsWith("\"")) { + sourcepath = sourcepath.substring(0, sourcepath.length() - 1); + } + } + + StringTokenizer sourcepathToken = new StringTokenizer(sourcepath, ";"); + while (sourcepathToken.hasMoreTokens()) { + String token = sourcepathToken.nextToken(); + if (token.equals(".")) { + f = null; + } else { + f = new File(token); + } + + if (pkg != null && !pkg.equals("")) { + StringTokenizer st = new StringTokenizer(pkg, "."); + while (st.hasMoreTokens()) { + f = new File(f, st.nextToken()); + } + } + + if (f.exists()) { + // This test is necessary so we don't terminate prematurely. + // It is, however, not sufficient. + File[] javasourcefiles = f.listFiles(new FilenameFilter() { + @Override + public boolean accept(File file, String filename) { + return filename.toLowerCase().endsWith(".java"); + } + }); + if (javasourcefiles.length > 0) { + return f; + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/main/java/org/stfm/texdoclet/TestFilter.java b/src/main/java/org/stfm/texdoclet/TestFilter.java new file mode 100755 index 0000000..84bc50f --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/TestFilter.java @@ -0,0 +1,20 @@ +package org.stfm.texdoclet; + +import com.sun.javadoc.ClassDoc; + +/** + * This class filters out classes beginning with "Test" when applied to the + * Doclet. + * + * @version $Revision: 1.2 $ + */ +public class TestFilter implements ClassFilter { + + /** + * Returns false if class name starts with "Test". + */ + @Override + public boolean includeClass(ClassDoc cd) { + return !cd.name().startsWith("Test"); + } +} diff --git a/src/main/java/org/stfm/texdoclet/doc-files/texdoclet.gif b/src/main/java/org/stfm/texdoclet/doc-files/texdoclet.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef35cdb473f92735d1db67e0bcc5f1f78cb8dab7 GIT binary patch literal 14643 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 XBS(DBmRO#f*Y~j#9XLq@0L%Xahl@1y literal 0 HcmV?d00001 diff --git a/src/main/java/org/stfm/texdoclet/doc-files/texdoclet.jpg b/src/main/java/org/stfm/texdoclet/doc-files/texdoclet.jpg new file mode 100755 index 0000000000000000000000000000000000000000..1139cb61dd277634f41c4b9bc448767da9fd2f56 GIT binary patch literal 5948 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%0D?9kAXb#m~tSzBg5yOx{o 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 literal 0 HcmV?d00001 diff --git a/src/main/java/org/stfm/texdoclet/doc-files/texdoclet.png b/src/main/java/org/stfm/texdoclet/doc-files/texdoclet.png new file mode 100644 index 0000000000000000000000000000000000000000..de981b5821eccc7f3fd6436f1989d3f0f76b2f46 GIT binary patch literal 30478 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 V|D{@(H5C8=002ovPDHLkV1lF<fc5|Y literal 0 HcmV?d00001 diff --git a/src/main/java/org/stfm/texdoclet/package.html b/src/main/java/org/stfm/texdoclet/package.html new file mode 100755 index 0000000..0ad89de --- /dev/null +++ b/src/main/java/org/stfm/texdoclet/package.html @@ -0,0 +1,6 @@ +<BODY> +This doclet is based on the doclet originally created by Greg Wonderly of +<A href=http://www.c2-tech.com>C2 technologies Inc.</A> and its revision by +<A HREF=http://www.xosoftware.dk>XO Software</A>. The project of Greg Wonderly is available here : +<A href=http://java.net/projects/texdoclet>http://java.net/projects/texdoclet</A>. +</BODY>