diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/tools/gnu | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/tools/gnu')
255 files changed, 57605 insertions, 0 deletions
diff --git a/libjava/classpath/tools/gnu/classpath/tools/FileSystemClassLoader.java b/libjava/classpath/tools/gnu/classpath/tools/FileSystemClassLoader.java new file mode 100644 index 000000000..a6bd72831 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/FileSystemClassLoader.java @@ -0,0 +1,312 @@ +/* gnu.classpath.tools.FileSystemClassLoader + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.io.StreamTokenizer; +import java.io.StringReader; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.util.LinkedList; +import java.util.List; +import java.util.ArrayList; +import java.util.StringTokenizer; + +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.jar.Attributes; + +/** + * A <code>ClassLoader</code> implementation which looks for classes + * on the local filesystem given a standard search path. + */ +public class FileSystemClassLoader extends ClassLoader { + + private File[] pathComponents; + + /** + * Initialize the class loader with a normal path string. The path + * string should contain path components separated by {@link + * File.pathSeparator}. Each path component should either denote a + * directory or a .jar or .zip file. + */ + public FileSystemClassLoader(String path) + { + List components = new ArrayList(); + for (StringTokenizer st = new StringTokenizer(path, File.pathSeparator); st.hasMoreTokens(); ) { + File pathComponent = new File(st.nextToken()); + components.add(pathComponent); + if (pathComponent.exists() && !pathComponent.isDirectory()) { + List subComponents = tryGetJarFileClassPathComponents(pathComponent); + if (null != subComponents) { + components.addAll(subComponents); + } + } + } + File[] componentArray = new File[components.size()]; + this.pathComponents = (File[])components.toArray(componentArray); + } + + /** + * Initialize the class loader with an array of path + * components. Each path component should either denote a + * directory or a .jar or .zip file. + */ + public FileSystemClassLoader(File[] pathComponents) + { + this.pathComponents = pathComponents; + for (int i = 0; i < pathComponents.length; ++i) { + if (!pathComponents[i].exists()) { + System.err.println("WARNING: Path component '" + pathComponents[i] + "' not found."); + } + } + } + + public Class loadClass(String name) + throws ClassNotFoundException { + + return super.loadClass(name); + } + + public Class findClass(String name) + throws ClassNotFoundException { + + byte[] b = loadClassData(name); + return defineClass(name, b, 0, b.length); + } + + public URL findResource(String name) + { + StreamInfo streamInfo = getResourceStream(name); + if (null == streamInfo) { + return super.findResource(name); + } + else { + try { + return streamInfo.getURL(); + } + catch (MalformedURLException e) { + System.err.println("WARNING: In FileSystemClassLoader: could not derive URL from file or jar entry: " + e.toString()); + return null; + } + } + } + + private byte[] readFromStream(InputStream in, long size) + throws IOException + { + byte[] result = new byte[(int)size]; + int nread = 0; + int offset = 0; + while (offset < size && (nread = in.read(result, offset, (int)(size - offset))) >= 0) { + offset += nread; + } + in.close(); + return result; + } + + private byte[] readFromStream(StreamInfo streamInfo) + throws IOException + { + InputStream in = streamInfo.openStream(); + long size = streamInfo.getSize(); + + byte[] result = new byte[(int)size]; + int nread = 0; + int offset = 0; + while (offset < size && (nread = in.read(result, offset, (int)(size - offset))) >= 0) { + offset += nread; + } + in.close(); + return result; + } + + private static interface StreamInfo + { + public InputStream openStream() + throws IOException; + public long getSize(); + public URL getURL() + throws MalformedURLException; + } + + private static class FileStreamInfo + implements StreamInfo + { + File file; + + FileStreamInfo(File file) + { + this.file = file; + } + + public InputStream openStream() + throws IOException + { + return new FileInputStream(file); + } + + public long getSize() + { + return file.length(); + } + + public URL getURL() + throws MalformedURLException + { + return file.toURL(); + } + } + + private static class JarStreamInfo + implements StreamInfo + { + private File file; + private JarFile jarFile; + private JarEntry jarEntry; + + JarStreamInfo(File file, JarFile jarFile, JarEntry jarEntry) + { + this.file = file; + this.jarFile = jarFile; + this.jarEntry = jarEntry; + } + + public InputStream openStream() + throws IOException + { + return jarFile.getInputStream(jarEntry); + } + + public long getSize() + { + return jarEntry.getSize(); + } + + public URL getURL() + throws MalformedURLException + { + String urlString = "jar:" + file.toURL() + "!/" + jarEntry.getName(); + return new URL(urlString); + } + } + + private StreamInfo getResourceStream(String path) + { + for (int i = 0; i < pathComponents.length; ++i) { + try { + File parent = pathComponents[i]; + if (parent.isDirectory()) { + File file = new File(parent, path); + if (file.exists()) { + return new FileStreamInfo(file); + } + } + else { + JarFile jarFile = new JarFile(parent, false, JarFile.OPEN_READ); + JarEntry jarEntry = jarFile.getJarEntry(path); + if (null != jarEntry) { + return new JarStreamInfo(parent, jarFile, jarEntry); + } + } + } + catch (IOException ignore) { + } + } + return null; + } + + private byte[] loadClassData(String className) + throws ClassNotFoundException + { + String classFileName = className.replace('.', File.separatorChar) + ".class"; + StreamInfo streamInfo = getResourceStream(classFileName); + + try { + if (null != streamInfo) { + return readFromStream(streamInfo); + } + } + catch (IOException ignore) { + } + + throw new ClassNotFoundException(className); + } + + private static List tryGetJarFileClassPathComponents(File file) + { + try { + JarFile jarFile = new JarFile(file, false, JarFile.OPEN_READ); + Manifest manifest = jarFile.getManifest(); + if (null != manifest) { + Attributes mainAttributes = manifest.getMainAttributes(); + if (null != mainAttributes) { + String classPath = mainAttributes.getValue(Attributes.Name.CLASS_PATH); + if (null != classPath) { + List result = new LinkedList(); + StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(classPath)); + tokenizer.resetSyntax(); + tokenizer.wordChars(0, Integer.MAX_VALUE); + tokenizer.whitespaceChars(9, 9); // tab + tokenizer.whitespaceChars(10, 10); // lf + tokenizer.whitespaceChars(13, 13); // cr + tokenizer.whitespaceChars(32, 32); // space + tokenizer.quoteChar('"'); + int token; + while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) { + if (StreamTokenizer.TT_WORD == token) { + result.add(new File(file.getParentFile(), tokenizer.sval)); + } + } + return result; + } + } + } + } + catch (IOException ignore) { + } + return null; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/IOToolkit.java b/libjava/classpath/tools/gnu/classpath/tools/IOToolkit.java new file mode 100644 index 000000000..e0ee7ba4f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/IOToolkit.java @@ -0,0 +1,216 @@ +/* gnu.classpath.tools.IOToolkit + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; + +import java.util.Set; + +/** + * Provides various I/O-related helper methods. + * + * @author Julian Scheid + */ +public class IOToolkit +{ + /** + * Prevents instantiation. + */ + private IOToolkit() {} + + /** + * Read all binary data from the given InputStream and write it to + * the given OutputStream. This method doesn't close either + * stream. + * + * @param in the stream from which to read data + * @param out the stream to which to write data + */ + public static void copyStream(InputStream in, OutputStream out) + throws IOException + { + byte[] buf = new byte[256]; + int nread; + + while ((nread = in.read(buf)) >= 0) { + out.write(buf, 0, nread); + } + } + + /** + * Read all character data from the given Reader and write it to + * the given Writer. This method doesn't close either stream. + * + * @param in the Reader from which to read character data + * @param out the Writer to which to write character data + */ + public static void copyStream(Reader in, Writer out) + throws IOException + { + char[] buf = new char[256]; + int nread; + + while ((nread = in.read(buf)) >= 0) { + out.write(buf, 0, nread); + } + } + + /** + * Recursively copy the contents of the input directory to the + * output directory. The output directory is created if it doesn't + * exist. If the output directory doesn't exist and can't be + * created, an IOException is thrown. + * + * @param sourceDir source directory from which to copy files + * @param targetDir target directory to which to copy files + * @param recursive if true, recursively copy subdirectoryies + * @param excludeDirs if non null, must be a Set of String. Each + * element from the set specifies the name of a direct + * subdirectory of the source directory which should be excluded + * from recursive copying. + */ + public static void copyDirectory(File sourceDir, File targetDir, + boolean recursive, + Set excludeDirs) + throws IOException + { + if (!targetDir.exists() && !targetDir.mkdirs()) { + throw new IOException("Cannot create directory " + targetDir); + } + + File[] sourceFiles = sourceDir.listFiles(); + for (int i=0; i<sourceFiles.length; ++i) { + if (sourceFiles[i].isDirectory()) { + if (recursive && (null == excludeDirs + || !excludeDirs.contains(sourceFiles[i].getName()))) { + File targetSubDir = new File(targetDir, + sourceFiles[i].getName()); + if (targetSubDir.exists() || targetSubDir.mkdir()) { + copyDirectory(sourceFiles[i], targetSubDir, recursive, null); + } + else { + throw new IOException("Cannot create directory " + targetSubDir); + } + } + } + else { + copyFile(sourceFiles[i], new File(targetDir, sourceFiles[i].getName())); + } + } + } + + /** + * Copy the contents of the input file to the output file. The + * output file's parent directory must exist. + * + * @param sourceFile specifies the file to copy + * @param targetFile specifies the file to create + */ + public static void copyFile(File sourceFile, File targetFile) + throws IOException + { + InputStream in = new FileInputStream(sourceFile); + OutputStream out = new FileOutputStream(targetFile); + int nread; + byte[] buf = new byte[512]; + while ((nread = in.read(buf)) >= 0) { + out.write(buf, 0, nread); + } + in.close(); + out.close(); + } + + /** + * Read the (remaining) contents of the given reader into a char + * array. This method doesn't close the reader when it is done. + * + * @param reader the Reader to read characters from + * @return an array with the contents of the Reader + */ + public static char[] readFully(Reader reader) + throws IOException + { + StringWriter writer = new StringWriter(); + final int readBufferSize = 256; + char[] chunk = new char[readBufferSize]; + int nread; + while ((nread=reader.read(chunk))>=0) { + writer.write(chunk,0,nread); + } + StringBuffer buffer = writer.getBuffer(); + char[] result = new char[buffer.length()]; + buffer.getChars(0, buffer.length(), result, 0); + return result; + } + + public static String getLineFromFile(File file, int line) + throws IOException + { + FileReader reader = new FileReader(file); + BufferedReader bufferedReader = new BufferedReader(reader); + while (line > 1) { + bufferedReader.readLine(); + -- line; + } + String result = bufferedReader.readLine(); + reader.close(); + return result; + } + + public static String getColumnDisplayLine(int column) + { + StringBuffer result = new StringBuffer(); + while (column > 0) { + result.append(' '); + --column; + } + result.append('^'); + return result.toString(); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/MalformedInputEvent.java b/libjava/classpath/tools/gnu/classpath/tools/MalformedInputEvent.java new file mode 100644 index 000000000..951766f6a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/MalformedInputEvent.java @@ -0,0 +1,110 @@ +/* gnu.classpath.tools.MalformedInputEvent + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools; + +import java.util.EventObject; + +/** + * Encapsulates information about malformed input encountered by a + * {@link NotifyingInputStreamReader}. + * + * You can use {@link getSource()} to fetch a reference to the + * <code>NotifyingInputStreamReader</code> which encountered the + * malformed input. + * + * @author Julian Scheid + */ +public class MalformedInputEvent + extends EventObject +{ + private int lineNumber; + private int columnNumber; + private int length; + + MalformedInputEvent(NotifyingInputStreamReader source, + int lineNumber, + int columnNumber, + int length) + { + super(source); + this.columnNumber = columnNumber; + this.lineNumber = lineNumber; + this.length = length; + } + + /** + * Return the 1-based line number where the malformed input begins + * in the stream read by the + * <code>NotifyingInputStreamReader</code>. + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * Return the 0-based column number where the malformed input + * begins in the stream read by the + * <code>NotifyingInputStreamReader</code>. + */ + public int getColumnNumber() + { + return columnNumber; + } + + /** + * Return the length (in bytes) of the malformed input encountered + * by the <code>NotifyingInputStreamReader</code>. Note that a + * consecutive run of malformed input isn't necessarily reported + * as a whole; depending on the <code>Charset</code> and + * implementation details of <code>CharsetDecoder</code>, the run + * could be reported in chunks down to individual bytes. + */ + public int getLength() + { + return length; + } + + public String toString() + { + return "MalformedInputEvent{line=" + lineNumber + + ",column=" + columnNumber + + ",length=" + length + + "}"; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/MalformedInputListener.java b/libjava/classpath/tools/gnu/classpath/tools/MalformedInputListener.java new file mode 100644 index 000000000..53f806bf8 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/MalformedInputListener.java @@ -0,0 +1,56 @@ +/* gnu.classpath.tools.MalformedInputListener + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools; + +/** + * Classes implementing this interface can be notified when a {@link + * NotifyingInputStreamReader} encounters malformed input. + * + * @author Julian Scheid + */ +public interface MalformedInputListener +{ + /** + * Invoked when a <code>NotifyingInputStreamReader</code> this + * listener is registered with encounters malformed input. + * + * @param MalformedInputEvent contains detailed information about + * the event. + */ + public void malformedInputEncountered(MalformedInputEvent event); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/NotifyingInputStreamReader.java b/libjava/classpath/tools/gnu/classpath/tools/NotifyingInputStreamReader.java new file mode 100644 index 000000000..70e19b1d3 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/NotifyingInputStreamReader.java @@ -0,0 +1,423 @@ +/* gnu.classpath.tools.NotifyingInputStreamReader + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; + +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Similar to {@link java.io.InputStreamReader}, but can give + * notification when malformed input is encountered. + * + * <p> Users of this class can register interest in the event of + * malformed input by calling {@link + * #addMalformedInputListener}. Each time a run of malformed input + * data is encountered, all listener objects are notified. For + * instance, this allows the calling code to inform the user about + * problems in the input stream. </p> + * + * <p> <strong>Background:</strong> The default + * <code>InputStreamReader</code> implementation will ignore + * malformed input, silently replacing it with the default + * replacement string (usually the question mark). Alternatively, you + * can configure a <code>CharsetDecoder</code> for the default + * <char>InputStreamReader</code> implementation, instructing it to + * ignore malformed input without replacing it; to replace it with a + * different string; or to throw an exception when malformed input is + * encountered. However, you cannot configure an + * <code>InputStreamReader</code> to gracefully handle malformed data + * but notify the client code about such + * problems. <code>NotifyingInputStreamReader</code> fills this + * gap. </p> + * + * @author Julian Scheid + */ +public class NotifyingInputStreamReader + extends Reader +{ + /** + * The default size (in bytes) for byteBuf, i.e. the size of data + * chunks read from the underlying input stream. + */ + private static final int DEFAULT_INPUT_BUFFER_SIZE = 64; + + /** + * The default size (in chars) for charBuf. This should be large + * enough to hold a decoded chunk of input data for the Charset + * with the minimum number of bytes per character. Since the + * minimum number of bytes per character for usual Charsets is + * one, this number should be identical to + * DEFAULT_INPUT_BUFFER_SIZE. + */ + private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 64; + + /** + * The underlying InputStream. + */ + private InputStream in; + + /** + * The CharsetDecoder used to decode the underlying stream. + */ + private CharsetDecoder decoder; + + /** + * Holds bytes already read from the underlying stream, but not + * yet decoded. + */ + private ByteBuffer byteBuffer; + + /** + * Holds characters already decoded, but not yet fetched via + * read(). + */ + private CharBuffer charBuffer; + + /** + * This is the primitive byte array wrapped in byteBuffer for + * passing to to InputStream.read(). + */ + private byte[] readBuffer; + + /** + * Keeps track of the current line number (1-based). + */ + private int lineNumber = 1; + + /** + * Keeps track of the current column number (0-based). + */ + private int columnNumber = 0; + + /** + * Becomes true as soon as EOF has been reached in the underlying + * InputStream. At this point, byteBuffer contains the last chunk + * of data from the underlying InputStream. + */ + private boolean allInputConsumed = false; + + /** + * Becomes true as soon as the decoder has been supplied with the + * last chunk of data from the InputStream after EOF has been + * reached. At this point, the last chunk of data has been drained + * from the byteBuffer. + */ + private boolean decodingFinished = false; + + /** + * Becomes true as soon as the decoder has been flushed. At this + * point, the last chunk of character data has been written to the + * charBuffer. + */ + private boolean flushed = false; + + /** + * Stores all registered MalformedInputListeners. + */ + private Set listeners = new LinkedHashSet(); + + /** + * Initializes a new instance for reading from the given + * InputStream using the default encoding. The default encoding is + * currently determined by looking at the system property + * <code>file.encoding</code>. If this property isn't set, + * <code>ISO-8859-1</code> is used as a fallback. + * + * <p>This method should use {@link Charset.defaultCharset()} + * instead, but this isn't implemented in Classpath at the + * moment.</p> + * + * @param in the <code>InputStream</code> to read from. + */ + public NotifyingInputStreamReader(InputStream in) + { + this(in, System.getProperty("file.encoding", "ISO-8859-1")); + } + + /** + * Initializes a new instance for reading from the given + * InputStream using the specified charset. + * + * @param in the <code>InputStream</code> to read from. + * @param charsetName the canonical name or an alias of a + * <code>Charset</code>. + * + * @throws IllegalCharsetNameException if there is no + * <code>Charset</code> with the given canonical name or alias. + * + * @throws UnsupportedCharsetException if there is no support for + * the specified <code>Charset</code> in the runtime environment. + */ + public NotifyingInputStreamReader(InputStream in, String charsetName) + throws IllegalCharsetNameException, UnsupportedCharsetException + { + this(in, Charset.forName(charsetName)); + } + + /** + * Initializes a new instance for reading from the given + * InputStream using the specified charset. + * + * @param in the <code>InputStream</code> to read from. + * @param charset the <code>Charset</code> to use for decoding + * characters. + */ + public NotifyingInputStreamReader(InputStream in, Charset charset) + { + this(in, charset.newDecoder()); + } + + /** + * Initializes a new instance for reading from the given + * InputStream using the specified charset decoder. + * + * <strong>Note:</strong> the + * <code>NotifyingInputStreamReader</code> will not exhibit the + * advertised behaviour if you changed the action to take on + * malformed input in the specified + * <code>CharsetDecoder</code>. In other words, you should not + * call {@link CharsetDecoder.onMalformedInput(CodingErrorAction)} + * on the specified decoder before or while this reader is being + * used unless you know what you're doing. + * + * @param in the <code>InputStream</code> to read from. + * @param charset the <code>CharsetDecoder</code> to use for + * decoding characters. + */ + public NotifyingInputStreamReader(InputStream in, CharsetDecoder decoder) + { + this.in = in; + this.decoder = decoder; + this.charBuffer = CharBuffer.wrap(new char[DEFAULT_INPUT_BUFFER_SIZE]); + this.charBuffer.position(charBuffer.limit()); + this.readBuffer = new byte[DEFAULT_OUTPUT_BUFFER_SIZE]; + this.byteBuffer = ByteBuffer.wrap(readBuffer); + this.byteBuffer.position(byteBuffer.limit()); + } + + public void close() + throws IOException + { + in.close(); + } + + /** + * Fill charBuffer with new character data. This method returns if + * either the charBuffer has been filled completely with decoded + * character data, or if EOF is reached in the underlying + * InputStream. When this method returns, charBuffer is flipped + * and ready to be read from. + */ + private void fillCharBuf() + throws IOException + { + charBuffer.clear(); + outer: + while (!flushed) { + CoderResult coderResult; + int charBufferPositionBefore = charBuffer.position(); + if (!decodingFinished) { + coderResult = decoder.decode(byteBuffer, charBuffer, allInputConsumed); + if (allInputConsumed) { + decodingFinished = true; + } + } + else { + coderResult = decoder.flush(charBuffer); + flushed = coderResult.isUnderflow(); + } + + int charBufferPositionAfter = charBuffer.position(); + for (int i=charBufferPositionBefore; i<charBufferPositionAfter; ++i) { + if (10 == charBuffer.get(i)) { + ++ lineNumber; + columnNumber = 0; + } + else { + ++ columnNumber; + } + } + if (coderResult.isOverflow()) { + break; + } + else if (coderResult.isUnderflow()) { + if (!allInputConsumed) { + int nRemainingBytes = 0; + if (byteBuffer.position() > 0) { + nRemainingBytes = Math.max(0, byteBuffer.limit() - byteBuffer.position()); + } + if (nRemainingBytes > 0) { + byteBuffer.get(readBuffer, 0, nRemainingBytes); + } + byteBuffer.rewind(); + int nread = in.read(readBuffer, nRemainingBytes, + readBuffer.length - nRemainingBytes); + if (nread < 0) { + allInputConsumed = true; + } + byteBuffer.limit(nRemainingBytes + Math.max(0, nread)); + } + else { + break; + } + } + else if (coderResult.isMalformed()) { + fireMalformedInputEncountered(coderResult.length()); + String replacement = decoder.replacement(); + for (int i=0; i<coderResult.length(); ++i) { + if (charBuffer.remaining() > replacement.length()) { + charBuffer.put(replacement); + byteBuffer.position(byteBuffer.position() + 1); + columnNumber ++; + } + else { + break outer; + } + } + } + else if (coderResult.isUnmappable()) { + // This should not happen, since unmappable input bytes + // trigger a "malformed" event instead. + coderResult.throwException(); + } + else { + // This should only happen if run in a future environment + // where additional events apart from underflow, overflow, + // malformed and unmappable can be generated. + coderResult.throwException(); + } + } + charBuffer.flip(); + } + + /** + * Fire a MalformedInputEvent, notifying all registered listeners. + */ + private void fireMalformedInputEncountered(int length) + { + MalformedInputEvent event + = new MalformedInputEvent(this, lineNumber, columnNumber, length); + Iterator it = listeners.iterator(); + while (it.hasNext()) { + MalformedInputListener listener + = (MalformedInputListener)it.next(); + listener.malformedInputEncountered(event); + } + } + + public int read(char[] cbuf, int offset, int length) + throws IOException + { + if (flushed) { + return -1; + } + else { + int nread = 0; + while (nread < length && !flushed) { + while (charBuffer.hasRemaining() && nread < length) { + int copyLen = Math.min(length - nread, + charBuffer.remaining()); + charBuffer.get(cbuf, offset + nread, copyLen); + nread += copyLen; + } + if (nread < length) { + fillCharBuf(); + } + } + return nread; + } + } + + public int read() + throws IOException + { + while (!flushed) { + if (charBuffer.hasRemaining()) { + return charBuffer.get(); + } + else { + fillCharBuf(); + } + } + return -1; + } + + /** + * Returns whether this reader is ready. The reader is ready if + * there is data in the internal buffer, or if additional data can + * be read from the underlying InputStream. + */ + public boolean ready() + { + return charBuffer.hasRemaining() || !flushed; + } + + /** + * Register a <code>MalformedInputListener</code> which should be + * notified when malformed input is encountered. + */ + public void addMalformedInputListener(MalformedInputListener listener) + { + this.listeners.add(listener); + } + + /** + * Unregister a previously registered + * <code>MalformedInputListener</code> if it should no longer be + * notified when malformed input is encountered. + */ + public void removeMalformedInputListener(MalformedInputListener listener) + { + this.listeners.remove(listener); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/StringToolkit.java b/libjava/classpath/tools/gnu/classpath/tools/StringToolkit.java new file mode 100644 index 000000000..dbb552707 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/StringToolkit.java @@ -0,0 +1,84 @@ +/* gnu.classpath.tools.StringToolkit + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools; + +/** + * Provides various String-related helper methods. + * + * @author Julian Scheid + */ +public class StringToolkit +{ + /** + * Prevents instantiation. + */ + private StringToolkit() {} + + /** + * Return <code>haystack</code> with all occurrences of + * <code>needle</code> replaced by </code>replacement</code>. + * + * @param haystack the string to replace occurrences of <code>needle</code> in + * @param needle the substring to replace + * @param replacement the substring to replace <code>needle</code> with + * + * @return <code>haystack</code> with all occurrences of + * <code>needle</code> replaced by </code>replacement</code>. + */ + public static String replace(String haystack, String needle, String replacement) + { + int ndx = haystack.indexOf(needle); + if (ndx < 0) { + return haystack; + } + else { + StringBuffer result = new StringBuffer(); + result.append(haystack.substring(0, ndx)); + result.append(replacement); + ndx += needle.length(); + int ndx2; + while ((ndx2 = haystack.indexOf(needle, ndx)) >= 0) { + result.append(haystack.substring(ndx, ndx2)); + result.append(replacement); + ndx = ndx2 + needle.length(); + } + result.append(haystack.substring(ndx)); + return result.toString(); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletClassLoader.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletClassLoader.java new file mode 100644 index 000000000..dfbedfe36 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletClassLoader.java @@ -0,0 +1,81 @@ +/* AppletClassLoader -- a loader for applet classes + Copyright (C) 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; + +public class AppletClassLoader extends URLClassLoader +{ + /** + * Constructs a new <code>AppletLoader</code> object. + * + * @param codebase the codebase of the applet + * @param archives the urls to add to the search path + */ + public AppletClassLoader(URL codebase, ArrayList archives) + { + super(new URL[0]); + + for (int count = 0; count < archives.size(); count++) + addURL((URL) archives.get(count)); + + addURL(codebase); + } + + /** + * Finds the specified class. This method should be overridden by + * class loader implementations that follow the delegation model for + * loading classes, and will be invoked by the loadClass method after + * checking the parent class loader for the requested class. The default + * implementation throws a ClassNotFoundException. + * + * (description copied from java.lang.ClassLoader.findClass(String)) + * + * @param name The name of the class. + * + * @return the resulting <code>Class</code> object. + * + * @exception ClassNotFoundException if the class is not found. + */ + protected Class findClass(String name) throws ClassNotFoundException + { + return super.findClass(name); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletSecurityManager.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletSecurityManager.java new file mode 100644 index 000000000..6522a9ec1 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletSecurityManager.java @@ -0,0 +1,95 @@ +/* AppletSecurityManager.java -- an applet security manager + Copyright (C) 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.io.FilePermission; +import java.net.SocketPermission; +import java.security.Permission; +import java.security.SecurityPermission; +import java.util.PropertyPermission; + +class AppletSecurityManager extends SecurityManager +{ + private boolean plugin; + + AppletSecurityManager(boolean plugin) + { + this.plugin = plugin; + } + + public void checkPermission(Permission permission) + { + if (permission == null) + throw new NullPointerException(); + + // FIXME: we need to restrict this. + // + // libgcj asks for "java.io.FilePermission <<ALL FILES>> execute" + // to be able to execute "addr2line" to get proper stack traces. + if (permission instanceof FilePermission) + return; + + // FIXME: we need to restrict this. + if (permission instanceof SecurityPermission) + return; + + // FIXME: is this really needed ? + if (permission instanceof PropertyPermission) + return; + + // Needed to allow to access AWT event queue. + if (permission.getName().equals("accessEventQueue")) + return; + + // Needed to create a class loader for each codebase. + if (permission.getName().equals("createClassLoader")) + return; + + // FIXME: we need to allow access to codebase here. + + if (permission instanceof SocketPermission // for net access + || permission instanceof RuntimePermission) // for checkWrite(FileDescriptor) + return; + + if (! plugin && permission.getName().equals("exitVM")) + return; + + // Reject all other permissions. + throw new SecurityException(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletTag.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletTag.java new file mode 100644 index 000000000..d3910a2a6 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/AppletTag.java @@ -0,0 +1,489 @@ +/* AppletTag.java -- a representation of an HTML APPLET tag + Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import gnu.xml.dom.html2.DomHTMLAppletElement; +import gnu.xml.dom.html2.DomHTMLEmbedElement; +import gnu.xml.dom.html2.DomHTMLObjectElement; + +import java.awt.Dimension; +import java.awt.Toolkit; + +import java.io.File; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.text.NumberFormat; +import java.text.ParseException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Locale; + +/** + * @author Lillian Angel (langel@redhat.com) + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + */ +class AppletTag +{ + + /** + * The document base of this applet. + */ + URL documentbase; + + /** + * name of applet tag. + */ + String name = ""; + + /** + * code of applet tag. + */ + String code = ""; + + /** + * codebase of applet tag. + */ + String codebase = ""; + + /** + * The archives. + */ + ArrayList archives = new ArrayList(); + + /** + * The parameters. + */ + HashMap parameters = new HashMap(); + + /** + * The screen size. + */ + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + + /** + * Default constructor. + */ + AppletTag() + { + // Do nothing. + } + + /** + * Constructs an AppletTag and parses the given applet element. + * + * @param appElement - the Applet element to parse. + */ + AppletTag(DomHTMLAppletElement appElement) + { + name = appElement.getName(); + parameters.put("name", name); + + parameters.put("object", appElement.getObject()); + parameters.put("align", appElement.getAlign()); + parameters.put("alt", appElement.getAlt()); + parameters.put("height", appElement.getHeight()); + parameters.put("hspace", Integer.toString(appElement.getHspace())); + parameters.put("vspace", Integer.toString(appElement.getVspace())); + parameters.put("width", appElement.getWidth()); + + TagParser.parseParams(appElement, this); + + if (code.equals("")) + { + code = appElement.getCode(); + if (code.equals("")) + code = appElement.getCls(); + } + + // Must initialize codebase before archives + if (codebase.equals("")) + { + codebase = appElement.getCodeBase(); + if (codebase.equals("")) + codebase = appElement.getSrc(); + } + + if (archives.size() == 0) + { + String arcs = ""; + String arch = appElement.getArchive(); + + if (code.indexOf(".") < 0) + arcs = code + ".jar"; + + if (!arch.equals("")) + arcs += "," + arch; + + if (!arcs.equals("")) + archives = TagParser.parseArchives(arcs, this); + } + } + + /** + * Constructs an AppletTag and parses the given embed element. + * + * @param embElement - the Embed element to parse. + */ + AppletTag(DomHTMLEmbedElement embElement) + { + // In an EMBED tag, a parameter is any non-standard attribute. This + // is a problem for applets that take parameters named "code", + // "codebase", "archive", "object", or "type". The solution is to + // allow the same attributes, prefixed by "java_". The presence of + // a "java_" attribute indicates that the non-prefixed attribute + // should be interpreted as a parameter. For example if "java_code" + // and "code" attributes are present in the EMBED tag then the + // "code" attribute is interpreted as a parameter. + + name = embElement.getName(); + parameters.put("name", name); + + String jobj = embElement.getJavaObject(); + if (!jobj.equals("")) + parameters.put("java_object", jobj); + else + parameters.put("object", embElement.getObject()); + + parameters.put("width", embElement.getWidth()); + parameters.put("height", embElement.getHeight()); + parameters.put("align", embElement.getAlign()); + parameters.put("alt", embElement.getAlt()); + parameters.put("hspace", Integer.toString(embElement.getHspace())); + parameters.put("mayscript", embElement.getMayscript()); + parameters.put("pluginspage", embElement.getPluginsPage()); + parameters.put("title", embElement.getTitle()); + parameters.put("type", embElement.getType()); + parameters.put("java_type", embElement.getJavaType()); + parameters.put("vspace", Integer.toString(embElement.getVspace())); + + TagParser.parseParams(embElement, this); + + // Must initialize codebase before archives + if (codebase.equals("")) + { + String javacb = embElement.getJavaCodeBase(); + if (!javacb.equals("")) + codebase = javacb; + else + codebase = embElement.getCodeBase(); + } + + if (code.equals("")) + { + String jcode = embElement.getJavaCode(); + if (!jcode.equals("")) + code = jcode; + else + code = embElement.getCode(); + } + + if (archives.size() == 0) + { + String arcs = ""; + String jarch = embElement.getJavaArchive(); + String arch = embElement.getArchive(); + + if (code.indexOf(".") < 0) + arcs = code + ".jar"; + + if (!jarch.equals("")) + arcs += "," + jarch; + else if (!arch.equals("")) + arcs += "," + arch; + + if (!arcs.equals("")) + archives = TagParser.parseArchives(arcs, this); + } + } + + /** + * Constructs an AppletTag and parses the given object element. + * + * @param objElement - the Object element to parse. + */ + AppletTag(DomHTMLObjectElement objElement) + { + // In an OBJECT tag, a parameter is any non-standard attribute. This + // is a problem for applets that take parameters named "code", + // "codebase", "archive", "object", or "type". The solution is to + // allow the same attributes, prefixed by "java_". The presence of + // a "java_" attribute indicates that the non-prefixed attribute + // should be interpreted as a parameter. For example if "java_code" + // and "code" attributes are present in the OBJECT tag then the + // "code" attribute is interpreted as a parameter. + + name = objElement.getName(); + parameters.put("name", name); + + String jobj = objElement.getJavaObject(); + if (!jobj.equals("")) + parameters.put("java_object", jobj); + else + parameters.put("object", objElement.getObject()); + + parameters.put("type", objElement.getType()); + parameters.put("java_type", objElement.getJavaType()); + parameters.put("align", objElement.getAlign()); + parameters.put("codetype", objElement.getCodeType()); + parameters.put("data", objElement.getData()); + parameters.put("declare", Boolean.toString(objElement.getDeclare())); + parameters.put("height", objElement.getHeight()); + parameters.put("hspace", Integer.toString(objElement.getHspace())); + parameters.put("border", objElement.getBorder()); + parameters.put("standby", objElement.getStandby()); + parameters.put("tabindex", Integer.toString(objElement.getTabIndex())); + parameters.put("usemap", objElement.getUseMap()); + parameters.put("vspace", Integer.toString(objElement.getVspace())); + parameters.put("width", objElement.getWidth()); + parameters.put("mayscript", objElement.getMayscript()); + parameters.put("scriptable", objElement.getScriptable()); + + TagParser.parseParams(objElement, this); + + // Must initialize codebase before archives + if (codebase.equals("")) + { + String javacb = objElement.getJavaCodeBase(); + if (! javacb.equals("")) + codebase = javacb; + else + codebase = objElement.getCodeBase(); + } + + if (code.equals("")) + { + String jcode = objElement.getJavaCode(); + if (!jcode.equals("")) + code = jcode; + else + code = objElement.getCode(); + } + + if (archives.size() == 0) + { + String arcs = ""; + String jarch = objElement.getJavaArchive(); + String arch = objElement.getArchive(); + + if (code.indexOf(".") < 0) + arcs = code + ".jar"; + + if (!jarch.equals("")) + arcs += "," + jarch; + else if (!arch.equals("")) + arcs += "," + arch; + + if (!arcs.equals("")) + archives = TagParser.parseArchives(arcs, this); + } + } + + /** + * String representation of the tag. + * + * @return the string representation. + */ + public String toString() + { + return (" name=" + name + "\n" + " code=" + code + "\n" + " codebase=" + + codebase + "\n" + " archive=" + archives + "\n" + " parameters=" + + parameters + "\n" + " documentbase=" + documentbase + "\n"); + } + + /** + * Returns the size of the applet. + * + * @return the size. + */ + Dimension getSize() + { + Dimension size = new Dimension(320, 200); + + try + { + String widthStr = (String) parameters.get("width"); + + if (widthStr != null && ! widthStr.equals("")) + { + if (widthStr.charAt(widthStr.length() - 1) == '%') + { + double p = NumberFormat.getPercentInstance(Locale.US).parse(widthStr).intValue() / 100.0; + size.width = (int)(p * screenSize.width); + } + else + size.width = NumberFormat.getInstance(Locale.US).parse(widthStr).intValue(); + } + } + catch (ParseException e) + { + // Use default. + } + + try + { + String heightStr = (String) parameters.get("height"); + + if (heightStr != null && !heightStr.equals("")) + { + if (heightStr.charAt(heightStr.length() - 1) == '%') + { + double p = NumberFormat.getPercentInstance(Locale.US).parse(heightStr).intValue() / 100.0; + size.height = (int) (p * screenSize.height); + } + else + size.height = NumberFormat.getInstance(Locale.US).parse(heightStr).intValue(); + } + } + catch (ParseException e) + { + // Use default. + } + + return size; + } + + /** + * Gets the code base. + * + * @return the codebase. + */ + String getCodeBase() + { + return codebase; + } + + /** + * Gets the archive list. + * + * @return the archive list. + */ + ArrayList getArchives() + { + return archives; + } + + /** + * Gets the code. + * + * @return the code. + */ + String getCode() + { + return code; + } + + /** + * Gets the document base. + * + * @return the document base. + */ + URL getDocumentBase() + { + return documentbase; + } + + /** + * Gets the specified parameter. + * + * @param name - the specified parameter. + * @return the parameter. + */ + String getParameter(String name) + { + return (String) parameters.get(name.toLowerCase()); + } + + /** + * Prepends the base to the codebase. + * + * @return the new URL. + */ + URL prependCodeBase(String base) throws MalformedURLException + { + if (documentbase == null) + documentbase = TagParser.db; + + URL fullcodebase; + + //If no codebase was specified, default to documentbase. + if (codebase.equals("")) + { + if (documentbase.getFile().endsWith(File.separator)) + fullcodebase = documentbase; + else + { + String dirname = documentbase.getFile(); + if (dirname.indexOf(".") < 0) + fullcodebase = new URL(documentbase + File.separator); + else + { + // Determine dirname for file by stripping everything + // past the last file separator. + dirname = dirname.substring(0, + dirname.lastIndexOf(File.separatorChar) + 1); + + fullcodebase = new URL(documentbase.getProtocol(), + documentbase.getHost(), + documentbase.getPort(), dirname); + } + } + } + else + { + // codebase was specified. + URL codebaseURL = new URL(documentbase, codebase); + + if ("file".equals(codebaseURL.getProtocol())) + { + if (new File(codebaseURL.getFile()).isDirectory() && !codebase.endsWith(File.separator)) + fullcodebase = new URL(documentbase, codebase + File.separator); + else + fullcodebase = new URL(documentbase, codebase); + } + else if (codebase.endsWith(File.separator)) + fullcodebase = new URL(documentbase, codebase); + else + fullcodebase = new URL(documentbase, codebase + File.separator); + } + + return new URL(fullcodebase, base); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/CommonAppletContext.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/CommonAppletContext.java new file mode 100644 index 000000000..ebdd35922 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/CommonAppletContext.java @@ -0,0 +1,133 @@ +/* CommonAppletContext.java -- a common applet's context + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.applet.Applet; +import java.applet.AppletContext; +import java.applet.AudioClip; +import java.awt.Image; +import java.awt.Toolkit; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + + +/* + * CommonAppletContext represents the common context stuff for both + * types, plugins and standalone. + */ +abstract class CommonAppletContext + implements AppletContext +{ + // FIXME: this needs to be static, and we need one AppletContext per + // Applet. + List applets = new ArrayList(); + HashMap streams = new HashMap(); + + void addApplet(Applet applet) + { + applets.add(applet); + } + + /////////////////////////////// + //// AppletContext methods //// + /////////////////////////////// + public AudioClip getAudioClip(URL url) + { + return Applet.newAudioClip(url); + } + + public Image getImage(URL url) + { + return Toolkit.getDefaultToolkit().getImage(url); + } + + public Applet getApplet(String name) + { + Applet a; + String appletName; + Iterator i = applets.iterator(); + + while (i.hasNext()) + { + a = (Applet) i.next(); + + appletName = a.getParameter("name"); + if (a != null && appletName != null && appletName.equals(name)) + return a; + } + return null; + } + + public Enumeration getApplets() + { + return Collections.enumeration(applets); + } + + public void showDocument(URL url) + { + showDocument(url, "_self"); + } + + /* + // FIXME: implement. + public abstract void showDocument (URL url, String target); + + // FIXME: implement. + public abstract void showStatus (String status); + */ + public void setStream(String key, InputStream stream) + { + streams.put(key, stream); + } + + public InputStream getStream(String key) + { + return (InputStream) streams.get(key); + } + + public Iterator getStreamKeys() + { + return streams.keySet().iterator(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/CommonAppletStub.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/CommonAppletStub.java new file mode 100644 index 000000000..bf14db573 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/CommonAppletStub.java @@ -0,0 +1,139 @@ +/* CommonAppletStub.java -- an applet-browser interface class + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.applet.AppletContext; +import java.applet.AppletStub; +import java.applet.Applet; +import java.net.MalformedURLException; +import java.net.URL; + + +class CommonAppletStub + implements AppletStub +{ + private AppletTag tag; + private AppletContext context; + private Applet applet; + + CommonAppletStub(AppletTag tag, AppletContext context, Applet applet) + { + this.tag = tag; + this.context = context; + this.applet = applet; + } + + //////////////////////////////// + ////// AppletStub Methods ////// + //////////////////////////////// + + /** + * Tests whether or not this applet is currently active. An applet + * becomes active just before the browser invokes start (), and + * becomes inactive just before the browser invokes stop (). + * + * @return true if applet is active, false otherwise + */ + public boolean isActive() + { + return true; + } + + /** + * Returns the basename URL of the document in which this applet is + * embedded. + * + * @return the document base url. + */ + public URL getDocumentBase() + { + return tag.getDocumentBase(); + } + + /** + * Returns the URL of the code base for this applet. + * + * @return the codebase url + */ + public URL getCodeBase() + { + try + { + return tag.prependCodeBase(""); + } + catch (MalformedURLException e) + { + throw new RuntimeException("Attempted to create" + + " invalid codebase URL.", e); + } + } + + /** + * Returns the value of the specified parameter that was specified + * in the <code>APPLET</code> tag for this applet. + * + * @param name the key name + * + * @return the key value + */ + public String getParameter(String name) + { + return tag.getParameter(name.toLowerCase()); + } + + /** + * Returns the applet context for this applet. + * + * @return the context + */ + public AppletContext getAppletContext() + { + return context; + } + + /** + * Requests that the applet window for this applet be resized. + * + * @param width the new witdh + * @param height the new height + */ + public void appletResize(int width, int height) + { + applet.setBounds (0, 0, width, height); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/ErrorApplet.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/ErrorApplet.java new file mode 100644 index 000000000..059dbee40 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/ErrorApplet.java @@ -0,0 +1,53 @@ +/* ErrorApplet.java -- an applet to load in case of an error + Copyright (C) 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.applet.Applet; +import java.awt.BorderLayout; +import java.awt.Button; + +public class ErrorApplet extends Applet +{ + public ErrorApplet(String message) + { + setLayout(new BorderLayout()); + + Button button = new Button(message); + add(button); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/Main.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/Main.java new file mode 100644 index 000000000..f6e02dbaa --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/Main.java @@ -0,0 +1,323 @@ +/* Main.java -- a standalone viewer for Java applets + Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import java.applet.Applet; +import java.awt.Dimension; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + + +class Main +{ + private static HashMap classLoaderCache = new HashMap(); + + private static ClassLoader getClassLoader(URL codebase, ArrayList archives) + { + // Should load class loader each time. It is possible that there + // are more than one applet to be loaded with different archives. + AppletClassLoader loader = new AppletClassLoader(codebase, archives); + classLoaderCache.put(codebase, loader); + + return loader; + } + + private static String code = null; + private static String codebase = null; + private static String archive = null; + private static List parameters = new ArrayList(); + private static Dimension dimensions = new Dimension(-1, -1); + private static String pipeInName = null; + private static String pipeOutName = null; + private static boolean pluginMode = false; + private static Parser parser = null; + + static Applet createApplet(AppletTag tag) + { + Applet applet = null; + + try + { + ClassLoader loader = getClassLoader(tag.prependCodeBase(""), + tag.getArchives()); + String code = tag.getCode(); + + if (code.endsWith(".class")) + code = code.substring(0, code.length() - 6).replace('/', '.'); + + Class c = loader.loadClass(code); + applet = (Applet) c.newInstance(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + if (applet == null) + applet = new ErrorApplet(Messages.getString ("Main.ErrorApplet")); + + return applet; + } + + protected static boolean verbose; + + /** + * The main method starting the applet viewer. + * + * @param args the arguments given on the command line. + * + * @exception IOException if an error occurs. + */ + public static void main(String[] args) throws IOException + { + parser = new ClasspathToolParser("appletviewer", true); + parser.setHeader(Messages.getString("Main.Usage")); + + OptionGroup attributeGroup + = new OptionGroup(Messages.getString("Main.AppletTagOptions")); + + attributeGroup.add(new Option("code", + Messages.getString("Main.CodeDescription"), + Messages.getString("Main.CodeArgument")) + { + public void parsed(String argument) throws OptionException + { + code = argument; + } + }); + attributeGroup.add + (new Option("codebase", + Messages.getString("Main.CodebaseDescription"), + Messages.getString("Main.CodebaseArgument")) + { + public void parsed(String argument) throws OptionException + { + codebase = argument; + } + }); + attributeGroup.add + (new Option("archive", + Messages.getString("Main.ArchiveDescription"), + Messages.getString("Main.ArchiveArgument")) + { + public void parsed(String argument) throws OptionException + { + archive = argument; + } + }); + attributeGroup.add(new Option("width", + Messages.getString("Main.WidthDescription"), + Messages.getString("Main.WidthArgument")) + { + public void parsed(String argument) throws OptionException + { + dimensions.width = Integer.parseInt(argument); + } + }); + attributeGroup.add(new Option("height", + Messages.getString("Main.HeightDescription"), + Messages.getString("Main.HeightArgument")) + { + public void parsed(String argument) throws OptionException + { + dimensions.height = Integer.parseInt(argument); + } + }); + attributeGroup.add(new Option("param", + Messages.getString("Main.ParamDescription"), + Messages.getString("Main.ParamArgument")) + { + public void parsed(String argument) throws OptionException + { + parameters.add(argument); + } + }); + OptionGroup pluginGroup + = new OptionGroup(Messages.getString("Main.PluginOption")); + pluginGroup.add(new Option("plugin", + Messages.getString("Main.PluginDescription"), + Messages.getString("Main.PluginArgument")) + { + public void parsed(String argument) throws OptionException + { + pluginMode = true; + int comma = argument.indexOf(','); + pipeInName = argument.substring(0, comma); + pipeOutName = argument.substring(comma + 1); + } + }); + OptionGroup debuggingGroup + = new OptionGroup(Messages.getString("Main.DebuggingOption")); + debuggingGroup.add + (new Option("verbose", + Messages.getString("Main.VerboseDescription"), + (String) null) + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + OptionGroup compatibilityGroup + = new OptionGroup(Messages.getString("Main.CompatibilityOptions")); + compatibilityGroup.add + (new Option("debug", + Messages.getString("Main.DebugDescription"), + (String) null) + { + public void parsed(String argument) throws OptionException + { + // Currently ignored. + } + }); + compatibilityGroup.add + (new Option("encoding", + Messages.getString("Main.EncodingDescription"), + Messages.getString("Main.EncodingArgument")) + { + public void parsed(String argument) throws OptionException + { + // FIXME: We should probably be using + // java.nio.charset.CharsetDecoder to handle the encoding. What + // is the status of Classpath's implementation? + } + }); + parser.add(attributeGroup); + parser.add(pluginGroup); + parser.add(debuggingGroup); + parser.add(compatibilityGroup); + + String[] urls = parser.parse(args); + + // Print arguments. + printArguments(args); + + args = urls; + + if (dimensions.height < 0) + dimensions.height = 200; + + if (dimensions.width < 0) + dimensions.width = (int) (1.6 * dimensions.height); + + //System.setSecurityManager(new AppletSecurityManager(pluginMode)); + + if (pluginMode) + { + // Plugin will warn user about missing security manager. + InputStream in; + OutputStream out; + + in = new FileInputStream(pipeInName); + out = new FileOutputStream(pipeOutName); + + PluginAppletViewer.start(in, out); + } + else + { + // Warn user about missing security manager. + System.err.println(Messages.getString("Main.SecurityWarning") + "\n"); + + System.err.println(Messages.getString("Main.ContinuationPrompt")); + + BufferedReader stdin + = new BufferedReader(new InputStreamReader(System.in)); + String response = null; + + try + { + response = stdin.readLine(); + } + catch (IOException e) + { + throw new RuntimeException("Failed to read response" + + " to continuation prompt.", e); + } + + if (!(response.equals("c") || response.equals("C"))) + { + System.exit(0); + } + + if (code == null) + { + // The --code option wasn't given and there are no URL + // arguments so we have nothing to work with. + if (args.length == 0) + { + System.err.println(Messages.getString("Main.NoInputFiles")); + System.exit(1); + } + // Create a standalone appletviewer from a list of URLs. + new StandaloneAppletViewer(args); + } + else + { + // Create a standalone appletviewer from the --code + // option. + new StandaloneAppletViewer(code, codebase, archive, + parameters, dimensions); + } + } + } + + static void printArguments(String[] args) + { + if (verbose) + { + System.out.println(Messages.getString("Main.RawArguments")); + + for (int i = 0; i < args.length; i++) + System.out.println(" " + args[i]); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/Messages.java new file mode 100644 index 000000000..614a509fd --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- localization support for appletviewer + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.appletviewer; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.appletviewer.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletContext.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletContext.java new file mode 100644 index 000000000..bc445a6b3 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletContext.java @@ -0,0 +1,72 @@ +/* PluginAppletContext.java -- an applet's context within a web browser + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.net.URL; +import java.io.IOException; + +/* + * PluginAppletContext represents the context within a webpage of a + * group of applets that all share the same codebase. + */ +class PluginAppletContext extends CommonAppletContext +{ + public void showDocument(URL url, String target) + { + try + { + PluginAppletViewer.write("url " + url + " " + target); + } + catch(IOException e) + { + throw new RuntimeException("showDocument failed.", e); + } + } + + public void showStatus(String status) + { + try + { + PluginAppletViewer.write("status " + status); + } + catch(IOException e) + { + throw new RuntimeException("showStatus failed.", e); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletViewer.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletViewer.java new file mode 100644 index 000000000..da8399b6b --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletViewer.java @@ -0,0 +1,178 @@ +/* PluginAppletViewer.java -- manages embeddable applet windows + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.MalformedURLException; +import java.nio.charset.Charset; +import java.util.HashMap; + + +/** + * PluginAppletViewer communicates through pipes with a web browser + * plugin. A PluginAppletViewer manages applet windows that may be + * embedded into web pages. + */ +class PluginAppletViewer +{ + // A mapping of instance IDs to PluginAppletWindows. + static HashMap appletWindows = new HashMap (); + + private static BufferedReader pluginInputStream; + private static BufferedWriter pluginOutputStream; + + static void start(InputStream inputStream, OutputStream outputStream) + throws MalformedURLException, IOException + { + // Set up input and output pipes. Use UTF-8 encoding. + pluginInputStream = + new BufferedReader(new InputStreamReader(inputStream, + Charset.forName("UTF-8"))); + pluginOutputStream = + new BufferedWriter(new OutputStreamWriter(outputStream, + Charset.forName("UTF-8"))); + + write("running"); + + // Read first message. + String message = read(); + + PluginAppletWindow currentWindow = null; + + while (true) + { + if (message.startsWith("instance")) + { + // Read applet instance identifier. + String key = message.substring(9); + + if (appletWindows.get(key) == null) + appletWindows.put(key, new PluginAppletWindow()); + + currentWindow = (PluginAppletWindow) appletWindows.get(key); + } + else if (message.startsWith("tag")) + { + int pos = message.indexOf(' ', 4); + String documentbase = message.substring(4, pos); + String tag = message.substring(pos + 1); + currentWindow.setParser(tag, documentbase); + } + else if (message.startsWith("handle")) + { + long handle = Long.parseLong(message.substring(7)); + + currentWindow.setHandle(handle); + } + else if (message.startsWith("width")) + { + int width = Integer.parseInt(message.substring(6)); + + currentWindow.setSize(width, currentWindow.getHeight()); + } + else if (message.startsWith("height")) + { + int height = Integer.parseInt(message.substring(7)); + + currentWindow.setSize(currentWindow.getWidth(), height); + } + else if (message.startsWith("destroy")) + { + appletWindows.remove(currentWindow); + currentWindow.dispose(); + } + + // Read next message. + message = read(); + } + } + + /** + * Write string to plugin. + * + * @param message the message to write + * + * @exception IOException if an error occurs + */ + static void write(String message) throws IOException + { + pluginOutputStream.write(message, 0, message.length()); + pluginOutputStream.newLine(); + pluginOutputStream.flush(); + + System.err.println + (" " + Messages.getString("PluginAppletViewer.AppletViewerWrote") + + message); + } + + /** + * Read string from plugin. + * + * @return the read string + * + * @exception IOException if an error occurs + */ + static String read() throws IOException + { + String message = pluginInputStream.readLine(); + + System.err.println + (" " + Messages.getString("PluginAppletViewer.AppletViewerRead") + + message); + + if (message == null || message.equals("shutdown")) + { + // Close input/output channels to plugin. + pluginInputStream.close(); + pluginOutputStream.close(); + + System.err.println + (Messages.getString("PluginAppletViewer.AppletViewerExiting")); + + System.exit(0); + } + + return message; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletWindow.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletWindow.java new file mode 100644 index 000000000..bc44a73d8 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletWindow.java @@ -0,0 +1,448 @@ +/* PluginAppletWindow.java -- an embeddable applet window + Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import gnu.java.awt.EmbeddedWindow; + +import java.applet.Applet; +import java.applet.AppletContext; +import java.awt.Dimension; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.io.IOException; +import java.io.StringReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; + + +class PluginAppletWindow + extends EmbeddedWindow + implements ContainerListener, ComponentListener, MouseListener, + MouseMotionListener, InputMethodListener, HierarchyListener, + HierarchyBoundsListener +{ + + // This class implements various listeners because the author of an applet + // may attach listeners to it, unaware of the applet's parent (this class). + // So, we must pass all listener events on this plugin applet window to the + // actual applet. + + private static HashMap contexts = new HashMap(); + private Applet applet; + private TagParser parser; + private AppletTag tag; + + PluginAppletWindow() + { + super(); + addContainerListener(this); + addComponentListener(this); + addMouseListener(this); + addMouseMotionListener(this); + addInputMethodListener(this); + addHierarchyListener(this); + addHierarchyBoundsListener(this); + } + + /////////////////////////////////// + /// ContainerListener Methods ///// + /////////////////////////////////// + + /** + * This method is called when a component is added to the container. + * + * @param event the <code>ContainerEvent</code> indicating component + * addition + */ + public void componentAdded(ContainerEvent event) + { + if (applet != null) + { + ContainerListener[] l = applet.getContainerListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentAdded(event); + } + } + + /** + * This method is called when a component is removed from the container. + * + * @param event the <code>ContainerEvent</code> indicating component removal + */ + public void componentRemoved(ContainerEvent event) + { + if (applet != null) + { + ContainerListener[] l = applet.getContainerListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentRemoved(event); + } + } + + /////////////////////////////////// + /// ComponentListener Methods ///// + /////////////////////////////////// + + /** + * This method is called when the component is resized. + * + * @param event the <code>ComponentEvent</code> indicating the resize + */ + public void componentResized(ComponentEvent event) + { + if (applet != null) + { + ComponentListener[] l = applet.getComponentListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentResized(event); + } + } + + /** + * This method is called when the component is moved. + * + * @param event the <code>ComponentEvent</code> indicating the move + */ + public void componentMoved(ComponentEvent event) + { + if (applet != null) + { + ComponentListener[] l = applet.getComponentListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentMoved(event); + } + } + + /** + * This method is called when the component is made visible. + * + * @param event the <code>ComponentEvent</code> indicating the visibility + */ + public void componentShown(ComponentEvent event) + { + if (applet != null) + { + ComponentListener[] l = applet.getComponentListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentShown(event); + } + } + + /** + * This method is called when the component is hidden. + * + * @param event the <code>ComponentEvent</code> indicating the visibility + */ + public void componentHidden(ComponentEvent event) + { + if (applet != null) + { + ComponentListener[] l = applet.getComponentListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentHidden(event); + } + } + + /////////////////////////////////// + ////// MouseListener Methods ////// + /////////////////////////////////// + + /** + * This method is called when the mouse is clicked (pressed and released + * in short succession) on a component. + * + * @param event the <code>MouseEvent</code> indicating the click + */ + public void mouseClicked(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseClicked(event); + } + } + + /** + * This method is called when the mouse is pressed over a component. + * + * @param event the <code>MouseEvent</code> for the press + */ + public void mousePressed(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mousePressed(event); + } + } + + /** + * This method is called when the mouse is released over a component. + * + * @param event the <code>MouseEvent</code> for the release + */ + public void mouseReleased(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseReleased(event); + } + } + + /** + * This method is called when the mouse enters a component. + * + * @param event the <code>MouseEvent</code> for the entry + */ + public void mouseEntered(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseEntered(event); + } + } + + /** + * This method is called when the mouse exits a component. + * + * @param event the <code>MouseEvent</code> for the exit + */ + public void mouseExited(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseExited(event); + } + } + + /////////////////////////////////// + /// MouseMotionListener Methods /// + /////////////////////////////////// + + /** + * This method is called when the mouse is moved over a component + * while a button has been pressed. + * + * @param event the <code>MouseEvent</code> indicating the motion + */ + public void mouseDragged(MouseEvent event) + { + if (applet != null) + { + MouseMotionListener[] l = applet.getMouseMotionListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseDragged(event); + } + } + + /** + * This method is called when the mouse is moved over a component + * while no button is pressed. + * + * @param event the <code>MouseEvent</code> indicating the motion + */ + public void mouseMoved(MouseEvent event) + { + if (applet != null) + { + MouseMotionListener[] l = applet.getMouseMotionListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseMoved(event); + } + } + + /////////////////////////////////// + /// InputMethodListener Methods /// + /////////////////////////////////// + + /** + * This method is called when the text is changed. + * + * @param event the <code>InputMethodEvent</code> indicating the text change + */ + public void inputMethodTextChanged(InputMethodEvent event) + { + if (applet != null) + { + InputMethodListener[] l = applet.getInputMethodListeners(); + for (int i = 0; i < l.length; i++) + l[i].inputMethodTextChanged(event); + } + } + + /** + * This method is called when the cursor position within the text is changed. + * + * @param event the <code>InputMethodEvent</code> indicating the change + */ + public void caretPositionChanged(InputMethodEvent event) + { + if (applet != null) + { + InputMethodListener[] l = applet.getInputMethodListeners(); + for (int i = 0; i < l.length; i++) + l[i].caretPositionChanged(event); + } + } + + /////////////////////////////////// + //// HierarchyListener Methods //// + /////////////////////////////////// + + /** + * Called when the hierarchy of this component changes. Use + * <code>getChangeFlags()</code> on the event to see what exactly changed. + * + * @param event the event describing the change + */ + public void hierarchyChanged(HierarchyEvent event) + { + if (applet != null) + { + HierarchyListener[] l = applet.getHierarchyListeners(); + for (int i = 0; i < l.length; i++) + l[i].hierarchyChanged(event); + } + } + + ///////////////////////////////////////// + //// HierarchyBoundsListener Methods //// + ///////////////////////////////////////// + + /** + * Called when an ancestor component of the source is moved. + * + * @param e the event describing the ancestor's motion + */ + public void ancestorMoved(HierarchyEvent e) + { + if (applet != null) + { + HierarchyBoundsListener[] l = applet.getHierarchyBoundsListeners(); + for (int i = 0; i < l.length; i++) + l[i].ancestorMoved(e); + } + } + + /** + * Called when an ancestor component is resized. + * + * @param e the event describing the ancestor's resizing + */ + public void ancestorResized(HierarchyEvent e) + { + if (applet != null) + { + HierarchyBoundsListener[] l = applet.getHierarchyBoundsListeners(); + for (int i = 0; i < l.length; i++) + l[i].ancestorResized(e); + } + } + + void setParser(String tag, String documentbase) throws MalformedURLException, IOException + { + URL documentbaseURL = TagParser.getLocationToURL(documentbase); + StringReader in = new StringReader(tag); + this.parser = new TagParser(in, documentbaseURL); + } + + // ///////////////////////////////// + // //// EmbeddedWindow Method ////// + // ///////////////////////////////// + + /** + * Set the native handle of the window system to embed the window in. + * + * @param handle the native handle. + */ + public void setHandle(long handle) + { + super.setHandle(handle); + addNotify(); + + ArrayList l = parser.parseAppletTags(); + int s = l.size(); + + for (int i = 0; i < s; i++) + { + tag = (AppletTag) l.get(i); + applet = Main.createApplet(tag); + + if (contexts.get(tag.getCodeBase()) == null) + contexts.put(tag.getCodeBase(), new PluginAppletContext()); + + add(applet); + + AppletContext context = (AppletContext) contexts.get(tag.getCodeBase()); + ((PluginAppletContext) context).addApplet(applet); + + applet.setStub(new CommonAppletStub(tag, context, applet)); + Dimension size = getSize(); + if (size.width == 0 || size.height == 0) + size = tag.getSize(); + applet.setSize(size); + + // Initialize the applet before showing this window so that + // the applet doesn't receive events before it has been + // initialized. + applet.init(); + applet.start(); + setVisible(true); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletContext.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletContext.java new file mode 100644 index 000000000..48468f84d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletContext.java @@ -0,0 +1,76 @@ +/* StandaloneAppletContext.java -- an applet's context within the + standalone viewer + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.net.URL; +import java.util.Iterator; +import java.util.List; + + +/** + * StandaloneAppletContext represents the context within a webpage of a + * group of applets that all share the same codebase. + */ +class StandaloneAppletContext extends CommonAppletContext +{ + private List appletWindows; + + StandaloneAppletContext(List appletWindows) + { + this.appletWindows = appletWindows; + } + + public void showDocument(URL url, String target) + { + System.err.println + (Messages.getString("StandaloneAppletContext.ShowDocumentError")); + } + + // In standalone mode, there are potentially several windows, each + // with its own status bar. In plugin mode, all the applets in the + // same context (on the same page) share the browser's status bar. + // The best way to simulate the plugin mode behaviour in standalone + // mode is to show the same status on each window's status bar. + public void showStatus(String status) + { + Iterator window = appletWindows.iterator(); + while (window.hasNext()) + ((StandaloneAppletWindow) window.next()).showStatus(status); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletViewer.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletViewer.java new file mode 100644 index 000000000..a59b03bfd --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletViewer.java @@ -0,0 +1,148 @@ +/* StandaloneAppletViewer.java -- a standalone viewer for Java applets + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.awt.Dimension; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; + + +/** + * StandaloneAppletViewer displays an applet in its own Frame. Most + * of the context that is available to an applet within a webpage is + * available to it in StandaloneAppletViewer. + */ +class StandaloneAppletViewer extends Main +{ + static ArrayList appletTags = new ArrayList(); + static ArrayList appletWindows = new ArrayList(); + + StandaloneAppletViewer(String[] urls) + throws MalformedURLException, IOException + { + // Handle each file specified on the command line. + for (int i = 0; i < urls.length; i++) + { + TagParser parser = new TagParser(urls[i]); + appletTags.addAll(parser.parseAppletTags()); + } + + printTags(); + createWindows(); + } + + StandaloneAppletViewer(String code, String codebase, String archives, + List parameters, Dimension dimensions) + throws IOException + { + if (!(code.equals("") || code.endsWith(".class"))) + { + System.err.println + (Messages.getString("StandaloneAppletViewer.CodeOptionError")); + System.exit(1); + } + + String tagString = + "<EMBED" + + " CODE=\"" + code + "\"" + + " WIDTH=" + dimensions.width + + " HEIGHT=" + dimensions.height + + " CODEBASE=\"" + codebase + "\"" + + " ARCHIVE=\"" + archives + "\">"; + + // Handle parameters. + Iterator pairs = parameters.iterator(); + while (pairs.hasNext()) + { + StringTokenizer paramTokenizer = + new StringTokenizer((String) pairs.next(), ","); + tagString += + "<PARAM NAME=" + paramTokenizer.nextToken().trim() + " VALUE=" + + paramTokenizer.nextToken().trim() + ">"; + } + + tagString += "</EMBED>"; + + StringReader reader = new StringReader(tagString); + String path = System.getProperty("user.dir") + File.separator; + TagParser parser = new TagParser(reader, + new URL("file", "", path)); + appletTags.addAll(parser.parseAppletTags()); + + printTags(); + createWindows(); + } + + void printTags() + { + if (verbose) + { + System.out.println + (Messages.getString("StandaloneAppletViewer.ParsedAppletTags")); + + for (int i = 0; i < appletTags.size(); i++) + { + AppletTag tag = (AppletTag) appletTags.get(i); + + System.out.println + (" " + Messages.getString("StandaloneAppletViewer.Tag") + + " " + i + ":"); + System.out.println(tag); + } + } + } + + void createWindows() + { + for (int i = 0; i < appletTags.size(); i++) + { + AppletTag tag = (AppletTag) appletTags.get(i); + + // Create a StandaloneAppletWindow and add it to the + // appletWindows list. + new StandaloneAppletWindow(tag, appletWindows); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletWindow.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletWindow.java new file mode 100644 index 000000000..64d3007f3 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletWindow.java @@ -0,0 +1,560 @@ +/* StandaloneAppletWindow.java -- an applet frame + Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import java.applet.Applet; +import java.applet.AppletContext; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.HashMap; +import java.util.List; + +class StandaloneAppletWindow + extends Frame + implements ActionListener, ContainerListener, ComponentListener, + MouseListener, MouseMotionListener, InputMethodListener, HierarchyListener, + HierarchyBoundsListener +{ + + // This class implements various listeners because the author of an applet + // may attach listeners to it, unaware of the applet's parent (this class). + // So, we must pass all listener events on this plugin applet window to the + // actual applet. + + private static int testWindowCount; + private static HashMap contexts = new HashMap(); + private Applet applet; + private Label status = new Label(); + + private MenuItem restartItem; + private MenuItem reloadItem; + private MenuItem cancelItem; + private MenuItem saveItem; + private MenuItem startItem; + private MenuItem cloneItem; + private MenuItem tagItem; + private MenuItem infoItem; + private MenuItem editItem; + private MenuItem encodingItem; + private MenuItem printItem; + private MenuItem propertiesItem; + private MenuItem closeItem; + private MenuItem quitItem; + + StandaloneAppletWindow(AppletTag tag, List appletWindows) + { + appletWindows.add(this); + applet = Main.createApplet(tag); + + if (contexts.get(tag.codebase) == null) + contexts.put(tag.codebase, new StandaloneAppletContext(appletWindows)); + + setLayout(new BorderLayout()); + add(applet, BorderLayout.CENTER); + add(status, BorderLayout.SOUTH); + + addWindowListener(new WindowAdapter() + { + public void windowClosing(WindowEvent event) + { + applet.stop(); + StandaloneAppletWindow.this.hide(); + System.exit(0); + } + }); + + addContainerListener(this); + addComponentListener(this); + addMouseListener(this); + addMouseMotionListener(this); + addInputMethodListener(this); + addHierarchyListener(this); + addHierarchyBoundsListener(this); + + restartItem = new MenuItem(Messages.getString("StandaloneAppletWindow.MenuRestart")); + restartItem.setEnabled(false); + restartItem.addActionListener(this); + reloadItem = new MenuItem(Messages.getString("StandaloneAppletWindow.MenuReload")); + reloadItem.setEnabled(false); + reloadItem.addActionListener(this); + cancelItem = new MenuItem(Messages.getString("StandaloneAppletWindow.MenuCancel")); + cancelItem.setEnabled(false); + cancelItem.addActionListener(this); + saveItem = new MenuItem(Messages.getString("StandaloneAppletWindow.MenuSave")); + saveItem.setEnabled(false); + saveItem.addActionListener(this); + startItem = new MenuItem(Messages.getString("StandaloneAppletWindow.MenuStart")); + startItem.setEnabled(false); + startItem.addActionListener(this); + cloneItem = new MenuItem(Messages.getString("StandaloneAppletWindow.MenuClone")); + cloneItem.setEnabled(false); + cloneItem.addActionListener(this); + closeItem = new MenuItem(Messages.getString("StandaloneAppletWindow.MenuClose")); + closeItem.setEnabled(false); + closeItem.addActionListener(this); + tagItem = + new MenuItem(Messages.getString("StandaloneAppletWindow.MenuTag")); + tagItem.setEnabled(false); + tagItem.addActionListener(this); + infoItem = + new MenuItem(Messages.getString("StandaloneAppletWindow.MenuInfo")); + infoItem.setEnabled(false); + infoItem.addActionListener(this); + editItem = + new MenuItem(Messages.getString("StandaloneAppletWindow.MenuEdit")); + editItem.setEnabled(false); + editItem.addActionListener(this); + editItem.setEnabled(false); + encodingItem = + new MenuItem(Messages.getString("StandaloneAppletWindow.MenuEncoding")); + encodingItem.setEnabled(false); + encodingItem.addActionListener(this); + printItem = + new MenuItem(Messages.getString("StandaloneAppletWindow.MenuPrint")); + printItem.setEnabled(false); + printItem.addActionListener(this); + propertiesItem = + new MenuItem(Messages.getString("StandaloneAppletWindow.MenuProperties")); + propertiesItem.setEnabled(false); + propertiesItem.addActionListener(this); + quitItem = new MenuItem(Messages.getString("StandaloneAppletWindow.MenuQuit")); + quitItem.addActionListener(this); + + MenuBar menuBar = new MenuBar(); + Menu menuApplet = new Menu(Messages.getString("StandaloneAppletWindow.MenuTitle")); + menuBar.add(menuApplet); + menuApplet.add(restartItem); + menuApplet.add(reloadItem); + menuApplet.add(cancelItem); + menuApplet.add(saveItem); + menuApplet.add(startItem); + menuApplet.add(cloneItem); + menuApplet.addSeparator(); + menuApplet.add(tagItem); + menuApplet.add(infoItem); + menuApplet.add(editItem); + menuApplet.add(encodingItem); + menuApplet.addSeparator(); + menuApplet.add(printItem); + menuApplet.addSeparator(); + menuApplet.add(propertiesItem); + menuApplet.addSeparator(); + menuApplet.add(closeItem); + menuApplet.add(quitItem); + setMenuBar(menuBar); + setTitle(Messages.getString("StandaloneAppletWindow.WindowTitle") + + " " + tag.code); + + AppletContext context = (AppletContext) contexts.get(tag.codebase); + ((StandaloneAppletContext) context).addApplet(applet); + + applet.setStub(new CommonAppletStub(tag, context, applet)); + + // Create the frame's peer. Otherwise getPreferredSize will read + // its insets as 0. + addNotify(); + Insets i = getInsets(); + Dimension size = tag.getSize(); + setSize(i.left + size.width + i.right, + i.top + size.height + status.getPreferredSize().height + + i.bottom); + applet.setSize(size); + + // Initialize the applet before showing this window so that the + // applet doesn't receive events before it has been initialized. + applet.init(); + applet.start(); + setVisible(true); + } + + private void closeWindow() + { + applet.stop(); + StandaloneAppletViewer.appletWindows.remove(this); + StandaloneAppletWindow.this.hide(); + } + + public void actionPerformed(ActionEvent e) + { + if (e.getSource() == quitItem) + { + closeWindow(); + System.exit(0); + } + else if (e.getSource() == closeItem) + { + // Close current window. + closeWindow(); + + // Exit if there are other windows left. + if (StandaloneAppletViewer.appletWindows.isEmpty()) + System.exit(0); + } + } + + void showStatus(String status) + { + this.status.setText(status); + } + + + /////////////////////////////////// + /// ContainerListener Methods ///// + /////////////////////////////////// + + /** + * This method is called when a component is added to the container. + * + * @param event the <code>ContainerEvent</code> indicating component + * addition + */ + public void componentAdded(ContainerEvent event) + { + if (applet != null) + { + ContainerListener[] l = applet.getContainerListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentAdded(event); + } + } + + /** + * This method is called when a component is removed from the container. + * + * @param event the <code>ContainerEvent</code> indicating component removal + */ + public void componentRemoved(ContainerEvent event) + { + if (applet != null) + { + ContainerListener[] l = applet.getContainerListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentRemoved(event); + } + } + + /////////////////////////////////// + /// ComponentListener Methods ///// + /////////////////////////////////// + + /** + * This method is called when the component is resized. + * + * @param event the <code>ComponentEvent</code> indicating the resize + */ + public void componentResized(ComponentEvent event) + { + if (applet != null) + { + ComponentListener[] l = applet.getComponentListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentResized(event); + } + } + + /** + * This method is called when the component is moved. + * + * @param event the <code>ComponentEvent</code> indicating the move + */ + public void componentMoved(ComponentEvent event) + { + if (applet != null) + { + ComponentListener[] l = applet.getComponentListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentMoved(event); + } + } + + /** + * This method is called when the component is made visible. + * + * @param event the <code>ComponentEvent</code> indicating the visibility + */ + public void componentShown(ComponentEvent event) + { + if (applet != null) + { + ComponentListener[] l = applet.getComponentListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentShown(event); + } + } + + /** + * This method is called when the component is hidden. + * + * @param event the <code>ComponentEvent</code> indicating the visibility + */ + public void componentHidden(ComponentEvent event) + { + if (applet != null) + { + ComponentListener[] l = applet.getComponentListeners(); + for (int i = 0; i < l.length; i++) + l[i].componentHidden(event); + } + } + + /////////////////////////////////// + ////// MouseListener Methods ////// + /////////////////////////////////// + + /** + * This method is called when the mouse is clicked (pressed and released + * in short succession) on a component. + * + * @param event the <code>MouseEvent</code> indicating the click + */ + public void mouseClicked(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseClicked(event); + } + } + + /** + * This method is called when the mouse is pressed over a component. + * + * @param event the <code>MouseEvent</code> for the press + */ + public void mousePressed(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mousePressed(event); + } + } + + /** + * This method is called when the mouse is released over a component. + * + * @param event the <code>MouseEvent</code> for the release + */ + public void mouseReleased(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseReleased(event); + } + } + + /** + * This method is called when the mouse enters a component. + * + * @param event the <code>MouseEvent</code> for the entry + */ + public void mouseEntered(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseEntered(event); + } + } + + /** + * This method is called when the mouse exits a component. + * + * @param event the <code>MouseEvent</code> for the exit + */ + public void mouseExited(MouseEvent event) + { + if (applet != null) + { + MouseListener[] l = applet.getMouseListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseExited(event); + } + } + + /////////////////////////////////// + /// MouseMotionListener Methods /// + /////////////////////////////////// + + /** + * This method is called when the mouse is moved over a component + * while a button has been pressed. + * + * @param event the <code>MouseEvent</code> indicating the motion + */ + public void mouseDragged(MouseEvent event) + { + if (applet != null) + { + MouseMotionListener[] l = applet.getMouseMotionListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseDragged(event); + } + } + + /** + * This method is called when the mouse is moved over a component + * while no button is pressed. + * + * @param event the <code>MouseEvent</code> indicating the motion + */ + public void mouseMoved(MouseEvent event) + { + if (applet != null) + { + MouseMotionListener[] l = applet.getMouseMotionListeners(); + for (int i = 0; i < l.length; i++) + l[i].mouseMoved(event); + } + } + + /////////////////////////////////// + /// InputMethodListener Methods /// + /////////////////////////////////// + + /** + * This method is called when the text is changed. + * + * @param event the <code>InputMethodEvent</code> indicating the text change + */ + public void inputMethodTextChanged(InputMethodEvent event) + { + if (applet != null) + { + InputMethodListener[] l = applet.getInputMethodListeners(); + for (int i = 0; i < l.length; i++) + l[i].inputMethodTextChanged(event); + } + } + + /** + * This method is called when the cursor position within the text is changed. + * + * @param event the <code>InputMethodEvent</code> indicating the change + */ + public void caretPositionChanged(InputMethodEvent event) + { + if (applet != null) + { + InputMethodListener[] l = applet.getInputMethodListeners(); + for (int i = 0; i < l.length; i++) + l[i].caretPositionChanged(event); + } + } + + /////////////////////////////////// + //// HierarchyListener Methods //// + /////////////////////////////////// + + /** + * Called when the hierarchy of this component changes. Use + * <code>getChangeFlags()</code> on the event to see what exactly changed. + * + * @param event the event describing the change + */ + public void hierarchyChanged(HierarchyEvent event) + { + if (applet != null) + { + HierarchyListener[] l = applet.getHierarchyListeners(); + for (int i = 0; i < l.length; i++) + l[i].hierarchyChanged(event); + } + } + + ///////////////////////////////////////// + //// HierarchyBoundsListener Methods //// + ///////////////////////////////////////// + + /** + * Called when an ancestor component of the source is moved. + * + * @param e the event describing the ancestor's motion + */ + public void ancestorMoved(HierarchyEvent e) + { + if (applet != null) + { + HierarchyBoundsListener[] l = applet.getHierarchyBoundsListeners(); + for (int i = 0; i < l.length; i++) + l[i].ancestorMoved(e); + } + } + + /** + * Called when an ancestor component is resized. + * + * @param e the event describing the ancestor's resizing + */ + public void ancestorResized(HierarchyEvent e) + { + if (applet != null) + { + HierarchyBoundsListener[] l = applet.getHierarchyBoundsListeners(); + for (int i = 0; i < l.length; i++) + l[i].ancestorResized(e); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/appletviewer/TagParser.java b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/TagParser.java new file mode 100644 index 000000000..711b26ee5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/appletviewer/TagParser.java @@ -0,0 +1,356 @@ +/* TagParser.java -- a parser for applet tags + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.appletviewer; + +import gnu.javax.swing.text.html.parser.HTML_401F; + +import gnu.xml.dom.DomNode; +import gnu.xml.dom.html2.DomHTMLAppletElement; +import gnu.xml.dom.html2.DomHTMLDocument; +import gnu.xml.dom.html2.DomHTMLEmbedElement; +import gnu.xml.dom.html2.DomHTMLObjectElement; +import gnu.xml.dom.html2.DomHTMLParamElement; +import gnu.xml.dom.html2.DomHTMLParser; + +import java.io.File; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.Reader; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.util.ArrayList; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.w3c.dom.NodeList; + + +public class TagParser +{ + + /** + * Parsed document. + */ + DomHTMLDocument document; + + /** + * The document base of this applet. + */ + URL documentbase; + + /** + * The document base of all the applets. + */ + static URL db; + + /** + * The tags in the document. + */ + Vector tags = new Vector(); + + /** + * Default constructor. + */ + TagParser() + { + // Do nothing. + } + + /** + * Constructs and parses document using the given location. + * + * @param location - location of applet + */ + TagParser(String location) throws IOException + { + documentbase = getLocationToURL(location); + db = documentbase; + InputStreamReader in = new InputStreamReader(documentbase.openStream()); + document = (DomHTMLDocument) (new DomHTMLParser(HTML_401F.getInstance()).parseDocument(in)); + } + + /** + * Constructs and parses document. + * + * @param in - Reader to parse document from. + * @param documentBase - the URL of the applet + * @throws IOException - is thrown if any IO error occurs. + */ + TagParser(Reader in, URL documentBase) throws IOException + { + documentbase = documentBase; + db = documentbase; + document = (DomHTMLDocument) (new DomHTMLParser(HTML_401F.getInstance()).parseDocument(in)); + } + + /** + * Parses all applet tags in document. + * + * @return a list of AppletTag objects representing the applet tags + * in document + */ + ArrayList parseAppletTags() + { + ArrayList allTags = new ArrayList(); + if (document == null) + return null; + + recurseDocument(document.getChildNodes()); + + int sz = tags.size(); + for (int j = 0; j < sz; j++) + { + Object curr = tags.get(j); + // Order of checking is important here. + // Must check embed element before applet element + // because DomHTMLEmbedElement extends DomHTMLAppletElement + AppletTag a = null; + if (curr instanceof DomHTMLEmbedElement) + a = new AppletTag((DomHTMLEmbedElement) curr); + else if (curr instanceof DomHTMLAppletElement) + a = new AppletTag((DomHTMLAppletElement) curr); + else if (curr instanceof DomHTMLObjectElement) + a = new AppletTag((DomHTMLObjectElement) curr); + a.documentbase = documentbase; + allTags.add(a); + } + + return allTags; + } + + /** + * Recurses the document in search for the appropriate tags. + * + * @param list - the Node list. + */ + private void recurseDocument(NodeList list) + { + // Recurse and store all APPLET, OBJECT and EMBED tags. + int length = list.getLength(); + for (int i = 0; i < length; i++) + { + DomNode curr = (DomNode) list.item(i); + if ((curr instanceof DomHTMLEmbedElement) || + (curr instanceof DomHTMLAppletElement) || + (curr instanceof DomHTMLObjectElement)) + tags.add(curr); + recurseDocument(curr.getChildNodes()); + } + } + + /** + * Parses the param elements for a given node. + * + * @param node - the node element to parse. + */ + static void parseParams(DomNode node, AppletTag t) + { + boolean ja = false; + boolean jb = false; + boolean jc = false; + NodeList l = node.getChildNodes(); + int size = l.getLength(); + + if (size != 0) + for (int i = 0; i < size; i++) + { + Object c = l.item(i); + if (! (c instanceof DomHTMLParamElement)) + continue; + DomHTMLParamElement curr = (DomHTMLParamElement) c; + String key = curr.getName(); + String val = curr.getValue(); + + if (key.equals("java_code")) + { + jc = true; + t.code = val; + } + else if (key.equals("java_codebase")) + { + jb = true; + t.codebase = val; + } + else if (!jc && key.equals("code")) + t.code = val; + else if (!jc && key.equals("classid")) + { + int x = val.indexOf(":"); + if (x != -1) + val = val.substring(x + 1); + t.code = val; + } + else if (!jb && key.equals("codebase")) + t.codebase = val; + else if (key.equals("java_archive")) + { + ja = true; + t.archives = parseArchives(val, t); + val = t.archives.toString(); + } + else if (!ja && key.equals("archive")) + { + t.archives = parseArchives(val, t); + val = t.archives.toString(); + } + val = unescapeString(val); + t.parameters.put(key.toLowerCase(), val); + } + } + + /** + * This method does the same thing as the g_strcompress function in glib. + * + * @param value + * @return value in its original one-byte equivalence. + */ + private static String unescapeString(String value) + { + String unescVal = ""; + for (int i = 0; i < value.length(); i++) + { + if (i == value.length() - 1) + { + unescVal = unescVal.concat(value.substring(i)); + break; + } + if (value.charAt(i) == '\\') + { + switch (value.charAt(i + 1)) + { + case 'b': + unescVal = unescVal.concat("\b"); + break; + case 'f': + unescVal = unescVal.concat("\f"); + break; + case 'n': + unescVal = unescVal.concat("\n"); + break; + case 'r': + unescVal = unescVal.concat("\r"); + break; + case 't': + unescVal = unescVal.concat("\t"); + break; + case '\\': + unescVal = unescVal.concat("\\"); + break; + case '\"': + unescVal = unescVal.concat("\""); + break; + default: + unescVal = unescVal.concat("\\"); + unescVal = unescVal.concat(value.substring(i + 1, i + 2)); + break; + } + i++; + } + else + unescVal = unescVal.concat(value.substring(i, i + 1)); + } + return unescVal; + } + + /** + * Parses the archive string and returns a list. + * + * @param arcs the list of archives (comma-separated) in a String. + */ + static ArrayList parseArchives(String arcs, AppletTag t) + { + try + { + ArrayList list = new ArrayList(); + + StringTokenizer tagTokenizer = new StringTokenizer(arcs, ","); + while (tagTokenizer.hasMoreTokens()) + list.add(t.prependCodeBase(tagTokenizer.nextToken().trim())); + + return list; + } + catch (MalformedURLException e) + { + } + return null; + } + + /** + * Gets the location to the URL, given a location. + * + * @param location - the given location. + * @return the URL. + */ + static URL getLocationToURL(String location) throws IOException + { + URL tmpDocumentBase = null; + + try + { + // Try parsing location as a URL. + tmpDocumentBase = new URL(location); + + // If no file was specified in the URL the assume the user + // meant the root page. + String f = tmpDocumentBase.getFile(); + if (f.indexOf(".") == -1 && !f.endsWith(File.separator)) + if (new File(tmpDocumentBase.getFile()).isDirectory()) + tmpDocumentBase = new URL(location.concat(File.separator)); + } + catch (MalformedURLException e) + { + // location is not a URL. See if it is an HTML file. + String path; + + if (location.startsWith(File.separator)) + path = new File(location).getCanonicalPath(); + else + path = new File(System.getProperty("user.dir") + File.separator + + location).getCanonicalPath(); + + tmpDocumentBase = new URL("file", "", path); + + if (new File(tmpDocumentBase.getFile()).isDirectory()) + tmpDocumentBase = new URL("file", "", path + File.separator); + } + + return tmpDocumentBase; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/common/CallbackUtil.java b/libjava/classpath/tools/gnu/classpath/tools/common/CallbackUtil.java new file mode 100644 index 000000000..838d7403a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/common/CallbackUtil.java @@ -0,0 +1,145 @@ +/* CallbackUtil.java -- Callback related utilities + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.common; + +import gnu.javax.security.auth.callback.ConsoleCallbackHandler; + +import java.security.Provider; +import java.security.Security; +import java.util.logging.Logger; + +import javax.security.auth.callback.CallbackHandler; + +/** + * A <i>Helper</i> class containing general purpose utlity methods dealing with + * callback handlers and their <i>Security Provider</i>. + */ +public abstract class CallbackUtil +{ + private static final Logger log = Logger.getLogger(CallbackUtil.class.getName()); + + // default 0-arguments constructor + + // Class methods + // -------------------------------------------------------------------------- + + /** + * Return an implementation of the {@link CallbackHandler}, from any + * {@link Provider}, capable of handling callbacks through the <i>console</i>; + * i.e. <code>System.in</code> and <code>System.out</code>. + * <p> + * If no <i>Security Provider</i> for this type of callback was found, this + * method returns the default GNU implementation. + * + * @return a console {@link CallbackHandler} implementation. + */ + public static final CallbackHandler getConsoleHandler() + { + CallbackHandler result = getHandler("Console"); + if (result == null) + { + log.fine("No console callback handler found. Will use ours"); + result = new ConsoleCallbackHandler(); + } + return result; + } + + /** + * Return a {@link CallbackHandler}, of a designated type, for interacting + * with the user. + * <p> + * This method first finds all currently installed <i>Security Providers</i> + * capable of providing such service and then in turn attempts to instantiate + * the handler from those providers. As soon as one provider returns a non- + * null instance of the callback handler, the search stops and that instance + * is returned. + * + * @return a {@link CallbackHandler} of the designated type, or + * <code>null</code> if no provider was found for theis type of + * callback. + */ + private static final CallbackHandler getHandler(String handlerType) + { + log.entering(CallbackUtil.class.getName(), "getHandler", handlerType); + + CallbackHandler result = null; + String service = "CallbackHandler." + handlerType; + Provider[] providers = Security.getProviders(service); + if (providers != null) + for (int i = 0; i < providers.length; i++) + { + Provider p = providers[i]; + String className = p.getProperty(service); + if (className != null) + try + { + result = (CallbackHandler) Class.forName(className.trim()).newInstance(); + } + catch (InstantiationException x) + { + log.fine("InstantiationException while creating [" + + className + "] from provider [" + p.getName() + + "]. Ignore"); + } + catch (IllegalAccessException x) + { + log.fine("IllegalAccessException while creating [" + + className + "] from provider [" + p.getName() + + "]. Ignore"); + } + catch (ClassNotFoundException x) + { + log.fine("ClassNotFoundException while creating [" + + className + "] from provider [" + p.getName() + + "]. Ignore"); + } + + if (result != null) + { + + log.fine("Will use [" + result.getClass().getName() + + "] from [" + p.getName() + "]"); + break; + } + } + + log.exiting(CallbackUtil.class.getName(), "getHandler", result); + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/common/ClasspathToolParser.java b/libjava/classpath/tools/gnu/classpath/tools/common/ClasspathToolParser.java new file mode 100644 index 000000000..d740646e5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/common/ClasspathToolParser.java @@ -0,0 +1,239 @@ +/* ClasspathToolParser.java -- Parser subclass for classpath tools + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.common; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.Reader; +import java.text.MessageFormat; +import java.util.ArrayList; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.getopt.FileArgumentCallback; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +/** + * This is like the Parser class, but is specialized for use by + * tools distributed with GNU Classpath. In particular it automatically + * computes the version string using the program's name. + */ +public class ClasspathToolParser + extends Parser +{ + private static String getVersionString(String programName) + { + String fmt = (Messages.getString("ClasspathToolParser.VersionFormat")); //$NON-NLS-1$ + return MessageFormat.format(fmt, + new Object[] + { + programName, + Configuration.CLASSPATH_VERSION + }); + } + + public ClasspathToolParser(String programName) + { + this(programName, false); + } + + public ClasspathToolParser(String programName, boolean longOnly) + { + super(programName, getVersionString(programName), longOnly); + addFinal(new Option('J', + Messages.getString("ClasspathToolParser.JArgument"),//$NON-NLS-1$ + Messages.getString("ClasspathToolParser.JName"), //$NON-NLS-1$ + true) + { + public void parsed(String argument) throws OptionException + { + // -J should be handled by the wrapper binary. + // We add it here so that it shows up in the --help output. + } + }); + } + + public void parse(String[] inArgs, FileArgumentCallback files, + boolean handleFileLists) + { + FileArgumentCallback cb; + + if (handleFileLists) + cb = new AtFileArgumentCallback(files); + else + cb = files; + + parse(inArgs, cb); + } + + public String[] parse(String[] inArgs, boolean handleFileLists) + { + final ArrayList<String> fileResult = new ArrayList<String>(); + + final FileArgumentCallback cb = new FileArgumentCallback() + { + public void notifyFile(String fileArgument) + { + fileResult.add(fileArgument); + } + }; + + if (handleFileLists) + parse(inArgs, new AtFileArgumentCallback(cb)); + else + parse(inArgs, cb); + + return fileResult.toArray(new String[fileResult.size()]); + } + + + /** + * Simple function that takes the given {@link Reader}, treats it like + * a textfile and reads all the whitespace separated entries from it + * and adds them to the @{link FileArgumentCallback} instance. + * + * @param reader the reader to read from. + * @param cb the callback to post the filenames to. + * @throws OptionException if an error occurs reading the list. + */ + public void parseFileList(Reader reader, FileArgumentCallback cb) + throws OptionException + { + BufferedReader breader = new BufferedReader(reader); + String line = null; + + try + { + while ((line = breader.readLine()) != null) + parseLine(line, cb); + + reader.close(); + } + catch (IOException ioe) + { + throw new OptionException("I/O error while reading a file list", ioe); + } + + } + + /** + * Parses whitespace separated file entries. + * + * Note: This is not coping with whitespace in files or quoting. + * + * @param line the line of the file to parse. + * @param cb the callback to pass the parsed file to. + * @throws IOException if an I/O error occurs. + * @throws OptionException if an error occurs in the callback. + */ + private void parseLine(String line, FileArgumentCallback cb) + throws IOException, OptionException + { + final int length = line.length(); + int start = 0; + int end = 0; + + // While not reached end of line ... + while (start < length) + { + // Search for first non-whitespace character for the start of a word. + while (Character.isWhitespace(line.codePointAt(start))) + { + start++; + + if (start == length) + return; + } + + end = start + 1; + + // Search for first whitespace character for the end of a word. + while (end < length && !Character.isWhitespace(line.codePointAt(end))) + end++; + + cb.notifyFile(line.substring(start, end)); + + start = end + 1; + } + } + + /** + * Implementation of {@link FileArgumentCallback} that handles + * file arguments in {@link #notifyFile} starting with a <code>@</code> + * through {@link ClasspathToolParser#parseFileList}. + */ + class AtFileArgumentCallback extends FileArgumentCallback + { + FileArgumentCallback cb; + + AtFileArgumentCallback(FileArgumentCallback cb) + { + this.cb = cb; + } + + @Override + public void notifyFile(String fileArgument) + throws OptionException + { + if (fileArgument.codePointAt(0) == '@') + { + FileReader fr = null; + + try + { + fr = new FileReader(fileArgument.substring(1)); + } + catch (FileNotFoundException fnfe) + { + throw new OptionException("File not found: " + fileArgument.substring(1), + fnfe); + } + + ClasspathToolParser.this.parseFileList(fr, cb); + } + else + cb.notifyFile(fileArgument); + } + + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/common/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/common/Messages.java new file mode 100644 index 000000000..1c92d348d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/common/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- i18n support for tools common code + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.common; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.common.Messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/common/Persistent.java b/libjava/classpath/tools/gnu/classpath/tools/common/Persistent.java new file mode 100644 index 000000000..5c85c9072 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/common/Persistent.java @@ -0,0 +1,87 @@ +/* PersistentBidiHasthable.java -- Constants for the persistent tables. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.common; + +import java.util.Timer; +import java.util.TimerTask; + +/** + * The static fields, shared by the multiple classes, implementing the + * persistent work. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public interface Persistent +{ + /** + * Sheduled termination task. + */ + static class ExitTask extends TimerTask + { + public void run() + { + System.exit(0); + } + } + + /** + * The timer, sheduling all disk database update events, shared by all + * instances. + */ + static Timer timer = new Timer(true); + + /** + * The longest time, in ms, after that the database content on the disk must + * be updated. The algorithm is written to avoid the very frequent writings to + * the disk. + */ + static long SAVE_AT_MOST_AFTER = 5000; + + /** + * States how long the database may stay not updated during the intensive + * operations, in ms. Otherwise the intensively used structure may never + * be stored to the disk. + */ + static long ALWAYS_UPDATE = 300000; + + /** + * Write the database content to the disk. + */ + void writeContent(); + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/common/ProviderUtil.java b/libjava/classpath/tools/gnu/classpath/tools/common/ProviderUtil.java new file mode 100644 index 000000000..bc6d93146 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/common/ProviderUtil.java @@ -0,0 +1,163 @@ +/* ProviderUtil.java -- Security Provider related utilities + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.common; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.Security; +import java.util.logging.Logger; + +/** + * A <i>Helper</i> class containing general purpose utlity methods dealing with + * installing and removing <i>Security Providers</i> at runtime. + */ +public abstract class ProviderUtil +{ + private static final Logger log = Logger.getLogger(ProviderUtil.class.getName()); + + // default 0-arguments constructor + + // Class methods + // -------------------------------------------------------------------------- + + /** + * Attempt to (a) instantiate, and (b) add a designated {@link Provider} by + * inserting at at the top of the list of <i>Security Providers</i> already + * present at runtime, only if it is not already installed. + * <p> + * <b>IMPORTANT</b>: This method overrides the security check usually carried + * out by the security manager when inserting a new {@link Provider}. + * + * @param providerClass a fully qualified, non-null, class name of a + * <i>Security Provider</i> to add if it is not already installed. + * @return an instance of {@link SecurityProviderInfo} referencing the + * {@link Provider} instance created with the designated class name, + * and its position in the underlying JVM runtime. + */ + public static final SecurityProviderInfo addProvider(String providerClass) + { + log.entering(ProviderUtil.class.getName(), "addProvider", providerClass); + + Provider provider = null; + try + { + provider = (Provider) Class.forName(providerClass.trim()).newInstance(); + } + catch (InstantiationException x) + { + log.fine("InstantiationException while creating [" + providerClass + + "]. Ignore"); + } + catch (IllegalAccessException x) + { + log.fine("IllegalAccessException while creating [" + providerClass + + "]. Ignore"); + } + catch (ClassNotFoundException x) + { + log.fine("ClassNotFoundException while creating [" + providerClass + + "]. Ignore"); + } + + int position = provider != null ? addProvider(provider) : -1; + SecurityProviderInfo result = new SecurityProviderInfo(provider, position); + + log.exiting(ProviderUtil.class.getName(), "addProvider", result); + return result; + } + + /** + * Attempt to add the designated {@link Provider} by inserting at at the top + * of the list of <i>Security Providers</i> already present at runtime, only + * if it is not already installed. + * <p> + * <b>IMPORTANT</b>: This method overrides the security check usually carried + * out by the security manager when inserting a new {@link Provider}. + * + * @param provider a non-null <i>Security Provider</i> to add if it is not + * already installed. + * @return the new position of the designated provider in the list if it was + * not already present, or <code>-1</code> if it was already + * installed. + */ + public static final int addProvider(final Provider provider) + { + log.entering(ProviderUtil.class.getName(), "addProvider", provider); + + Integer actualPosition = (Integer) AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + int result = Security.insertProviderAt(provider, 1); + return Integer.valueOf(result); + } + }); + + int result = actualPosition.intValue(); + log.fine("Provider [" + provider.getName() + "] installed? " + (result != - 1)); + + log.exiting(ProviderUtil.class.getName(), "addProvider", actualPosition); + return result; + } + + /** + * Remove a designated <i>Security Provider</i>. + * <p> + * <b>IMPORTANT</b>: This method overrides the security check usually carried + * out by the security manager when removing a {@link Provider}. + * + * @param providerName the name of the {@link Provider} to remove. + */ + public static final void removeProvider(final String providerName) + { + log.entering(ProviderUtil.class.getName(), "removeProvider", providerName); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + Security.removeProvider(providerName); + return null; + } + }); + + log.exiting(ProviderUtil.class.getName(), "removeProvider"); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/common/SecurityProviderInfo.java b/libjava/classpath/tools/gnu/classpath/tools/common/SecurityProviderInfo.java new file mode 100644 index 000000000..44faebf76 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/common/SecurityProviderInfo.java @@ -0,0 +1,99 @@ +/* SecurityProviderInfo.java -- Data Access Object for a security provider + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.common; + +import java.security.Provider; + +/** + * A Data Access Object (DAO) referenceing a <i>Security Provider</i> and its + * position in the list of installed <i>Security Providers</i> in the underlying + * JVM runtime. + */ +public class SecurityProviderInfo +{ + final private Provider provider; + final private int position; + private transient String str; + + /** + * Constructs an instance of <code>SecurityProviderInfo</code>. + * <p> + * Used by {@link ProviderUtil} to indicate the result of adding a provider, + * given its class name. + * + * @param provider the possibly <code>null</code> {@link Provider}. + * @param position the position of <code>provider</code> in the list of + * <i>Security Providers</i> in the underlying JVM runtime. <code>-1</code> + * if that provider (a) is <code>null</code>, or (b) was not added because it + * was already there. + */ + SecurityProviderInfo(Provider provider, int position) + { + super(); + + this.provider = provider; + this.position = position; + } + + /** @return the possibly <code>null</code> {@link Provider} instance. */ + public Provider getProvider() + { + return this.provider; + } + + /** + * @return the position of the {@link Provider}, or <code>-1</code> if it + * was not added. + */ + public int getPosition() + { + return this.position; + } + + public String toString() + { + if (str == null) + if (provider == null) + str = "SecurityProviderInfo{null, -1}"; + else + str = "SecurityProviderInfo{" + provider.getName() + ", " + position + "}"; + + return str; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/AbstractDoclet.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/AbstractDoclet.java new file mode 100644 index 000000000..4369782f9 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/AbstractDoclet.java @@ -0,0 +1,1386 @@ +/* gnu.classpath.tools.doclets.AbstractDoclet + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +import com.sun.javadoc.ClassDoc; +import com.sun.javadoc.ConstructorDoc; +import com.sun.javadoc.Doc; +import com.sun.javadoc.Doclet; +import com.sun.javadoc.ExecutableMemberDoc; +import com.sun.javadoc.FieldDoc; +import com.sun.javadoc.MethodDoc; +import com.sun.javadoc.PackageDoc; +import com.sun.javadoc.Parameter; +import com.sun.javadoc.RootDoc; +import com.sun.javadoc.Tag; +import com.sun.javadoc.Type; + +import com.sun.tools.doclets.Taglet; + +import gnu.classpath.tools.taglets.GnuExtendedTaglet; +import gnu.classpath.tools.taglets.AuthorTaglet; +import gnu.classpath.tools.taglets.CodeTaglet; +import gnu.classpath.tools.taglets.DeprecatedTaglet; +import gnu.classpath.tools.taglets.GenericTaglet; +import gnu.classpath.tools.taglets.SinceTaglet; +import gnu.classpath.tools.taglets.ValueTaglet; +import gnu.classpath.tools.taglets.VersionTaglet; +import gnu.classpath.tools.taglets.TagletContext; + +import gnu.classpath.tools.IOToolkit; +import gnu.classpath.tools.FileSystemClassLoader; + +import java.io.File; +import java.io.IOException; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.InvocationTargetException; + +import java.text.MessageFormat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.SortedSet; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * An abstract Doclet implementation with helpers for common tasks + * performed by Doclets. + */ +public abstract class AbstractDoclet +{ + /** + * Mapping from tag type to Taglet for user Taglets specified on + * the command line. + */ + protected Map tagletMap = new LinkedHashMap(); + + /** + * Stores the package groups specified in the user + * options. Contains objects of type PackageGroup. + */ + private List packageGroups = new LinkedList(); + + /** + * The current classpath for loading taglet classes. + */ + private String tagletPath; + + /** + * Keeps track of the tags mentioned by the user during option + * processiong so that an error can be emitted if a tag is + * mentioned more than once. + */ + private List mentionedTags = new LinkedList(); + + public static int optionLength(String option) { + return instance.getOptionLength(option); + } + + public static boolean validOptions(String[][] options) { + return true; + } + + private static AbstractDoclet instance; + + protected static void setInstance(AbstractDoclet instance) + { + AbstractDoclet.instance = instance; + } + + protected abstract void run() + throws DocletConfigurationException, IOException; + + public static boolean start(RootDoc rootDoc) + { + try { + + instance.startInstance(rootDoc); + return true; + } + catch (DocletConfigurationException e) { + instance.printError(e.getMessage()); + return false; + } + catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + protected RootDoc getRootDoc() + { + return this.rootDoc; + } + + private RootDoc rootDoc; + + protected abstract InlineTagRenderer getInlineTagRenderer(); + + private void startInstance(RootDoc rootDoc) + throws DocletConfigurationException, IOException + { + this.rootDoc = rootDoc; + + // Set the default Taglet order + + registerTaglet(new VersionTaglet()); + registerTaglet(new AuthorTaglet()); + registerTaglet(new SinceTaglet(getInlineTagRenderer())); + registerTaglet(new StandardTaglet("serial")); + registerTaglet(new StandardTaglet("deprecated")); + registerTaglet(new StandardTaglet("see")); + registerTaglet(new StandardTaglet("param")); + registerTaglet(new StandardTaglet("return")); + + registerTaglet(new ValueTaglet()); + registerTaglet(new CodeTaglet()); + + // Process command line options + + for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) { + + String[] optionArr = rootDoc.options()[i]; + String _optionTag = optionArr[0]; + + DocletOption option = (DocletOption)nameToOptionMap.get(_optionTag.toLowerCase()); + + if (null != option) { + option.set(optionArr); + } + } + + // Enable/disable standard taglets based on user input + + AuthorTaglet.setTagletEnabled(optionAuthor.getValue()); + VersionTaglet.setTagletEnabled(optionVersion.getValue()); + SinceTaglet.setTagletEnabled(!optionNoSince.getValue()); + DeprecatedTaglet.setTagletEnabled(!optionNoDeprecated.getValue()); + + if (!getTargetDirectory().exists()) { + if (!getTargetDirectory().mkdirs()) { + throw new DocletConfigurationException("Cannot create target directory " + + getTargetDirectory()); + } + } + + run(); + } + + public File getTargetDirectory() + { + return optionTargetDirectory.getValue(); + } + + private DocletOptionFile optionTargetDirectory = + new DocletOptionFile("-d", + new File(System.getProperty("user.dir"))); + + private DocletOptionFlag optionNoEmailWarn = + new DocletOptionFlag("-noemailwarn"); + + private DocletOptionFlag optionAuthor = + new DocletOptionFlag("-author"); + + private DocletOptionFlag optionVersion = + new DocletOptionFlag("-version"); + + private DocletOptionFlag optionNoSince = + new DocletOptionFlag("-nosince"); + + private DocletOptionFlag optionNoDeprecated = + new DocletOptionFlag("-nodeprecated"); + + private DocletOptionGroup optionGroup = + new DocletOptionGroup("-group"); + + private DocletOptionPackageWildcard optionNoQualifier = + new DocletOptionPackageWildcard("-noqualifier", true); + + private DocletOptionFlag optionDocFilesSubDirs = + new DocletOptionFlag("-docfilessubdirs"); + + private DocletOptionColonSeparated optionExcludeDocFilesSubDir = + new DocletOptionColonSeparated("-excludedocfilessubdir"); + + private DocletOptionTagletPath optionTagletPath = + new DocletOptionTagletPath("-tagletpath"); + + private DocletOptionTag optionTaglet = + new DocletOptionTag("-taglet"); + + private DocletOptionTag optionTag = + new DocletOptionTag("-tag"); + + private class DocletOptionTaglet + extends DocletOption + { + DocletOptionTaglet(String optionName) + { + super(optionName); + } + + public int getLength() + { + return 2; + } + + public boolean set(String[] optionArr) + { + + boolean tagletLoaded = false; + + String useTagletPath = AbstractDoclet.this.tagletPath; + if (null == useTagletPath) { + useTagletPath = System.getProperty("java.class.path"); + } + + try { + Class tagletClass; + try { + tagletClass + = new FileSystemClassLoader(useTagletPath).loadClass(optionArr[1]); + } + catch (ClassNotFoundException e) { + // If not found on specified tagletpath, try default classloader + tagletClass + = Class.forName(optionArr[1]); + } + Method registerTagletMethod + = tagletClass.getDeclaredMethod("register", new Class[] { java.util.Map.class }); + + if (!registerTagletMethod.getReturnType().equals(Void.TYPE)) { + printError("Taglet class '" + optionArr[1] + "' found, but register method doesn't return void."); + } + else if (registerTagletMethod.getExceptionTypes().length > 0) { + printError("Taglet class '" + optionArr[1] + "' found, but register method contains throws clause."); + } + else if ((registerTagletMethod.getModifiers() & (Modifier.STATIC | Modifier.PUBLIC | Modifier.ABSTRACT)) != (Modifier.STATIC | Modifier.PUBLIC)) { + printError("Taglet class '" + optionArr[1] + "' found, but register method isn't public static, or is abstract.."); + } + else { + Map tempMap = new HashMap(); + registerTagletMethod.invoke(null, new Object[] { tempMap }); + tagletLoaded = true; + String name = (String)tempMap.keySet().iterator().next(); + Taglet taglet = (Taglet)tempMap.get(name); + tagletMap.put(name, taglet); + mentionedTags.add(taglet); + } + } + catch (NoSuchMethodException e) { + printError("Taglet class '" + optionArr[1] + "' found, but doesn't contain the register method."); + } + catch (SecurityException e) { + printError("Taglet class '" + optionArr[1] + "' cannot be loaded: " + e.getMessage()); + } + catch (InvocationTargetException e) { + printError("Taglet class '" + optionArr[1] + "' found, but register method throws exception: " + e.toString()); + } + catch (IllegalAccessException e) { + printError("Taglet class '" + optionArr[1] + "' found, but there was a problem when accessing the register method: " + e.toString()); + } + catch (IllegalArgumentException e) { + printError("Taglet class '" + optionArr[1] + "' found, but there was a problem when accessing the register method: " + e.toString()); + } + catch (ClassNotFoundException e) { + printError("Taglet class '" + optionArr[1] + "' cannot be found."); + } + return tagletLoaded; + } + } + + private class DocletOptionGroup + extends DocletOption + { + DocletOptionGroup(String optionName) + { + super(optionName); + } + + public int getLength() + { + return 3; + } + + public boolean set(String[] optionArr) + { + try { + PackageMatcher packageMatcher = new PackageMatcher(); + + StringTokenizer tokenizer = new StringTokenizer(optionArr[2], ":"); + while (tokenizer.hasMoreTokens()) { + String packageWildcard = tokenizer.nextToken(); + packageMatcher.addWildcard(packageWildcard); + } + + SortedSet groupPackages = packageMatcher.filter(rootDoc.specifiedPackages()); + + packageGroups.add(new PackageGroup(optionArr[1], groupPackages)); + + return true; + } + catch (InvalidPackageWildcardException e) { + return false; + } + } + } + + + private class DocletOptionTagletPath + extends DocletOption + { + DocletOptionTagletPath(String optionName) + { + super(optionName); + } + + public int getLength() + { + return 2; + } + + public boolean set(String[] optionArr) + { + AbstractDoclet.this.tagletPath = optionArr[1]; + return true; + } + } + + private class DocletOptionTag + extends DocletOption + { + DocletOptionTag(String optionName) + { + super(optionName); + } + + public int getLength() + { + return 2; + } + + public boolean set(String[] optionArr) + { + String tagSpec = optionArr[1]; + boolean validTagSpec = false; + int ndx1 = tagSpec.indexOf(':'); + if (ndx1 < 0) { + Taglet taglet = (Taglet)tagletMap.get(tagSpec); + if (null == taglet) { + printError("There is no standard tag '" + tagSpec + "'."); + } + else { + if (mentionedTags.contains(taglet)) { + printError("Tag '" + tagSpec + "' has been added or moved before."); + } + else { + mentionedTags.add(taglet); + + // re-append taglet + tagletMap.remove(tagSpec); + tagletMap.put(tagSpec, taglet); + } + } + } + else { + int ndx2 = tagSpec.indexOf(':', ndx1 + 1); + if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) { + String tagName = tagSpec.substring(0, ndx1); + String tagHead = null; + if (tagSpec.charAt(ndx2 + 1) == '\"') { + if (tagSpec.charAt(tagSpec.length() - 1) == '\"') { + tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1); + validTagSpec = true; + } + } + else { + tagHead = tagSpec.substring(ndx2 + 1); + validTagSpec = true; + } + + boolean tagScopeOverview = false; + boolean tagScopePackages = false; + boolean tagScopeTypes = false; + boolean tagScopeConstructors = false; + boolean tagScopeMethods = false; + boolean tagScopeFields = false; + boolean tagDisabled = false; + + tag_option_loop: + for (int n=ndx1+1; n<ndx2; ++n) { + switch (tagSpec.charAt(n)) { + case 'X': + tagDisabled = true; + break; + case 'a': + tagScopeOverview = true; + tagScopePackages = true; + tagScopeTypes = true; + tagScopeConstructors = true; + tagScopeMethods = true; + tagScopeFields = true; + break; + case 'o': + tagScopeOverview = true; + break; + case 'p': + tagScopePackages = true; + break; + case 't': + tagScopeTypes = true; + break; + case 'c': + tagScopeConstructors = true; + break; + case 'm': + tagScopeMethods = true; + break; + case 'f': + tagScopeFields = true; + break; + default: + validTagSpec = false; + break tag_option_loop; + } + } + + if (validTagSpec) { + GenericTaglet taglet + = new GenericTaglet(tagName, + tagHead, + tagScopeOverview, + tagScopePackages, + tagScopeTypes, + tagScopeConstructors, + tagScopeMethods, + tagScopeFields); + taglet.setTagletEnabled(!tagDisabled); + taglet.register(tagletMap); + mentionedTags.add(taglet); + } + } + } + if (!validTagSpec) { + printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\"."); + } + return validTagSpec; + } + } + + private DocletOption[] commonOptions = + { + optionTargetDirectory, + optionAuthor, + optionVersion, + optionNoSince, + optionNoDeprecated, + optionGroup, + optionDocFilesSubDirs, + optionExcludeDocFilesSubDir, + optionTagletPath, + optionTaglet, + optionTag, + }; + + private void registerOptions() + { + if (!optionsRegistered) { + for (int i=0; i<commonOptions.length; ++i) { + DocletOption option = commonOptions[i]; + registerOption(option); + } + DocletOption[] docletOptions = getOptions(); + for (int i=0; i<docletOptions.length; ++i) { + DocletOption option = docletOptions[i]; + registerOption(option); + } + optionsRegistered = true; + } + } + + protected abstract DocletOption[] getOptions(); + + private boolean optionsRegistered = false; + + private void registerOption(DocletOption option) + { + nameToOptionMap.put(option.getName(), option); + } + + private Map nameToOptionMap = new HashMap(); + + private int getOptionLength(String optionName) + { + registerOptions(); + DocletOption option = (DocletOption)nameToOptionMap.get(optionName.toLowerCase()); + if (null != option) { + return option.getLength(); + } + else { + return -1; + } + } + + protected List getKnownDirectSubclasses(ClassDoc classDoc) + { + List result = new LinkedList(); + if (!"java.lang.Object".equals(classDoc.qualifiedName())) { + ClassDoc[] classes = rootDoc.classes(); + for (int i=0; i<classes.length; ++i) { + if (classDoc == classes[i].superclass()) { + result.add(classes[i]); + } + } + } + return result; + } + + protected static class IndexKey + implements Comparable + { + private String name; + private String lowerName; + + public IndexKey(String name) + { + this.name = name; + this.lowerName = name.toLowerCase(); + } + + public boolean equals(Object other) + { + return this.lowerName.equals(((IndexKey)other).lowerName); + } + + public int hashCode() + { + return lowerName.hashCode(); + } + + public int compareTo(Object other) + { + return lowerName.compareTo(((IndexKey)other).lowerName); + } + + public String getName() + { + return name; + } + } + + private Map categorizedIndex; + + protected Map getCategorizedIndex() + { + if (null == categorizedIndex) { + categorizedIndex = new LinkedHashMap(); + + Map indexMap = getIndexByName(); + LinkedList keys = new LinkedList(); //indexMap.keySet().size()); + keys.addAll(indexMap.keySet()); + Collections.sort(keys); + Iterator it = keys.iterator(); //indexMap.keySet().iterator(); + char previousCategoryLetter = '\0'; + Character keyLetter = null; + while (it.hasNext()) { + IndexKey key = (IndexKey)it.next(); + char firstChar = Character.toUpperCase(key.getName().charAt(0)); + if (firstChar != previousCategoryLetter) { + keyLetter = new Character(firstChar); + previousCategoryLetter = firstChar; + categorizedIndex.put(keyLetter, new LinkedList()); + } + List letterList = (List)categorizedIndex.get(keyLetter); + letterList.add(indexMap.get(key)); + } + } + + return categorizedIndex; + } + + + private Map indexByName; + + protected Map getIndexByName() + { + if (null == indexByName) { + // Create index + + // Collect index + + indexByName = new HashMap(); //TreeMap(); + + // Add packages to index + + PackageDoc[] packages = rootDoc.specifiedPackages(); + for (int i=0, ilim=packages.length; i<ilim; ++i) { + PackageDoc c = packages[i]; + if (c.name().length() > 0) { + indexByName.put(new IndexKey(c.name()), c); + } + } + + // Add classes, fields and methods to index + + ClassDoc[] sumclasses = rootDoc.classes(); + for (int i=0, ilim=sumclasses.length; i<ilim; ++i) { + ClassDoc c = sumclasses[i]; + if (null == c.containingClass()) { + indexByName.put(new IndexKey(c.name() + " " + c.containingPackage().name()), c); + } + else { + indexByName.put(new IndexKey(c.name().substring(c.containingClass().name().length() + 1) + + " " + c.containingClass().name() + " " + c.containingPackage().name()), c); + } + FieldDoc[] fields = c.fields(); + for (int j=0, jlim=fields.length; j<jlim; ++j) { + indexByName.put(new IndexKey(fields[j].name() + " " + fields[j].containingClass().name() + " " + fields[j].containingPackage().name()), fields[j]); + } + MethodDoc[] methods = c.methods(); + for (int j=0, jlim=methods.length; j<jlim; ++j) { + MethodDoc method = methods[j]; + indexByName.put(new IndexKey(method.name() + method.signature() + " " + method.containingClass().name() + " " + method.containingPackage().name()), method); + } + ConstructorDoc[] constructors = c.constructors(); + for (int j=0, jlim=constructors.length; j<jlim; ++j) { + ConstructorDoc constructor = constructors[j]; + indexByName.put(new IndexKey(constructor.name() + constructor.signature() + " " + constructor.containingClass().name() + " " + constructor.containingPackage().name()), constructor); + } + } + } + return indexByName; + } + + private void registerTaglet(Taglet taglet) + { + tagletMap.put(taglet.getName(), taglet); + } + + protected void printTaglets(Tag[] tags, TagletContext context, TagletPrinter output, boolean inline) + { + for (Iterator it = tagletMap.keySet().iterator(); it.hasNext(); ) { + String tagName = (String)it.next(); + Object o = tagletMap.get(tagName); + Taglet taglet = (Taglet)o; + Doc doc = context.getDoc(); + if (inline == taglet.isInlineTag() + && ((doc == null + && taglet.inOverview()) + || (doc != null + && ((doc.isConstructor() && taglet.inConstructor()) + || (doc.isField() && taglet.inField()) + || (doc.isMethod() && taglet.inMethod()) + || (doc instanceof PackageDoc && taglet.inPackage()) + || ((doc.isClass() || doc.isInterface()) && taglet.inType()))))) { + + List tagsOfThisType = new LinkedList(); + for (int i=0; i<tags.length; ++i) { + if (tags[i].name().substring(1).equals(tagName)) { + tagsOfThisType.add(tags[i]); + } + } + + Tag[] tagletTags = (Tag[])tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]); + + String tagletString; + if (taglet instanceof StandardTaglet) { + tagletString = renderTag(tagName, tagletTags, context); + } + else if (taglet instanceof GnuExtendedTaglet) { + tagletString = ((GnuExtendedTaglet)taglet).toString(tagletTags, context); + } + else { + tagletString = taglet.toString(tagletTags); + } + if (null != tagletString) { + output.printTagletString(tagletString); + } + } + } + } + + protected void printInlineTaglet(Tag tag, TagletContext context, TagletPrinter output) + { + Taglet taglet = (Taglet)tagletMap.get(tag.name().substring(1)); + if (null != taglet) { + String tagletString; + if (taglet instanceof GnuExtendedTaglet) { + tagletString = ((GnuExtendedTaglet)taglet).toString(tag, context); + } + else { + tagletString = taglet.toString(tag); + } + if (null != tagletString) { + output.printTagletString(tagletString); + } + } + else { + printWarning("Unknown tag: " + tag.name()); + } + } + + protected void printMainTaglets(Tag[] tags, TagletContext context, TagletPrinter output) + { + printTaglets(tags, context, output, false); + } + + /** + * @param usedClassToPackagesMap ClassDoc to (PackageDoc to (UsageType to (Set of Doc))) + */ + private void addUsedBy(Map usedClassToPackagesMap, + ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage) + { + Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(usedClass); + if (null == packageToUsageTypeMap) { + packageToUsageTypeMap = new HashMap(); + usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap); + } + + Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(userPackage); + if (null == usageTypeToUsersMap) { + usageTypeToUsersMap = new TreeMap(); + packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap); + } + + Set userSet = (Set)usageTypeToUsersMap.get(usageType); + if (null == userSet) { + userSet = new TreeSet(); // FIXME: we need the collator from Main here + usageTypeToUsersMap.put(usageType, userSet); + } + userSet.add(user); + } + + /** + * Create the cross reference database. + */ + private Map collectUsage() { + + Map _usedClassToPackagesMap = new HashMap(); + + ClassDoc[] classes = rootDoc.classes(); + for (int i = 0, ilim = classes.length; i < ilim; ++ i) { + ClassDoc clazz = classes[i]; + + if (clazz.isInterface()) { + // classes implementing + InterfaceRelation relation + = (InterfaceRelation)getInterfaceRelations().get(clazz); + Iterator it = relation.implementingClasses.iterator(); + while (it.hasNext()) { + ClassDoc implementor = (ClassDoc)it.next(); + addUsedBy(_usedClassToPackagesMap, + clazz, UsageType.CLASS_IMPLEMENTING, implementor, implementor.containingPackage()); + } + } + else { + // classes derived from + for (ClassDoc superclass = clazz.superclass(); superclass != null; + superclass = superclass.superclass()) { + addUsedBy(_usedClassToPackagesMap, + superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage()); + } + } + + FieldDoc[] fields = clazz.fields(); + for (int j = 0, jlim = fields.length; j < jlim; ++ j) { + FieldDoc field = fields[j]; + + // fields of type + ClassDoc fieldType = field.type().asClassDoc(); + if (null != fieldType) { + addUsedBy(_usedClassToPackagesMap, + fieldType, UsageType.FIELD_OF_TYPE, + field, clazz.containingPackage()); + } + } + + MethodDoc[] methods = clazz.methods(); + for (int j = 0, jlim = methods.length; j < jlim; ++ j) { + MethodDoc method = methods[j]; + + // methods with return type + + ClassDoc returnType = method.returnType().asClassDoc(); + if (null != returnType) { + addUsedBy(_usedClassToPackagesMap, + returnType, UsageType.METHOD_WITH_RETURN_TYPE, + method, clazz.containingPackage()); + } + Parameter[] parameters = method.parameters(); + for (int k=0; k<parameters.length; ++k) { + + // methods with parameter type + + Parameter parameter = parameters[k]; + ClassDoc parameterType = parameter.type().asClassDoc(); + if (null != parameterType) { + addUsedBy(_usedClassToPackagesMap, + parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE, + method, clazz.containingPackage()); + } + } + + // methods which throw + + ClassDoc[] thrownExceptions = method.thrownExceptions(); + for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) { + ClassDoc thrownException = thrownExceptions[k]; + addUsedBy(_usedClassToPackagesMap, + thrownException, UsageType.METHOD_WITH_THROWN_TYPE, + method, clazz.containingPackage()); + } + } + + ConstructorDoc[] constructors = clazz.constructors(); + for (int j = 0, jlim = constructors.length; j < jlim; ++ j) { + + ConstructorDoc constructor = constructors[j]; + + Parameter[] parameters = constructor.parameters(); + for (int k = 0, klim = parameters.length; k < klim; ++ k) { + + // constructors with parameter type + + Parameter parameter = parameters[k]; + ClassDoc parameterType = parameter.type().asClassDoc(); + if (null != parameterType) { + addUsedBy(_usedClassToPackagesMap, + parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE, + constructor, clazz.containingPackage()); + } + } + + // constructors which throw + + ClassDoc[] thrownExceptions = constructor.thrownExceptions(); + for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) { + ClassDoc thrownException = thrownExceptions[k]; + addUsedBy(_usedClassToPackagesMap, + thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE, + constructor, clazz.containingPackage()); + } + } + } + return _usedClassToPackagesMap; + } + + private Map usedClassToPackagesMap = null; + + protected Map getUsageOfClass(ClassDoc classDoc) + { + if (null == this.usedClassToPackagesMap) { + this.usedClassToPackagesMap = collectUsage(); + } + return (Map)this.usedClassToPackagesMap.get(classDoc); + } + + protected static class UsageType + implements Comparable + { + public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from"); + public static final UsageType CLASS_IMPLEMENTING = new UsageType("class-implementing"); + public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type"); + public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type"); + public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type"); + public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type"); + public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type"); + public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type"); + private String id; + + private UsageType(String id) + { + this.id = id; + } + + public int compareTo(Object other) + { + return this.id.compareTo(((UsageType)other).id); + } + + public String toString() { + return "UsageType{id=" + id + "}"; + } + + public String getId() { + return id; + } + } + + private ResourceBundle resources; + + protected String getString(String key) + { + if (null == resources) { + Locale currentLocale = Locale.getDefault(); + + resources + = ResourceBundle.getBundle("htmldoclet.HtmlDoclet", currentLocale); + } + + return resources.getString(key); + } + + protected String format(String key, String value1) + { + return MessageFormat.format(getString(key), new Object[] { value1 }); + } + + protected List getPackageGroups() + { + return packageGroups; + } + + protected void copyDocFiles(File sourceDir, File targetDir) + throws IOException + { + File sourceDocFiles = new File(sourceDir, "doc-files"); + File targetDocFiles = new File(targetDir, "doc-files"); + + if (sourceDocFiles.exists()) { + IOToolkit.copyDirectory(sourceDocFiles, + targetDocFiles, + optionDocFilesSubDirs.getValue(), + optionExcludeDocFilesSubDir.getComponents()); + } + } + + private Set sourcePaths; + + /** + * Try to determine the source directory for the given package by + * looking at the path specified by -sourcepath, or the current + * directory if -sourcepath hasn't been specified. + * + * @throws IOException if the source directory couldn't be + * located. + * + * @return List of File + */ + protected List getPackageSourceDirs(PackageDoc packageDoc) + throws IOException + { + if (null == sourcePaths) { + for (int i=0; i<rootDoc.options().length; ++i) { + if ("-sourcepath".equals(rootDoc.options()[i][0]) + || "-s".equals(rootDoc.options()[i][0])) { + sourcePaths = new LinkedHashSet(); + String sourcepathString = rootDoc.options()[i][1]; + StringTokenizer st = new StringTokenizer(sourcepathString, File.pathSeparator); + while (st.hasMoreTokens()) { + sourcePaths.add(new File(st.nextToken())); + } + } + } + if (null == sourcePaths) { + sourcePaths = new LinkedHashSet(); + sourcePaths.add(new File(System.getProperty("user.dir"))); + } + } + + String packageSubDir = packageDoc.name().replace('.', File.separatorChar); + Iterator it = sourcePaths.iterator(); + List result = new LinkedList(); + while (it.hasNext()) { + File pathComponent = (File)it.next(); + File packageDir = new File(pathComponent, packageSubDir); + if (packageDir.exists()) { + result.add(packageDir); + } + } + if (result.isEmpty()) { + throw new IOException("Couldn't locate source directory for package " + packageDoc.name()); + } + else { + return result; + } + } + + protected File getSourceFile(ClassDoc classDoc) + throws IOException + { + List packageDirs = getPackageSourceDirs(classDoc.containingPackage()); + Iterator it = packageDirs.iterator(); + while (it.hasNext()) { + File packageDir = (File)it.next(); + File sourceFile = new File(packageDir, getOuterClassDoc(classDoc).name() + ".java"); + if (sourceFile.exists()) { + return sourceFile; + } + } + + throw new IOException("Couldn't locate source file for class " + classDoc.qualifiedTypeName()); + } + + protected void printError(String error) + { + if (null != rootDoc) { + rootDoc.printError(error); + } + else { + System.err.println("ERROR: "+error); + } + } + + protected void printWarning(String warning) + { + if (null != rootDoc) { + rootDoc.printWarning(warning); + } + else { + System.err.println("WARNING: "+warning); + } + } + + protected void printNotice(String notice) + { + if (null != rootDoc) { + rootDoc.printNotice(notice); + } + else { + System.err.println(notice); + } + } + + protected static ClassDoc getOuterClassDoc(ClassDoc classDoc) + { + while (null != classDoc.containingClass()) { + classDoc = classDoc.containingClass(); + } + return classDoc; + } + + private SortedSet allPackages; + + protected Set getAllPackages() + { + if (null == this.allPackages) { + allPackages = new TreeSet(); + PackageDoc[] specifiedPackages = rootDoc.specifiedPackages(); + for (int i=0; i<specifiedPackages.length; ++i) { + allPackages.add(specifiedPackages[i]); + } + ClassDoc[] specifiedClasses = rootDoc.specifiedClasses(); + for (int i=0; i<specifiedClasses.length; ++i) { + allPackages.add(specifiedClasses[i].containingPackage()); + } + } + return this.allPackages; + } + + protected boolean omitPackageQualifier(PackageDoc packageDoc) + { + if (!optionNoQualifier.isSpecified()) { + return false; + } + else { + return optionNoQualifier.match(packageDoc); + } + } + + protected String possiblyQualifiedName(Type type) + { + if (null == type.asClassDoc() + || !omitPackageQualifier(type.asClassDoc().containingPackage())) { + return type.qualifiedTypeName(); + } + else { + return type.typeName(); + } + } + + protected static class InterfaceRelation + { + public Set superInterfaces; + public Set subInterfaces; + public Set implementingClasses; + + public InterfaceRelation() + { + superInterfaces = new TreeSet(); + subInterfaces = new TreeSet(); + implementingClasses = new TreeSet(); + } + } + + private void addAllInterfaces(ClassDoc classDoc, Set allInterfaces) + { + ClassDoc[] interfaces = classDoc.interfaces(); + for (int i=0; i<interfaces.length; ++i) { + allInterfaces.add(interfaces[i]); + addAllInterfaces(interfaces[i], allInterfaces); + } + } + + private Map allSubClasses; + + protected Map getAllSubClasses() + { + if (null == allSubClasses) { + allSubClasses = new HashMap(); + + ClassDoc[] classDocs = getRootDoc().classes(); + for (int i=0; i<classDocs.length; ++i) { + if (!classDocs[i].isInterface()) { + for (ClassDoc cd = classDocs[i].superclass(); + null != cd; + cd = cd.superclass()) { + + if (!cd.qualifiedTypeName().equals("java.lang.Object")) { + List subClasses = (List)allSubClasses.get(cd); + if (null == subClasses) { + subClasses = new LinkedList(); + allSubClasses.put(cd, subClasses); + } + subClasses.add(classDocs[i]); + } + } + } + } + } + return allSubClasses; + } + + private Map interfaceRelations; + + private void addToInterfaces(ClassDoc classDoc, ClassDoc[] interfaces) + { + for (int i=0; i<interfaces.length; ++i) { + InterfaceRelation interfaceRelation + = (InterfaceRelation)interfaceRelations.get(interfaces[i]); + if (null == interfaceRelation) { + interfaceRelation = new InterfaceRelation(); + interfaceRelations.put(interfaces[i], interfaceRelation); + } + interfaceRelation.implementingClasses.add(classDoc); + addToInterfaces(classDoc, interfaces[i].interfaces()); + } + } + + protected Map getInterfaceRelations() + { + if (null == interfaceRelations) { + interfaceRelations = new HashMap(); + + ClassDoc[] classDocs = getRootDoc().classes(); + for (int i=0; i<classDocs.length; ++i) { + if (classDocs[i].isInterface()) { + InterfaceRelation relation = new InterfaceRelation(); + addAllInterfaces(classDocs[i], relation.superInterfaces); + interfaceRelations.put(classDocs[i], relation); + } + } + + Iterator it = interfaceRelations.keySet().iterator(); + while (it.hasNext()) { + ClassDoc interfaceDoc = (ClassDoc)it.next(); + InterfaceRelation relation + = (InterfaceRelation)interfaceRelations.get(interfaceDoc); + Iterator superIt = relation.superInterfaces.iterator(); + while (superIt.hasNext()) { + ClassDoc superInterfaceDoc = (ClassDoc)superIt.next(); + InterfaceRelation superRelation + = (InterfaceRelation)interfaceRelations.get(superInterfaceDoc); + if (null != superRelation) { + superRelation.subInterfaces.add(interfaceDoc); + } + } + } + + for (int i=0; i<classDocs.length; ++i) { + if (!classDocs[i].isInterface()) { + for (ClassDoc cd = classDocs[i]; null != cd; cd = cd.superclass()) { + addToInterfaces(classDocs[i], cd.interfaces()); + } + } + } + } + + return interfaceRelations; + } + + private Map sortedMethodMap = new HashMap(); + + protected MethodDoc[] getSortedMethods(ClassDoc classDoc) + { + MethodDoc[] result = (MethodDoc[])sortedMethodMap.get(classDoc); + if (null == result) { + MethodDoc[] methods = classDoc.methods(); + result = (MethodDoc[])methods.clone(); + Arrays.sort(result); + return result; + } + return result; + } + + private Map sortedConstructorMap = new HashMap(); + + protected ConstructorDoc[] getSortedConstructors(ClassDoc classDoc) + { + ConstructorDoc[] result = (ConstructorDoc[])sortedConstructorMap.get(classDoc); + if (null == result) { + ConstructorDoc[] constructors = classDoc.constructors(); + result = (ConstructorDoc[])constructors.clone(); + Arrays.sort(result); + return result; + } + return result; + } + + private Map sortedFieldMap = new HashMap(); + + protected FieldDoc[] getSortedFields(ClassDoc classDoc) + { + FieldDoc[] result = (FieldDoc[])sortedFieldMap.get(classDoc); + if (null == result) { + FieldDoc[] fields = classDoc.fields(); + result = (FieldDoc[])fields.clone(); + Arrays.sort(result); + return result; + } + return result; + } + + private Map sortedInnerClassMap = new HashMap(); + + protected ClassDoc[] getSortedInnerClasses(ClassDoc classDoc) + { + ClassDoc[] result = (ClassDoc[])sortedInnerClassMap.get(classDoc); + if (null == result) { + ClassDoc[] innerClasses = classDoc.innerClasses(); + result = (ClassDoc[])innerClasses.clone(); + Arrays.sort(result); + return result; + } + return result; + } + + protected abstract String renderTag(String tagName, Tag[] tags, TagletContext context); + + protected abstract String getDocletVersion(); + + protected SortedSet getThrownExceptions(ExecutableMemberDoc execMemberDoc) + { + SortedSet result = new TreeSet(); + ClassDoc[] thrownExceptions = execMemberDoc.thrownExceptions(); + for (int j=0; j<thrownExceptions.length; ++j) { + result.add(thrownExceptions[j]); + } + return result; + } + + protected boolean isUncheckedException(ClassDoc classDoc) + { + if (classDoc.isException()) { + while (null != classDoc) { + if (classDoc.qualifiedTypeName().equals("java.lang.RuntimeException")) { + return true; + } + classDoc = classDoc.superclass(); + } + return false; + } + else { + return false; + } + } + + protected FieldDoc findField(ClassDoc classDoc, String fieldName) + { + for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) { + FieldDoc[] fields = cd.fields(false); + for (int i=0; i<fields.length; ++i) { + if (fields[i].name().equals(fieldName)) { + return fields[i]; + } + } + } + return null; + } + + private Map implementedInterfacesCache = new HashMap(); + + protected Set getImplementedInterfaces(ClassDoc classDoc) + { + Set result = (Set)implementedInterfacesCache.get(classDoc); + if (null == result) { + result = new TreeSet(); + + for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) { + ClassDoc[] interfaces = cd.interfaces(); + for (int i=0; i<interfaces.length; ++i) { + result.add(interfaces[i]); + InterfaceRelation relation + = (InterfaceRelation)getInterfaceRelations().get(interfaces[i]); + if (null != relation) { + result.addAll(relation.superInterfaces); + } + } + } + + implementedInterfacesCache.put(classDoc, result); + } + + return result; + } + + protected boolean isSinglePackage() + { + return getAllPackages().size() <= 1; + } + + protected PackageDoc getSinglePackage() + { + return (PackageDoc)getAllPackages().iterator().next(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletConfigurationException.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletConfigurationException.java new file mode 100644 index 000000000..0e29df928 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletConfigurationException.java @@ -0,0 +1,54 @@ +/* gnu.classpath.tools.doclets.DocletConfigurationException + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +/** + * Thrown by <code>AbstractDoclet</code> members or members of + * <code>AbstractDoclet</code> subclasses when a problem caused by + * user-specified options is detected. + * + * @see AbstractDoclet + */ +public class DocletConfigurationException + extends Exception +{ + public DocletConfigurationException(String message) + { + super(message); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOption.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOption.java new file mode 100644 index 000000000..fa67f5ec7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOption.java @@ -0,0 +1,56 @@ +/* gnu.classpath.tools.doclets.DocletOption + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +public abstract class DocletOption +{ + private String optionName; + + protected DocletOption(String optionName) + { + this.optionName = optionName; + } + + public String getName() + { + return this.optionName; + } + + public abstract int getLength(); + public abstract boolean set(String[] optionArr); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionColonSeparated.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionColonSeparated.java new file mode 100644 index 000000000..e02e1ebb4 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionColonSeparated.java @@ -0,0 +1,76 @@ +/* gnu.classpath.tools.doclets.DocletOptionColonSeparated + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * Processes a doclet option whose value consists of a + * colon-separated list of strings. + */ +public class DocletOptionColonSeparated + extends DocletOption +{ + private Set components = new LinkedHashSet(); + + DocletOptionColonSeparated(String optionName) + { + super(optionName); + } + + public int getLength() + { + return 2; + } + + public boolean set(String[] optionArr) + { + StringTokenizer st = new StringTokenizer(":"); + while (st.hasMoreTokens()) { + components.add(st.nextToken()); + } + return true; + } + + public Set getComponents() + { + return components; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionFile.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionFile.java new file mode 100644 index 000000000..b83685755 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionFile.java @@ -0,0 +1,77 @@ +/* gnu.classpath.tools.doclets.DocletOptionFile + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +import java.io.File; + +/** + * Processes a doclet option whose value denotes an existing or + * non-existing file in the local file system. + */ +public class DocletOptionFile + extends DocletOption +{ + private File value; + + public DocletOptionFile(String optionName) + { + this(optionName, null); + } + + public DocletOptionFile(String optionName, File defaultFile) + { + super(optionName); + this.value = defaultFile; + } + + public File getValue() + { + return this.value; + } + + public int getLength() + { + return 2; + } + + public boolean set(String[] optionArr) + { + this.value = new File(optionArr[1]); + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionFlag.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionFlag.java new file mode 100644 index 000000000..13afe407f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionFlag.java @@ -0,0 +1,69 @@ +/* gnu.classpath.tools.doclets.DocletOptionFlag + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +/** + * Processes a doclet option without a value whose existance denotes + * that a specific feature should be enabled or disabled. + */ +public class DocletOptionFlag + extends DocletOption +{ + private boolean value = false; + + public DocletOptionFlag(String optionName) + { + super(optionName); + } + + public boolean getValue() + { + return this.value; + } + + public int getLength() + { + return 1; + } + + public boolean set(String[] optionArr) + { + value = true; + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionPackageWildcard.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionPackageWildcard.java new file mode 100644 index 000000000..33a637db8 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionPackageWildcard.java @@ -0,0 +1,124 @@ +/* gnu.classpath.tools.doclets.DocletOptionPackageWildcard + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.StringTokenizer; +import java.util.TreeSet; + +import com.sun.javadoc.PackageDoc; + +/** + * Processes a doclet option whose value consists of a + * colon-separated list of package wildcards, or - optionally - + * equals the string "all", denoting that all packages should match. + */ +public class DocletOptionPackageWildcard + extends DocletOption +{ + private PackageMatcher packageMatcher; + private boolean allowAll; + private boolean specified; + + DocletOptionPackageWildcard(String optionName, boolean allowAll) + { + super(optionName); + this.allowAll = allowAll; + } + + public int getLength() + { + return 2; + } + + public boolean isSpecified() + { + return specified; + } + + public boolean set(String[] optionArr) + { + this.specified = true; + try { + if (allowAll && "all".equals(optionArr[2])) { + packageMatcher = null; + } + else { + packageMatcher = new PackageMatcher(); + + StringTokenizer tokenizer = new StringTokenizer(optionArr[2], ":"); + while (tokenizer.hasMoreTokens()) { + String packageWildcard = tokenizer.nextToken(); + packageMatcher.addWildcard(packageWildcard); + } + } + return true; + } + catch (InvalidPackageWildcardException e) { + // FIXME: output problem description here, better throw + // DocletConfigurationException + return false; + } + } + + public SortedSet filter(PackageDoc[] packages) + { + if (null != packageMatcher) { + return packageMatcher.filter(packages); + } + else { + SortedSet result = new TreeSet(); + for (int i=0; i<packages.length; ++i) { + result.add(packages[i]); + } + return result; + } + } + + public boolean match(PackageDoc packageDoc) + { + if (null != packageMatcher) { + return packageMatcher.match(packageDoc); + } + else { + return true; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionString.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionString.java new file mode 100644 index 000000000..ecab86541 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/DocletOptionString.java @@ -0,0 +1,68 @@ +/* gnu.classpath.tools.doclets.DocletOptionString + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +/** + * Processes a doclet option whose value is an arbitrary string. + */ +public class DocletOptionString + extends DocletOption +{ + private String value = null; + + public DocletOptionString(String optionName) + { + super(optionName); + } + + public String getValue() + { + return this.value; + } + + public int getLength() + { + return 2; + } + + public boolean set(String[] optionArr) + { + value = optionArr[1]; + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/InlineTagRenderer.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/InlineTagRenderer.java new file mode 100644 index 000000000..f7d4ac573 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/InlineTagRenderer.java @@ -0,0 +1,46 @@ +/* gnu.classpath.tools.doclets.InlineTagRenderer + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +import com.sun.javadoc.Tag; +import gnu.classpath.tools.taglets.TagletContext; + +public interface InlineTagRenderer +{ + public String renderInlineTags(Tag[] tags, TagletContext context); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/InvalidPackageWildcardException.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/InvalidPackageWildcardException.java new file mode 100644 index 000000000..8f34288a3 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/InvalidPackageWildcardException.java @@ -0,0 +1,51 @@ +/* gnu.classpath.tools.doclets.InvalidPackageWildcardException + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +/** + * Thrown when an invalid wildcard pattern is passed to {@link + * PackageMatcher.addWildcard(String)}. + */ +public class InvalidPackageWildcardException + extends Exception +{ + public InvalidPackageWildcardException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/PackageGroup.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/PackageGroup.java new file mode 100644 index 000000000..4278ffb98 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/PackageGroup.java @@ -0,0 +1,58 @@ +/* gnu.classpath.tools.doclets.PackageGroup + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +import java.util.SortedSet; + +/** + * Stores a package group given on the command line. + */ +public class PackageGroup +{ + private String name; + private SortedSet packages; // contains PackageDoc + + public PackageGroup(String name, SortedSet packages) + { + this.name = name; + this.packages = packages; + } + + public String getName() { return name; } + public SortedSet getPackages() { return packages; } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/PackageMatcher.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/PackageMatcher.java new file mode 100644 index 000000000..196b74c88 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/PackageMatcher.java @@ -0,0 +1,152 @@ +/* gnu.classpath.tools.doclets.PackageMatcher + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +import java.util.Iterator; +import java.util.HashSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.sun.javadoc.PackageDoc; + +/** + * Filters a set of packages according to a set of wildcards. + */ +public class PackageMatcher +{ + private Set patterns = new HashSet(); + + /** + * Add a wildcard to be matched. Wildcards can contain asterisk + * characters which match zero or more characters. + * + * @throw InvalidPackageWildcardException if the wildcard cannot + * match any valid package name. + */ + public void addWildcard(String wildcard) + throws InvalidPackageWildcardException + { + final int STATE_ID_START = 0; + final int STATE_ID = 1; + + int state = STATE_ID_START; + + char[] wildcardChars = wildcard.toCharArray(); + StringBuffer regexString = new StringBuffer(); + + for (int i=0; i<wildcardChars.length; ++i) { + char c = wildcardChars[i]; + switch (state) { + case STATE_ID_START: + if ('*' == c) { + regexString.append(".*"); + } + else if (Character.isJavaIdentifierStart(c)) { + regexString.append(c); + } + else { + throw new InvalidPackageWildcardException(wildcard); + } + state = STATE_ID; + break; + + case STATE_ID: + if ('.' == c) { + regexString.append("\\."); + state = STATE_ID_START; + } + else if ('*' == c) { + regexString.append(".*"); + } + else if (Character.isJavaIdentifierPart(c)) { + regexString.append(c); + } + else { + throw new InvalidPackageWildcardException(wildcard); + } + } + } + if (STATE_ID_START == state) { + throw new InvalidPackageWildcardException(wildcard); + } + + patterns.add(Pattern.compile(regexString.toString())); + } + + /** + * Return a sorted, filtered set of packages. A package from the + * array given will be put into the output list if it matches one + * or more of the wildcards added to this PackageMatcher before. + */ + public SortedSet filter(PackageDoc[] packageDocs) + { + SortedSet result = new TreeSet(); + for (int i=0; i<packageDocs.length; ++i) { + if (match(packageDocs[i])) { + result.add(packageDocs[i]); + } + } + return result; + } + + /** + * Return true when the given PackageDoc matches one or more of + * the wildcard added to this PackageMatcher before. + */ + public boolean match(PackageDoc packageDoc) + { + Iterator it = patterns.iterator(); + while (it.hasNext()) { + Pattern pattern = (Pattern)it.next(); + Matcher matcher = pattern.matcher(packageDoc.name()); + if (matcher.matches()) { + return true; + } + } + return false; + } + + public String toString() + { + return "PackageMatcher{patterns=" + patterns + "}"; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/StandardTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/StandardTaglet.java new file mode 100644 index 000000000..f43f2a13c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/StandardTaglet.java @@ -0,0 +1,100 @@ +/* gnu.classpath.tools.doclets.StandardTaglet + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +import com.sun.javadoc.Tag; +import com.sun.tools.doclets.Taglet; + +/** + * Represents one of the built-in taglets. Used for specifying taglet + * order. + */ +public class StandardTaglet + implements Taglet +{ + private String name; + + /** + * Initialize with one of the built-in taglet names. + */ + public StandardTaglet(String name) { + this.name = name; + } + + public boolean inField() { + return true; + } + + public boolean inConstructor() { + return true; + } + + public boolean inMethod() { + return true; + } + + public boolean inOverview() { + return true; + } + + public boolean inPackage() { + return true; + } + + public boolean inType() { + return true; + } + + public boolean isInlineTag() { + return false; + } + + public String getName() { + return this.name; + } + + public String toString(Tag tag) { + //assert(false); + return null; + } + + public String toString(Tag[] tags) { + //assert(false); + return null; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/TagletPrinter.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/TagletPrinter.java new file mode 100644 index 000000000..b59704a51 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/TagletPrinter.java @@ -0,0 +1,46 @@ +/* gnu.classpath.tools.doclets.TagletPrinter + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets; + +/** + * Call-back interface for taglet string output. + */ +public interface TagletPrinter +{ + public void printTagletString(String s); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/debugdoclet/DebugDoclet.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/debugdoclet/DebugDoclet.java new file mode 100644 index 000000000..18bf57950 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/debugdoclet/DebugDoclet.java @@ -0,0 +1,174 @@ +/* DebugDoclet.java - Doclet for debugging + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.debugdoclet; + +import com.sun.javadoc.ClassDoc; +import com.sun.javadoc.FieldDoc; +import com.sun.javadoc.MethodDoc; +import com.sun.javadoc.PackageDoc; +import com.sun.javadoc.RootDoc; +import com.sun.javadoc.Tag; + +import java.io.PrintStream; + +import java.util.Arrays; + +public class DebugDoclet +{ + public static boolean start(RootDoc rootDoc) + { + new DebugDoclet().run(rootDoc); + return true; + } + + private PrintStream out; + + public DebugDoclet() + { + out = System.out; + } + + private void printHeader(String header) + { + out.println(); + out.println("******** " + header + " ********"); + out.println(); + } + + private void printSubHeader(String header) + { + out.println(); + out.println("======== " + header + " ========"); + out.println(); + } + + private void printSub2Header(String header) + { + out.println(); + out.println("-------- " + header + " --------"); + out.println(); + } + + private void run(RootDoc rootDoc) + { + printHeader("Overview"); + + printSubHeader("Specified Packages"); + + PackageDoc[] specifiedPackages = rootDoc.specifiedPackages(); + Arrays.sort(specifiedPackages); + for (int i=0; i<specifiedPackages.length; ++i) { + out.println(specifiedPackages[i].name()); + } + + printSubHeader("Specified Classes"); + + ClassDoc[] specifiedClasses = rootDoc.specifiedClasses(); + Arrays.sort(specifiedClasses); + for (int i=0; i<specifiedClasses.length; ++i) { + out.println(specifiedClasses[i].qualifiedTypeName()); + } + printSubHeader("Classes"); + + ClassDoc[] classes = rootDoc.classes(); + Arrays.sort(classes); + for (int i=0; i<classes.length; ++i) { + out.println(classes[i].qualifiedTypeName()); + } + + printHeader("Class Detail"); + + for (int i=0; i<classes.length; ++i) { + printSubHeader(classes[i].qualifiedTypeName()); + + printTags(classes[i].firstSentenceTags()); + + printSub2Header("Methods"); + + MethodDoc[] methods = classes[i].methods(); + + for (int j=0; j<methods.length; ++j) { + out.println("name: \"" + methods[j].name() + "\""); + out.println("signature: \"" + methods[j].signature() + "\""); + out.println("modifiers: \"" + methods[j].modifiers() + "\""); + out.print("throws: "); + ClassDoc[] thrownExceptions = methods[j].thrownExceptions(); + for (int k=0; k<thrownExceptions.length; ++k) { + if (k>0) { out.print(", "); } + out.print(thrownExceptions[k].qualifiedTypeName()); + } + out.println(); + } + + printSub2Header("Fields"); + + FieldDoc[] fields = classes[i].fields(); + + for (int j=0; j<fields.length; ++j) { + out.println("name: \"" + fields[j].name() + "\""); + out.println("modifiers: \"" + fields[j].modifiers() + "\""); + out.println(); + } + + printSub2Header("Serializable Fields"); + + FieldDoc[] sfields = classes[i].serializableFields(); + + for (int j=0; j<sfields.length; ++j) { + out.println("name: \"" + sfields[j].name() + "\""); + out.println("modifiers: \"" + sfields[j].modifiers() + "\""); + out.println(); + } + } + } + + private void printTag(Tag tag) + { + if (null != tag.text()) { + System.out.println(tag.text()); + } + } + + private void printTags(Tag[] tags) + { + for (int i=0; i<tags.length; ++i) { + out.println("Tag #" + (i+1) + ":"); + printTag(tags[i]); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/CssClass.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/CssClass.java new file mode 100644 index 000000000..ba4c8b685 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/CssClass.java @@ -0,0 +1,300 @@ +/* gnu.classpath.tools.doclets.htmldoclet.CssClass + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.htmldoclet; + +/** + * Represents a CSS (Cascading Stylesheet) class. Supports + * substituting <code>div</code> and <code>span</code> tags by more + * specialized HTML tags. + */ +public class CssClass +{ + public static final CssClass BODY_MENU_PACKAGES = new CssClass("menu packages"); + public static final CssClass BODY_MENU_CLASSES = new CssClass("menu classes"); + public static final CssClass BODY_CONTENT_CLASS = new CssClass("content class"); + public static final CssClass BODY_CONTENT_DEPRECATED = new CssClass("content deprecated"); + public static final CssClass BODY_CONTENT_PACKAGE = new CssClass("content package"); + public static final CssClass BODY_CONTENT_OVERVIEW = new CssClass("content overview"); + public static final CssClass BODY_CONTENT_ABOUT = new CssClass("content about"); + public static final CssClass BODY_CONTENT_HELP = new CssClass("content help"); + public static final CssClass BODY_CONTENT_PACKAGE_TREE = new CssClass("content packagetree"); + public static final CssClass BODY_CONTENT_FULL_TREE = new CssClass("content fulltree"); + public static final CssClass BODY_CONTENT_INDEX = new CssClass("content index"); + public static final CssClass BODY_CONTENT_USES = new CssClass("content uses"); + public static final CssClass BODY_CONTENT_SOURCE = new CssClass("content source"); + + public static final CssClass OVERVIEW_TITLE = new CssClass("overview title", "h1"); + public static final CssClass OVERVIEW_SUMMARY = new CssClass("overview summary"); + public static final CssClass OVERVIEW_SUMMARY_LEFT = new CssClass("left"); + public static final CssClass OVERVIEW_SUMMARY_RIGHT = new CssClass("right"); + public static final CssClass OVERVIEW_DESCRIPTION_TOP = new CssClass("overview description top"); + public static final CssClass OVERVIEW_DESCRIPTION_FULL = new CssClass("overview description full"); + + public static final CssClass DEPRECATION_TITLE = new CssClass("deprecation title", "h1"); + public static final CssClass DEPRECATION_SUMMARY = new CssClass("summary"); + public static final CssClass DEPRECATION_SUMMARY_LEFT = new CssClass("left"); + public static final CssClass DEPRECATION_SUMMARY_DESCRIPTION = new CssClass("description"); + public static final CssClass DEPRECATION_TOC = new CssClass("dep-toc"); + public static final CssClass DEPRECATION_TOC_HEADER = new CssClass("header", "h3"); + public static final CssClass DEPRECATION_TOC_LIST = new CssClass("list", "ul"); + public static final CssClass DEPRECATION_TOC_ENTRY = new CssClass("entry", "li"); + public static final CssClass DEPRECATION_EMPTY = new CssClass("dep-empty", "p"); + public static final CssClass DEPRECATION_LIST = new CssClass("dep-list", "div"); + + public static final CssClass SERIALIZED_TITLE = new CssClass("serialized title", "h1"); + public static final CssClass SERIALIZED_PACKAGE_HEADER = new CssClass("serialized package header", "h2"); + public static final CssClass SERIALIZED_CLASS_HEADER = new CssClass("serialized class header", "h3"); + public static final CssClass SERIALIZED_SVUID_OUTER = new CssClass("serialized svuid outer", "p"); + public static final CssClass SERIALIZED_SVUID_HEADER = new CssClass("serialized svuid header", "b"); + public static final CssClass SERIALIZED_SVUID_VALUE = new CssClass("serialized svuid header"); + public static final CssClass SERIALIZED_SECTION = new CssClass("serialized section"); + public static final CssClass SERIALIZED_SECTION_HEADER = new CssClass("serialized section header", "h4"); + + public static final CssClass PACKAGE_TITLE = new CssClass("package title", "h1"); + public static final CssClass PACKAGE_SUMMARY = new CssClass("package summary"); + public static final CssClass PACKAGE_SUMMARY_LEFT = new CssClass("left"); + public static final CssClass PACKAGE_SUMMARY_RIGHT = new CssClass("right"); + public static final CssClass PACKAGE_DESCRIPTION_TOP = new CssClass("package description top"); + public static final CssClass PACKAGE_DESCRIPTION_FULL = new CssClass("package description full"); + public static final CssClass PACKAGE_TREE_TITLE = new CssClass("package tree title", "h1"); + public static final CssClass PACKAGE_TREE_SECTION_TITLE = new CssClass("package tree section title", "h2"); + public static final CssClass PACKAGE_TREE = new CssClass("tree", "ul"); + + public static final CssClass TREE_LINK = new CssClass("tree link", "b"); + + public static final CssClass FULL_TREE_PACKAGELIST = new CssClass("fulltree package list", "dl"); + public static final CssClass FULL_TREE_PACKAGELIST_HEADER = new CssClass("fulltree package header", "dt", "b"); + public static final CssClass FULL_TREE_PACKAGELIST_ITEM = new CssClass("fulltree package item", "dd"); + + public static final CssClass PACKAGE_MENU_LIST = new CssClass("package menu-list", "div"); + public static final CssClass PACKAGE_MENU_ENTRY = new CssClass("package menu-entry"); + public static final CssClass PACKAGE_MENU_TITLE = new CssClass("package menu-title", "h4"); + + public static final CssClass CLASS_MENU_LIST = new CssClass("classes menu-list", "div"); + public static final CssClass CLASS_MENU_TITLE = new CssClass("classes menu-title", "h4"); + public static final CssClass CLASS_MENU_SUBTITLE = new CssClass("classes menu-subtitle", "p"); + public static final CssClass CLASS_MENU_ENTRY_CLASS = new CssClass("classes menu-entry class"); + public static final CssClass CLASS_MENU_ENTRY_INTERFACE = new CssClass("classes menu-entry interface", "i"); + + public static final CssClass INDEX_TITLE = new CssClass("index title", "h1"); + public static final CssClass INDEX_CATEGORY = new CssClass("index category"); + public static final CssClass INDEX_CATEGORY_HEADER = new CssClass("index category header", "h2"); + public static final CssClass INDEX_ENTRY = new CssClass("index entry"); + public static final CssClass INDEX_ENTRY_DESCRIPTION = new CssClass("description"); + public static final CssClass INDEX_ENTRY_KEY = new CssClass("key"); + public static final CssClass INDEX_LETTERS = new CssClass("index letters"); + public static final CssClass INDEX_LETTER = new CssClass("index letter"); + public static final CssClass INDEX_LETTER_SPACER = new CssClass("index letter spacer"); + + public static final CssClass CLASS_TITLE = new CssClass("class title outer"); + public static final CssClass CLASS_TITLE_PACKAGE = new CssClass("class title-package", "h3"); + public static final CssClass CLASS_TITLE_CLASS = new CssClass("class title-class", "h1"); + public static final CssClass CLASS_SUBCLASSES = new CssClass("class subclasses", "dl"); + public static final CssClass CLASS_SUBCLASSES_HEADER = new CssClass("class subclasses header", "dt", "b"); + public static final CssClass CLASS_SUBCLASSES_ITEM = new CssClass("class subclasses header", "dd"); + public static final CssClass CLASS_ENCLOSINGCLASS = new CssClass("class enclosing", "dl"); + public static final CssClass CLASS_ENCLOSINGCLASS_HEADER = new CssClass("class enclosing header", "dt", "b"); + public static final CssClass CLASS_ENCLOSINGCLASS_ITEM = new CssClass("class enclosing item", "dd"); + public static final CssClass CLASS_KNOWNIMPLEMENTING = new CssClass("class knownimplementing", "dl"); + public static final CssClass CLASS_KNOWNIMPLEMENTING_HEADER = new CssClass("header", "dt", "b"); + public static final CssClass CLASS_KNOWNIMPLEMENTING_ITEM = new CssClass("item", "dd"); + public static final CssClass CLASS_INHERITANCETREE = new CssClass("class inheritance-tree"); + public static final CssClass CLASS_SYNOPSIS = new CssClass("class synopsis outer"); + public static final CssClass CLASS_SYNOPSIS_NAME = new CssClass("class synopsis name", "b"); + public static final CssClass CLASS_SYNOPSIS_DECLARATION = new CssClass("class synopsis declaration", "div", "code"); + public static final CssClass CLASS_SYNOPSIS_SUPERCLASS = new CssClass("class synopsis superclass", "div", "code"); + public static final CssClass CLASS_SYNOPSIS_IMPLEMENTS = new CssClass("class synopsis implements", "div", "code"); + public static final CssClass CLASS_DESCRIPTION = new CssClass("class description"); + public static final CssClass CLASS_SUMMARY = new CssClass("class summary"); + public static final CssClass CLASS_SUMMARY_LEFT = new CssClass("left", new String[] { "valign" }, new String[] { "top" }); + public static final CssClass CLASS_SUMMARY_LEFT_SYNOPSIS = new CssClass("synopsis", "code"); + public static final CssClass CLASS_SUMMARY_RIGHT = new CssClass("right"); + public static final CssClass CLASS_SUMMARY_RIGHT_LIST = new CssClass("list", "dl"); + public static final CssClass CLASS_SUMMARY_RIGHT_SYNOPSIS = new CssClass("synopsis", "dt", "code"); + public static final CssClass CLASS_SUMMARY_RIGHT_DESCRIPTION = new CssClass("description", "dd"); + public static final CssClass CLASS_SUMMARY_INHERITED = new CssClass("inherited"); + public static final CssClass CLASS_SUMMARY_INHERITED_MEMBER = new CssClass("member", "code"); + public static final CssClass CLASS_BOILERPLATE = new CssClass("boilerplate", "pre", new String[] { "style" }, new String[] { "font-size: x-small;" }); + + public static final CssClass USAGE_TITLE = new CssClass("usage title", "h1"); + public static final CssClass USAGE_PACKAGE_TITLE = new CssClass("usage package title", "h2"); + public static final CssClass USAGE_USAGE_TITLE = new CssClass("usage usage title", "h3"); + public static final CssClass USAGE_SUMMARY = new CssClass("usage summary"); + public static final CssClass USAGE_SUMMARY_LEFT = new CssClass("left"); + public static final CssClass USAGE_SUMMARY_RIGHT = new CssClass("right"); + public static final CssClass USAGE_SUMMARY_SYNOPSIS = new CssClass("synopsis"); + public static final CssClass USAGE_SUMMARY_DESCRIPTION = new CssClass("description"); + public static final CssClass USAGE_TABLE_HEADER = new CssClass("table header", "h3"); + public static final CssClass USAGE_EMPTY = new CssClass("usage empty", "p"); + + public static final CssClass MEMBER_DETAIL = new CssClass("member detail outer"); + public static final CssClass MEMBER_DETAIL_NAME = new CssClass("member detail name", "h3"); + public static final CssClass MEMBER_DETAIL_BODY = new CssClass("member detail name", "blockquote"); + public static final CssClass MEMBER_DETAIL_SYNOPSIS = new CssClass("member detail synopsis", "pre"); + public static final CssClass MEMBER_DETAIL_DESCRIPTION = new CssClass("member detail description"); + public static final CssClass MEMBER_DETAIL_SPECIFIED_BY_LIST = new CssClass("member detail specified by list", "dl"); + public static final CssClass MEMBER_DETAIL_SPECIFIED_BY_HEADER = new CssClass("member detail specified by header", "dt", "b"); + public static final CssClass MEMBER_DETAIL_SPECIFIED_BY_ITEM = new CssClass("member detail specified by item", "dd"); + public static final CssClass MEMBER_DETAIL_OVERRIDDEN_LIST = new CssClass("member detail overridden list", "dl"); + public static final CssClass MEMBER_DETAIL_OVERRIDDEN_HEADER = new CssClass("member detail overridden header", "dt", "b"); + public static final CssClass MEMBER_DETAIL_OVERRIDDEN_ITEM = new CssClass("member detail overridden item", "dd"); + public static final CssClass MEMBER_DETAIL_PARAMETER_LIST = new CssClass("parameter", "div", "dl"); + public static final CssClass MEMBER_DETAIL_PARAMETER_HEADER = new CssClass("header", "dt", "b"); + public static final CssClass MEMBER_DETAIL_PARAMETER_ITEM = new CssClass("item", "dd"); + public static final CssClass MEMBER_DETAIL_PARAMETER_ITEM_NAME = new CssClass("name", "code"); + public static final CssClass MEMBER_DETAIL_PARAMETER_ITEM_SEPARATOR = new CssClass("separator"); + public static final CssClass MEMBER_DETAIL_PARAMETER_ITEM_DESCRIPTION = new CssClass("description"); + public static final CssClass MEMBER_DETAIL_RETURN_LIST = new CssClass("member detail return list", "div", "dl"); + public static final CssClass MEMBER_DETAIL_RETURN_HEADER = new CssClass("member detail return header", "dt", "b"); + public static final CssClass MEMBER_DETAIL_RETURN_ITEM = new CssClass("member detail return item", "dd"); + public static final CssClass MEMBER_DETAIL_THROWN_LIST = new CssClass("member detail thrown list", "div", "dl"); + public static final CssClass MEMBER_DETAIL_THROWN_HEADER = new CssClass("member detail thrown header", "dt", "b"); + public static final CssClass MEMBER_DETAIL_THROWN_ITEM = new CssClass("member detail thrown item", "dd"); + public static final CssClass MEMBER_DETAIL_THROWN_ITEM_NAME = new CssClass("name", "code"); + public static final CssClass MEMBER_DETAIL_THROWN_ITEM_SEPARATOR = new CssClass("separator"); + public static final CssClass MEMBER_DETAIL_THROWN_ITEM_DESCRIPTION = new CssClass("description"); + + public static final CssClass TABLE_HEADER = new CssClass("table header", "h2"); + public static final CssClass TABLE_SUB_HEADER = new CssClass("table sub header", "h3"); + public static final CssClass TABLE_CONTAINER = new CssClass("table container", "dl", "dd"); + + public static final CssClass SECTION = new CssClass("section", "div"); + public static final CssClass SECTION_HEADER = new CssClass("section header", "h2"); + + public static final CssClass NAVBAR_TOP = new CssClass("navbar div top"); + public static final CssClass NAVBAR_TOP_NAVI = new CssClass("navi"); + public static final CssClass NAVBAR_TOP_HEADER = new CssClass("header", new String[] { "rowspan" }, new String[] { "2" }); + public static final CssClass NAVBAR_BOTTOM = new CssClass("navbar div bottom"); + public static final CssClass NAVBAR_BOTTOM_SPACER = new CssClass("navbar bottom spacer", "p"); + public static final CssClass NAVBAR_ITEM_ENABLED = new CssClass("navbar item enabled"); + public static final CssClass NAVBAR_ITEM_DISABLED = new CssClass("navbar item disabled"); + public static final CssClass NAVBAR_ITEM_ACTIVE = new CssClass("navbar item active"); + + public static final CssClass TAGLET = new CssClass("taglet", "div"); + + public static final CssClass ABOUT_TITLE = new CssClass("about title", "h1"); + public static final CssClass ABOUT_GENERATOR = new CssClass("about generator", "p"); + + public static final CssClass SOURCE = new CssClass("source body"); + public static final CssClass SOURCE_TITLE = new CssClass("source title", "h1"); + + public static final CssClass DEPRECATED = new CssClass("deprecated", "span"); + public static final CssClass DEPRECATED_INLINE = new CssClass("deprecated", "p"); + public static final CssClass DEPRECATED_HEADER = new CssClass("deprecated header", "b"); + public static final CssClass DEPRECATED_BODY = new CssClass("deprecated", "i"); + + private String name; + private String elementName; + private String innerElementName; + private String[] attributeNames; + private String[] attributeValues; + + private CssClass(String name) + { + this(name, null); + } + + private CssClass(String name, String elementName) + { + this(name, elementName, null); + } + + private CssClass(String name, String elementName, String innerElementName) + { + this(name, elementName, innerElementName, null, null); + } + + private CssClass(String name, String elementName, String[] attributeNames, String[] attributeValues) + { + this(name, null, null, attributeNames, attributeValues); + } + + private CssClass(String name, String[] attributeNames, String[] attributeValues) + { + this(name, null, null, attributeNames, attributeValues); + } + + private CssClass(String name, String elementName, String innerElementName, String[] attributeNames, String[] attributeValues) + { + this.name = name; + this.elementName = elementName; + this.innerElementName = innerElementName; + this.attributeNames = attributeNames; + this.attributeValues = attributeValues; + } + + public String getSpanElementName() + { + if (null != this.elementName) { + return this.elementName; + } + else { + return "span"; + } + } + + public String getDivElementName() + { + if (null != this.elementName) { + return this.elementName; + } + else { + return "div"; + } + } + + public String getInnerElementName() + { + return this.innerElementName; + } + + public String[] getAttributeNames() + { + return this.attributeNames; + } + + public String[] getAttributeValues() + { + return this.attributeValues; + } + + public String getName() + { + return name; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/ExternalDocSet.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/ExternalDocSet.java new file mode 100644 index 000000000..f3b2fa8ca --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/ExternalDocSet.java @@ -0,0 +1,187 @@ +/* gnu.classpath.tools.doclets.htmldoclet.ExternalDocSet + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.htmldoclet; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +import com.sun.javadoc.ClassDoc; + +public class ExternalDocSet +{ + private String url; + private String packageListDir; + private URL docSetDirectoryURL; + + public String getPackageListDir() + { + return packageListDir; + } + + public ExternalDocSet(String url, + String packageListDir) + { + this.url = url; + this.packageListDir = packageListDir; + } + + private Set packageNames = new HashSet(); + private boolean javadocCompatible; + + public void load(File targetDirectory) + throws IOException, MalformedURLException + { + if (!url.endsWith("/")) { + url += "/"; + } + + this.docSetDirectoryURL = new URL(targetDirectory.toURL(), + url); + + URL packageListDirURL; + if (null != packageListDir) { + if (!packageListDir.endsWith("/")) { + packageListDir += "/"; + } + packageListDirURL = new URL(new File(System.getProperty("user.dir")).toURL(), + packageListDir); + } + else { + packageListDirURL = docSetDirectoryURL; + } + + URL packageListURL = new URL(packageListDirURL, + "package-list"); + InputStream in = packageListURL.openStream(); + if (null != in) { + readPackages(in); + in.close(); + } + else { + throw new FileNotFoundException(packageListURL.toString()); + } + + URL gjdocPropertiesURL = new URL(packageListDirURL, + "gjdoc.properties"); + try { + InputStream propertiesIn = gjdocPropertiesURL.openStream(); + if (null != in) { + Properties properties = new Properties(); + properties.load(propertiesIn); + propertiesIn.close(); + + String gjdocCompatProperty = properties.getProperty("gjdoc.compat"); + if (null != gjdocCompatProperty) { + javadocCompatible = "true".equals(properties.getProperty("gjdoc.compat")); + } + else { + javadocCompatible = true; + } + } + else { + javadocCompatible = true; + } + } + catch (FileNotFoundException e) { + javadocCompatible = true; + } + } + + public String getPackageDocURL(String packageName) + { + try { + URL packageURL = new URL(docSetDirectoryURL, + packageName.replace('.', '/')); + return packageURL.toString(); + } + catch (MalformedURLException e) { + // This should not happen since we know that packageName is a + // proper Java identifiers, so the resulting URL can't be + // invalid + throw new RuntimeException(e); + } + } + + public String getClassDocURL(String packageName, String typeName) + { + try { + URL fileURL = new URL(docSetDirectoryURL, + packageName.replace('.', '/') + "/" + typeName + ".html"); + return fileURL.toString(); + } + catch (MalformedURLException e) { + // This should not happen since we know that packageName and + // typeName are proper Java identifiers, so the resulting URL + // can't be invalid + throw new RuntimeException(e); + } + } + + protected void readPackages(InputStream in) + throws IOException + { + BufferedReader reader + = new BufferedReader(new InputStreamReader(in, "UTF-8")); + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + packageNames.add(line); + } + } + + public Set getPackageNames() + { + return packageNames; + } + + public boolean isJavadocCompatible() + { + return javadocCompatible; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlDoclet.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlDoclet.java new file mode 100644 index 000000000..5ec980634 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlDoclet.java @@ -0,0 +1,3887 @@ +/* gnu.classpath.tools.doclets.htmldoclet.HtmlDoclet + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.htmldoclet; + +import gnu.classpath.tools.IOToolkit; + +import gnu.classpath.tools.doclets.AbstractDoclet; +import gnu.classpath.tools.doclets.DocletConfigurationException; +import gnu.classpath.tools.doclets.DocletOption; +import gnu.classpath.tools.doclets.DocletOptionFile; +import gnu.classpath.tools.doclets.DocletOptionFlag; +import gnu.classpath.tools.doclets.DocletOptionString; +import gnu.classpath.tools.doclets.PackageGroup; +import gnu.classpath.tools.doclets.TagletPrinter; +import gnu.classpath.tools.doclets.InlineTagRenderer; + +import gnu.classpath.tools.doclets.xmldoclet.HtmlRepairer; + +import gnu.classpath.tools.taglets.GnuExtendedTaglet; +import gnu.classpath.tools.taglets.TagletContext; + +import gnu.classpath.tools.java2xhtml.Java2xhtml; + +import gnu.classpath.tools.StringToolkit; + +import com.sun.javadoc.*; +import com.sun.tools.doclets.Taglet; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.net.MalformedURLException; + +import java.nio.charset.Charset; + +import java.text.DateFormat; +import java.text.MessageFormat; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.SortedSet; +import java.util.TimeZone; +import java.util.TreeSet; + +public class HtmlDoclet + extends AbstractDoclet + implements InlineTagRenderer +{ + private static String filenameExtension = ".html"; + + /** + * Contains ExternalDocSet. + */ + private List externalDocSets = new LinkedList(); + + /** + * Contains String->ExternalDocSet. + */ + private Map packageNameToDocSet = new HashMap(); + + /** + * Cache for version string from resource /version.properties + */ + private String docletVersion; + + /** + * For now, do not output a help page. + */ + private static final boolean outputHelpPage = false; + + /** + * Stores the output encoding (either the one specified using + * -charset, or the platform default encoding). + */ + private String outputCharset; + + private void printNavBar(HtmlPage output, String currentPage, ClassDoc currentClass) + { + output.beginDiv(CssClass.NAVBAR_TOP); + + boolean overviewLevel + = ("overview".equals(currentPage) + || "full-tree".equals(currentPage) + || "index".equals(currentPage) + || "split-index".equals(currentPage) + || "serialized".equals(currentPage) + || "deprecated".equals(currentPage) + || "about".equals(currentPage) + ); + + if (!isSinglePackage()) { + if ("overview".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Overview"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + output.beginAnchor(output.getPathToRoot() + "/overview-summary" + filenameExtension); + output.print("Overview"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + + output.print(" "); + } + + if (!overviewLevel || isSinglePackage()) { + if ("package".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Package"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + String packageHref; + if (isSinglePackage()) { + packageHref = output.getPathToRoot() + "/" + getPackageURL(getSinglePackage()) + "package-summary" + filenameExtension; + } + else { + packageHref = "package-summary" + filenameExtension; + } + output.beginAnchor(packageHref); + output.print("Package"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_DISABLED); + output.print("Package"); + output.endSpan(CssClass.NAVBAR_ITEM_DISABLED); + } + + if (optionUse.getValue() || optionLinkSource.getValue()) { + output.print(" "); + + if (null != currentClass) { + if ("class".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Class"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + output.beginAnchor(currentClass.name() + filenameExtension); + output.print("Class"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_DISABLED); + output.print("Class"); + output.endSpan(CssClass.NAVBAR_ITEM_DISABLED); + } + + if (optionUse.getValue()) { + output.print(" "); + + if (null != currentClass) { + if ("uses".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Use"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + output.beginAnchor(currentClass.name() + "-uses" + filenameExtension); + output.print("Use"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_DISABLED); + output.print("Use"); + output.endSpan(CssClass.NAVBAR_ITEM_DISABLED); + } + } + + if (optionLinkSource.getValue()) { + output.print(" "); + + + if ("source".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Source"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + + if (null != currentClass) { + + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + String targetClassName = currentClass.name(); + String targetAnchor = ""; + if (null != currentClass.containingClass()) { + targetClassName = getOuterClassDoc(currentClass).name(); + targetAnchor = "#line." + currentClass.position().line(); + } + output.beginAnchor(targetClassName + "-source" + filenameExtension + targetAnchor); + output.print("Source"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_DISABLED); + output.print("Source"); + output.endSpan(CssClass.NAVBAR_ITEM_DISABLED); + } + } + } + } + + + if (!optionNoTree.getValue()) { + output.print(" "); + + if ("full-tree".equals(currentPage) + || "package-tree".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Tree"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + String treeHref; + if (isSinglePackage() && overviewLevel) { + treeHref = getPackageURL(getSinglePackage()) + "tree" + filenameExtension; + } + else { + treeHref = "tree" + filenameExtension; + } + + output.beginAnchor(treeHref); + output.print("Tree"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + } + + output.print(" "); + + String indexName; + if (optionSplitIndex.getValue()) { + indexName = "alphaindex-1"; + } + else { + indexName = "alphaindex"; + } + + if ("index".equals(currentPage) || "split-index".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Index"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + output.beginAnchor(output.getPathToRoot() + "/" + indexName + filenameExtension); + output.print("Index"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + + if (!optionNoDeprecatedList.getValue()) { + output.print(" "); + + if ("deprecated".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Deprecated"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + output.beginAnchor(output.getPathToRoot() + "/deprecated" + filenameExtension); + output.print("Deprecated"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + } + + if (outputHelpPage) { + if (!optionNoHelp.getValue()) { + output.print(" "); + + if ("help".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("Help"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + output.beginAnchor(output.getPathToRoot() + "/help" + filenameExtension); + output.print("Help"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + } + } + + output.print(" "); + + if ("about".equals(currentPage)) { + output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE); + output.print("About"); + output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE); + } + else { + output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED); + output.beginAnchor(output.getPathToRoot() + "/about" + filenameExtension); + output.print("About"); + output.endAnchor(); + output.endSpan(CssClass.NAVBAR_ITEM_ENABLED); + } + + output.endDiv(CssClass.NAVBAR_TOP); + } + + private void printNavBarTopRow(HtmlPage output, String currentPage, ClassDoc currentClass) + { + output.beginRow(); + output.beginCell(CssClass.NAVBAR_TOP); + printNavBar(output, currentPage, currentClass); + output.endCell(); + if (null != optionHeader.getValue()) { + output.beginCell(CssClass.NAVBAR_TOP_HEADER); + output.print(replaceDocRoot(output, optionHeader.getValue())); + output.endCell(); + } + output.endRow(); + } + + private void printNavBarTopNaviCell(HtmlPage output) + { + output.beginCell(CssClass.NAVBAR_TOP_NAVI); + output.beginAnchor(output.getPathToRoot() + "/index" + filenameExtension, "Show in a frameset", "_top"); + output.print("Frames"); + output.endAnchor(); + output.print(" | "); + + output.beginAnchor(output.getFile().getName(), "Show without frames", "_top"); + output.print("No Frames"); + output.endAnchor(); + output.print(" "); + + output.endCell(); + } + + private void printNavBarTop(HtmlPage output, String currentPage) + { + printNavBarTop(output, currentPage, null, null, null); + } + + private void printNavBarTop(HtmlPage output, String currentPage, + ClassDoc currentClass, Object prev, Object next) + { + if (!optionNoNavBar.getValue()) { + output.beginTable(CssClass.NAVBAR_TOP); + printNavBarTopRow(output, currentPage, currentClass); + output.beginRow(); + if ("class".equals(currentPage)) { + output.beginCell(CssClass.NAVBAR_TOP_NAVI); + ClassDoc prevClass = (ClassDoc)prev; + ClassDoc nextClass = (ClassDoc)next; + if (null != prevClass) { + output.anchor(getClassDocURL(output, prevClass), "Prev Class"); + } + else { + output.print("Prev Class"); + } + output.print(" | "); + if (null != nextClass) { + output.anchor(getClassDocURL(output, nextClass), "Next Class"); + } + else { + output.print("Next Class"); + } + output.endCell(); + } + else if ("split-index".equals(currentPage)) { + output.beginCell(CssClass.NAVBAR_TOP_NAVI); + Integer prevLetter = (Integer)prev; + Integer nextLetter = (Integer)next; + if (null != prevLetter) { + output.anchor("alphaindex-" + prevLetter + filenameExtension, "Prev Letter"); + } + else { + output.print("Prev Letter"); + } + output.print(" | "); + if (null != nextLetter) { + output.anchor("alphaindex-" + nextLetter + filenameExtension, "Next Letter"); + } + else { + output.print("Next Letter"); + } + output.endCell(); + } + else { + output.beginCell(CssClass.NAVBAR_TOP_NAVI); + output.endCell(); + } + + printNavBarTopNaviCell(output); + output.endRow(); + + if ("class".equals(currentPage)) { + output.beginRow(); + + output.beginCell(CssClass.NAVBAR_TOP_NAVI); + output.print("Summary: "); + + if (currentClass.innerClasses().length > 0) { + output.anchor("#summary-inner", "Nested"); + } + else { + output.print("Nested"); + } + + output.print(" | "); + + if (currentClass.fields().length > 0) { + output.anchor("#summary-fields", "Field"); + } + else { + output.print("Field"); + } + + output.print(" | "); + + if (currentClass.methods().length > 0) { + output.anchor("#summary-methods", "Method"); + } + else { + output.print("Method"); + } + + output.print(" | "); + + if (currentClass.constructors().length > 0) { + output.anchor("#summary-constructors", "Constr"); + } + else { + output.print("Constr"); + } + + output.endCell(); + + output.beginCell(CssClass.NAVBAR_TOP_NAVI); + output.print("Detail: "); + + if (currentClass.innerClasses().length > 0) { + output.anchor("#detail-inner", "Nested"); + } + else { + output.print("Nested"); + } + + output.print(" | "); + + if (currentClass.fields().length > 0) { + output.anchor("#detail-fields", "Field"); + } + else { + output.print("Field"); + } + + output.print(" | "); + + if (currentClass.methods().length > 0) { + output.anchor("#detail-methods", "Method"); + } + else { + output.print("Method"); + } + + output.print(" | "); + + if (currentClass.constructors().length > 0) { + output.anchor("#detail-constructors", "Constr"); + } + else { + output.print("Constr"); + } + + output.endCell(); + output.endRow(); + } + output.endTable(); + } + } + + private void printNavBarTopPackage(HtmlPage output, String currentPage, + PackageDoc prevPackage, PackageDoc nextPackage) + { + if (!optionNoNavBar.getValue()) { + output.beginTable(CssClass.NAVBAR_TOP); + printNavBarTopRow(output, currentPage, null); + + output.beginRow(); + output.beginCell(CssClass.NAVBAR_TOP_NAVI); + if (null != prevPackage) { + output.anchor(output.getPathToRoot() + "/" + getPackageURL(prevPackage) + "package-summary" + filenameExtension, "Prev Package"); + } + else { + output.print("Prev Package"); + } + output.print(" | "); + if (null != nextPackage) { + output.anchor(output.getPathToRoot() + "/" + getPackageURL(nextPackage) + "package-summary" + filenameExtension, "Next Package"); + } + else { + output.print("Next Package"); + } + output.endCell(); + + printNavBarTopNaviCell(output); + output.endRow(); + + output.endTable(); + } + } + + private void printNavBarBottom(HtmlPage output, String currentPage) + { + printNavBarBottom(output, currentPage, null); + } + + private void printNavBarBottom(HtmlPage output, String currentPage, ClassDoc currentClass) + { + if ("class".equals(currentPage)) { + String boilerplate = null; + Tag[] boilerplateTags = getOuterClassDoc(currentClass).tags("@boilerplate"); + if (boilerplateTags.length > 0) { + boilerplate = boilerplateTags[0].text(); + } + if (null != boilerplate) { + output.hr(); + output.beginDiv(CssClass.CLASS_BOILERPLATE); + output.print(boilerplate); + output.endDiv(CssClass.CLASS_BOILERPLATE); + output.hr(); + } + } + + if (!optionNoNavBar.getValue()) { + output.beginDiv(CssClass.NAVBAR_BOTTOM_SPACER); + output.print(" "); + output.endDiv(CssClass.NAVBAR_BOTTOM_SPACER); + output.beginTable(CssClass.NAVBAR_BOTTOM); + output.beginRow(); + output.beginCell(); + printNavBar(output, currentPage, currentClass); + output.endCell(); + if (null != optionFooter.getValue()) { + output.beginCell(); + output.print(replaceDocRoot(output, optionFooter.getValue())); + output.endCell(); + } + output.endRow(); + output.endTable(); + } + + if (null != optionBottom.getValue()) { + output.hr(); + output.print(replaceDocRoot(output, optionBottom.getValue())); + } + } + + private void printPackagePageClasses(HtmlPage output, ClassDoc[] classDocs, String header) + { + if (classDocs.length > 0) { + output.beginDiv(CssClass.TABLE_CONTAINER); + output.beginTable(CssClass.PACKAGE_SUMMARY, new String[] { "border", "width" }, new String[] { "1", "100%" }); + output.rowDiv(CssClass.TABLE_HEADER, header); + + for (int i=0; i<classDocs.length; ++i) { + ClassDoc classDoc = classDocs[i]; + if (classDoc.isIncluded()) { + output.beginRow(); + + output.beginCell(CssClass.PACKAGE_SUMMARY_LEFT); + printType(output, classDoc); + output.endCell(); + + output.beginCell(CssClass.PACKAGE_SUMMARY_RIGHT); + printTags(output, classDoc, classDoc.firstSentenceTags(), true); + output.endCell(); + output.endRow(); + } + } + output.endTable(); + output.endDiv(CssClass.TABLE_CONTAINER); + output.print("\n"); + } + } + + private void printPackagesListFile() + throws IOException + { + PrintWriter out + = new PrintWriter(new OutputStreamWriter(new FileOutputStream(new File(getTargetDirectory(), + "package-list")), + "UTF-8")); + + PackageDoc[] packages = getRootDoc().specifiedPackages(); + for (int i=0; i<packages.length; ++i) { + String packageName = packages[i].name(); + if (packageName.length() > 0) { + out.println(packageName); + } + } + + out.close(); + } + + private void printPackagePage(File packageDir, String pathToRoot, + PackageDoc packageDoc, + PackageDoc prevPackageDoc, + PackageDoc nextPackageDoc) + throws IOException + { + HtmlPage output = newHtmlPage(new File(packageDir, "package-summary" + filenameExtension), + pathToRoot); + + Set keywords = new LinkedHashSet(); + keywords.add(packageDoc.name() + " packages"); + + output.beginPage(getPageTitle(packageDoc.name()), getOutputCharset(), + keywords, getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_PACKAGE); + printNavBarTopPackage(output, "package", prevPackageDoc, nextPackageDoc); + + output.beginDiv(CssClass.PACKAGE_TITLE); + output.print("Package "); + if (packageDoc.name().length() > 0) { + output.print(packageDoc.name()); + } + else { + output.print("<Unnamed>"); + } + output.endDiv(CssClass.PACKAGE_TITLE); + + output.beginDiv(CssClass.PACKAGE_DESCRIPTION_TOP); + printTags(output, packageDoc, packageDoc.firstSentenceTags(), true); + output.endDiv(CssClass.PACKAGE_DESCRIPTION_TOP); + + printPackagePageClasses(output, packageDoc.interfaces(), + "Interface Summary"); + printPackagePageClasses(output, packageDoc.ordinaryClasses(), + "Class Summary"); + printPackagePageClasses(output, packageDoc.exceptions(), + "Exception Summary"); + printPackagePageClasses(output, packageDoc.errors(), + "Error Summary"); + + output.anchorName("description"); + output.beginDiv(CssClass.PACKAGE_DESCRIPTION_FULL); + printTags(output, packageDoc, packageDoc.inlineTags(), false); + output.endDiv(CssClass.PACKAGE_DESCRIPTION_FULL); + + printNavBarBottom(output, "package"); + output.endBody(); + output.endPage(); + output.close(); + } + + static class TreeNode + implements Comparable + { + ClassDoc classDoc; + SortedSet children = new TreeSet(); + + TreeNode(ClassDoc classDoc) { + TreeNode.this.classDoc = classDoc; + } + + public boolean equals(Object other) + { + return classDoc.equals(((TreeNode)other).classDoc); + } + + public int compareTo(Object other) + { + return classDoc.compareTo(((TreeNode)other).classDoc); + } + + public int hashCode() + { + return classDoc.hashCode(); + } + } + + private TreeNode addClassTreeNode(Map treeMap, ClassDoc classDoc) + { + TreeNode node = (TreeNode)treeMap.get(classDoc.qualifiedName()); + if (null == node) { + node = new TreeNode(classDoc); + treeMap.put(classDoc.qualifiedName(), node); + + ClassDoc superClassDoc = (ClassDoc)classDoc.superclass(); + if (null != superClassDoc) { + TreeNode parentNode = addClassTreeNode(treeMap, superClassDoc); + parentNode.children.add(node); + } + } + return node; + } + + private TreeNode addInterfaceTreeNode(Map treeMap, ClassDoc classDoc) + { + TreeNode node = (TreeNode)treeMap.get(classDoc.qualifiedName()); + if (null == node) { + node = new TreeNode(classDoc); + treeMap.put(classDoc.qualifiedName(), node); + + ClassDoc[] superInterfaces = classDoc.interfaces(); + if (null != superInterfaces && superInterfaces.length > 0) { + for (int i=0; i<superInterfaces.length; ++i) { + TreeNode parentNode = addInterfaceTreeNode(treeMap, superInterfaces[i]); + parentNode.children.add(node); + } + } + else { + TreeNode rootNode = (TreeNode)treeMap.get("<root>"); + if (null == rootNode) { + rootNode = new TreeNode(null); + treeMap.put("<root>", rootNode); + } + rootNode.children.add(node); + } + } + return node; + } + + private void printPackageTreeRec(HtmlPage output, TreeNode node, TreeNode parentNode) + { + output.beginElement("li", "class", "node"); + output.beginElement("div"); + if (node.classDoc.isIncluded()) { + String packageName = node.classDoc.containingPackage().name(); + if (packageName.length() > 0) { + output.print(packageName); + output.print("."); + } + output.beginSpan(CssClass.TREE_LINK); + printType(output, node.classDoc); + output.endSpan(CssClass.TREE_LINK); + } + else { + output.print(possiblyQualifiedName(node.classDoc)); + } + ClassDoc[] interfaces = node.classDoc.interfaces(); + ClassDoc parentClassDoc = null; + if (null != parentNode) { + parentClassDoc = parentNode.classDoc; + } + if (interfaces.length > 0 + && !(interfaces.length == 1 && interfaces[0].equals(parentClassDoc))) { + if (node.classDoc.isInterface()) { + output.print(" (also implements "); + } + else { + output.print(" (implements "); + } + + boolean firstItem = true; + for (int i=0; i<interfaces.length; ++i) { + ClassDoc implemented = interfaces[i]; + if (!implemented.equals(parentClassDoc)) { + if (!firstItem) { + output.print(", "); + } + firstItem = false; + if (implemented.isIncluded()) { + output.print(implemented.containingPackage().name()); + output.print("."); + printType(output, implemented); + } + else { + output.print(possiblyQualifiedName(implemented)); + } + } + } + output.print(")"); + } + + output.endElement("div"); + output.endElement("li"); + if (!node.children.isEmpty()) { + output.beginElement("li", "class", "level"); + output.beginElement("ul"); + Iterator it = node.children.iterator(); + while (it.hasNext()) { + TreeNode child = (TreeNode)it.next(); + printPackageTreeRec(output, child, node); + } + output.endElement("ul"); + output.endElement("li"); + } + } + + private void printClassTree(HtmlPage output, ClassDoc[] classDocs) + { + Map classTreeMap = new HashMap(); + + for (int i=0; i<classDocs.length; ++i) { + ClassDoc classDoc = classDocs[i]; + if (!classDoc.isInterface()) { + addClassTreeNode(classTreeMap, classDoc); + } + } + + TreeNode root = (TreeNode)classTreeMap.get("java.lang.Object"); + if (null != root) { + output.div(CssClass.PACKAGE_TREE_SECTION_TITLE, "Class Hierarchy"); + output.beginDiv(CssClass.PACKAGE_TREE); + printPackageTreeRec(output, root, null); + output.endDiv(CssClass.PACKAGE_TREE); + } + } + + private void printInterfaceTree(HtmlPage output, ClassDoc[] classDocs) + { + Map interfaceTreeMap = new HashMap(); + + for (int i=0; i<classDocs.length; ++i) { + ClassDoc classDoc = classDocs[i]; + if (classDoc.isInterface()) { + addInterfaceTreeNode(interfaceTreeMap, classDoc); + } + } + + TreeNode interfaceRoot = (TreeNode)interfaceTreeMap.get("<root>"); + if (null != interfaceRoot) { + Iterator it = interfaceRoot.children.iterator(); + if (it.hasNext()) { + output.div(CssClass.PACKAGE_TREE_SECTION_TITLE, "Interface Hierarchy"); + output.beginDiv(CssClass.PACKAGE_TREE); + while (it.hasNext()) { + TreeNode node = (TreeNode)it.next(); + printPackageTreeRec(output, node, null); + } + output.endDiv(CssClass.PACKAGE_TREE); + } + } + + } + + private void printPackageTreePage(File packageDir, String pathToRoot, PackageDoc packageDoc) + throws IOException + { + HtmlPage output = newHtmlPage(new File(packageDir, + "tree" + filenameExtension), + pathToRoot); + output.beginPage(getPageTitle(packageDoc.name() + " Hierarchy"), + getOutputCharset(), + getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_PACKAGE_TREE); + printNavBarTop(output, "package-tree"); + + output.div(CssClass.PACKAGE_TREE_TITLE, "Hierarchy for Package " + packageDoc.name()); + + ClassDoc[] classDocs = packageDoc.allClasses(); + printClassTree(output, classDocs); + printInterfaceTree(output, classDocs); + + printNavBarBottom(output, "package-tree"); + output.endBody(); + output.endPage(); + output.close(); + } + + private void printFullTreePage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "tree" + filenameExtension), + "."); + output.beginPage(getPageTitle("Hierarchy"), + getOutputCharset(), + getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_FULL_TREE); + printNavBarTop(output, "full-tree"); + + output.div(CssClass.PACKAGE_TREE_TITLE, "Hierarchy for All Packages"); + + output.beginDiv(CssClass.FULL_TREE_PACKAGELIST); + output.div(CssClass.FULL_TREE_PACKAGELIST_HEADER, "Package Hierarchies:"); + output.beginDiv(CssClass.FULL_TREE_PACKAGELIST_ITEM); + Set allPackages = getAllPackages(); + Iterator it = allPackages.iterator(); + while (it.hasNext()) { + PackageDoc packageDoc = (PackageDoc)it.next(); + output.beginAnchor(getPackageURL(packageDoc) + "tree" + filenameExtension); + output.print(packageDoc.name()); + output.endAnchor(); + if (it.hasNext()) { + output.print(", "); + } + } + output.endDiv(CssClass.FULL_TREE_PACKAGELIST_ITEM); + output.endDiv(CssClass.FULL_TREE_PACKAGELIST); + + ClassDoc[] classDocs = getRootDoc().classes(); + printClassTree(output, classDocs); + printInterfaceTree(output, classDocs); + + printNavBarBottom(output, "full-tree"); + output.endBody(); + output.endPage(); + output.close(); + } + + private void printIndexEntry(HtmlPage output, Doc entry) + { + output.beginDiv(CssClass.INDEX_ENTRY); + output.beginDiv(CssClass.INDEX_ENTRY_KEY); + String anchor = null; + String description = null; + if (entry instanceof PackageDoc) { + output.beginAnchor(getPackageURL((PackageDoc)entry) + "package-summary" + filenameExtension); + output.print(entry.name()); + output.endAnchor(); + output.print(" - package"); + } + else if (entry instanceof ClassDoc) { + ClassDoc classDoc = (ClassDoc)entry; + output.beginAnchor(getClassURL(classDoc)); + output.print(entry.name() + getTypeParameters(classDoc)); + output.endAnchor(); + output.print(" - "); + if (entry.isInterface()) { + output.print("interface "); + } + else if (entry.isException()) { + output.print("exception "); + } + else if (entry.isError()) { + output.print("error "); + } + else { + output.print("class "); + } + String packageName = classDoc.containingPackage().name(); + if (packageName.length() > 0) { + output.print(packageName); + output.print("."); + } + printType(output, classDoc); + } + else { + ProgramElementDoc memberDoc = (ProgramElementDoc)entry; + output.beginAnchor(getMemberDocURL(output, memberDoc)); + output.print(entry.name()); + if (memberDoc instanceof ExecutableMemberDoc) { + output.print(((ExecutableMemberDoc)memberDoc).signature()); + } + output.endAnchor(); + output.print(" - "); + + if (memberDoc.isStatic()) { + output.print("static "); + } + + if (entry.isConstructor()) { + output.print("constructor for class "); + } + else if (entry.isMethod()) { + output.print("method in class "); + } + else if (entry.isField()) { + output.print("field in class "); + } + ClassDoc containingClass = memberDoc.containingClass(); + String packageName = containingClass.containingPackage().name(); + if (packageName.length() > 0) { + output.print(packageName); + output.print("."); + } + printType(output, containingClass); + } + output.endDiv(CssClass.INDEX_ENTRY_KEY); + output.beginDiv(CssClass.INDEX_ENTRY_DESCRIPTION); + printTags(output, entry, entry.firstSentenceTags(), true); + output.endDiv(CssClass.INDEX_ENTRY_DESCRIPTION); + output.endDiv(CssClass.INDEX_ENTRY); + } + + private void printFrameSetPage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "index" + filenameExtension), + ".", + HtmlPage.DOCTYPE_FRAMESET); + + String title = getWindowTitle(); + output.beginPage(title, getOutputCharset(), getStylesheets()); + output.beginElement("frameset", "cols", "20%,80%"); + + String contentURL; + if (isSinglePackage()) { + output.atomicElement("frame", + new String[] { "src", "name" }, + new String[] { getPackageURL(getSinglePackage()) + "classes" + filenameExtension, "classes" }); + contentURL = getPackageURL(getSinglePackage()) + "package-summary.html"; + } + else { + output.beginElement("frameset", "rows", "25%,75%"); + output.atomicElement("frame", + new String[] { "src", "name" }, + new String[] { "all-packages" + filenameExtension, "packages" }); + output.atomicElement("frame", + new String[] { "src", "name" }, + new String[] { "all-classes" + filenameExtension, "classes" }); + output.endElement("frameset"); + contentURL = "overview-summary" + filenameExtension; + } + output.atomicElement("frame", + new String[] { "src", "name" }, + new String[] { contentURL, "content" }); + output.endElement("frameset"); + output.endPage(); + output.close(); + } + + private void printPackagesMenuPage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "all-packages" + filenameExtension), + "."); + output.beginPage(getPageTitle("Package Menu"), getOutputCharset(), getStylesheets()); + output.beginBody(CssClass.BODY_MENU_PACKAGES, false); + + output.beginSpan(CssClass.PACKAGE_MENU_ENTRY); + output.beginAnchor("all-classes" + filenameExtension, + null, + "classes"); + output.print("All Classes"); + output.endAnchor(); + output.endSpan(CssClass.PACKAGE_MENU_ENTRY); + + output.div(CssClass.PACKAGE_MENU_TITLE, "Packages"); + + output.beginDiv(CssClass.PACKAGE_MENU_LIST); + + Set packageDocs = getAllPackages(); + Iterator it = packageDocs.iterator(); + while (it.hasNext()) { + PackageDoc packageDoc = (PackageDoc)it.next(); + output.beginSpan(CssClass.PACKAGE_MENU_ENTRY); + output.beginAnchor(getPackageURL(packageDoc) + "classes" + filenameExtension, + null, + "classes"); + if (packageDoc.name().length() > 0) { + output.print(packageDoc.name()); + } + else { + output.print("<unnamed package>"); + } + output.endAnchor(); + output.endSpan(CssClass.PACKAGE_MENU_ENTRY); + output.br(); + } + + output.endDiv(CssClass.PACKAGE_MENU_LIST); + output.endBody(); + output.endPage(); + output.close(); + } + + private void printClassMenuEntry(HtmlPage output, ClassDoc classDoc) + { + CssClass entryClass; + if (classDoc.isInterface()) { + entryClass = CssClass.CLASS_MENU_ENTRY_INTERFACE; + } + else { + entryClass = CssClass.CLASS_MENU_ENTRY_CLASS; + } + output.beginSpan(entryClass); + output.beginAnchor(getClassDocURL(output, classDoc), + classDoc.qualifiedTypeName(), + "content"); + output.print(classDoc.name()); + output.endAnchor(); + output.endSpan(entryClass); + output.br(); + } + + private void printClassMenuSection(HtmlPage output, Collection classDocs, String header) + { + if (!classDocs.isEmpty()) { + output.div(CssClass.CLASS_MENU_SUBTITLE, header); + Iterator it = classDocs.iterator(); + while (it.hasNext()) { + ClassDoc classDoc = (ClassDoc)it.next(); + printClassMenuEntry(output, classDoc); + } + } + } + + private void printClassMenuList(HtmlPage output, ClassDoc[] classDocs, boolean categorized) + { + output.beginDiv(CssClass.CLASS_MENU_LIST); + + if (categorized) { + Set classes = new TreeSet(); + Set interfaces = new TreeSet(); + Set exceptions = new TreeSet(); + Set errors = new TreeSet(); + + for (int i=0; i<classDocs.length; ++i) { + ClassDoc classDoc = classDocs[i]; + if (classDoc.isInterface()) { + interfaces.add(classDoc); + } + else if (classDoc.isException()) { + exceptions.add(classDoc); + } + else if (classDoc.isError()) { + errors.add(classDoc); + } + else { + classes.add(classDoc); + } + } + printClassMenuSection(output, interfaces, "Interfaces"); + printClassMenuSection(output, classes, "Classes"); + printClassMenuSection(output, exceptions, "Exceptions"); + printClassMenuSection(output, errors, "Errors"); + } + else { + for (int i=0; i<classDocs.length; ++i) { + ClassDoc classDoc = classDocs[i]; + if (classDoc.isIncluded()) { + printClassMenuEntry(output, classDoc); + } + } + } + + output.endDiv(CssClass.CLASS_MENU_LIST); + } + + private void printAllClassesMenuPage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "all-classes" + filenameExtension), + "."); + output.beginPage(getPageTitle("Class Menu"), getOutputCharset(), getStylesheets()); + output.beginBody(CssClass.BODY_MENU_CLASSES, false); + + output.div(CssClass.CLASS_MENU_TITLE, "All Classes"); + + printClassMenuList(output, getRootDoc().classes(), false); + + output.endBody(); + output.endPage(); + output.close(); + } + + private void printPackageClassesMenuPage(File packageDir, String pathToRoot, PackageDoc packageDoc) + throws IOException + { + HtmlPage output = newHtmlPage(new File(packageDir, + "classes" + filenameExtension), + pathToRoot); + + output.beginPage(getPageTitle(packageDoc.name() + " Class Menu"), + getOutputCharset(), getStylesheets()); + output.beginBody(CssClass.BODY_MENU_CLASSES, false); + + output.beginDiv(CssClass.CLASS_MENU_TITLE); + output.beginAnchor("package-summary" + filenameExtension, "", "content"); + if (packageDoc.name().length() > 0) { + output.print(packageDoc.name()); + } + else { + output.print("<Unnamed>"); + } + output.endAnchor(); + output.endDiv(CssClass.CLASS_MENU_TITLE); + + printClassMenuList(output, packageDoc.allClasses(), true); + + output.endBody(); + output.endPage(); + output.close(); + } + + private void printSplitIndex() + throws IOException + { + Map categorizedIndex = getCategorizedIndex(); + Iterator it = categorizedIndex.keySet().iterator(); + int n = 1; + int count = categorizedIndex.size(); + while (it.hasNext()) { + Character c = (Character)it.next(); + List classList = (List)categorizedIndex.get(c); + printIndexPage(n++, count, c, classList); + } + } + + private void printIndexPage() + throws IOException + { + printIndexPage(0, 0, null, null); + } + + private void printIndexPage(int index, int maxIndex, Character letter, List classList) + throws IOException + { + String pageName = "alphaindex"; + if (null != letter) { + pageName += "-" + index; + } + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + pageName + filenameExtension), + "."); + output.beginPage(getPageTitle("Alphabetical Index"), + getOutputCharset(), + getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_INDEX); + if (null == letter) { + printNavBarTop(output, "index"); + } + else { + printNavBarTop(output, "split-index", null, + (index > 1) ? new Integer(index - 1) : null, + (index < maxIndex) ? new Integer(index + 1) : null); + } + + { + String title; + if (null == letter) { + title = "Alphabetical Index"; + } + else { + title = "Alphabetical Index: " + letter; + } + output.div(CssClass.INDEX_TITLE, title); + + if (null != letter || getCategorizedIndex().keySet().size() > 1) { + output.beginDiv(CssClass.INDEX_LETTERS); + + Iterator it = getCategorizedIndex().keySet().iterator(); + int n = 1; + while (it.hasNext()) { + Character c = (Character)it.next(); + output.beginSpan(CssClass.INDEX_LETTER); + if (letter != null) { + output.beginAnchor("alphaindex-" + n + filenameExtension); + } + else { + output.beginAnchor("#" + c); + } + output.print(c.toString()); + output.endAnchor(); + output.endSpan(CssClass.INDEX_LETTER); + output.beginSpan(CssClass.INDEX_LETTER_SPACER); + output.print(" "); + output.endSpan(CssClass.INDEX_LETTER_SPACER); + ++n; + } + } + + output.endDiv(CssClass.INDEX_LETTERS); + } + + if (null != letter) { + printIndexCategory(output, letter, classList); + } + else { + Map categorizedIndex = getCategorizedIndex(); + Iterator categoryIt = categorizedIndex.keySet().iterator(); + + while (categoryIt.hasNext()) { + letter = (Character)categoryIt.next(); + classList = (List)categorizedIndex.get(letter); + output.anchorName(letter.toString()); + printIndexCategory(output, letter, classList); + } + } + + printNavBarBottom(output, "index"); + output.endBody(); + output.endPage(); + output.close(); + } + + private void printIndexCategory(HtmlPage output, Character letter, List classList) + { + Iterator it = classList.iterator(); + + output.div(CssClass.INDEX_CATEGORY_HEADER, letter.toString()); + output.beginDiv(CssClass.INDEX_CATEGORY); + while (it.hasNext()) { + Doc entry = (Doc)it.next(); + printIndexEntry(output, entry); + } + output.endDiv(CssClass.INDEX_CATEGORY); + } + + private void printDeprecationSummary(HtmlPage output, List docs, String header) + { + if (!docs.isEmpty()) { + output.beginDiv(CssClass.TABLE_CONTAINER); + output.beginTable(CssClass.DEPRECATION_SUMMARY, new String[] { "border", "width" }, new String[] { "1", "100%" }); + output.rowDiv(CssClass.TABLE_HEADER, header); + + Iterator it = docs.iterator(); + while (it.hasNext()) { + Doc doc = (Doc)it.next(); + output.beginRow(); + + output.beginCell(CssClass.DEPRECATION_SUMMARY_LEFT); + if (doc instanceof Type) { + printType(output, (Type)doc); + } + else { + ProgramElementDoc memberDoc = (ProgramElementDoc)doc; + output.beginAnchor(getMemberDocURL(output, memberDoc)); + output.print(memberDoc.containingClass().qualifiedName()); + output.print("."); + output.print(memberDoc.name()); + if (memberDoc instanceof ExecutableMemberDoc) { + output.print(((ExecutableMemberDoc)memberDoc).flatSignature()); + } + output.endAnchor(); + } + output.beginDiv(CssClass.DEPRECATION_SUMMARY_DESCRIPTION); + printTags(output, doc, doc.tags("deprecated")[0].firstSentenceTags(), true); + output.endDiv(CssClass.DEPRECATION_SUMMARY_DESCRIPTION); + + output.endCell(); + + output.endRow(); + } + output.endTable(); + output.endDiv(CssClass.TABLE_CONTAINER); + output.print("\n"); + } + } + + + private void printSerializationPage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "serialized-form" + filenameExtension), + "."); + output.beginPage(getPageTitle("Serialized Form"), + getOutputCharset(), + getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_DEPRECATED); + printNavBarTop(output, "serialized"); + + output.div(CssClass.SERIALIZED_TITLE, "Serialized Form"); + + Iterator it = getAllPackages().iterator(); + + while (it.hasNext()) { + + PackageDoc packageDoc = (PackageDoc)it.next(); + + List serializableClasses = new LinkedList(); + ClassDoc[] classes = packageDoc.allClasses(); + for (int i=0; i<classes.length; ++i) { + ClassDoc classDoc = classes[i]; + if (classDoc.isSerializable() || classDoc.isExternalizable()) { + serializableClasses.add(classDoc); + } + } + + if (!serializableClasses.isEmpty()) { + output.div(CssClass.SERIALIZED_PACKAGE_HEADER, "Package " + packageDoc.name()); + + Iterator cit = serializableClasses.iterator(); + while (cit.hasNext()) { + ClassDoc classDoc = (ClassDoc)cit.next(); + + output.anchorName(classDoc.qualifiedTypeName()); + + output.beginDiv(CssClass.SERIALIZED_CLASS_HEADER); + output.print("Class "); + printType(output, classDoc, true); + output.print(" extends "); + printType(output, classDoc.superclass()); + output.print(" implements Serializable"); + output.endDiv(CssClass.SERIALIZED_CLASS_HEADER); + + FieldDoc serialVersionUidField = findField(classDoc, "serialVersionUID"); + if (null != serialVersionUidField + && serialVersionUidField.isFinal() + && serialVersionUidField.isStatic() + && serialVersionUidField.type().typeName().equals("long")) { + + String fieldValue = serialVersionUidField.constantValueExpression(); + if (null != fieldValue) { + output.beginDiv(CssClass.SERIALIZED_SVUID_OUTER); + output.span(CssClass.SERIALIZED_SVUID_HEADER, "serialVersionUID: "); + output.span(CssClass.SERIALIZED_SVUID_VALUE, fieldValue); + output.endDiv(CssClass.SERIALIZED_SVUID_OUTER); + } + } + printMemberDetails(output, + classDoc.serializationMethods(), + "Serialization Methods", + true, null); + printMemberDetails(output, + classDoc.serializableFields(), + "Serialized Fields", + true, null); + } + } + } + + printNavBarBottom(output, "serialized"); + + output.endBody(); + output.endPage(); + output.close(); + } + + + private void printDeprecationPage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "deprecated" + filenameExtension), + "."); + output.beginPage(getPageTitle("Deprecated API"), + getOutputCharset(), + getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_DEPRECATED); + printNavBarTop(output, "deprecated"); + + output.div(CssClass.DEPRECATION_TITLE, "Deprecated API"); + + List deprecatedInterfaces = new LinkedList(); + List deprecatedExceptions = new LinkedList(); + List deprecatedErrors = new LinkedList(); + List deprecatedClasses = new LinkedList(); + List deprecatedFields = new LinkedList(); + List deprecatedMethods = new LinkedList(); + List deprecatedConstructors = new LinkedList(); + + ClassDoc[] classDocs = getRootDoc().classes(); + for (int i=0; i<classDocs.length; ++i) { + ClassDoc classDoc = classDocs[i]; + { + Tag[] deprecatedTags = classDoc.tags("deprecated"); + if (null != deprecatedTags && deprecatedTags.length > 0) { + if (classDoc.isInterface()) { + deprecatedInterfaces.add(classDoc); + } + else if (classDoc.isException()) { + deprecatedExceptions.add(classDoc); + } + else if (classDoc.isError()) { + deprecatedErrors.add(classDoc); + } + else { + deprecatedClasses.add(classDoc); + } + } + } + ConstructorDoc[] constructors = classDoc.constructors(); + for (int j=0; j<constructors.length; ++j) { + Tag[] deprecatedTags = constructors[j].tags("deprecated"); + if (null != deprecatedTags && deprecatedTags.length > 0) { + deprecatedConstructors.add(constructors[j]); + } + } + MethodDoc[] methods = classDoc.methods(); + for (int j=0; j<methods.length; ++j) { + Tag[] deprecatedTags = methods[j].tags("deprecated"); + if (null != deprecatedTags && deprecatedTags.length > 0) { + deprecatedMethods.add(methods[j]); + } + } + FieldDoc[] fields = classDoc.fields(); + for (int j=0; j<fields.length; ++j) { + Tag[] deprecatedTags = fields[j].tags("deprecated"); + if (null != deprecatedTags && deprecatedTags.length > 0) { + deprecatedFields.add(fields[j]); + } + } + } + + if (!deprecatedInterfaces.isEmpty() + || !deprecatedClasses.isEmpty() + || !deprecatedExceptions.isEmpty() + || !deprecatedErrors.isEmpty() + || !deprecatedFields.isEmpty() + || !deprecatedMethods.isEmpty() + || !deprecatedConstructors.isEmpty()) { + + output.beginDiv(CssClass.DEPRECATION_TOC); + output.div(CssClass.DEPRECATION_TOC_HEADER, "Contents"); + output.beginDiv(CssClass.DEPRECATION_TOC_LIST); + if (!deprecatedInterfaces.isEmpty()) { + output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY); + output.anchor("#interfaces", "Deprecated Interfaces"); + output.endDiv(CssClass.DEPRECATION_TOC_ENTRY); + } + if (!deprecatedClasses.isEmpty()) { + output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY); + output.anchor("#classes", "Deprecated Classes"); + output.endDiv(CssClass.DEPRECATION_TOC_ENTRY); + } + if (!deprecatedExceptions.isEmpty()) { + output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY); + output.anchor("#exceptions", "Deprecated Exceptions"); + output.endDiv(CssClass.DEPRECATION_TOC_ENTRY); + } + if (!deprecatedErrors.isEmpty()) { + output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY); + output.anchor("#errors", "Deprecated Errors"); + output.endDiv(CssClass.DEPRECATION_TOC_ENTRY); + } + if (!deprecatedFields.isEmpty()) { + output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY); + output.anchor("#fields", "Deprecated Fields"); + output.endDiv(CssClass.DEPRECATION_TOC_ENTRY); + } + if (!deprecatedMethods.isEmpty()) { + output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY); + output.anchor("#methods", "Deprecated Methods"); + output.endDiv(CssClass.DEPRECATION_TOC_ENTRY); + } + if (!deprecatedConstructors.isEmpty()) { + output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY); + output.anchor("#constructors", "Deprecated Constructors"); + output.endDiv(CssClass.DEPRECATION_TOC_ENTRY); + } + output.endDiv(CssClass.DEPRECATION_TOC_LIST); + output.endDiv(CssClass.DEPRECATION_TOC); + output.beginDiv(CssClass.DEPRECATION_LIST); + + output.anchorName("interfaces"); + printDeprecationSummary(output, deprecatedInterfaces, "Deprecated Interfaces"); + + output.anchorName("classes"); + printDeprecationSummary(output, deprecatedClasses, "Deprecated Classes"); + + output.anchorName("exceptions"); + printDeprecationSummary(output, deprecatedExceptions, "Deprecated Exceptions"); + + output.anchorName("errors"); + printDeprecationSummary(output, deprecatedErrors, "Deprecated Errors"); + + output.anchorName("fields"); + printDeprecationSummary(output, deprecatedFields, "Deprecated Fields"); + + output.anchorName("methods"); + printDeprecationSummary(output, deprecatedMethods, "Deprecated Methods"); + + output.anchorName("constructors"); + printDeprecationSummary(output, deprecatedConstructors, "Deprecated Constructors"); + + output.endDiv(CssClass.DEPRECATION_LIST); + } + else { + output.beginDiv(CssClass.DEPRECATION_EMPTY); + output.print("No deprecated classes or class members in this API."); + output.endDiv(CssClass.DEPRECATION_EMPTY); + + } + + printNavBarBottom(output, "deprecated"); + output.endBody(); + output.endPage(); + output.close(); + } + + private void printAboutPage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "about" + filenameExtension), + "."); + output.beginPage(getPageTitle("About"), + getOutputCharset(), + getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_ABOUT); + + printNavBarTop(output, "about"); + + output.div(CssClass.ABOUT_TITLE, "About"); + + output.beginDiv(CssClass.ABOUT_GENERATOR); + output.print("Generated by "); + output.print("Gjdoc"); + output.print(" HtmlDoclet "); + output.print(getDocletVersion()); + output.print(", part of "); + output.beginAnchor("http://www.gnu.org/software/classpath/cp-tools/", "", "_top"); + output.print("GNU Classpath Tools"); + output.endAnchor(); + output.print(", on "); + DateFormat format = DateFormat.getDateTimeInstance(DateFormat.LONG, + DateFormat.LONG, + Locale.US); + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"), + Locale.US); + format.setCalendar(cal); + output.print(format.format(new Date())); + output.print("."); + output.endDiv(CssClass.ABOUT_GENERATOR); + + printNavBarBottom(output, "about"); + + output.endBody(); + output.endPage(); + output.close(); + } + + private void printSourcePage(File packageDir, ClassDoc classDoc, String sourceXhtml) + throws IOException + { + HtmlPage output = newHtmlPage(new File(packageDir, + classDoc.name() + "-source" + filenameExtension), + getPathToRoot(packageDir, getTargetDirectory())); + output.beginPage(getPageTitle("Source for " + classDoc.qualifiedTypeName()), + getOutputCharset(), + getStylesheets()); + + output.beginBody(CssClass.BODY_CONTENT_SOURCE); + + printNavBarTop(output, "source", classDoc, null, null); + + output.div(CssClass.SOURCE_TITLE, "Source for " + classDoc.qualifiedTypeName()); + output.beginDiv(CssClass.SOURCE); + output.print(sourceXhtml); + output.endDiv(CssClass.SOURCE); + + printNavBarBottom(output, "source", classDoc); + + output.endBody(); + output.endPage(); + + output.close(); + } + + private void printHelpPage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "help" + filenameExtension), + "."); + output.beginPage(getPageTitle("Help"), + getOutputCharset(), + getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_HELP); + + printNavBarTop(output, "help"); + + InputStream helpIn; + if (null != optionHelpFile.getValue()){ + helpIn = new FileInputStream(optionHelpFile.getValue()); + } + else { + helpIn = getClass().getResourceAsStream("/htmldoclet/help.xhtml"); + } + output.insert(new InputStreamReader(helpIn, "utf-8")); + helpIn.close(); + + printNavBarBottom(output, "help"); + + output.endBody(); + output.endPage(); + output.close(); + } + + private void printOverviewPage() + throws IOException + { + HtmlPage output = newHtmlPage(new File(getTargetDirectory(), + "overview-summary" + filenameExtension), + "."); + output.beginPage(getWindowTitle(), + getOutputCharset(), + getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_OVERVIEW); + + printNavBarTop(output, "overview"); + + String overviewHeader; + if (null != optionDocTitle.getValue()) { + overviewHeader = optionDocTitle.getValue(); + } + else if (null != optionTitle.getValue()) { + overviewHeader = optionTitle.getValue(); + } + else { + overviewHeader = null; + } + + if (null != overviewHeader) { + output.div(CssClass.OVERVIEW_TITLE, overviewHeader); + } + + output.beginDiv(CssClass.OVERVIEW_DESCRIPTION_TOP); + printTags(output, getRootDoc(), getRootDoc().firstSentenceTags(), true); + output.endDiv(CssClass.OVERVIEW_DESCRIPTION_TOP); + + List packageGroups = getPackageGroups(); + + if (packageGroups.isEmpty()) { + + printOverviewPackages(output, getAllPackages(), + "All Packages"); + } + else { + Set otherPackages = new LinkedHashSet(); + otherPackages.addAll(getAllPackages()); + + Iterator it = packageGroups.iterator(); + while (it.hasNext()) { + PackageGroup packageGroup = (PackageGroup)it.next(); + printOverviewPackages(output, + packageGroup.getPackages(), + packageGroup.getName()); + otherPackages.removeAll(packageGroup.getPackages()); + } + + if (!otherPackages.isEmpty()) { + printOverviewPackages(output, + otherPackages, + "Other Packages"); + } + } + + output.anchorName("description"); + output.beginDiv(CssClass.OVERVIEW_DESCRIPTION_FULL); + printTags(output, getRootDoc(), getRootDoc().inlineTags(), false); + output.endDiv(CssClass.OVERVIEW_DESCRIPTION_FULL); + + printNavBarBottom(output, "overview"); + output.endBody(); + output.endPage(); + output.close(); + } + + private void printOverviewPackages(HtmlPage output, Collection packageDocs, String header) + { + output.beginDiv(CssClass.TABLE_CONTAINER); + output.beginTable(CssClass.OVERVIEW_SUMMARY, new String[] { "border", "width" }, new String[] { "1", "100%" }); + output.rowDiv(CssClass.TABLE_HEADER, header); + + Iterator it = packageDocs.iterator(); + while (it.hasNext()) { + PackageDoc packageDoc = (PackageDoc)it.next(); + output.beginRow(); + + output.beginCell(CssClass.OVERVIEW_SUMMARY_LEFT); + output.beginAnchor(getPackageURL(packageDoc) + "package-summary" + filenameExtension); + output.print(packageDoc.name()); + output.endAnchor(); + output.endCell(); + + output.beginCell(CssClass.OVERVIEW_SUMMARY_RIGHT); + printTags(output, packageDoc, packageDoc.firstSentenceTags(), true); + output.endCell(); + output.endRow(); + } + output.endTable(); + output.endDiv(CssClass.TABLE_CONTAINER); + } + + private void printClassUsagePage(File packageDir, String pathToRoot, ClassDoc classDoc) + throws IOException + { + HtmlPage output = newHtmlPage(new File(packageDir, + classDoc.name() + "-uses" + filenameExtension), + pathToRoot); + output.beginPage(getPageTitle(classDoc.name()), getOutputCharset(), getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_USES); + printNavBarTop(output, "uses", classDoc, null, null); + + output.div(CssClass.USAGE_TITLE, + "Uses of " + getClassTypeName(classDoc) + + " " + classDoc.qualifiedName()); + + Map packageToUsageTypeMap = getUsageOfClass(classDoc); + if (null != packageToUsageTypeMap && !packageToUsageTypeMap.isEmpty()) { + + Iterator packagesIterator = packageToUsageTypeMap.keySet().iterator(); + while (packagesIterator.hasNext()) { + PackageDoc packageDoc = (PackageDoc)packagesIterator.next(); + + output.div(CssClass.USAGE_PACKAGE_TITLE, "Uses in package " + packageDoc.name()); + + Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(packageDoc); + Iterator usageTypeIterator = usageTypeToUsersMap.keySet().iterator(); + while (usageTypeIterator.hasNext()) { + UsageType usageType = (UsageType)usageTypeIterator.next(); + + output.beginTable(CssClass.USAGE_SUMMARY, new String[] { "border", "width" }, new String[] { "1", "100%" }); + output.rowDiv(CssClass.USAGE_TABLE_HEADER, format("usagetype." + usageType.getId(), + classDoc.qualifiedName())); + + Set users = (Set)usageTypeToUsersMap.get(usageType); + Iterator userIterator = users.iterator(); + while (userIterator.hasNext()) { + Doc user = (Doc)userIterator.next(); + + output.beginRow(); + + if (user instanceof ClassDoc) { + output.beginCell(CssClass.USAGE_SUMMARY_LEFT); + output.print("class"); + output.endCell(); + + output.beginCell(CssClass.USAGE_SUMMARY_RIGHT); + output.beginDiv(CssClass.USAGE_SUMMARY_SYNOPSIS); + printType(output, ((ClassDoc)user)); + output.endDiv(CssClass.USAGE_SUMMARY_SYNOPSIS); + output.beginDiv(CssClass.USAGE_SUMMARY_DESCRIPTION); + printTags(output, ((ClassDoc)user), ((ClassDoc)user).firstSentenceTags(), true); + output.endDiv(CssClass.USAGE_SUMMARY_DESCRIPTION); + output.endCell(); + } + else if (user instanceof FieldDoc) { + FieldDoc fieldDoc = (FieldDoc)user; + + output.beginCell(CssClass.USAGE_SUMMARY_LEFT); + printType(output, ((FieldDoc)user).type()); + output.endCell(); + + output.beginCell(CssClass.USAGE_SUMMARY_RIGHT); + output.beginDiv(CssClass.USAGE_SUMMARY_SYNOPSIS); + printType(output, ((FieldDoc)user).containingClass()); + output.print("."); + output.beginAnchor(getMemberDocURL(output, (FieldDoc)user)); + output.print(((FieldDoc)user).name()); + output.endAnchor(); + output.endDiv(CssClass.USAGE_SUMMARY_SYNOPSIS); + output.beginDiv(CssClass.USAGE_SUMMARY_DESCRIPTION); + printTags(output, ((FieldDoc)user), ((FieldDoc)user).firstSentenceTags(), true); + output.endDiv(CssClass.USAGE_SUMMARY_DESCRIPTION); + output.endCell(); + } + else if (user instanceof MethodDoc) { + MethodDoc methodDoc = (MethodDoc)user; + + output.beginCell(CssClass.USAGE_SUMMARY_LEFT); + printType(output, ((MethodDoc)user).returnType()); + output.endCell(); + + output.beginCell(CssClass.USAGE_SUMMARY_RIGHT); + output.beginDiv(CssClass.USAGE_SUMMARY_SYNOPSIS); + printType(output, ((MethodDoc)user).containingClass()); + output.print("."); + output.beginAnchor(getMemberDocURL(output, (MethodDoc)user)); + output.print(((MethodDoc)user).name()); + output.endAnchor(); + printParameters(output, (ExecutableMemberDoc)user); + output.endDiv(CssClass.USAGE_SUMMARY_SYNOPSIS); + output.beginDiv(CssClass.USAGE_SUMMARY_DESCRIPTION); + printTags(output, ((MethodDoc)user), ((MethodDoc)user).firstSentenceTags(), true); + output.endDiv(CssClass.USAGE_SUMMARY_DESCRIPTION); + output.endCell(); + } + else if (user instanceof ConstructorDoc) { + ConstructorDoc constructorDoc = (ConstructorDoc)user; + + output.beginCell(CssClass.USAGE_SUMMARY_RIGHT); + output.beginDiv(CssClass.USAGE_SUMMARY_SYNOPSIS); + printType(output, ((ConstructorDoc)user).containingClass()); + output.print("."); + output.beginAnchor(getMemberDocURL(output, (ConstructorDoc)user)); + output.print(((ConstructorDoc)user).name()); + output.endAnchor(); + printParameters(output, (ExecutableMemberDoc)user); + output.endDiv(CssClass.USAGE_SUMMARY_SYNOPSIS); + output.beginDiv(CssClass.USAGE_SUMMARY_DESCRIPTION); + printTags(output, ((ConstructorDoc)user), + ((ConstructorDoc)user).firstSentenceTags(), true); + output.endDiv(CssClass.USAGE_SUMMARY_DESCRIPTION); + output.endCell(); + } + + output.endRow(); + } + output.endTable(); + } + } + } + else { + output.div(CssClass.USAGE_EMPTY, + getClassTypeName(classDoc) + + " " + classDoc.qualifiedName() + " is not used by any class in this documentation set."); + } + printNavBarBottom(output, "uses", classDoc); + output.endBody(); + output.endPage(); + output.close(); + } + + private void printSuperTreeRec(HtmlPage output, ListIterator it, int level) + { + if (it.hasPrevious()) { + ClassDoc cd = (ClassDoc)it.previous(); + output.beginElement("li", new String[] { "class" }, new String[] { "inheritance " + level }); + output.beginElement("code"); + if (it.hasPrevious()) { + printType(output, cd, true); + } + else { + output.print(cd.qualifiedName() + getTypeParameters(cd)); + } + output.endElement("code"); + output.endElement("li"); + + output.beginElement("li"); + + if (it.hasPrevious()) { + output.beginElement("ul", new String[] { "class" }, new String[] { "inheritance " + (level + 1) }); + printSuperTreeRec(output, it, level + 1); + output.endElement("ul"); + } + + output.endElement("li"); + } + } + + private static boolean isSubInterface(ClassDoc classDoc, ClassDoc otherClassDoc) + { + ClassDoc[] interfaces = otherClassDoc.interfaces(); + for (int i=0; i<interfaces.length; ++i) { + if (classDoc == interfaces[i]) { + return true; + } + else if (isSubInterface(classDoc, interfaces[i])) { + return true; + } + } + return false; + } + + private void printCommaSeparatedTypes(HtmlPage output, + Collection list, + String header, + CssClass cssClass) + { + if (!list.isEmpty()) { + output.beginDiv(cssClass); + output.div(CssClass.CLASS_KNOWNIMPLEMENTING_HEADER, header); + output.beginDiv(CssClass.CLASS_KNOWNIMPLEMENTING_ITEM); + Iterator it = list.iterator(); + while (it.hasNext()) { + Type type = (Type)it.next(); + printType(output, type); + if (it.hasNext()) { + output.print(", "); + } + } + output.endDiv(CssClass.CLASS_KNOWNIMPLEMENTING_ITEM); + output.endDiv(cssClass); + } + } + + private void printClassPage(File packageDir, String pathToRoot, + ClassDoc classDoc, ClassDoc prevClassDoc, ClassDoc nextClassDoc) + throws IOException + { + HtmlPage output = newHtmlPage(new File(packageDir, + classDoc.name() + filenameExtension), + pathToRoot); + Set keywords = new LinkedHashSet(); + { + keywords.add(classDoc.qualifiedName() + " class"); + FieldDoc[] fieldDocs = classDoc.fields(); + for (int i=0; i<fieldDocs.length; ++i) { + FieldDoc fieldDoc = fieldDocs[i]; + keywords.add(fieldDoc.name()); + } + + MethodDoc[] methodDocs = classDoc.methods(); + for (int i=0; i<methodDocs.length; ++i) { + MethodDoc methodDoc = methodDocs[i]; + keywords.add(methodDoc.name() + "()"); + } + } + String parameters = getTypeParameters(classDoc); + + output.beginPage(getPageTitle(classDoc.name()), getOutputCharset(), + keywords, getStylesheets()); + output.beginBody(CssClass.BODY_CONTENT_CLASS); + printNavBarTop(output, "class", classDoc, prevClassDoc, nextClassDoc); + + output.beginDiv(CssClass.CLASS_TITLE); + output.div(CssClass.CLASS_TITLE_PACKAGE, + classDoc.containingPackage().name()); + output.div(CssClass.CLASS_TITLE_CLASS, + getClassTypeName(classDoc) + + " " + classDoc.name() + + parameters); + output.endDiv(CssClass.CLASS_TITLE); + + boolean needSep = false; + + if (classDoc.isInterface()) { + + InterfaceRelation relation + = (InterfaceRelation)getInterfaceRelations().get(classDoc); + + printCommaSeparatedTypes(output, + relation.superInterfaces, + "All Superinterfaces:", + CssClass.CLASS_KNOWNIMPLEMENTING); + + printCommaSeparatedTypes(output, + relation.subInterfaces, + "Known Subinterfaces:", + CssClass.CLASS_KNOWNIMPLEMENTING); + + printCommaSeparatedTypes(output, + relation.implementingClasses, + "Known Implementing Classes:", + CssClass.CLASS_KNOWNIMPLEMENTING); + + needSep = !relation.superInterfaces.isEmpty() + || !relation.subInterfaces.isEmpty() + || !relation.implementingClasses.isEmpty(); + } + else { + needSep = true; + + if (!"java.lang.Object".equals(classDoc.qualifiedName())) { + LinkedList superClasses = new LinkedList(); + for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) { + superClasses.add(cd); + } + output.beginDiv(CssClass.CLASS_INHERITANCETREE); + output.beginElement("ul", new String[] { "class" }, new String[] { "inheritance 0" }); + printSuperTreeRec(output, superClasses.listIterator(superClasses.size()), 0); + output.endElement("ul"); + output.endDiv(CssClass.CLASS_INHERITANCETREE); + + if (null != classDoc.containingClass()) { + output.beginDiv(CssClass.CLASS_ENCLOSINGCLASS); + output.div(CssClass.CLASS_ENCLOSINGCLASS_HEADER, "Enclosing Class:"); + output.beginDiv(CssClass.CLASS_ENCLOSINGCLASS_ITEM); + printType(output, classDoc.containingClass()); + output.endDiv(CssClass.CLASS_ENCLOSINGCLASS_ITEM); + output.endDiv(CssClass.CLASS_ENCLOSINGCLASS); + } + + Set implementedInterfaces = getImplementedInterfaces(classDoc); + + printCommaSeparatedTypes(output, + implementedInterfaces, + "Implemented Interfaces:", + CssClass.CLASS_KNOWNIMPLEMENTING); + + List knownDirectSubclasses = getKnownDirectSubclasses(classDoc); + if (!knownDirectSubclasses.isEmpty()) { + output.beginDiv(CssClass.CLASS_SUBCLASSES); + output.div(CssClass.CLASS_SUBCLASSES_HEADER, "Known Direct Subclasses:"); + output.beginDiv(CssClass.CLASS_SUBCLASSES_ITEM); + Iterator it = knownDirectSubclasses.iterator(); + while (it.hasNext()) { + printType(output, (ClassDoc)it.next()); + if (it.hasNext()) { + output.print(", "); + } + } + + output.endDiv(CssClass.CLASS_SUBCLASSES_ITEM); + output.endDiv(CssClass.CLASS_SUBCLASSES_HEADER); + output.endDiv(CssClass.CLASS_SUBCLASSES); + } + } + } + + if (needSep) { + output.hr(); + } + + output.beginDiv(CssClass.CLASS_SYNOPSIS); + output.beginDiv(CssClass.CLASS_SYNOPSIS_DECLARATION); + output.print(getFullModifiers(classDoc) + ' ' + getClassTypeKeyword(classDoc) + + ' '); + output.beginSpan(CssClass.CLASS_SYNOPSIS_NAME); + if (optionLinkSource.getValue() && null != classDoc.position()) { + output.beginAnchor(getOuterClassDoc(classDoc).name() + "-source" + filenameExtension + "#line." + classDoc.position()); + output.print(classDoc.name() + parameters); + output.endAnchor(); + } + else { + output.print(classDoc.name() + parameters); + } + output.endSpan(CssClass.CLASS_SYNOPSIS_NAME); + output.endDiv(CssClass.CLASS_SYNOPSIS_DECLARATION); + + if (!classDoc.isInterface()) { + if (null != classDoc.superclass()) { + output.beginDiv(CssClass.CLASS_SYNOPSIS_SUPERCLASS); + output.print("extends "); + printType(output, classDoc.superclass()); + output.endDiv(CssClass.CLASS_SYNOPSIS_SUPERCLASS); + } + } + + ClassDoc[] interfaces = classDoc.interfaces(); + if (interfaces.length > 0) { + output.beginDiv(CssClass.CLASS_SYNOPSIS_IMPLEMENTS); + if (!classDoc.isInterface()) { + output.print("implements "); + } + else { + output.print("extends "); + } + for (int i=0; i<interfaces.length; ++i) { + if (i>0) { + output.print(", "); + } + printType(output, interfaces[i]); + } + output.endDiv(CssClass.CLASS_SYNOPSIS_IMPLEMENTS); + } + output.endDiv(CssClass.CLASS_SYNOPSIS); + + output.hr(); + + if (!optionNoComment.getValue()) { + output.beginDiv(CssClass.CLASS_DESCRIPTION); + printTags(output, classDoc, classDoc.inlineTags(), false); + output.endDiv(CssClass.CLASS_DESCRIPTION); + + printTaglets(output, classDoc.tags(), new HtmlTagletContext(classDoc, output, false)); + } + + + Set implementedInterfaces = getImplementedInterfaces(classDoc); + + boolean haveInheritedFields = false; + boolean haveInheritedMethods = false; + boolean haveInheritedClasses = false; + { + if (!classDoc.isInterface()) { + ClassDoc superClassDoc = classDoc.superclass(); + while (null != superClassDoc + && (!haveInheritedFields + || !haveInheritedMethods + || !haveInheritedClasses)) { + if (superClassDoc.fields().length > 0) { + haveInheritedFields = true; + } + if (superClassDoc.methods().length > 0) { + haveInheritedMethods = true; + } + if (superClassDoc.innerClasses().length > 0) { + haveInheritedClasses = true; + } + superClassDoc = superClassDoc.superclass(); + } + } + } + + printProgramElementDocs(output, getSortedInnerClasses(classDoc), + "Nested Class Summary", haveInheritedClasses, + "summary-inner"); + + { + ClassDoc superClassDoc = classDoc.superclass(); + while (null != superClassDoc) { + printInheritedMembers(output, getSortedInnerClasses(superClassDoc), + "Nested classes/interfaces inherited from class {0}", + superClassDoc); + superClassDoc = superClassDoc.superclass(); + } + } + + printProgramElementDocs(output, getSortedFields(classDoc), + "Field Summary", haveInheritedFields, + "summary-fields"); + + { + ClassDoc superClassDoc = classDoc.superclass(); + while (null != superClassDoc) { + printInheritedMembers(output, getSortedFields(superClassDoc), + "Fields inherited from class {0}", + superClassDoc); + superClassDoc = superClassDoc.superclass(); + } + } + + { + Iterator it = implementedInterfaces.iterator(); + while (it.hasNext()) { + ClassDoc implementedInterface + = (ClassDoc)it.next(); + if (!"java.io.Serializable".equals(implementedInterface.qualifiedName()) + && !"java.io.Externalizable".equals(implementedInterface.qualifiedName())) { + printInheritedMembers(output, getSortedFields(implementedInterface), + "Fields inherited from interface {0}", + implementedInterface); + } + } + } + + printProgramElementDocs(output, getSortedConstructors(classDoc), + "Constructor Summary", false, + "summary-constructors"); + printProgramElementDocs(output, getSortedMethods(classDoc), + "Method Summary", haveInheritedMethods, + "summary-methods"); + + if (classDoc.isInterface()) { + InterfaceRelation relation + = (InterfaceRelation)getInterfaceRelations().get(classDoc); + Iterator it = relation.superInterfaces.iterator(); + while (it.hasNext()) { + ClassDoc superClassDoc = (ClassDoc)it.next(); + printInheritedMembers(output, getSortedMethods(superClassDoc), + "Methods inherited from interface {0}", + superClassDoc); + } + } + else { + ClassDoc superClassDoc = classDoc.superclass(); + while (null != superClassDoc) { + printInheritedMembers(output, getSortedMethods(superClassDoc), + "Methods inherited from class {0}", + superClassDoc); + superClassDoc = superClassDoc.superclass(); + } + } + + printMemberDetails(output, getSortedFields(classDoc), + "Field Details", false, "detail-fields"); + printMemberDetails(output, getSortedConstructors(classDoc), + "Constructor Details", false, "detail-constructors"); + printMemberDetails(output, getSortedMethods(classDoc), + "Method Details", false, "detail-methods"); + + printNavBarBottom(output, "class", classDoc); + + output.endBody(); + output.endPage(); + output.close(); + } + + private void printInheritedMembers(HtmlPage output, + ProgramElementDoc[] memberDocs, + String headerFormat, + ClassDoc superclass) + { + if (memberDocs.length > 0) { + + output.beginDiv(CssClass.TABLE_CONTAINER); + output.beginTable(CssClass.CLASS_SUMMARY, new String[] { "border", "width" }, new String[] { "1", "100%" }); + String superclassLink; + if (superclass.isIncluded()) { + superclassLink = superclass.containingPackage().name() + + "." + createTypeHref(output, superclass, false); + } + else { + superclassLink = createTypeHref(output, superclass, true); + } + output.rowDiv(CssClass.TABLE_SUB_HEADER, + new MessageFormat(headerFormat).format(new Object[] { + superclassLink + })); + + output.beginRow(); + output.beginCell(CssClass.CLASS_SUMMARY_INHERITED); + for (int i=0; i<memberDocs.length; ++i) { + ProgramElementDoc memberDoc = memberDocs[i]; + if (i > 0) { + output.print(", "); + } + String title = null; + if (memberDoc.isMethod()) { + title = memberDoc.name() + ((MethodDoc)memberDoc).flatSignature(); + } + else if (memberDoc.isInterface()) { + title = "interface " + ((ClassDoc)memberDoc).qualifiedName(); + } + else if (memberDoc.isClass()) { + title = "class " + ((ClassDoc)memberDoc).qualifiedName(); + } + output.beginAnchor(getMemberDocURL(output, memberDoc), title); + output.beginSpan(CssClass.CLASS_SUMMARY_INHERITED_MEMBER); + output.print(memberDoc.name()); + output.endSpan(CssClass.CLASS_SUMMARY_INHERITED_MEMBER); + output.endAnchor(); + } + output.endCell(); + output.endRow(); + output.endTable(); + output.endDiv(CssClass.TABLE_CONTAINER); + } + } + + private void collectSpecifiedByRecursive(Set specifyingInterfaces, + ClassDoc classDoc, + MethodDoc methodDoc) + { + ClassDoc[] interfaces = classDoc.interfaces(); + for (int i=0; i<interfaces.length; ++i) { + MethodDoc[] methods = interfaces[i].methods(); + for (int j=0; j<methods.length; ++j) { + if (methods[j].name().equals(methodDoc.name()) + && methods[j].signature().equals(methodDoc.signature())) { + specifyingInterfaces.add(methods[j]); + break; + } + } + collectSpecifiedByRecursive(specifyingInterfaces, + interfaces[i], + methodDoc); + } + } + + private void printMemberDetails(HtmlPage output, + ProgramElementDoc[] memberDocs, String header, + boolean isOnSerializedPage, + String anchor) + { + if (memberDocs.length > 0) { + + if (null != anchor) { + output.anchorName(anchor); + } + + CssClass sectionClass; + CssClass headerClass; + if (isOnSerializedPage) { + sectionClass = CssClass.SERIALIZED_SECTION; + headerClass = CssClass.SERIALIZED_SECTION_HEADER; + } + else { + sectionClass = CssClass.SECTION; + headerClass = CssClass.SECTION_HEADER; + } + output.div(headerClass, header); + output.beginDiv(sectionClass); + + for (int i=0; i<memberDocs.length; ++i) { + if (i>0) { + output.hr(); + } + + ProgramElementDoc memberDoc = memberDocs[i]; + + output.anchorName(getMemberAnchor(memberDoc)); + + output.beginDiv(CssClass.MEMBER_DETAIL); + output.div(CssClass.MEMBER_DETAIL_NAME, memberDoc.name()); + + StringBuffer synopsis = new StringBuffer(); + int synopsisLength = 0; + + if (!isOnSerializedPage || !memberDoc.isField()) { + String fullModifiers = getFullModifiers(memberDoc); + synopsis.append(fullModifiers); + synopsisLength += fullModifiers.length(); + + } + if (memberDoc.isMethod() || memberDoc.isField()) { + Type type; + if (memberDoc.isMethod()) { + type = ((MethodDoc)memberDoc).returnType(); + } + else { + type = ((FieldDoc)memberDoc).type(); + } + + synopsis.append(" "); + synopsisLength ++; + synopsis.append(createTypeHref(output, type, false)); + if (null != type.asClassDoc() && type.asClassDoc().isIncluded()) { + synopsisLength += type.asClassDoc().name().length(); + } + else { + synopsisLength += type.qualifiedTypeName().length(); + } + synopsisLength += type.dimension().length(); + } + + synopsis.append(" "); + synopsisLength ++; + + if (optionLinkSource.getValue() && null != memberDoc.position()) { + ClassDoc containingClass = memberDoc.containingClass(); + while (null != containingClass.containingClass()) { + containingClass = containingClass.containingClass(); + } + String href = containingClass.name() + "-source" + filenameExtension + "#line." + memberDoc.position().line(); + synopsis.append(output.createHrefString(href, memberDoc.name())); + } + else { + synopsis.append(memberDoc.name()); + } + synopsisLength += memberDoc.name().length(); + + if (memberDoc.isConstructor() || memberDoc.isMethod()) { + //printParameters(output, (ExecutableMemberDoc)memberDoc); + synopsis.append("("); + ++ synopsisLength; + StringBuffer paddingLeft = new StringBuffer(); + for (int j=0; j<synopsisLength; ++j) { + paddingLeft.append(' '); + } + Parameter[] parameters = ((ExecutableMemberDoc)memberDoc).parameters(); + for (int j=0; j<parameters.length; ++j) { + Parameter parameter = parameters[j]; + synopsis.append(createTypeHref(output, parameter.type(), false)); + synopsis.append(" "); + synopsis.append(parameter.name()); + if (j < parameters.length - 1) { + synopsis.append(",\n"); + synopsis.append(paddingLeft); + } + } + synopsis.append(")"); + ClassDoc[] exceptions = ((ExecutableMemberDoc)memberDoc).thrownExceptions(); + if (exceptions.length > 0) { + synopsis.append("\n throws "); + for (int j=0; j<exceptions.length; ++j) { + ClassDoc exception = exceptions[j]; + synopsis.append(createTypeHref(output, exception, false)); + if (j < exceptions.length - 1) { + synopsis.append(",\n "); + } + } + } + } + + output.beginDiv(CssClass.MEMBER_DETAIL_SYNOPSIS); + output.print(synopsis.toString()); + output.endDiv(CssClass.MEMBER_DETAIL_SYNOPSIS); + + output.beginDiv(CssClass.MEMBER_DETAIL_BODY); + + Tag[] deprecatedTags = memberDoc.tags("deprecated"); + if (deprecatedTags.length > 0) { + output.beginDiv(CssClass.DEPRECATED_INLINE); + output.beginSpan(CssClass.DEPRECATED_HEADER); + output.print("Deprecated. "); + output.endSpan(CssClass.DEPRECATED_HEADER); + output.beginSpan(CssClass.DEPRECATED_BODY); + } + for (int j=0; j<deprecatedTags.length; ++j) { + printTags(output, memberDoc, deprecatedTags[j].inlineTags(), true); + } + if (deprecatedTags.length > 0) { + output.endSpan(CssClass.DEPRECATED_BODY); + output.beginDiv(CssClass.DEPRECATED_INLINE); + } + + output.beginDiv(CssClass.MEMBER_DETAIL_DESCRIPTION); + printTags(output, memberDoc, memberDoc.inlineTags(), false); + output.endDiv(CssClass.MEMBER_DETAIL_DESCRIPTION); + + if (memberDoc.isConstructor() || memberDoc.isMethod()) { + + if (memberDoc.isMethod()) { + Set specifyingInterfaces = new LinkedHashSet(); + if (memberDoc.containingClass().isInterface()) { + collectSpecifiedByRecursive(specifyingInterfaces, + memberDoc.containingClass(), + (MethodDoc)memberDoc); + } + else { + for (ClassDoc cd = memberDoc.containingClass(); + null != cd; cd = cd.superclass()) { + collectSpecifiedByRecursive(specifyingInterfaces, + cd, + (MethodDoc)memberDoc); + } + } + + if (!specifyingInterfaces.isEmpty() + && !isOnSerializedPage) { + output.beginDiv(CssClass.MEMBER_DETAIL_SPECIFIED_BY_LIST); + output.div(CssClass.MEMBER_DETAIL_SPECIFIED_BY_HEADER, "Specified by:"); + Iterator it = specifyingInterfaces.iterator(); + while (it.hasNext()) { + MethodDoc specifyingInterfaceMethod = (MethodDoc)it.next(); + output.beginDiv(CssClass.MEMBER_DETAIL_SPECIFIED_BY_ITEM); + output.beginAnchor(getMemberDocURL(output, + specifyingInterfaceMethod)); + output.print(memberDoc.name()); + output.endAnchor(); + output.print(" in interface "); + printType(output, specifyingInterfaceMethod.containingClass()); + output.endDiv(CssClass.MEMBER_DETAIL_SPECIFIED_BY_ITEM); + } + output.endDiv(CssClass.MEMBER_DETAIL_SPECIFIED_BY_LIST); + } + + ClassDoc overriddenClassDoc = null; + MemberDoc specifyingSuperMethod = null; + + for (ClassDoc superclassDoc = memberDoc.containingClass().superclass(); + null != superclassDoc && null == overriddenClassDoc; + superclassDoc = superclassDoc.superclass()) { + + MethodDoc[] methods = superclassDoc.methods(); + for (int j=0; j<methods.length; ++j) { + if (methods[j].name().equals(memberDoc.name()) + && methods[j].signature().equals(((MethodDoc)memberDoc).signature())) { + overriddenClassDoc = superclassDoc; + specifyingSuperMethod = methods[j]; + break; + } + } + } + + if (null != overriddenClassDoc) { + output.beginDiv(CssClass.MEMBER_DETAIL_OVERRIDDEN_LIST); + output.div(CssClass.MEMBER_DETAIL_OVERRIDDEN_HEADER, "Overrides:"); + output.beginDiv(CssClass.MEMBER_DETAIL_OVERRIDDEN_ITEM); + + output.beginAnchor(getMemberDocURL(output, + specifyingSuperMethod)); + output.print(memberDoc.name()); + output.endAnchor(); + output.print(" in interface "); + printType(output, overriddenClassDoc); + + output.endDiv(CssClass.MEMBER_DETAIL_OVERRIDDEN_ITEM); + output.endDiv(CssClass.MEMBER_DETAIL_OVERRIDDEN_LIST); + } + } + + if (!optionNoComment.getValue()) { + + ExecutableMemberDoc execMemberDoc + = (ExecutableMemberDoc)memberDoc; + + if (execMemberDoc.paramTags().length > 0) { + output.beginDiv(CssClass.MEMBER_DETAIL_PARAMETER_LIST); + output.div(CssClass.MEMBER_DETAIL_PARAMETER_HEADER, "Parameters:"); + Parameter[] parameters = execMemberDoc.parameters(); + for (int j=0; j<parameters.length; ++j) { + Parameter parameter = parameters[j]; + ParamTag[] paramTags = execMemberDoc.paramTags(); + ParamTag paramTag = null; + for (int k=0; k<paramTags.length; ++k) { + if (paramTags[k].parameterName().equals(parameter.name())) { + paramTag = paramTags[k]; + break; + } + } + + if (null != paramTag) { + output.beginDiv(CssClass.MEMBER_DETAIL_PARAMETER_ITEM); + output.beginSpan(CssClass.MEMBER_DETAIL_PARAMETER_ITEM_NAME); + output.print(parameter.name()); + output.endSpan(CssClass.MEMBER_DETAIL_PARAMETER_ITEM_NAME); + output.beginSpan(CssClass.MEMBER_DETAIL_PARAMETER_ITEM_SEPARATOR); + output.print(" - "); + output.endSpan(CssClass.MEMBER_DETAIL_PARAMETER_ITEM_SEPARATOR); + output.beginSpan(CssClass.MEMBER_DETAIL_PARAMETER_ITEM_DESCRIPTION); + printTags(output, execMemberDoc, paramTag.inlineTags(), false); + output.endSpan(CssClass.MEMBER_DETAIL_PARAMETER_ITEM_DESCRIPTION); + output.endDiv(CssClass.MEMBER_DETAIL_PARAMETER_ITEM); + } + } + output.endDiv(CssClass.MEMBER_DETAIL_PARAMETER_LIST); + } + + if (execMemberDoc.isMethod() + && !"void".equals(((MethodDoc)execMemberDoc).returnType().typeName())) { + + Tag[] returnTags = execMemberDoc.tags("return"); + if (returnTags.length > 0) { + Tag returnTag = returnTags[0]; + + output.beginDiv(CssClass.MEMBER_DETAIL_RETURN_LIST); + output.div(CssClass.MEMBER_DETAIL_RETURN_HEADER, "Returns:"); + output.beginDiv(CssClass.MEMBER_DETAIL_RETURN_ITEM); + + printTags(output, execMemberDoc, returnTag.inlineTags(), false); + + output.endDiv(CssClass.MEMBER_DETAIL_RETURN_ITEM); + output.endDiv(CssClass.MEMBER_DETAIL_RETURN_LIST); + } + } + + Set thrownExceptions = getThrownExceptions(execMemberDoc); + boolean haveThrowsInfo = false; + ThrowsTag[] throwsTags = execMemberDoc.throwsTags(); + for (int k=0; k<throwsTags.length; ++k) { + ThrowsTag throwsTag = throwsTags[k]; + if (null != throwsTags[k].exception() + && (isUncheckedException(throwsTags[k].exception()) + || thrownExceptions.contains(throwsTag.exception()))) { + haveThrowsInfo = true; + break; + } + } + + if (haveThrowsInfo) { + output.beginDiv(CssClass.MEMBER_DETAIL_THROWN_LIST); + output.div(CssClass.MEMBER_DETAIL_THROWN_HEADER, "Throws:"); + + for (int k=0; k<throwsTags.length; ++k) { + ThrowsTag throwsTag = throwsTags[k]; + if (null != throwsTag.exception() + && (isUncheckedException(throwsTag.exception()) + || thrownExceptions.contains(throwsTag.exception()))) { + output.beginDiv(CssClass.MEMBER_DETAIL_THROWN_ITEM); + output.beginSpan(CssClass.MEMBER_DETAIL_THROWN_ITEM_NAME); + printType(output, throwsTags[k].exception()); + output.endSpan(CssClass.MEMBER_DETAIL_THROWN_ITEM_NAME); + if (null != throwsTag) { + output.beginSpan(CssClass.MEMBER_DETAIL_THROWN_ITEM_SEPARATOR); + output.print(" - "); + output.endSpan(CssClass.MEMBER_DETAIL_THROWN_ITEM_SEPARATOR); + output.beginSpan(CssClass.MEMBER_DETAIL_THROWN_ITEM_DESCRIPTION); + printTags(output, execMemberDoc, throwsTag.inlineTags(), false); + output.endSpan(CssClass.MEMBER_DETAIL_THROWN_ITEM_DESCRIPTION); + } + output.endDiv(CssClass.MEMBER_DETAIL_THROWN_ITEM); + } + } + output.endDiv(CssClass.MEMBER_DETAIL_THROWN_LIST); + } + } + } + + if (!optionNoComment.getValue()) { + + if (memberDoc.isField()) { + FieldDoc fieldDoc = ((FieldDoc)memberDoc); + if (null != fieldDoc.constantValue()) { + output.beginDiv(CssClass.MEMBER_DETAIL_THROWN_LIST); + output.div(CssClass.MEMBER_DETAIL_THROWN_HEADER, "Field Value:"); + output.div(CssClass.MEMBER_DETAIL_THROWN_ITEM, + fieldDoc.constantValueExpression().toString()); + output.endDiv(CssClass.MEMBER_DETAIL_THROWN_LIST); + } + } + + TagletContext context = new HtmlTagletContext(memberDoc, output, isOnSerializedPage); + printTaglets(output, memberDoc.tags(), context); + } + + output.endDiv(CssClass.MEMBER_DETAIL_BODY); + output.endDiv(CssClass.MEMBER_DETAIL); + } + output.endDiv(sectionClass); + } + } + + + private void printParameters(HtmlPage output, ExecutableMemberDoc memberDoc) + { + Parameter[] parameters = memberDoc.parameters(); + output.print("("); + for (int j=0; j<parameters.length; ++j) { + if (j > 0) { + output.print(", "); + } + printType(output, parameters[j].type()); + output.print(" "); + output.print(parameters[j].name()); + } + output.print(")"); + } + + private void printProgramElementDocs(HtmlPage output, + ProgramElementDoc[] memberDocs, + String header, + boolean forceOutputHeader, + String anchor) + { + if (memberDocs.length > 0 || forceOutputHeader) { + output.anchorName(anchor); + output.beginDiv(CssClass.TABLE_CONTAINER); + output.beginTable(CssClass.CLASS_SUMMARY, new String[] { "border", "width" }, new String[] { "1", "100%" }); + output.rowDiv(CssClass.TABLE_HEADER, header); + + for (int i=0; i<memberDocs.length; ++i) { + ProgramElementDoc memberDoc = memberDocs[i]; + output.beginRow(); + + if (!memberDoc.isConstructor()) { + output.beginCell(CssClass.CLASS_SUMMARY_LEFT); + output.beginDiv(CssClass.CLASS_SUMMARY_LEFT_SYNOPSIS); + output.print(getSummaryModifiers(memberDoc) + " "); + if (memberDoc.isMethod()) { + printType(output, ((MethodDoc)memberDoc).returnType()); + } + else if (memberDoc.isField()) { + printType(output, ((FieldDoc)memberDoc).type()); + } + else if (memberDoc.isInterface()) { + output.print(" interface"); + } + else if (memberDoc.isClass()) { + output.print(" class"); + } + output.endDiv(CssClass.CLASS_SUMMARY_LEFT_SYNOPSIS); + output.endCell(); + } + + output.beginCell(CssClass.CLASS_SUMMARY_RIGHT); + output.beginDiv(CssClass.CLASS_SUMMARY_RIGHT_LIST); + output.beginDiv(CssClass.CLASS_SUMMARY_RIGHT_SYNOPSIS); + if (memberDoc.isClass() || memberDoc.isInterface()) { + output.beginAnchor(getClassDocURL(output, (ClassDoc)memberDoc)); + } + else { + output.beginAnchor("#" + getMemberAnchor(memberDoc)); + } + output.print(memberDoc.name()); + output.endAnchor(); + if (memberDoc.isConstructor() || memberDoc.isMethod()) { + printParameters(output, (ExecutableMemberDoc)memberDoc); + } + output.endDiv(CssClass.CLASS_SUMMARY_RIGHT_SYNOPSIS); + Tag[] firstSentenceTags; + Tag[] deprecatedTags = memberDoc.tags("deprecated"); + if (deprecatedTags.length > 0) { + firstSentenceTags = deprecatedTags[0].firstSentenceTags(); + } + else { + firstSentenceTags = memberDoc.firstSentenceTags(); + } + + if (null != firstSentenceTags && firstSentenceTags.length > 0) { + output.beginDiv(CssClass.CLASS_SUMMARY_RIGHT_DESCRIPTION); + if (deprecatedTags.length > 0) { + output.beginDiv(CssClass.DEPRECATED); + output.beginSpan(CssClass.DEPRECATED_HEADER); + output.print("Deprecated. "); + output.endSpan(CssClass.DEPRECATED_HEADER); + output.beginSpan(CssClass.DEPRECATED_BODY); + } + printTags(output, memberDoc, firstSentenceTags, true); + if (deprecatedTags.length > 0) { + output.endSpan(CssClass.DEPRECATED_BODY); + output.beginDiv(CssClass.DEPRECATED); + } + output.endDiv(CssClass.CLASS_SUMMARY_RIGHT_DESCRIPTION); + } + output.endDiv(CssClass.CLASS_SUMMARY_RIGHT_LIST); + output.endCell(); + output.endRow(); + } + output.endTable(); + output.endDiv(CssClass.TABLE_CONTAINER); + } + } + + private void printTag(final HtmlPage output, + HtmlRepairer repairer, + Tag tag, boolean firstSentence, + boolean inline, + Doc contextDoc) + { + TagletContext context = new HtmlTagletContext(contextDoc, output, false); + if (firstSentence) { + output.print(renderInlineTags(tag.firstSentenceTags(), context)); + } + else { + output.print(renderInlineTags(tag.inlineTags(), context)); + } + } + + private void printTags(HtmlPage output, Doc contextDoc, Tag[] tags, boolean firstSentence) + { + printTags(output, contextDoc, tags, firstSentence, false); + } + + private void printTags(HtmlPage output, Doc contextDoc, Tag[] tags, boolean firstSentence, boolean inline) + { + if (!optionNoComment.getValue()) { + output.print(renderInlineTags(tags, new HtmlTagletContext(contextDoc, output, false))); + } + + /* + if (!optionNoComment.getValue()) { + output.print(renderInlineTags(tag.firstSentenceTags(), output)); + HtmlRepairer repairer = new HtmlRepairer(getRootDoc(), + true, false, + null, null, + true); + for (int i=0; i<tags.length; ++i) { + printTag(output, repairer, tags[i], firstSentence, inline); + } + output.print(repairer.terminateText()); + } + */ + } + + private String getClassDocURL(HtmlPage output, ClassDoc classDoc) + { + return output.getPathToRoot() + + "/" + + getPackageURL(classDoc.containingPackage()) + + classDoc.name() + filenameExtension; + } + + private String getMemberDocURL(HtmlPage output, ProgramElementDoc memberDoc) + { + ClassDoc classDoc = memberDoc.containingClass(); + PackageDoc packageDoc = classDoc.containingPackage(); + ExternalDocSet externalDocSet = null; + if (classDoc.containingPackage().name().length() > 0) { + externalDocSet = (ExternalDocSet)packageNameToDocSet.get(packageDoc.name()); + } + StringBuffer result = new StringBuffer(); + result.append(getClassDocURL(output, classDoc)); + result.append('#'); + if (null == externalDocSet) { + result.append(getMemberAnchor(memberDoc)); + } + else { + result.append(getMemberAnchor(memberDoc, externalDocSet.isJavadocCompatible())); + } + return result.toString(); + } + + private void printType(HtmlPage output, Type type) + { + printType(output, type, false); + } + + private void printType(HtmlPage output, Type type, boolean fullyQualified) + { + output.print(createTypeHref(output, type, fullyQualified)); + } + + private String createTypeHref(HtmlPage output, Type type, boolean fullyQualified) + { + ClassDoc asClassDoc = type.asClassDoc(); + String url = null; + if (null != asClassDoc && asClassDoc.isIncluded()) { + url = getClassDocURL(output, asClassDoc); + } + else if (!type.isPrimitive()) { + if (type.qualifiedTypeName().length() > type.typeName().length()) { + String packageName = type.qualifiedTypeName(); + packageName = packageName.substring(0, packageName.length() - type.typeName().length() - 1); + + ExternalDocSet externalDocSet + = (ExternalDocSet)packageNameToDocSet.get(packageName); + if (null != externalDocSet) { + url = externalDocSet.getClassDocURL(packageName, type.typeName()); + } + } + } + + StringBuffer result = new StringBuffer(); + + if (null != url && null != asClassDoc) { + String parameters = getTypeParameters(asClassDoc); + if (fullyQualified) { + result.append(output.createHrefString(url,possiblyQualifiedName(asClassDoc) + parameters)); + } + else { + StringBuffer title = new StringBuffer(); + title.append(getClassTypeName(asClassDoc)); + title.append(" in "); + title.append(asClassDoc.containingPackage().name()); + result.append(output.createHrefString(url, asClassDoc.name() + parameters, title.toString())); + } + } + else { + result.append(possiblyQualifiedName(type)); + } + result.append(type.dimension()); + return result.toString(); + } + + private void printTaglets(final HtmlPage output, Tag[] tags, TagletContext context) + { + super.printMainTaglets(tags, context, new TagletPrinter() { + public void printTagletString(String tagletString) { + output.beginDiv(CssClass.TAGLET); + output.print(tagletString); + output.endDiv(CssClass.TAGLET); + } + }); + } + + private String getPackageURL(PackageDoc packageDoc) + { + if (packageDoc.name().length() > 0) { + ExternalDocSet externalDocSet = (ExternalDocSet)packageNameToDocSet.get(packageDoc.name()); + String url; + if (null != externalDocSet) { + url = externalDocSet.getPackageDocURL(packageDoc.name()); + } + else { + url = packageDoc.name().replace('.', '/'); + } + if (!url.endsWith("/")) { + return url + '/'; + } + else { + return url; + } + } + else { + return ""; + } + } + + private String getClassURL(ClassDoc classDoc) + { + ExternalDocSet externalDocSet = null; + if (classDoc.containingPackage().name().length() > 0) { + externalDocSet = (ExternalDocSet)packageNameToDocSet.get(classDoc.containingPackage().name()); + } + if (null != externalDocSet) { + return externalDocSet.getClassDocURL(classDoc.containingPackage().name(), + classDoc.name()); + } + else { + return getPackageURL(classDoc.containingPackage()) + classDoc.name() + filenameExtension; + } + } + + protected void run() + throws DocletConfigurationException, IOException + { + if (optionSerialWarn.getValue()) { + printWarning("Option -serialwarn is currently ignored."); + } + + if (null != optionTitle.getValue()) { + printWarning("Option -title is deprecated."); + } + + if (!optionValidHtml.getValue()) { + printWarning("Option -validhtml hasn't been specified. Generated HTML will not validate."); + } + + + { + boolean warningEmitted = false; + Iterator it = externalDocSets.iterator(); + while (it.hasNext()) { + ExternalDocSet externalDocSet = (ExternalDocSet)it.next(); + printNotice("Fetching package list for external documentation set."); + try { + externalDocSet.load(getTargetDirectory()); + if (!isJavadocCompatibleNames() && externalDocSet.isJavadocCompatible() + && !warningEmitted) { + printWarning("Linking to javadoc-compatible documentation. Generated HTML will not validate "); + warningEmitted = true; + } + } + catch (FileNotFoundException e) { + printWarning("Cannot fetch package list from " + externalDocSet.getPackageListDir()); + } + Iterator pit = externalDocSet.getPackageNames().iterator(); + while (pit.hasNext()) { + String packageName = (String)pit.next(); + packageNameToDocSet.put(packageName, externalDocSet); + } + } + } + printNotice("Building cross-reference information..."); + getInterfaceRelations(); + getAllSubClasses(); + + printNotice("Writing overview files..."); + printFrameSetPage(); + if (!isSinglePackage()) { + printPackagesMenuPage(); + printAllClassesMenuPage(); + printOverviewPage(); + if (!optionNoTree.getValue()) { + printNotice("Writing full tree..."); + printFullTreePage(); + } + } + printPackagesListFile(); + printAboutPage(); + if (!optionNoIndex.getValue()) { + printNotice("Writing index..."); + if (!optionSplitIndex.getValue()) { + printIndexPage(); + } + else { + printSplitIndex(); + } + } + if (outputHelpPage && !optionNoHelp.getValue()) { + printHelpPage(); + } + + // Copy resources + + File resourcesDir = new File(getTargetDirectory(), + "resources"); + + if ((resourcesDir.exists() && !resourcesDir.isDirectory()) + || (!resourcesDir.exists() && !resourcesDir.mkdirs())) { + throw new IOException("Cannot create directory " + resourcesDir); + } + + // Copy resources + + String[] resourceNames = { + "gjdoc.js", + "gjdochtml-clean-layout.css", + "gjdochtml-clean-color1.css", + "inherit.png", + "xhtml11-target10.dtd", + }; + + for (int i=0; i<resourceNames.length; ++i) { + String resourceName = resourceNames[i]; + File targetFile = new File(resourcesDir, + resourceName); + InputStream in = getClass().getResourceAsStream("/htmldoclet/" + resourceName); + if (in == null) { + in = new FileInputStream("src/resources/htmldoclet/" + resourceName); + } + FileOutputStream out = new FileOutputStream(targetFile); + IOToolkit.copyStream(in, out); + in.close(); + out.close(); + } + + // Copy stylesheets + + if (null != optionAddStylesheet.getValue()) { + File addStylesheetTargetFile = new File(resourcesDir, + "user.css"); + + IOToolkit.copyFile(optionAddStylesheet.getValue(), + addStylesheetTargetFile); + } + + if (null != optionStylesheetFile.getValue()) { + File stylesheetTargetFile = new File(resourcesDir, + "user.css"); + + IOToolkit.copyFile(optionStylesheetFile.getValue(), + stylesheetTargetFile); + } + + // Write gjdoc.properties + + File gjdocPropertiesTargetFile = new File(getTargetDirectory(), + "gjdoc.properties"); + writeGjdocProperties(gjdocPropertiesTargetFile); + + /* + else { + InputStream cssIn = getClass().getResourceAsStream("/htmldoclet/gjdochtml-vanilla.css"); + FileOutputStream cssOut = new FileOutputStream(stylesheetTargetFile); + IOToolkit.copyStream(cssIn, cssOut); + cssIn.close(); + cssOut.close(); + } + */ + + if (!optionNoDeprecatedList.getValue()) { + printDeprecationPage(); + } + + printSerializationPage(); + + Collection packageDocsCollection = getAllPackages(); + PackageDoc[] packageDocs + = (PackageDoc[])packageDocsCollection.toArray(new PackageDoc[0]); + + for (int i=0; i<packageDocs.length; ++i) { + PackageDoc packageDoc = packageDocs[i]; + File packageDir = new File(getTargetDirectory(), + packageDoc.name().replace('.', File.separatorChar)); + if (!packageDir.exists() && !packageDir.mkdirs()) { + throw new IOException("Couldn't create directory " + packageDir); + } + try { + List packageSourceDirs = getPackageSourceDirs(packageDoc); + Iterator pdIt = packageSourceDirs.iterator(); + while (pdIt.hasNext()) { + File sourcePackageDir = (File)pdIt.next(); + copyDocFiles(sourcePackageDir, packageDir); + } + } + catch (IOException ignore) { + } + String pathToRoot = getPathToRoot(packageDir, getTargetDirectory()); + String packageName = packageDoc.name(); + if (0 == packageName.length()) { + packageName = "<unnamed>"; + } + printNotice("Writing HTML files for package " + packageName); + printPackagePage(packageDir, pathToRoot, packageDoc, + (i > 0) ? packageDocs[i - 1] : null, + (i < packageDocs.length - 1) ? packageDocs[i + 1] : null); + if (!optionNoTree.getValue()) { + printPackageTreePage(packageDir, pathToRoot, packageDoc); + } + printPackageClassesMenuPage(packageDir, pathToRoot, packageDoc); + ClassDoc[] classDocs = packageDoc.allClasses(); + for (int j=0; j<classDocs.length; ++j) { + ClassDoc classDoc = classDocs[j]; + if (classDoc.isIncluded()) { + printClassPage(packageDir, pathToRoot, + classDocs[j], + (j > 0) ? classDocs[j - 1] : null, + (j < classDocs.length - 1) ? classDocs[j + 1] : null + ); + if (optionUse.getValue()) { + printClassUsagePage(packageDir, pathToRoot, classDocs[j]); + } + if (optionLinkSource.getValue() && null == classDoc.containingClass()) { + try { + File sourceFile = getSourceFile(classDoc); + + Java2xhtml java2xhtml = new Java2xhtml(); + Properties properties = new Properties(); + properties.setProperty("isCodeSnippet", "true"); + properties.setProperty("hasLineNumbers", "true"); + java2xhtml.setProperties(properties); + + StringWriter sourceBuffer = new StringWriter(); + FileReader sourceReader = new FileReader(sourceFile); + IOToolkit.copyStream(sourceReader, sourceBuffer); + sourceReader.close(); + String result = java2xhtml.makeHTML(sourceBuffer.getBuffer(), sourceFile.getName()); + + printSourcePage(packageDir, + classDoc, + result); + } + catch (IOException e) { + printWarning("Cannot locate source file for class " + classDoc.qualifiedTypeName()); + } + } + } + } + } + } + + private String getPathToRoot(File subDir, File rootDir) + { + StringBuffer result = new StringBuffer(); + while (!subDir.equals(rootDir)) { + if (result.length() > 0) { + result.append("/"); + } + subDir = subDir.getParentFile(); + result.append(".."); + } + if (0 == result.length()) { + result.append("."); + } + return result.toString(); + } + + private String getClassTypeName(ClassDoc classDoc) + { + if (classDoc.isInterface()) { + return "Interface"; + } + else { + return "Class"; + } + } + + private String getClassTypeKeyword(ClassDoc classDoc) + { + if (classDoc.isInterface()) { + return "interface"; + } + else { + return "class"; + } + } + + private String getMemberAnchor(ProgramElementDoc memberDoc) + { + return getMemberAnchor(memberDoc, isJavadocCompatibleNames()); + } + + private String getMemberAnchor(ProgramElementDoc memberDoc, boolean javadocCompatibility) + { + StringBuffer anchor = new StringBuffer(); + anchor.append(memberDoc.name()); + if (memberDoc.isConstructor() || memberDoc.isMethod()) { + if (javadocCompatibility) { + anchor.append(((ExecutableMemberDoc)memberDoc).signature()); + } + else { + anchor.append(':'); + Parameter[] parameters = ((ExecutableMemberDoc)memberDoc).parameters(); + for (int i=0; i<parameters.length; ++i) { + anchor.append(parameters[i].type().typeName()); + for (int j=0; j<parameters[i].type().dimension().length()/2; ++j) { + anchor.append('-'); + } + if (i < parameters.length - 1) { + anchor.append(':'); + } + } + } + } + return anchor.toString(); + } + + private String getFullModifiers(ProgramElementDoc memberDoc) + { + StringBuffer result = new StringBuffer(); + if (memberDoc.isPackagePrivate()) { + result.append("(package private) "); + } + result.append(memberDoc.modifiers()); + if ((memberDoc.isClass() && ((ClassDoc)memberDoc).isAbstract()) + || (memberDoc.isMethod() && ((MethodDoc)memberDoc).isAbstract())) { + result.append(" abstract"); + } + return result.toString(); + } + + private String getSummaryModifiers(ProgramElementDoc memberDoc) + { + StringBuffer result = new StringBuffer(); + if (memberDoc.isPackagePrivate()) { + result.append("(package private) "); + } + else if (memberDoc.isPrivate()) { + result.append("private "); + } + else if (memberDoc.isProtected()) { + result.append("protected "); + } + if (memberDoc.isStatic()) { + result.append("static"); + } + else if ((memberDoc.isClass() && ((ClassDoc)memberDoc).isAbstract()) + || (memberDoc.isMethod() && ((MethodDoc)memberDoc).isAbstract())) { + result.append("abstract"); + } + return result.toString(); + } + + protected DocletOption[] getOptions() + { + return options; + } + + private DocletOptionFlag optionNoNavBar = + new DocletOptionFlag("-nonavbar"); + + private DocletOptionFlag optionNoTree = + new DocletOptionFlag("-notree"); + + private DocletOptionFlag optionNoDeprecatedList = + new DocletOptionFlag("-nodeprecatedlist"); + + private DocletOptionFlag optionNoIndex = + new DocletOptionFlag("-noindex"); + + private DocletOptionFlag optionUse = + new DocletOptionFlag("-use"); + + private DocletOptionFlag optionNoHelp = + new DocletOptionFlag("-nohelp"); + + private DocletOptionFlag optionNoComment = + new DocletOptionFlag("-nocomment"); + + private DocletOptionFlag optionSerialWarn = + new DocletOptionFlag("-serialwarn"); + + private DocletOptionFlag optionSplitIndex = + new DocletOptionFlag("-splitindex"); + + private DocletOptionString optionHeader = + new DocletOptionString("-header"); + + private DocletOptionString optionFooter = + new DocletOptionString("-footer"); + + private DocletOptionString optionBottom = + new DocletOptionString("-bottom"); + + private DocletOptionString optionWindowTitle = + new DocletOptionString("-windowtitle"); + + private DocletOptionString optionDocTitle = + new DocletOptionString("-doctitle"); + + private DocletOptionString optionTitle = + new DocletOptionString("-title"); + + private DocletOptionFile optionHelpFile = + new DocletOptionFile("-helpfile"); + + private DocletOptionFile optionStylesheetFile = + new DocletOptionFile("-stylesheetfile"); + + private DocletOptionFlag optionLinkSource = + new DocletOptionFlag("-linksource"); + + private DocletOption optionLink = + new DocletOption("-link") { + + public int getLength() + { + return 2; + } + + public boolean set(String[] optionArr) + { + externalDocSets.add(new ExternalDocSet(optionArr[1], null)); + return true; + } + }; + + private DocletOption optionLinkOffline = + new DocletOption("-linkoffline") { + + public int getLength() + { + return 3; + } + + public boolean set(String[] optionArr) + { + externalDocSets.add(new ExternalDocSet(optionArr[1], optionArr[2])); + return true; + } + }; + + private DocletOptionString optionDocEncoding = + new DocletOptionString("-docencoding"); + + private DocletOptionString optionEncoding = + new DocletOptionString("-encoding"); + + private DocletOptionString optionCharset = + new DocletOptionString("-charset"); + + private DocletOptionFile optionAddStylesheet = + new DocletOptionFile("-addstylesheet"); + + private DocletOptionFlag optionValidHtml = + new DocletOptionFlag("-validhtml"); + + private DocletOptionString optionBaseUrl = + new DocletOptionString("-baseurl"); + + private DocletOption[] options = + { + optionNoNavBar, + optionNoTree, + optionNoDeprecatedList, + optionNoIndex, + optionNoHelp, + optionNoComment, + optionUse, + optionSplitIndex, + optionHeader, + optionFooter, + optionBottom, + optionHelpFile, + optionStylesheetFile, + optionWindowTitle, + optionDocTitle, + optionTitle, + optionLinkSource, + optionLink, + optionLinkOffline, + optionDocEncoding, + optionEncoding, + optionCharset, + optionAddStylesheet, + optionValidHtml, + optionBaseUrl, + }; + + static { + setInstance(new HtmlDoclet()); + } + + private static String replaceDocRoot(HtmlPage output, String str) + { + return StringToolkit.replace(str, "{@docRoot}", output.getPathToRoot()); + } + + private String getOutputDocEncoding() + { + String encoding = optionDocEncoding.getValue(); + + if (null == encoding) { + encoding = optionEncoding.getValue(); + } + + return encoding; + } + + private String getOutputCharset() + { + if (null == outputCharset) { + + if (null != optionCharset.getValue()) { + outputCharset = optionCharset.getValue(); + } + else { + String fileEncoding = System.getProperty("file.encoding"); + if (null != fileEncoding) { + try { + outputCharset = Charset.forName(fileEncoding).name(); + } + catch (Exception ignore) { + } + } + + if (null == outputCharset) { + printWarning("Cannot determine platform default charset, falling back to ISO-8859-1."); + outputCharset = "ISO-8859-1"; + } + } + } + return outputCharset; + } + + public InlineTagRenderer getInlineTagRenderer() + { + return this; + } + + public String renderInlineTags(Tag[] tags, TagletContext context) + { + StringBuffer result = new StringBuffer(); + + HtmlRepairer repairer = new HtmlRepairer(getRootDoc(), + true, false, + null, null, + true); + + for (int i=0; i<tags.length; ++i) { + + Tag tag = tags[i]; + + if ("Text".equals(tag.name())) { + result.append(repairer.getWellformedHTML(tag.text())); + } + else if ("@link".equals(tag.name())) { + result.append(renderSeeTag((SeeTag)tag, context, false)); + } + else if ("@linkplain".equals(tag.name())) { + result.append(renderSeeTag((SeeTag)tag, context, true)); + } + else if ("@docRoot".equals(tag.name())) { + result.append(((HtmlTagletContext)context).getOutput().getPathToRoot()); + } + else { + //TagletContext context = TagletContext.OVERVIEW; // FIXME + Taglet taglet = (Taglet)tagletMap.get(tag.name().substring(1)); + if (null != taglet) { + if (taglet instanceof GnuExtendedTaglet) { + result.append(((GnuExtendedTaglet)taglet).toString(tag, context)); + } + else { + result.append(taglet.toString(tag)); + } + } + } + } + result.append(repairer.terminateText()); + return result.toString(); + } + + public String renderSeeTag(SeeTag seeTag, TagletContext context, boolean plainFont) + { + StringBuffer result = new StringBuffer(); + + String href = null; + String label = null; + MemberDoc referencedMember = seeTag.referencedMember(); + if (null != seeTag.referencedClass()) { + + href = getClassDocURL(((HtmlTagletContext)context).getOutput(), seeTag.referencedClass()); + + Doc doc = context.getDoc(); + ClassDoc classDoc = null; + if (doc.isClass() || doc.isInterface()) { + classDoc = (ClassDoc)doc; + } + else if (doc.isField() || doc.isMethod() || doc.isConstructor()) { + classDoc = ((MemberDoc)doc).containingClass(); + } + + if (null == referencedMember + || seeTag.referencedClass() != classDoc + || ((HtmlTagletContext)context).isOnSerializedPage()) { + + if (!seeTag.referencedClass().isIncluded()) { + label = possiblyQualifiedName(seeTag.referencedClass()); + } + else { + label = seeTag.referencedClass().typeName(); + } + if (null != referencedMember) { + label += '.'; + } + } + else { + label = ""; + } + + if (null != referencedMember) { + label += referencedMember.name(); + if (referencedMember.isMethod() || referencedMember.isConstructor()) { + label += ((ExecutableMemberDoc)referencedMember).flatSignature(); + } + href += '#' + getMemberAnchor(referencedMember); + } + else if (null != seeTag.referencedMemberName()) { + href = null; + } + } + else { + String referencedClassName = seeTag.referencedClassName(); + + if (null != referencedClassName) { + + String referencedPackageName = null; + + Iterator it = packageNameToDocSet.keySet().iterator(); + while (it.hasNext()) { + String packageName = (String)it.next(); + if ((null == referencedPackageName + || packageName.length() > referencedPackageName.length()) + && referencedClassName.startsWith(packageName + '.')) { + referencedPackageName = packageName; + } + } + + if (null != referencedPackageName) { + ExternalDocSet externalDocSet + = (ExternalDocSet)packageNameToDocSet.get(referencedPackageName); + + String className = referencedClassName.substring(referencedPackageName.length() + 1); + href = externalDocSet.getClassDocURL(referencedPackageName, + className); + label = className; + + String referencedMemberName = seeTag.referencedMemberName(); + + if (null != referencedMemberName) { + label += '.'; + label += referencedMemberName; + href += '#' + transformReferencedMemberName(referencedMemberName, + externalDocSet.isJavadocCompatible()); + } + else if (null != seeTag.referencedMemberName()) { + href = null; + } + } + } + } + + if (null != seeTag.label() + && seeTag.label().length() > 0) { + label = seeTag.label(); + } + + if (null == label) { + label = seeTag.text(); + if (label.startsWith("#")) { + label = label.substring(1); + } + else { + label = label.replace('#', '.'); + } + label.trim(); + } + + if (null != href) { + result.append("<a href=\""); + result.append(href); + result.append("\">"); + if (!plainFont) { + result.append("<code>"); + } + result.append(label); + if (!plainFont) { + result.append("</code>"); + } + result.append("</a>"); + } + else { + if (!plainFont) { + result.append("<code>"); + } + result.append(label); + if (!plainFont) { + result.append("</code>"); + } + } + + return result.toString(); + } + + protected String renderTag(String tagName, Tag[] tags, TagletContext context) + { + Doc doc = context.getDoc(); + + if ("see".equals(tagName) + && ((tags.length > 0) + || (doc.isClass() + && (((ClassDoc)doc).isSerializable() + || ((ClassDoc)doc).isExternalizable())))) { + + StringBuffer result = new StringBuffer(); + result.append("<dl class=\"tag list\">"); + result.append("<dt class=\"tag section header\"><b>"); + result.append("See Also:"); + result.append("</b></dt>"); + + boolean oneLine = true; + + if (oneLine) { + result.append("<dd>"); + } + + for (int i = 0; i < tags.length; ++i) { + if (oneLine) { + if (i > 0) { + result.append(", "); + } + } + else { + result.append("<dd>"); + } + result.append(renderSeeTag((SeeTag)tags[i], context, false)); + if (!oneLine) { + result.append("</dd>"); + } + } + + if ((doc instanceof ClassDoc) + && (((ClassDoc)doc).isSerializable() || ((ClassDoc)doc).isExternalizable())) { + if (tags.length > 0) { + result.append(", "); + } + HtmlPage output = ((HtmlTagletContext)context).getOutput(); + result.append("<a href=\"" + output.getPathToRoot() + "/serialized-form" + filenameExtension + "#" + ((ClassDoc)doc).qualifiedName() + "\">Serialized Form</a>"); + } + + if (oneLine) { + result.append("</dd>"); + } + result.append("</dl>"); + return result.toString(); + } + else if (tags.length > 0 + && "serial".equals(tagName) + && ((HtmlTagletContext)context).isOnSerializedPage()) { + + return renderInlineTags(tags[0].inlineTags(), context); + } + else { + return ""; + } + } + + private String getWindowTitle() + { + if (null == optionWindowTitle.getValue()) { + return "Generated API Documentation"; + } + else { + return optionWindowTitle.getValue(); + } + } + + private String getPageTitle(String title) + { + if (null == optionWindowTitle.getValue()) { + return title; + } + else { + return title + " (" + optionWindowTitle.getValue() + ")"; + } + } + + protected String getDocletVersion() + { + if (null == docletVersion) { + docletVersion = gnu.classpath.Configuration.CLASSPATH_VERSION; + } + return docletVersion; + } + + private Map getStylesheets() + { + Map sheets = new HashMap(); + if (null != optionStylesheetFile.getValue()) { + sheets.put("User-specified", new String[] { + "resources/user.css" + }); + } + else { + List cleanSheets = new LinkedList(); + cleanSheets.add("resources/gjdochtml-clean-layout.css"); + cleanSheets.add("resources/gjdochtml-clean-color1.css"); + if (null != optionAddStylesheet.getValue()) { + cleanSheets.add("resources/user.css"); + } + sheets.put("GNU Clean", cleanSheets.toArray(new String[0])); + } + return sheets; + } + + protected boolean isSinglePackage() + { + if (getRootDoc().firstSentenceTags().length > 0) { + return false; + } + else if (null != optionDocTitle.getValue() + || null != optionTitle.getValue()) { + return false; + } + else { + return super.isSinglePackage(); + } + } + + private String getTypeParameters(ClassDoc classDoc) + { + String parameters = ""; + TypeVariable[] params = classDoc.typeParameters(); + if (params != null && params.length > 0) + { + parameters = "<"; + for (int a = 0; a < params.length; ++a) + { + parameters += params[a].typeName(); + Type[] bounds = params[a].bounds(); + if (bounds != null) + { + parameters += " extends "; + for (int b = 0; a < bounds.length; ++b) + { + parameters += bounds[a]; + if (b != bounds.length - 1) + parameters += " & "; + } + } + if (a != params.length - 1) + parameters += ","; + } + parameters += ">"; + } + return parameters; + } + + private String transformReferencedMemberName(String referencedMemberName, + boolean javadocCompatibility) + { + if (!javadocCompatibility) { + StringBuffer result = new StringBuffer(); + for (int i=0; i<referencedMemberName.length(); ++i) { + char c = referencedMemberName.charAt(i); + switch (c) { + case '(': result.append(':'); break; + case ')': break; + case ',': result.append(':'); break; + case '[': result.append('-'); break; + case ']': break; + default: result.append(c); break; + } + } + return result.toString(); + } + else { + return referencedMemberName; + } + } + + public void writeGjdocProperties(File outputFile) + throws IOException + { + Properties properties = new Properties(); + properties.setProperty("gjdoc.version", getDocletVersion()); + properties.setProperty("gjdoc.compat", Boolean.toString(isJavadocCompatibleNames())); + + FileOutputStream out = new FileOutputStream(outputFile); + properties.store(out, "GNU Gjdoc API Documentation Set Descriptor"); + out.close(); + } + + public boolean isJavadocCompatibleNames() + { + return !optionValidHtml.getValue(); + } + + private HtmlPage newHtmlPage(File file, + String pathToRoot) + throws IOException + { + return new HtmlPage(file, + pathToRoot, + getOutputDocEncoding(), + optionBaseUrl.getValue(), + getTargetDirectory()); + } + + private HtmlPage newHtmlPage(File file, + String pathToRoot, + String docType) + throws IOException + { + return new HtmlPage(file, + pathToRoot, + getOutputDocEncoding(), + optionBaseUrl.getValue(), + getTargetDirectory(), + docType); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlPage.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlPage.java new file mode 100644 index 000000000..0315cb5df --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlPage.java @@ -0,0 +1,535 @@ +/* gnu.classpath.tools.doclets.htmldoclet.HtmlPage + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.htmldoclet; + +import gnu.classpath.tools.IOToolkit; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.sun.javadoc.Tag; + +/** + * Allows outputting an HTML document without having to build the + * document tree in-memory. + */ +public class HtmlPage +{ + private File file; + private PrintWriter out; + private String pathToRoot; + private String docType; + private String baseUrl; + private File rootDir; + + public static final String DOCTYPE_FRAMESET = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">"; + + public HtmlPage(File file, String pathToRoot, String encoding, String baseUrl, File rootDir) + throws IOException + { + this(file, pathToRoot, encoding, baseUrl, rootDir, "<!DOCTYPE html PUBLIC \"-//gnu.org///DTD XHTML 1.1 plus Target 1.0//EN\" \"" + pathToRoot + "/resources/xhtml11-target10.dtd\">"); + } + + public HtmlPage(File file, String pathToRoot, String encoding, String baseUrl, File rootDir, String docType) + throws IOException + { + this.file = file; + OutputStream fileOut = new FileOutputStream(file); + Writer writer; + if (null != encoding) { + writer = new OutputStreamWriter(fileOut, + encoding); + } + else { + writer = new OutputStreamWriter(fileOut); + } + this.out = new PrintWriter(new BufferedWriter(writer)); + this.pathToRoot = pathToRoot; + this.docType = docType; + this.baseUrl = baseUrl; + this.rootDir = rootDir; + } + + public void beginElement(String elementName) + { + print('<'); + print(elementName); + print('>'); + } + + public void beginElement(String elementName, String attributeName, String attributeValue) + { + print('<'); + print(elementName); + print(' '); + print(attributeName); + print('='); + print('\"'); + print(attributeValue); + print('\"'); + print('>'); + } + + public void beginElement(String elementName, String[] attributeNames, String[] attributeValues) + { + print('<'); + print(elementName); + for (int i=0; i<attributeNames.length; ++i) { + if (null != attributeValues[i]) { + print(' '); + print(attributeNames[i]); + print('='); + print('\"'); + print(attributeValues[i]); + print('\"'); + } + } + print('>'); + } + + public void beginElement(String elementName, String attributeName, String attributeValue, String[] attributeNames, String[] attributeValues) + { + print('<'); + print(elementName); + print(' '); + print(attributeName); + print('='); + print('\"'); + print(attributeValue); + print('\"'); + if (null != attributeNames) { + for (int i=0; i<attributeNames.length; ++i) { + if (null != attributeValues[i]) { + print(' '); + print(attributeNames[i]); + print('='); + print('\"'); + print(attributeValues[i]); + print('\"'); + } + } + } + print('>'); + } + + public void atomicElement(String elementName) + { + print('<'); + print(elementName); + print("/>"); + } + + public void atomicElement(String elementName, String attributeName, String attributeValue) + { + print('<'); + print(elementName); + print(' '); + print(attributeName); + print('='); + print('\"'); + print(attributeValue); + print('\"'); + print("/>"); + } + + public void atomicElement(String elementName, String[] attributeNames, String[] attributeValues) + { + print('<'); + print(elementName); + for (int i=0; i<attributeNames.length; ++i) { + if (null != attributeValues[i]) { + print(' '); + print(attributeNames[i]); + print('='); + print('\"'); + print(attributeValues[i]); + print('\"'); + } + } + print("/>"); + } + + + public void endElement(String elementName) + { + print("</"); + print(elementName); + print('>'); + } + + + public void beginDiv(CssClass cssClass) + { + String[] divAttributeNames = cssClass.getAttributeNames(); + String[] divAttributeValues = cssClass.getAttributeValues(); + if (null == divAttributeNames) { + divAttributeNames = new String[0]; + } + if (null == divAttributeValues) { + divAttributeValues = new String[0]; + } + + String[] attributeNames = new String[1 + divAttributeNames.length]; + String[] attributeValues = new String[1 + divAttributeValues.length]; + + System.arraycopy(divAttributeNames, 0, attributeNames, 1, divAttributeNames.length); + System.arraycopy(divAttributeValues, 0, attributeValues, 1, divAttributeNames.length); + + attributeNames[0] = "class"; + attributeValues[0] = cssClass.getName(); + + beginElement(cssClass.getDivElementName(), attributeNames, attributeValues); + if (null != cssClass.getInnerElementName()) { + beginElement(cssClass.getInnerElementName()); + } + } + + public void endDiv(CssClass cssClass) + { + if (null != cssClass.getInnerElementName()) { + endElement(cssClass.getInnerElementName()); + } + endElement(cssClass.getDivElementName()); + } + + public void beginSpan(CssClass cssClass) + { + beginElement(cssClass.getSpanElementName(), "class", cssClass.getName()); + } + + public void endSpan(CssClass cssClass) + { + endElement(cssClass.getSpanElementName()); + } + + public void hr() + { + atomicElement("hr"); + } + + public void br() + { + atomicElement("br"); + } + + public void print(String text) + { + out.print(text); + } + + public void print(char c) + { + out.print(c); + } + + public void div(CssClass cssClass, String contents) + { + beginDiv(cssClass); + print(contents); + endDiv(cssClass); + } + + public void span(CssClass cssClass, String contents) + { + beginSpan(cssClass); + print(contents); + endSpan(cssClass); + } + + public void beginPage(String title, String charset, Map stylesheets) + throws IOException + { + beginPage(title, charset, Collections.EMPTY_SET, stylesheets); + } + + public void beginPage(String title, String charset, + Collection keywords, Map stylesheets) + throws IOException + { + print("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\n"); + print(docType); + print("<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">"); + beginElement("head"); + beginElement("title"); + print(title); + endElement("title"); + if (null != baseUrl && baseUrl.length() > 0) { + StringBuffer url = new StringBuffer(); + url.append(baseUrl); + if ('/' == url.charAt(url.length() - 1)) { + url.delete(url.length() - 1, url.length()); + } + url.append(file.getCanonicalPath().substring(rootDir.getCanonicalPath().length())); + atomicElement("base", + new String[] { "href" }, + new String[] { url.toString() }); + } + beginElement("script", + new String[] { "src", "type" }, + new String[] { pathToRoot + "/resources/gjdoc.js", "text/javascript" }); + print("<!-- this comment required for konqueror 3.2.2 -->"); + endElement("script"); + atomicElement("meta", + new String[] { "http-equiv", "content" }, + new String[] { "Content-Type", "text/html; charset=" + charset }); + atomicElement("meta", + new String[] { "name", "content" }, + new String[] { "generator", "GNU Gjdoc Standard Doclet" }); + Iterator keywordIt = keywords.iterator(); + while (keywordIt.hasNext()) { + String keyword = (String)keywordIt.next(); + atomicElement("meta", + new String[] { "name", "content" }, + new String[] { "keywords", keyword }); + } + + Iterator cssIt = stylesheets.keySet().iterator(); + while (cssIt.hasNext()) { + String sheetName = (String)cssIt.next(); + String[] sheetFiles = (String[])stylesheets.get(sheetName); + + for (int i=0; i<sheetFiles.length; ++i) { + String sheetFile = sheetFiles[i]; + atomicElement("link", + new String[] { "rel", "type", "href", "title" }, + new String[] { "stylesheet", "text/css", + pathToRoot + "/" + sheetFile, sheetName }); + } + } + + endElement("head"); + } + + public void endPage() + { + endElement("html"); + } + + public void close() + { + out.close(); + } + + public void beginTable(CssClass cssClass) + { + beginElement("table", "class", cssClass.getName()); + } + + public void beginTable(CssClass cssClass, String[] attributeNames, String[] attributeValues) + { + beginElement("table", "class", cssClass.getName(), attributeNames, attributeValues); + } + + public void beginRow() + { + beginElement("tr"); + } + + public void beginRow(CssClass cssClass) + { + beginElement("tr", "class", cssClass.getName(), cssClass.getAttributeNames(), cssClass.getAttributeValues()); + } + + public void beginRow(String attribute, String value) + { + beginElement("tr", attribute, value); + } + + public void beginCell() + { + beginElement("td"); + } + + public void beginCell(String attribute, String value) + { + beginElement("td", attribute, value); + } + + public void beginCell(CssClass cssClass) + { + beginElement("td", "class", cssClass.getName(), cssClass.getAttributeNames(), cssClass.getAttributeValues()); + } + + public void endCell() + { + endElement("td"); + } + + public void cell(CssClass cssClass, String contents) + { + beginCell(cssClass); + print(contents); + endCell(); + } + + public void endRow() + { + endElement("tr"); + } + + public void rowDiv(CssClass cssClass, String contents) + { + beginRow(cssClass); + beginCell("colspan", "2"); + beginDiv(cssClass); + print(contents); + endDiv(cssClass); + endCell(); + endRow(); + } + + public void endTable() + { + endElement("table"); + } + + public void beginAnchor(String href) + { + beginElement("a", "href", href); + } + + public void beginAnchor(String href, String title) + { + beginElement("a", + new String[] { "href", "title" }, + new String[] { href, title }); + } + + public void beginAnchor(String href, String title, String target) + { + beginElement("a", + new String[] { "href", "title", "target" }, + new String[] { href, title, target }); + } + + public void endAnchor() + { + endElement("a"); + } + + public void anchor(String href, String label) + { + beginAnchor(href); + print(label); + endAnchor(); + } + + public void anchorName(String name) + { + atomicElement("a", new String[] { "name", "id" }, new String[] { name, name }); + } + + public String getPathToRoot() + { + return pathToRoot; + } + + public void beginBody(CssClass cssClass) + { + beginBody(cssClass, true); + } + + public void beginBody(CssClass cssClass, boolean setTitle) + { + if (setTitle) { + beginElement("body", + new String[] { "class", "onload" }, + new String[] { cssClass.getName(), "if(parent.contentPageLoaded)parent.contentPageLoaded(document.title)" } + ); + } + else { + beginElement("body", + new String[] { "class", "onload" }, + new String[] { cssClass.getName(), "if(parent.contentPageLoaded)parent.contentPageLoaded()" } + ); + } + } + + public void endBody() + { + endElement("body"); + } + + public void insert(Reader in) + throws IOException + { + IOToolkit.copyStream(in, out); + } + + public String createHrefString(String url, String content) + { + return createHrefString(url, content, null); + } + + public String createHrefString(String url, String content, String title) + { + StringBuffer result = new StringBuffer(); + result.append("<a href=\""); + result.append(url); + result.append("\""); + if (null != title) { + result.append(" title=\""); + result.append(title); + result.append("\""); + } + result.append(">"); + result.append(content); + result.append("</a>"); + return result.toString(); + } + + public File getFile() + { + return this.file; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlTagletContext.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlTagletContext.java new file mode 100644 index 000000000..7b8961361 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/htmldoclet/HtmlTagletContext.java @@ -0,0 +1,65 @@ +/* gnu.classpath.tools.doclets.htmldoclet.HtmlTagletContext + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.htmldoclet; + +import com.sun.javadoc.Doc; +import gnu.classpath.tools.taglets.TagletContext; + +public class HtmlTagletContext + extends TagletContext +{ + private HtmlPage output; + private boolean isOnSerializedPage; + + public HtmlTagletContext(Doc doc, HtmlPage output, boolean isOnSerializedPage) + { + super(doc); + this.output = output; + this.isOnSerializedPage = isOnSerializedPage; + } + + public HtmlPage getOutput() + { + return output; + } + + public boolean isOnSerializedPage() + { + return isOnSerializedPage; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/Driver.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/Driver.java new file mode 100644 index 000000000..29a9e0906 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/Driver.java @@ -0,0 +1,2451 @@ +/* gnu.classpath.tools.doclets.xmldoclet.Driver + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet; + +import com.sun.javadoc.*; +import java.io.*; + +import com.sun.tools.doclets.Taglet; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import java.text.DateFormat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.TreeSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.HashMap; +import java.util.Properties; +import java.util.Set; +import java.util.SortedSet; +import java.util.StringTokenizer; +import java.util.TreeMap; + +import gnu.classpath.tools.gjdoc.TemporaryStore; +import gnu.classpath.tools.gjdoc.GjdocPackageDoc; + +import gnu.classpath.tools.doclets.PackageGroup; +import gnu.classpath.tools.doclets.PackageMatcher; +import gnu.classpath.tools.doclets.InvalidPackageWildcardException; + +import gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTranslet; +import gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTransletOptions; + +import gnu.classpath.tools.taglets.AuthorTaglet; +import gnu.classpath.tools.taglets.VersionTaglet; +import gnu.classpath.tools.taglets.SinceTaglet; +import gnu.classpath.tools.taglets.DeprecatedTaglet; +import gnu.classpath.tools.taglets.GenericTaglet; +import gnu.classpath.tools.doclets.StandardTaglet; + +import gnu.classpath.tools.java2xhtml.Java2xhtml; + +import gnu.classpath.tools.IOToolkit; +import gnu.classpath.tools.FileSystemClassLoader; + +/** + * A Doclet which retrieves all information presented by the Doclet + * API, dumping it to stdout in XML format. + * + * @author Julian Scheid + */ +public class Driver { + + public static final String XMLDOCLET_VERSION = "0.6.1"; + + /** + * Used for redirecting error messages to <code>/dev/null</code>. + */ + private static class NullErrorReporter implements DocErrorReporter { + public void printError(String ignore) {} + public void printWarning(String ignore) {} + public void printNotice(String ignore) {} + } + + /* + * Taglet context constants. + */ + private static final int CONTEXT_CONSTRUCTOR = 1; + private static final int CONTEXT_FIELD = 2; + private static final int CONTEXT_METHOD = 3; + private static final int CONTEXT_OVERVIEW = 4; + private static final int CONTEXT_PACKAGE = 5; + private static final int CONTEXT_TYPE = 6; + + /** + * All XML output will go to this stream. + */ + private PrintWriter out; + + /** + * How many spaces to indent each XML node level, + * i.e. Tab size for output. + */ + private static int indentStep = 1; + + /** + * Won't output superfluous spaces if set to true. + * If set to false, output will be more legible. + */ + private boolean compress = false; + + /** + * Won't output warning messages while fixing + * HTML code if set to true. + */ + private boolean noHTMLWarn = false; + + /** + * Won't output warning messages when encountering tags + * that look like an email address if set to true. + */ + private boolean noEmailWarn = false; + + /** + * Will fix HTML if necessary so that each comment + * contains valid XML code if set to true. If set + * to false, HTML code will not be modified and + * instead encapsulated in a CDATA section. + */ + private boolean fixHTML = true; + + /** + * User-specified name of the directory where the final version of + * the generated files will be written to. + * + * If no XSLT sheet is given, the XML output will go directly into + * this directory. Otherwise, XML output will go to a temporary + * directory and XSLT output will go to this directory. + */ + private File targetDirectory = null; + + /** + * Directory where XML output will be written to. If no XSLT + * sheet was given, this is the target directory specified + * by the user. Otherwise, this is a temporary directory. + */ + private File xmlTargetDirectory; + + /** + * Contains a number of TargetContexts which describe which XSLT + * sheet to apply to the output of this doclet, to what directory + * the XSLT output is written, and which postprocess driver to use + * to process XSLT output. + */ + private List targets = new ArrayList(); + + /** + * XML text to include at the end of every generated page. Read + * from the file specified on the command line using -bottomnote. + * If present, this will be written to the main output file + * (index.xml) in node /gjdoc:rootDoc/gjdoc:bottomnote. + */ + private String bottomNote; + + /** + * Brief description of the package set. Can be specified on the + * command line using -title. This will be written to the main + * output file (index.xml) in node + * /gjdoc:rootDoc/gjdoc:title. The HTML generating XSLT sheet + * uses this for example in window titles. + */ + private String title; + + /** + * Path to the directory where temporary files should be stored. + * Defaults to system tempdir, but can be overridden by user + * with -workpath. + */ + private String workingPath = System.getProperty("java.io.tmpdir"); + + /** + * Temporary directory created by this doclet where all + * temporary files will be stored in. If no temporary + * files are needed (i.e. no XSLT postprocessing stage + * specified by user), this is <code>null</code>. + */ + private File workingDirectory; + + /** + * Whether to deep-copy the doc-files subdirectory. + */ + private boolean docFilesSubdirsEnabled = false; + + /** + * Which direct subdirectories of the doc-files directories to exclude. + * Set of String. + */ + private Set excludeDocFilesSubDirs = new HashSet(); + + /** + * Stores the Doclet API RootDoc we are operating on. + */ + private RootDoc rootDoc; + + /** + * XML namespace prefix used for all tags, except for HTML + * tags copied from Javadoc comments. Excluding colon. + */ + public static final String tagPrefix = "gjdoc"; + + /** + * Classpath for loading Taglet classes. + */ + private String tagletPath = null; + + /** + * The current class that is being processed. + * Set in outputClassDoc(). + */ + private ClassDoc currentClass; + + /** + * The current member that is being processed. + * Set in outputMemberDoc(). + */ + private MemberDoc currentMember; + + /** + * The current constructor/method that is being processed. + * Set in outputExecutableMemberDoc(). + */ + private ExecutableMemberDoc currentExecMember; + + /** + * Mapping from tag type to Taglet for user Taglets specified on + * the command line. + */ + private Map tagletMap = new LinkedHashMap(); + + /** + * Keeps track of the tags mentioned by the user during option + * processiong so that an error can be emitted if a tag is + * mentioned more than once. + */ + private List mentionedTags = new LinkedList(); + + /** + * Stores options to be passed to the DocTranslet. + */ + private DocTransletOptions docTransletOptions = new DocTransletOptions(); + + /** + * Stores the package groups specified in the user + * options. Contains objects of type PackageGroup. + */ + private List packageGroups = new LinkedList(); + + private HtmlRepairer htmlRepairer; + + public static boolean start(TemporaryStore _rootDocWrapper) { + return new Driver().instanceStart((RootDoc)_rootDocWrapper.getAndClear()); + } + + /** + * Official Doclet entry point. + */ + public static boolean start(RootDoc _rootDoc) { + + // Create a new XmlDoclet instance and delegate control. + TemporaryStore tstore = new TemporaryStore(_rootDoc); + _rootDoc = null; + return new Driver().instanceStart((RootDoc)tstore.getAndClear()); + } + + /** + * Output an XML tag describing a com.sun.javadoc.Type object. + * Assumes that the tag does not have subtags. + * + * @param level Level of indentation. Will be multiplied by + * <code>indentStep</code> to yield actual amount + * of whitespace inserted at start of line. + * @param tag Identifier for the XML tag being output. + * @param type The Javadoc Type to be output. + */ + protected void outputType(int level, String tag, Type type) { + outputType(level, tag, type, true); + } + + protected void outputType(int level, String tag, Type type, boolean atomic) { + + boolean isIncluded = false; + ClassDoc typeAsClassDoc = type.asClassDoc(); + String packageName = null; + if (null != typeAsClassDoc) { + isIncluded = typeAsClassDoc.isIncluded(); + packageName = typeAsClassDoc.containingPackage().name(); + } + println(level, "<"+tagPrefix+":"+tag + " typename=\""+type.typeName()+"\""+ + " qualifiedtypename=\""+type.qualifiedTypeName()+"\"" + +(type.dimension().length()==0?"":" dimension=\""+type.dimension()+"\"") + +(isIncluded?" isIncluded=\"true\"" : "") + +((null != packageName)?" package=\"" + packageName + "\"" : "") + +(atomic?"/":"")+">"); + } + + protected void outputExecutableMemberDocBody(int level, ExecutableMemberDoc memberDoc) { + + currentExecMember = memberDoc; + + outputMemberDocBody(level, memberDoc); + + Parameter[] parameters = memberDoc.parameters(); + for (int i=0, ilim=parameters.length; i<ilim; ++i) { + Parameter parameter = parameters[i]; + outputType(level, "parameter name=\""+parameter.name()+"\"", parameter.type()); + } + + ClassDoc[] exceptions = memberDoc.thrownExceptions(); + for (int i=0, ilim=exceptions.length; i<ilim; ++i) { + ClassDoc exception = exceptions[i]; + outputType(level, "thrownException", exception); + } + + printAtomTag(level, "signature full=\""+memberDoc.signature()+"\" flat=\""+memberDoc.flatSignature()+"\""); + + if (memberDoc.isNative()) { + printAtomTag(level, "isNative"); + } + + if (memberDoc.isSynchronized()) { + printAtomTag(level, "isSynchronized"); + } + } + + protected void outputMethodDoc(int level, MethodDoc methodDoc) { + println(); + printOpenTag(level, "methoddoc name=\""+methodDoc.name()+"\""); + outputExecutableMemberDocBody(level+1, methodDoc); + outputType(level+1, "returns", methodDoc.returnType()); + printCloseTag(level, "methoddoc"); + } + + protected void outputMemberDocBody(int level, MemberDoc memberDoc) { + currentMember = memberDoc; + outputProgramElementDocBody(level, memberDoc); + } + + protected void outputFieldDocBody(int level, FieldDoc fieldDoc) { + outputType(level, "type", fieldDoc.type()); + if (fieldDoc.isTransient()) { + printAtomTag(level, "isTransient"); + } + if (fieldDoc.isVolatile()) { + printAtomTag(level, "isVolatile"); + } + } + + private void outputFieldDoc(int level, FieldDoc fieldDoc) { + println(); + printOpenTag(level, "fielddoc name=\""+fieldDoc.name()+"\""); + outputMemberDocBody(level+1, fieldDoc); + outputFieldDocBody(level+1, fieldDoc); + printCloseTag(level, "fielddoc"); + } + + protected void outputConstructorDoc(int level, ConstructorDoc constructorDoc) { + println(); + printOpenTag(level, "constructordoc name=\""+constructorDoc.name()+"\""); + outputExecutableMemberDocBody(level+1, constructorDoc); + printCloseTag(level, "constructordoc"); + } + + protected void outputSuperInterfacesRec(int level, ClassDoc classDoc) { + if (null!=classDoc) { + ClassDoc[] interfaces = classDoc.interfaces(); + if (null != interfaces) { + for (int i=0, ilim=interfaces.length; i<ilim; ++i) { + outputType(level, "superimplements", interfaces[i]); + } + } + outputSuperInterfacesRec(level, classDoc.superclass()); + } + } + + protected void outputClassDocSummary(ClassDoc classDoc) { + println(); + printOpenTag(1, "classdoc name=\""+classDoc.name()+"\" qualifiedtypename=\""+classDoc.qualifiedName()+"\" isIncluded=\"true\""); + if (null!=classDoc.superclass()) { + outputType(2, "superclass", classDoc.superclass()); + } + + ClassDoc[] interfaces = classDoc.interfaces(); + for (int i=0, ilim=interfaces.length; i<ilim; ++i) { + outputType(2, "implements", interfaces[i]); + } + outputSuperInterfacesRec(2, classDoc.superclass()); + + printAtomTag(2, "containingPackage name=\""+classDoc.containingPackage().name()+"\""); + if (classDoc.isError()) { + printAtomTag(2, "isError"); + } + if (classDoc.isException()) { + printAtomTag(2, "isException"); + } + if (classDoc.isInterface()) { + printAtomTag(2, "isInterface"); + } + if (classDoc.isOrdinaryClass()) { + printAtomTag(2, "isOrdinaryClass"); + } + + printCloseTag(1, "classdoc"); + } + + protected void outputPackageDoc(PackageDoc packageDoc) { + println(); + printOpenTag(1, "packagedoc name=\""+packageDoc.name()+"\""); + if (packageDoc.firstSentenceTags().length > 0) { + printOpenTag(2, "firstSentenceTags", false); + outputTags(3, packageDoc.firstSentenceTags(), true, CONTEXT_PACKAGE); + printCloseTag(0, "firstSentenceTags"); + printOpenTag(2, "inlineTags", false); + outputTags(3, packageDoc.inlineTags(), true, CONTEXT_PACKAGE); + printCloseTag(0, "inlineTags"); + } + + if (packageDoc.tags().length > 0) { + printOpenTag(2, "tags"); + outputTags(3, packageDoc.tags(), true, CONTEXT_PACKAGE); + printCloseTag(2, "tags"); + } + + if (packageDoc.seeTags().length > 0) { + printOpenTag(2, "seeTags"); + outputTags(3, packageDoc.seeTags(), true, CONTEXT_PACKAGE); + printCloseTag(2, "seeTags"); + } + + ClassDoc[] allClasses = (ClassDoc[]) packageDoc.allClasses().clone(); + Arrays.sort(allClasses); + + if (false) { + for (int i = 0, ilim = allClasses.length; i < ilim; ++ i) { + printAtomTag(2, "containsClass qualifiedtypename=\""+allClasses[i].qualifiedTypeName()+"\""); + } + } + + printCloseTag(1, "packagedoc"); + } + + protected void outputClassDoc(ClassDoc classDoc) throws IOException { + + currentClass = classDoc; + + println(); + printOpenTag(1, "classdoc xmlns=\"http://www.w3.org/TR/REC-html40\" xmlns:"+tagPrefix+"=\"http://www.gnu.org/software/cp-tools/gjdocxml\" name=\""+classDoc.name()+"\" qualifiedtypename=\""+classDoc.qualifiedName()+"\""); + + ClassDoc[] interfaces = classDoc.interfaces(); + for (int i=0, ilim=interfaces.length; i<ilim; ++i) { + outputType(2, "implements", interfaces[i]); + } + outputSuperInterfacesRec(2, classDoc.superclass()); + + outputProgramElementDocBody(2, classDoc); + if (classDoc.isAbstract()) + printAtomTag(2, "isAbstract"); + if (classDoc.isSerializable()) + printAtomTag(2, "isSerializable"); + if (classDoc.isExternalizable()) + printAtomTag(2, "isExternalizable"); + if (classDoc.definesSerializableFields()) { + printAtomTag(2, "definesSerializableFields"); + } + + ConstructorDoc[] constructors = classDoc.constructors(); + for (int i=0, ilim=constructors.length; i<ilim; ++i) { + outputConstructorDoc(2, constructors[i]); + } + + MethodDoc[] methods = classDoc.methods(); + for (int i=0, ilim=methods.length; i<ilim; ++i) { + outputMethodDoc(2, methods[i]); + } + + FieldDoc[] fields = classDoc.fields(); + for (int i=0, ilim=fields.length; i<ilim; ++i) { + outputFieldDoc(2, fields[i]); + } + + if (classDoc.serializableFields().length > 0) { + printOpenTag(2, "serializableFields"); + + FieldDoc[] sfields = classDoc.serializableFields(); + for (int i=0, ilim=sfields.length; i<ilim; ++i) { + outputFieldDoc(2, sfields[i]); + } + printCloseTag(2, "serializableFields"); + } + + Java2xhtml java2xhtml = new Java2xhtml(); + Properties properties = new Properties(); + properties.setProperty("isCodeSnippet", "true"); + properties.setProperty("hasLineNumbers", "true"); + java2xhtml.setProperties(properties); + + if (null == classDoc.containingClass() && docTransletOptions.linksource) { + printOpenTag(2, "source"); + StringWriter sourceBuffer = new StringWriter(); + File sourceFile = new File(((GjdocPackageDoc)classDoc.containingPackage()).packageDirectory(), + classDoc.name() + ".java"); + FileReader sourceReader = new FileReader(sourceFile); + IOToolkit.copyStream(sourceReader, sourceBuffer); + print(java2xhtml.makeHTML(sourceBuffer.getBuffer(), sourceFile.getName())); + printCloseTag(2, "source"); + } + + ClassDoc superclassDoc = classDoc.superclass(); + while (superclassDoc != null) { + outputType(2, "superclass", superclassDoc, false); + + // FIXME: remove the following after adjusting the XSLT sheets: + printAtomTag(3, "containingPackage name=\"" + superclassDoc.containingPackage().name() + "\""); + + MethodDoc[] superMethods = superclassDoc.methods(); + if (null != superMethods) { + for (int i=0, ilim=superMethods.length; i<ilim; ++i) { + printAtomTag(3, "methoddoc name=\"" + superMethods[i].name() + "\" signature=\"" + superMethods[i].signature() + "\""); + } + } + + FieldDoc[] superFields = superclassDoc.fields(); + if (null != superFields) { + for (int i=0, ilim=superFields.length; i<ilim; ++i) { + printAtomTag(3, "fielddoc name=\"" + superFields[i].name() + "\""); + } + } + printCloseTag(2, "superclass"); + + superclassDoc = superclassDoc.superclass(); + } + + outputUsage(classDoc, 2); + + printCloseTag(1, "classdoc"); + + currentClass = null; + currentMember = null; + currentExecMember = null; + } + + protected int outputHeritageOpen(int level, ClassDoc classDoc) { + + ClassDoc superClassDoc = classDoc.superclass(); + if (null != superClassDoc) { + level = outputHeritageOpen(level, superClassDoc); + ++ level; + } + outputType(level, "heritage", classDoc, false); + return level; + } + + protected void outputHeritageClose(int level, ClassDoc classDoc) { + + ClassDoc superClassDoc = classDoc.superclass(); + if (null != superClassDoc) { + outputHeritageClose(level + 1, superClassDoc); + } + printCloseTag(level, "heritage"); + } + + protected void outputDocBody(int level, Doc doc) { + + int context = CONTEXT_TYPE; + + if (doc.isClass()) { + printAtomTag(level, "isClass"); + + ClassDoc classDoc = (ClassDoc)doc; + ClassDoc[] classes = rootDoc.classes(); + for (int i=0, ilim=classes.length; i<ilim; ++i) { + if (classes[i].superclass() == classDoc) { + outputType(level, "extended-by", classes[i]); + } + } + + outputHeritageOpen(level, classDoc); + outputHeritageClose(level, classDoc); + } + if (doc.isConstructor()) { + printAtomTag(level, "isConstructor"); + context = CONTEXT_CONSTRUCTOR; + } + if (doc.isError()) { + printAtomTag(level, "isError"); + } + if (doc.isException()) { + printAtomTag(level, "isException"); + } + if (doc.isField()) { + printAtomTag(level, "isField"); + context = CONTEXT_FIELD; + } + if (doc.isIncluded()) { + printAtomTag(level, "isIncluded"); + } + if (doc.isInterface()) { + printAtomTag(level, "isInterface"); + + ClassDoc classDoc = (ClassDoc)doc; + ClassDoc[] classes = rootDoc.classes(); + for (int i=0, ilim=classes.length; i<ilim; ++i) { + ClassDoc[] implementedInterfaces = classes[i].interfaces(); + for (int j=0; j<implementedInterfaces.length; ++j) { + if (implementedInterfaces[j] == classDoc) { + if (classDoc.isInterface()) { + outputType(level, "subinterface", classes[i]); + } + else { + outputType(level, "implemented-by", classes[i]); + } + break; + } + } + } + } + if (doc.isMethod()) { + printAtomTag(level, "isMethod"); + context = CONTEXT_METHOD; + } + if (doc.isOrdinaryClass()) { + printAtomTag(level, "isOrdinaryClass"); + } + + if (doc.inlineTags().length > 0) { + printOpenTag(level, "inlineTags", false); + outputTags(level+1, doc.inlineTags(), true, context); + printCloseTag(0, "inlineTags"); + } + + if (doc.firstSentenceTags().length > 0) { + printOpenTag(level, "firstSentenceTags", false); + outputTags(level+1, doc.firstSentenceTags(), true, context); + printCloseTag(0, "firstSentenceTags"); + } + + if (doc.tags().length > 0) { + printOpenTag(level, "tags"); + outputTaglets(level+1, doc.tags(), true, context); + printCloseTag(level, "tags"); + } + + if (doc.seeTags().length > 0) { + printOpenTag(level, "seeTags"); + outputTags(level+1, doc.seeTags(), true, context); + printCloseTag(level, "seeTags"); + } + + SourcePosition position = doc.position(); + if (null != position) { + printAtomTag(level, "position file=\"" + position.file().getAbsolutePath() + "\" line=\"" + position.line() + "\" column=\"" + position.column() + "\""); + } + } + + protected void outputProgramElementDocBody(int level, ProgramElementDoc programElementDoc) { + outputDocBody(level, programElementDoc); + printAtomTag(level, "containingPackage name=\""+programElementDoc.containingPackage().name()+"\""); + if (null!=programElementDoc.containingClass()) { + outputType(level, "containingClass", programElementDoc.containingClass()); + } + String access; + if (programElementDoc.isPublic()) + access="public"; + else if (programElementDoc.isProtected()) + access="protected"; + else if (programElementDoc.isPrivate()) + access="private"; + else if (programElementDoc.isPackagePrivate()) + access="package"; + else + throw new RuntimeException("Huh? "+programElementDoc+" is neither public, protected, private nor package protected."); + printAtomTag(level, "access scope=\""+access+"\""); + if (programElementDoc.isFinal()) + printAtomTag(level, "isFinal"); + if (programElementDoc.isStatic()) + printAtomTag(level, "isStatic"); + } + + protected void outputTags(int level, Tag[] tags, boolean descend, int context) { + + for (int i=0; i<tags.length; ++i) { + outputTag(tags[i], level, descend, context, i == tags.length-1); + } + } + + protected void outputTag(Tag tag, int level, boolean descend, int context, boolean lastTag) { + + if (!"Text".equals(tag.name())) { + printOpenTag(0 /* don't introduce additional whitespace */, + "tag kind=\""+tag.kind()+"\" name=\""+tag.name()+"\"", false); + } + if (tag instanceof ThrowsTag) { + ThrowsTag throwsTag = (ThrowsTag)tag; + if (null!=throwsTag.exception()) { + outputType(level+1, "exception", throwsTag.exception()); + } + else { + StringBuffer sb = new StringBuffer("Exception "); + sb.append(throwsTag.exceptionName()); + sb.append(" not found in "); + if (currentExecMember instanceof MethodDoc) { + MethodDoc m = (MethodDoc)currentExecMember; + sb.append(m.returnType().typeName()); + sb.append(m.returnType().dimension()); + sb.append(' '); + } + sb.append(currentClass.qualifiedName()); + sb.append('.'); + sb.append(currentExecMember.name()); + sb.append('('); + Parameter[] params = currentExecMember.parameters(); + for (int j=0; j < params.length; j++) { + sb.append(params[j].type().typeName()); + sb.append(params[j].type().dimension()); + sb.append(' '); + sb.append(params[j].name()); + if (j != params.length-1) + sb.append(", "); + } + sb.append(')'); + printWarning(sb.toString()); + + printAtomTag(level+1, "exception typename=\""+throwsTag.exceptionName()+"\""); + } + } + else if (tag instanceof ParamTag) { + ParamTag paramTag = (ParamTag)tag; + printAtomTag(level+1, "parameter name=\""+paramTag.parameterName()+"\""); + } + + if (null != tag.text()) { + //printOpenTag(level+1, "text", false); + if (fixHTML) { + print(htmlRepairer.getWellformedHTML(tag.text())); + } + else { + print("<![CDATA["+cdata(tag.text())+"]]>"); + } + //printCloseTag(0 /* don't introduce additional whitespace */, "text"); + } + else { + printWarning("Tag got null text: "+tag); + } + + if ((descend && ("@throws".equals(tag.name()) || "@param".equals(tag.name()))) || "@deprecated".equals(tag.name())) { + if (tag.firstSentenceTags().length>0) { + printOpenTag(level+1, "firstSentenceTags", false); + outputTags(level+2, tag.firstSentenceTags(), false, context); + printCloseTag(0, "firstSentenceTags"); + } + + if (tag.inlineTags().length>0) { + printOpenTag(level+1, "inlineTags", false); + outputTags(level+2, tag.firstSentenceTags(), false, context); + printCloseTag(0, "inlineTags"); + } + } + + if (fixHTML && lastTag) { + String terminateText = htmlRepairer.terminateText(); + if (null != terminateText && terminateText.length() > 0) { + print(terminateText); + } + } + + if (!"Text".equals(tag.name())) { + + Taglet inlineTaglet = (Taglet)tagletMap.get(tag.name().substring(1)); + if (null != inlineTaglet && inlineTaglet.isInlineTag()) { + printOpenTag(0, "inlineTagletText", false); + print(inlineTaglet.toString(tag)); + printCloseTag(0, "inlineTagletText"); + } + + printCloseTag(0, "tag", false); + } + } + + void outputTaglets(int level, Tag[] tags, boolean descend, int context) + { + for (Iterator it = tagletMap.keySet().iterator(); it.hasNext(); ) { + String tagName = (String)it.next(); + Object o = tagletMap.get(tagName); + Taglet taglet = (Taglet)o; + + if (!taglet.isInlineTag() + && ((context != CONTEXT_CONSTRUCTOR || taglet.inConstructor()) + || (context != CONTEXT_FIELD || taglet.inField()) + || (context != CONTEXT_METHOD || taglet.inMethod()) + || (context != CONTEXT_OVERVIEW || taglet.inOverview()) + || (context != CONTEXT_PACKAGE || taglet.inPackage()) + || (context != CONTEXT_TYPE || taglet.inType()))) { + + List tagsOfThisType = new ArrayList(); + for (int i=0, ilim=tags.length; i<ilim; ++i) { + if (tags[i].name().substring(1).equals(tagName)) { + tagsOfThisType.add(tags[i]); + } + } + + if (!tagsOfThisType.isEmpty()) { + Tag[] tagletTags = (Tag[])tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]); + if (taglet instanceof StandardTaglet) { + Iterator tagIterator = tagsOfThisType.iterator(); + while (tagIterator.hasNext()) { + Tag tag = (Tag)tagIterator.next(); + outputTag(tag, level, descend, context, !tagIterator.hasNext()); + } + } + else { + String tagletString = taglet.toString(tagletTags); + if (null != tagletString) { + printOpenTag(0, "tag name=\"" + tagName + "\" taglet-generated=\"true\""); + if (fixHTML) { + print(htmlRepairer.getWellformedHTML(tagletString)); + print(htmlRepairer.terminateText()); + } + else { + print("<![CDATA["+cdata(tagletString)+"]]>"); + } + printCloseTag(0, "tag", false); + } + } + } + } + } + } + + /** + * Inofficial entry point. We got an instance here. + */ + protected boolean instanceStart(RootDoc _rootDoc) { + + this.rootDoc = _rootDoc; + _rootDoc = null; + + boolean xmlOnly = true; + + // Set the default Taglet order + + registerTaglet(new VersionTaglet()); + registerTaglet(new AuthorTaglet()); + //registerTaglet(new SinceTaglet()); + registerTaglet(new StandardTaglet("deprecated")); + registerTaglet(new StandardTaglet("see")); + registerTaglet(new StandardTaglet("param")); + + // Set the built-in Taglet filter + + AuthorTaglet.setTagletEnabled(false); + VersionTaglet.setTagletEnabled(false); + SinceTaglet.setTagletEnabled(true); + DeprecatedTaglet.setTagletEnabled(true); + + try { + { + + // Process command line options passed through to this doclet + + TargetContext targetContext = null; + + TargetContext htmlTargetContext + = new TargetContext(DocTranslet.fromClasspath("/doctranslets/html/gjdoc.xsl"), + targetDirectory); + + for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) { + + String[] option = rootDoc.options()[i]; + String optionTag = option[0]; + + if ("-d".equals(optionTag)) { + if (null == targetDirectory) { + targetDirectory = new File(option[1]); + } + if (null != targetContext) { + targetContext.setTargetDirectory(targetDirectory); + } + } + + else if ("-nofixhtml".equals(optionTag)) { + fixHTML = false; + printError("-nofixhtml currently not supported."); + return false; + } + else if ("-compress".equals(optionTag)) { + compress = true; + } + else if ("-nohtmlwarn".equals(optionTag)) { + noHTMLWarn = true; + } + else if ("-noemailwarn".equals(optionTag)) { + noEmailWarn = true; + } + else if ("-indentstep".equals(optionTag)) { + indentStep = Integer.parseInt(option[1]); + } + else if ("-doctranslet".equals(optionTag)) { + targets.add(targetContext = new TargetContext(DocTranslet.fromJarFile(new File(option[1])), + targetDirectory)); + } + else if ("-genhtml".equals(optionTag)) { + htmlTargetContext.setTargetDirectory(targetDirectory); + targets.add(targetContext = htmlTargetContext); + xmlOnly = false; + } + else if ("-geninfo".equals(optionTag)) { + targetContext + = new TargetContext(DocTranslet.fromClasspath("/doctranslets/info/gengj.xsl"), + targetDirectory); + targets.add(targetContext); + if (!fixHTML) { + printNotice("NOTE: -geninfo implies -fixhtml."); + fixHTML = true; + } + xmlOnly = false; + } + else if ("-gendocbook".equals(optionTag)) { + targetContext = new TargetContext(DocTranslet.fromClasspath("/doctranslets/docbook/gengj.xsl"), + targetDirectory); + targets.add(targetContext); + if (!fixHTML) { + printNotice("NOTE: -gendocbook implies -fixhtml."); + fixHTML = true; + } + } + else if ("-genpdf".equals(optionTag)) { + targetContext + = new TargetContext(DocTranslet.fromClasspath("/doctranslets/docbook/gengj.xsl"), + targetDirectory); + /** "gnu.classpath.tools.doclets.xmldoclet.DocBookPostprocessor") **/ + targets.add(targetContext); + if (!fixHTML) { + printNotice("NOTE: -genpdf implies -fixhtml."); + fixHTML = true; + } + } + else if ("-xmlonly".equals(optionTag)) { + xmlOnly = true; + } + else if ("-bottomnote".equals(optionTag)) { + + FileReader reader = new FileReader(option[1]); + StringWriter writer = new StringWriter(); + char[] buf = new char[256]; + int nread; + while ((nread = reader.read(buf)) >= 0) { + writer.write(buf, 0, nread); + } + writer.flush(); + bottomNote = writer.toString(); + writer.close(); + reader.close(); + } + else if ("-title".equals(optionTag)) { + + title = option[1]; + } + else if ("-workpath".equals(optionTag)) { + + workingPath = option[1]; + } + else if ("-tagletpath".equals(optionTag)) { + + if (null == tagletPath) { + tagletPath = option[1]; + } + else { + tagletPath = tagletPath + File.pathSeparator + option[1]; + } + } + else if ("-taglet".equals(optionTag)) { + + boolean tagletLoaded = false; + + String useTagletPath = this.tagletPath; + if (null == useTagletPath) { + useTagletPath = System.getProperty("java.class.path"); + } + + try { + Class tagletClass; + try { + tagletClass + = new FileSystemClassLoader(useTagletPath).loadClass(option[1]); + } + catch (ClassNotFoundException e) { + // If not found on specified tagletpath, try default classloader + tagletClass + = Class.forName(option[1]); + } + Method registerTagletMethod + = tagletClass.getDeclaredMethod("register", new Class[] { java.util.Map.class }); + + if (!registerTagletMethod.getReturnType().equals(Void.TYPE)) { + printError("Taglet class '" + option[1] + "' found, but register method doesn't return void."); + } + else if (registerTagletMethod.getExceptionTypes().length > 0) { + printError("Taglet class '" + option[1] + "' found, but register method contains throws clause."); + } + else if ((registerTagletMethod.getModifiers() & (Modifier.STATIC | Modifier.PUBLIC | Modifier.ABSTRACT)) != (Modifier.STATIC | Modifier.PUBLIC)) { + printError("Taglet class '" + option[1] + "' found, but register method isn't public static, or is abstract.."); + } + else { + Map tempMap = new HashMap(); + registerTagletMethod.invoke(null, new Object[] { tempMap }); + tagletLoaded = true; + String name = (String)tempMap.keySet().iterator().next(); + Taglet taglet = (Taglet)tempMap.get(name); + tagletMap.put(name, taglet); + mentionedTags.add(taglet); + } + } + catch (NoSuchMethodException e) { + printError("Taglet class '" + option[1] + "' found, but doesn't contain the register method."); + } + catch (SecurityException e) { + printError("Taglet class '" + option[1] + "' cannot be loaded: " + e.getMessage()); + } + catch (InvocationTargetException e) { + printError("Taglet class '" + option[1] + "' found, but register method throws exception: " + e.toString()); + } + catch (IllegalAccessException e) { + printError("Taglet class '" + option[1] + "' found, but there was a problem when accessing the register method: " + e.toString()); + } + catch (IllegalArgumentException e) { + printError("Taglet class '" + option[1] + "' found, but there was a problem when accessing the register method: " + e.toString()); + } + catch (ClassNotFoundException e) { + printError("Taglet class '" + option[1] + "' cannot be found."); + } + if (!tagletLoaded) { + return false; + } + } + else if ("-author".equals(optionTag)) { + AuthorTaglet.setTagletEnabled(true); + } + else if ("-version".equals(optionTag)) { + VersionTaglet.setTagletEnabled(true); + } + else if ("-nosince".equals(optionTag)) { + SinceTaglet.setTagletEnabled(false); + } + else if ("-nodeprecated".equals(optionTag)) { + DeprecatedTaglet.setTagletEnabled(false); + } + else if ("-authormail".equals(optionTag)) { + + if ("no-replace".equalsIgnoreCase(option[1])) { + AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NO_REPLACEMENT); + } + else if ("mailto-name".equalsIgnoreCase(option[1])) { + AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.MAILTO_NAME); + } + else if ("name-mailto-address".equalsIgnoreCase(option[1])) { + AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NAME_MAILTO_ADDRESS); + } + else if ("name-mangled-address".equalsIgnoreCase(option[1])) { + AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NAME_MANGLED_ADDRESS); + } + else { + printError("Invalid value for option '-authortag-email'. Allowed values are:" + + " no-replace, mailto-name, name-mailto-address, name-mangled-address."); + return false; + } + } + else if ("-mailmangledot".equals(optionTag)) { + AuthorTaglet.setDotReplacement(option[1]); + } + else if ("-mailmangleat".equals(optionTag)) { + AuthorTaglet.setAtReplacement(option[1]); + } + else if ("-docfilessubdirs".equals(optionTag)) { + docFilesSubdirsEnabled = true; + } + else if ("-excludedocfilessubdir".equals(optionTag)) { + StringTokenizer st = new StringTokenizer(option[1]); + while (st.hasMoreTokens()) { + excludeDocFilesSubDirs.add(st.nextToken()); + } + } + else if ("-nonavbar".equals(optionTag)) { + docTransletOptions.nonavbar = true; + } + else if ("-noindex".equals(optionTag)) { + docTransletOptions.noindex = true; + } + else if ("-notree".equals(optionTag)) { + docTransletOptions.notree = true; + } + else if ("-nocomment".equals(optionTag)) { + docTransletOptions.nocomment = true; + } + else if ("-nohelp".equals(optionTag)) { + docTransletOptions.nohelp = true; + } + else if ("-splitindex".equals(optionTag)) { + docTransletOptions.splitindex = true; + } + else if ("-linksource".equals(optionTag)) { + docTransletOptions.linksource = true; + } + else if ("-windowtitle".equals(optionTag)) { + docTransletOptions.windowtitle = option[1]; + } + else if ("-helpfile".equals(optionTag)) { + docTransletOptions.helpfile = new File(option[1]).toURL().toString(); + } + else if ("-stylesheetfile".equals(optionTag)) { + docTransletOptions.stylesheetfile = new File(option[1]).toURL().toString(); + } + else if ("-header".equals(optionTag)) { + docTransletOptions.header = option[1]; + } + else if ("-footer".equals(optionTag)) { + docTransletOptions.footer = option[1]; + } + else if ("-bottom".equals(optionTag)) { + docTransletOptions.bottom = option[1]; + } + else if ("-doctitle".equals(optionTag)) { + docTransletOptions.doctitle = option[1]; + } + else if ("-nodeprecatedlist".equals(optionTag)) { + docTransletOptions.nodeprecatedlist = true; + } + else if ("-uses".equals(optionTag)) { + docTransletOptions.uses = true; + } + else if ("-group".equals(optionTag)) { + if (!processGroupOption(option[1], option[2])) { + printError("Invalid package wildcard list in -group option \"" + option[1] + "\" " + option[2]); + return false; + } + } + else if ("-tag".equals(optionTag)) { + String tagSpec = option[1]; + boolean validTagSpec = false; + int ndx1 = tagSpec.indexOf(':'); + if (ndx1 < 0) { + Taglet taglet = (Taglet)tagletMap.get(tagSpec); + if (null == taglet) { + printError("There is no standard tag '" + tagSpec + "'."); + } + else { + if (mentionedTags.contains(taglet)) { + printError("Tag '" + tagSpec + "' has been added or moved before."); + } + else { + mentionedTags.add(taglet); + + // re-append taglet + tagletMap.remove(tagSpec); + tagletMap.put(tagSpec, taglet); + } + } + } + else { + int ndx2 = tagSpec.indexOf(':', ndx1 + 1); + if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) { + String tagName = tagSpec.substring(0, ndx1); + String tagHead = null; + if (tagSpec.charAt(ndx2 + 1) == '\"') { + if (tagSpec.charAt(tagSpec.length() - 1) == '\"') { + tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1); + validTagSpec = true; + } + } + else { + tagHead = tagSpec.substring(ndx2 + 1); + validTagSpec = true; + } + + boolean tagScopeOverview = false; + boolean tagScopePackages = false; + boolean tagScopeTypes = false; + boolean tagScopeConstructors = false; + boolean tagScopeMethods = false; + boolean tagScopeFields = false; + boolean tagDisabled = false; + + tag_option_loop: + for (int n=ndx1+1; n<ndx2; ++n) { + switch (tagSpec.charAt(n)) { + case 'X': + tagDisabled = true; + break; + case 'a': + tagScopeOverview = true; + tagScopePackages = true; + tagScopeTypes = true; + tagScopeConstructors = true; + tagScopeMethods = true; + tagScopeFields = true; + break; + case 'o': + tagScopeOverview = true; + break; + case 'p': + tagScopePackages = true; + break; + case 't': + tagScopeTypes = true; + break; + case 'c': + tagScopeConstructors = true; + break; + case 'm': + tagScopeMethods = true; + break; + case 'f': + tagScopeFields = true; + break; + default: + validTagSpec = false; + break tag_option_loop; + } + } + + if (validTagSpec) { + GenericTaglet taglet + = new GenericTaglet(tagName, + tagHead, + tagScopeOverview, + tagScopePackages, + tagScopeTypes, + tagScopeConstructors, + tagScopeMethods, + tagScopeFields); + taglet.setTagletEnabled(!tagDisabled); + taglet.register(tagletMap); + mentionedTags.add(taglet); + } + } + } + if (!validTagSpec) { + printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\"."); + } + } + } + + // Use current directory if target directory hasn't been set. + if (null == targetDirectory) { + targetDirectory = new File(System.getProperty("user.dir")); + } + if (null != targetContext) { + targetContext.setTargetDirectory(targetDirectory); + } + + // It is illegal to specify targets AND -xmlonly. + + if (xmlOnly && targets.size() > 0) { + + printError("You can only specify one of -xmlonly and a target format."); + return false; + } + + // If no target was specified and XML only was not + // requested, use HTML as default target. + + if (!xmlOnly && targets.size() == 0) { + targets.add(targetContext = htmlTargetContext); + } + + // Set the same target directory for all output. + + // FIXME: Allow separate target directories for different + // output formats. + + for (Iterator it = targets.iterator(); it.hasNext(); ) { + TargetContext t = (TargetContext)it.next(); + t.setTargetDirectory(targetDirectory); + } + + // Create temporary directory if necessary + + if (xmlOnly) { + + xmlTargetDirectory = targetDirectory; + } + else { + + File workingTopDirectory = new File(workingPath); + + workingDirectory = new File(workingTopDirectory, "gjdoc.tmp."+System.currentTimeMillis()); + + if (!workingDirectory.mkdir()) { + printError("Cannot create temporary directory at "+System.getProperty("java.io.tmpdir")); + return false; + } + + File xmlTempDirectory = new File(workingDirectory, "xmloutput"); + + if (!xmlTempDirectory.mkdir()) { + printError("Cannot create temporary directory for XML output at "+System.getProperty("java.io.tmpdir")); + return false; + } + + xmlTargetDirectory = xmlTempDirectory; + } + + // Create target directory if necessary + + if (!targetDirectory.exists()) { + printNotice("Creating destination directory: \"" + + targetDirectory + "\""); + if (!targetDirectory.mkdirs()) { + printError("Failed to create destination directory \"" + + targetDirectory + "\""); + return false; + } + } + + // Check for deprecation + + boolean hasDeprecatedClasses = false; + boolean hasDeprecatedInterfaces = false; + boolean hasDeprecatedExceptions = false; + boolean hasDeprecatedErrors = false; + boolean hasDeprecatedMethods = false; + boolean hasDeprecatedFields = false; + + { + ClassDoc[] classes = rootDoc.classes(); + for (int i = 0, ilim = classes.length; i < ilim; ++ i) { + ClassDoc c = classes[i]; + Tag[] deprecatedTags = c.tags("deprecated"); + if (null != deprecatedTags && 0 != deprecatedTags.length) { + if (c.isInterface()) { + hasDeprecatedInterfaces = true; + } + else if (c.isException()) { + hasDeprecatedExceptions = true; + } + else if (c.isError()) { + hasDeprecatedErrors = true; + } + else /*if (c.isOrdinaryClass())*/ { + hasDeprecatedClasses = true; + } + } + + MethodDoc[] methods = c.methods(); + for (int j = 0, jlim = methods.length; j < jlim; ++ j) { + MethodDoc m = methods[j]; + deprecatedTags = m.tags("deprecated"); + if (null != deprecatedTags && 0 != deprecatedTags.length) { + hasDeprecatedMethods = true; + } + } + + FieldDoc[] fields = c.fields(); + for (int j = 0, jlim = fields.length; j < jlim; ++ j) { + FieldDoc f = fields[j]; + deprecatedTags = f.tags("deprecated"); + if (null != deprecatedTags && 0 != deprecatedTags.length) { + hasDeprecatedFields = true; + } + } + } + } + + htmlRepairer = new HtmlRepairer(rootDoc, noHTMLWarn, noEmailWarn, + currentClass, currentMember, + false); + + collectUsage(); + + // Begin XML generation + + printNotice("Writing XML Index file..."); + + // Assign output stream + + setTargetFile("index.xml"); + + // Output XML document header + + println(0, "<?xml version=\"1.0\"?>"); + println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc.dtd\">"); + println(); + printOpenTag(0, "rootdoc xmlns=\"http://www.w3.org/TR/REC-html40\" xmlns:gjdoc=\"http://www.gnu.org/software/cp-tools/gjdocxml\""); + + println(); + println(1, "<!-- Tags from overview page, if available -->"); + + if (rootDoc.firstSentenceTags().length > 0) { + printOpenTag(2, "firstSentenceTags", false); + outputTags(3, rootDoc.firstSentenceTags(), true, CONTEXT_PACKAGE); + printCloseTag(0, "firstSentenceTags"); + } + + if (rootDoc.inlineTags().length > 0) { + printOpenTag(2, "inlineTags"); + outputTags(3, rootDoc.inlineTags(), true, CONTEXT_PACKAGE); + printCloseTag(2, "inlineTags"); + } + + if (null != bottomNote) { + printOpenTag(1, "bottomnote"); + print(bottomNote); + printCloseTag(1, "bottomnote"); + } + + if (null != title) { + printOpenTag(1, "title"); + println(2, title); + printCloseTag(1, "title"); + } + + printOpenTag(1, "created"); + println(2, DateFormat.getDateInstance(DateFormat.LONG, Locale.US).format(new java.util.Date())); + printCloseTag(1, "created"); + + if (hasDeprecatedClasses) printAtomTag(1, "hasDeprecatedClasses"); + if (hasDeprecatedInterfaces) printAtomTag(1, "hasDeprecatedInterfaces"); + if (hasDeprecatedExceptions) printAtomTag(1, "hasDeprecatedExceptions"); + if (hasDeprecatedErrors) printAtomTag(1, "hasDeprecatedErrors"); + if (hasDeprecatedMethods) printAtomTag(1, "hasDeprecatedMethods"); + if (hasDeprecatedFields) printAtomTag(1, "hasDeprecatedFields"); + + // Output summary of all classes specified on command line + + println(); + println(1, "<!-- Classes specified by user on command line -->"); + ClassDoc[] specifiedClasses = rootDoc.specifiedClasses(); + for (int i=0, ilim=specifiedClasses.length; i<ilim; ++i) { + ClassDoc sc = specifiedClasses[i]; + printAtomTag(1, "specifiedclass fqname=\""+sc.qualifiedName()+"\" name=\""+sc.name()+"\""); + } + specifiedClasses = null; + + // Output summary of all packages specified on command line + + println(); + println(1, "<!-- Packages specified by user on command line -->"); + PackageDoc[] specifiedPackages = rootDoc.specifiedPackages(); + for (int i=0, ilim=specifiedPackages.length; i<ilim; ++i) { + PackageDoc sp = specifiedPackages[i]; + printAtomTag(1, "specifiedpackage name=\""+sp.name()+"\""); + } + specifiedPackages = null; + + // Output package group information specified on the + // command line + + println(); + println(1, "<!-- Package groups specified by user on command line -->"); + { + Iterator packageGroupIt = packageGroups.iterator(); + while (packageGroupIt.hasNext()) { + PackageGroup packageGroup = (PackageGroup)packageGroupIt.next(); + SortedSet groupedPackages = packageGroup.getPackages(); + if (groupedPackages.isEmpty()) { + printWarning("Package group named '" + + packageGroup.getName() + "' didn't match any packages."); + } + else { + printOpenTag(1, "packagegroup name=\"" + packageGroup.getName() + "\""); + Iterator groupedPackageIt = groupedPackages.iterator(); + while (groupedPackageIt.hasNext()) { + PackageDoc groupedPackageDoc = (PackageDoc)groupedPackageIt.next(); + printAtomTag(2, "package name=\"" + groupedPackageDoc.name() + "\""); + } + printCloseTag(1, "packagegroup"); + } + } + packageGroups = null; + } + + // Output information on all packages for which documentation + // has been made available via the Doclet API + + println(); + println(1, "<!-- Documentation for all packages -->"); + PackageDoc[] packages = rootDoc.specifiedPackages(); + for (int i=0, ilim=packages.length; i<ilim; ++i) { + PackageDoc c = packages[i]; + outputPackageDoc(c); + } + packages = null; + + // Output brief summary on all classes for which documentation + // has been made available via the Doclet API. + // + // While this is redundant, it can speed up XSLT + // processing by orders of magnitude + + println(); + println(1, "<!-- Brief summary for all classes -->"); + ClassDoc[] sumclasses = rootDoc.classes(); + for (int i=0, ilim=sumclasses.length; i<ilim; ++i) { + ClassDoc c = sumclasses[i]; + outputClassDocSummary(c); + } + sumclasses = null; + + // Output closing tag, finish output stream + + println(); + printCloseTag(0, "rootdoc"); + + closeTargetFile(); + + createIndexByName(); + + + + // Output information on all classes for which documentation + // has been made available via the Doclet API + + println(); + println(1, "<!-- Documentation for all classes -->"); + ClassDoc[] classes = rootDoc.classes(); + String prevPackageName = null; + for (int i = 0, ilim = classes.length; i < ilim; ++ i) { + ClassDoc c = classes[i]; + + if (isVerbose()) { + printNotice("Writing XML information for "+c.qualifiedName()+"..."); + } + else { + String packageName = c.containingPackage().name(); + if (null == prevPackageName || !packageName.equals(prevPackageName)) { + printNotice("Writing XML information for "+packageName+"..."); + prevPackageName = packageName; + } + } + + setTargetFile(c.qualifiedName().replace('/','.')+".xml"); + + println("<?xml version=\"1.0\"?>"); + println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc.dtd\">"); + + outputClassDoc(c); + + closeTargetFile(); + } + classes = null; + } + + // Copy DTD files to temporary directory + + // FIXME: try to solve this via jar: URLs. but this will + // probably break libxmlj compatibility (?) + + String[] resources = new String[] { + "gjdoc.dtd", + "gjdoc-alphaindex.dtd", + "dbcentx.mod", + "ent/iso-amsa.ent", + "ent/iso-amsb.ent", + "ent/iso-amsc.ent", + "ent/iso-amsn.ent", + "ent/iso-amso.ent", + "ent/iso-amsr.ent", + "ent/iso-box.ent", + "ent/iso-cyr1.ent", + "ent/iso-cyr2.ent", + "ent/iso-dia.ent", + "ent/iso-grk1.ent", + "ent/iso-grk2.ent", + "ent/iso-grk3.ent", + "ent/iso-grk4.ent", + "ent/iso-lat1.ent", + "ent/iso-lat2.ent", + "ent/iso-num.ent", + "ent/iso-pub.ent", + "ent/iso-tech.ent", + }; + + File tempDtdDirectory = new File(xmlTargetDirectory, "dtd"); + File tempDtdEntDirectory = new File(tempDtdDirectory, "ent"); + + if ((tempDtdDirectory.exists() || tempDtdDirectory.mkdir()) + && (tempDtdEntDirectory.exists() || tempDtdEntDirectory.mkdir())) { + for (int i = 0; i < resources.length; ++ i) { + copyResourceToFile("/dtd/" + resources[i], + new File(tempDtdDirectory, resources[i])); + } + } + else { + printError("Cannot create temporary directories for DTD data at " + tempDtdDirectory); + return false; + } + + // Copy package data-dir directory + + { + PackageDoc[] packages = rootDoc.specifiedPackages(); + for (int i=0, ilim=packages.length; i<ilim; ++i) { + PackageDoc c = packages[i]; + if (c instanceof GjdocPackageDoc) { + copyPackageDataDir((GjdocPackageDoc)c); + } + } + } + + // All information has been output. Apply stylesheet if given. + + gnu.classpath.tools.gjdoc.Main.releaseRootDoc(); + + this.currentClass = null; + this.currentMember = null; + this.currentExecMember = null; + + System.gc(); + + // From this point we are only operating on files, so we don't + // need this anymore and can free up some memory + + for (Iterator it = targets.iterator(); it.hasNext(); ) { + + TargetContext target = (TargetContext)it.next(); + + // We have XSLT postprocessing, run DocTranslet. + + //DocTranslet docTranslet = DocTranslet.fromClasspath("/doctranslets/html/gjdoc.xsl"); + + //docTranslet.setOptions(docTransletOptions); + + target.getDocTranslet().setOptions(docTransletOptions); + + target.getDocTranslet().apply(xmlTargetDirectory, + target.getTargetDirectory(), + rootDoc); + } + + // Done + + targets = null; + + System.gc(); + Runtime.getRuntime().runFinalization(); + + return true; + } + catch (Exception e) { + + // Something went wrong. Report to stderr and pass error to + // Javadoc Reporter + + e.printStackTrace(); + printError(e.toString()); + + Throwable rootCause = e.getCause(); + if (null != rootCause) { + while (null != rootCause.getCause()) { + rootCause = rootCause.getCause(); + } + System.err.println("Root cause:"); + rootCause.printStackTrace(); + } + + return false; + } + finally { + + // In any case, delete the working directory if we created one + + if (null != workingDirectory) { + + if (!deleteRecursive(workingDirectory)) { + printWarning("Could not delete temporary directory at "+workingDirectory); + } + } + + printNotice("Done."); + } + } + + /** + * Recursively delete the specified directory and its contents, + * like <code>rm -Rf directory</code> + * + * @return <code>true</code> on success + */ + private static boolean deleteRecursive(File directory) { + + boolean success = true; + + File[] files = directory.listFiles(); + + for (int i=0, ilim=files.length; i<ilim; ++i) { + + File file = files[i]; + + if (file.isDirectory()) { + + success = deleteRecursive(file) && success; + } + else { + + success = file.delete() && success; + } + } + + return directory.delete() && success; + } + + /** + * Prints a string to stdout and appends a newline. Convenience + * method. + */ + protected void println(String str) { + out.println(str); + } + + /** + * Prints a string to stdout without appending a newline. + * Convenience method. + */ + protected void print(String str) { + out.print(str); + } + + /** + * In standard mode, prints an empty line to stdout. + * In thight mode, nothing happens. + * Convenience method. + */ + protected void println() { + if (!compress) { + out.println(); + } + } + + /** + * In standard mode, prints the given text indented to stdout and appends newline. + * In tight mode, doesn't print indentation or newlines. + */ + protected void print(int indentLevel, String msg) { + if (compress) { + out.print(msg); + } + else { + StringBuffer indentation = new StringBuffer(); + for (int i=0; i<indentLevel*indentStep; ++i) { + indentation.append(' '); + } + out.print(indentation+msg); + } + } + + /** + * In tight mode, prints a message at a given indentation level. + * In standard mode, appends a newline in addition. + */ + protected void println(int indentLevel, String msg) { + print(indentLevel, msg); + if (!compress) out.println(); + } + + /** + * Prints an atom tag at the given indentation level. + */ + protected void printAtomTag(int level, String tag) { + println(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+"/>"); + } + + /** + * Prints an open tag at the given indentation level. + */ + protected void printOpenTag(int level, String tag) { + printOpenTag(level, replaceCharsInTag(tag), true); + } + + /** + * Prints an open tag at the given indentation level and + * conditionally appends a newline (if not in tight mode). + */ + protected void printOpenTag(int level, String tag, boolean appendNewline) { + if (appendNewline && !compress) { + println(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+">"); + } + else { + print(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+">"); + } + } + + /** + * Prints a close tag at the given indentation level. + */ + protected void printCloseTag(int level, String tag) { + printCloseTag(level, tag, true); + } + + /** + * Prints a close tag at the given indentation level and + * conditionally appends a newline (if not in tight mode). + */ + protected void printCloseTag(int level, String tag, boolean appendNewline) { + if (appendNewline && !compress) { + println(level, "</"+tagPrefix+":"+replaceCharsInTag(tag)+">"); + } + else { + print(level, "</"+tagPrefix+":"+replaceCharsInTag(tag)+">"); + } + } + + public static int optionLength(String option) { + if ("-d".equals(option)) return 2; + else if ("-fixhtml".equals(option)) return 1; + else if ("-compress".equals(option)) return 1; + else if ("-nohtmlwarn".equals(option)) return 1; + else if ("-noemailwarn".equals(option)) return 1; + else if ("-indentstep".equals(option)) return 2; + else if ("-xslsheet".equals(option)) return 2; + else if ("-xsltdriver".equals(option)) return 2; + else if ("-postprocess".equals(option)) return 2; + else if ("-genhtml".equals(option)) return 1; + else if ("-geninfo".equals(option)) return 1; + else if ("-gendocbook".equals(option)) return 1; + else if ("-xmlonly".equals(option)) return 1; + else if ("-bottomnote".equals(option)) return 2; + else if ("-workpath".equals(option)) return 2; + else if ("-title".equals(option)) return 2; + else if ("-tagletpath".equals(option)) return 2; + else if ("-taglet".equals(option)) return 2; + else if ("-authormail".equals(option)) return 2; + else if ("-mailmangledot".equals(option)) return 2; + else if ("-mailmangleat".equals(option)) return 2; + else if ("-noindex".equals(option)) return 1; + else if ("-nocomment".equals(option)) return 1; + else if ("-notree".equals(option)) return 1; + else if ("-nohelp".equals(option)) return 1; + else if ("-nonavbar".equals(option)) return 1; + else if ("-splitindex".equals(option)) return 1; + else if ("-author".equals(option)) return 1; + else if ("-version".equals(option)) return 1; + else if ("-nosince".equals(option)) return 1; + else if ("-nodeprecated".equals(option)) return 1; + else if ("-linksource".equals(option)) return 1; + else if ("-windowtitle".equals(option)) return 2; + else if ("-helpfile".equals(option)) return 2; + else if ("-stylesheetfile".equals(option)) return 2; + else if ("-tag".equals(option)) return 2; + else if ("-header".equals(option)) return 2; + else if ("-footer".equals(option)) return 2; + else if ("-bottom".equals(option)) return 2; + else if ("-doctitle".equals(option)) return 2; + else if ("-nodeprecatedlist".equals(option)) return 1; + else if ("-uses".equals(option)) return 1; + else if ("-group".equals(option)) return 3; + + else return -1; + } + + public static boolean validOptions(String[][] options) { + return true; + } + + + /** + * Workaround for non well-formed comments: fix tag contents + * by replacing <code><</code> with <code>&lt;</code>, + * <code>></code> with <code>&gt;</code> and + * <code>&</code> with <code>&amp;</code>. + * + * @param tagContent String to process + * + * @return given String with all special characters replaced by + * HTML entities. + */ + private static String replaceCharsInTag(String tagContent) { + return + replaceString( + replaceString( + replaceString( + tagContent, + "<", "<" + ), + ">", ">" + ), + "&", "&" + ); + } + + /** + * Replaces all occurences of string <code>needle</code> within string + * <code>haystack</code> by string <code>replacement</code>. + * + * @param haystack The string to search and replace in. + * @param needle The string which is searched for. + * @param replacement The string by which every occurence of <code>needle</code> is replaced. + */ + private static String replaceString(String haystack, String needle, String replacement) { + int ndx = haystack.indexOf(needle); + if (ndx<0) + return haystack; + else + return haystack.substring(0, ndx) + replacement + + replaceString(haystack.substring(ndx+needle.length()), needle, replacement); + } + + protected void setTargetFile(String filename) throws IOException { + + OutputStream fileOut = new FileOutputStream(new File(xmlTargetDirectory, filename)); + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fileOut, "UTF8")));; + } + + protected void closeTargetFile() { + + out.flush(); + out.close(); + } + + private String cdata(String str) { + + if (null==str) { + return str; + } // end of if ((null==str) + + StringBuffer rc = new StringBuffer(); + for (int i=0; i<str.length(); ++i) { + char c = str.charAt(i); + if (c==0x09 || c==0x0a || c==0x0d || (c>=0x20 && c<=0xd7ff) || (c>=0xe000 && c<=0xfffd) || (c>=0x10000 && c<=0x10ffff)) { + rc.append(c); + } + else { + printWarning("Invalid Unicode character 0x"+Integer.toString(c, 16)+" in javadoc markup has been stripped."); + } // end of else + + } + return rc.toString(); + } + + static void copyResourceToFile(String resourceName, File target) throws IOException { + + InputStream in = Driver.class.getResourceAsStream(resourceName); + + if (null != in) { + + FileOutputStream out = new FileOutputStream(target); + int size; + byte[] buffer = new byte[512]; + while ((size = in.read(buffer)) >= 0) { + out.write(buffer, 0, size); + } + out.close(); + } + else { + + throw new IOException("Can't find resource named "+resourceName); + } + } + + private void printError(String error) { + if (null != rootDoc) { + rootDoc.printError(error); + } + else { + System.err.println("ERROR: "+error); + } + } + + private void printWarning(String warning) { + if (null != rootDoc) { + rootDoc.printWarning(warning); + } + else { + System.err.println("WARNING: "+warning); + } + } + + private void printNotice(String notice) { + if (null != rootDoc) { + rootDoc.printNotice(notice); + } + else { + System.err.println(notice); + } + } + + /** + * Copy the contents of the input directory to the output + * directory. The output directory must exist. + */ + private void copyPackageDataDir(GjdocPackageDoc packageDoc) throws IOException { + File docFilesSourceDirectory + = new File(packageDoc.packageDirectory(), "doc-files"); + File docFilesTargetDirectory + = new File(this.targetDirectory, + packageDoc.name().replace('.', File.separatorChar)); + if (docFilesSourceDirectory.exists()) { + printNotice("Copying files from " + docFilesSourceDirectory); + copyDirectory(docFilesSourceDirectory, docFilesTargetDirectory, + docFilesSubdirsEnabled, + excludeDocFilesSubDirs); + } + } + + /** + * Recursively copy the contents of the input directory to the + * output directory. The output directory must exist. + */ + private static void copyDirectory(File sourceDir, File targetDir, + boolean recursive, + Set excludeDirs) throws IOException { + if (!targetDir.exists() && !targetDir.mkdirs()) { + throw new IOException("Cannot create directory " + targetDir); + } + + File[] sourceFiles = sourceDir.listFiles(); + for (int i=0; i<sourceFiles.length; ++i) { + if (sourceFiles[i].isDirectory()) { + if (recursive && (null == excludeDirs + || !excludeDirs.contains(sourceFiles[i].getName()))) { + File targetSubDir = new File(targetDir, + sourceFiles[i].getName()); + if (targetSubDir.exists() || targetSubDir.mkdir()) { + copyDirectory(sourceFiles[i], targetSubDir, recursive, null); + } + else { + throw new IOException("Cannot create directory " + targetSubDir); + } + } + } + else { + copyFile(sourceFiles[i], new File(targetDir, sourceFiles[i].getName())); + } + } + } + + /** + * Copy the contents of the input file to the output file. The + * output file's parent directory must exist. + */ + private static void copyFile(File sourceFile, File targetFile) throws IOException { + + InputStream in = new FileInputStream(sourceFile); + OutputStream out = new FileOutputStream(targetFile); + int nread; + byte[] buf = new byte[512]; + while ((nread = in.read(buf)) >= 0) { + out.write(buf, 0, nread); + } + in.close(); + out.close(); + } + + private void createIndexByName() throws IOException { + // Create index + + // Collect index + + Map indexMap = new TreeMap(new Comparator() { + public int compare(Object o1, Object o2) { + return o1.toString().toLowerCase().compareTo(o2.toString().toLowerCase()); + } + }); + + // Add packages to index + + PackageDoc[] packages = rootDoc.specifiedPackages(); + for (int i=0, ilim=packages.length; i<ilim; ++i) { + PackageDoc c = packages[i]; + indexMap.put(c.name(), c); + } + + // Add classes, fields and methods to index + + ClassDoc[] sumclasses = rootDoc.classes(); + for (int i=0, ilim=sumclasses.length; i<ilim; ++i) { + ClassDoc c = sumclasses[i]; + if (null == c.containingClass()) { + indexMap.put(c.name(), c); + } + else { + indexMap.put(c.name().substring(c.containingClass().name().length() + 1), c); + } + FieldDoc[] fields = c.fields(); + for (int j=0, jlim=fields.length; j<jlim; ++j) { + indexMap.put(fields[j].name(), fields[j]); + } + MethodDoc[] methods = c.methods(); + for (int j=0, jlim=methods.length; j<jlim; ++j) { + MethodDoc method = methods[j]; + StringBuffer signature = new StringBuffer(); + signature.append(method.name()); + signature.append('('); + Parameter[] parameters = method.parameters(); + for (int k=0, klim=parameters.length; k<klim; ++k) { + if (k > 0) { + signature.append(", "); + } + signature.append(parameters[k].typeName()); + } + signature.append(')'); + indexMap.put(signature.toString(), method); + } + } + + // Assign output stream + + setTargetFile("alphaindex.xml"); + + // Output XML document header + + println(0, "<?xml version=\"1.0\"?>"); + println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc-alphaindex.dtd\">"); + println(); + printOpenTag(0, "alphaindex xmlns=\"http://www.w3.org/TR/REC-html40\" xmlns:gjdoc=\"http://www.gnu.org/software/cp-tools/gjdocxml\""); + + Iterator it = indexMap.keySet().iterator(); + + char previousCategoryLetter = '\0'; + boolean categoryOpen = false; + + while (it.hasNext()) { + String key = (String)it.next(); + Doc entry = (Doc)indexMap.get(key); + + char firstChar = Character.toUpperCase(key.charAt(0)); + if (firstChar != previousCategoryLetter) { + if (categoryOpen) { + printCloseTag(1, "category"); + } + printOpenTag(1, "category letter=\"" + firstChar + "\""); + categoryOpen = true; + previousCategoryLetter = firstChar; + } + + printOpenTag(2, "entry name=\"" + key + "\""); + if (entry instanceof PackageDoc) { + printAtomTag(3, "isPackage"); + } + else if (entry instanceof ClassDoc) { + printAtomTag(3, "isClass"); + ClassDoc centry = (ClassDoc)entry; + currentClass = centry; + printAtomTag(3, "containingPackage name=\"" + centry.containingPackage().name() + "\""); + if (null != centry.containingClass()) { + printAtomTag(3, "containingClass name=\"" + centry.containingClass().name() + "\""); + } + if (centry.isInterface()) { + printAtomTag(3, "isInterface"); + } + if (centry.isException()) { + printAtomTag(3, "isException"); + } + if (centry.isError()) { + printAtomTag(3, "isError"); + } + if (centry.isOrdinaryClass()) { + printAtomTag(3, "isOrdinaryClass"); + } + } + else if (entry instanceof ProgramElementDoc) { + ProgramElementDoc pentry = (ProgramElementDoc)entry; + currentClass = pentry.containingClass(); + printAtomTag(3, "containingPackage name=\"" + pentry.containingPackage().name() + "\""); + printAtomTag(3, "containingClass name=\"" + pentry.containingClass().name() + "\""); + if (pentry.isMethod()) { + printAtomTag(3, "isMethod"); + ExecutableMemberDoc mentry = (ExecutableMemberDoc)pentry; + printAtomTag(3, "signature full=\""+mentry.signature()+"\" flat=\""+mentry.flatSignature()+"\""); + printAtomTag(3, "method name=\"" + mentry.name() + "\""); + } + if (pentry.isField()) { + printAtomTag(3, "isField"); + } + } + + Tag[] tags = entry.firstSentenceTags(); + for (int i=0, ilim=tags.length; i<ilim; ++i) { + Tag tag = tags[i]; + if (tag.firstSentenceTags().length>0) { + printOpenTag(3, "firstSentenceTags", false); + outputTags(4, tag.firstSentenceTags(), false, CONTEXT_TYPE); + printCloseTag(3, "firstSentenceTags"); + } + } + + + printCloseTag(2, "entry"); + } + + if (categoryOpen) { + printCloseTag(1, "category"); + } + + printCloseTag(0, "alphaindex"); + + closeTargetFile(); + } + + private static class UsageType + { + public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from"); + public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type"); + public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type"); + public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type"); + public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type"); + public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type"); + public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type"); + private String id; + + private UsageType(String id) + { + this.id = id; + } + + public String toString() { + return "UsageType{id=" + id + "}"; + } + + public String getId() { + return id; + } + } + + /** + * ClassDoc -> (PackageDoc -> (UsageType -> (Set of Doc))) + */ + private Map usedClassToPackagesMap = new HashMap(); + + private void addUsedBy(ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage) + { + Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(usedClass); + if (null == packageToUsageTypeMap) { + packageToUsageTypeMap = new HashMap(); + usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap); + } + + Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(userPackage); + if (null == usageTypeToUsersMap) { + usageTypeToUsersMap = new HashMap(); + packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap); + } + + Set userSet = (Set)usageTypeToUsersMap.get(usageType); + if (null == userSet) { + userSet = new TreeSet(); // FIXME: we need the collator from Main here + usageTypeToUsersMap.put(usageType, userSet); + } + userSet.add(user); + } + + /** + * Create the cross reference database. + */ + private void collectUsage() { + + ClassDoc[] classes = rootDoc.classes(); + for (int i = 0, ilim = classes.length; i < ilim; ++ i) { + ClassDoc clazz = classes[i]; + + // classes derived from + for (ClassDoc superclass = clazz.superclass(); superclass != null; + superclass = superclass.superclass()) { + addUsedBy(superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage()); + } + + FieldDoc[] fields = clazz.fields(); + for (int j = 0, jlim = fields.length; j < jlim; ++ j) { + FieldDoc field = fields[j]; + + // fields of type + ClassDoc fieldType = field.type().asClassDoc(); + if (null != fieldType) { + addUsedBy(fieldType, UsageType.FIELD_OF_TYPE, + field, clazz.containingPackage()); + } + } + + MethodDoc[] methods = clazz.methods(); + for (int j = 0, jlim = methods.length; j < jlim; ++ j) { + MethodDoc method = methods[j]; + + // methods with return type + + ClassDoc returnType = method.returnType().asClassDoc(); + if (null != returnType) { + addUsedBy(returnType, UsageType.METHOD_WITH_RETURN_TYPE, + method, clazz.containingPackage()); + } + Parameter[] parameters = method.parameters(); + for (int k=0; k<parameters.length; ++k) { + + // methods with parameter type + + Parameter parameter = parameters[k]; + ClassDoc parameterType = parameter.type().asClassDoc(); + if (null != parameterType) { + addUsedBy(parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE, + method, clazz.containingPackage()); + } + } + + // methods which throw + + ClassDoc[] thrownExceptions = method.thrownExceptions(); + for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) { + ClassDoc thrownException = thrownExceptions[k]; + addUsedBy(thrownException, UsageType.METHOD_WITH_THROWN_TYPE, + method, clazz.containingPackage()); + } + } + + ConstructorDoc[] constructors = clazz.constructors(); + for (int j = 0, jlim = constructors.length; j < jlim; ++ j) { + + ConstructorDoc constructor = constructors[j]; + + Parameter[] parameters = constructor.parameters(); + for (int k = 0, klim = parameters.length; k < klim; ++ k) { + + // constructors with parameter type + + Parameter parameter = parameters[k]; + ClassDoc parameterType = parameter.type().asClassDoc(); + if (null != parameterType) { + addUsedBy(parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE, + constructor, clazz.containingPackage()); + } + } + + // constructors which throw + + ClassDoc[] thrownExceptions = constructor.thrownExceptions(); + for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) { + ClassDoc thrownException = thrownExceptions[k]; + addUsedBy(thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE, + constructor, clazz.containingPackage()); + } + } + } + } + + private void outputUsage(ClassDoc clazz, int level) { + + Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(clazz); + if (null != packageToUsageTypeMap) { + printOpenTag(level, "references"); + + Iterator packagesIterator = packageToUsageTypeMap.keySet().iterator(); + + while (packagesIterator.hasNext()) { + PackageDoc packageDoc = (PackageDoc)packagesIterator.next(); + printOpenTag(level + 1, "referencing-package name=\"" + packageDoc.name() + "\""); + Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(packageDoc); + Iterator usageTypeIterator = usageTypeToUsersMap.keySet().iterator(); + while (usageTypeIterator.hasNext()) { + UsageType usageType = (UsageType)usageTypeIterator.next(); + printOpenTag(level + 2, "usage-type id=\"" + usageType.getId() + "\""); + Set users = (Set)usageTypeToUsersMap.get(usageType); + Iterator userIterator = users.iterator(); + while (userIterator.hasNext()) { + Doc user = (Doc)userIterator.next(); + if (user instanceof ClassDoc) { + printAtomTag(level + 3, "user" + + " class=\"" + ((ClassDoc)user).name() + "\""); + } + else if (user instanceof FieldDoc) { + FieldDoc fieldDoc = (FieldDoc)user; + printAtomTag(level + 3, "user" + + " class=\"" + fieldDoc.containingClass().name() + "\"" + + " field=\"" + fieldDoc.name() + "\""); + } + else if (user instanceof MethodDoc) { + MethodDoc methodDoc = (MethodDoc)user; + printAtomTag(level + 3, "user" + + " class=\"" + methodDoc.containingClass().name() + "\"" + + " method=\"" + methodDoc.name() + "\"" + + " signature=\"" + methodDoc.signature() + "\"" + + " flatSignature=\"" + methodDoc.flatSignature() + "\""); + } + else if (user instanceof ConstructorDoc) { + ConstructorDoc constructorDoc = (ConstructorDoc)user; + printAtomTag(level + 3, "user" + + " class=\"" + constructorDoc.containingClass().name() + "\"" + + " signature=\"" + constructorDoc.signature() + "\"" + + " flatSignature=\"" + constructorDoc.flatSignature() + "\""); + } + } + printCloseTag(level +2, "usage-type"); + } + printCloseTag(level + 1, "referencing-package"); + } + + printCloseTag(level, "references"); + } + } + + private boolean processGroupOption(String groupName, String colonSeparatedPackageList) + { + try { + PackageMatcher packageMatcher = new PackageMatcher(); + + StringTokenizer tokenizer = new StringTokenizer(colonSeparatedPackageList, ":"); + while (tokenizer.hasMoreTokens()) { + String packageWildcard = tokenizer.nextToken(); + packageMatcher.addWildcard(packageWildcard); + } + + SortedSet groupPackages = packageMatcher.filter(rootDoc.specifiedPackages()); + + packageGroups.add(new PackageGroup(groupName, groupPackages)); + + return true; + } + catch (InvalidPackageWildcardException e) { + return false; + } + } + + private void registerTaglet(Taglet taglet) + { + tagletMap.put(taglet.getName(), taglet); + } + + private boolean isVerbose() + { + return false; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/Driver1_4.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/Driver1_4.java new file mode 100644 index 000000000..6744947af --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/Driver1_4.java @@ -0,0 +1,84 @@ +/* gnu.classpath.tools.doclets.xmldoclet.Driver1_4 + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet; + +import com.sun.javadoc.*; +import java.io.IOException; + +/** + * A Doclet which retrieves all information presented by the Doclet + * API, dumping it to stdout in XML format. + * + * Supports Doclet API Version 1.4. + * + * @author Julian Scheid + */ +public class Driver1_4 extends Driver { + + /** + * Official Doclet entry point. + */ + public static boolean start(RootDoc root) { + + // Create a new XmlDoclet instance and delegate control. + return new Driver1_4().instanceStart(root); + } + + /* since 1.4 + private void outputSourcePosition(int level, SourcePosition sourcePosition) { + println(level, "<sourceposition" + + " file=\""+sourcePosition.file().toString()+"\"" + + " line=\""+sourcePosition.line()+"\"" + + " column=\""+sourceposition.column()+"\"" + + "/>"); + } + */ + + protected void outputClassDoc(ClassDoc classDoc) + throws IOException + { + super.outputClassDoc(classDoc); + //outputSourcePosition(level, doc.position()); + } + + protected void outputFieldDocBody(int level, FieldDoc fieldDoc) { + super.outputFieldDocBody(level, fieldDoc); + //println(level, "<constantValueExpression>"+fieldDoc.constantValueExpression()+"</constantValueExpression>"); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/HtmlRepairer.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/HtmlRepairer.java new file mode 100644 index 000000000..582a45b32 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/HtmlRepairer.java @@ -0,0 +1,690 @@ +/* gnu.classpath.tools.doclets.xmldoclet.HtmlRepairer.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet; + +import java.io.*; +import java.util.*; +import com.sun.javadoc.DocErrorReporter; +import com.sun.javadoc.ClassDoc; +import com.sun.javadoc.MemberDoc; + +/** + * Provides methods for tidying up HTML source. + * + * @author Julian Scheid + */ +public final class HtmlRepairer { + + private static class TagInfo { + + private Set parentTags = new HashSet(); + + public TagInfo(String parentTag) { + this.parentTags.add(parentTag); + } + + public TagInfo(String[] parentTagArr) { + for (int i=0; i<parentTagArr.length; ++i) { + this.parentTags.add(parentTagArr[i]); + } + } + + public boolean isLegalParentTag(String tag) { + return this.parentTags.contains(tag); + } + } + + private DocErrorReporter warningReporter; + private boolean noWarn; + private boolean noEmailWarn; + private ClassDoc contextClass; + private MemberDoc contextMember; + private StringBuffer output = new StringBuffer(); + private Stack tagStack = new Stack(); + private boolean isLeadingTag = true; + private boolean throwAwayLeadingPara = false; + + private static Map tagInfoMap; + + private static Set noTextParentTags; + + static { + tagInfoMap = new HashMap(); + tagInfoMap.put("li", new TagInfo(new String[] { "ul", "ol", "nl", "menu", "dir" })); + tagInfoMap.put("td", new TagInfo(new String[] { "tr" })); + tagInfoMap.put("th", new TagInfo(new String[] { "tr" })); + tagInfoMap.put("tr", new TagInfo(new String[] { "table" })); + tagInfoMap.put("dt", new TagInfo(new String[] { "dl" })); + tagInfoMap.put("dd", new TagInfo(new String[] { "dl" })); + tagInfoMap.put("param", new TagInfo(new String[] { "applet" })); + + String[] noTextParentTagArr = { + "area", "base", "body", "br", "dd", "dt", "head", "hr", "html", + "img", "input", "link", "map", "meta", "ol", "optgroup", "param", + "select", "table", "tbody", "tfoot", "thead", "tr", "ul", + }; + + noTextParentTags = new HashSet(); + for (int i=0; i<noTextParentTagArr.length; ++i) { + noTextParentTags.add(noTextParentTagArr[i]); + } + } + + public HtmlRepairer(DocErrorReporter warningReporter, + boolean noWarn, boolean noEmailWarn, + ClassDoc contextClass, MemberDoc contextMember, + boolean throwAwayLeadingPara) { + this.warningReporter = warningReporter; + this.noWarn = noWarn; + this.noEmailWarn = noEmailWarn; + this.contextClass = contextClass; + this.contextMember = contextMember; + this.throwAwayLeadingPara = throwAwayLeadingPara; + } + + private static String replaceStr(String haystack, String needle, String replacement) { + int ndx=haystack.indexOf(needle); + if (ndx<0) + return haystack; + else + return haystack.substring(0, ndx)+replacement + + replaceStr(haystack.substring(ndx+needle.length()), needle, replacement); + } + + private void haveText(String text) { + + if (isLeadingTag && throwAwayLeadingPara) { + if (0 != text.trim().length()) { + isLeadingTag = false; + } + } + + if (tagStack.isEmpty() || !noTextParentTags.contains(tagStack.peek())) { + + text = replaceStr(text, "<1", "<1"); + text = replaceStr(text, "&&", "&&"); + text = replaceStr(text, "& ", "& "); + text = replaceStr(text, "&\t", "&\t"); + text = replaceStr(text, "&\r", "&\r"); + text = replaceStr(text, "&\n", "&\n"); + for (char c='0'; c<='9'; ++c) + text = replaceStr(text, "&"+c, "&"+c); + text = replaceStr(text, "\u00a7", "§"); + output.append(text); + } + else { + printWarning("Discarded text in <" + tagStack.peek() + "> element"); + } + } + + private void haveStartOrEndTag(String tag) { + + boolean _isLeadingTag = isLeadingTag; + isLeadingTag = false; + + tag = tag.trim(); + + boolean isEndTag = tag.startsWith("/"); + boolean isAtomTag = tag.endsWith("/"); + + if (isEndTag && isAtomTag) { + // got something like '</a/>' which is invalid. + // suppose a close tag was intended. + tag = tag.substring(0, tag.length()-1); + } + + if (tag.length() < 1) { + printWarning("Deleting broken tag"); + return; + } + + String tagName = tag.substring(isEndTag?1:0, isAtomTag?tag.length()-1:tag.length()); + String tagAttributes = ""; + + for (int i=0; i<tagName.length(); ++i) { + if (" \t\r\n".indexOf(tagName.charAt(i))>=0) { + tagAttributes = tagName.substring(i).trim(); + tagName = tagName.substring(0, i); + break; + } + } + + if (!isEndTag && tagName.indexOf('@')>0) { + if (!noEmailWarn) { + printWarning("Tag looks like email address: <"+tagName+">"); + } + output.append("<"+tag+">"); + return; + } + + tagName = tagName.toLowerCase(); + + if (_isLeadingTag && "p".equals(tagName) && !isEndTag && throwAwayLeadingPara) { + return; + } + + if ("p".equals(tagName) || "br".equals(tagName) || "hr".equals(tagName)) { + // throw away </p> and </br> + if (isEndTag) { + return; + } + // make sure every <p> is a <p/> and every <br> is a <br/> + else if (!isAtomTag) { + tag += "/"; + isAtomTag = true; + } + } + + if (isEndTag) { + + // check whether this close tag is on the stack + // if yes, close all tags up to this tag + if (tagStack.contains(tagName)) { + String popped; + do { + popped = (String)tagStack.pop(); + if (!popped.equals(tagName)) + printWarning("Inserting '</"+popped+">"); + output.append("</"+popped+">"); + } + while (!popped.equals(tagName)); + } + // if not, just throw it away + else { + printWarning("Deleting <"+tag+">"); + } + } + else { + + final int STATE_INITIAL = 1; + final int STATE_EXPECT_ATTRIBUTENAME = 2; + final int STATE_UNQUOTED_ATTRIBUTEVALUE = 3; + final int STATE_SINGLEQUOTE_ATTRIBUTEVALUE = 4; + final int STATE_DOUBLEQUOTE_ATTRIBUTEVALUE = 5; + final int STATE_EXPECT_ATTRIBUTEVALUE = 6; + final int STATE_EXPECT_EQUALSIGN = 7; + + int state = STATE_INITIAL; + + String newAttributes = ""; + String attributeName = null; + StringBuffer buf = new StringBuffer(); + + char[] attrsAsChars = tagAttributes.toCharArray(); + for (int i=0, ilim=attrsAsChars.length+1; i<ilim; ++i) { + int c; + if (i<attrsAsChars.length) + c = (int)attrsAsChars[i]; + else + c = -1; + + switch (state) { + + case STATE_INITIAL: + if (" \t\r\n".indexOf(c)>=0){ + continue; + } + else if (-1==c) { + continue; + } + else { + state = STATE_EXPECT_ATTRIBUTENAME; + buf.append((char)c); + } + break; + + case STATE_EXPECT_ATTRIBUTENAME: + if ('='==c) { + attributeName = buf.toString(); + buf.setLength(0); + state = STATE_EXPECT_ATTRIBUTEVALUE; + } + else if (-1==c) { + attributeName = buf.toString(); + buf.setLength(0); + printWarning("In Tag '"+tag+"':\nAttribute name without a value, inserting value =\""+attributeName+"\""); + } + else if (" \t\r\n".indexOf(c)>=0) { + state = STATE_EXPECT_EQUALSIGN; + } + else { + buf.append((char)c); + } + break; + + case STATE_EXPECT_EQUALSIGN: + if (" \t\r\n".indexOf(c)>=0){ + continue; + } + else if ('='==c) { + state = STATE_EXPECT_ATTRIBUTEVALUE; + attributeName = buf.toString(); + buf.setLength(0); + } + else { + attributeName = buf.toString(); + buf.setLength(0); + printWarning("In Tag '"+tag+"':\nAttribute name without a value, inserting value =\""+attributeName+"\""); + newAttributes += " "+attributeName+"=\""+attributeName+"\""; + buf.append((char)c); + state = STATE_EXPECT_ATTRIBUTENAME; + } + break; + + case STATE_EXPECT_ATTRIBUTEVALUE: + if (" \t\r\n".indexOf(c)>=0){ + continue; + } + else if ('\"'==c) { + state = STATE_DOUBLEQUOTE_ATTRIBUTEVALUE; + } + else if ('\''==c) { + state = STATE_SINGLEQUOTE_ATTRIBUTEVALUE; + } + else { + state = STATE_UNQUOTED_ATTRIBUTEVALUE; + buf.append((char)c); + } + break; + + case STATE_UNQUOTED_ATTRIBUTEVALUE: + if (-1==c || " \t\r\n".indexOf(c)>=0){ + state = STATE_INITIAL; + newAttributes += " "+attributeName + "=\"" + buf.toString() + "\""; + buf.setLength(0); + } + else { + buf.append((char)c); + } + break; + + case STATE_SINGLEQUOTE_ATTRIBUTEVALUE: + if ('\''==c) { + state = STATE_INITIAL; + newAttributes += " "+attributeName + "=\"" + buf.toString() + "\""; + buf.setLength(0); + } + else { + buf.append((char)c); + } + break; + + case STATE_DOUBLEQUOTE_ATTRIBUTEVALUE: + if ('\"'==c) { + state = STATE_INITIAL; + newAttributes += " "+attributeName + "=\"" + buf.toString() + "\""; + buf.setLength(0); + } + else { + buf.append((char)c); + } + break; + } + } + + + if (!isAtomTag) { + + // check whether this open tag is equal to the topmost + // entry on the stack; if yes, emit a close tag first + + // corrects stuff like '<tr><td>...<td>...'); + if (!tagStack.isEmpty() && tagStack.peek().equals(tagName)) { + printWarning("Inserting </"+tagName+">"); + output.append("</"+tagName+">"); + tagStack.pop(); + } + else { + processKnownChildTags(tagName, tagStack, output); + } + + // otherwise, we assume there are no close tags required + // before this open tag. + tagStack.push(tagName); + + output.append("<"+tagName+newAttributes+">"); + } + else { + output.append("<"+tagName+newAttributes+"/>"); + } + } + } + + private boolean processKnownChildTags(String tagName, Stack tagStack, StringBuffer output) { + + TagInfo tagInfo = (TagInfo)tagInfoMap.get(tagName); + if (null != tagInfo) { + + String parentTag = null; + for (Enumeration en = tagStack.elements(); en.hasMoreElements(); ) { + String tag = (String)en.nextElement(); + if (tagInfo.isLegalParentTag(tag)) { + parentTag = tag; + break; + } + } + if (parentTag != null) { + while (((String)tagStack.peek()) != parentTag) { + String poppedTagName = (String)tagStack.pop(); + output.append("</"+poppedTagName+">"); + printWarning("Inserting </"+poppedTagName+">"); + } + return true; + } + } + return false; + } + + private void flush() { + + // close all pending tags + while (!tagStack.isEmpty()) { + String tagName = (String)tagStack.pop(); + printWarning("Inserting </"+tagName+">"); + output.append("</"+tagName+">"); + } + } + + /** + * Takes HTML fragment and returns a well-formed XHTML + * equivalent. + * + * In the returned String, all tags are properly closed and + * nested. + * + * Currently, the returned String is not guaranteed to be + * well-formed. In particular there are no checks on the tag + * names, attribute names and entity names. + */ + public String getWellformedHTML(String text) { + + final int STATE_INITIAL = 1; + final int STATE_TAG_START = 2; + final int STATE_TAG = 3; + final int STATE_TAG_DOUBLEQUOTE = 4; + final int STATE_TAG_SINGLEQUOTE = 5; + final int STATE_AMP = 6; + + int state = STATE_INITIAL; + output.setLength(0); + + + StringBuffer buf = new StringBuffer(); + char[] textAsChars = text.toCharArray(); + + outer_loop: + for (int i=0, ilim=textAsChars.length+1; i<ilim; ++i) { + int c; + + if (i<textAsChars.length) { + c = textAsChars[i]; + } + else { + c = -1; + } + + switch (state) { + + case STATE_INITIAL: + if ('<'==c) { + state = STATE_TAG_START; + if (buf.length()>0) { + haveText(buf.toString()); + buf.setLength(0); + } + } + else if ('>'==c) { + // assume this is a greater-than sign + buf.append(">"); + } + else if ('&'==c) { + state = STATE_AMP; + } + else if (-1==c) { + if (buf.length()>0) { + haveText(buf.toString()); + buf.setLength(0); + } + continue; + } + else { + buf.append((char)c); + } + break; + + case STATE_AMP: + if ('<'==c) { + buf.append("&"); + state = STATE_TAG_START; + if (buf.length()>0) { + haveText(buf.toString()); + buf.setLength(0); + } + } + else if ('>'==c) { + // assume this is a greater-than sign + buf.append("&"); + buf.append(">"); + state = STATE_INITIAL; + } + else if ('&'==c) { + buf.append("&"); + buf.append("&"); + state = STATE_INITIAL; + } + else if (-1==c) { + buf.append("&"); + haveText(buf.toString()); + buf.setLength(0); + state = STATE_INITIAL; + continue; + } + else { + // peek forward and see whether this is a valid entity. + if ('#'==c) { + buf.append("&"); + buf.append((char)c); + state = STATE_INITIAL; + continue outer_loop; + } + else if (Character.isLetter((char)c)) { + for (int i2=i+1; i2<ilim-1; i2++) { + if (';' == textAsChars[i2]) { + buf.append("&"); + buf.append((char)c); + state = STATE_INITIAL; + continue outer_loop; + } + else if (!Character.isLetter((char)c) + && !Character.isDigit((char)c) + && ".-_:".indexOf((char)c) < 0 + //&& !isCombiningChar(c) // FIXME + //&& !isExtender(c) // FIXME + ) { + break; + } + } + // not a valid entity declaration; assume & + } + buf.append("&"); + buf.append((char)c); + state = STATE_INITIAL; + } + + /* + else if ('#'==c || Character.isLetter((char)c)) { + buf.append("&"); + buf.append((char)c); + state = STATE_INITIAL; + } + else { + buf.append("&"); + buf.append((char)c); + state = STATE_INITIAL; + } + */ + break; + + case STATE_TAG_START: + if (" \t\r\n".indexOf(c)>=0) { + //continue; + + // new: assume this is a less-sign + haveText("<"+c); + state = STATE_INITIAL; + } + else if ('/'==c) { + buf.append((char)c); + state = STATE_TAG; + } + else if ('<'==c) { + // assume this is a less-sign + haveText("<<"); + state = STATE_INITIAL; + } + else if ('>'==c) { + // assume this is a less-sign + haveText("<>"); + state = STATE_INITIAL; + } + //else if ('-'==c || '+'==c || '='==c || '\''==c || "0123456789".indexOf(c)>=0) { + else if (!Character.isLetter((char)c)) { + // assume this is a less-sign + haveText("<"+(char)c); + state = STATE_INITIAL; + } + else { + buf.append((char)c); + state = STATE_TAG; + } + break; + + case STATE_TAG: + if ('\"'==c) { + buf.append((char)c); + state = STATE_TAG_DOUBLEQUOTE; + } + else if ('\''==c) { + buf.append((char)c); + state = STATE_TAG_SINGLEQUOTE; + } + else if ('>'==c) { + state = STATE_INITIAL; + haveStartOrEndTag(buf.toString()); + buf.setLength(0); + } + else if ('<'==c) { + // notify user, missing greater-than sign + haveStartOrEndTag(buf.toString()); + buf.setLength(0); + } + else if (-1==c) { + printWarning("Unclosed tag at end-of-comment: <"+buf); + haveStartOrEndTag(buf.toString()); + buf.setLength(0); + } + else { + buf.append((char)c); + } + break; + + case STATE_TAG_DOUBLEQUOTE: + if ('\"'==c) { + buf.append((char)c); + state = STATE_TAG; + } + else if (-1==c) { + printWarning("Unclosed attribute value at end-of-comment."); + haveStartOrEndTag(buf.toString()+"\""); + } + else { + buf.append((char)c); + } + break; + + case STATE_TAG_SINGLEQUOTE: + if ('\''==c) { + buf.append((char)c); + state = STATE_TAG; + } + else if (-1==c) { + printWarning("Unclosed attribute value at end-of-comment."); + haveStartOrEndTag(buf.toString()+"'"); + } + else { + buf.append((char)c); + } + break; + } + } + + return output.toString(); + } + + private String getContext() { + if (null != contextClass) { + StringBuffer rc = new StringBuffer(); + rc.append(contextClass.qualifiedTypeName()); + if (null != contextMember) { + rc.append("."+contextMember.toString()); + } + return rc.toString(); + } + else { + return null; + } + } + + private void printWarning(String msg) { + if (null != warningReporter && !noWarn) { + String context = getContext(); + if (null != context) { + warningReporter.printWarning("In "+getContext()+": "+msg); + } + else { + warningReporter.printWarning("In overview page: "+msg); + } + } + } + + public String terminateText() { + output.setLength(0); + flush(); + return output.toString(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/TargetContext.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/TargetContext.java new file mode 100644 index 000000000..6e05a5e96 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/TargetContext.java @@ -0,0 +1,103 @@ +/* gnu.classpath.tools.doclets.xmldoclet.TargetContext + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import java.net.URL; + +import java.util.List; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; + +import gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTranslet; + +/** + * Stores any XSL transformation and postprocessing-specific + * information given by the user on the doclet command line. + * + * @author Julian Scheid + */ +public class TargetContext { + + /** + * The DocTranslet to use for processing doclet output. + */ + private DocTranslet docTranslet; + + /** + * Directory to write final output to. + */ + private File targetDirectory; + + /** + * Directory where XSLT output will be written to. If an XSLT + * sheet was specified, but no postprocessing driver was given, + * this is the target directory specified by the user. Otherwise, + * this is a temporary directory. + */ + private File xsltTargetDirectory; + + + public TargetContext(DocTranslet docTranslet, File targetDirectory) { + this.docTranslet = docTranslet; + this.targetDirectory = targetDirectory; + } + + public File getTargetDirectory() { + return targetDirectory; + } + + public void setTargetDirectory(File targetDirectory) { + this.targetDirectory = targetDirectory; + } + + public DocTranslet getDocTranslet() { + return docTranslet; + } + + public void setDocTranslet(DocTranslet docTranslet) { + this.docTranslet = docTranslet; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTranslet.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTranslet.java new file mode 100644 index 000000000..c5a9f82b9 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTranslet.java @@ -0,0 +1,460 @@ +/* gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTranslet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet.doctranslet; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.HashMap; +import java.util.Map; + +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.jar.Attributes; + +import javax.xml.transform.ErrorListener; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.URIResolver; + +import javax.xml.transform.dom.DOMResult; + +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import org.xml.sax.SAXException; + +import gnu.classpath.tools.IOToolkit; +import gnu.classpath.tools.doclets.xmldoclet.Driver; + +import com.sun.javadoc.DocErrorReporter; + +public class DocTranslet implements ErrorListener { + + private static class DocErrorReporterOutputStream + extends OutputStream + { + private ByteArrayOutputStream out = new ByteArrayOutputStream(); + private DocErrorReporter reporter; + + public DocErrorReporterOutputStream(DocErrorReporter reporter) { + this.reporter = reporter; + } + + public void write(int ch) { + out.write(ch); + if (ch == 10) { + reporter.printNotice(out.toString()); + out.reset(); + } + } + } + + private String mainResourceFilename; + private ClassLoader classLoader; + private Map transformerMap = new java.util.HashMap(); //WeakHashMap(); + private DocTransletOptions options; + + protected DocTranslet(String mainResourceFilename, + ClassLoader classLoader) + throws DocTransletConfigurationException { + + if (mainResourceFilename.length() > 0 && mainResourceFilename.charAt(0) == '/') { + mainResourceFilename = mainResourceFilename.substring(1); + } + this.mainResourceFilename = mainResourceFilename; + this.classLoader = classLoader; + } + + private static boolean equalsFile(File file1, File file2) { + return file1.getAbsolutePath().equals(file2.getAbsolutePath()); + } + + private static File getParentFile(File file) { + String filename = file.getAbsolutePath(); + if (filename.endsWith(File.separator)) { + filename = filename.substring(0, filename.length() - 1); + } + int lastSlash = filename.lastIndexOf(File.separatorChar); + if (lastSlash > 0) { + filename = filename.substring(0, lastSlash); + } + else { + filename = File.separator; + } + + return new File(filename); + } + + private static boolean cacheXSLTSheets = true; + + public void apply(File xmlSourceDirectory, File targetDirectory, + DocErrorReporter reporter) + throws DocTransletException { + + PrintStream err = System.err; + + try{ + URL mainResourceURL = classLoader == null ? + ClassLoader.getSystemResource(mainResourceFilename): + classLoader.getResource(mainResourceFilename); + + if (null == mainResourceURL) { + throw new DocTransletException("Cannot find resource '" + mainResourceFilename + "'"); + } + + + Map parameters = new HashMap(); + parameters.put("gjdoc.xmldoclet.version", Driver.XMLDOCLET_VERSION); + + parameters.put("gjdoc.option.nonavbar", xsltBoolean(options.nonavbar)); + parameters.put("gjdoc.option.noindex", xsltBoolean(options.noindex)); + parameters.put("gjdoc.option.notree", xsltBoolean(options.notree)); + parameters.put("gjdoc.option.nocomment", xsltBoolean(options.nocomment)); + parameters.put("gjdoc.option.nohelp", xsltBoolean(options.nohelp)); + parameters.put("gjdoc.option.splitindex", xsltBoolean(options.splitindex)); + parameters.put("gjdoc.option.linksource", xsltBoolean(options.linksource)); + parameters.put("gjdoc.option.nodeprecatedlist", xsltBoolean(options.nodeprecatedlist)); + parameters.put("gjdoc.option.uses", xsltBoolean(options.uses)); + parameters.put("gjdoc.option.windowtitle", options.windowtitle); + parameters.put("gjdoc.option.helpfile", options.helpfile); + parameters.put("gjdoc.option.stylesheetfile", options.stylesheetfile); + parameters.put("gjdoc.option.header", options.header); + parameters.put("gjdoc.option.footer", options.footer); + parameters.put("gjdoc.option.bottom", options.bottom); + parameters.put("gjdoc.option.doctitle", options.doctitle); + + List outputFileList = getOutputFileList(mainResourceURL, + xmlSourceDirectory, + parameters); + + reporter.printNotice("Running DocTranslet..."); + + TransformerFactory transformerFactory + = TransformerFactory.newInstance(); + + transformerFactory.setErrorListener(this); + + boolean isLibxmlJ + = transformerFactory.getClass().getName().equals("gnu.xml.libxmlj.transform.TransformerFactoryImpl"); + + for (Iterator it = outputFileList.iterator(); it.hasNext(); ) { + + if (isLibxmlJ) { + System.gc(); + Runtime.getRuntime().runFinalization(); + } + + OutputFileInfo fileInfo = (OutputFileInfo)it.next(); + + File targetFile = new File(targetDirectory, fileInfo.getName()); + File packageTargetDir = getParentFile(targetFile); + + if (!packageTargetDir.exists() && !packageTargetDir.mkdirs()) { + throw new DocTransletException("Target directory " + packageTargetDir + " does not exist and cannot be created."); + } + + if (options.linksource) { + File sourceTargetDirectory = new File(targetDirectory, "src-html"); + File sourceTargetFile = new File(sourceTargetDirectory, fileInfo.getName()); + File sourcePackageTargetDir = getParentFile(sourceTargetFile); + + if (!sourcePackageTargetDir.exists() && !sourcePackageTargetDir.mkdirs()) { + throw new DocTransletException("Target directory " + packageTargetDir + " does not exist and cannot be created."); + } + } + + if (options.uses) { + File usesTargetDirectory = new File(targetDirectory, "class-use"); + File usesTargetFile = new File(usesTargetDirectory, fileInfo.getName()); + File usesPackageTargetDir = getParentFile(usesTargetFile); + + if (!usesPackageTargetDir.exists() && !usesPackageTargetDir.mkdirs()) { + throw new DocTransletException("Target directory " + packageTargetDir + " does not exist and cannot be created."); + } + } + + if (null != fileInfo.getSource()) { + + reporter.printNotice("Copying " + fileInfo.getComment() + "..."); + InputStream in = new URL(mainResourceURL, fileInfo.getSource()).openStream(); + FileOutputStream out = new FileOutputStream(targetFile.getAbsolutePath()); + IOToolkit.copyStream(in, out); + in.close(); + out.close(); + } + else { + + reporter.printNotice("Generating " + fileInfo.getComment() + "..."); + + String pathToRoot = ""; + for (File file = getParentFile(targetFile); !equalsFile(file, targetDirectory); file = getParentFile(file)) { + pathToRoot += "../"; + } + + StreamResult out = new StreamResult(targetFile.getAbsolutePath()); + + StreamSource in = new StreamSource(new File(xmlSourceDirectory, "index.xml").getAbsolutePath()); + URL resource = new URL(mainResourceURL, fileInfo.getSheet()); + + + StreamSource xsltSource = new StreamSource(resource.toExternalForm()); + + if (null != fileInfo.getInfo()) { + parameters.put("gjdoc.outputfile.info", fileInfo.getInfo()); + } + parameters.put("gjdoc.pathtoroot", pathToRoot); + + Transformer transformer; + transformer = (Transformer)transformerMap.get(xsltSource.getSystemId()); + if (null == transformer) { + transformer = transformerFactory.newTransformer(xsltSource); + if (cacheXSLTSheets) { + transformerMap.put(xsltSource.getSystemId(), transformer); + } + } + + transformer.clearParameters(); + for (Iterator pit = parameters.keySet().iterator(); pit.hasNext(); ) { + String key = (String)pit.next(); + String value = (String)parameters.get(key); + transformer.setParameter(key, value); + } + + transformer.setErrorListener(this); + DocErrorReporterOutputStream errorReporterOut + = new DocErrorReporterOutputStream(reporter); + System.setErr(new PrintStream(errorReporterOut)); + + transformer.transform(in, out); + errorReporterOut.flush(); + } + } + } + catch (MalformedURLException e) { + throw new DocTransletException(e); + } + catch (TransformerFactoryConfigurationError e) { + throw new DocTransletException(e); + } + catch (TransformerException e) { + throw new DocTransletException(e.getMessageAndLocation(), e); + } + catch (IOException e) { + throw new DocTransletException(e); + } + finally { + System.setErr(err); + } + } + + private List getOutputFileList(URL resource, File xmlSourceDirectory, Map parameters) + throws DocTransletException { + + try { + List result; + + OutputStream out = new ByteArrayOutputStream(); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.newDocument(); + DOMResult domResult = new DOMResult(document); + { + StreamSource source = new StreamSource(resource.toExternalForm()); + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = (Transformer)transformerFactory.newTransformer(source); + + transformer.clearParameters(); + for (Iterator pit = parameters.keySet().iterator(); pit.hasNext(); ) { + String key = (String)pit.next(); + String value = (String)parameters.get(key); + transformer.setParameter(key, value); + } + + transformer.transform(new StreamSource(new File(xmlSourceDirectory, + "index.xml").getAbsolutePath()), + domResult); + } + + { + NodeList nodeList = document.getElementsByTagName("outputfile"); + result = new ArrayList(nodeList.getLength()); + + for (int i=0; i<nodeList.getLength(); ++i) { + Element elem = (Element)nodeList.item(i); + String name = getTextContent(elem.getElementsByTagName("name").item(0)); + String source + = (null != elem.getElementsByTagName("source").item(0)) + ? getTextContent(elem.getElementsByTagName("source").item(0)) + : null; + String sheet + = (null != elem.getElementsByTagName("sheet").item(0)) + ? getTextContent(elem.getElementsByTagName("sheet").item(0)) + : null; + String comment = getTextContent(elem.getElementsByTagName("comment").item(0)); + String info = null; + if (elem.getElementsByTagName("info").getLength() > 0) { + if (null != elem.getElementsByTagName("info").item(0).getFirstChild()) { + info = getTextContent(elem.getElementsByTagName("info").item(0)); + } + else { + info = ""; + } + } + result.add(new OutputFileInfo(name, source, sheet, comment, info)); + } + } + return result; + } + catch (TransformerFactoryConfigurationError e) { + throw new DocTransletException(e); + } + catch (TransformerException e) { + throw new DocTransletException(e.getMessageAndLocation(), e); + } + catch (ParserConfigurationException e) { + throw new DocTransletException(e); + } + } + + private String getTextContent(Node elem) + { + StringBuffer result = new StringBuffer(); + NodeList children = elem.getChildNodes(); + for (int i=0; i<children.getLength(); ++i) { + Node item = children.item(i); + if (null != item) { + String value = item.getNodeValue(); + if (null != value) { + result.append(value); + } + } + } + return result.toString(); + } + + public void setOptions(DocTransletOptions options) { + this.options = options; + } + + + public static DocTranslet fromClasspath(String resourceName) + throws DocTransletConfigurationException { + + return new DocTranslet(resourceName, + DocTranslet.class.getClassLoader()); + } + + public static DocTranslet fromJarFile(File jarFile) + throws DocTransletConfigurationException { + + try { + JarFile inputJarFile = new JarFile(jarFile, false, JarFile.OPEN_READ); + + Manifest manifest = inputJarFile.getManifest(); + + if (null == manifest) { + + throw new DocTransletConfigurationException("Jar file '" + jarFile + "' doesn't contain a manifest."); + } + + Attributes mainAttributes = manifest.getMainAttributes(); + + String docTransletMainEntry = mainAttributes.getValue("doctranslet-main-entry"); + + if (null == docTransletMainEntry) { + + throw new DocTransletConfigurationException("Manifest in Jar file '" + jarFile + "' doesn't contain a doctranslet-main-entry specification."); + } + + return new DocTranslet(docTransletMainEntry, + new JarClassLoader(inputJarFile)); + } + catch (IOException e) { + throw new DocTransletConfigurationException(e); + } + } + + private static String xsltBoolean(boolean b) { + return b ? "1" : ""; + } + + public void error (TransformerException exception) + throws TransformerException { + + throw exception; + } + + public void fatalError (TransformerException exception) + throws TransformerException { + + throw exception; + } + + public void warning (TransformerException exception) + throws TransformerException { + + System.err.println("WWW: " + exception.getMessage()); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletConfigurationException.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletConfigurationException.java new file mode 100644 index 000000000..092d0cea7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletConfigurationException.java @@ -0,0 +1,53 @@ +/* gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTransletConfigurationException + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet.doctranslet; + +public class DocTransletConfigurationException extends Exception { + + public DocTransletConfigurationException(String msg) { + super(msg); + } + + public DocTransletConfigurationException(String msg, Throwable cause) { + super(msg, cause); + } + + public DocTransletConfigurationException(Throwable cause) { + super(cause); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletException.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletException.java new file mode 100644 index 000000000..96ffefa0f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletException.java @@ -0,0 +1,63 @@ +/* gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTransletException + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet.doctranslet; + +public class DocTransletException extends Exception { + + public DocTransletException(String msg) { + super(msg); + } + + private void initCauseDynamic(Throwable cause) + { + try { + getClass().getMethod("initCause", new Class[]{Throwable.class}).invoke(this, new Object[] { cause }); + } + catch (Exception ignore) { + } + } + + public DocTransletException(String msg, Throwable cause) { + super(msg); + initCauseDynamic(cause); + } + + public DocTransletException(Throwable cause) { + initCauseDynamic(cause); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletOptions.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletOptions.java new file mode 100644 index 000000000..5c99ec3bd --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/DocTransletOptions.java @@ -0,0 +1,62 @@ +/* gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTransletOptions + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet.doctranslet; + +/** + * Value class for carrying command line options which need to be + * passed through to the stylesheets. + */ +public class DocTransletOptions +{ + public boolean nonavbar; + public boolean noindex; + public boolean notree; + public boolean nocomment; + public boolean nohelp; + public boolean splitindex; + public boolean linksource; + public boolean nodeprecatedlist; + public boolean uses; + public String windowtitle = ""; + public String helpfile = ""; + public String stylesheetfile = ""; + public String header = ""; + public String footer = ""; + public String bottom = ""; + public String doctitle = ""; +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/JarClassLoader.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/JarClassLoader.java new file mode 100644 index 000000000..b7b100be1 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/JarClassLoader.java @@ -0,0 +1,91 @@ +/* gnu.classpath.tools.doclets.xmldoclet.doctranslet.JarClassLoader + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet.doctranslet; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import java.util.jar.JarFile; +import java.util.jar.JarEntry; + +public class JarClassLoader extends ClassLoader { + + private JarFile jarFile; + + public JarClassLoader(JarFile jarFile) { + this.jarFile = jarFile; + } + + public Class findClass(String name) + throws ClassNotFoundException { + + byte[] b = loadClassData(name); + return defineClass(name, b, 0, b.length); + } + + private byte[] loadClassData(String className) + throws ClassNotFoundException + { + String classFileName = className.replace('.', File.separatorChar) + ".class"; + + try { + JarEntry jarEntry = jarFile.getJarEntry(classFileName); + if (null != jarEntry) { + return readFromStream(jarFile.getInputStream(jarEntry), + jarEntry.getSize()); + } + } + catch (IOException ignore_) { + } + throw new ClassNotFoundException(className); + } + + private byte[] readFromStream(InputStream in, long size) + throws IOException + { + byte[] result = new byte[(int)size]; + int nread = 0; + int offset = 0; + while (offset < size && (nread = in.read(result, offset, (int)(size - offset))) >= 0) { + offset += nread; + } + in.close(); + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/OutputFileInfo.java b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/OutputFileInfo.java new file mode 100644 index 000000000..b0ede615d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/doclets/xmldoclet/doctranslet/OutputFileInfo.java @@ -0,0 +1,66 @@ +/* gnu.classpath.tools.doclets.xmldoclet.doctranslet.OutputFileInfo + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.doclets.xmldoclet.doctranslet; + +/** + * Holds information about a file to be generated by the DocTranslet. + */ +public class OutputFileInfo { + + private String name; + private String source; + private String sheet; + private String comment; + private String info; + + public OutputFileInfo(String name, String source, String sheet, String comment, String info) { + this.name = name; + this.source = source; + this.sheet = sheet; + this.comment = comment; + this.info = info; + } + + public String getName() { return name; } + public String getSource() { return source; } + public String getSheet() { return sheet; } + public String getComment() { return comment; } + public String getInfo() { return info; } + + public String toString() { return "OutputFileInfo{name="+name+",source="+source+",sheet="+sheet+",comment="+comment+",info="+info+"}"; } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/getopt/FileArgumentCallback.java b/libjava/classpath/tools/gnu/classpath/tools/getopt/FileArgumentCallback.java new file mode 100644 index 000000000..53a4f3d1d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/getopt/FileArgumentCallback.java @@ -0,0 +1,62 @@ +/* FileArgumentCallback.java - handle non-option command line arguments + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.getopt; + +/** + * This is a callback class which is used when a "file name" is found by the + * command-line parser. A file name is any command-line argument which does not + * start with a dash and which is not the argument of some preceding option. + */ +public abstract class FileArgumentCallback +{ + /** + * Create a new instance. + */ + protected FileArgumentCallback() + { + } + + /** + * This is called when a file argument is seen. + * + * @param fileArgument the file name + */ + public abstract void notifyFile(String fileArgument) + throws OptionException; +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/getopt/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/getopt/Messages.java new file mode 100644 index 000000000..19f101743 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/getopt/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- i18n support for getopt + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.getopt; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.getopt.Messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/getopt/Option.java b/libjava/classpath/tools/gnu/classpath/tools/getopt/Option.java new file mode 100644 index 000000000..e7b5d82e7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/getopt/Option.java @@ -0,0 +1,266 @@ +/* Option.java - represent a command-line option + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.getopt; + +/** + * This is the base class representing an option. An option can have a short + * form. This is a single character, like '-x'. An option can have a long form, + * like '--verbose'; if the parser is working in "long option only" mode, then a + * long flag has a single dash, like '-verbose'. Both a long and a short form + * may be specified; it is not valid to have neither. A description is mandatory + * for options; this is used to automatically generate '--help' output. An option + * which takes an argument and which has a short form can also be "joined", in + * this case the option's argument can either be separated, like "-I path" or + * joined with the short option name, like "-Ipath". + */ +public abstract class Option +{ + private char shortName; + + private String longName; + + private String description; + + private String argumentName; + + private boolean joined; + + /** + * Create a new option with the given short name and description. + * + * @param shortName the short name + * @param description the description + */ + protected Option(char shortName, String description) + { + if (shortName == 0) + throw new IllegalArgumentException("short name must not be \\0"); + this.shortName = shortName; + this.description = description; + } + + /** + * Create a new option with the given short name and description. + * + * @param shortName the short name + * @param description the description + * @param argumentName the descriptive name of the argument, if this option + * takes an argument; otherwise null + */ + protected Option(char shortName, String description, String argumentName) + { + if (shortName == 0) + throw new IllegalArgumentException("short name must not be \\0"); + this.shortName = shortName; + this.description = description; + this.argumentName = argumentName; + } + + /** + * Create a new option with the given short name and description. + * + * @param shortName the short name + * @param description the description + * @param argumentName the descriptive name of the argument, if this option + * takes an argument; otherwise null + * @param joined true if the short option is joined to its argument + */ + protected Option(char shortName, String description, String argumentName, + boolean joined) + { + if (shortName == 0) + throw new IllegalArgumentException("short name must not be \\0"); + this.shortName = shortName; + this.description = description; + this.argumentName = argumentName; + this.joined = joined; + } + + /** + * Create a new option with the given long name and description. The long name + * should be specified without any leading dashes. + * + * @param longName the long name + * @param description the description + */ + protected Option(String longName, String description) + { + this.longName = longName; + this.description = description; + } + + /** + * Create a new option with the given long name and description. The long name + * should be specified without any leading dashes. + * + * @param longName the long name + * @param description the description + * @param argumentName the descriptive name of the argument, if this option + * takes an argument; otherwise null + */ + protected Option(String longName, String description, String argumentName) + { + this.longName = longName; + this.description = description; + this.argumentName = argumentName; + } + + /** + * Create a new option with the given short and long names and description. + * The long name should be specified without any leading dashes. + * + * @param longName the long name + * @param shortName the short name + * @param description the description + */ + protected Option(String longName, char shortName, String description) + { + if (shortName == 0) + throw new IllegalArgumentException("short name must not be \\0"); + this.shortName = shortName; + this.longName = longName; + this.description = description; + } + + /** + * Create a new option with the given short and long names and description. + * The long name should be specified without any leading dashes. + * + * @param longName the long name + * @param shortName the short name + * @param description the description + * @param argumentName the descriptive name of the argument, if this option + * takes an argument; otherwise null + */ + protected Option(String longName, char shortName, String description, + String argumentName) + { + if (shortName == 0) + throw new IllegalArgumentException("short name must not be \\0"); + this.shortName = shortName; + this.longName = longName; + this.argumentName = argumentName; + this.description = description; + } + + /** + * Create a new option with the given short and long names and description. + * The long name should be specified without any leading dashes. + * + * @param longName the long name + * @param shortName the short name + * @param description the description + * @param argumentName the descriptive name of the argument, if this option + * takes an argument; otherwise null + * @param joined true if the short option is joined to its argument + */ + protected Option(String longName, char shortName, String description, + String argumentName, boolean joined) + { + if (shortName == 0) + throw new IllegalArgumentException("short name must not be \\0"); + this.shortName = shortName; + this.longName = longName; + this.argumentName = argumentName; + this.description = description; + this.joined = joined; + } + + /** + * Return the short name of the option, or \0 if none. + */ + public char getShortName() + { + return shortName; + } + + /** + * Return the long name of the option, or null if none. + */ + public String getLongName() + { + return longName; + } + + /** + * Return true if the argument takes an option. + */ + public boolean getTakesArgument() + { + return argumentName != null; + } + + /** + * Return the name of the argument. If the option does not take an argument, + * returns null. + */ + public String getArgumentName() + { + return argumentName; + } + + /** + * Return the description of the option. + */ + public String getDescription() + { + return description; + } + + /** + * Return true if this is a "joined" option, false otherwise. + * Only the short form of an option can be joined; this will always + * return false for an option which does not have a short form. + */ + public boolean isJoined() + { + return joined; + } + + /** + * This is called by the parser when this option is recognized. It may be + * called multiple times during a single parse. If this option takes an + * argument, the argument will be passed in. Otherwise the argument will be + * null. + * + * @param argument the argument + * @throws OptionException if the option or its argument is somehow invalid + */ + public abstract void parsed(String argument) throws OptionException; +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/getopt/OptionException.java b/libjava/classpath/tools/gnu/classpath/tools/getopt/OptionException.java new file mode 100644 index 000000000..2d7f77a55 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/getopt/OptionException.java @@ -0,0 +1,58 @@ +/* OptionException.java - when command-line processing fails + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.getopt; + +/** + * An OptionException is thrown internally when an error is seen when parsing a + * command line. + */ +public class OptionException + extends Exception +{ + public OptionException(String message) + { + super(message); + } + + public OptionException(String message, Throwable cause) + { + super(message, cause); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/getopt/OptionGroup.java b/libjava/classpath/tools/gnu/classpath/tools/getopt/OptionGroup.java new file mode 100644 index 000000000..83fcca0f8 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/getopt/OptionGroup.java @@ -0,0 +1,268 @@ +/* OptionGroup.java - a group of related command-line options + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.getopt; + +import java.io.PrintStream; +import java.text.BreakIterator; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Locale; + +/** + * An option group holds a collection of Options. It also has a name. Option + * groups are primarily useful for grouping help output. + */ +public class OptionGroup +{ + /** An 80-character string of whitespaces to use as a source for padding. */ + private static final String FILLER = " " + + " "; + private String name; + + ArrayList options = new ArrayList(); + + /** + * Create a new nameless option group. This can only be used by Parser. + */ + OptionGroup() + { + } + + /** + * Create a new option group with the indicated name. + * + * @param name the name + */ + public OptionGroup(String name) + { + this.name = name; + } + + /** + * Print a designated text to a {@link PrintStream}, eventually wrapping the + * lines of text so as to ensure that the width of each line does not overflow + * {@link Parser#MAX_LINE_LENGTH} columns. The line-wrapping is done with a + * {@link BreakIterator} using the default {@link Locale}. + * <p> + * The text to print may contain <code>\n</code> characters. This method will + * force a line-break for each such character. + * + * @param out the {@link PrintStream} destination of the formatted text. + * @param text the text to print. + * @param leftMargin a positive value indicating the column position of the + * start of the first line. Continuation lines, if they exist, are + * printed starting at <code>leftMargin + 2</code> as per GNU + * convention. + * @see Parser#MAX_LINE_LENGTH + */ + protected static void formatText(PrintStream out, String text, int leftMargin) + { + formatText(out, text, leftMargin, Locale.getDefault()); + } + + /** + * Similar to the method with the same name and three arguments, except that + * the caller MUST specify a non-null {@link Locale} instance. + * <p> + * Print a designated text to a {@link PrintStream}, eventually wrapping the + * lines of text so as to ensure that the width of each line does not overflow + * {@link Parser#MAX_LINE_LENGTH} columns. The line-wrapping is done with a + * {@link BreakIterator} using the designated {@link Locale}. + * <p> + * The text to print may contain <code>\n</code> characters. This method will + * force a line-break for each such character. + * + * @param out the {@link PrintStream} destination of the formatted text. + * @param text the text to print. + * @param leftMargin a positive value indicating the column position of the + * start of the first line. Continuation lines, if they exist, are + * printed starting at <code>leftMargin + 2</code> as per GNU + * convention. + * @param aLocale the {@link Locale} instance to use when constructing the + * {@link BreakIterator}. + * @see Parser#MAX_LINE_LENGTH + */ + protected static void formatText(PrintStream out, String text, int leftMargin, + Locale aLocale) + { + BreakIterator bit = BreakIterator.getLineInstance(aLocale); + String[] lines = text.split("\n"); + int length = leftMargin; + String leftPadding = FILLER.substring(0, leftMargin + 2); + for (int i = 0; i < lines.length; i++) + { + text = lines[i]; + bit.setText(text); + int start = bit.first(); + int finish; + while ((finish = bit.next()) != BreakIterator.DONE) + { + String word = text.substring(start, finish); + length += word.length(); + if (length >= Parser.MAX_LINE_LENGTH) + { + out.println(); + out.print(leftPadding); + length = word.length() + leftMargin + 2; + } + out.print(word); + start = finish; + } + out.println(); + if (i != lines.length - 1) + { + length = leftMargin + 2; + out.print(leftPadding); + } + } + } + + /** + * Add an option to this option group. + * + * @param opt the option to add + */ + public void add(Option opt) + { + options.add(opt); + } + + /** + * Print the help output for this option group. + * + * @param out the stream to which to print + */ + public void printHelp(PrintStream out, boolean longOnly) + { + // Compute maximum lengths. + int maxArgLen = 0; + boolean shortOptionSeen = false; + Iterator it; + + // The first pass only looks to see if we have a short option. + it = options.iterator(); + while (it.hasNext()) + { + Option option = (Option) it.next(); + if (option.getShortName() != '\0') + { + shortOptionSeen = true; + break; + } + } + + it = options.iterator(); + while (it.hasNext()) + { + Option option = (Option) it.next(); + String argName = option.getArgumentName(); + // First compute the width required for the short + // option. "2" is the initial indentation. In the + // GNU style we don't print an argument name for + // a short option if there is also a long name for + // the option. + int thisArgLen = 2; + if (shortOptionSeen) + thisArgLen += 4; + if (option.getLongName() != null) + { + // Handle either '-' or '--'. + thisArgLen += 1 + option.getLongName().length(); + if (! longOnly) + ++thisArgLen; + } + // Add in the width of the argument name. + if (argName != null) + thisArgLen += 1 + argName.length(); + maxArgLen = Math.max(maxArgLen, thisArgLen); + } + + // Print the help. + if (name != null) + out.println(name + ":"); + it = options.iterator(); + while (it.hasNext()) + { + Option option = (Option) it.next(); + String argName = option.getArgumentName(); + int column = 0; + if (option.getShortName() != '\0') + { + out.print(" -"); + out.print(option.getShortName()); + column += 4; + if (option.getLongName() == null) + { + if (argName != null) + { + if (! option.isJoined()) + { + out.print(' '); + ++column; + } + out.print(argName); + column += argName.length(); + } + out.print(" "); + } + else + out.print(", "); + column += 2; + } + // Indent the long option past the short options, if one + // was seen. + for (; column < (shortOptionSeen ? 6 : 2); ++column) + out.print(' '); + if (option.getLongName() != null) + { + out.print(longOnly ? "-" : "--"); + out.print(option.getLongName()); + column += (longOnly ? 1 : 2) + option.getLongName().length(); + if (argName != null) + { + out.print(" " + argName); + column += 1 + argName.length(); + } + } + // FIXME: should have a better heuristic for padding. + out.print(FILLER.substring(0, maxArgLen + 4 - column)); + formatText(out, option.getDescription(), maxArgLen + 4); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/getopt/Parser.java b/libjava/classpath/tools/gnu/classpath/tools/getopt/Parser.java new file mode 100644 index 000000000..f23250eca --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/getopt/Parser.java @@ -0,0 +1,495 @@ +/* Parser.java - parse command line options + Copyright (C) 2006, 2008 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.getopt; + +import java.io.PrintStream; +import java.text.BreakIterator; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Locale; + +/** + * An instance of this class is used to parse command-line options. It does "GNU + * style" argument recognition and also automatically handles "--help" and + * "--version" processing. It can also be put in "long option only" mode. In + * this mode long options are recognized with a single dash (as well as a double + * dash) and strings of options like "-abc" are never parsed as a collection of + * short options. + */ +public class Parser +{ + /** The maximum right column position. */ + public static final int MAX_LINE_LENGTH = 80; + + private String programName; + + private String headerText; + + private String footerText; + + private boolean longOnly; + + // All of the options. This is null initially; users must call + // requireOptions before access. + private ArrayList options; + + private ArrayList optionGroups = new ArrayList(); + + private OptionGroup defaultGroup = new OptionGroup(); + + private OptionGroup finalGroup; + + // These are used while parsing. + private int currentIndex; + + private String[] args; + + /** + * Create a new parser. The program name is used when printing error messages. + * The version string is printed verbatim in response to "--version". + * + * @param programName the name of the program + * @param versionString the program's version information + */ + public Parser(String programName, String versionString) + { + this(programName, versionString, false); + } + + /** + * Print a designated text to a {@link PrintStream}, eventually wrapping the + * lines of text so as to ensure that the width of each line does not overflow + * {@link #MAX_LINE_LENGTH} columns. The line-wrapping is done with a + * {@link BreakIterator} using the default {@link Locale}. + * <p> + * The text to print may contain <code>\n</code> characters. This method will + * force a line-break for each such character. + * + * @param out the {@link PrintStream} destination of the formatted text. + * @param text the text to print. + * @see Parser#MAX_LINE_LENGTH + */ + protected static void formatText(PrintStream out, String text) + { + formatText(out, text, Locale.getDefault()); + } + + /** + * Similar to the method with the same name and two arguments, except that the + * caller MUST specify a non-null {@link Locale} instance. + * <p> + * Print a designated text to a {@link PrintStream}, eventually wrapping the + * lines of text so as to ensure that the width of each line does not overflow + * {@link #MAX_LINE_LENGTH} columns. The line-wrapping is done with a + * {@link BreakIterator} using the designated {@link Locale}. + * <p> + * The text to print may contain <code>\n</code> characters. This method will + * force a line-break for each such character. + * + * @param out the {@link PrintStream} destination of the formatted text. + * @param text the text to print. + * @param aLocale the {@link Locale} instance to use when constructing the + * {@link BreakIterator}. + * @see Parser#MAX_LINE_LENGTH + */ + protected static void formatText(PrintStream out, String text, Locale aLocale) + { + BreakIterator bit = BreakIterator.getLineInstance(aLocale); + String[] lines = text.split("\n"); //$NON-NLS-1$ + for (int i = 0; i < lines.length; i++) + { + text = lines[i]; + bit.setText(text); + int length = 0; + int finish; + int start = bit.first(); + while ((finish = bit.next()) != BreakIterator.DONE) + { + String word = text.substring(start, finish); + length += word.length(); + if (length >= MAX_LINE_LENGTH) + { + out.println(); + length = word.length(); + } + out.print(word); + start = finish; + } + out.println(); + } + } + + /** + * Create a new parser. The program name is used when printing error messages. + * The version string is printed verbatim in response to "--version". + * + * @param programName the name of the program + * @param versionString the program's version information + * @param longOnly true if the parser should work in long-option-only mode + */ + public Parser(String programName, final String versionString, boolean longOnly) + { + this.programName = programName; + this.longOnly = longOnly; + + // Put standard options in their own section near the end. + finalGroup = new OptionGroup(Messages.getString("Parser.StdOptions")); //$NON-NLS-1$ + finalGroup.add(new Option("help", Messages.getString("Parser.PrintHelp")) //$NON-NLS-1$ //$NON-NLS-2$ + { + public void parsed(String argument) throws OptionException + { + printHelp(System.out); + System.exit(0); + } + }); + finalGroup.add(new Option("version", Messages.getString("Parser.PrintVersion")) //$NON-NLS-1$ //$NON-NLS-2$ + { + public void parsed(String argument) throws OptionException + { + System.out.println(versionString); + System.exit(0); + } + }); + add(finalGroup); + + add(defaultGroup); + } + + /** + * Set the header text that is printed by --help. + * + * @param headerText the header text + */ + public synchronized void setHeader(String headerText) + { + this.headerText = headerText; + } + + /** + * Set the footer text that is printed by --help. + * + * @param footerText the footer text + */ + public synchronized void setFooter(String footerText) + { + this.footerText = footerText; + } + + /** + * Add an option to this parser. The option is added to the default option + * group; this affects where it is placed in the help output. + * + * @param opt the option + */ + public synchronized void add(Option opt) + { + defaultGroup.add(opt); + } + + /** + * This is like {@link #add(Option)}, but adds the option to the "final" + * group. This should be used sparingly, if at all; it is intended for + * other very generic options like --help or --version. + * @param opt the option to add + */ + protected synchronized void addFinal(Option opt) + { + finalGroup.add(opt); + } + + /** + * Add an option group to this parser. All the options in this group will be + * recognized by the parser. + * + * @param group the option group + */ + public synchronized void add(OptionGroup group) + { + // This ensures that the final group always appears at the end + // of the options. + if (optionGroups.isEmpty()) + optionGroups.add(group); + else + optionGroups.add(optionGroups.size() - 1, group); + } + + // Make sure the 'options' field is properly initialized. + private void requireOptions() + { + if (options != null) + return; + options = new ArrayList(); + Iterator it = optionGroups.iterator(); + while (it.hasNext()) + { + OptionGroup group = (OptionGroup) it.next(); + options.addAll(group.options); + } + } + + public void printHelp() + { + this.printHelp(System.out); + } + + synchronized void printHelp(PrintStream out) + { + requireOptions(); + + if (headerText != null) + { + formatText(out, headerText); + out.println(); + } + + Iterator it = optionGroups.iterator(); + while (it.hasNext()) + { + OptionGroup group = (OptionGroup) it.next(); + // An option group might be empty, in which case we don't + // want to print it.. + if (! group.options.isEmpty()) + { + group.printHelp(out, longOnly); + out.println(); + } + } + + if (footerText != null) + formatText(out, footerText); + } + + /** + * This method can be overridden by subclassses to provide some option + * validation. It is called by the parser after all options have been + * parsed. If an option validation problem is encountered, this should + * throw an {@link OptionException} whose message should be shown to + * the user. + * <p> + * It is better to do validation here than after {@link #parse(String[])} + * returns, because the parser will print a message referring the + * user to the <code>--help</code> option. + * <p> + * The base implementation does nothing. + * + * @throws OptionException the error encountered + */ + protected void validate() throws OptionException + { + // Base implementation does nothing. + } + + private String getArgument(String request) throws OptionException + { + ++currentIndex; + if (currentIndex >= args.length) + { + String message + = MessageFormat.format(Messages.getString("Parser.ArgReqd"), //$NON-NLS-1$ + new Object[] { request }); + throw new OptionException(request); + } + return args[currentIndex]; + } + + private void handleLongOption(String real, int index) throws OptionException + { + String option = real.substring(index); + String justName = option; + int eq = option.indexOf('='); + if (eq != -1) + justName = option.substring(0, eq); + boolean isPlainShort = justName.length() == 1; + char shortName = justName.charAt(0); + Option found = null; + for (int i = options.size() - 1; i >= 0; --i) + { + Option opt = (Option) options.get(i); + if (justName.equals(opt.getLongName())) + { + found = opt; + break; + } + if ((isPlainShort || opt.isJoined()) + && opt.getShortName() == shortName) + { + if (! isPlainShort) + { + // The rest of the option string is the argument. + eq = 0; + } + found = opt; + break; + } + } + if (found == null) + { + String msg = MessageFormat.format(Messages.getString("Parser.Unrecognized"), //$NON-NLS-1$ + new Object[] { real }); + throw new OptionException(msg); + } + String argument = null; + if (found.getTakesArgument()) + { + if (eq == -1) + argument = getArgument(real); + else + argument = option.substring(eq + 1); + } + else if (eq != - 1) + { + String msg + = MessageFormat.format(Messages.getString("Parser.NoArg"), //$NON-NLS-1$ + new Object[] { real.substring(0, eq + index) }); + throw new OptionException(msg); + } + found.parsed(argument); + } + + private void handleShortOptions(String option) throws OptionException + { + for (int charIndex = 1; charIndex < option.length(); ++charIndex) + { + char optChar = option.charAt(charIndex); + Option found = null; + for (int i = options.size() - 1; i >= 0; --i) + { + Option opt = (Option) options.get(i); + if (optChar == opt.getShortName()) + { + found = opt; + break; + } + } + if (found == null) + { + String msg = MessageFormat.format(Messages.getString("Parser.UnrecDash"), //$NON-NLS-1$ + new Object[] { "" + optChar }); //$NON-NLS-1$ + throw new OptionException(msg); + } + String argument = null; + if (found.getTakesArgument()) + { + // If this is a joined short option, and there are more + // characters left in this argument, use those as the + // argument. + if (found.isJoined() && charIndex + 1 < option.length()) + { + argument = option.substring(charIndex + 1); + charIndex = option.length(); + } + else + argument = getArgument("-" + optChar); //$NON-NLS-1$ + } + found.parsed(argument); + } + } + + /** + * Parse a command line. Any files which are found will be passed to the file + * argument callback. This method will exit on error or when --help or + * --version is specified. + * + * @param inArgs the command-line arguments + * @param files the file argument callback + */ + public synchronized void parse(String[] inArgs, FileArgumentCallback files) + { + requireOptions(); + try + { + args = inArgs; + for (currentIndex = 0; currentIndex < args.length; ++currentIndex) + { + if (args[currentIndex].length() == 0 + || args[currentIndex].charAt(0) != '-' + || "-".equals(args[currentIndex])) //$NON-NLS-1$ + { + files.notifyFile(args[currentIndex]); + continue; + } + if ("--".equals(args[currentIndex])) //$NON-NLS-1$ + break; + if (args[currentIndex].charAt(1) == '-') + handleLongOption(args[currentIndex], 2); + else if (longOnly) + handleLongOption(args[currentIndex], 1); + else + handleShortOptions(args[currentIndex]); + } + // Add remaining arguments to leftovers. + for (++currentIndex; currentIndex < args.length; ++currentIndex) + files.notifyFile(args[currentIndex]); + // See if something went wrong. + validate(); + } + catch (OptionException err) + { + System.err.println(programName + ": " + err.getMessage()); //$NON-NLS-1$ + String fmt; + if (longOnly) + fmt = Messages.getString("Parser.TryHelpShort"); //$NON-NLS-1$ + else + fmt = Messages.getString("Parser.TryHelpLong"); //$NON-NLS-1$ + String msg = MessageFormat.format(fmt, new Object[] { programName }); + System.err.println(programName + ": " + msg); //$NON-NLS-1$ + System.exit(1); + } + } + + /** + * Parse a command line. Any files which are found will be returned. This + * method will exit on error or when --help or --version is specified. + * + * @param inArgs the command-line arguments + */ + public String[] parse(String[] inArgs) + { + final ArrayList fileResult = new ArrayList(); + parse(inArgs, new FileArgumentCallback() + { + public void notifyFile(String fileArgument) + { + fileResult.add(fileArgument); + } + }); + return (String[]) fileResult.toArray(new String[0]); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/AbstractTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/AbstractTagImpl.java new file mode 100644 index 000000000..adb5b65d5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/AbstractTagImpl.java @@ -0,0 +1,107 @@ +/* gnu.classpath.tools.gjdoc.AbstractTagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; + +public abstract class AbstractTagImpl + implements Tag, TagContainer { + + private static final Tag[] emptyTags = new Tag[0]; + + protected String text; + protected Map tagMap; + + protected AbstractTagImpl(String text) { + this.text=text; + } + + public void resolve() { + Tag[] allTags=inlineTags(); + if (null != allTags) { + for (int i=0; i<allTags.length; ++i) { + if (allTags[i]!=this) ((AbstractTagImpl)allTags[i]).resolve(); + } + } + else { + System.err.println("Null tags for " + this); + } + } + + protected void setBody(String body, ClassDocImpl contextClass, MemberDocImpl contextMember) { + this.tagMap=DocImpl.parseCommentTags(body.toCharArray(), + 0, + body.length(), + contextClass, + contextMember, + this, + null); + } + + public Tag[] firstSentenceTags() { + return (tagMap!=null)? (Tag[])tagMap.get("first") : emptyTags; + } + public Tag[] inlineTags() { + return (tagMap!=null)? (Tag[])tagMap.get("inline") : emptyTags; + } + + public String name() { + return kind(); + } + + public String text() { + return text; + } + + public Map getTagMap() { + return tagMap; + } + + /** + * Debug string containing class, name, text and tagMap. + */ + public String toString() + { + return (this.getClass().getName() + + "[name=" + name() + + ", text=" + text() + + ", tagMap=" + getTagMap() + + "]"); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ArrayCharacterIterator.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ArrayCharacterIterator.java new file mode 100644 index 000000000..cde5cbe90 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ArrayCharacterIterator.java @@ -0,0 +1,121 @@ +/* gnu.classpath.tools.gjdoc.ArrayCharacterIterator + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.text.CharacterIterator; + +public final class ArrayCharacterIterator implements CharacterIterator { + + private char[] data; + private int beginIndex; + private int endIndex; + private int currentIndex; + + public ArrayCharacterIterator(char[] data, + int beginIndex) { + this(data,beginIndex,data.length,beginIndex); + } + + public ArrayCharacterIterator(char[] data, + int beginIndex, + int endIndex) { + this(data,beginIndex,endIndex,beginIndex); + } + + public ArrayCharacterIterator(char[] data, + int beginIndex, + int endIndex, + int currentIndex) { + this.data=data; + this.beginIndex=beginIndex; + this.endIndex=endIndex; + this.currentIndex=currentIndex; + } + + // Create a copy of this iterator + public Object clone() { + return new ArrayCharacterIterator(data,beginIndex,endIndex,currentIndex); + } + + // Gets the character at the current position (as returned by getIndex()). + public char current() { + return (currentIndex>=beginIndex && currentIndex<endIndex) ? data[currentIndex] : DONE; + } + + // Sets the position to getBeginIndex() and returns the character at that position. + public char first() { + return data[currentIndex=beginIndex]; + } + + // Returns the start index of the text. + public int getBeginIndex() { + return beginIndex; + } + + // Returns the end index of the text. + public int getEndIndex() { + return endIndex; + } + + // Returns the current index. + public int getIndex() { + return currentIndex; + } + + // Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty) and returns the character at that position. + public char last() { + return data[currentIndex=((endIndex>beginIndex)?endIndex-1:endIndex)]; + } + + // Increments the iterator's index by one and returns the character at the new index. + public char next() { + return (++currentIndex<endIndex)?data[currentIndex]:DONE; + } + + // Decrements the iterator's index by one and returns the character at the new index. + public char previous() { + return (--currentIndex>=beginIndex)?data[currentIndex]:DONE; + } + + // Sets the position to the specified position in the text and returns that character. + public char setIndex(int position) { + this.currentIndex=position; + return current(); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocImpl.java new file mode 100644 index 000000000..b38c2b083 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocImpl.java @@ -0,0 +1,1260 @@ +/* gnu.classpath.tools.gjdoc.ClassDocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.io.*; +import gnu.classpath.tools.gjdoc.expr.EvaluatorEnvironment; +import gnu.classpath.tools.gjdoc.expr.CircularExpressionException; +import gnu.classpath.tools.gjdoc.expr.IllegalExpressionException; +import gnu.classpath.tools.gjdoc.expr.UnknownIdentifierException; + +public class ClassDocImpl + extends ProgramElementDocImpl + implements ClassDoc, WritableType, EvaluatorEnvironment { + + private ClassDoc baseClassDoc; + private ClassDoc[] importedClasses; + private PackageDoc[] importedPackages; + private boolean definesSerializableFields; + private FieldDoc[] serialPersistentField; + private MethodDoc[] serializationMethods; + private String dimension = ""; + + public ClassDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + int accessLevel, + boolean isFinal, + boolean isStatic, + SourcePosition position) { + super(containingClass, containingPackage, accessLevel, isFinal, isStatic, + position); + this.baseClassDoc = this; + } + + public ClassDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + ClassDoc[] importedClasses, + PackageDoc[] importedPackages, + SourcePosition position) { + super(containingClass, containingPackage, + position); + this.importedClasses=importedClasses; + this.importedPackages=importedPackages; + this.baseClassDoc = this; + } + + // Return constructors in class. + public ConstructorDoc[] constructors() { + return constructors(true); + } + + public ConstructorDoc[] constructors(boolean filter) { + return filter ? filteredConstructors : unfilteredConstructors; + } + + // Return true if Serializable fields are explicitly defined with the special class member serialPersistentFields. + public boolean definesSerializableFields() { + return definesSerializableFields; + } + + // Return fields in class. + public FieldDoc[] fields() { + return fields(true); + } + + public FieldDoc[] fields(boolean filter) { + return filter ? filteredFields : unfilteredFields; + } + + private static Set primitiveNames; + static { + primitiveNames = new HashSet(); + primitiveNames.add("int"); + primitiveNames.add("long"); + primitiveNames.add("char"); + primitiveNames.add("short"); + primitiveNames.add("byte"); + primitiveNames.add("float"); + primitiveNames.add("double"); + primitiveNames.add("boolean"); + } + + private Map findClassCache = new HashMap(); + + public ClassDoc findClass(String className, String dimension) + { + ClassDoc cached = (ClassDoc)findClassCache.get(className + dimension); + if (null != cached) { + return cached; + } + else { + ClassDoc classDoc = findClass(className); + + if (null!=classDoc) { + try { + if (classDoc.dimension().equals(dimension)) { + return classDoc; + } + else { + ClassDoc rc = (ClassDoc) ((WritableType)classDoc).clone(); + ((WritableType)rc).setDimension(dimension); + findClassCache.put(className + dimension, rc); + return rc; + } + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + else { + return null; + } + } + } + + public ClassDoc findClass(String className) + { + String qualifiedName = Main.getRootDoc().resolveClassName(className, this); + ClassDoc rc=Main.getRootDoc().classNamed(qualifiedName); + + if (null == rc) { + for (ClassDoc cdi=this; cdi!=null; cdi=cdi.containingClass()) { + for (ClassDoc sdi=cdi; sdi!=null; sdi=sdi.superclass()) { + if (sdi instanceof ClassDocProxy) { + ClassDoc realClass = Main.getRootDoc().classNamed(sdi.qualifiedName()); + if (null != realClass) { + sdi = realClass; + } + } + rc=Main.getRootDoc().classNamed(sdi.qualifiedName()+"."+className); + if (rc!=null) return rc; + } + } + } + + return rc; + } + + // Get the list of classes declared as imported. + public ClassDoc[] importedClasses() { + return importedClasses; + } + + // Get the list of packages declared as imported. + public PackageDoc[] importedPackages() { + return importedPackages; + } + + // Return inner classes within this class. + public ClassDoc[] innerClasses() { + return innerClasses(true); + } + + public ClassDoc[] innerClasses(boolean filtered) { + return filtered ? filteredInnerClasses : unfilteredInnerClasses; + } + + void setFilteredInnerClasses(ClassDoc[] filteredInnerClasses) { + this.filteredInnerClasses=filteredInnerClasses; + } + + void setInnerClasses(ClassDoc[] unfilteredInnerClasses) { + this.unfilteredInnerClasses=unfilteredInnerClasses; + } + + // Return interfaces implemented by this class or interfaces extended by this interface. + public ClassDoc[] interfaces() { + return interfaces; + } + + public void setInterfaces(ClassDoc[] interfaces) { + this.interfaces=interfaces; + } + + // Return true if this class is abstract + public boolean isAbstract() { + return isAbstract || isInterface(); + } + + public boolean isInterface() { + return isInterface; + } + + public boolean isAnnotation() { + return isAnnotation; + } + + public boolean isEnum() + { + return isEnum; + } + + // Return true if this class is abstract + public void setIsAbstract(boolean b) { + this.isAbstract=b; + } + + // Return true if this class implements java.io.Externalizable. + public boolean isExternalizable() { + return implementsInterface("java.io.Externalizable"); + } + + // Return true if this class implements java.io.Serializable. + public boolean isSerializable() { + return implementsInterface("java.io.Serializable"); + } + + public boolean implementsInterface(String name) { + for (ClassDoc cdi=this; cdi!=null; cdi=(ClassDoc)cdi.superclass()) { + if (cdi instanceof ClassDocImpl) { + ClassDoc[] cdiInterfaces=(ClassDoc[])cdi.interfaces(); + if (null != cdiInterfaces) { + for (int i=0; i<cdiInterfaces.length; ++i) { + if (cdiInterfaces[i].qualifiedName().equals(name)) + return true; + } + } + } + else { + //throw new RuntimeException("implementsInterface(\""+name+"\") failed: Not a ClassDocImpl:"+cdi); + } + } + return false; + } + + // Return methods in class. + public MethodDoc[] methods() { + return methods(true); + } + + // Return methods in class. + public MethodDoc[] methods(boolean filter) { + return filter ? filteredMethods : unfilteredMethods; + } + + // Return the Serializable fields of class. Return either a list of default fields documented by serial tag or return a single FieldDoc for serialPersistentField member. + public FieldDoc[] serializableFields() { + if (serialPersistentField!=null) { + return serialPersistentField; + } + else{ + return serializableFields; + } + } + + // Return the serialization methods for this class. + public MethodDoc[] serializationMethods() { + return serializationMethods; + } + + // Test whether this class is a subclass of the specified class. + public boolean subclassOf(ClassDoc cd) { + for (ClassDocImpl cdi=(ClassDocImpl)superclass(); cdi!=null; cdi=(ClassDocImpl)cdi.superclass()) { + if (cdi.equals(cd)) + return true; + } + return false; + } + + // Return the superclass of this class + public ClassDoc superclass() { + return superclass; + } + + // Implementation of Interface Type + + public ClassDoc asClassDoc() { + + return (ClassDoc)this; + } + + public String typeName() { return name(); } + + public String qualifiedTypeName() { + return (containingPackage()!=null && !containingPackage().equals(PackageDocImpl.DEFAULT_PACKAGE))?(containingPackage().name()+"."+name()):(name()); + } + + public String qualifiedName() { return qualifiedTypeName(); } + + public String dimension() { return dimension; } + + public String toString() { return "ClassDoc{"+qualifiedTypeName()+"}"; } + + public TypeVariable asTypeVariable() { return null; } + + public static ClassDocImpl createInstance(ClassDoc containingClass, + PackageDoc containingPackage, + ClassDoc[] importedClasses, + PackageDoc[] importedPackages, + char[] source, int startIndex, int endIndex, + List importStatementList) throws ParseException, IOException { + + String superclassName = "java.lang.Object"; + + ClassDocImpl rc=new ClassDocImpl(containingClass, + containingPackage, + importedClasses, + importedPackages, + null); + rc.setImportStatementList(importStatementList); + List implementedInterfaces = new ArrayList(); + + String word=""; + int item=0; + + final int STATE_NORMAL = 1; + final int STATE_SLASHC = 2; + final int STATE_STARC = 3; + final int STATE_ANNO = 4; + + int state=STATE_NORMAL; + int varLevel=0; + int parLevel=0; + char prev=0; + for (int ndx=startIndex; ndx<=endIndex; ++ndx) { + char c=(ndx==endIndex)?10:source[ndx]; + boolean processWord=false; + if (state==STATE_SLASHC) { + if (c=='\n') { + state=STATE_NORMAL; + c=0; + } + } + else if (state==STATE_STARC) { + if (c=='/' && prev=='*') { + state=STATE_NORMAL; + c=0; + } + } + else { + if (c=='/' && prev=='/') { + state=STATE_SLASHC; + c=0; + word=word.substring(0,word.length()-1); + processWord=true; + } + else if (c=='*' && prev=='/') { + state=STATE_STARC; + c=0; + word=word.substring(0,word.length()-1); + processWord=true; + } + else if (c=='@') { + state=STATE_ANNO; + word += c; + } + else if (c=='(' && state==STATE_ANNO) { + ++parLevel; + word += c; + } + else if (c==')' && state==STATE_ANNO) { + --parLevel; + word += c; + if (parLevel == 0) + state=STATE_NORMAL; + } + else if (c=='<') + { + ++varLevel; + word += c; + } + else if (c=='>') + { + --varLevel; + word += c; + } + else if (c=='{' && parLevel == 0 || + c==',' && varLevel == 0 && parLevel == 0 || + Parser.WHITESPACE.indexOf(c)>=0 && parLevel == 0 && varLevel == 0) { + processWord=true; + state=STATE_NORMAL; + } + else { + word+=c; + } + + if (processWord && word.length()>0) { + if (item==0) { + if (rc.processModifier(word)) { + } + else if (word.equals("abstract")) { + rc.setIsAbstract(true); + } + else if (word.equals("class")) { + rc.setIsInterface(false); + item=1; + } + else if (word.equals("enum")) + { + rc.setIsEnum(true); + item = 1; + } + else if (word.equals("interface")) { + rc.setIsInterface(true); + item=1; + } + else if (word.equals("@interface")) { + rc.setIsInterface(true); + rc.setIsAnnotation(true); + item=1; + } + else if (word.equals("strictfp")) { + } + else { + Main.getRootDoc().printWarning("unknown modifier '"+word+"'"); + } + } + else if (word.equals("extends") && !rc.isAnnotation()) { + if (rc.isInterface()) { + item=3; + } + else { + item=2; + } + } + else if (word.equals("implements") && !rc.isAnnotation()) { + item=3; + } + else if (item==1) { + int parameterIndex = word.indexOf("<"); + if (parameterIndex == -1) + rc.setClass(word); + else + { + rc.setClass(word.substring(0, parameterIndex)); + parseTypeVariables(rc,word.substring(parameterIndex, + word.length())); + } + } + else if (item==2) { + //Debug.log(9,"setting baseclass of "+rc+" to "+word); + int parameterIndex = word.indexOf("<"); + if (parameterIndex == -1) + superclassName=word; + else + { + /* FIXME: Parse type parameters */ + superclassName=word.substring(0,parameterIndex); + } + } + else if (item==3) { + int parameterIndex = word.indexOf("<"); + if (parameterIndex == -1) + implementedInterfaces.add(word); + else + { + /* FIXME: Parse type parameters */ + implementedInterfaces.add(word.substring(0,parameterIndex)); + } + } + word=""; + } + + if (c=='{' && state==STATE_NORMAL) break; + } + prev=c; + } + + if (null != containingClass + && containingClass.isInterface()) { + rc.accessLevel = ACCESS_PUBLIC; + } + + if (rc.name()==null) { + throw new ParseException("No classdef found in expression \""+new String(source,startIndex,endIndex-startIndex)+"\""); + } + + rc.setPosition(ClassDocImpl.getPosition(rc, source, startIndex)); + + ClassDoc superclassProxy=new ClassDocProxy(superclassName, rc); + + if (!rc.qualifiedName().equals("java.lang.Object")) { + rc.setSuperclass(superclassProxy); + } + + ClassDoc[] interfaces=new ClassDoc[implementedInterfaces.size()]; + for (int i=0; i<interfaces.length; ++i) { + interfaces[i]=new ClassDocProxy((String)implementedInterfaces.get(i), rc); + } + rc.setInterfaces(interfaces); + + if (rc.isInterface() && rc.containingClass()!=null) { + rc.setIsStatic(true); + } + return rc; + } + + public void setFields(FieldDoc[] fields) { + this.unfilteredFields=fields; + } + + public void setFilteredFields(FieldDoc[] fields) { + this.filteredFields=fields; + } + + public void setSerializableFields(FieldDoc[] sfields) { + this.serializableFields=sfields; + } + + public void setMethods(MethodDoc[] methods) { + this.unfilteredMethods=methods; + } + + public void setFilteredMethods(MethodDoc[] methods) { + this.filteredMethods=methods; + } + + public void setConstructors(ConstructorDoc[] constructors) { + this.unfilteredConstructors=constructors; + } + + public void setFilteredConstructors(ConstructorDoc[] constructors) { + this.filteredConstructors=constructors; + } + + // Returns the name of this Doc item. + public String name() { + if (containingClass==null) { + return className; + } + else { + return containingClass.name()+"."+className; + } + } + + public String getClassName() { + return className; + } + + public void setClass(String className) { + this.className=className; + } + + void setSuperclass(ClassDoc superclass) { + this.superclass=superclass; + } + + public void resolve() throws ParseException { + if (!resolved) { + resolved=true; + + if (containingClass!=null) + ((ClassDocImpl)containingClass).resolve(); + + //Debug.log(9,"resolving class '"+qualifiedName()+"'"); + /* + for (int i=0; i<importedPackages.length; ++i) { + Debug.log(9,"class "+qualifiedName()+" imports "+importedPackages[i].name()); + } + */ + + if (superclass instanceof ClassDocProxy) { + + ClassDoc realClassDoc=findClass(superclass.qualifiedName()); + + if (realClassDoc==null) { + /* + if (true) { // Main.recursiveClasses) { + throw new ParseException("In class '"+qualifiedName()+"': class '"+className+"' not found."); + } + */ + } + else { + superclass=realClassDoc; + } + } + + if (null != interfaces) { + for (int i=0; i<interfaces.length; ++i) { + if (interfaces[i] instanceof ClassDocProxy) { + //Debug.log(9,"class "+qualifiedName()+" implements "+interfaces[i].qualifiedName()); + ClassDoc realClassDoc=findClass(interfaces[i].qualifiedName()); + if (realClassDoc==null) { + /* + if (Main.recursiveClasses) { + throw new ParseException("In class '"+qualifiedName()+"': class '"+className+"' not found."); + } + */ + } + else { + //Debug.log(9,"found class '"+className+"': "+interfaces[i]); + interfaces[i]=realClassDoc; + } + } + } + } + + if (unfilteredFields!=null) { + for (int i=0; i<unfilteredFields.length; ++i) { + ((FieldDocImpl)unfilteredFields[i]).resolve(); + if (unfilteredFields[i].name().equals("serialPersistentField")) { + serialPersistentField=new FieldDoc[]{unfilteredFields[i]}; + definesSerializableFields=true; + } + } + } + + if (unfilteredMethods!=null) { + for (int i=0; i<unfilteredMethods.length; ++i) { + ((MethodDocImpl)unfilteredMethods[i]).resolve(); + } + } + + if (unfilteredConstructors!=null) { + for (int i=0; i<unfilteredConstructors.length; ++i) { + ((ConstructorDocImpl)unfilteredConstructors[i]).resolve(); + } + } + + List isSerMethodList=new ArrayList(); + + if (null != maybeSerMethodList) { + for (Iterator it=maybeSerMethodList.iterator(); it.hasNext(); ) { + MethodDocImpl method=(MethodDocImpl)it.next(); + method.resolve(); + + if (((method.name().equals("readObject") + && method.signature().equals("(java.io.ObjectInputStream)")) + || (method.name().equals("writeObject") + && method.signature().equals("(java.io.ObjectOutputStream)")) + || (method.name().equals("readExternal") + && method.signature().equals("(java.io.ObjectInput)")) + || (method.name().equals("writeExternal") + && method.signature().equals("(java.io.ObjectOutput)")) + || (method.name().equals("readResolve") + && method.signature().equals("()")))) { + + isSerMethodList.add(method); + } + } + this.serializationMethods=(MethodDoc[])isSerMethodList.toArray(new MethodDoc[0]); + maybeSerMethodList=null; + } + } + } + + public FieldDoc findFieldRec(String name) { + return findFieldRec(this, name); + } + + private static FieldDoc findFieldRec(ClassDoc classDoc, String name) + { + FieldDoc field = findField(classDoc, name); + if (null!=field) { + return field; + } + else { + ClassDoc[] interfaces = classDoc.interfaces(); + for (int i=0; i<interfaces.length; ++i) { + field = findFieldRec(interfaces[i], name); + if (null != field) { + return field; + } + } + if (null != classDoc.superclass()) { + return findFieldRec(classDoc.superclass(), name); + } + else { + return null; + } + } + } + + private static FieldDoc findField(ClassDoc classDoc, String name) + { + FieldDoc[] fields = classDoc.fields(false); + for (int i=0; i<fields.length; ++i) { + if (fields[i].name().equals(name)) { + return fields[i]; + } + } + return null; + } + + public FieldDoc findField(String fieldName) { + for (int i=0; i<filteredFields.length; ++i) { + if (filteredFields[i].name().equals(fieldName)) { + return filteredFields[i]; + } + } + return null; + } + + public void resolveComments() { + + super.resolveComments(); + + if (null != unfilteredFields) { + for (int i=0; i<unfilteredFields.length; ++i) { + ((FieldDocImpl)unfilteredFields[i]).resolveComments(); + } + } + + if (null != serializableFields) { + for (int i=0; i<serializableFields.length; ++i) { + ((FieldDocImpl)serializableFields[i]).resolveComments(); + } + } + if (null != unfilteredMethods) { + for (int i=0; i<unfilteredMethods.length; ++i) { + ((MethodDocImpl)unfilteredMethods[i]).resolveComments(); + } + } + if (null != unfilteredConstructors) { + for (int i=0; i<unfilteredConstructors.length; ++i) { + ((ConstructorDocImpl)unfilteredConstructors[i]).resolveComments(); + } + } + + resolveTags(); + } + + + private String className=null; + + private boolean isAbstract; + private boolean isInterface; + private boolean isAnnotation; + private boolean isEnum; + private ClassDoc[] interfaces; + private ClassDoc[] filteredInnerClasses; + private ClassDoc[] unfilteredInnerClasses; + private FieldDoc[] filteredFields; + private FieldDoc[] unfilteredFields; + private FieldDoc[] serializableFields; + private MethodDoc[] filteredMethods; + private MethodDoc[] unfilteredMethods; + private ConstructorDoc[] filteredConstructors; + private ConstructorDoc[] unfilteredConstructors; + private TypeVariable[] typeParameters; + + private boolean resolved=false; + + private ClassDoc superclass; + + // Is this Doc item a class. + public boolean isClass() { + return !isInterface; + } + + // return true if this Doc is include in the active set. + public boolean isIncluded() { + if (this == baseClassDoc) { + return isIncluded + || (null != containingClass && Main.getInstance().includeAccessLevel(accessLevel)); + } + else { + return baseClassDoc.isIncluded(); + } + } + + void setIsIncluded(boolean b) { + this.isIncluded=b; + } + + private boolean isIncluded=false; + + void setImportedClasses(ClassDoc[] importedClasses) { + this.importedClasses=importedClasses; + } + + private static Map typeMap = new HashMap(); + + Type typeForString(String typeName) throws ParseException { + String orgTypename=typeName; + int ndx=typeName.indexOf('['); + String dim=""; + if (ndx>=0) { + for (int i=ndx; i<typeName.length(); ++i) { + if ("[]".indexOf(typeName.charAt(i))>=0) { + dim+=typeName.charAt(i); + } + } + typeName=typeName.substring(0,ndx).trim(); + } + + ClassDoc classDoc = findClass(typeName, dim); + if (null != classDoc) { + return classDoc; + } + + Type type = (Type)typeMap.get(typeName+dim); + if (null!=type) { + try { + if (type.dimension().equals(dim)) { + return type; + } + else { + Type rc = (Type) ((WritableType)type).clone(); + ((WritableType)rc).setDimension(dim); + return rc; + } + } + catch (CloneNotSupportedException e) { + throw new ParseException(e.toString()); + } + } + + if ("boolean".equals(typeName) + || "char".equals(typeName) + || "byte".equals(typeName) + || "short".equals(typeName) + || "int".equals(typeName) + || "long".equals(typeName) + || "void".equals(typeName) + || "float".equals(typeName) + || "double".equals(typeName)) { + Type rc=new TypeImpl(null, typeName, dim); + typeMap.put(typeName+dim, rc); + return rc; + } + + if (Main.getInstance().isDocletRunning()) { + //System.err.println(findClass("java.lang.String")); + //throw new ParseException("Doclet running, class not found: "+typeName+" ("+orgTypename+")"); + } + Type rc=new ClassDocProxy(typeName, this); + ((WritableType)rc).setDimension(dim); + return rc; + } + + public boolean isException() { + for (ClassDoc cdi=this; + cdi!=null; + cdi=cdi.superclass()) { + + if ("java.lang.Exception".equals(cdi.qualifiedName())) + return true; + } + return false; + } + + public boolean isError() { + for (ClassDoc cdi=this; cdi!=null; cdi=cdi.superclass()) { + if ("java.lang.Error".equals(cdi.qualifiedName())) + return true; + } + return false; + } + + public boolean isOrdinaryClass() { + return !isException() && !isError() && !isInterface(); + } + + public void setIsInterface(boolean b) { + this.isInterface=b; + } + + public void setIsAnnotation(boolean b) { + this.isAnnotation=b; + } + + public void setIsEnum(boolean b) + { + isEnum = b; + } + + public ExecutableMemberDoc findExecutableRec(String nameAndSignature) { + + ExecutableMemberDoc rc; + for (ClassDoc cdi=this; cdi!=null; ) { + rc=findMethod(cdi, nameAndSignature); + if (rc!=null) return rc; + rc=findConstructor(cdi, nameAndSignature); + if (rc!=null) return rc; + + ClassDoc _superclass = cdi.superclass(); + if (null == _superclass) { + break; + } + else { + cdi = _superclass; + } + } + return null; + } + + public static ConstructorDoc findConstructor(ClassDoc classDoc, String nameAndSignature) { + int ndx=nameAndSignature.indexOf('('); + if (ndx<=0) + return null; + else { + String fullSignature = resolveSignature(classDoc, nameAndSignature.substring(ndx)); + return findConstructor(classDoc, + nameAndSignature.substring(0,ndx), + fullSignature); + } + } + + public static ConstructorDoc findConstructor(ClassDoc classDoc, String name, String signature) { + ConstructorDoc[] filteredConstructors = classDoc.constructors(true); + if (null != filteredConstructors) { + for (int i=0; i<filteredConstructors.length; ++i) { + ConstructorDoc constructor = filteredConstructors[i]; + if (constructor.name().equals(name) && constructor.signature().equals(signature)) + return constructor; + } + } + return null; + } + + public static MethodDoc findMethod(ClassDoc classDoc, String nameAndSignature) { + int ndx=nameAndSignature.indexOf('('); + if (ndx<=0) { + return null; + } + else { + String name = nameAndSignature.substring(0,ndx); + String fullSignature = resolveSignature(classDoc, nameAndSignature.substring(ndx)); + return findMethod(classDoc, name, fullSignature); + } + } + + private static String resolveSignature(ClassDoc classDoc, String signature) + { + signature = signature.substring(1, signature.length() - 1).trim(); + if (0 == signature.length()) { + return "()"; + } + StringTokenizer st = new StringTokenizer(signature, ","); + StringBuffer fullSignature = new StringBuffer("("); + while (st.hasMoreTokens()) { + String type = st.nextToken().trim(); + int ndx = type.length(); + while (ndx > 0 && type.charAt(ndx - 1) == '[' || type.charAt(ndx - 1) == ']') { + -- ndx; + } + String dim = type.substring(ndx); + type = type.substring(0, ndx); + ClassDoc typeClass = classDoc.findClass(type); + if (fullSignature.length() > 1) { + fullSignature.append(","); + } + if (null != typeClass) { + fullSignature.append(typeClass.qualifiedName()); + } + else { + fullSignature.append(type); + } + fullSignature.append(dim); + } + fullSignature.append(')'); + return fullSignature.toString(); + } + + public static MethodDoc findMethod(ClassDoc classDoc, String name, String signature) { + MethodDoc[] filteredMethods = classDoc.methods(true); + if (null != filteredMethods) { + for (int i=0; i<filteredMethods.length; ++i) { + MethodDoc method = filteredMethods[i]; + if (method.name().equals(name) && method.signature().equals(signature)) + return method; + } + } + return null; + } + + public boolean equals(Object o) { + return (o!=null) && (o instanceof ClassDoc) && ((ClassDoc)o).qualifiedName().equals(qualifiedName()); + } + + private List maybeSerMethodList; + + void setMaybeSerMethodList(List maybeSerMethodList) { + this.maybeSerMethodList=maybeSerMethodList; + } + + public void setDimension(String dimension) { + this.dimension = dimension; + } + + public Object clone() throws CloneNotSupportedException { + ClassDocImpl result = (ClassDocImpl)super.clone(); + result.baseClassDoc = baseClassDoc; + return result; + } + + public int superHashCode() + { + return super.hashCode(); + } + + public int hashCode() + { + return qualifiedTypeName().hashCode(); + } + + public ClassDoc getBaseClassDoc() + { + return baseClassDoc; + } + + public FieldDoc getFieldDoc(String name) + { + for (int i=0; i<unfilteredFields.length; ++i) { + if (name.equals(unfilteredFields[i].name())) { + return unfilteredFields[i]; + } + } + return null; + } + + public MethodDoc getMethodDoc(String name, String signature) + { + for (int i=0; i<unfilteredMethods.length; ++i) { + if (name.equals(unfilteredMethods[i].name()) + && signature.equals(unfilteredMethods[i].signature())) { + return unfilteredMethods[i]; + } + } + return null; + } + + + public ConstructorDoc getConstructorDoc(String signature) + { + for (int i=0; i<unfilteredConstructors.length; ++i) { + if (signature.equals(unfilteredConstructors[i].signature())) { + return unfilteredConstructors[i]; + } + } + return null; + } + + private Object findFieldValue(String identifier, + ClassDoc classDoc, + String fieldName, + Set visitedFields) + throws UnknownIdentifierException, IllegalExpressionException + { + while (classDoc != null) { + if (classDoc instanceof ClassDocImpl) { + FieldDocImpl fieldDoc + = (FieldDocImpl)((ClassDocImpl)classDoc).getFieldDoc(fieldName); + if (visitedFields.contains(fieldDoc)) { + throw new CircularExpressionException("Circular reference detected"); + } + else if (null != fieldDoc) { + return fieldDoc.constantValue(visitedFields); + } + } + else { + ClassDoc[] _interfaces = classDoc.interfaces(); + if (null != _interfaces) { + for (int i=0; i<_interfaces.length; ++i) { + if (_interfaces[i] instanceof ClassDocImpl) { + FieldDocImpl fieldDoc + = (FieldDocImpl)((ClassDocImpl)_interfaces[i]).getFieldDoc(fieldName); + if (visitedFields.contains(fieldDoc)) { + throw new CircularExpressionException("Circular reference detected"); + } + else if (null != fieldDoc) { + return fieldDoc.constantValue(visitedFields); + } + } + } + } + } + classDoc = classDoc.superclass(); + } + throw new UnknownIdentifierException(identifier); + } + + public Object getValue(String identifier, Set visitedFields) + throws UnknownIdentifierException, IllegalExpressionException + { + int ndx = identifier.lastIndexOf('.'); + if (ndx >= 0) { + String _className = identifier.substring(0, ndx); + String _fieldName = identifier.substring(ndx + 1); + + ClassDoc _classDoc = findClass(_className); + if (null != _classDoc) { + return findFieldValue(identifier, _classDoc, _fieldName, visitedFields); + } + else { + throw new UnknownIdentifierException(identifier); + } + } + else { + return findFieldValue(identifier, this, identifier, visitedFields); + } + } + + public boolean isPrimitive() + { + return false; + } + + // Compares this Object with the specified Object for order. + public int compareTo(java.lang.Object o) { + int rc; + + if (o instanceof ClassDocImpl) { + + ClassDocImpl c1 = this; + ClassDocImpl c2 = (ClassDocImpl)o; + + if (null != c1.containingClass() && null == c2.containingClass()) { + rc = c1.containingClass().compareTo(c2); + if (0 == rc) { + rc = 1; + } + return rc; + } + else if (null == c1.containingClass() && null != c2.containingClass()) { + rc = c1.compareTo(c2.containingClass()); + if (0 == rc) { + rc = -1; + } + return rc; + } + else if (null != c1.containingClass() && null != c2.containingClass()) { + rc = c1.containingClass().compareTo(c2.containingClass()); + if (0 != rc) { + return rc; + } + } + + rc = super.compareTo(o); + if (0 == rc) { + return Main.getInstance().getCollator().compare(containingPackage().name(), + ((ClassDocImpl)o).containingPackage().name()); + } + else { + return rc; + } + } + else { + return 1; + } + } + + private List importStatementList; + + public void setImportStatementList(List importStatementList) + { + this.importStatementList = new LinkedList(); + this.importStatementList.addAll(importStatementList); + } + + public List getImportSpecifierList() + { + return importStatementList; + } + + public TypeVariable[] typeParameters() + { + return typeParameters; + } + + /** + * <p> + * Parses the type variables declared in the class definition. + * The syntax is: + * </p> + * <p> + * <dl> + * <dt>TypeParameters:</dt> + * <dd><code>< <em>TypeParameter</em> { <em>, TypeParameter }</code></dd> + * <dt>TypeParameter:</dt> + * <dd><code><em>Identifier</em> { <strong>extends</strong> <em>Bound</em> + * }</dd> + * <dt>Bound:</dt> + * <dd><code><em>Type</em>{<strong>&</strong> <em>Type</em> } </dd> + * </dl> + * + * @param rc the owning class. + * @param typeVariables the string to be parsed. + * @throws ParseException if parsing fails. + */ + public static void parseTypeVariables(ClassDocImpl rc, + String typeVariables) + throws ParseException + { + List parsedBounds = null; + StringTokenizer parameters = new StringTokenizer(typeVariables, + Parser.WHITESPACE + + "<>,"); + List variables = new ArrayList(); + while (parameters.hasMoreTokens()) + { + String parameter = parameters.nextToken(); + StringTokenizer parts = new StringTokenizer(parameter, + Parser.WHITESPACE); + TypeVariableImpl variable = new TypeVariableImpl(rc.qualifiedName(), + parts.nextToken(),"", + rc); + if (parts.hasMoreTokens()) + { + if (!parts.nextToken().equals("extends")) + throw new ParseException("Invalid type parameter: " + parameter); + StringTokenizer bounds = new StringTokenizer(parts.nextToken(), + Parser.WHITESPACE + + "&"); + parsedBounds = new ArrayList(); + while (bounds.hasMoreTokens()) + { + String bound = bounds.nextToken(); + int nameSep = bound.lastIndexOf("."); + String packageName = bound.substring(0, nameSep); + String boundName = bound.substring(nameSep, bound.length()); + parsedBounds.add(new TypeImpl(packageName,boundName,"")); + } + } + if (parsedBounds != null) + variable.setBounds(parsedBounds); + variables.add(variable); + } + rc.setTypeParameters(variables); + } + + /** + * Set the type parameters to the contents of the supplied list. + * + * @param variables a list of type parameters. + */ + void setTypeParameters(List variables) + { + typeParameters = + (TypeVariable[]) variables.toArray(new TypeVariable[variables.size()]); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocProxy.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocProxy.java new file mode 100644 index 000000000..253cf5ec4 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocProxy.java @@ -0,0 +1,169 @@ +/* gnu.classpath.tools.gjdoc.ClassDocProxy + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; + +import java.io.*; + +public class ClassDocProxy implements ClassDoc, WritableType { + + private String name; + private String qualifiedName; + private ClassDoc classContext; + private String dimension = ""; + + public ClassDocProxy(String qualifiedName, ClassDoc classContext) + { + this.qualifiedName + = Main.getRootDoc().resolveClassName(qualifiedName, + (ClassDocImpl)classContext); + this.classContext=classContext; + int pndx=qualifiedName.lastIndexOf('.'); + if (pndx>=0) { + this.name=qualifiedName.substring(pndx+1); + } + else { + this.name=qualifiedName; + } + } + + private final String errorText() + { + return "CLASS "+qualifiedName+" NOT LOADED."; + } + + public ConstructorDoc[] constructors() { return new ConstructorDoc[0]; } + public ConstructorDoc[] constructors(boolean filtered) { return new ConstructorDoc[0]; } + public boolean definesSerializableFields() { return false; } + public FieldDoc[] fields() { return new FieldDoc[0]; } + public FieldDoc[] fields(boolean filtered) { return new FieldDoc[0]; } + public ClassDoc findClass(java.lang.String className) { return null; } + public ClassDoc[] importedClasses() { return new ClassDoc[0]; } + public PackageDoc[] importedPackages() { return new PackageDoc[0]; } + public ClassDoc[] innerClasses() { return new ClassDoc[0]; } + public ClassDoc[] innerClasses(boolean filtered) { return new ClassDoc[0]; } + public ClassDoc[] interfaces() { return new ClassDoc[0]; } + public boolean isAbstract() { return false; } + public boolean isExternalizable() { return false; } + public boolean isSerializable() { return false; } + public MethodDoc[] methods() { return new MethodDoc[0]; } + public MethodDoc[] methods(boolean filtered) { return new MethodDoc[0]; } + public FieldDoc[] serializableFields() { return new FieldDoc[0]; } + public MethodDoc[] serializationMethods() { return new MethodDoc[0]; } + public boolean subclassOf(ClassDoc cd) { return false; } + public ClassDoc superclass() { return null; } + public ClassDoc containingClass() { return null; } + public PackageDoc containingPackage() { + /* + try { + File file=Main.getRootDoc().findScheduledClass(qualifiedName, classContext); + if (file!=null) { + //String clsName=file.getCanonicalFile().getAbsolutePath().substring(new File(Main.getRootDoc().getSourcePath()).getCanonicalFile().getAbsolutePath().length()+1); + String clsName=file.getAbsolutePath().substring(new File(Main.getRootDoc().getSourcePath()).getAbsolutePath().length()+1); + clsName=clsName.substring(0,clsName.length()-5).replace(File.separatorChar,'.'); + Debug.log(9,"ClassDocProxy '"+qualifiedName+"': found class "+clsName); + qualifiedName=clsName; + } + return new PackageDocImpl("test."); + } + catch (Exception e) { + return PackageDocImpl.DEFAULT_PACKAGE; + } + */ + return PackageDocImpl.DEFAULT_PACKAGE; + } + + public boolean isFinal() { return false; } + public boolean isPackagePrivate() { return false; } + public boolean isPrivate() { return false; } + public boolean isProtected() { return false; } + public boolean isPublic() { return false; } + public boolean isStatic() { return false; } + public String modifiers() { return ""; } + public int modifierSpecifier() { return 0; } + public String qualifiedName() { return qualifiedName; } + public String commentText() { return null; } + public Tag[] firstSentenceTags() { return new Tag[0]; } + public String getRawCommentText() { return null; } + public Tag[] inlineTags() { return new Tag[0]; } + public boolean isClass() { return false; } + public boolean isConstructor() { return false; } + public boolean isError() { return false; } + public boolean isException() { return false; } + public boolean isField() { return false; } + public boolean isIncluded() { return false; } + public boolean isInterface() { return false; } + public boolean isMethod() { return false; } + public boolean isOrdinaryClass() { return false; } + public String name() { return name; } + public SourcePosition position() { return null; } + public SeeTag[] seeTags() { return new SeeTag[0]; } + public void setRawCommentText(java.lang.String rawDocumentation) {} + public Tag[] tags() { return new Tag[0]; } + public Tag[] tags(java.lang.String tagname) { return new Tag[0]; } + public String typeName() { return name; } + public String qualifiedTypeName() { return qualifiedName; } + public String dimension() { return dimension; } + public ClassDoc asClassDoc() { return this; } + public TypeVariable asTypeVariable() { return null; } + public boolean isPrimitive() { return false; } + + public String toString() { return "ClassDocProxy{"+qualifiedName+", context="+classContext+"}"; } + + public void setDimension(String dimension) { + this.dimension = dimension; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + // Compares this Object with the specified Object for order. + public int compareTo(java.lang.Object o) { + if (o instanceof Doc) { + return Main.getInstance().getCollator().compare(name(), ((Doc)o).name()); + } + else { + return 0; + } + } + + public TypeVariable[] typeParameters() { return new TypeVariable[0]; } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocReflectedImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocReflectedImpl.java new file mode 100644 index 000000000..9a81cb793 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ClassDocReflectedImpl.java @@ -0,0 +1,219 @@ +/* gnu.classpath.tools.gjdoc.ClassDocReflectedImpl + Copyright (C) 2001 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.Map; +import java.util.HashMap; + +public class ClassDocReflectedImpl + implements ClassDoc, WritableType +{ + private Class clazz; + private String name; + private ClassDoc superclassDoc; + private ClassDoc[] unfilteredInnerClasses; + private String dimension = ""; + + private static Map reflectionCache = new HashMap(); + + public static ClassDocReflectedImpl newInstance(Class clazz) + { + ClassDocReflectedImpl result + = (ClassDocReflectedImpl)reflectionCache.get(clazz); + if (null != result) { + return result; + } + else { + return new ClassDocReflectedImpl(clazz); + } + } + + public ClassDocReflectedImpl(Class clazz) + { + reflectionCache.put(clazz, this); + + //System.err.println("ClassDocReflectedImpl: " + clazz); + + this.clazz = clazz; + String className = clazz.getName(); + int ndx = className.lastIndexOf('.'); + if (ndx >= 0) { + this.name = className.substring(ndx + 1); + } + else { + this.name = className; + } + + Class superclass = clazz.getSuperclass(); + if (null != superclass && !clazz.getName().equals("java.lang.Object")) { + this.superclassDoc = (ClassDocReflectedImpl)reflectionCache.get(superclass); + if (null == this.superclassDoc) { + this.superclassDoc = new ClassDocReflectedImpl(superclass); + } + } + + Class[] innerclasses = clazz.getDeclaredClasses(); + this.unfilteredInnerClasses = new ClassDoc[innerclasses.length]; + for (int i=0; i<innerclasses.length; ++i) { + this.unfilteredInnerClasses[i] = (ClassDocReflectedImpl)reflectionCache.get(innerclasses[i]); + if (null == this.unfilteredInnerClasses[i]) { + this.unfilteredInnerClasses[i] = new ClassDocReflectedImpl(innerclasses[i]); + //System.err.println("adding " + this.unfilteredInnerClasses[i] + " [" + innerclasses[i] + "] as inner class of " + this + " [" + clazz + "]"); + } + } + } + + public ConstructorDoc[] constructors() { return new ConstructorDoc[0]; } + public ConstructorDoc[] constructors(boolean filtered) { return new ConstructorDoc[0]; } + public boolean definesSerializableFields() { return false; } + public FieldDoc[] fields() { return new FieldDoc[0]; } + public FieldDoc[] fields(boolean filtered) { return new FieldDoc[0]; } + public ClassDoc findClass(java.lang.String className) { return null; } + public ClassDoc[] importedClasses() { return new ClassDoc[0]; } + public PackageDoc[] importedPackages() { return new PackageDoc[0]; } + public ClassDoc[] innerClasses() { return new ClassDoc[0]; } + public ClassDoc[] innerClasses(boolean filtered) + { + if (filtered) { + return new ClassDoc[0]; + } + else { + return unfilteredInnerClasses; + } + } + + public ClassDoc[] interfaces() { return new ClassDoc[0]; } + public boolean isAbstract() { return false; } + public boolean isExternalizable() { return false; } + public boolean isSerializable() { return false; } + public MethodDoc[] methods() { return new MethodDoc[0]; } + public MethodDoc[] methods(boolean filtered) { return new MethodDoc[0]; } + public FieldDoc[] serializableFields() { return new FieldDoc[0]; } + public MethodDoc[] serializationMethods() { return new MethodDoc[0]; } + public boolean subclassOf(ClassDoc cd) { return false; } + public ClassDoc superclass() { + return superclassDoc; + } + public ClassDoc containingClass() + { + Class declaringClass = clazz.getDeclaringClass(); + if (null != declaringClass) { + return new ClassDocReflectedImpl(declaringClass); + } + else { + return null; + } + } + public PackageDoc containingPackage() + { + Class outerClass = clazz; + while (null != outerClass.getDeclaringClass()) { + outerClass = outerClass.getDeclaringClass(); + } + + String packageName = outerClass.getName(); + int ndx = packageName.lastIndexOf('.'); + if (ndx > 0) { + packageName = packageName.substring(0, ndx); + } + else { + packageName = ""; + } + PackageDoc result = Main.getRootDoc().findOrCreatePackageDoc(packageName); + return result; + } + + public boolean isFinal() { return false; } + public boolean isPackagePrivate() { return false; } + public boolean isPrivate() { return false; } + public boolean isProtected() { return false; } + public boolean isPublic() { return false; } + public boolean isStatic() { return false; } + public String modifiers() { return ""; } + public int modifierSpecifier() { return 0; } + public String qualifiedName() { return clazz.getName().replace('$', '.'); } + public String commentText() { return null; } + public Tag[] firstSentenceTags() { return new Tag[0]; } + public String getRawCommentText() { return null; } + public Tag[] inlineTags() { return new Tag[0]; } + public boolean isClass() { return false; } + public boolean isConstructor() { return false; } + public boolean isError() { return false; } + public boolean isException() { return false; } + public boolean isField() { return false; } + public boolean isIncluded() { return false; } + public boolean isInterface() { return false; } + public boolean isMethod() { return false; } + public boolean isOrdinaryClass() { return false; } + public String name() { return name; } + public SourcePosition position() { return null; } + public SeeTag[] seeTags() { return new SeeTag[0]; } + public void setRawCommentText(java.lang.String rawDocumentation) {} + public Tag[] tags() { return new Tag[0]; } + public Tag[] tags(java.lang.String tagname) { return new Tag[0]; } + public String typeName() { return name; } + public String qualifiedTypeName() { return qualifiedName(); } + public ClassDoc asClassDoc() { return this; } + public TypeVariable asTypeVariable() { return null; } + public boolean isPrimitive() { return false; } + + public String toString() { return "ClassDocReflectedImpl{"+qualifiedName()+"}"; } + + public int compareTo(java.lang.Object o) { + if (o instanceof Doc) { + return Main.getInstance().getCollator().compare(name(), ((Doc)o).name()); + } + else { + return 0; + } + } + + public String dimension() { return dimension; } + + public void setDimension(String dimension) { + this.dimension = dimension; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + public TypeVariable[] typeParameters() { return new TypeVariable[0]; } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ConstructorDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ConstructorDocImpl.java new file mode 100644 index 000000000..ad31ba69e --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ConstructorDocImpl.java @@ -0,0 +1,59 @@ +/* gnu.classpath.tools.gjdoc.ConstructorDocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.util.*; +import com.sun.javadoc.*; + +public class ConstructorDocImpl extends ExecutableMemberDocImpl implements ConstructorDoc { + + public ConstructorDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + SourcePosition position) { + + super(containingClass, + containingPackage, + position); + } + + // Is this Doc item a constructor. + public boolean isConstructor() { + return true; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Debug.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Debug.java new file mode 100644 index 000000000..d5020236c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Debug.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.Debug + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +public final class Debug { + + //private static int logLevel = 7; + private static int logLevel = 0; + + static { + String llProp = System.getProperty("gnu.classpath.tools.gjdoc.LogLevel"); + if (null!=llProp) logLevel = Integer.parseInt(llProp); + } + + public static final void log(String msg) { + System.err.println(msg); + } + + public static final void log(int level, String msg) { + if (level<=logLevel) { + System.err.println(msg); + } + } + + public static final void dumpArray(int level, Object[] array) { + if (level<=logLevel) { + for (int i=0; i<array.length; ++i) { + System.err.println(" #"+i+": "+array[i]); + } + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/DirectoryTree.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/DirectoryTree.java new file mode 100644 index 000000000..706e9b6cc --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/DirectoryTree.java @@ -0,0 +1,74 @@ +/* gnu.classpath.tools.gjdoc.DirectoryTree + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.io.*; + +public class DirectoryTree { + + class FileNode { + File file; + FileNode[] subNodes; + + FileNode(File file) { + this.file=file; + if (file.isDirectory()) { + File[] subFiles=file.listFiles(); + subNodes=new FileNode[subFiles.length]; + for (int i=0; i<subFiles.length; ++i) { + subNodes[i]=new FileNode(subFiles[i]); + } + } + } + } + + FileNode root; + + DirectoryTree(File path) { + + System.err.print("Scanning "+path.getAbsolutePath()+"... "); + + long now1=System.currentTimeMillis(); + + root=new FileNode(path); + + long now2=System.currentTimeMillis(); + + System.err.println("took "+(now2-now1)+" ms"); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/DocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/DocImpl.java new file mode 100644 index 000000000..ecd810040 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/DocImpl.java @@ -0,0 +1,1090 @@ +/* gnu.classpath.tools.gjdoc.DocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.text.*; +import java.io.File; +import javax.swing.text.Segment; + +/** + * Represents the least common denominator of all Javadoc + * comment classes. + */ +public abstract class DocImpl implements Doc, TagContainer { + + protected static Tag[] seeTagEmptyArr = new SeeTagImpl[0]; + protected static Tag[] linkTagEmptyArr = new LinkTagImpl[0]; + protected static Tag[] paramTagEmptyArr = new ParamTagImpl[0]; + protected static Tag[] throwsTagEmptyArr = new ThrowsTagImpl[0]; + protected SourcePosition position; + private String boilerplateComment; + + // Return the text of the comment for this doc item. + public String commentText() { + + StringBuffer rc=new StringBuffer(); + + Tag[] textTags=(Tag[])tagMap.get("text"); + if (textTags!=null) { + for (int i=0; i<textTags.length; ++i) { + rc.append(textTags[i].text()); + } + } + return rc.toString(); + } + + // Compares this Object with the specified Object for order. + public int compareTo(java.lang.Object o) { + return Main.getInstance().getCollator().compare(name(), ((Doc)o).name()); + } + + // Return the first sentence of the comment as tags. + public Tag[] firstSentenceTags() { + + Tag[] rc=(Tag[])tagMap.get("first"); + if (rc==null) rc=new Tag[0]; + return rc; + } + + // Return the full unprocessed text of the comment. + public String getRawCommentText() { + if (rawDocumentation!=null) + return rawDocumentation; + else if (rawDocOffset>=0) + return Main.getRootDoc().readRawComment(rawDocOffset); + else + return null; + } + + // Return comment as tags. + public Tag[] inlineTags() { + + Tag[] rc=(Tag[])tagMap.get("inline"); + if (rc==null) rc=new Tag[0]; + return rc; + } + + // Is this Doc item a class. + public boolean isClass() { + return false; + } + + // Is this Doc item a constructor? False until overridden. + public boolean isConstructor() { + return false; + } + + // Is this Doc item a error class? False until overridden. + public boolean isError() { + return false; + } + + // Is this Doc item a exception class? False until overridden. + public boolean isException() { + return false; + } + + // Is this Doc item a field? False until overridden. + public boolean isField() { + return false; + } + + // return true if this Doc is include in the active set. + public boolean isIncluded() { + return false; + } + + // Is this Doc item a interface? False until overridden. + public boolean isInterface() { + return false; + } + + // Is this Doc item a simple method (i.e. + public boolean isMethod() { + return false; + } + + public boolean isPackage() { + return false; + } + + // Is this Doc item a ordinary class (i.e. + public boolean isOrdinaryClass() { + return false; + } + + // Return the see also tags in this Doc item. + public SeeTag[] seeTags() { + return (SeeTag[])getTagArr("see", seeTagEmptyArr); + } + + protected Tag[] getTagArr(String kindOfTag, Tag[] defaultRc) { + Tag[] rc=(Tag[])tagMap.get(kindOfTag); + if (rc==null) rc=defaultRc; + return rc; + } + + // Set the full unprocessed text of the comment. + public void setRawCommentText(String rawDocumentation) { + this.rawDocumentation=rawDocumentation; + } + + public void resolveComments() { + + if (rawDocumentation!=null && tagMap.isEmpty()) { + char[] charArray = rawDocumentation.toCharArray(); + int length = rawDocumentation.length(); + int startOffset = 0; + int endOffset = 0; + if (charArray[0] == '/' + && charArray[1] == '*' + && charArray[2] == '*' + && charArray[length - 2] == '*' + && charArray[length - 1] == '/') { + + startOffset = 3; + endOffset = 2; + } + + this.tagMap=parseCommentTags(charArray, + startOffset, + length - endOffset, + getContextClass(), + getContextMember(), + null, + boilerplateComment); + + if (Main.getInstance().isCacheRawComments()) { + rawDocOffset=Main.getRootDoc().writeRawComment(rawDocumentation); + rawDocumentation=null; + } + + resolveTags(); + } + else if (tagMap.isEmpty() && null != boilerplateComment) { + tagMap.put("all", new Tag[] { new TagImpl("@boilerplate", boilerplateComment,getContextClass(),null) }); + tagMap.put("@boilerplate", new Tag[] { new TagImpl("@boilerplate", boilerplateComment,getContextClass(),null) }); + } + } + + public static int skipHtmlWhitespace(char[] buffer, int startIndex) { + while (startIndex < buffer.length) { + char c=buffer[startIndex]; + if (!Parser.isWhitespace(c)) { + break; + } + else { + ++ startIndex; + } + } + return startIndex; + } + + /** + * Looks for an end-of-sentence marker in <code>text</code>, + * starting at <code>startIndex</code> and stopping at + * <code>endIndex</code>. + * + * @param text the text to be searched + * @param startIndex index in <code>text</code> at which to start + * @param endIndex index in <code>text</code> at which to stop + * + * @return the index of the character following the end-of-sentence + * marker, <code>endIndex</code> if no end-of-sentence + * marker could be found, or -1 if not implemented. + */ + private static int findEndOfSentence(char[] text, int startIndex, + int endIndex) + { + if (Main.getInstance().isUseBreakIterator()) { + Segment segment = new Segment(text, startIndex, endIndex - startIndex); + BreakIterator breakIterator = BreakIterator.getSentenceInstance(Main.getInstance().getLocale()); + breakIterator.setText(segment); + int result = breakIterator.next(); + if (BreakIterator.DONE == result) { + return endIndex; + } + else { + return result; + } + } + else { + while (startIndex < endIndex) { + if (text[startIndex] == '.' + && (startIndex+1 == endIndex + || Character.isWhitespace(text[startIndex+1]) + || isHTMLBreakTag(text, startIndex+1, endIndex) + )) { + return startIndex; + } + + startIndex++; + } + return endIndex; + } + } + + /** + * Returns true is the text from start to end begins with a 'p' or 'br' tag. + */ + private static boolean isHTMLBreakTag(char[] text, int start, int end) + { + String[] breakTags = { + "p>", "/p>", "h1>", "h2>", "h3>", "h4>", "h5>", "h6>", "hr>", + "pre>", "/pre>" + }; + + if (text[start] == '<') { + + outer: + for (int i=0; i<breakTags.length; ++i) { + String tag = breakTags[i]; + int len = tag.length(); + if (start + len < end) { + for (int j=0; j<len; ++j) { + char c = tag.charAt(j); + if (Character.toLowerCase(text[start + 1 + j]) != c) { + continue outer; + } + } + return true; + } + } + } + return false; + } + + //private static final StringBuffer buf=new StringBuffer(32768); + private static final StringBuffer whitespaceBuf=new StringBuffer(); + private static char[] charBuf = new char[60000]; + private static int bufPos = 0; + + private static void appendToBuf(char c) + { + if (bufPos < charBuf.length) { + charBuf[bufPos++] = c; + } + else { + // + } + } + + private static void appendToBuf(StringBuffer s) + { + if (bufPos + s.length() <= charBuf.length) { + s.getChars(0, s.length(), charBuf, bufPos); + bufPos += s.length(); + } + else { + // + } + } + + private static void setBufLength(int length) + { + bufPos = 0; + } + + private static String bufToString() + { + return new String(charBuf, 0, bufPos); + } + + private static int bufLength() + { + return bufPos; + } + + public static Map parseCommentTags(char[] comment, int startIndex, int endIndex, + ClassDocImpl contextClass, MemberDocImpl contextMember, + AbstractTagImpl contextTag, String boilerplateComment) { + + int rawDocStart=skipHtmlWhitespace(comment, startIndex); + + int firstSentenceEnd = 0; + + if (comment.length>rawDocStart) { + + firstSentenceEnd = findEndOfSentence(comment, rawDocStart, comment.length); + + if (firstSentenceEnd < 0) { + BreakIterator boundary = BreakIterator.getSentenceInstance(Locale.ENGLISH); + boundary.setText(new ArrayCharacterIterator(comment, rawDocStart)); + boundary.first(); + boundary.next(); + firstSentenceEnd = boundary.current(); + } + + // Always include period at end of sentence if there is one. + if (firstSentenceEnd < comment.length + && '.' == comment[firstSentenceEnd]) { + ++ firstSentenceEnd; + } + } + + final int STATE_BEGOFLINE = 1; + final int STATE_TEXT = 2; + final int STATE_PARAM = 3; + final int STATE_PARAMVALUE = 4; + final int STATE_PARAMWRAP = 5; + final int STATE_INLINEPARAM = 6; + final int STATE_INLINEPARAMVALUE = 7; + final int STATE_WHITESPACE = 8; + final int STATE_INLINEPARAMVALUE_BOL = 9; + final int STATE_IPV_WHITESPACE = 10; + + int state=STATE_BEGOFLINE; + int prevState=STATE_TEXT; + + setBufLength(0); + whitespaceBuf.setLength(0); + + String paramName="", paramValue=""; + + Map tags=new HashMap(); + tags.put("inline", new LinkedList()); + tags.put("first", new LinkedList()); + tags.put("all", new LinkedList()); + + final char EOL=(char)-1; + + for (int i=rawDocStart; i<=endIndex; ++i) { + char c=(i<endIndex)?comment[i]:EOL; + char peek=(i<endIndex-1)?comment[i+1]:EOL; + + switch (state){ + + case STATE_BEGOFLINE: + if (i==firstSentenceEnd) { + AbstractTagImpl newTag = addTag(tags, "text", bufToString(), true, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + setBufLength(0); + } + + if (Parser.isWhitespace(c)) { + // ignore + } + else if (c=='*') { + // ignore, but go to STATE_TEXT + if (peek!='*' && peek!='@' && peek!=EOL) { + state=STATE_WHITESPACE; + } + } + else if (c=='@' || (c=='{' && peek=='@') || c==EOL) { + if (bufLength()>0) { + addTag(tags, "text", bufToString(), i<firstSentenceEnd, contextClass, contextMember, contextTag, false); + setBufLength(0); + } + if (c=='{') { + ++i; + state=STATE_INLINEPARAM; + } + else { + state=STATE_PARAM; + } + } + else { + state=STATE_TEXT; + appendToBuf(whitespaceBuf); + whitespaceBuf.setLength(0); + appendToBuf(c); + } + break; + + case STATE_WHITESPACE: + if (i==firstSentenceEnd) { + AbstractTagImpl newTag = addTag(tags, "text", bufToString(), true, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + setBufLength(0); + } + + if (c=='\n') { + whitespaceBuf.append(c); + state=STATE_BEGOFLINE; + } + else if (Parser.isWhitespace(c)) { + whitespaceBuf.append(c); + } + else if (c=='@' || (c=='{' && peek=='@') || c==EOL) { + if (bufLength()>0) { + AbstractTagImpl newTag = addTag(tags, "text", bufToString(), i<firstSentenceEnd, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + setBufLength(0); + } + if (c=='{') { + ++i; + state=STATE_INLINEPARAM; + } + else { + state=STATE_PARAM; + } + } + else { + appendToBuf(whitespaceBuf); + whitespaceBuf.setLength(0); + appendToBuf(c); + state=STATE_TEXT; + } + break; + + case STATE_PARAMWRAP: + if (c=='\n') { + appendToBuf(c); + } + else if (Parser.isWhitespace(c)) { + // ignore + } + else if (c=='*') { + // ignore, but go to STATE_TEXT + /* + if (i<endIndex && comment[i+1]!='*' && comment[i+1]!='@') { + state=STATE_PARAMVALUE; + } + */ + } + else if (c=='@' || c==EOL) { + paramValue=bufToString(); + AbstractTagImpl newTag = addTag(tags, paramName, paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + setBufLength(0); + if (c=='{') { + ++i; + state=STATE_INLINEPARAM; + } + else { + state=STATE_PARAM; + } + } + else { + state=STATE_PARAMVALUE; + appendToBuf(c); + } + break; + + case STATE_PARAM: + if (!(c==EOL || Parser.isWhitespace(c))) { + appendToBuf(c); + } + else if (c=='\n') { + paramName=bufToString(); + setBufLength(0); + state=STATE_PARAMWRAP; + } + else { + paramName=bufToString(); + setBufLength(0); + state=STATE_PARAMVALUE; + } + break; + + case STATE_INLINEPARAM: + if (c=='}') { + // tag without value + paramName=bufToString(); + AbstractTagImpl newTag = addTag(tags, paramName, "", i<firstSentenceEnd, contextClass, contextMember, contextTag, true); + if (null != newTag) { + contextTag = newTag; + } + state=prevState; + setBufLength(0); + } + else if (!(c==EOL || Parser.isWhitespace(c))) { + appendToBuf(c); + } + else if (c=='\n') { + paramName=bufToString(); + setBufLength(0); + state=STATE_INLINEPARAMVALUE_BOL; + } + else { + paramName=bufToString(); + setBufLength(0); + state=STATE_INLINEPARAMVALUE; + } + break; + + case STATE_PARAMVALUE: + if (c==EOL) { + paramValue=bufToString(); + AbstractTagImpl newTag = addTag(tags, paramName, paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + } + else if (c=='\n') { + appendToBuf(c); + state=STATE_PARAMWRAP; + } + else { + appendToBuf(c); + } + break; + + case STATE_INLINEPARAMVALUE: + if (c=='\n') { + appendToBuf(c); + state=STATE_INLINEPARAMVALUE_BOL; + } + else if (c==EOL || c=='}') { + paramValue=bufToString(); + AbstractTagImpl newTag = addTag(tags, paramName, paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, true); + if (null != newTag) { + contextTag = newTag; + } + state=prevState; + setBufLength(0); + } + else { + appendToBuf(c); + } + break; + + case STATE_INLINEPARAMVALUE_BOL: + if (Parser.isWhitespace(c)) { + // ignore + } + else if (c=='*') { + // ignore, but go to STATE_TEXT + if (i<endIndex && peek!='*') { + state=STATE_IPV_WHITESPACE; + } + } + else if (c==EOL) { + if (bufLength()>0) { + AbstractTagImpl newTag = addTag(tags, "text", bufToString(), i<firstSentenceEnd, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + } + } + else { + state=STATE_INLINEPARAMVALUE; + appendToBuf(whitespaceBuf); + whitespaceBuf.setLength(0); + appendToBuf(c); + } + break; + + case STATE_IPV_WHITESPACE: + if (c=='\n') { + whitespaceBuf.append(c); + state=STATE_INLINEPARAMVALUE_BOL; + } + else if (Parser.isWhitespace(c)) { + whitespaceBuf.append(c); + } + else if (c==EOL) { + if (bufLength()>0) { + AbstractTagImpl newTag = addTag(tags, "text", bufToString(), i<firstSentenceEnd, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + setBufLength(0); + } + } + else { + appendToBuf(whitespaceBuf); + whitespaceBuf.setLength(0); + appendToBuf(c); + state=STATE_INLINEPARAMVALUE; + } + break; + + case STATE_TEXT: + if (i==firstSentenceEnd) { + AbstractTagImpl newTag = addTag(tags, "text", bufToString(), true, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + setBufLength(0); + } + + if (c==EOL) { + paramValue=bufToString(); + AbstractTagImpl newTag = addTag(tags, "text", paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + } + else if (c=='\n') { + appendToBuf(c); + state=STATE_BEGOFLINE; + } + else if (c=='{' && peek=='@') { + paramValue=bufToString(); + AbstractTagImpl newTag = addTag(tags, "text", paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, false); + if (null != newTag) { + contextTag = newTag; + } + ++i; + setBufLength(0); + state=STATE_INLINEPARAM; + } + else { + appendToBuf(c); + } + break; + + default: + throw new Error("illegal state "+state); + } + } + + + if (null == contextMember && null != boilerplateComment && Main.getInstance().isCopyLicenseText()) { + addTag(tags, "@boilerplate", boilerplateComment, false, contextClass, null, null, false); + } + + Map rc=new HashMap(); + + for (Iterator it=tags.keySet().iterator(); it.hasNext(); ) { + String key=(String)it.next(); + Tag[] templateArr; + List list=(List)tags.get(key); + + if ("see".equals(key)) + templateArr=new SeeTag[list.size()]; + else if ("param".equals(key)) + templateArr=new ParamTag[list.size()]; + else if ("serialField".equals(key)) + templateArr=new SerialFieldTag[list.size()]; + else if ("throws".equals(key) || "exception".equals(key)) + templateArr=new ThrowsTag[list.size()]; + else { + templateArr=new Tag[list.size()]; + } + + rc.put(key, list.toArray(templateArr)); + } + + return rc; + } + + private ClassDocImpl getContextClass() { + if (isClass() || isInterface()) { + return (ClassDocImpl)this; + } + else if (isField() || isMethod() || isConstructor()) { + return (ClassDocImpl)((MemberDocImpl)this).containingClass(); + } + else { + return null; + } + } + + private MemberDocImpl getContextMember() { + if (isField() || isMethod() || isConstructor()) { + return (MemberDocImpl)this; + } + else { + return null; + } + } + + protected static AbstractTagImpl addTag(Map tags, String name, + String value, boolean isFirstSentence, + ClassDocImpl contextClass, + MemberDocImpl contextMember, + AbstractTagImpl contextTag, + boolean isInline) { + + AbstractTagImpl tag = null; + + boolean haveValue = (0 != value.trim().length()); + + String emptyWarning = "Empty @" + name + " tag."; + + if (name.equals("param")) { + if (haveValue) { + tag=new ParamTagImpl(value, contextClass, contextMember); + } + else { + //printWarning(emptyWarning); + } + } + else if (name.equals("see")) { + if (haveValue) { + tag=new SeeTagImpl(value, contextClass); + } + else { + //printWarning(emptyWarning); + } + } + else if (name.equals("link") || name.equals("linkplain")) { + if (haveValue) { + tag=new LinkTagImpl("@" + name, value, contextClass); + isInline = true; + } + else { + //printWarning(emptyWarning); + } + } + else if (name.equals("value")) { + if (haveValue) { + tag=new ValueTagImpl(value, contextClass); + isInline = true; + } + else { + //printWarning(emptyWarning); + } + } + else if (name.equals("inheritDoc")) { + if (haveValue) { + //printWarning("@inheritDoc tags are not supposed to have any content."); + } + tag=new InheritDocTagImpl(contextClass, contextMember, contextTag); + isInline = true; + } + else if (name.equals("serialField")) { + if (haveValue) { + tag=new SerialFieldTagImpl(value, contextClass, contextMember); + } + else { + //printWarning(emptyWarning); + } + } + else if (name.equals("throws") || name.equals("exception")) { + if (haveValue) { + tag=new ThrowsTagImpl(value, contextClass, contextMember); + } + else { + //printWarning(emptyWarning); + } + name="throws"; + } + else if (name.equals("text")) { + tag=new TextTagImpl(value); + isInline = true; + } + else { + tag=new TagImpl("@"+name, value.trim(), contextClass, contextMember); + // FIXME: consider taglets + } + + if (tag != null) { + if (isInline) { + ((List)tags.get("inline")).add(tag); + if (isFirstSentence) { + if (name.equals("text")) { + String txt = ((TextTagImpl)tag).getText(); + Tag newTag; + if (txt.startsWith("<p>")) { + newTag = new TextTagImpl(txt.substring(3)); + } + else if (txt.endsWith("</p>")) { + newTag = new TextTagImpl(txt.substring(0, txt.length() - 4)); + } + else { + newTag = tag; + } + ((List)tags.get("first")).add(newTag); + + } + else { + ((List)tags.get("first")).add(tag); + } + } + } + else { + ((List)tags.get("all")).add(tag); + } + + List l=((List)tags.get(name)); + if (l==null) { + l=new LinkedList(); + tags.put(name,l); + } + l.add(tag); + + return isInline ? tag : contextTag; + } + else { + return null; + } + } + + // Return all tags in this Doc item. + public Tag[] tags() { + Tag[] rc=(Tag[])tagMap.get("all"); + if (rc==null) rc=new Tag[0]; + return rc; + } + + // Return tags of the specified kind in this Doc item. + public Tag[] tags(java.lang.String tagname) { + Tag[] rc=(Tag[])tagMap.get(tagname); + if (rc==null) rc=new Tag[0]; + return rc; + } + + protected String rawDocumentation; + protected long rawDocOffset=-1; + + protected Map tagMap = new HashMap(); + + public Map getTagMap() { return tagMap; } + + protected void resolveTags() { + + Tag[] tags=tags(); + for (int i=0; i<tags.length; ++i) { + ((AbstractTagImpl)tags[i]).resolve(); + } + + Tag[] inlineTags=inlineTags(); + for (int i=0; i<inlineTags.length; ++i) { + ((AbstractTagImpl)inlineTags[i]).resolve(); + } + } + + private static Map classDocToFileMap = new HashMap(); + + private static File getFile(ClassDoc classDoc) { + File result = (File)classDocToFileMap.get(classDoc); + if (null == result) { + result = new File(((GjdocPackageDoc)classDoc.containingPackage()).packageDirectory(), + classDoc.name() + ".java"); + classDocToFileMap.put(classDoc, result); + } + return result; + } + + public static SourcePosition getPosition(ClassDoc classDoc) + { + return new SourcePositionImpl(getFile(classDoc), 0, 0); + } + + public static SourcePosition getPosition(ClassDoc classDoc, char[] source, int startIndex) + { + int column = 0; + int line = 0; + for (int i=0; i<startIndex; ++i) { + if (10 == source[i]) { + ++ line; + column = 0; + } + else if (13 != source[i]) { + ++ column; + } + } + while (true) { + ClassDoc containingClassDoc = classDoc.containingClass(); + if (null != containingClassDoc) { + classDoc = containingClassDoc; + } + else { + break; + } + } + + File file = getFile(classDoc); + + return new SourcePositionImpl(file, line + 1, column + 1); + } + + public SourcePosition position() + { + return this.position; + } + + public DocImpl(SourcePosition position) + { + this.position = position; + } + + public void setPosition(SourcePosition position) + { + this.position = position; + } + + private static TagContainer checkForInheritedDoc(ClassDoc classDoc, + MemberDocImpl memberDoc, + AbstractTagImpl tag) + { + DocImpl result; + + if (!(classDoc instanceof ClassDocImpl)) { + result = null; + } + else if (null == memberDoc) { + result = (DocImpl)classDoc; + } + else if (memberDoc.isField()) { + result = (DocImpl)((ClassDocImpl)classDoc).getFieldDoc(memberDoc.name()); + } + else if (memberDoc.isMethod()) { + result = (DocImpl)((ClassDocImpl)classDoc).getMethodDoc(memberDoc.name(), + ((MethodDoc)memberDoc).signature()); + } + else if (memberDoc.isConstructor()) { + result = (DocImpl)((ClassDocImpl)classDoc).getConstructorDoc(((ConstructorDoc)memberDoc).signature()); + } + else { + //assert(false); + throw new RuntimeException("memberDoc is supposed to be field, method or constructor"); + } + + if (null != result + && null != memberDoc + && null != tag) { + + TagContainer tagDoc = null; + + Tag[] tags = result.tags(); + for (int i=0; i<tags.length; ++i) { + if (tags[i].kind().equals(tag.kind())) { + if ("@param".equals(tag.kind())) { + if (((ParamTagImpl)tags[i]).parameterName().equals(((ParamTagImpl)tag).parameterName())) { + tagDoc = (TagContainer)tags[i]; + break; + } + } + else if ("@throws".equals(tag.kind())) { + if (((ThrowsTagImpl)tags[i]).exceptionName().equals(((ThrowsTagImpl)tag).exceptionName())) { + tagDoc = (TagContainer)tags[i]; + break; + } + } + else if ("@return".equals(tag.kind())) { + tagDoc = (TagContainer)tags[i]; + } + } + } + + return tagDoc; + } + + if (null == result || result.isEmptyDoc()) { + return null; + } + else { + return result; + } + } + + public static TagContainer findInheritedDoc(ClassDoc classDoc, + MemberDocImpl memberDoc, + AbstractTagImpl tag) + { + TagContainer result; + + // (Taken from Javadoc Solaris Tool documentation 1.5, + // section "Automatic Copying of Method Comments") + + // Algorithm for Inheriting Method Comments - If a method does + // not have a doc comment, or has an {@inheritDoc} tag, the + // Javadoc tool searches for an applicable comment using the + // following algorithm, which is designed to find the most + // specific applicable doc comment, giving preference to + // interfaces over superclasses: + + // 1. Look in each directly implemented (or extended) interface + // in the order they appear following the word implements (or + // extends) in the method declaration. Use the first doc comment + // found for this method. + + ClassDoc[] interfaces = classDoc.interfaces(); + if (null != interfaces) { + for (int i=0; i<interfaces.length; ++i) { + result = checkForInheritedDoc(interfaces[i], memberDoc, tag); + if (null != result) { + return result; + } + } + } + + // 2. If step 1 failed to find a doc comment, recursively apply + // this entire algorithm to each directly implemented (or + // extended) interface, in the same order they were examined + // in step 1. + + if (null != interfaces) { + for (int i=0; i<interfaces.length; ++i) { + result = findInheritedDoc(interfaces[i], memberDoc, tag); + if (null != result) { + return result; + } + } + } + + ClassDoc superclassDoc = classDoc.superclass(); + + // 3. If step 2 failed to find a doc comment and this is a class + // other than Object (not an interface): + if (!classDoc.isInterface() + && null != superclassDoc + && !"java.lang.Object".equals(classDoc.qualifiedTypeName())) { + + // 3a. If the superclass has a doc comment for this method, use it. + + result = checkForInheritedDoc(superclassDoc, memberDoc, tag); + if (null != result) { + return result; + } + + // 3b. If step 3a failed to find a doc comment, recursively + // apply this entire algorithm to the superclass. + + return findInheritedDoc(superclassDoc, + memberDoc, tag); + } + else { + return null; + } + } + + public boolean isEmptyDoc() + { + return tagMap.isEmpty(); + } + + void setBoilerplateComment(String boilerplateComment) + { + this.boilerplateComment = boilerplateComment; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ErrorReporter.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ErrorReporter.java new file mode 100644 index 000000000..3c139ab25 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ErrorReporter.java @@ -0,0 +1,121 @@ +/* gnu.classpath.tools.gjdoc.ErrorReporter + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.io.*; +import java.util.*; +import java.lang.reflect.*; + +/** + * Simple implementation of a <code>DocErrorReporter</code>: writes + * to <code>System.err</code>. + */ +public class ErrorReporter implements DocErrorReporter { + + private PrintStream out; + + /** + * Keeps track of the number of errors occured + * during generation. + */ + private int errorCount=0; + + /** + * Keeps track of the number of warnings occured + * during generation. + */ + private int warningCount=0; + + /* + * When <code>true</code>, no notices will be emitted. + */ + private boolean quiet = false; + + /* + * When <code>true</code>, no warnings will be emitted. + */ + private boolean noWarn = false; + + public ErrorReporter() + { + this.out = System.err; + } + + // Print error message, increment error count. + public void printError(java.lang.String msg) { + out.println("ERROR: "+msg); + ++errorCount; + } + + // Print error message, increment error count. + public void printFatal(java.lang.String msg) { + out.println("FATAL: "+msg); + System.exit(10); + } + + // Print a message. + public void printNotice(java.lang.String msg) { + if (!quiet) { + out.println(msg); + } + } + + // Print warning message, increment warning count. + public void printWarning(java.lang.String msg) { + if (!noWarn) { + out.println("WARNING: "+msg); + ++warningCount;; + } + } + + public int getErrorCount() { + return errorCount; + } + + public int getWarningCount() { + return warningCount; + } + + /** + * Specify whether notices should be printed. + */ + public void setQuiet(boolean quiet) { + this.quiet = quiet; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ExecutableMemberDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ExecutableMemberDocImpl.java new file mode 100644 index 000000000..d5b1b1eb0 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ExecutableMemberDocImpl.java @@ -0,0 +1,427 @@ +/* gnu.classpath.tools.gjdoc.ExecutableMemberDocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.util.*; +import java.io.*; +import com.sun.javadoc.*; + +public class ExecutableMemberDocImpl extends MemberDocImpl implements ExecutableMemberDoc { + + protected ExecutableMemberDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + SourcePosition position) { + + super(containingClass, + containingPackage, + position); + } + + protected boolean processModifier(String word) { + if (super.processModifier(word)) { + return true; + } + else if (word.equals("synchronized")) { + isSynchronized=true; + return true; + } + else if (word.equals("native")) { + isNative=true; + return true; + } + else if (word.equals("abstract")) { + isAbstract=true; + return true; + } + else { + return false; + } + } + + private boolean isAbstract=false; + private boolean isNative=false; + private boolean isSynchronized=false; + + public boolean isAbstract() { return isAbstract; } + + public boolean isNative() { return isNative; } + + public boolean isSynchronized() { return isSynchronized; } + + public ClassDoc[] thrownExceptions() { return thrownExceptions; } + + public Parameter[] parameters() { return parameters; } + + public ThrowsTag[] throwsTags() { + return (ThrowsTag[])getTagArr("throws", throwsTagEmptyArr); + } + + public ParamTag[] paramTags() { + return (ParamTag[])getTagArr("param", paramTagEmptyArr); + } + + public String signature() { return signature; } + public String flatSignature() { return flatSignature; } + + public ClassDoc overriddenClass() { + for (ClassDoc cdi=(ClassDoc)containingClass().superclass(); cdi!=null; cdi=(ClassDoc)cdi.superclass()) { + if (null!=ClassDocImpl.findMethod(cdi, name(), signature())) + return cdi; + } + return null; + } + + public static ExecutableMemberDocImpl createFromSource(ClassDoc containingClass, + PackageDoc containingPackage, + char[] source, int startIndex, int endIndex) throws IOException, ParseException { + + int lastchar=32; + StringBuffer methodName=new StringBuffer(); + for (int i=startIndex; i<endIndex && source[i]!='('; ++i) { + if ((Parser.WHITESPACE.indexOf(lastchar)>=0 && Parser.WHITESPACE.indexOf(source[i])<0) + || (lastchar == ']' && Parser.WHITESPACE.indexOf(source[i])<0 && '[' != source[i])) { + methodName.setLength(0); + methodName.append(source[i]); + } + else if (Parser.WHITESPACE.indexOf(source[i])<0) { + methodName.append(source[i]); + } + lastchar=source[i]; + } + + ExecutableMemberDocImpl rc; + + SourcePosition position = DocImpl.getPosition(containingClass, source, startIndex); + + if (methodName.toString().equals(((ClassDocImpl)containingClass).getClassName())) { + + // Constructor + + rc=new ConstructorDocImpl(containingClass, + containingPackage, + position); + } + else { + + // Normal method + + rc=new MethodDocImpl(containingClass, + containingPackage, + position); + } + + if (containingClass.isInterface()) + rc.accessLevel=ACCESS_PUBLIC; + + int ndx=rc.parseModifiers(source, startIndex, endIndex); + StringBuffer name = new StringBuffer(); + + final int STATE_NORMAL=1; + final int STATE_STARC=2; + final int STATE_SLASHC=3; + + int state=STATE_NORMAL; + + while (source[ndx]!='(' && ndx<endIndex) { + if (state==STATE_NORMAL) { + if (ndx<endIndex-1 && source[ndx]=='/' && source[ndx+1]=='/') { + ++ndx; + state=STATE_SLASHC; + } + else if (ndx<endIndex-1 && source[ndx]=='/' && source[ndx+1]=='*') { + ++ndx; + state=STATE_STARC; + } + else { + name.append(source[ndx]); + } + } + else if (state==STATE_SLASHC) { + if (source[ndx]=='\n') + state=STATE_NORMAL; + } + else if (state==STATE_STARC) { + if (ndx<endIndex-1 && source[ndx]=='*' && source[ndx+1]=='/') { + ++ndx; + state=STATE_NORMAL; + } + } + ++ndx; + } + rc.setName(name.toString().trim()); + + state=STATE_NORMAL; + + ++ndx; + int endx; + String param=""; + List parameterList=new ArrayList(); + for (endx=ndx; endx<endIndex; ++endx) { + if (state==STATE_SLASHC) { + if (source[endx]=='\n') { + state=STATE_NORMAL; + } + } + else if (state==STATE_STARC) { + if (source[endx]=='*' && source[endx+1]=='/') { + state=STATE_NORMAL; + ++endx; + } + } + else if (source[endx]=='/' && source[endx+1]=='*') { + state=STATE_STARC; + ++endx; + } + else if (source[endx]=='/' && source[endx+1]=='/') { + state=STATE_SLASHC; + ++endx; + } + else if (source[endx]==',' || source[endx]==')') { + param=param.trim(); + if (param.length()>0) { + int n = param.length()-1; + int paramNameStart = 0; + while (n >= 0) { + char c = param.charAt(n); + if ('[' == c || ']' == c || Parser.WHITESPACE.indexOf(c)>=0) { + paramNameStart = n + 1; + break; + } + else { + -- n; + } + } + while (n >= 0 && ('[' == param.charAt(n) + || ']' == param.charAt(n) + || Parser.WHITESPACE.indexOf(param.charAt(n))>=0)) { + -- n; + } + int paramTypeEnd = n + 1; + int paramTypeStart = 0; + while (n >= 0) { + char c = param.charAt(n); + if ('[' == c || ']' == c || Parser.WHITESPACE.indexOf(c)>=0) { + paramTypeStart = n + 1; + break; + } + else { + -- n; + } + } + + String paramType; + String paramName; + if (0 != paramNameStart) { + paramType=param.substring(paramTypeStart, paramTypeEnd); + paramName=param.substring(paramNameStart); + } + else { + paramName = ""; + StringBuffer paramTypeBuffer = new StringBuffer(); + for (int i=0; i<param.length(); ++i) { + char c = param.charAt(i); + if ('[' != c && ']' != c && Parser.WHITESPACE.indexOf(c)<0) { + paramTypeBuffer.append(c); + } + } + paramType = paramTypeBuffer.toString(); + } + String dimSuffix=""; + + for (int i=0; i<param.length(); ++i) { + if ('[' == param.charAt(i)) { + dimSuffix += "[]"; + } + } + paramType+=dimSuffix; + + if (paramType.startsWith("[")) { + System.err.println("broken param type in " + rc + " in " +containingClass); + } + + parameterList.add(new ParameterImpl(paramName, paramType, + ((ClassDocImpl)containingClass).typeForString(paramType))); + + param=""; + } + } + else + param+=source[endx]; + + if (source[endx]==')' && state==STATE_NORMAL) + break; + } + + rc.setParameters((Parameter[])parameterList.toArray(new Parameter[0])); + + ++endx; + String word=""; + String dimSuffix=""; + boolean haveThrowsKeyword=false; + List thrownExceptionsList=new ArrayList(); + + state=STATE_NORMAL; + for (; endx<endIndex; ++endx) { + if (state==STATE_SLASHC) { + if (source[endx]=='\n') state=STATE_NORMAL; + } + else if (state==STATE_STARC) { + if (source[endx]=='*' && source[endx+1]=='/') { + state=STATE_NORMAL; + ++endx; + } + } + else if (source[endx]=='/' && source[endx+1]=='*') { + state=STATE_STARC; + ++endx; + } + else if (source[endx]=='/' && source[endx+1]=='/') { + state=STATE_SLASHC; + ++endx; + } + else if (Parser.WHITESPACE.indexOf(source[endx])>=0) { + word=word.trim(); + if (!haveThrowsKeyword && word.length()>0) { + if (word.equals("throws")) haveThrowsKeyword=true; + else System.err.println("ARGH! "+word); + word=""; + } + } + else if (source[endx]=='[' || source[endx]==']') { + dimSuffix += source[endx]; + } + else if (source[endx]==',' || source[endx]=='{' || source[endx]==';') { + word=word.trim(); + if (word.length()>0) { + ClassDoc exceptionType=rc.containingClass().findClass(word); + if (exceptionType==null) { + exceptionType=new ClassDocProxy(word, + rc.containingClass()); + } + thrownExceptionsList.add(exceptionType); + } + if (source[endx]=='{') { + break; + } + else { + word=""; + } + } + else { + word+=source[endx]; + } + } + + if (dimSuffix.length()>0) { + rc.setTypeName(rc.getTypeName()+dimSuffix); + } + + rc.setThrownExceptions((ClassDoc[])thrownExceptionsList.toArray(new ClassDoc[0])); + + return rc; + } + + private ClassDoc[] thrownExceptions; + private Parameter[] parameters; + private String signature; + private String flatSignature; + + void setParameters(Parameter[] parameters) { + this.parameters=parameters; + } + + void setThrownExceptions(ClassDoc[] thrownExceptions) { + this.thrownExceptions=thrownExceptions; + } + + void resolve() { + + for (int i=0; i<thrownExceptions.length; ++i) { + if (thrownExceptions[i] instanceof ClassDocProxy) { + String className=thrownExceptions[i].qualifiedName(); + ClassDoc realClassDoc=containingClass().findClass(className); + if (realClassDoc!=null) + thrownExceptions[i]=realClassDoc; + } + } + + StringBuffer signatureBuf=new StringBuffer(); + StringBuffer flatSignatureBuf=new StringBuffer(); + + for (int i=0; i<parameters.length; ++i) { + ((ParameterImpl)parameters[i]).resolve(containingClass()); + + if (signatureBuf.length()>0) { + signatureBuf.append(","); + flatSignatureBuf.append(","); + } + signatureBuf.append(parameters[i].type().qualifiedTypeName()); + flatSignatureBuf.append(parameters[i].type().typeName()); + signatureBuf.append(parameters[i].type().dimension()); + flatSignatureBuf.append(parameters[i].type().dimension()); + } + this.signature="("+signatureBuf.toString()+")"; + this.flatSignature="("+flatSignatureBuf.toString()+")"; + + super.resolve(); + + } + + public int compareTo(Object other) { + int rc; + if (other instanceof MemberDocImpl) { + MemberDocImpl otherMember = (MemberDocImpl)other; + rc = name().compareTo(otherMember.name()); + if (0 == rc) { + if (other instanceof ExecutableMemberDocImpl) { + rc = signature().compareTo(((ExecutableMemberDocImpl)other).signature()); + if (0 == rc) { + return containingClass().compareTo(otherMember.containingClass()); + } + } + else { + rc = 1; + } + } + } + else { + rc = 1; + } + return rc; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/FieldDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/FieldDocImpl.java new file mode 100644 index 000000000..f99024daa --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/FieldDocImpl.java @@ -0,0 +1,409 @@ +/* gnu.classpath.tools.gjdoc.FieldDocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.util.*; +import com.sun.javadoc.*; +import java.lang.reflect.Modifier; + +import gnu.classpath.tools.gjdoc.expr.Evaluator; +import gnu.classpath.tools.gjdoc.expr.CircularExpressionException; +import gnu.classpath.tools.gjdoc.expr.IllegalExpressionException; + +public class FieldDocImpl + extends MemberDocImpl + implements FieldDoc, Cloneable +{ + + private boolean isTransient; + private boolean isVolatile; + private String valueLiteral; + private Object constantValue; + private boolean constantValueEvaluated; + + private FieldDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + SourcePosition position) { + + super(containingClass, + containingPackage, + position); + } + + private static FieldDocImpl createFieldDoc(FieldDocImpl prototype, + String fieldDef, + String fieldValueLiteral) + { + if (null != fieldValueLiteral && fieldValueLiteral.length() == 0) { + fieldValueLiteral = null; + } + + try { + FieldDocImpl fieldDoc=(FieldDocImpl)prototype.clone(); + String dimSuffix=""; + while (fieldDef.trim().endsWith("[") + || fieldDef.trim().endsWith("]")) { + fieldDef=fieldDef.trim(); + dimSuffix=fieldDef.charAt(fieldDef.length()-1)+dimSuffix; + fieldDef=fieldDef.substring(0,fieldDef.length()-1); + } + + fieldDoc.setTypeName(fieldDoc.getTypeName()+dimSuffix); + fieldDoc.setName(fieldDef.trim()); + fieldDoc.setValueLiteral(fieldValueLiteral); + return fieldDoc; + } + catch (CloneNotSupportedException e) { + // should not happen + e.printStackTrace(); + return null; + } + } + + public static Collection createFromSource(ClassDoc containingClass, + PackageDoc containingPackage, + char[] source, int startIndex, int endIndex) { + + List rcList=new ArrayList(); + + FieldDocImpl fd=new FieldDocImpl(containingClass, + containingPackage, + DocImpl.getPosition(containingClass, source, startIndex)); + + int ndx=fd.parseModifiers(source, startIndex, endIndex); + + if (containingClass.isInterface()) { + fd.accessLevel = ACCESS_PUBLIC; + } + + final int STATE_FIELDNAME = 1; + final int STATE_FIELDVALUE = 2; + final int STATE_QUOTE = 3; + final int STATE_QUOTEBS = 4; + final int STATE_SQUOTE = 5; + final int STATE_SQUOTEBS = 6; + final int STATE_COMMENT = 7; + final int STATE_LINECOMMENT = 8; + + int lastFieldDefStart = ndx; + int state = STATE_FIELDNAME; + int prevState = state; + + int bracketCount = 0; + + StringBuffer fieldNameBuf = new StringBuffer(); + StringBuffer fieldValueLiteralBuf = new StringBuffer(); + + for (int i=ndx; i<endIndex; ++i) { + + char c = source[i]; + char nextChar = '\0'; + if (i + 1 < endIndex) { + nextChar = source[i + 1]; + } + switch (state) { + case STATE_FIELDNAME: + if ('/' == c && '/' == nextChar) { + prevState = state; + state = STATE_LINECOMMENT; + } + else if ('/' == c && '*' == nextChar) { + prevState = state; + state = STATE_COMMENT; + } + else if (',' == c || ';' == c) { + rcList.add(createFieldDoc(fd, fieldNameBuf.toString(), null)); + fieldNameBuf.setLength(0); + } + else if ('=' == c) { + state = STATE_FIELDVALUE; + } + else if (!(' ' == c || '\n' == c || '\r' == c || '\t' == c)) { + fieldNameBuf.append(c); + } + break; + + case STATE_FIELDVALUE: + if ('/' == c && '/' == nextChar) { + prevState = state; + state = STATE_LINECOMMENT; + } + else if ('/' == c && '*' == nextChar) { + prevState = state; + state = STATE_COMMENT; + } + else if ('\"' == c) { + prevState = state; + state = STATE_QUOTE; + fieldValueLiteralBuf.append(c); + } + else if ('\'' == c) { + prevState = state; + state = STATE_SQUOTE; + fieldValueLiteralBuf.append(c); + } + else if ('{' == c || '(' == c) { + ++ bracketCount; + fieldValueLiteralBuf.append(c); + } + else if ('}' == c || ')' == c) { + -- bracketCount; + fieldValueLiteralBuf.append(c); + } + else if (0 == bracketCount && (',' == c || ';' == c)) { + rcList.add(createFieldDoc(fd, fieldNameBuf.toString(), + fieldValueLiteralBuf.toString())); + fieldNameBuf.setLength(0); + fieldValueLiteralBuf.setLength(0); + state = STATE_FIELDNAME; + } + else { + fieldValueLiteralBuf.append(c); + } + break; + + case STATE_QUOTE: + fieldValueLiteralBuf.append(c); + if ('\\' == c) { + state = STATE_QUOTEBS; + } + else if ('\"' == c) { + state = prevState; + } + break; + + case STATE_SQUOTE: + fieldValueLiteralBuf.append(c); + if ('\\' == c) { + state = STATE_SQUOTEBS; + } + else if ('\'' == c) { + state = prevState; + } + break; + + case STATE_QUOTEBS: + fieldValueLiteralBuf.append(c); + state = STATE_QUOTE; + break; + + case STATE_SQUOTEBS: + fieldValueLiteralBuf.append(c); + state = STATE_SQUOTE; + break; + + case STATE_LINECOMMENT: + if ('\n' == c) { + state = prevState; + } + break; + + case STATE_COMMENT: + if ('*' == c && '/' == nextChar) { + ++ i; + state = prevState; + } + break; + } + } + + if (fieldNameBuf.length() > 0) { + rcList.add(createFieldDoc(fd, fieldNameBuf.toString(), + fieldValueLiteralBuf.toString())); + } + + return rcList; + } + + public boolean isField() { + return true; + } + + public boolean isTransient() { return isTransient; } + + public boolean isVolatile() { return isVolatile; } + + public SerialFieldTag[] serialFieldTags() { return new SerialFieldTag[0]; } + + public int modifierSpecifier() { + return super.modifierSpecifier() + | (isVolatile()?Modifier.VOLATILE:0) + | (isTransient()?Modifier.TRANSIENT:0) + ; + } + + protected boolean processModifier(String word) { + if (super.processModifier(word)) { + return true; + } + else if (word.equals("transient")) { + isTransient=true; + return true; + } + else if (word.equals("volatile")) { + isVolatile=true; + return true; + } + else { + return false; + } + } + + void resolve() { + resolveTags(); + } + + public boolean hasSerialTag() { + return true; //tagMap.get("serial")!=null; + } + + public String toString() { return name(); } + + public Object constantValue() { + return constantValue(new HashSet()); + } + + public Object constantValue(Set visitedFields) { + if (!isStatic() + || !isFinal() + || (!type().isPrimitive() && !"java.lang.String".equals(type().qualifiedTypeName())) + || type.dimension().length()>0 + || null == valueLiteral) { + + return null; + + } + else { + if (!constantValueEvaluated) { + + visitedFields.add(this); + + String expression = "(" + type().typeName() + ")(" + valueLiteral + ")"; + try { + this.constantValue = Evaluator.evaluate(expression, + visitedFields, + (ClassDocImpl)containingClass()); + } + catch (CircularExpressionException e) { + // FIXME: This should use the error reporter + System.err.println("WARNING: Cannot resolve expression for field " + containingClass.qualifiedTypeName() + "." + name() + ": " + e.getMessage()); + } + catch (IllegalExpressionException ignore) { + } + constantValueEvaluated = true; + } + return this.constantValue; + } + } + + private static void appendCharString(StringBuffer result, char c, boolean inSingleCuotes) + { + switch (c) { + case '\b': result.append("\\b"); break; + case '\t': result.append("\\t"); break; + case '\n': result.append("\\n"); break; + case '\f': result.append("\\f"); break; + case '\r': result.append("\\r"); break; + case '\"': result.append("\\\""); break; + case '\'': result.append(inSingleCuotes ? "\\'" : "'"); break; + default: + if (c >= 32 && c <= 127) { + result.append(c); + } + else { + result.append("\\u"); + String hexValue = Integer.toString((int)c, 16); + int zeroCount = 4 - hexValue.length(); + for (int i=0; i<zeroCount; ++i) { + result.append('0'); + } + result.append(hexValue); + } + } + } + + public String constantValueExpression() { + Object value = constantValue(); + + if (null == value) { + return "null"; + } + else if (value instanceof String) { + StringBuffer result = new StringBuffer("\""); + char[] chars = ((String)value).toCharArray(); + for (int i=0; i<chars.length; ++i) { + appendCharString(result, chars[i], false); + } + result.append("\""); + return result.toString(); + } + else if (value instanceof Float) { + return value.toString() + "f"; + } + else if (value instanceof Long) { + return value.toString() + "L"; + } + else if (value instanceof Character) { + StringBuffer result = new StringBuffer("'"); + appendCharString(result, ((Character)value).charValue(), false); + result.append("'"); + return result.toString(); + } + else /* if (value instanceof Double + || value instanceof Integer + || value instanceof Short + || value instanceof Byte) */ { + return value.toString(); + } + } + + void setValueLiteral(String valueLiteral) + { + this.valueLiteral = valueLiteral; + } + + public boolean isStatic() + { + return super.isStatic() || containingClass().isInterface(); + } + + public boolean isFinal() + { + return super.isFinal() || containingClass().isInterface(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/GjdocPackageDoc.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/GjdocPackageDoc.java new file mode 100644 index 000000000..4cfbbe451 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/GjdocPackageDoc.java @@ -0,0 +1,56 @@ +/* gnu.classpath.tools.gjdoc.GjdocPackageDoc + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.io.File; +import com.sun.javadoc.PackageDoc; + +/** + * Extension of the PackageDoc interface which additionally provides + * the directory the package's source files are located in. + * + * @author Julian Scheid + */ +public interface GjdocPackageDoc extends PackageDoc +{ + /** + * Returns the directory this package's source files are located + * in. + */ + public File packageDirectory(); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/GjdocRootDoc.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/GjdocRootDoc.java new file mode 100644 index 000000000..d45786fb9 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/GjdocRootDoc.java @@ -0,0 +1,56 @@ +/* gnu.classpath.tools.gjdoc.GjdocRootDoc + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.io.File; +import com.sun.javadoc.RootDoc; + +/** + * Extension of the RootDoc interface which additionally provides + * a method for flushing all state. + * + * @author Julian Scheid + */ +public interface GjdocRootDoc extends RootDoc +{ + /** + * Invalidate this RootDoc by flushing all associated data, but + * keep its error reporting functionality intact. + */ + public void flush(); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/InheritDocTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/InheritDocTagImpl.java new file mode 100644 index 000000000..3be63dd79 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/InheritDocTagImpl.java @@ -0,0 +1,103 @@ +/* gnu.classpath.tools.gjdoc.InheritDocTagImpl + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.text.*; + +/** + * Represents the <code>inheritDoc</code> tag. + */ +public class InheritDocTagImpl + extends AbstractTagImpl +{ + private ClassDocImpl contextClass; + private MemberDocImpl contextMember; + private AbstractTagImpl contextTag; + + public InheritDocTagImpl(ClassDocImpl contextClass, + MemberDocImpl contextMember, + AbstractTagImpl contextTag) + { + super(""); + this.contextClass = contextClass; + this.contextMember = contextMember; + this.contextTag = contextTag; + } + + public String kind() { + return "@inheritDoc"; + } + + public String name() { + return "@inheritDoc"; + } + + private TagContainer inheritedDoc; + private boolean inheritedDocInitialized = false; + + private TagContainer getInheritedDoc() + { + if (!inheritedDocInitialized) { + inheritedDoc = DocImpl.findInheritedDoc(contextClass, contextMember, contextTag); + inheritedDocInitialized = true; + } + return inheritedDoc; + } + + public Tag[] firstSentenceTags() { + TagContainer _inheritedDoc = getInheritedDoc(); + if (_inheritedDoc != null) { + return _inheritedDoc.firstSentenceTags(); + } + else { + return null; + } + } + + public Tag[] inlineTags() { + TagContainer _inheritedDoc = getInheritedDoc(); + if (_inheritedDoc != null) { + return _inheritedDoc.inlineTags(); + } + else { + return null; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/JavadocWrapper.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/JavadocWrapper.java new file mode 100644 index 000000000..93c08b94d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/JavadocWrapper.java @@ -0,0 +1,53 @@ +/* gnu.classpath.tools.gjdoc.JavadocWrapper + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.util.*; +import java.io.*; +import com.sun.javadoc.*; + +public class JavadocWrapper { + + public static void main(String[] args) throws Exception { + + Timer.setStartTime(); + Class sunJavadocMain=Class.forName("com.sun.tools.javadoc.Main"); + sunJavadocMain.getMethod("main", new Class[]{String[].class}).invoke(null, new Object[]{args}); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/LinkTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/LinkTagImpl.java new file mode 100644 index 000000000..3b2b8a848 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/LinkTagImpl.java @@ -0,0 +1,63 @@ +/* gnu.classpath.tools.gjdoc.LinkTagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.text.*; + +public class LinkTagImpl extends SeeTagImpl { + + private String name; + + public LinkTagImpl(String name, String _text, ClassDocImpl contextClass) { + super(_text, contextClass); + this.name = name; + } + + public String name() { return name; } + + public Tag[] firstSentenceTags() { + return new Tag[0]; //inlineTags(); + } + + public Tag[] inlineTags() { + return new Tag[0]; //new Tag[]{new TextTagImpl(referencedClassName)}; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Main.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Main.java new file mode 100644 index 000000000..ce9e96d82 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Main.java @@ -0,0 +1,1854 @@ +/* gnu.classpath.tools.gjdoc.Main + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.io.*; +import java.util.*; +import java.lang.reflect.*; +import java.text.Collator; + +import gnu.classpath.tools.FileSystemClassLoader; + +/** + * Class that will launch the gjdoc tool. + */ +public final class Main +{ + + /** + * Do we load classes that are referenced as base class? + */ + static final boolean DESCEND_SUPERCLASS = true; + + /** + * Do we load classes that are referenced as interface? + */ + static final boolean DESCEND_INTERFACES = false; + + /** + * Do we load classes that are imported in a source file? + */ + static final boolean DESCEND_IMPORTED = true; + + /** + * Document only public members. + */ + static final int COVERAGE_PUBLIC = 0; + + /** + * Document only public and protected members. + */ + static final int COVERAGE_PROTECTED = 1; + + /** + * Document public, protected and package private members. + */ + static final int COVERAGE_PACKAGE = 2; + + /** + * Document all members. + */ + static final int COVERAGE_PRIVATE = 3; + + /* + * FIXME: This should come from a ResourceBundle + */ + private static final String STRING_TRY_GJDOC_HELP = + "Try `gjdoc --help' for more information."; + + /** + * Grid for looking up whether a particular access level is included in the + * documentation. + */ + static final boolean[][] coverageTemplates = new boolean[][] + { new boolean[] + { true, false, false, false }, // public + new boolean[] + { true, true, false, false }, // protected + new boolean[] + { true, true, true, false }, // package + new boolean[] + { true, true, true, true }, // private + }; + + /** + * Holds the Singleton instance of this class. + */ + private static Main instance = new Main(); + + /** + * Avoid re-instantiation of this class. + */ + private Main() + { + } + + private static RootDocImpl rootDoc; + + private ErrorReporter reporter; + + /** + * Cache for version string from resource /version.properties + */ + private String gjdocVersion; + + /** + * <code>false</code> during Phase I: preparation of the documentation data. + * <code>true</code> during Phase II: documentation output by doclet. + */ + boolean docletRunning = false; + + //---- Command line options + + /** + * Option "-doclet": name of the Doclet class to use. + */ + private String option_doclet = "gnu.classpath.tools.doclets.htmldoclet.HtmlDoclet"; + + /** + * Option "-overview": path to the special overview file. + */ + private String option_overview; + + /** + * Option "-coverage": which members to include in generated documentation. + */ + private int option_coverage = COVERAGE_PROTECTED; + + /** + * Option "-help": display command line usage. + */ + private boolean option_help; + + /** + * Option "-docletpath": path to doclet classes. + */ + private String option_docletpath; + + /** + * Option "-classpath": path to additional classes. + */ + private String option_classpath; + + /** + * Option "-sourcepath": path to the Java source files to be documented. + * FIXME: this should be a list of paths + */ + private List option_sourcepath = new ArrayList(); + + /** + * Option "-extdirs": path to Java extension files. + */ + private String option_extdirs; + + /** + * Option "-verbose": Be verbose when generating documentation. + */ + private boolean option_verbose; + + /** + * Option "-nowarn": Do not print warnings. + */ + private boolean option_nowarn; + + /** + * Option "-locale:" Specify the locale charset of Java source files. + */ + private Locale option_locale = new Locale("en", "us"); + + /** + * Option "-encoding": Specify character encoding of Java source files. + */ + private String option_encoding; + + /** + * Option "-J": Specify flags to be passed to Java runtime. + */ + private List option_java_flags = new LinkedList(); //ArrayList(); + + /** + * Option "-source:" should be 1.4 to handle assertions, 1.1 is no + * longer supported. + */ + private String option_source = "1.2"; + + /** + * Option "-subpackages": list of subpackages to be recursively + * added. + */ + private List option_subpackages = new ArrayList(); + + /** + * Option "-exclude": list of subpackages to exclude. + */ + private List option_exclude = new ArrayList(); + + /** + * Option "-breakiterator" - whether to use BreakIterator for + * detecting the end of the first sentence. + */ + private boolean option_breakiterator; + + /** + * Option "-licensetext" - whether to copy license text. + */ + private boolean option_licensetext; + + /** + * The locale-dependent collator used for sorting. + */ + private Collator collator; + + /** + * true when --version has been specified on the command line. + */ + private boolean option_showVersion; + + /** + * true when -bootclasspath has been specified on the command line. + */ + private boolean option_bootclasspath_specified; + + /** + * true when -all has been specified on the command line. + */ + private boolean option_all; + + /** + * true when -reflection has been specified on the command line. + */ + private boolean option_reflection; + + // TODO: add the rest of the options as instance variables + + /** + * Parse all source files/packages and subsequentially start the Doclet given + * on the command line. + * + * @param allOptions List of all command line tokens + */ + private boolean startDoclet(List allOptions) + { + + try + { + + //--- Fetch the Class object for the Doclet. + + Debug.log(1, "loading doclet class..."); + + Class docletClass; + + if (null != option_docletpath) { + try { + FileSystemClassLoader docletPathClassLoader + = new FileSystemClassLoader(option_docletpath); + System.err.println("trying to load class " + option_doclet + " from path " + option_docletpath); + docletClass = docletPathClassLoader.findClass(option_doclet); + } + catch (Exception e) { + docletClass = Class.forName(option_doclet); + } + } + else { + docletClass = Class.forName(option_doclet); + } + //Object docletInstance = docletClass.newInstance(); + + Debug.log(1, "doclet class loaded..."); + + Method startTempMethod = null; + Method startMethod = null; + Method optionLenMethod = null; + Method validOptionsMethod = null; + + //--- Try to find the optionLength method in the Doclet class. + + try + { + optionLenMethod = docletClass.getMethod("optionLength", new Class[] + { String.class }); + } + catch (NoSuchMethodException e) + { + // Ignore if not found; it's OK it the Doclet class doesn't define + // this method. + } + + //--- Try to find the validOptions method in the Doclet class. + + try + { + validOptionsMethod = docletClass.getMethod("validOptions", new Class[] + { String[][].class, DocErrorReporter.class }); + } + catch (NoSuchMethodException e) + { + // Ignore if not found; it's OK it the Doclet class doesn't define + // this method. + } + + //--- Find the start method in the Doclet class; complain if not found + + try + { + startTempMethod = docletClass.getMethod("start", new Class[] + { TemporaryStore.class }); + } + catch (Exception e) + { + // ignore + } + startMethod = docletClass.getMethod("start", new Class[] + { RootDoc.class }); + + //--- Feed the custom command line tokens to the Doclet + + // stores all recognized options + List options = new LinkedList(); + + // stores packages and classes defined on the command line + List packageAndClasses = new LinkedList(); + + for (Iterator it = allOptions.iterator(); it.hasNext();) + { + String option = (String) it.next(); + + Debug.log(9, "parsing option '" + option + "'"); + + if (option.startsWith("-")) + { + + //--- Parse option + + int optlen = optionLength(option); + + //--- Try to get option length from Doclet class + + if (optlen <= 0 && optionLenMethod != null) + { + + optionLenMethod.invoke(null, new Object[] + { option }); + + Debug.log(3, "invoking optionLen method"); + + optlen = ((Integer) optionLenMethod.invoke(null, new Object[] + { option })).intValue(); + + Debug.log(3, "done"); + } + + if (optlen <= 0) { + + if (option.startsWith("-JD")) { + // Simulate VM option -D + String propertyValue = option.substring(3); + int ndx = propertyValue.indexOf('='); + if (ndx <= 0) { + reporter.printError("Illegal format in option " + option + ": use -JDproperty=value"); + return false; + } + else { + String property = propertyValue.substring(0, ndx); + String value = propertyValue.substring(ndx + 1); + System.setProperty(property, value); + } + } + else if (option.startsWith("-J")) { + //--- Warn if VM option is encountered + reporter.printWarning("Ignored option " + option + ". Pass this option to the VM if required."); + } + else { + //--- Complain if not found + + reporter.printError("Unknown option " + option); + reporter.printNotice(STRING_TRY_GJDOC_HELP); + return false; + } + } + else + { + + //--- Read option values + + String[] optionAndValues = new String[optlen]; + optionAndValues[0] = option; + for (int i = 1; i < optlen; ++i) + { + if (!it.hasNext()) + { + reporter.printError("Missing value for option " + option); + return false; + } + else + { + optionAndValues[i] = (String) it.next(); + } + } + + //--- Store option for processing later + + options.add(optionAndValues); + } + } + else if (option.length() > 0) + { + + //--- Add to list of packages/classes if not option or option + // value + + packageAndClasses.add(option); + } + } + + Debug.log(9, "options parsed..."); + + //--- For each package specified with the -subpackages option on + // the command line, recursively find all valid java files + // beneath it. + + //--- For each class or package specified on the command line, + // check that it exists and find out whether it is a class + // or a package + + for (Iterator it = option_subpackages.iterator(); it.hasNext();) + { + String subpackage = (String) it.next(); + Set foundPackages = new LinkedHashSet(); + + for (Iterator pit = option_sourcepath.iterator(); pit.hasNext(); ) { + File sourceDir = (File)pit.next(); + File packageDir = new File(sourceDir, subpackage.replace('.', File.separatorChar)); + findPackages(subpackage, packageDir, foundPackages); + } + + addFoundPackages(subpackage, foundPackages); + } + + if (option_all) { + Set foundPackages = new LinkedHashSet(); + for (Iterator pit = option_sourcepath.iterator(); pit.hasNext(); ) { + File sourceDir = (File)pit.next(); + findPackages("", sourceDir, foundPackages); + } + addFoundPackages(null, foundPackages); + for (Iterator packageIt = foundPackages.iterator(); packageIt.hasNext(); ) { + String packageName = (String)packageIt.next(); + if (null == packageName) { + packageName = ""; + } + rootDoc.addSpecifiedPackageName(packageName); + } + } + + for (Iterator it = packageAndClasses.iterator(); it.hasNext();) + { + + String classOrPackage = (String) it.next(); + + boolean foundSourceFile = false; + + if (classOrPackage.endsWith(".java")) { + for (Iterator pit = option_sourcepath.iterator(); pit.hasNext() && !foundSourceFile; ) { + File sourceDir = (File)pit.next(); + File sourceFile = new File(sourceDir, classOrPackage); + if (sourceFile.exists() && !sourceFile.isDirectory()) { + rootDoc.addSpecifiedSourceFile(sourceFile); + foundSourceFile = true; + break; + } + } + if (!foundSourceFile) { + File sourceFile = new File(classOrPackage); + if (sourceFile.exists() && !sourceFile.isDirectory()) { + rootDoc.addSpecifiedSourceFile(sourceFile); + foundSourceFile = true; + } + } + } + + if (!foundSourceFile) { + //--- Check for illegal name + + if (classOrPackage.startsWith(".") + || classOrPackage.endsWith(".") + || classOrPackage.indexOf("..") > 0 + || !checkCharSet(classOrPackage, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.")) + { + throw new ParseException("Illegal class or package name '" + + classOrPackage + "'"); + } + + //--- Assemble absolute path to package + + String classOrPackageRelPath = classOrPackage.replace('.', + File.separatorChar); + + //--- Create one file object each for a possible package directory + // and a possible class file, and find out if they exist. + + List packageDirs = rootDoc.findSourceFiles(classOrPackageRelPath); + List sourceFiles = rootDoc.findSourceFiles(classOrPackageRelPath + ".java"); + + boolean packageDirExists = !packageDirs.isEmpty(); + boolean sourceFileExists = !sourceFiles.isEmpty(); + + //--- Complain if neither exists: not found + + if (!packageDirExists && !sourceFileExists) + { + reporter.printError("Class or package " + classOrPackage + + " not found."); + return false; + } + + //--- Complain if both exist: ambigious + + else + if (packageDirExists && sourceFileExists) + { + reporter.printError("Ambigious class/package name " + + classOrPackage + "."); + return false; + } + + //--- Otherwise, if the package directory exists, it is a package + + else + if (packageDirExists) { + Iterator packageDirIt = packageDirs.iterator(); + boolean packageDirFound = false; + while (packageDirIt.hasNext()) { + File packageDir = (File)packageDirIt.next(); + if (packageDir.isDirectory()) { + rootDoc.addSpecifiedPackageName(classOrPackage); + packageDirFound = true; + break; + } + } + if (!packageDirFound) { + reporter.printError("No suitable file or directory found for" + classOrPackage); + return false; + } + } + + //--- Otherwise, emit error message + + else { + reporter.printError("No sources files found for package " + classOrPackage); + } + } + } + + //--- Complain if no packages or classes specified + + if (option_help) { + usage(); + return true; + } + + //--- Validate custom options passed on command line + // by asking the Doclet if they are OK. + + String[][] customOptionArr = (String[][]) options + .toArray(new String[0][0]); + if (validOptionsMethod != null + && !((Boolean) validOptionsMethod.invoke(null, new Object[] + { customOptionArr, reporter })).booleanValue()) + { + // Not ok: shutdown system. + reporter.printNotice(STRING_TRY_GJDOC_HELP); + return false; + } + + if (!rootDoc.hasSpecifiedPackagesOrClasses()) { + reporter.printError("No packages or classes specified."); + reporter.printNotice(STRING_TRY_GJDOC_HELP); + return false; + } + + rootDoc.setOptions(customOptionArr); + + rootDoc.build(); + + //--- Bail out if no classes found + + if (0 == rootDoc.classes().length + && 0 == rootDoc.specifiedPackages().length + && 0 == rootDoc.specifiedClasses().length) + { + reporter.printError("No packages or classes found(!)."); + return false; + } + + //--- Our work is done, tidy up memory + + System.gc(); + System.gc(); + + //--- Set flag indicating Phase II of documentation generation + + docletRunning = true; + + //--- Invoke the start method on the Doclet: produce output + + reporter.printNotice("Running doclet..."); + + TemporaryStore tstore = new TemporaryStore(Main.rootDoc); + + Thread.currentThread().setContextClassLoader(docletClass.getClassLoader()); + + if (null != startTempMethod) + { + startTempMethod.invoke(null, new Object[] + { tstore }); + } + else + { + startMethod.invoke(null, new Object[] + { tstore.getAndClear() }); + } + + //--- Let the user know how many warnings/errors occured + + if (reporter.getWarningCount() > 0) + { + reporter.printNotice(reporter.getWarningCount() + " warnings"); + } + + if (reporter.getErrorCount() > 0) + { + reporter.printNotice(reporter.getErrorCount() + " errors"); + } + + System.gc(); + + //--- Done. + return true; + } + catch (Exception e) + { + e.printStackTrace(); + return false; + } + } + + private void addFoundPackages(String subpackage, Set foundPackages) + { + if (foundPackages.isEmpty()) { + reporter.printWarning("No classes found under subpackage " + subpackage); + } + else { + boolean onePackageAdded = false; + for (Iterator rit = foundPackages.iterator(); rit.hasNext();) { + String foundPackage = (String)rit.next(); + boolean excludeThisPackage = false; + + for (Iterator eit = option_exclude.iterator(); eit.hasNext();) { + String excludePackage = (String)eit.next(); + if (foundPackage.equals(excludePackage) || + foundPackage.startsWith(excludePackage + ":")) { + excludeThisPackage = true; + break; + } + } + + if (!excludeThisPackage) { + rootDoc.addSpecifiedPackageName(foundPackage); + onePackageAdded = true; + } + } + if (!onePackageAdded) { + if (null != subpackage) { + reporter.printWarning("No non-excluded classes found under subpackage " + subpackage); + } + else { + reporter.printWarning("No non-excluded classes found."); + } + } + } + } + + /** + * Verify that the given file is a valid Java source file and that + * it specifies the given package. + */ + private boolean isValidJavaFile(File file, + String expectedPackage) + { + try { + InputStream in = new BufferedInputStream(new FileInputStream(file)); + + int ch, prevChar = 0; + + final int STATE_DEFAULT = 0; + final int STATE_COMMENT = 1; + final int STATE_LINE_COMMENT = 2; + + int state = STATE_DEFAULT; + + StringBuffer word = new StringBuffer(); + int wordIndex = 0; + + while ((ch = in.read()) >= 0) { + String completeWord = null; + + switch (state) { + case STATE_COMMENT: + if (prevChar == '*' && ch == '/') { + state = STATE_DEFAULT; + } + break; + + case STATE_LINE_COMMENT: + if (ch == '\n') { + state = STATE_DEFAULT; + } + break; + + case STATE_DEFAULT: + if (prevChar == '/' && ch == '*') { + word.deleteCharAt(word.length() - 1); + if (word.length() > 0) { + completeWord = word.toString(); + word.setLength(0); + } + state = STATE_COMMENT; + } + else if (prevChar == '/' && ch == '/') { + word.deleteCharAt(word.length() - 1); + if (word.length() > 0) { + completeWord = word.toString(); + word.setLength(0); + } + state = STATE_LINE_COMMENT; + } + else if (" \t\r\n".indexOf(ch) >= 0) { + if (word.length() > 0) { + completeWord = word.toString(); + word.setLength(0); + } + } + else if (1 == wordIndex && ';' == ch) { + if (word.length() > 0) { + completeWord = word.toString(); + word.setLength(0); + } + else { + // empty package name in source file: "package ;" -> invalid source file + in.close(); + return false; + } + } + else { + word.append((char)ch); + } + break; + } + + if (null != completeWord) { + if (0 == wordIndex && !"package".equals(completeWord)) { + in.close(); + return "".equals(expectedPackage); + } + else if (1 == wordIndex) { + in.close(); + return expectedPackage.equals(completeWord); + } + ++ wordIndex; + } + + prevChar = ch; + } + + // no package or class found before end-of-file -> invalid source file + + in.close(); + return false; + } + catch (IOException e) { + reporter.printWarning("Could not examine file " + file + ": " + e); + return false; + } + } + + /** + * Recursively try to locate valid Java packages under the given + * package specified by its name and its directory. Add the names + * of all valid packages to the result list. + */ + private void findPackages(String subpackage, + File packageDir, + Set result) + { + File[] files = packageDir.listFiles(); + if (null != files) { + for (int i=0; i<files.length; ++i) { + File file = files[i]; + if (!file.isDirectory() && file.getName().endsWith(".java")) { + if (isValidJavaFile(file, subpackage)) { + if ("".equals(subpackage)) { + result.add(null); + } + else { + result.add(subpackage); + } + break; + } + } + } + for (int i=0; i<files.length; ++i) { + File file = files[i]; + if (file.isDirectory()) { + String newSubpackage; + if (null != subpackage && subpackage.length() > 0) { + newSubpackage = subpackage + "." + file.getName(); + } + else { + newSubpackage = file.getName(); + } + findPackages(newSubpackage, file, result); + } + } + } + } + + /** + * + */ + private static boolean validOptions(String options[][], + DocErrorReporter reporter) + { + + boolean foundDocletOption = false; + for (int i = 0; i < options.length; i++) + { + String[] opt = options[i]; + if (opt[0].equalsIgnoreCase("-doclet")) + { + if (foundDocletOption) + { + reporter.printError("Only one -doclet option allowed."); + return false; + } + else + { + foundDocletOption = true; + } + } + } + + return true; + } + + /** + * Main entry point. This is the method called when gjdoc is invoked from the + * command line. + * + * @param args + * command line arguments + */ + public static void main(String[] args) + { + + try + { + //--- Remember current time for profiling purposes + + Timer.setStartTime(); + + //--- Handle control to the Singleton instance of this class + + int result = instance.start(args); + + if (result < 0) { + // fatal error + System.exit(5); + } + else if (result > 0) { + // errors encountered + System.exit(1); + } + else { + // success + System.exit(0); + } + } + catch (Exception e) + { + //--- unexpected error + e.printStackTrace(); + System.exit(1); + } + } + + /** + * Parses command line arguments and subsequentially handles control to the + * startDoclet() method + * + * @param args The command line parameters. + */ + public static int execute(String[] args) + { + try + { + int result = instance.start(args); + if (result < 0) { + // fatal error + return 5; + } + else if (result > 0) { + // errors encountered + return 1; + } + else { + // success + return 0; + } + } + catch (Exception e) + { + // unexpected error + return 1; + } + } + + /** + * @param programName Name of the program (for error messages). *disregarded* + * @param args The command line parameters. + * @returns The return code. + */ + public static int execute(String programName, + String[] args) + { + return execute(args); + } + + /** + * @param programName Name of the program (for error messages). + * @param defaultDocletClassName Fully qualified class name. + * @param args The command line parameters. + * @returns The return code. + *//* + public static int execute(String programName, + String defaultDocletClassName, + String[] args) + { + // not yet implemented + }*/ + + /** + * @param programName Name of the program (for error messages). + * @param defaultDocletClassName Fully qualified class name. + * @param args The command line parameters. + * @returns The return code. + *//* + public static int execute(String programName, + String defaultDocletClassName, + String[] args) + { + // not yet implemented + }*/ + + /** + * @param programName Name of the program (for error messages). + * @param errWriter PrintWriter to receive error messages. + * @param warnWriter PrintWriter to receive error messages. + * @param noticeWriter PrintWriter to receive error messages. + * @param defaultDocletClassName Fully qualified class name. + * @param args The command line parameters. + * @returns The return code. + *//* + public static int execute(String programName, + PrintWriter errWriter, + PrintWriter warnWriter, + PrintWriter noticeWriter, + String defaultDocletClassName, + String[] args) + { + // not yet implemented + }*/ + + /** + * Parses command line arguments and subsequentially handles control to the + * startDoclet() method + * + * @param args + * Command line arguments, as passed to the main() method + * @return {@code -1} in case of a fatal error (invalid arguments), + * or the number of errors encountered. + * @exception ParseException + * FIXME + * @exception IOException + * if an IO problem occur + */ + public int start(String[] args) throws ParseException, IOException + { + + //--- Collect unparsed arguments in array and resolve references + // to external argument files. + + List arguments = new ArrayList(args.length); + + for (int i = 0; i < args.length; ++i) + { + if (!args[i].startsWith("@")) + { + arguments.add(args[i]); + } + else + { + FileReader reader = new FileReader(args[i].substring(1)); + StreamTokenizer st = new StreamTokenizer(reader); + st.resetSyntax(); + st.wordChars('\u0000', '\uffff'); + st.quoteChar('\"'); + st.quoteChar('\''); + st.whitespaceChars(' ', ' '); + st.whitespaceChars('\t', '\t'); + st.whitespaceChars('\r', '\r'); + st.whitespaceChars('\n', '\n'); + while (st.nextToken() != StreamTokenizer.TT_EOF) + { + arguments.add(st.sval); + } + } + } + + //--- Initialize Map for option parsing + + initOptions(); + + //--- This will hold all options recognized by gjdoc itself + // and their associated arguments. + // Contains objects of type String[], where each entry + // specifies an option along with its aguments. + + List options = new LinkedList(); + + //--- This will hold all command line tokens not recognized + // to be part of a standard option. + // These options are intended to be processed by the doclet + // Contains objects of type String, where each entry is + // one unrecognized token. + + List customOptions = new LinkedList(); + + rootDoc = new RootDocImpl(); + reporter = rootDoc.getReporter(); + + //--- Iterate over all options given on the command line + + for (Iterator it = arguments.iterator(); it.hasNext();) + { + + String arg = (String) it.next(); + + //--- Check if gjdoc recognizes this option as a standard option + // and remember the options' argument count + + int optlen = optionLength(arg); + + //--- Argument count == 0 indicates that the option is not recognized. + // Add it to the list of custom option tokens + + //--- Otherwise the option is recognized as a standard option. + // if all required arguments are supplied. Create a new String + // array for the option and its arguments, and store it + // in the options array. + + if (optlen > 0) + { + String[] option = new String[optlen]; + option[0] = arg; + boolean optargs_ok = true; + for (int j = 1; j < optlen && optargs_ok; ++j) + { + if (it.hasNext()) + { + option[j] = (String) it.next(); + if (option[j].startsWith("-")) + { + optargs_ok = false; + } + } + else + { + optargs_ok = false; + } + } + if (optargs_ok) + options.add(option); + else + { + // If the option requires more arguments than given on the + // command line, issue a fatal error + + reporter.printFatal("Missing value for option " + arg + "."); + } + } + } + + //--- Create an array of String arrays from the dynamic array built above + + String[][] optionArr = (String[][]) options.toArray(new String[options + .size()][0]); + + //--- Validate all options and issue warnings/errors + + if (validOptions(optionArr, rootDoc)) + { + + //--- We got valid options; parse them and store the parsed values + // in 'option_*' fields. + + readOptions(optionArr); + + //--- Show version and exit if requested by user + + if (option_showVersion) { + System.out.println("gjdoc " + getGjdocVersion()); + System.exit(0); + } + + if (option_bootclasspath_specified) { + reporter.printWarning("-bootclasspath ignored: not supported by" + + " gjdoc wrapper script, or no wrapper script in use."); + } + + // If we have an empty source path list, add the current directory ('.') + + if (option_sourcepath.size() == 0) + option_sourcepath.add(new File(".")); + + //--- We have all information we need to start the doclet at this time + + if (null != option_encoding) { + rootDoc.setSourceEncoding(option_encoding); + } + else { + // be quiet about this for now: + // reporter.printNotice("No encoding specified, using platform default: " + System.getProperty("file.encoding")); + rootDoc.setSourceEncoding(System.getProperty("file.encoding")); + } + rootDoc.setSourcePath(option_sourcepath); + + //addJavaLangClasses(); + + if (!startDoclet(arguments)) { + return -1; + } + } + + return reporter.getErrorCount(); + } + + private void addJavaLangClasses() + throws IOException + { + String resourceName = "/java.lang-classes-" + option_source + ".txt"; + InputStream in = getClass().getResourceAsStream(resourceName); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + + String className = line.trim(); + if (className.length() > 0) { + ClassDocImpl classDoc = + new ClassDocImpl(null, new PackageDocImpl("java.lang"), + ProgramElementDocImpl.ACCESS_PUBLIC, + false, false, null); + classDoc.setClass(className); + rootDoc.addClassDoc(classDoc); + } + } + } + + /** + * Helper class for parsing command line arguments. An instance of this class + * represents a particular option accepted by gjdoc (e.g. '-sourcepath') along + * with the number of expected arguments and behavior to parse the arguments. + */ + private abstract class OptionProcessor + { + + /** + * Number of arguments expected by this option. + */ + private int argCount; + + /** + * Initializes this instance. + * + * @param argCount + * number of arguments + */ + public OptionProcessor(int argCount) + { + this.argCount = argCount; + } + + /** + * Overridden by derived classes with behavior to parse the arguments + * specified with this option. + * + * @param args + * command line arguments + */ + abstract void process(String[] args); + } + + /** + * Maps option tags (e.g. '-sourcepath') to OptionProcessor objects. + * Initialized only once by method initOptions(). FIXME: Rename to + * 'optionProcessors'. + */ + private static Map options = null; + + /** + * Initialize all OptionProcessor objects needed to scan/parse command line + * options. This cannot be done in a static initializer block because + * OptionProcessors need access to the Singleton instance of the Main class. + */ + private void initOptions() + { + + options = new HashMap(); + + //--- Put one OptionProcessor object into the map + // for each option recognized. + + options.put("-overview", new OptionProcessor(2) + { + + void process(String[] args) + { + option_overview = args[0]; + } + }); + options.put("-public", new OptionProcessor(1) + { + + void process(String[] args) + { + option_coverage = COVERAGE_PUBLIC; + } + }); + options.put("-protected", new OptionProcessor(1) + { + + void process(String[] args) + { + option_coverage = COVERAGE_PROTECTED; + } + }); + options.put("-package", new OptionProcessor(1) + { + + void process(String[] args) + { + option_coverage = COVERAGE_PACKAGE; + } + }); + options.put("-private", new OptionProcessor(1) + { + + void process(String[] args) + { + option_coverage = COVERAGE_PRIVATE; + } + }); + OptionProcessor helpProcessor = new OptionProcessor(1) + { + + void process(String[] args) + { + option_help = true; + } + }; + + options.put("-help", helpProcessor); + options.put("--help", helpProcessor); + options.put("-doclet", new OptionProcessor(2) + { + + void process(String[] args) + { + option_doclet = args[0]; + } + }); + options.put("-docletpath", new OptionProcessor(2) + { + + void process(String[] args) + { + option_docletpath = args[0]; + } + }); + options.put("-nowarn", new OptionProcessor(1) + { + + void process(String[] args) + { + option_nowarn = true; + } + }); + options.put("-source", new OptionProcessor(2) + { + + void process(String[] args) + { + option_source = args[0]; + if (!"1.2".equals(option_source) + && !"1.3".equals(option_source) + && !"1.4".equals(option_source)) { + + throw new RuntimeException("Only he following values are currently" + + " supported for option -source: 1.2, 1.3, 1.4."); + } + } + }); + OptionProcessor sourcePathProcessor = new OptionProcessor(2) { + void process(String[] args) + { + Debug.log(1, "-sourcepath is '" + args[0] + "'"); + for (StringTokenizer st = new StringTokenizer(args[0], + File.pathSeparator); st.hasMoreTokens();) + { + String path = st.nextToken(); + File file = new File(path); + if (!(file.exists())) + { + throw new RuntimeException("The source path " + path + + " does not exist."); + } + option_sourcepath.add(file); + } + } + }; + options.put("-s", sourcePathProcessor); + options.put("-sourcepath", sourcePathProcessor); + options.put("-subpackages", new OptionProcessor(2) + { + void process(String[] args) + { + StringTokenizer st = new StringTokenizer(args[0], ":"); + while (st.hasMoreTokens()) { + String packageName = st.nextToken(); + + if (packageName.startsWith(".") + || packageName.endsWith(".") + || packageName.indexOf("..") > 0 + || !checkCharSet(packageName, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.")) { + throw new RuntimeException("Illegal package name '" + + packageName + "'"); + } + option_subpackages.add(packageName); + } + } + }); + options.put("-exclude", new OptionProcessor(2) + { + void process(String[] args) + { + StringTokenizer st = new StringTokenizer(args[0], ":"); + while (st.hasMoreTokens()) { + String packageName = st.nextToken(); + + if (packageName.startsWith(".") + || packageName.endsWith(".") + || packageName.indexOf("..") > 0 + || !checkCharSet(packageName, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.")) { + throw new RuntimeException("Illegal package name '" + + packageName + "'"); + } + option_exclude.add(packageName); + } + } + }); + // TODO include other options here + options.put("-verbose", new OptionProcessor(1) + { + + void process(String[] args) + { + option_verbose = true; + System.err.println("WARNING: Unsupported option -verbose ignored"); + } + }); + options.put("-quiet", new OptionProcessor(1) + { + + void process(String[] args) + { + reporter.setQuiet(true); + } + }); + options.put("-locale", new OptionProcessor(2) + { + + void process(String[] args) + { + String localeName = args[0]; + String language = null; + String country = null; + String variant = null; + StringTokenizer st = new StringTokenizer(localeName, "_"); + if (st.hasMoreTokens()) { + language = st.nextToken(); + } + if (st.hasMoreTokens()) { + country = st.nextToken(); + } + if (st.hasMoreTokens()) { + variant = st.nextToken(); + } + if (variant != null) { + option_locale = new Locale(language, country, variant); + } + else if (country != null) { + option_locale = new Locale(language, country); + } + else if (language != null) { + option_locale = new Locale(language); + } + else { + throw new RuntimeException("Illegal locale specification '" + + localeName + "'"); + } + } + }); + options.put("-encoding", new OptionProcessor(2) + { + + void process(String[] args) + { + option_encoding = args[0]; + } + }); + options.put("-breakiterator", new OptionProcessor(1) + { + void process(String[] args) + { + option_breakiterator = true; + } + }); + options.put("-licensetext", new OptionProcessor(1) + { + void process(String[] args) + { + option_licensetext = true; + } + }); + options.put("-overview", new OptionProcessor(2) + { + void process(String[] args) + { + try { + getRootDoc().setRawCommentText(RootDocImpl.readHtmlBody(new File(args[0]))); + } + catch (IOException e) { + throw new RuntimeException("Cannot read file specified in option -overview: " + e.getMessage()); + } + } + }); + options.put("-classpath", new OptionProcessor(2) + { + void process(String[] args) + { + reporter.printWarning("-classpath option could not be passed to the VM. Faking it with "); + reporter.printWarning(" System.setProperty(\"java.class.path\", \"" + args[0] + "\");"); + System.setProperty("java.class.path", args[0]); + } + }); + options.put("--version", new OptionProcessor(1) + { + void process(String[] args) + { + option_showVersion = true; + } + }); + options.put("-bootclasspath", new OptionProcessor(1) + { + void process(String[] args) + { + option_bootclasspath_specified = true; + } + }); + options.put("-all", new OptionProcessor(1) + { + void process(String[] args) + { + option_all = true; + } + }); + options.put("-reflection", new OptionProcessor(1) + { + void process(String[] args) + { + option_reflection = true; + } + }); + } + + /** + * Determine how many arguments the given option requires. + * + * @param option + * The name of the option without leading dash. + */ + private static int optionLength(String option) + { + + OptionProcessor op = (OptionProcessor) options.get(option.toLowerCase()); + if (op != null) + return op.argCount; + else + return 0; + } + + /** + * Process all given options. Assumes that the options have been validated + * before. + * + * @param optionArr + * Each element is a series of Strings where [0] is the name of the + * option and [1..n] are the arguments to the option. + */ + private void readOptions(String[][] optionArr) + { + + //--- For each option, find the appropriate OptionProcessor + // and call its process() method + + for (int i = 0; i < optionArr.length; ++i) + { + String[] opt = optionArr[i]; + String[] args = new String[opt.length - 1]; + System.arraycopy(opt, 1, args, 0, opt.length - 1); + OptionProcessor op = (OptionProcessor) options.get(opt[0].toLowerCase()); + op.process(args); + } + } + + /** + * Print command line usage. + */ + private static void usage() + { + System.out + .print("\n" + + "USAGE: gjdoc [options] [packagenames] " + + "[sourcefiles] [@files]\n\n" + + " --version Show version information and exit\n" + + " -all Process all source files found in the source path\n" + + " -overview <file> Read overview documentation from HTML file\n" + + " -public Include only public classes and members\n" + + " -protected Include protected and public classes and members\n" + + " This is the default\n" + + " -package Include package/protected/public classes and members\n" + + " -private Include all classes and members\n" + + " -help, --help Show this information\n" + + " -doclet <class> Doclet class to use for generating output\n" + + " -docletpath <classpath> Specifies the search path for the doclet and\n" + + " dependencies\n" + + " -source <release> Provide source compatibility with specified\n" + + " release (1.4 to handle assertion)\n" + + " -sourcepath <pathlist> Where to look for source files\n" + + " -s <pathlist> Alias for -sourcepath\n" + + " -subpackages <spkglist> List of subpackages to recursively load\n" + + " -exclude <pkglist> List of packages to exclude\n" + + " -verbose Output messages about what Gjdoc is doing [ignored]\n" + + " -quiet Do not print non-error and non-warning messages\n" + + " -locale <name> Locale to be used, e.g. en_US or en_US_WIN\n" + + " -encoding <name> Source file encoding name\n" + + " -breakiterator Compute first sentence with BreakIterator\n" + + " -classpath <pathlist> Set the path used for loading auxilliary classes\n" + + "\n" + + "Standard doclet options:\n" + + " -d Set target directory\n" + + " -use Includes the 'Use' page for each documented class\n" + + " and package\n" + + " -version Includes the '@version' tag\n" + + " -author Includes the '@author' tag\n" + + " -splitindex Splits the index file into multiple files\n" + + " -windowtitle <text> Browser window title\n" + + " -doctitle <text> Title near the top of the overview summary file\n" + + " (HTML allowed)\n" + + " -title <text> Title for this set of API documentation\n" + + " (deprecated, -doctitle should be used instead)\n" + + " -header <text> Text to include in the top navigation bar\n" + + " (HTML allowed)\n" + + " -footer <text> Text to include in the bottom navigation bar\n" + + " (HTML allowed)\n" + + " -bottom <text> Text to include at the bottom of each output file\n" + + " (HTML allowed)\n" + + " -link <extdoc URL> Link to external generated documentation at URL\n" + + " -linkoffline <extdoc URL> <packagelistLoc>\n" + + " Link to external generated documentation for\n" + + " the specified package-list\n" + + " -linksource Creates an HTML version of each source file\n" + + " -group <groupheading> <packagepattern:packagepattern:...>\n" + + " Separates packages on the overview page into groups\n" + + " -nodeprecated Prevents the generation of any deprecated API\n" + + " -nodeprecatedlist Prevents the generation of the file containing\n" + + " the list of deprecated APIs and the link to the\n" + + " navigation bar to that page\n" + + " -nosince Omit the '@since' tag\n" + + " -notree Do not generate the class/interface hierarchy page\n" + + " -noindex Do not generate the index file\n" + + " -nohelp Do not generate the help link\n" + + " -nonavbar Do not generate the navbar, header and footer\n" + + " -helpfile <filen> Path to an alternate help file\n" + + " -stylesheetfile <file> Path to an alternate CSS stylesheet\n" + + " -addstylesheet <file> Path to an additional CSS stylesheet\n" + + " -serialwarn Complain about missing '@serial' tags [ignored]\n" + + " -charset <IANACharset> Specifies the HTML charset\n" + + " -docencoding <IANACharset>\n" + + " Specifies the encoding of the generated HTML files\n" + + " -tag <tagname>:Xaoptcmf:\"<taghead>\"\n" + + " Enables gjdoc to interpret a custom tag\n" + + " -taglet Adds a Taglet class to the map of taglets\n" + + " -tagletpath Sets the CLASSPATH to load subsequent Taglets from\n" + + " -docfilessubdirs Enables deep copy of 'doc-files' directories\n" + + " -excludedocfilessubdir <name1:name2:...>\n" + + " Excludes 'doc-files' subdirectories with a give name\n" + + " -noqualifier all|<packagename1:packagename2:...>\n" + + " Do never fully qualify given package names\n" + + " -nocomment Suppress the entire comment body including the main\n" + + " description and all tags, only generate declarations\n" + + "\n" + + "Gjdoc extension options:\n" + + " -reflection Use reflection for resolving unqualified class names\n" + + " -licensetext Include license text from source files\n" + + " -validhtml Use valid HTML/XML names (breaks compatibility)\n" + + " -baseurl <url> Hardwire the given base URL into generated pages\n" + /** + + " -genhtml Generate HTML code instead of XML code. This is the\n" + + " default.\n" + + " -geninfo Generate Info code instead of XML code.\n" + + " -xslsheet <file> If specified, XML files will be written to a\n" + + " temporary directory and transformed using the\n" + + " given XSL sheet. The result of the transformation\n" + + " is written to the output directory. Not required if\n" + + " -genhtml or -geninfo has been specified.\n" + + " -xmlonly Generate XML code only, do not generate HTML code.\n" + + " -bottomnote HTML code to include at the bottom of each page.\n" + + " -nofixhtml If not specified, heurestics will be applied to\n" + + " fix broken HTML code in comments.\n" + + " -nohtmlwarn Do not emit warnings when encountering broken HTML\n" + + " code.\n" + + " -noemailwarn Do not emit warnings when encountering strings like\n" + + " <abc@foo.com>.\n" + + " -indentstep <n> How many spaces to indent each tag level in\n" + + " generated XML code.\n" + + " -xsltdriver <class> Specifies the XSLT driver to use for transformation.\n" + + " By default, xsltproc is used.\n" + + " -postprocess <class> XmlDoclet postprocessor class to apply after XSL\n" + + " transformation.\n" + + " -compress Generated info pages will be Zip-compressed.\n" + + " -workpath Specify a temporary directory to use.\n" + + " -authormail <type> Specify handling of mail addresses in @author tags.\n" + + " no-replace do not replace mail addresses (default).\n" + + " mailto-name replace by <a>Real Name</a>.\n" + + " name-mailto-address replace by Real Name (<a>abc@foo.com</a>).\n" + + " name-mangled-address replace by Real Name (<a>abc AT foo DOT com</a>).\n" + **/ + ); + } + + /** + * The root of the gjdoc tool. + * + * @return all the options of the gjdoc application. + */ + public static RootDocImpl getRootDoc() + { + return rootDoc; + } + + /** + * Get the gjdoc singleton. + * + * @return the gjdoc instance. + */ + public static Main getInstance() + { + return instance; + } + + /** + * Is this access level covered? + * + * @param accessLevel + * the access level we want to know if it is covered. + * @return true if the access level is covered. + */ + public boolean includeAccessLevel(int accessLevel) + { + return coverageTemplates[option_coverage][accessLevel]; + } + + /** + * Is the doclet running? + * + * @return true if it's running + */ + public boolean isDocletRunning() + { + return docletRunning; + } + + /** + * Check the charset. Check that all the characters of the string 'toCheck' + * and query if they exist in the 'charSet'. The order does not matter. The + * number of times a character is in the variable does not matter. + * + * @param toCheck + * the charset to check. + * @param charSet + * the reference charset + * @return true if they match. + */ + public static boolean checkCharSet(String toCheck, String charSet) + { + for (int i = 0; i < toCheck.length(); ++i) + { + if (charSet.indexOf(toCheck.charAt(i)) < 0) + return false; + } + return true; + } + + /** + * Makes the RootDoc eligible for the GC. + */ + public static void releaseRootDoc() + { + rootDoc.flush(); + } + + /** + * Return whether the -breakiterator option has been specified. + */ + public boolean isUseBreakIterator() + { + return this.option_breakiterator + || !getLocale().getLanguage().equals(Locale.ENGLISH.getLanguage()); + } + + /** + * Return whether boilerplate license text should be copied. + */ + public boolean isCopyLicenseText() + { + return this.option_licensetext; + } + + /** + * Return the locale specified using the -locale option or the + * default locale; + */ + public Locale getLocale() + { + return this.option_locale; + } + + /** + * Return the collator to use based on the specified -locale + * option. If no collator can be found for the given locale, a + * warning is emitted and the default collator is used instead. + */ + public Collator getCollator() + { + if (null == this.collator) { + Locale locale = getLocale(); + this.collator = Collator.getInstance(locale); + Locale defaultLocale = Locale.getDefault(); + if (null == this.collator + && !defaultLocale.equals(locale)) { + this.collator = Collator.getInstance(defaultLocale); + if (null != this.collator) { + reporter.printWarning("No collator found for locale " + + locale.getDisplayName() + + "; using collator for default locale " + + defaultLocale.getDisplayName() + + "."); + } + else { + this.collator = Collator.getInstance(); + reporter.printWarning("No collator found for specified locale " + + locale.getDisplayName() + + " or default locale " + + defaultLocale.getDisplayName() + + ": using default collator."); + } + } + if (null == this.collator) { + this.collator = Collator.getInstance(); + reporter.printWarning("No collator found for locale " + + locale.getDisplayName() + + ": using default collator."); + } + } + return this.collator; + } + + public boolean isCacheRawComments() + { + return true; + } + + public String getGjdocVersion() + { + if (null == gjdocVersion) { + gjdocVersion = gnu.classpath.Configuration.CLASSPATH_VERSION; + } + return gjdocVersion; + } + + public boolean isReflectionEnabled() + { + return this.option_reflection; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/MemberDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/MemberDocImpl.java new file mode 100644 index 000000000..acc812863 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/MemberDocImpl.java @@ -0,0 +1,235 @@ +/* gnu.classpath.tools.gjdoc.MemberDocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.util.*; +import com.sun.javadoc.*; + +public abstract class MemberDocImpl extends ProgramElementDocImpl implements MemberDoc { + + protected String typeName; + protected Type type; + + public MemberDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + SourcePosition position) { + + super(containingClass, + containingPackage, + position); + } + + public String qualifiedName() { + return containingClass().qualifiedName()+"."+name(); + } + + public boolean isSynthetic() { + return false; + } + + int parseModifiers(char[] source, int startIndex, int endIndex) { + + Debug.log(9,"parseModifiers '"+new String(source,startIndex,endIndex-startIndex)+"'"); + + final int STATE_NORMAL = 1; + final int STATE_STARC = 2; + final int STATE_SLASHC = 3; + + int state = STATE_NORMAL; + + StringBuffer word = new StringBuffer(); + StringBuffer typeNameBuf = new StringBuffer(); + int lastWordStart = startIndex; + int firstChar = 0; + int lastChar = 0; + for (; startIndex<endIndex; ++startIndex) { + if (state==STATE_STARC) { + if (startIndex<endIndex-1 && source[startIndex]=='*' && source[startIndex+1]=='/') { + ++startIndex; + state=STATE_NORMAL; + } + } + else if (state==STATE_SLASHC) { + if (source[startIndex]=='\n') { + state=STATE_NORMAL; + } + } + else if (startIndex<endIndex-1 && source[startIndex]=='/' && source[startIndex+1]=='*') { + ++startIndex; + state=STATE_STARC; + } + else if (source[startIndex]=='=' || source[startIndex]=='(' || source[startIndex]==';') { + typeName = typeNameBuf.toString(); + return lastWordStart; + } + else if (Parser.WHITESPACE.indexOf(source[startIndex])>=0 + || (startIndex > 0 && source[startIndex-1] == ']' && source[startIndex] != '[')) { + if (word.length()>0 && lastChar != '.') { + if (processModifier(word.toString())) { + } + else if (typeNameBuf.length()==0 && !isConstructor()) { + typeNameBuf.setLength(0); + typeNameBuf.append(word); + } + else if ((firstChar=='[' || firstChar==']') && !isConstructor()) { + typeNameBuf.append(word); + } + else { + typeName = typeNameBuf.toString(); + return lastWordStart; + } + word.setLength(0); + lastWordStart=startIndex; + } + } + else { + if (lastWordStart<0) lastWordStart=startIndex; + lastChar = source[startIndex]; + if (0 == word.length()) { + firstChar = lastChar; + } + word.append((char)lastChar); + } + } + + typeName = typeNameBuf.toString(); + return startIndex; + } + + public Type type() { + //public Type type() throws ParseException { + Debug.log(9,"type() called on "+containingClass()+"."+this); + if (type==null) { + try { + type=((ClassDocImpl)containingClass()).typeForString(typeName); + } catch (ParseException e) { + System.err.println("FIXME: add try-catch to force compilation"); + e.printStackTrace(); + } + } + return type; + } + + + protected void setName(String name) { + this.name=name; + } + private String name; + + + public String name() { + return name; + } + + public void setTypeName(String typeName) { + this.typeName=typeName; + this.type=null; + } + + public String getTypeName() { + return typeName; + } + + // return true if this Doc is include in the active set. + public boolean isIncluded() { + return Main.getInstance().includeAccessLevel(accessLevel); + } + + public int compareTo(Object o) { + if (o instanceof MemberDocImpl) { + int rc=name().compareTo(((MemberDocImpl)o).name()); + if (rc==0) + rc=containingClass().qualifiedName().compareTo(((MemberDocImpl)o).containingClass().qualifiedName()); + return rc; + } + else { + return super.compareTo(o); + } + } + + void resolve() { + + if (type==null && typeName!=null) { + Debug.log(1, "MemberDocImpl.resolve(), looking up type named "+typeName); + try { + type=((ClassDocImpl)containingClass()).typeForString(typeName); + } catch (ParseException e) { + //System.err.println("FIXME: add try-catch to force compilation"); + //e.printStackTrace(); + Debug.log(1, "INTERNAL WARNING: Couldn't find type for name '"+typeName+"'"); + } + } + + if (type instanceof ClassDocProxy) { + String className=type.qualifiedTypeName(); + ClassDoc realClassDoc=((ClassDocImpl)containingClass()).findClass(className, type.dimension()); + if (realClassDoc!=null) { + type=realClassDoc; + } + else { + //throw new Error("Class not found: "+className); + /*** This is not an error, the class was not included + * on the command line. Perhaps emit a notice here. + * + + Main.getRootDoc().printError("Class not found '" + + className + + "' in class '" + + containingClass().qualifiedName() + + "' member '" + + name() + + "'"); + */ + } + } + } + + public void resolveComments() + { + super.resolveComments(); + + if (tagMap.isEmpty()) { + TagContainer inheritedTagMap = ClassDocImpl.findInheritedDoc(containingClass(), + this, + null); + if (null != inheritedTagMap) { + this.tagMap = inheritedTagMap.getTagMap(); + } + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/MethodDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/MethodDocImpl.java new file mode 100644 index 000000000..78748c848 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/MethodDocImpl.java @@ -0,0 +1,64 @@ +/* gnu.classpath.tools.gjdoc.MethodDocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.util.*; +import com.sun.javadoc.*; + +public class MethodDocImpl extends ExecutableMemberDocImpl implements MethodDoc { + + public MethodDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + SourcePosition position) { + + super(containingClass, + containingPackage, + position); + } + + // Is this Doc item a class. + public boolean isMethod() { + return true; + } + + public Type returnType() { + return super.type(); + } + + public String toString() { return name()+((signature()==null)?"()":signature()); } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/PackageDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/PackageDocImpl.java new file mode 100644 index 000000000..84960bcf3 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/PackageDocImpl.java @@ -0,0 +1,223 @@ +/* gnu.classpath.tools.gjdoc.PackageDocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.io.File; + +class PackageDocImpl extends DocImpl implements GjdocPackageDoc { + + private String packageName; + private File packageDirectory; + + private Set allClassesSet = new TreeSet(); + private List ordinaryClassesList = new ArrayList(); + private List exceptionsList = new ArrayList(); + private List interfacesList = new ArrayList(); + private List errorsList = new ArrayList(); + + private ClassDoc[] allClasses; + private ClassDoc[] ordinaryClasses; + private ClassDoc[] exceptions; + private ClassDoc[] interfaces; + private ClassDoc[] errors; + + PackageDocImpl(String packageName) { + super(null); + this.packageName=packageName; + } + + public void addClass(ClassDoc classDoc) { + if (Main.getInstance().includeAccessLevel(((ClassDocImpl)classDoc).accessLevel)) { + allClassesSet.add(classDoc); + } + } + + public void resolve() { + for (Iterator it=allClassesSet.iterator(); it.hasNext(); ) { + ClassDocImpl classDoc=(ClassDocImpl)it.next(); + try { + classDoc.resolve(); + } catch (ParseException e) { + System.err.println("FIXME: add try-catch to force compilation" + + e); + } + + if (classDoc.isInterface()) { + interfacesList.add(classDoc); + } + else if (classDoc.isException()) { + exceptionsList.add(classDoc); + } + else if (classDoc.isError()) { + errorsList.add(classDoc); + } + else { + ordinaryClassesList.add(classDoc); + } + } + } + + public void resolveComments() { + if (rawDocumentation!=null) { + this.tagMap=parseCommentTags(rawDocumentation.toCharArray(), + 0, + rawDocumentation.length(), + null, + null, + null, + null); + } + + resolveTags(); + } + + public String name() { + return packageName; + } + + public ClassDoc[] allClasses() + { + if (null == this.allClasses) { + this.allClasses = toClassDocArray(allClassesSet); + } + return this.allClasses; + } + + public ClassDoc[] ordinaryClasses() + { + if (null == this.ordinaryClasses) { + this.ordinaryClasses = toClassDocArray(ordinaryClassesList); + } + return this.ordinaryClasses; + } + + + public ClassDoc[] exceptions() + { + if (null == this.exceptions) { + this.exceptions = toClassDocArray(exceptionsList); + } + return this.exceptions; + } + + public ClassDoc[] interfaces() + { + if (null == this.interfaces) { + this.interfaces = toClassDocArray(interfacesList); + } + return this.interfaces; + } + + public ClassDoc[] errors() + { + if (null == this.errors) { + this.errors = toClassDocArray(errorsList); + } + return this.errors; + } + + private ClassDoc[] toClassDocArray(Collection classDocList) + { + ClassDoc[] result = (ClassDoc[])classDocList.toArray(new ClassDoc[classDocList.size()]); + Arrays.sort(result); + return result; + } + + public ClassDoc findClass(String name) { + return Main.getRootDoc().classNamed(packageName+"."+name); + } + + public void dump(int level) { + Debug.log(level, "All classes:"); + Debug.dumpArray(level, allClasses()); + + Debug.log(level, "Ordinary classes:"); + Debug.dumpArray(level, ordinaryClasses()); + + } + + public static final PackageDocImpl DEFAULT_PACKAGE = new PackageDocImpl(""); + + public boolean isPackage() { + return true; + } + + public boolean isIncluded() { + return isIncluded; + } + + void setIsIncluded(boolean b) { + this.isIncluded=b; + } + + private boolean isIncluded = false; + + public String toString() { + return packageName; + } + + public int compareTo(Object o) { + if (o!=null && o instanceof PackageDocImpl) + return name().compareTo(((PackageDocImpl)o).name()); + else + return 0; + } + + public boolean equals(Object o) { + if (o!=null && o instanceof PackageDocImpl) + return name().equals(((PackageDocImpl)o).name()); + else + return false; + } + + /** + * Sets the directory containing this package's source files. + */ + public void setPackageDirectory(File packageDirectory) { + this.packageDirectory = packageDirectory; + } + + /** + * Gets the directory containing this package's source files. + */ + public File packageDirectory() { + return this.packageDirectory; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParamTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParamTagImpl.java new file mode 100644 index 000000000..a7491dc8e --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParamTagImpl.java @@ -0,0 +1,81 @@ +/* gnu.classpath.tools.gjdoc.ParamTagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.text.*; + +public class ParamTagImpl extends AbstractTagImpl implements ParamTag { + + private String parameterName; + private String parameterComment; + + public ParamTagImpl(String text, + ClassDocImpl contextClass, + MemberDocImpl contextMember) { + super(text); + char[] textarr=text.toCharArray(); + int i=0; + for (; i<textarr.length; ++i) { + if (!Parser.isWhitespace(textarr[i])) break; + } + for (; i<textarr.length; ++i) { + if (Parser.isWhitespace(textarr[i])) { + parameterName=new String(textarr,0,i).trim(); + parameterComment=new String(textarr,i,textarr.length-i).trim(); + break; + } + } + if (parameterComment!=null) { + setBody(parameterComment, contextClass, contextMember); + } + } + + public String parameterComment() { + return parameterComment; + } + + public String parameterName() { + return parameterName; + } + + public String kind() { + return "@param"; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParameterImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParameterImpl.java new file mode 100644 index 000000000..29b8e9b70 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParameterImpl.java @@ -0,0 +1,69 @@ +/* gnu.classpath.tools.gjdoc.ParameterImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; + +public class ParameterImpl implements Parameter { + + String name; + String typeName; + Type type; + + ParameterImpl(String name, String typeName, Type type) { + + this.name=name; + this.typeName=typeName; + this.type=type; + } + + public void resolve(ClassDoc classContext) { + if (type instanceof ClassDocProxy) { + String className=type.qualifiedTypeName(); + ClassDoc realClassDoc=((ClassDocImpl)classContext).findClass(className, type.dimension()); + if (realClassDoc!=null) { + type=realClassDoc; + } + } + } + + public String name() { return name; } + public String typeName() { return typeName; } + public Type type() { return type; } + public String toString() { return typeName+" "+name; } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParseException.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParseException.java new file mode 100644 index 000000000..3ecd84592 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ParseException.java @@ -0,0 +1,51 @@ +/* gnu.classpath.tools.gjdoc.ParseException + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +class ParseException extends Exception { + + public ParseException() + { + } + + public ParseException(String msg) + { + super(msg); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Parser.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Parser.java new file mode 100644 index 000000000..d355b5384 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Parser.java @@ -0,0 +1,1064 @@ +/* gnu.classpath.tools.gjdoc.Parser + Copyright (C) 2001, 2005, 2008 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.util.*; + +import com.sun.javadoc.*; + +import gnu.classpath.tools.IOToolkit; +import gnu.classpath.tools.NotifyingInputStreamReader; +import gnu.classpath.tools.MalformedInputListener; +import gnu.classpath.tools.MalformedInputEvent; + + class IgnoredFileParseException extends ParseException + { + // marker exception + } + + abstract class SourceComponent { + + abstract int match(char[] source, int index) throws ParseException; + + int process(Parser parser, char[] source, int startIndex, int endIndex) throws ParseException, IOException { + return endIndex; + } + + int getEndIndex(char[] source, int endIndex) throws ParseException { + return endIndex; + } + } + + abstract class BlockSourceComponent extends SourceComponent { + + int getEndIndex(char[] source, int endIndex) throws ParseException { + return Parser.skipExpression(source, endIndex, 1, '\0'); + } + + } + + class Whitespace extends SourceComponent { + + int match(char[] source, int index) { + + int rc=index; + int slen=source.length; + while (rc<slen && Parser.WHITESPACE.indexOf(source[rc])>=0) ++rc; + + return (rc!=index) ? rc : -1; + } + } + + class BracketClose extends SourceComponent { + + int match(char[] source, int index) { + if (source[index]=='}') { + return index+1; + } + else { + return -1; + } + } + + int process(Parser parser, char[] source, int startIndex, int endIndex) + throws ParseException, IOException + { + parser.classClosed(); + return endIndex; + } + } + + class CommentComponent extends SourceComponent { + + int match(char[] source, int index) throws ParseException { + if (index+1<source.length && source[index]=='/' && source[index+1]=='*') { + for (index+=2; index+1<source.length; ++index) { + if (source[index]=='*' && source[index+1]=='/') + return index+2; + } + throw new ParseException("unexpected end of input"); + } + return -1; + } + + int process(Parser parser, char[] source, int startIndex, int endIndex) { + + if (source[startIndex+0]=='/' + && source[startIndex+1]=='*' + && source[startIndex+2]=='*') { + + parser.setLastComment(new String(source, startIndex, endIndex-startIndex)); + } + else if (null == parser.getBoilerplateComment() && Main.getInstance().isCopyLicenseText()) { + String boilerplateComment = new String(source, startIndex + 2, endIndex-startIndex - 4); + if (boilerplateComment.toLowerCase().indexOf("copyright") >= 0) { + parser.setBoilerplateComment(boilerplateComment); + } + } + + return endIndex; + } + } + + class SlashSlashCommentComponent extends SourceComponent { + + int match(char[] source, int index) { + if (index+1<source.length && source[index]=='/' && source[index+1]=='/') { + index+=2; + while (index<source.length && source[index]!='\n') + ++index; + return index; + } + else { + return -1; + } + } + } + + class EmptyStatementComponent extends SourceComponent { + + int match(char[] source, int index) { + while (index < source.length + && Parser.isWhitespace(source[index])) { + ++ index; + } + if (index < source.length && source[index] == ';') { + return index+1; + } + else { + return -1; + } + } + } + + class ImportComponent extends SourceComponent { + + int match(char[] source, int index) { + if (index+7<source.length) { + if (source[index+0]=='i' + && source[index+1]=='m' + && source[index+2]=='p' + && source[index+3]=='o' + && source[index+4]=='r' + && source[index+5]=='t' + && Parser.WHITESPACE.indexOf(source[index+6])>=0) { + + for (index+=7; index<source.length && source[index]!=';'; ++index) + ; + + return index+1; + } + } + return -1; + } + + int process(Parser parser, char[] source, int startIndex, int endIndex) throws ParseException, IOException { + String importString=new String(source,startIndex+7,endIndex-startIndex-7-1).trim(); + parser.importEncountered(importString); + return endIndex; + } + } + + class PackageComponent extends SourceComponent { + + int match(char[] source, int index) { + if (index+10<source.length) { + if (source[index+0]=='p' + && source[index+1]=='a' + && source[index+2]=='c' + && source[index+3]=='k' + && source[index+4]=='a' + && source[index+5]=='g' + && source[index+6]=='e' + && Parser.WHITESPACE.indexOf(source[index+7])>=0) { + + for (index+=7; index<source.length && source[index]!=';'; ++index) + ; + + return index+1; + } + } + return -1; + } + + int process(Parser parser, char[] source, int startIndex, int endIndex) { + String packageName=new String(source,startIndex+8,endIndex-startIndex-8-1).trim(); + parser.packageOpened(packageName); + return endIndex; + } + } + + class FieldComponent extends SourceComponent { + + int match(char[] source, int index) throws ParseException { + boolean isField=false; + final int STATE_NORMAL=1; + final int STATE_SLASHC=2; + final int STATE_STARC=3; + final int STATE_FIELDVAL=4; + final int STATE_STRING=5; + final int STATE_SINGLEQUOTED=6; + final int STATE_STRING_BS=7; + final int STATE_SINGLEQUOTED_BS=8; + + int state=STATE_NORMAL; + int prevState=STATE_NORMAL; + + int fieldValueLevel = 0; + + for (; index<source.length && !isField; ++index) { + if (state==STATE_STARC) { + if (index<source.length-1 && source[index]=='*' && source[index+1]=='/') { + ++index; + state=prevState; + } + } + else if (state==STATE_SLASHC) { + if (source[index]=='\n') { + state=prevState; + } + } + else if (state==STATE_STRING) { + if (source[index]=='\\') { + state=STATE_STRING_BS; + } + else if (source[index]=='\"') { + state=prevState; + } + } + else if (state==STATE_STRING_BS) { + state=STATE_STRING; + } + else if (state==STATE_SINGLEQUOTED) { + if (source[index]=='\\') { + state=STATE_SINGLEQUOTED_BS; + } + else if (source[index]=='\'') { + state=prevState; + } + } + else if (state==STATE_SINGLEQUOTED_BS) { + state=STATE_SINGLEQUOTED; + } + else if (state==STATE_FIELDVAL) { + if (source[index]=='/') { + if (index<source.length-1 && source[index+1]=='*') { + state=STATE_STARC; + ++index; + } + else if (index<source.length-1 && source[index+1]=='/') { + state=STATE_SLASHC; + ++index; + } + } + else if (source[index]=='{') { + ++ fieldValueLevel; + } + else if (source[index]=='}') { + -- fieldValueLevel; + } + else if (source[index]=='\"') { + state=STATE_STRING; + } + else if (source[index]=='\'') { + state=STATE_SINGLEQUOTED; + } + else if (source[index]==';' && 0 == fieldValueLevel) { + isField=true; + break; + } + } + else switch (source[index]) { + case '/': + if (index<source.length-1 && source[index+1]=='*') { + state=STATE_STARC; + ++index; + } + else if (index<source.length-1 && source[index+1]=='/') { + state=STATE_SLASHC; + ++index; + } + break; + case '{': // class + case '(': // method + return -1; + case '=': // field + state=STATE_FIELDVAL; + prevState=state; + continue; + case ';': // field + isField=true; + break; + } + if (isField) break; + } + if (!isField || index==source.length) { + return -1; + } + + //System.err.println("char is "+source[index]); + + if (source[index]!=';') { + index=Parser.skipExpression(source, index, 0, ';'); + } + return index+1; + } + + int process(Parser parser, char[] source, int startIndex, int endIndex) { + + //Debug.log(9,"found package statement: \""+str+"\""); + //Debug.log(9,"found function component: '"+str+"'"); + //xxx(new FieldDocImpl(ctx.classDoc, ctx.classDoc.containingPackage(), 0, false, false)); + + // Ignore superfluous semicoli after class definition + if (endIndex-startIndex<=1) return endIndex; + + //assert (parser.ctx!=null); + Collection fields=FieldDocImpl.createFromSource(parser.ctx.classDoc, + parser.ctx.classDoc.containingPackage(), + source, startIndex, endIndex); + + for (Iterator it=fields.iterator(); it.hasNext(); ) { + FieldDocImpl field=(FieldDocImpl)it.next(); + boolean fieldHasSerialTag=!field.isTransient() && !field.isStatic(); //field.hasSerialTag(); + if ((field.isIncluded() || fieldHasSerialTag) && parser.getAddComments()) { + field.setRawCommentText(parser.getLastComment()); + } + parser.ctx.fieldList.add(field); + if (field.isIncluded()) { + parser.ctx.filteredFieldList.add(field); + } + if (fieldHasSerialTag) { + parser.ctx.sfieldList.add(field); + } + } + + parser.setLastComment(null); + return endIndex; + } + + + } + + class FunctionComponent extends BlockSourceComponent { + + int getEndIndex(char[] source, int endIndex) throws ParseException { + if (source[endIndex-1]==';') { + return endIndex; + } + else { + return super.getEndIndex(source, endIndex); + } + } + + int process(Parser parser, char[] source, int startIndex, int endIndex) throws IOException, ParseException { + + //ctx.fieldList.add(FieldDocImpl.createFromSource(source, startIndex, endIndex)); + + //System.out.println("function match '"+new String(source,startIndex,endIndex-startIndex)+"'"); + ExecutableMemberDocImpl execDoc=MethodDocImpl.createFromSource(parser.ctx.classDoc, + parser.ctx.classDoc.containingPackage(), + source, startIndex, endIndex); + + if (parser.getAddComments()) + execDoc.setRawCommentText(parser.getLastComment()); + + parser.setLastComment(null); + + if (execDoc.isMethod()) { + parser.ctx.methodList.add(execDoc); + if (execDoc.isIncluded()) { + parser.ctx.filteredMethodList.add(execDoc); + } + } + else { + parser.ctx.constructorList.add(execDoc); + if (execDoc.isIncluded()) { + parser.ctx.filteredConstructorList.add(execDoc); + } + } + + if (execDoc.isMethod() + && (execDoc.name().equals("readObject") + || execDoc.name().equals("writeObject") + || execDoc.name().equals("readExternal") + || execDoc.name().equals("writeExternal") + || execDoc.name().equals("readResolve"))) { + // FIXME: add readExternal here? + + parser.ctx.maybeSerMethodList.add(execDoc); + } + + return endIndex; + } + + int match(char[] source, int index) { + boolean isFunc=false; + final int STATE_NORMAL=1; + final int STATE_SLASHC=2; + final int STATE_STARC=3; + int state=STATE_NORMAL; + for (; index<source.length && !isFunc; ++index) { + if (state==STATE_STARC) { + if (source[index]=='*' && source[index+1]=='/') { + ++index; + state=STATE_NORMAL; + } + } + else if (state==STATE_SLASHC) { + if (source[index]=='\n') { + state=STATE_NORMAL; + } + } + else switch (source[index]) { + case '/': + if (source[index+1]=='*') { + state=STATE_STARC; + ++index; + } + else if (source[index+1]=='/') { + state=STATE_SLASHC; + ++index; + } + break; + case '=': // field + case ';': // field + case '{': // class + return -1; + case '(': + isFunc=true; + break; + } + if (isFunc) break; + } + if (!isFunc || index==source.length) + return -1; + + for (; index<source.length && (state!=STATE_NORMAL || (source[index]!='{' && source[index]!=';')); ++index) + if (state==STATE_SLASHC && source[index]=='\n') { + state=STATE_NORMAL; + } + else if (index<source.length-1) { + if (state==STATE_STARC) { + if (source[index]=='*' && source[index+1]=='/') { + state=STATE_NORMAL; + } + } + else { + if (source[index]=='/' && source[index+1]=='*') { + state=STATE_STARC; + } + else if (source[index]=='/' && source[index+1]=='/') { + state=STATE_SLASHC; + } + } + } + return index+1; + } + + + } + + class StaticBlockComponent extends BlockSourceComponent { + + int process(Parser parser, char[] source, int startIndex, int endIndex) { + //Debug.log(9,"found package statement: \""+str+"\""); + //Debug.log(9,"found function component: '"+str+"'"); + parser.setLastComment(null); + return endIndex; + } + + int match(char[] source, int index) { + if (source[index]=='{') return index+1; + + if (index+7<source.length) { + if (source[index+0]=='s' + && source[index+1]=='t' + && source[index+2]=='a' + && source[index+3]=='t' + && source[index+4]=='i' + && source[index+5]=='c') { + + for (index+=6; index<source.length && Parser.WHITESPACE.indexOf(source[index])>=0; ++index) + ; + + if (index<source.length && source[index]=='{') + return index+1; + else + return -1; + } + } + return -1; + } + + } + + class ClassComponent extends SourceComponent { + + int match(char[] source, int index) { + boolean isClass=false; + for (; index<source.length && !isClass; ++index) { + switch (source[index]) { + case '/': // possible comment + if (index<source.length-1) { + char c = source[index+1]; + if ('/' == c) { + index += 2; + while (index<source.length && source[index]!=10) { + ++ index; + } + } + else if ('*' == c) { + index += 3; + while (index<source.length && (source[index-1] != '*' || source[index]!='/')) { + ++ index; + } + } + } + break; + case '@': // annotation + index += 1; + while(index<source.length && Character.isJavaIdentifierPart(source[index])) { + ++ index; + } + if (index<source.length && source[index]=='(') { + int parLevel = 1; + index += 1; + while (index<source.length && parLevel>0) { + if (source[index] == '(') + ++ parLevel; + if (source[index] == ')') + -- parLevel; + ++ index; + if (parLevel==0) + break; + } + } + break; + case '=': // field + case ';': // field + case '(': // function + return -1; + case '{': + isClass=true; + break; + } + if (isClass) break; + } + if (!isClass || index>=source.length) + return -1; + + return index+1; + } + + int process(Parser parser, char[] source, int startIndex, int endIndex) throws ParseException, IOException { + + parser.classOpened(source, startIndex, endIndex); + if (parser.getAddComments()) + parser.ctx.classDoc.setRawCommentText(parser.getLastComment()); + parser.setLastComment(null); + if (parser.ctx.classDoc.isEnum()) + { + int depth = 0; + for (int a = endIndex; a < source.length; ++a) + { + Debug.log(9, "Enum skipping " + a); + if (source[a] == '{') + { + Debug.log(1, "Found inner { in enum"); + ++depth; + } + if (source[a] == '}') + { + if (depth > 0) + { + Debug.log(1, "Found inner } in enum"); + --depth; + } + else + { + Debug.log(1, "Found enum }"); + parser.classClosed(); + return a + 1; + } + } + } + } + int rc=parser.parse(source, endIndex, parser.getClassLevelComponents()); + return rc; + } + + } + + +public class Parser { + + + static int skipExpression(char[] source, int endIndex, int level, char delimiter) throws ParseException { + + int orgEndIndex=endIndex; + + final int STATE_NORMAL=1; + final int STATE_STARC=2; + final int STATE_SLASHC=3; + final int STATE_CHAR=4; + final int STATE_STRING=5; + + int state=STATE_NORMAL; + int prev=0; + for (; !((level==0 && state==STATE_NORMAL && (delimiter=='\0' || source[endIndex]==delimiter))) && endIndex<source.length; ++endIndex) { + int c=source[endIndex]; + if (state==STATE_NORMAL) { + if (c=='}') --level; + else if (c=='{') ++level; + else if (c=='/' && prev=='/') { state=STATE_SLASHC; c=0; } + else if (c=='*' && prev=='/') { state=STATE_STARC; c=0; } + else if (c=='\'' && prev!='\\') { state=STATE_CHAR; c=0; } + else if (c=='\"' && prev!='\\') { state=STATE_STRING; c=0; } + } + else if (state==STATE_SLASHC) { + if (c=='\n') state=STATE_NORMAL; + } + else if (state==STATE_CHAR) { + if (c=='\'' && prev!='\\') state=STATE_NORMAL; + else if (c=='\\' && prev=='\\') c=0; + } + else if (state==STATE_STRING) { + if (c=='\"' && prev!='\\') state=STATE_NORMAL; + else if (c=='\\' && prev=='\\') c=0; + } + else { + if (c=='/' && prev=='*') { state=STATE_NORMAL; c=0; } + } + prev=c; + } + if (level>0) + throw new ParseException("Unexpected end of source."); + else { + String rc=new String(source, orgEndIndex, endIndex-orgEndIndex); + return endIndex; + } + } + + private boolean addComments = false; + + public boolean getAddComments() + { + return this.addComments; + } + + public static final String WHITESPACE=" \t\r\n"; + + public static final boolean isWhitespace(char c) { + return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ''); + //return WHITESPACE.indexOf(c)>=0; + } + + private int currentLine; + + static char[] loadFile(final File file, String encoding) + throws IOException + { + InputStream in = new FileInputStream(file); + NotifyingInputStreamReader notifyingInput + = new NotifyingInputStreamReader(in, encoding); + notifyingInput.addMalformedInputListener(new MalformedInputListener() { + public void malformedInputEncountered(MalformedInputEvent event) { + Main.getRootDoc().printWarning("Illegal character in file " + file + ", line " + event.getLineNumber() + ", column " + event.getColumnNumber()); + try { + Main.getRootDoc().printWarning(IOToolkit.getLineFromFile(file, event.getLineNumber())); + Main.getRootDoc().printWarning(IOToolkit.getColumnDisplayLine(event.getColumnNumber())); + } + catch (IOException ignore) { + } + } + }); + Reader reader + = new BufferedReader(notifyingInput); + char[] result = IOToolkit.readFully(reader); + reader.close(); + return result; + } + + private SourceComponent[] sourceLevelComponents; + private SourceComponent[] classLevelComponents; + + public SourceComponent[] getClassLevelComponents() + { + return this.classLevelComponents; + } + + public Parser() { + try { + + sourceLevelComponents=new SourceComponent[] { + new Whitespace(), + new CommentComponent(), + new SlashSlashCommentComponent(), + new PackageComponent(), + new EmptyStatementComponent(), + new ImportComponent(), + new ClassComponent(), + }; + + classLevelComponents=new SourceComponent[] { + new Whitespace(), + new BracketClose(), + new CommentComponent(), + new SlashSlashCommentComponent(), + new FunctionComponent(), + new StaticBlockComponent(), + new ImportComponent(), + new ClassComponent(), + new FieldComponent(), + }; + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public int getNumberOfProcessedFiles() { + return processedFiles.size(); + } + + static Set processedFiles = new HashSet(); + + ClassDocImpl processSourceFile(File file, boolean addComments, + String encoding, String expectedPackageName) + throws IOException, ParseException + { + //System.err.println("Processing " + file + "..."); + this.currentFile = file; + this.currentPackage = null; + this.currentPackageName = null; + this.expectedPackageName = expectedPackageName; + this.outerClass = null; + this.boilerplateComment = null; + + this.addComments=addComments; + + if (processedFiles.contains(file)) { + return null; + } + + processedFiles.add(file); + + Debug.log(1,"Processing file "+file); + + contextStack.clear(); + ctx=null; + + importedClassesList.clear(); + importedStringList.clear(); + importedPackagesList.clear(); + importedStatementList.clear(); + + currentLine = 1; + + char[] source = loadFile(file, encoding); + + try { + parse(source, 0, sourceLevelComponents); + + ClassDoc[] importedClasses=(ClassDoc[])importedClassesList.toArray(new ClassDoc[0]); + PackageDoc[] importedPackages=(PackageDoc[])importedPackagesList.toArray(new PackageDoc[0]); + + if (Main.DESCEND_IMPORTED) { + for (int i=0; i<importedClasses.length; ++i) { + Main.getRootDoc().scheduleClass(currentClass, importedClasses[i].qualifiedName()); + } + } + + + if (contextStack.size()>0) { + Debug.log(1,"-->contextStack not empty! size is "+contextStack.size()); + } + + return outerClass; + } + catch (IgnoredFileParseException ignore) { + Debug.log(1, "File ignored: " + ignore); + return null; + } + } + + int parse(char[] source, int index, SourceComponent[] componentTypes) throws ParseException, IOException { + + while (index<source.length) { + + int match=-1; + int i=0; + for (; i<componentTypes.length; ++i) { + if ((match=componentTypes[i].match(source, index))>=0) { + //Debug.log(1,componentTypes[i].getClass().getName()+" ("+match+"/"+source.length+")"); + break; + } + } + + if (i<componentTypes.length) { + int endIndex=componentTypes[i].getEndIndex(source, match); + Debug.log(9, "Processing " + new String(source,index,endIndex-index) + " with " + componentTypes[i]); + index=componentTypes[i].process(this, source, index, endIndex); + if (index<0) { + //Debug.log(9,"exiting parse because of "+componentTypes[i].getClass().getName()+" (\""+new String(source, index, endIndex-index)+"\")"); + return endIndex; + } + } + else { + //Debug.log(9,"index="+index+", source.length()="+source.length); + throw new ParseException("unmatched input in line "+currentLine+": "+new String(source, index, Math.min(50,source.length-index))); + } + + } + //Debug.log(9,"exiting parse normally, index="+index+" source.length="+source.length); + return index; + } + + private static int countNewLines(String source) { + int i=0; + int rc=0; + while ((i=source.indexOf('\n',i)+1)>0) + ++rc; + return rc; + } + + public void processSourceDir(File dir, String encoding, String expectedPackageName) + throws IOException, ParseException + { + Debug.log(9,"Processing "+dir.getParentFile().getName()+"."+dir.getName()); + File[] files=dir.listFiles(); + if (null!=files) { + for (int i=0; i<files.length; ++i) { + if (files[i].getName().toLowerCase().endsWith(".java")) { + processSourceFile(files[i], true, encoding, expectedPackageName); + } + } + } + } + + void classOpened(char[] source, int startIndex, int endIndex) throws ParseException, IOException { + + referencedClassesList.clear(); + + if (null == currentPackage) { + + if (expectedPackageName != null) { + if (null == currentPackageName || + !currentPackageName.equals(expectedPackageName)) { + + Main.getRootDoc().printWarning("Ignoring file " + currentFile + ": (wrong package, " + currentPackageName + "!=" + expectedPackageName + ")"); + throw new IgnoredFileParseException(); + } + } + + if (null != currentPackageName) { + currentPackage = Main.getRootDoc().findOrCreatePackageDoc(currentPackageName); + } + else { + currentPackage = Main.getRootDoc().findOrCreatePackageDoc(""); + } + } + + if (currentPackageName != null) + importedStatementList.add(currentPackageName + ".*"); + importedStatementList.add("java.lang.*"); + + ClassDocImpl classDoc + = ClassDocImpl.createInstance((ctx!=null)?(ctx.classDoc):null, currentPackage, + null, + (PackageDoc[])importedPackagesList.toArray(new PackageDoc[0]), + source, startIndex, endIndex, + importedStatementList); + + if (ctx != null) { + ctx.innerClassesList.add(classDoc); + if (classDoc.isIncluded()) { + ctx.filteredInnerClassesList.add(classDoc); + } + } + + if (importedClassesList.isEmpty()) { + for (Iterator it=importedStringList.iterator(); it.hasNext(); ) { + importedClassesList.add(new ClassDocProxy((String)it.next(), classDoc)); + } + } + classDoc.setImportedClasses((ClassDoc[])importedClassesList.toArray(new ClassDoc[0])); + + currentPackage.addClass(classDoc); + + currentClass = classDoc; + + if (null == outerClass) { + outerClass = classDoc; + } + + if (classDoc.superclass()!=null) + referencedClassesList.add(classDoc.superclass()); + + Debug.log(1,"classOpened "+classDoc+", adding superclass "+classDoc.superclass()); + Debug.log(1,"Pushing " + ctx); + contextStack.push(ctx); + ctx=new Context(classDoc); + //Debug.log(9,"ctx="+ctx); + } + + private Doc[] toArray(List list, Doc[] template) + { + Doc[] result = (Doc[])list.toArray(template); + return result; + } + + void classClosed() throws ParseException, IOException { + ctx.classDoc.setFields((FieldDoc[])toArray(ctx.fieldList, + new FieldDoc[0])); + ctx.classDoc.setFilteredFields((FieldDoc[])toArray(ctx.filteredFieldList, + new FieldDoc[0])); + ctx.classDoc.setSerializableFields((FieldDoc[])toArray(ctx.sfieldList, new FieldDoc[0])); + ctx.classDoc.setMethods((MethodDoc[])toArray(ctx.methodList, new MethodDoc[0])); + ctx.classDoc.setFilteredMethods((MethodDoc[])toArray(ctx.filteredMethodList, new MethodDoc[0])); + ctx.classDoc.setMaybeSerMethodList(ctx.maybeSerMethodList); + ctx.classDoc.setConstructors((ConstructorDoc[])toArray(ctx.constructorList, new ConstructorDoc[0])); + ctx.classDoc.setFilteredConstructors((ConstructorDoc[])toArray(ctx.filteredConstructorList, new ConstructorDoc[0])); + + ctx.classDoc.setInnerClasses((ClassDocImpl[])toArray(ctx.innerClassesList, new ClassDocImpl[0])); + ctx.classDoc.setFilteredInnerClasses((ClassDocImpl[])toArray(ctx.filteredInnerClassesList, new ClassDocImpl[0])); + ctx.classDoc.setBoilerplateComment(boilerplateComment); + + Main.getRootDoc().addClassDoc(ctx.classDoc); + + if (Main.DESCEND_INTERFACES) { + for (int i=0; i<ctx.classDoc.interfaces().length; ++i) { + Main.getRootDoc().scheduleClass(ctx.classDoc, ctx.classDoc.interfaces()[i].qualifiedName()); + } + } + + Debug.log(1,"classClosed: "+ctx.classDoc); + + ctx=(Context)contextStack.pop(); + Debug.log(1, "Popping " + ctx); + ClassDoc[] referencedClasses=(ClassDoc[])referencedClassesList.toArray(new ClassDoc[0]); + + if (Main.DESCEND_SUPERCLASS) { + for (int i=0; i<referencedClasses.length; ++i) { + Main.getRootDoc().scheduleClass(currentClass, referencedClasses[i].qualifiedName()); + } + } + } + + Context ctx = null; + Stack contextStack = new Stack(); + class Context { + Context(ClassDocImpl classDoc) { this.classDoc=classDoc; } + ClassDocImpl classDoc = null; + List fieldList = new LinkedList(); + List filteredFieldList = new LinkedList(); + List sfieldList = new LinkedList(); + List methodList = new LinkedList(); + List filteredMethodList = new LinkedList(); + List maybeSerMethodList = new LinkedList(); + List constructorList = new LinkedList(); + List filteredConstructorList = new LinkedList(); + List innerClassesList = new LinkedList(); + List filteredInnerClassesList = new LinkedList(); + } + + File currentFile = null; + String lastComment = null; + String expectedPackageName = null; + String currentPackageName = null; + PackageDocImpl currentPackage = null; + ClassDocImpl currentClass = null; + ClassDocImpl outerClass = null; + List ordinaryClassesList = new LinkedList(); + List allClassesList = new LinkedList(); + List interfacesList = new LinkedList(); + + List importedClassesList = new LinkedList(); + List importedStringList = new LinkedList(); + List importedPackagesList = new LinkedList(); + List importedStatementList = new LinkedList(); + + List referencedClassesList = new LinkedList(); + + String boilerplateComment = null; + + void packageOpened(String packageName) { + currentPackageName = packageName; + } + + void importEncountered(String importString) throws ParseException, IOException { + //Debug.log(9,"importing '"+importString+"'"); + + importedStatementList.add(importString); + + if (importString.endsWith(".*")) { + importedPackagesList.add(Main.getRootDoc().findOrCreatePackageDoc(importString.substring(0,importString.length()-2))); + } + else { + importedStringList.add(importString); + } + } + + + void setLastComment(String lastComment) { + this.lastComment=lastComment; + } + + String getLastComment() { + return this.lastComment; + } + + void setBoilerplateComment(String boilerplateComment) + { + this.boilerplateComment = boilerplateComment; + } + + String getBoilerplateComment() + { + return boilerplateComment; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ProgramElementDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ProgramElementDocImpl.java new file mode 100644 index 000000000..bdcf61bf6 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ProgramElementDocImpl.java @@ -0,0 +1,177 @@ +/* gnu.classpath.tools.gjdoc.ProgramElementDocImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.lang.reflect.Modifier; + +public abstract class ProgramElementDocImpl extends DocImpl implements ProgramElementDoc { + + protected ClassDoc containingClass; + protected PackageDoc containingPackage; + + protected boolean isFinal; + protected boolean isStatic; + protected int accessLevel=ProgramElementDocImpl.ACCESS_PACKAGEPRIVATE; + + public static final int ACCESS_PUBLIC = 0; + public static final int ACCESS_PROTECTED = 1; + public static final int ACCESS_PACKAGEPRIVATE = 2; + public static final int ACCESS_PRIVATE = 3; + + private static final String[] accessModifiers = { "public ", "protected ", "", "private "}; + + public ProgramElementDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + SourcePosition position) { + super(position); + this.containingClass=containingClass; + this.containingPackage=containingPackage; + } + public ProgramElementDocImpl(ClassDoc containingClass, SourcePosition position) { + super(position); + this.containingClass=containingClass; + this.containingPackage=containingClass.containingPackage(); + } + public ProgramElementDocImpl(ClassDoc containingClass, + PackageDoc containingPackage, + int accessLevel, + boolean isFinal, + boolean isStatic, + SourcePosition position) { + super(position); + this.containingClass=containingClass; + this.containingPackage=containingPackage; + this.accessLevel=accessLevel; + this.isFinal=isFinal; + this.isStatic=isStatic; + } + + //Get the containing class of this program element. + public ClassDoc containingClass() { + return containingClass; + } + + // Get the package that this program element is contained in. + public PackageDoc containingPackage() { + return containingPackage; + } + + // Return true if this program element is final + public boolean isFinal() { + return isFinal; + } + + // Return true if this program element is package private + public boolean isPackagePrivate() { + return accessLevel==ACCESS_PACKAGEPRIVATE; + } + + // Return true if this program element is private + public boolean isPrivate() { + return accessLevel==ACCESS_PRIVATE; + } + + // Return true if this program element is protected + public boolean isProtected() { + return accessLevel==ACCESS_PROTECTED; + } + + // Return true if this program element is public + public boolean isPublic() { + return accessLevel==ACCESS_PUBLIC; + } + + // Return true if this program element is static + public boolean isStatic() { + return isStatic; + } + + // Get modifiers string. + public String modifiers() { + return + (accessModifiers[accessLevel]+ + (isStatic()?"static ":"")+ + (isFinal()?"final ":"")).trim(); + } + + // Get the modifier specifier integer. + public int modifierSpecifier() { + return (isStatic()?Modifier.STATIC:0) + | (isFinal()?Modifier.FINAL:0) + | (isPublic()?Modifier.PUBLIC:0) + | (isProtected()?Modifier.PROTECTED:0) + | (isPrivate()?Modifier.PRIVATE:0) +// | (isAbstract()?Modifier.ABSTRACT:0) + ; + } + + // Get the fully qualified name. + public abstract String qualifiedName(); + + protected boolean processModifier(String word) { + if (word.equals("public")) { + accessLevel=ACCESS_PUBLIC; + return true; + } + else if (word.equals("protected")) { + accessLevel=ACCESS_PROTECTED; + return true; + } + else if (word.equals("private")) { + accessLevel=ACCESS_PRIVATE; + return true; + } + else if (word.equals("static")) { + isStatic=true; + return true; + } + else if (word.equals("final")) { + isFinal=true; + return true; + } + else { + return false; + } + } + + void setIsStatic(boolean b) { + this.isStatic=b; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/RootDocImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/RootDocImpl.java new file mode 100644 index 000000000..09d1be73b --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/RootDocImpl.java @@ -0,0 +1,1334 @@ +/* gnu.classpath.tools.gjdoc.RootDocImpl + Copyright (C) 2001, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.io.*; +import java.lang.reflect.*; + +public class RootDocImpl + extends DocImpl + implements GjdocRootDoc { + + private ErrorReporter reporter = new ErrorReporter(); + + private RandomAccessFile rawCommentCache; + + /** + * All options and their corresponding values which are not recognized + * by Gjdoc. These are passed to the Doclet as "custom options". + * Each element in this array is again a String array, with the + * option name as first element (including prefix dash) and possible + * option values as following elements. + */ + private String[][] customOptionArr; + + /** + * All source files explicitly specified on the command line. + * + * @contains File + */ + private List specifiedSourceFiles = new LinkedList(); + + /** + * The names of all packages explicitly specified on the + * command line. + * + * @contains String + */ + private Set specifiedPackageNames = new LinkedHashSet(); + + /** + * Stores all classes specified by the user: those given by + * individual class names on the command line, and those + * contained in the packages given on the command line. + * + * @contains ClassDocImpl + */ + private List classesList = new LinkedList(); //new LinkedList(); + + /** + * Stores all classes loaded in the course of preparing + * the documentation data. Maps the fully qualified name + * of a class to its ClassDocImpl representation. + * + * @contains String->ClassDocImpl + */ + private Map classDocMap = new HashMap(); + + /** + * Stores all packages loaded in the course of preparing + * the documentation data. Maps the package name + * to its PackageDocImpl representation. + * + * @contains String->PackageDocImpl + */ + private Map packageDocMap = new HashMap(); + + /** + * All classes specified by the user, both those explicitly + * individually specified on the command line and those contained + * in packages specified on the command line (as Array for quick + * retrieval by Doclet). This is created from classesList after + * all classes have been loaded. + */ + private ClassDocImpl[] classes; + + /** + * All classes which were individually specified on the command + * line (as Array for quick retrieval by Doclet). This is created + * from specifiedClassNames after all classes have been loaded. + */ + private List specifiedClasses; + + /** + * All packages which were specified on the command line (as Array + * for quick retrieval by Doclet). This is created from + * specifiedPackageNames after all classes have been loaded. + */ + private Set specifiedPackages; + + + /** + * Temporarily stores a list of classes which are referenced + * by classes already loaded and which still have to be + * resolved. + */ + private List scheduledClasses=new LinkedList(); + + private List sourcePath; + + private String sourceEncoding; + + private Parser parser = new Parser(); + + private Set unlocatableReportedSet = new HashSet(); + + private Set inaccessibleReportedSet = new HashSet(); + + //-------------------------------------------------------------------------- + // + // Implementation of RootDoc interface + // + //-------------------------------------------------------------------------- + + /** + * Return classes and interfaces to be documented. + */ + public ClassDoc[] classes() { return classes; } + + /** + * Return a ClassDoc object for the specified class/interface + * name. + * + * @return a ClassDoc object describing the given class, or + * <code>null</code> if no corresponding ClassDoc object + * has been constructed. + */ + public ClassDoc classNamed(String qualifiedName) { + return (ClassDoc)classDocMap.get(qualifiedName); + } + + /** + * Return an xxx + */ + public String[][] options() { return customOptionArr; } + + // Return a PackageDoc for the specified package name + public PackageDoc packageNamed(String name) { + return (PackageDoc)packageDocMap.get(name); + } + + + // classes and interfaces specified on the command line. + public ClassDoc[] specifiedClasses() + { + return (ClassDocImpl[]) specifiedClasses.toArray(new ClassDocImpl[0]); + } + + // packages specified on the command line. + public PackageDoc[] specifiedPackages() + { + return (PackageDocImpl[])specifiedPackages.toArray(new PackageDocImpl[0]); + } + + // Print error message, increment error count. + public void printError(java.lang.String msg) { + reporter.printError(msg); + } + + // Print error message, increment error count. + public void printFatal(java.lang.String msg) { + reporter.printFatal(msg); + } + + // Print a message. + public void printNotice(java.lang.String msg) { + reporter.printNotice(msg); + } + + // Print warning message, increment warning count. + public void printWarning(java.lang.String msg) { + reporter.printWarning(msg); + } + + public String name() { + return "RootDoc"; + } + + public ErrorReporter getReporter() { + return reporter; + } + + public void build() throws ParseException, IOException { + + //--- Create a temporary random access file for caching comment text. + + //File rawCommentCacheFile=File.createTempFile("gjdoc_rawcomment",".cache"); + File rawCommentCacheFile = new File("gjdoc_rawcomment.cache"); + rawCommentCacheFile.deleteOnExit(); + rawCommentCache = new RandomAccessFile(rawCommentCacheFile, "rw"); + + //--- Parse all files in "java.lang". + + List javaLangSourceDirs = findSourceFiles("java/lang"); + if (!javaLangSourceDirs.isEmpty()) { + Iterator it = javaLangSourceDirs.iterator(); + while (it.hasNext()) { + File javaLangSourceDir = (File)it.next(); + parser.processSourceDir(javaLangSourceDir, + sourceEncoding, "java.lang"); + } + } + else { + + Debug.log(1, "Sourcepath is "+sourcePath); + + // Core docs not included in source-path: + // we need to gather the information about java.lang + // classes via reflection... + + } + + //--- Parse all files in explicitly specified package directories. + + for (Iterator it=specifiedPackageNames.iterator(); it.hasNext(); ) { + + String specifiedPackageName = (String)it.next(); + String displayPackageName = specifiedPackageName; + if (null == displayPackageName || 0 == displayPackageName.length()) { + displayPackageName = "<unnamed>"; + } + printNotice("Loading classes for package "+displayPackageName+"..."); + String relPath; + if (null != specifiedPackageName) { + relPath = specifiedPackageName.replace('.',File.separatorChar); + } + else { + relPath = ""; + } + List sourceDirs = findSourceFiles(relPath); + if (!sourceDirs.isEmpty()) { + Iterator sourceDirIt = sourceDirs.iterator(); + while (sourceDirIt.hasNext()) { + File sourceDir = (File)sourceDirIt.next(); + parser.processSourceDir(sourceDir, sourceEncoding, specifiedPackageName); + } + } + else { + printError("Package '"+specifiedPackageName+"' not found."); + } + } + + specifiedClasses = new LinkedList(); + + //--- Parse all explicitly specified source files. + + for (Iterator it=specifiedSourceFiles.iterator(); it.hasNext(); ) { + + File specifiedSourceFile = (File)it.next(); + printNotice("Loading source file "+specifiedSourceFile+" ..."); + ClassDocImpl classDoc = parser.processSourceFile(specifiedSourceFile, true, sourceEncoding, null); + if (null != classDoc) { + specifiedClasses.add(classDoc); + classesList.add(classDoc); + classDoc.setIsIncluded(true); + addPackageDoc(classDoc.containingPackage()); + } + } + + + //--- Let the user know that all specified classes are loaded. + + printNotice("Constructing Javadoc information..."); + + //--- Load all classes implicitly referenced by explicitly specified classes. + + loadScheduledClasses(parser); + + printNotice("Resolving references in comments..."); + + resolveComments(); + + //--- Resolve pending references in all ClassDocImpls + + printNotice("Resolving references in classes..."); + + for (Iterator it = classDocMap.values().iterator(); it.hasNext(); ) { + ClassDoc cd=(ClassDoc)it.next(); + if (cd instanceof ClassDocImpl) { + ((ClassDocImpl)cd).resolve(); + } + } + + //--- Resolve pending references in all PackageDocImpls + + printNotice("Resolving references in packages..."); + + for (Iterator it = packageDocMap.values().iterator(); it.hasNext(); ) { + PackageDocImpl pd=(PackageDocImpl)it.next(); + pd.resolve(); + } + + //--- Assemble the array with all specified packages + + specifiedPackages = new LinkedHashSet(); + for (Iterator it = specifiedPackageNames.iterator(); it.hasNext(); ) { + String specifiedPackageName = (String)it.next(); + PackageDoc specifiedPackageDoc = (PackageDoc)packageDocMap.get(specifiedPackageName); + if (null!=specifiedPackageDoc) { + ((PackageDocImpl)specifiedPackageDoc).setIsIncluded(true); + specifiedPackages.add(specifiedPackageDoc); + + ClassDoc[] packageClassDocs=specifiedPackageDoc.allClasses(); + for (int i=0; i<packageClassDocs.length; ++i) { + ClassDocImpl specifiedPackageClassDoc=(ClassDocImpl)packageClassDocs[i]; + + specifiedPackageClassDoc.setIsIncluded(true); + classesList.add(specifiedPackageClassDoc); + } + } + } + + //--- Resolve pending references in comment data of all classes + + printNotice("Resolving references in class comments..."); + + for (Iterator it=classDocMap.values().iterator(); it.hasNext(); ) { + ClassDoc cd=(ClassDoc)it.next(); + if (cd instanceof ClassDocImpl) { + ((ClassDocImpl)cd).resolveComments(); + } + } + + //--- Resolve pending references in comment data of all packages + + printNotice("Resolving references in package comments..."); + + for (Iterator it=packageDocMap.values().iterator(); it.hasNext(); ) { + PackageDocImpl pd=(PackageDocImpl)it.next(); + pd.resolveComments(); + } + + //--- Create array with all loaded classes + + this.classes=(ClassDocImpl[])classesList.toArray(new ClassDocImpl[0]); + Arrays.sort(this.classes); + + //--- Close comment cache + + parser = null; + System.gc(); + System.gc(); + } + + public long writeRawComment(String rawComment) { + try { + long pos=rawCommentCache.getFilePointer(); + //rawCommentCache.writeUTF(rawComment); + byte[] bytes = rawComment.getBytes("utf-8"); + rawCommentCache.writeInt(bytes.length); + rawCommentCache.write(bytes); + return pos; + } + catch (IOException e) { + printFatal("Cannot write to comment cache: "+e.getMessage()); + return -1; + } + } + + public String readRawComment(long pos) { + try { + rawCommentCache.seek(pos); + int sz = rawCommentCache.readInt(); + byte[] bytes = new byte[sz]; + rawCommentCache.read(bytes); + return new String(bytes, "utf-8"); + //return rawCommentCache.readUTF(); + } + catch (IOException e) { + e.printStackTrace(); + printFatal("Cannot read from comment cache: "+e.getMessage()); + return null; + } + } + + List findSourceFiles(String relPath) { + + List result = new LinkedList(); + for (Iterator it = sourcePath.iterator(); it.hasNext(); ) { + File path = (File)it.next(); + File file = new File(path, relPath); + if (file.exists()) { + result.add(file); + } + } + + return result; + } + + PackageDocImpl findOrCreatePackageDoc(String packageName) { + PackageDocImpl rc=(PackageDocImpl)getPackageDoc(packageName); + if (null==rc) { + rc=new PackageDocImpl(packageName); + if (specifiedPackageNames.contains(packageName)) { + String packageDirectoryName = packageName.replace('.', File.separatorChar); + List packageDirectories = findSourceFiles(packageDirectoryName); + Iterator it = packageDirectories.iterator(); + boolean packageDocFound = false; + while (it.hasNext()) { + File packageDirectory = (File)it.next(); + File packageDocFile = new File(packageDirectory, "package.html"); + rc.setPackageDirectory(packageDirectory); + packageDocFound = true; + if (null!=packageDocFile && packageDocFile.exists()) { + try { + rc.setRawCommentText(readHtmlBody(packageDocFile)); + } + catch (IOException e) { + printWarning("Error while reading documentation for package "+packageName+": "+e.getMessage()); + } + break; + } + } + if (!packageDocFound) { + printNotice("No description found for package "+packageName); + } + } + addPackageDoc(rc); + } + return rc; + } + + public void addClassDoc(ClassDoc cd) { + classDocMap.put(cd.qualifiedName(), cd); + } + + public void addClassDocRecursive(ClassDoc cd) { + classDocMap.put(cd.qualifiedName(), cd); + ClassDoc[] innerClasses = cd.innerClasses(false); + for (int i=0; i<innerClasses.length; ++i) { + addClassDocRecursive(innerClasses[i]); + } + } + + public void addPackageDoc(PackageDoc pd) { + packageDocMap.put(pd.name(), pd); + } + + public PackageDocImpl getPackageDoc(String name) { + return (PackageDocImpl)packageDocMap.get(name); + } + + public ClassDocImpl getClassDoc(String qualifiedName) { + return (ClassDocImpl)classDocMap.get(qualifiedName); + } + + class ScheduledClass { + + ClassDoc contextClass; + String qualifiedName; + ScheduledClass(ClassDoc contextClass, String qualifiedName) { + this.contextClass=contextClass; + this.qualifiedName=qualifiedName; + } + + public String toString() { return "ScheduledClass{"+qualifiedName+"}"; } + } + + public void scheduleClass(ClassDoc context, String qualifiedName) throws ParseException, IOException { + + if (classDocMap.get(qualifiedName)==null) { + + //Debug.log(9,"Scheduling "+qualifiedName+", context "+context+"."); + //System.err.println("Scheduling " + qualifiedName + ", context " + context); + + scheduledClasses.add(new ScheduledClass(context, qualifiedName)); + } + } + + /** + * Load all classes that were implictly referenced by the classes + * (already loaded) that the user explicitly specified on the + * command line. + * + * For example, if the user generates Documentation for his simple + * 'class Test {}', which of course 'extends java.lang.Object', + * then 'java.lang.Object' is implicitly referenced because it is + * the base class of Test. + * + * Gjdoc needs a ClassDocImpl representation of all classes + * implicitly referenced through derivation (base class), + * or implementation (interface), or field type, method argument + * type, or method return type. + * + * The task of this method is to ensure that Gjdoc has all this + * information at hand when it exits. + * + * + */ + public void loadScheduledClasses(Parser parser) throws ParseException, IOException { + + // Because the referenced classes could in turn reference other + // classes, this method runs as long as there are still unloaded + // classes. + + while (!scheduledClasses.isEmpty()) { + + // Make a copy of scheduledClasses and empty it. This + // prevents any Concurrent Modification issues. + // As the copy won't need to grow (as it won't change) + // we make it an Array for performance reasons. + + ScheduledClass[] scheduledClassesArr = (ScheduledClass[])scheduledClasses.toArray(new ScheduledClass[0]); + scheduledClasses.clear(); + + // Load each class specified in our array copy + + for (int i=0; i<scheduledClassesArr.length; ++i) { + + // The name of the class we are looking for. This name + // needs not be fully qualified. + + String scheduledClassName=scheduledClassesArr[i].qualifiedName; + + // The ClassDoc in whose context the scheduled class was looked for. + // This is necessary in order to resolve non-fully qualified + // class names. + ClassDoc scheduledClassContext=scheduledClassesArr[i].contextClass; + + // If there already is a class doc with this name, skip. There's + // nothing to do for us. + if (classDocMap.get(scheduledClassName)!=null) { + continue; + } + + try { + // Try to load the class + //printNotice("Trying to load " + scheduledClassName); + loadScheduledClass(parser, scheduledClassName, scheduledClassContext); + } + catch (ParseException e) { + + /********************************************************** + + // Check whether the following is necessary at all. + + + if (scheduledClassName.indexOf('.')>0) { + + // Maybe the dotted notation doesn't mean a package + // name but instead an inner class, as in 'Outer.Inner'. + // so let's assume this and try to load the outer class. + + String outerClass=""; + for (StringTokenizer st=new StringTokenizer(scheduledClassName,"."); st.hasMoreTokens(); ) { + if (outerClass.length()>0) outerClass+="."; + outerClass+=st.nextToken(); + if (!st.hasMoreTokens()) break; + try { + loadClass(outerClass); + //FIXME: shouldn't this be loadScheduledClass(outerClass, scheduledClassContext); ??? + continue; + } + catch (Exception ee) { + // Ignore: try next level + } + } + } + + **********************************************************/ + + // If we arrive here, the class could not be found + + printWarning("Couldn't load class "+scheduledClassName+" referenced by "+scheduledClassContext); + + //FIXME: shouldn't this be throw new Error("cannot load: "+scheduledClassName); + } + } + } + } + + private void loadScheduledClass(Parser parser, String scheduledClassName, ClassDoc scheduledClassContext) throws ParseException, IOException { + + ClassDoc loadedClass=(ClassDoc)scheduledClassContext.findClass(scheduledClassName); + + if (loadedClass==null || loadedClass instanceof ClassDocProxy) { + + ClassDoc classDoc = findScheduledClassFile(scheduledClassName, scheduledClassContext); + if (null != classDoc) { + + if (classDoc instanceof ClassDocReflectedImpl) { + Main.getRootDoc().addClassDocRecursive(classDoc); + } + + if (Main.DESCEND_SUPERCLASS + && null != classDoc.superclass() + && (classDoc.superclass() instanceof ClassDocProxy)) { + scheduleClass(classDoc, classDoc.superclass().qualifiedName()); + } + } + else { + // It might be an inner class of one of the outer/super classes. + // But we can only check that when they are all fully loaded. + boolean retryLater = false; + + int numberOfProcessedFilesBefore = parser.getNumberOfProcessedFiles(); + + ClassDoc cc = scheduledClassContext.containingClass(); + while (cc != null && !retryLater) { + ClassDoc sc = cc.superclass(); + while (sc != null && !retryLater) { + if (sc instanceof ClassDocProxy) { + ((ClassDocImpl)cc).resolve(); + retryLater = true; + } + sc = sc.superclass(); + } + cc = cc.containingClass(); + } + + // Now that outer/super references have been resolved, try again + // to find the class. + + loadedClass = (ClassDoc)scheduledClassContext.findClass(scheduledClassName); + + int numberOfProcessedFilesAfter = parser.getNumberOfProcessedFiles(); + + boolean filesWereProcessed = numberOfProcessedFilesAfter > numberOfProcessedFilesBefore; + + // Only re-schedule class if additional files have been processed + // If there haven't, there's no point in re-scheduling. + // Will avoid infinite loops of re-scheduling + if (null == loadedClass && retryLater && filesWereProcessed) + scheduleClass(scheduledClassContext, scheduledClassName); + + /* A warning needn't be emitted - this is normal, can happen + if the scheduled class is in a package which is not + included on the command line. + + else if (null == loadedClass) + printWarning("Can't find scheduled class '" + + scheduledClassName + + "' in context '" + + scheduledClassContext.qualifiedName() + + "'"); + */ + } + } + } + + private static interface ResolvedImport + { + public String match(String name); + public boolean mismatch(String name); + public ClassDoc tryFetch(String name); + } + + private class ResolvedImportNotFound + implements ResolvedImport + { + private String importSpecifier; + private String name; + + ResolvedImportNotFound(String importSpecifier) + { + this.importSpecifier = importSpecifier; + int ndx = importSpecifier.lastIndexOf('.'); + if (ndx >= 0) { + this.name = importSpecifier.substring(ndx + 1); + } + else { + this.name = importSpecifier; + } + } + + public String toString() + { + return "ResolvedImportNotFound{" + importSpecifier + "}"; + } + + public String match(String name) + { + if ((name.equals(this.name)) || (importSpecifier.equals(name))) + return this.name; + // FIXME: note that we don't handle on-demand imports here. + return null; + } + + public boolean mismatch(String name) + { + return true; // FIXME! + } + + public ClassDoc tryFetch(String name) + { + return null; + } + } + + private class ResolvedImportPackageFile + implements ResolvedImport + { + private Set topLevelClassNames; + private File packageFile; + private String packageName; + private Map cache = new HashMap(); + + ResolvedImportPackageFile(File packageFile, String packageName) + { + this.packageFile = packageFile; + this.packageName = packageName; + topLevelClassNames = new HashSet(); + File[] files = packageFile.listFiles(); + for (int i=0; i<files.length; ++i) { + if (!files[i].isDirectory() && files[i].getName().endsWith(".java")) { + String topLevelClassName = files[i].getName(); + topLevelClassName + = topLevelClassName.substring(0, topLevelClassName.length() - 5); + topLevelClassNames.add(topLevelClassName); + } + } + } + + public String match(String name) + { + ClassDoc loadedClass = classNamed(packageName + "." + name); + if (null != loadedClass) { + return loadedClass.qualifiedName(); + } + else { + String topLevelName = name; + int ndx = topLevelName.indexOf('.'); + String innerClassName = null; + if (ndx > 0) { + innerClassName = topLevelName.substring(ndx + 1); + topLevelName = topLevelName.substring(0, ndx); + } + + if (topLevelClassNames.contains(topLevelName)) { + //System.err.println(this + ".match returns " + packageName + "." + name); + return packageName + "." + name; + } + // FIXME: inner classes + else { + return null; + } + } + } + + public boolean mismatch(String name) + { + return null == match(name); + } + + public ClassDoc tryFetch(String name) + { + ClassDoc loadedClass = classNamed(packageName + "." + name); + if (null != loadedClass) { + return loadedClass; + } + else if (null != match(name)) { + + String topLevelName = name; + int ndx = topLevelName.indexOf('.'); + String innerClassName = null; + if (ndx > 0) { + innerClassName = topLevelName.substring(ndx + 1); + topLevelName = topLevelName.substring(0, ndx); + } + + ClassDoc topLevelClass = (ClassDoc)cache.get(topLevelName); + if (null == topLevelClass) { + File classFile = new File(packageFile, topLevelName + ".java"); + try { + // FIXME: inner classes + topLevelClass = parser.processSourceFile(classFile, false, sourceEncoding, null); + } + catch (Exception ignore) { + printWarning("Could not parse source file " + classFile); + } + cache.put(topLevelName, topLevelClass); + } + if (null == innerClassName) { + return topLevelClass; + } + else { + return getInnerClass(topLevelClass, innerClassName); + } + } + else { + return null; + } + } + + public String toString() + { + return "ResolvedImportPackageFile{" + packageFile + "," + packageName + "}"; + } + } + + private ClassDoc getInnerClass(ClassDoc topLevelClass, String innerClassName) + { + StringTokenizer st = new StringTokenizer(innerClassName, "."); + outer: + + while (st.hasMoreTokens()) { + String innerClassNameComponent = st.nextToken(); + ClassDoc[] innerClasses = topLevelClass.innerClasses(); + for (int i=0; i<innerClasses.length; ++i) { + if (innerClasses[i].name().equals(innerClassNameComponent)) { + topLevelClass = innerClasses[i]; + continue outer; + } + } + printWarning("Could not find inner class " + innerClassName + " in class " + topLevelClass.qualifiedName()); + return null; + } + return topLevelClass; + } + + private class ResolvedImportClassFile + implements ResolvedImport + { + private File classFile; + private String innerClassName; + private String name; + private ClassDoc classDoc; + private boolean alreadyFetched; + private String qualifiedName; + + ResolvedImportClassFile(File classFile, String innerClassName, String name, String qualifiedName) + { + this.classFile = classFile; + this.innerClassName = innerClassName; + this.name = name; + this.qualifiedName = qualifiedName; + } + + public String toString() + { + return "ResolvedImportClassFile{" + classFile + "," + innerClassName + "}"; + } + + public String match(String name) + { + String topLevelName = name; + int ndx = topLevelName.indexOf('.'); + + String _innerClassName = null; + if (ndx > 0) { + _innerClassName = topLevelName.substring(ndx + 1); + topLevelName = topLevelName.substring(0, ndx); + } + + if (this.name.equals(topLevelName)) { + if (null == _innerClassName) { + return qualifiedName; + } + else { + return qualifiedName + "." + _innerClassName; + } + } + else { + return null; + } + } + + public boolean mismatch(String name) + { + return null == match(name); + } + + public ClassDoc tryFetch(String name) + { + if (null != match(name)) { + ClassDoc topLevelClass = null; + if (alreadyFetched) { + topLevelClass = classDoc; + } + else { + alreadyFetched = true; + try { + topLevelClass = parser.processSourceFile(classFile, false, sourceEncoding, null); + } + catch (Exception ignore) { + printWarning("Could not parse source file " + classFile); + } + } + if (null == topLevelClass) { + return null; + } + else { + return getInnerClass(topLevelClass, innerClassName); + } + } + else { + return null; + } + } + + public String getName() + { + if (innerClassName != null) { + return name + innerClassName; + } + else { + return name; + } + } + } + + private class ResolvedImportReflectionClass + implements ResolvedImport + { + private Class clazz; + private String name; + + ResolvedImportReflectionClass(Class clazz) + { + this.clazz = clazz; + String className = clazz.getName(); + int ndx = className.lastIndexOf('.'); + if (ndx >= 0) { + this.name = className.substring(ndx + 1); + } + else { + this.name = className; + } + } + + public String toString() + { + return "ResolvedImportReflectionClass{" + clazz.getName() + "}"; + } + + public String match(String name) + { + if ((this.name.equals(name)) || (clazz.getName().equals(name))) { + return clazz.getName(); + } + else { + return null; + } + } + + public boolean mismatch(String name) + { + return null == match(name); + } + + public ClassDoc tryFetch(String name) + { + if (null != match(name)) { + return new ClassDocReflectedImpl(clazz); + } + // FIXME: inner classes? + else { + return null; + } + } + + public String getName() + { + return name; + } + } + + private class ResolvedImportReflectionPackage + implements ResolvedImport + { + private String packagePrefix; + + ResolvedImportReflectionPackage(String packagePrefix) + { + this.packagePrefix = packagePrefix; + } + + public String toString() + { + return "ResolvedImportReflectionPackage{" + packagePrefix + ".*}"; + } + + public String match(String name) + { + try { + Class clazz = Class.forName(packagePrefix + "." + name); + return clazz.getName(); + } + catch (Exception e) { + return null; + } + } + + public boolean mismatch(String name) + { + return null == match(name); + } + + public ClassDoc tryFetch(String name) + { + try { + Class clazz = Class.forName(packagePrefix + name); + return ClassDocReflectedImpl.newInstance(clazz); + } + catch (Exception e) { + return null; + } + } + + public String getName() + { + return packagePrefix; + } + } + + private List unlocatablePrefixes = new LinkedList(); + + private ResolvedImport resolveImport(String importSpecifier) + { + ResolvedImport result = resolveImportFileSystem(importSpecifier); + if (null == result && Main.getInstance().isReflectionEnabled()) { + result = resolveImportReflection(importSpecifier); + } + if (null == result) { + result = new ResolvedImportNotFound(importSpecifier); + } + return result; + } + + private ResolvedImport resolveImportReflection(String importSpecifier) + { + String importedPackageOrClass = importSpecifier; + if (importedPackageOrClass.endsWith(".*")) { + importedPackageOrClass = importedPackageOrClass.substring(0, importedPackageOrClass.length() - 2); + + return new ResolvedImportReflectionPackage(importedPackageOrClass); + + //return null; + } + else { + try { + Class importedClass = Class.forName(importSpecifier); + return new ResolvedImportReflectionClass(importedClass); + } + catch (Throwable ignore) { + return null; + } + } + } + + private ResolvedImport resolveImportFileSystem(String importSpecifier) + { + for (Iterator it = unlocatablePrefixes.iterator(); it.hasNext(); ) { + String unlocatablePrefix = (String)it.next(); + if (importSpecifier.startsWith(unlocatablePrefix)) { + return null; + } + } + + String longestUnlocatablePrefix = ""; + + for (Iterator it=sourcePath.iterator(); it.hasNext(); ) { + + File _sourcePath = (File)it.next(); + + StringBuffer packageOrClassPrefix = new StringBuffer(); + StringTokenizer st = new StringTokenizer(importSpecifier, "."); + while (st.hasMoreTokens() && _sourcePath.isDirectory()) { + String token = st.nextToken(); + if ("*".equals(token)) { + return new ResolvedImportPackageFile(_sourcePath, + packageOrClassPrefix.substring(0, packageOrClassPrefix.length() - 1)); + } + else { + packageOrClassPrefix.append(token); + packageOrClassPrefix.append('.'); + File classFile = new File(_sourcePath, token + ".java"); + //System.err.println(" looking for file " + classFile); + if (classFile.exists()) { + StringBuffer innerClassName = new StringBuffer(); + while (st.hasMoreTokens()) { + token = st.nextToken(); + if (innerClassName.length() > 0) { + innerClassName.append('.'); + } + innerClassName.append(token); + } + return new ResolvedImportClassFile(classFile, innerClassName.toString(), token, importSpecifier); + } + else { + _sourcePath = new File(_sourcePath, token); + } + } + } + if (st.hasMoreTokens()) { + if (packageOrClassPrefix.length() > longestUnlocatablePrefix.length()) { + longestUnlocatablePrefix = packageOrClassPrefix.toString(); + } + } + } + + if (longestUnlocatablePrefix.length() > 0) { + unlocatablePrefixes.add(longestUnlocatablePrefix); + } + + return null; + } + + private Map resolvedImportCache = new HashMap(); + + private ResolvedImport getResolvedImport(String importSpecifier) + { + ResolvedImport result + = (ResolvedImport)resolvedImportCache.get(importSpecifier); + if (null == result) { + result = resolveImport(importSpecifier); + resolvedImportCache.put(importSpecifier, result); + } + return result; + } + + public String resolveClassName(String className, ClassDocImpl context) + { + Iterator it = context.getImportSpecifierList().iterator(); + while (it.hasNext()) { + String importSpecifier = (String)it.next(); + ResolvedImport resolvedImport = getResolvedImport(importSpecifier); + String resolvedScheduledClassName = resolvedImport.match(className); + + if (null != resolvedScheduledClassName) { + return resolvedScheduledClassName; + } + } + return className; + } + + public ClassDoc findScheduledClassFile(String scheduledClassName, + ClassDoc scheduledClassContext) + throws ParseException, IOException + { + String resolvedScheduledClassName = null; + + if (scheduledClassContext instanceof ClassDocImpl) { + + //((ClassDocImpl)scheduledClassContext).resolveReferencedName(scheduledClassName); + Iterator it = ((ClassDocImpl)scheduledClassContext).getImportSpecifierList().iterator(); + while (it.hasNext()) { + String importSpecifier = (String)it.next(); + ResolvedImport resolvedImport = getResolvedImport(importSpecifier); + //System.err.println(" looking in import '" + resolvedImport + "'"); + resolvedScheduledClassName = resolvedImport.match(scheduledClassName); + if (null != resolvedScheduledClassName) { + ClassDoc result = resolvedImport.tryFetch(scheduledClassName); + if (null != result) { + return result; + } + else { + if (!inaccessibleReportedSet.contains(scheduledClassName)) { + inaccessibleReportedSet.add(scheduledClassName); + printWarning("Error while loading class " + scheduledClassName); + } + // FIXME: output resolved class name here + return null; + } + } + } + } + else { + System.err.println("findScheduledClassFile for '" + scheduledClassName + "' in proxy for " + scheduledClassContext); + } + + // interpret as fully qualified name on file system + + ResolvedImport fqImport = resolveImportFileSystem(scheduledClassName); + if (null != fqImport && fqImport instanceof ResolvedImportClassFile) { + return fqImport.tryFetch(((ResolvedImportClassFile)fqImport).getName()); + } + + // use reflection, assume fully qualified class name + + if (!unlocatableReflectedClassNames.contains(scheduledClassName)) { + if (Main.getInstance().isReflectionEnabled()) { + try { + Class clazz = Class.forName(scheduledClassName); + printWarning("Cannot locate class " + scheduledClassName + " on file system, falling back to reflection."); + ClassDoc result = new ClassDocReflectedImpl(clazz); + return result; + } + catch (Throwable ignore) { + unlocatableReflectedClassNames.add(scheduledClassName); + } + } + else { + unlocatableReflectedClassNames.add(scheduledClassName); + } + } + + if (null == resolvedScheduledClassName) { + resolvedScheduledClassName = scheduledClassName; + } + if (!unlocatableReportedSet.contains(resolvedScheduledClassName)) { + unlocatableReportedSet.add(resolvedScheduledClassName); + printWarning("Cannot locate class " + resolvedScheduledClassName + " referenced in class " + scheduledClassContext.qualifiedName()); + } + return null; + } + + private Set unlocatableReflectedClassNames = new HashSet(); + + public static boolean recursiveClasses = false; + + public void addSpecifiedPackageName(String packageName) { + specifiedPackageNames.add(packageName); + } + + public void addSpecifiedSourceFile(File sourceFile) { + specifiedSourceFiles.add(sourceFile); + } + + public boolean hasSpecifiedPackagesOrClasses() { + return !specifiedPackageNames.isEmpty() + || !specifiedSourceFiles.isEmpty(); + } + + public void setOptions(String[][] customOptionArr) { + this.customOptionArr = customOptionArr; + } + + public void setSourcePath(List sourcePath) { + this.sourcePath = sourcePath; + } + + public void finalize() throws Throwable { + super.finalize(); + } + + public void flush() + { + try { + rawCommentCache.close(); + } + catch (IOException e) { + printError("Cannot close raw comment cache"); + } + + rawCommentCache = null; + customOptionArr = null; + specifiedPackageNames = null; + classesList = null; + classDocMap = null; + packageDocMap = null; + classes = null; + specifiedClasses = null; + specifiedPackages = null; + scheduledClasses = null; + sourcePath = null; + parser = null; + unlocatableReportedSet = null; + inaccessibleReportedSet = null; + } + + public void setSourceEncoding(String sourceEncoding) + { + this.sourceEncoding = sourceEncoding; + } + + public RootDocImpl() + { + super(null); + } + + public static String readHtmlBody(File file) + throws IOException + { + FileReader fr=new FileReader(file); + long size = file.length(); + char[] packageDocBuf=new char[(int)(size)]; + int index = 0; + int i = fr.read(packageDocBuf, index, (int)size); + while (i > 0) { + index += i; + size -= i; + i = fr.read(packageDocBuf, index, (int)size); + } + fr.close(); + + // We only need the part between the begin and end body tag. + String html = new String(packageDocBuf); + int start = html.indexOf("<body"); + if (start == -1) + start = html.indexOf("<BODY"); + int end = html.indexOf("</body>"); + if (end == -1) + end = html.indexOf("</BODY>"); + if (start != -1 && end != -1) { + // Start is end of body tag. + start = html.indexOf('>', start) + 1; + if (start != -1 && start < end) + html = html.substring(start, end); + } + return html.trim(); + } + + public Parser getParser() + { + return parser; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SeeTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SeeTagImpl.java new file mode 100644 index 000000000..12e2256dc --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SeeTagImpl.java @@ -0,0 +1,215 @@ +/* gnu.classpath.tools.gjdoc.SeeTagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.text.*; + +public class SeeTagImpl extends AbstractTagImpl implements SeeTag { + + protected String reference; + private String referencedClassName; + private String referencedMemberName; + private ClassDoc referencedClass; + private MemberDoc referencedMember; + private PackageDoc referencedPackage; + private String label; + private ClassDocImpl contextClass; + + public SeeTagImpl(String text, ClassDocImpl contextClass) { + super(text); + this.contextClass=contextClass; + } + + public void resolve() { + + super.resolve(); + + text = text.trim(); + + if (text.startsWith("<") || text.startsWith("\"")) { + label = text; + return; + } + + int labelNdx=text.indexOf(';'); + if (labelNdx>=0) { + label=""; + return; + } + + for (int i=0; i<text.length(); ++i) { + if (" \t\r\n".indexOf(text.charAt(i)) >= 0) { + labelNdx = i; + break; + } + } + + int openParenNdx = text.indexOf('('); + if (openParenNdx >= 0 && openParenNdx < labelNdx) { + labelNdx=text.indexOf(')', openParenNdx); + if (labelNdx >= 0) { + ++ labelNdx; + } + } + + if (labelNdx<0 || labelNdx>=text.length()) { + reference=text.trim(); + label=""; + } + else { + reference=text.substring(0,labelNdx).trim(); + label=text.substring(labelNdx).trim(); + } + + int mspecNdx=reference.indexOf('#'); + String referencedFqName; + if (mspecNdx<0) { + referencedFqName=reference; + } + else { + referencedFqName=reference.substring(0,mspecNdx); + referencedMemberName=reference.substring(mspecNdx+1); + } + + // the following is in contradiction to the api docs, but + // conform to sun javadoc: return fully qualified classname + // with referencedClassName(). + if (referencedFqName.trim().length()>0) { + referencedClassName=referencedFqName; + if (contextClass==null) + referencedClass=Main.getRootDoc().classNamed(referencedFqName); + else + referencedClass=contextClass.findClass(referencedFqName); + } + else { + referencedClassName=""; + referencedClass=contextClass; + } + + if (referencedClass==null) { + referencedClass = Main.getRootDoc().classNamed("java.lang." + referencedFqName); + } + + if (referencedClass!=null && !referencedClass.isIncluded()) referencedClass=null; + + if (referencedClass!=null) { + referencedPackage=referencedClass.containingPackage(); + referencedClassName=referencedClass.qualifiedName(); + + if (referencedMemberName!=null) { + + if (referencedMemberName.indexOf('(')<0) { + referencedMember=((ClassDocImpl)referencedClass).findFieldRec(referencedMemberName); + if (null == referencedMember) { + MethodDoc[] methods = ((ClassDocImpl)referencedClass).methods(); + for (int i=0; i<methods.length; ++i) { + if (methods[i].name().equals(referencedMemberName)) { + if (null == referencedMember) { + referencedMember = methods[i]; + } + else { + referencedClass = null; + referencedMember = null; + //print warning here + break; + } + } + } + } + else { + referencedClass = referencedMember.containingClass(); + } + } + else { + referencedMember=((ClassDocImpl)referencedClass).findExecutableRec(referencedMemberName); + if (referencedMember==null) { + //System.err.println("cannot find member for '"+referencedMemberName+"'"); + referencedClass = null; + } + } + } + } + /* + else { + System.err.println("class not found: '"+referencedFqName + "' in context class " + contextClass + " in " + this); + } + */ + } + + public ClassDoc referencedClass() { + return referencedClass; + } + + public String referencedClassName() { + return referencedClassName; + } + + public MemberDoc referencedMember() { + return referencedMember; + } + + public String referencedMemberName() { + return referencedMemberName; + } + + public PackageDoc referencedPackage() { + return referencedPackage; + } + + public String label() { + return label; + } + + public String kind() { + return "@see"; + } + + public String name() { + return "@see"; + } + + public Tag[] firstSentenceTags() { + return inlineTags(); + } + + public Tag[] inlineTags() { + return new Tag[]{new TextTagImpl(referencedClassName)}; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SerialFieldTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SerialFieldTagImpl.java new file mode 100644 index 000000000..0ecfe637c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SerialFieldTagImpl.java @@ -0,0 +1,128 @@ +/* gnu.classpath.tools.gjdoc.SerialFieldTagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.text.*; + +public class SerialFieldTagImpl extends AbstractTagImpl implements SerialFieldTag { + + private String fieldName; + private String fieldType; + private String description; + private ClassDoc fieldTypeDoc; + private ClassDocImpl contextClass; + + public SerialFieldTagImpl(String text, + ClassDocImpl contextClass, + MemberDocImpl contextMember) { + super(text); + this.contextClass=contextClass; + + if (fieldName==null) + fieldName=""; + if (fieldType==null) + fieldType=""; + if (description==null) + description=""; + + int state=1; + char[] textArr=text.toCharArray(); + for (int i=0; i<textArr.length; ++i) { + char c=textArr[i]; + switch (state) { + case 1: + if (Parser.isWhitespace(c)) state=2; + else fieldName+=c; + break; + case 2: + if (Parser.isWhitespace(c)) state=3; + else fieldType+=c; + break; + case 3: + description+=c; + break; + } + } + + setBody(description, contextClass, contextMember); + + } + + public void resolve() { + + super.resolve(); + try { + Type type=contextClass.typeForString(fieldType); + this.fieldTypeDoc=type.asClassDoc(); + } catch (ParseException e) { + System.err.println("FIXME: add try-catch to force compilation" + + e); + } + } + + public ClassDoc fieldTypeDoc() { + return fieldTypeDoc; + } + + public String fieldName() { + return fieldName; + } + + public String fieldType() { + return fieldType; + } + + public String description() { + return description; + } + + public String kind() { + return "@serialField"; + } + + public int compareTo(Object o) { + if (o!=null && o instanceof SerialFieldTagImpl) { + return fieldName().compareTo(((SerialFieldTagImpl)o).fieldName()); + } + else { + return 0; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SourcePositionImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SourcePositionImpl.java new file mode 100644 index 000000000..e2d73bbd5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/SourcePositionImpl.java @@ -0,0 +1,77 @@ +/* gnu.classpath.tools.gjdoc.SourcePositionImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.SourcePosition; + +import java.io.File; + +public class SourcePositionImpl + implements SourcePosition +{ + private File file; + private int line; + private int column; + + public SourcePositionImpl(File file, int line, int column) + { + this.file = file; + this.line = line; + this.column = column; + } + + public File file() + { + return this.file; + } + + public int line() + { + return this.line; + } + + public int column() + { + return this.column; + } + + public String toString() + { + return this.file + ":" + this.line; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TagContainer.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TagContainer.java new file mode 100644 index 000000000..1b9bc40d9 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TagContainer.java @@ -0,0 +1,52 @@ +/* gnu.classpath.tools.gjdoc.TagContainer + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.Tag; + +import java.util.Map; + +/** + * Implemented by Doclet API classes that are associated with tags. + */ +interface TagContainer +{ + public Tag[] firstSentenceTags(); + public Tag[] inlineTags(); + public Map getTagMap(); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TagImpl.java new file mode 100644 index 000000000..a4b1f4b2a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TagImpl.java @@ -0,0 +1,60 @@ +/* gnu.classpath.tools.gjdoc.TagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; + +public class TagImpl extends AbstractTagImpl implements Tag { + + private String kind; + private String name; + + TagImpl(String name, String text, ClassDocImpl contextClass, MemberDocImpl contextMember) { + super(text); + this.kind=name; + this.name=name; + + setBody(text, contextClass, contextMember); + } + + public String kind() { return kind; } + public String name() { return name; } + public String toString() { return kind()+":"+text(); } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TemporaryStore.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TemporaryStore.java new file mode 100644 index 000000000..0333ee2b7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TemporaryStore.java @@ -0,0 +1,132 @@ +/* gnu.classpath.tools.gjdoc.TemporaryStore + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +/** + * Useful for passing big objects that are no longer needed by the + * calling method, reducing memory usage. <p/> + * + * Consider the following problem: + * <pre> + * public class A { + * public static void foo() { + * long[] hugeArray = new long[1000000]; // takes around 8 MB + * // ... fill hugeArray with some information ... + * bar(hugeArray); + * // ... hugeArray is no more required at this point + * } + * public static void bar(long[] arr) { + * // ... process contents of arr ... + * arr = null; + * System.gc(); // NOTE: will not collect arr! + * // ... do something memory-intensive where arr is not needed + * } + * } + * </pre> + * + * In method <code>bar()</code>, the array cannot be garbage + * collected because the local variable <code>hugeArray</code> in + * method <code>foo()</code> still holds a reference to the array. + * <p/> + * + * When calling <code>bar(new long[1000000]);</code> in + * <code>arr</code> the array <i>can</i> be collected in + * <code>bar()</code>, but that way it can't be initialized in + * <code>foo()</code>. A local variable is needed for + * initialization, but the variable can't be cleared before it is + * passed to <code>bar()</code>! <p/> + * + * <code>TemporaryStore</code> is the solution for this + * dilemma. The modified method <code>foo()</code> which uses a + * <code>TemporaryStore</code> object would look like this: + * + * <pre> + * public static void foo() { + * long[] hugeArray = new long[1000000]; // takes around 7 MB + * // ... fill hugeArray with some very important information ... + * TemporaryStore tstore = new TemporaryStore(hugeArray); + * hugeArray = null; + * bar((long[])tstore.getAndClear()); + * } + * </pre> + * + * When control flow is transferred to <code>bar()</code>, + * <code>foo()</code> will hold no more references to the array + * and so it can be garbage collected in <code>bar()</code>. + * + */ +public class TemporaryStore { + + private Object storedObject; + + /** + * Temporarily store the given object for passing it to a + * different method. <p/> + * + * The method constructing a new TemporaryStore object should + * clear all other references to the stored object, so that + * this TemporaryStore is the only object referencing it. + * + * @param storedObject the object to store temporarily + * + */ + public TemporaryStore(Object storedObject) { + this.storedObject = storedObject; + } + + /** + * Return the stored object after clearing the reference to it. + * <p/> + * + * When the user of this class followed the recommendations in + * the documentation of @link{TemporaryStore(Object)}, the + * returned reference will be the only reference to the stored + * object after this method returns. If the returned reference + * is passed in a method call, the called method will hold the + * only reference to the stored object and can release it by + * nulling the corresponding parameter. + * + * @return the object which was passed to the constructor. + * + */ + public Object getAndClear() { + Object rc = this.storedObject; + this.storedObject = null; + return rc; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TextTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TextTagImpl.java new file mode 100644 index 000000000..812fd2fde --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TextTagImpl.java @@ -0,0 +1,56 @@ +/* gnu.classpath.tools.gjdoc.TextTagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; + +public class TextTagImpl extends AbstractTagImpl implements Tag { + + TextTagImpl(String text) { + super(text); + } + + public Tag[] firstSentenceTags() { return new Tag[]{this}; } + public Tag[] inlineTags() { return new Tag[]{this}; } + public String kind() { return "Text"; } + + public String toString() { return "TextTagImpl{text=" + text + "}"; } + + public String getText() { return text; } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ThrowsTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ThrowsTagImpl.java new file mode 100644 index 000000000..e4e7bbfcb --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ThrowsTagImpl.java @@ -0,0 +1,105 @@ +/* gnu.classpath.tools.gjdoc.ThrowsTagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.text.*; + +public class ThrowsTagImpl extends AbstractTagImpl implements ThrowsTag { + + private ClassDoc exception; + private String exceptionName; + private String exceptionComment; + + public ThrowsTagImpl(String text, + ClassDocImpl contextClass, + MemberDocImpl contextMember) { + super(text); + + char[] textarr=text.toCharArray(); + int i=0; + for (; i<textarr.length; ++i) { + if (!Parser.isWhitespace(textarr[i])) break; + } + for (; i<textarr.length; ++i) { + if (Parser.isWhitespace(textarr[i])) { + this.exceptionName=new String(textarr,0,i).trim(); + this.exceptionComment=new String(textarr,i,textarr.length-i).trim(); + break; + } + } + if (null != exceptionName) { + if (contextClass==null) { + this.exception=Main.getRootDoc().classNamed(exceptionName); + } + else { + this.exception=contextClass.findClass(exceptionName); + } + if (exception!=null) + this.exceptionName=exception.qualifiedName(); + else { + if (text.trim().startsWith("<")) { + Main.getRootDoc().printWarning("Expected exception name but got '"+text+"' in class "+contextClass.getClassName()); + } + } + } + else { + Main.getRootDoc().printWarning("@throws tag in comment for " + contextClass.qualifiedName() + "." + contextMember.name() + " doesn't specify an exception."); + } + if (this.exceptionComment!=null) { + setBody(this.exceptionComment, contextClass, contextMember); + } + } + + public ClassDoc exception() { + return exception; + } + + public String exceptionName() { + return exceptionName; + } + + public String exceptionComment() { + return exceptionComment; + } + + public String kind() { + return "@throws"; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Timer.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Timer.java new file mode 100644 index 000000000..93404d08e --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/Timer.java @@ -0,0 +1,88 @@ +/* gnu.classpath.tools.gjdoc.Timer + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import java.io.*; +import java.text.*; + +public class Timer { + + private static long startTime, beforeDocletTime, stopTime, memoryUsed, maxDriverHeap=-1, maxDocletHeap=-1; + + static void shutdown() { + try{ + if (stopTime==0) return; + PrintWriter pw=new PrintWriter(new FileWriter("timer.out")); + pw.println("Preparation (driver) took "+(((double)(beforeDocletTime-startTime))/1000.)+" s"); + pw.println("Generation (doclet) took "+(((double)(stopTime-beforeDocletTime))/1000.)+" s"); + pw.println(""); + pw.println("Memory used for documentation tree: "+(memoryUsed/(1024*1024))+" MB"); + pw.println("Max. heap used for driver: "+((maxDriverHeap<0)?"N/A":((maxDriverHeap/(1024*1024))+" MB"))); + pw.println("Max. heap used for doclet: "+((maxDocletHeap<0)?"N/A":((maxDocletHeap/(1024*1024))+" MB"))); + pw.println(""); + pw.println("TOTAL TIME: "+(((double)(stopTime-startTime))/1000.)+" s"); + pw.println("TOTAL HEAP: "+((maxDocletHeap<0)?"N/A":(Math.max(maxDocletHeap,maxDriverHeap)/(1024*1024))+" MB")); + pw.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + public static void setStartTime() { + Timer.startTime=System.currentTimeMillis(); + } + + public static void setStopTime() { + Timer.stopTime=System.currentTimeMillis(); + } + + public static void setBeforeDocletTime() { + Timer.beforeDocletTime=System.currentTimeMillis(); + Timer.memoryUsed=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); + } + + public static void setMaxDocletHeap(long maximumHeap) { + Timer.maxDocletHeap=maximumHeap; + } + + public static void setMaxDriverHeap(long maximumHeap) { + Timer.maxDriverHeap=maximumHeap; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TimerDoclet.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TimerDoclet.java new file mode 100644 index 000000000..c2c3c3edf --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TimerDoclet.java @@ -0,0 +1,104 @@ +/* gnu.classpath.tools.gjdoc.TimerDoclet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; + +public class TimerDoclet { + + private static Object doclet = null; + + private static long maximumHeap = -1; + + private static Thread memThread; + + private static boolean runMemThread = true; + + private static void init() throws Exception { + if (doclet==null) { + doclet=Class.forName("com.sun.tools.doclets.standard.Standard").newInstance(); + memThread=new Thread() { + + public void run() { + while (runMemThread) { + synchronized (TimerDoclet.class) { + TimerDoclet.maximumHeap=Math.max(TimerDoclet.maximumHeap, + Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()); + } + try { Thread.sleep(50); } catch (Exception e) {} + } + } + }; + //memThread.start(); + } + } + + public static boolean validOptions(String[][] options, DocErrorReporter reporter) + throws Exception { + + init(); + return ((Boolean)doclet.getClass().getMethod("validOptions", new Class[]{String[][].class, DocErrorReporter.class}).invoke(null, new Object[]{options, reporter})).booleanValue(); + //return false; //doclet.validOptions(options, reporter); + } + + public static int optionLength(String option) throws Exception { + init(); + return ((Integer)doclet.getClass().getMethod("optionLength", new Class[]{String.class}).invoke(null, new Object[]{option})).intValue(); + } + + public static boolean start(RootDoc root) throws Exception { + Timer.setBeforeDocletTime(); + synchronized (TimerDoclet.class) { + Timer.setMaxDriverHeap(maximumHeap); + maximumHeap=-1; + } + //new com.sun.tools.doclets.standard.Standard().validOptions(root.options(), root); + //new com.sun.tools.doclets.standard.Standard().start(root); + + if (validOptions(root.options(), root)) { + doclet.getClass().getMethod("start", new Class[]{RootDoc.class}).invoke(null, new Object[]{root}); + } + runMemThread=false; + Timer.setStopTime(); + synchronized (TimerDoclet.class) { + Timer.setMaxDocletHeap(maximumHeap); + } + Timer.shutdown(); + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TypeImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TypeImpl.java new file mode 100644 index 000000000..8c1bd5a0d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TypeImpl.java @@ -0,0 +1,109 @@ +/* gnu.classpath.tools.gjdoc.TypeImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; + +public class TypeImpl implements Type, WritableType { + + private String packageName; + private String typeName; + private String dimension; + + TypeImpl(String packageName, String typeName, String dimension) { + this.packageName=packageName; + this.typeName=typeName; + this.dimension=dimension; + + if (typeName.indexOf('[') >= 0 || typeName.indexOf(']') >= 0) { + throw new RuntimeException("Typename must not contain dimension information."); + } + } + + public ClassDoc asClassDoc() { + + if (this instanceof ClassDoc) + return ((ClassDocImpl)(ClassDoc)this).getBaseClassDoc(); + else + return null; + } + + public String typeName() { return typeName; } + + public String qualifiedTypeName() { return (packageName!=null)?(packageName+"."+typeName):(typeName); } + + public String dimension() { return dimension; } + public void setDimension(String dimension) { this.dimension = dimension; } + + public String toString() { return "Type{"+qualifiedTypeName()+dimension()+"}"; } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + public boolean isPrimitive() + { + return null == packageName && primitiveNames.contains(typeName); + } + + private static final Set primitiveNames; + static { + Set _primitiveNames = new HashSet(); + _primitiveNames.add("boolean"); + _primitiveNames.add("char"); + _primitiveNames.add("byte"); + _primitiveNames.add("short"); + _primitiveNames.add("int"); + _primitiveNames.add("long"); + _primitiveNames.add("float"); + _primitiveNames.add("double"); + primitiveNames = Collections.unmodifiableSet(_primitiveNames); + } + + public TypeVariable asTypeVariable() + { + if (this instanceof TypeVariable) + return (TypeVariable) this; + else + return null; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TypeVariableImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TypeVariableImpl.java new file mode 100644 index 000000000..08a236683 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/TypeVariableImpl.java @@ -0,0 +1,108 @@ +/* gnu.classpath.tools.gjdoc.TypeVariableImpl + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.ProgramElementDoc; +import com.sun.javadoc.Type; +import com.sun.javadoc.TypeVariable; + +import java.util.List; + +public class TypeVariableImpl + extends TypeImpl + implements TypeVariable, WritableType +{ + + /** + * The bounds of this particular type variable. + */ + Type[] bounds; + + /** + * The owning program element of this type variable. + */ + ProgramElementDoc owner; + + /** + * Constructs a new type variable with the supplied name and owner. + * + * @param packageName the name of the package containing the type variable. + * @param typeName the name of the type variable. + * @param dimension the dimensions of the type variable (always ""). + * @param owner the owning program element of the type variable. + */ + TypeVariableImpl(String packageName, String typeName, String dimension, + ProgramElementDoc owner) + { + super(packageName, typeName, dimension); + this.owner = owner; + } + + /** + * Set the bounds to the contents of the supplied list. + * + * @param parsedBounds a list of type bounds. + */ + void setBounds(List parsedBounds) + { + bounds = (Type[]) parsedBounds.toArray(new Type[parsedBounds.size()]); + } + + /** + * Returns the bounds of this type variable. + * + * @return the bounds of the variable. + */ + public Type[] bounds() + { + return bounds; + } + + /** + * Returns the owning program element for this type variable. + * + * @return the owning program element, whether a class, interface, + * constructor or method. + */ + public ProgramElementDoc owner() + { + return owner; + } + + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ValueTagImpl.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ValueTagImpl.java new file mode 100644 index 000000000..024594c6a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/ValueTagImpl.java @@ -0,0 +1,65 @@ +/* gnu.classpath.tools.gjdoc.ValueTagImpl + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +import com.sun.javadoc.*; +import java.util.*; +import java.text.*; + +public class ValueTagImpl extends SeeTagImpl { + + public ValueTagImpl(String _text, ClassDocImpl contextClass) { + super(_text, contextClass); + } + + public String name() { return "@value"; } + + public Tag[] firstSentenceTags() { + return inlineTags(); + } + + public Tag[] inlineTags() { + if (null != reference && reference.trim().length() > 0) { + return new Tag[] { new TextTagImpl(reference) }; + } + else { + return new Tag[0]; + } + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/WritableType.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/WritableType.java new file mode 100644 index 000000000..39cfb3f25 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/WritableType.java @@ -0,0 +1,44 @@ +/* gnu.classpath.tools.gjdoc.WritableType + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc; + +public interface WritableType extends Cloneable { + + public void setDimension(String dimension); + public Object clone() throws CloneNotSupportedException; +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/AdditionExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/AdditionExpression.java new file mode 100644 index 000000000..82502c431 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/AdditionExpression.java @@ -0,0 +1,86 @@ +/* gnu.classpath.tools.gjdoc.expr.AdditionExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class AdditionExpression + extends BinaryComputationExpression +{ + public AdditionExpression(Expression left, Expression right) + { + super(left, right); + } + + protected double compute(double leftValue, double rightValue) + { + return leftValue + rightValue; + } + + protected float compute(float leftValue, float rightValue) + { + return leftValue + rightValue; + } + + protected long compute(long leftValue, long rightValue) + { + return leftValue + rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue + rightValue; + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression leftValue = left.evaluate(context); + ConstantExpression rightValue = right.evaluate(context); + + if (Type.STRING == leftValue.getType() + || Type.STRING == rightValue.getType()) { + + return new ConstantString(leftValue.asObject().toString() + + rightValue.asObject().toString()); + } + else { + return super.evaluate(leftValue, rightValue); + } + + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/AndExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/AndExpression.java new file mode 100644 index 000000000..ac807728b --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/AndExpression.java @@ -0,0 +1,57 @@ +/* gnu.classpath.tools.gjdoc.expr.AndExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class AndExpression + extends BinaryBitwiseExpression +{ + public AndExpression(Expression left, Expression right) + { + super(left, right); + } + + protected long compute(long leftValue, long rightValue) + { + return leftValue & rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue & rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryBitwiseExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryBitwiseExpression.java new file mode 100644 index 000000000..3c44ce7d8 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryBitwiseExpression.java @@ -0,0 +1,68 @@ +/* gnu.classpath.tools.gjdoc.expr.BinaryBitwiseExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class BinaryBitwiseExpression + extends BinaryExpression +{ + protected BinaryBitwiseExpression(Expression left, Expression right) + { + super(left, right); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression leftValue = left.evaluate(context); + ConstantExpression rightValue = right.evaluate(context); + + if (Type.LONG == leftValue.getType() + || Type.LONG == rightValue.getType()) { + + return new ConstantLong(compute(leftValue.asNumber().longValue(), + rightValue.asNumber().longValue())); + } + else { + return new ConstantInteger(compute(leftValue.asNumber().intValue(), + rightValue.asNumber().intValue())); + } + } + + protected abstract long compute(long leftValue, long rightValue); + protected abstract int compute(int leftValue, int rightValue); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryComputationExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryComputationExpression.java new file mode 100644 index 000000000..15d016e1f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryComputationExpression.java @@ -0,0 +1,92 @@ +/* gnu.classpath.tools.gjdoc.expr.BinaryComputationExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class BinaryComputationExpression + extends BinaryExpression +{ + protected BinaryComputationExpression(Expression left, Expression right) + { + super(left, right); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression leftValue = left.evaluate(context); + ConstantExpression rightValue = right.evaluate(context); + + return evaluate(leftValue, rightValue); + } + + protected ConstantExpression evaluate(ConstantExpression leftValue, + ConstantExpression rightValue) + throws IllegalExpressionException + { + if (Type.DOUBLE == leftValue.getType() + || Type.DOUBLE == rightValue.getType()) { + + return new ConstantDouble(compute(leftValue.asNumber().doubleValue(), + rightValue.asNumber().doubleValue())); + } + else if (Type.FLOAT == leftValue.getType() + || Type.FLOAT == rightValue.getType()) { + + return new ConstantFloat(compute(leftValue.asNumber().floatValue(), + rightValue.asNumber().floatValue())); + } + else if (Type.LONG == leftValue.getType() + || Type.LONG == rightValue.getType()) { + + return new ConstantLong(compute(leftValue.asNumber().longValue(), + rightValue.asNumber().longValue())); + } + else if (leftValue.isNumber() && rightValue.isNumber()) { + return new ConstantInteger(compute(leftValue.asNumber().intValue(), + rightValue.asNumber().intValue())); + } + else { + throw new IllegalExpressionException("Operator ? cannot be applied to " + leftValue.getType() + "," + rightValue.getType()); + } + } + + protected abstract double compute(double leftValue, double rightValue); + protected abstract float compute(float leftValue, float rightValue); + protected abstract long compute(long leftValue, long rightValue); + protected abstract int compute(int leftValue, int rightValue); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryEqualityExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryEqualityExpression.java new file mode 100644 index 000000000..1b37c233a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryEqualityExpression.java @@ -0,0 +1,89 @@ +/* gnu.classpath.tools.gjdoc.expr.BinaryEqualityExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class BinaryEqualityExpression + extends BinaryExpression +{ + protected BinaryEqualityExpression(Expression left, Expression right) + { + super(left, right); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression leftValue = left.evaluate(context); + ConstantExpression rightValue = right.evaluate(context); + + if (Type.DOUBLE == leftValue.getType() + || Type.DOUBLE == rightValue.getType()) { + + return new ConstantBoolean(compute(leftValue.asNumber().doubleValue(), + rightValue.asNumber().doubleValue())); + } + else if (Type.FLOAT == leftValue.getType() + || Type.FLOAT == rightValue.getType()) { + + return new ConstantBoolean(compute(leftValue.asNumber().floatValue(), + rightValue.asNumber().floatValue())); + } + else if (Type.LONG == leftValue.getType() + || Type.LONG == rightValue.getType()) { + + return new ConstantBoolean(compute(leftValue.asNumber().longValue(), + rightValue.asNumber().longValue())); + } + else if (Type.BOOLEAN == leftValue.getType() + && Type.BOOLEAN == rightValue.getType()) { + + return new ConstantBoolean(compute(((ConstantBoolean)leftValue).booleanValue(), + ((ConstantBoolean)rightValue).booleanValue())); + } + else { + return new ConstantBoolean(compute(leftValue.asNumber().intValue(), + rightValue.asNumber().intValue())); + } + } + + protected abstract boolean compute(double leftValue, double rightValue); + protected abstract boolean compute(float leftValue, float rightValue); + protected abstract boolean compute(long leftValue, long rightValue); + protected abstract boolean compute(int leftValue, int rightValue); + protected abstract boolean compute(boolean leftValue, boolean rightValue); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryExpression.java new file mode 100644 index 000000000..557b514c7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryExpression.java @@ -0,0 +1,51 @@ +/* gnu.classpath.tools.gjdoc.expr.BinaryExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class BinaryExpression + implements Expression +{ + protected Expression left; + protected Expression right; + + protected BinaryExpression(Expression left, Expression right) + { + this.left = left; + this.right = right; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryLogicalExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryLogicalExpression.java new file mode 100644 index 000000000..7fa51869c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryLogicalExpression.java @@ -0,0 +1,64 @@ +/* gnu.classpath.tools.gjdoc.expr.BinaryLogicalExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class BinaryLogicalExpression + extends BinaryExpression +{ + protected BinaryLogicalExpression(Expression left, Expression right) + { + super(left, right); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression leftValue = left.evaluate(context); + ConstantExpression rightValue = right.evaluate(context); + + if (leftValue.getType() != Type.BOOLEAN || rightValue.getType() != Type.BOOLEAN) { + throw new IllegalExpressionException("logical expression expects boolean subexpressions"); + } + else { + return new ConstantBoolean(compute(((ConstantBoolean)leftValue).booleanValue(), + ((ConstantBoolean)rightValue).booleanValue())); + } + } + + protected abstract boolean compute(boolean leftValue, boolean rightValue); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryRelationExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryRelationExpression.java new file mode 100644 index 000000000..f6ef45cf5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryRelationExpression.java @@ -0,0 +1,82 @@ +/* gnu.classpath.tools.gjdoc.expr.BinaryRelationExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class BinaryRelationExpression + extends BinaryExpression +{ + protected BinaryRelationExpression(Expression left, Expression right) + { + super(left, right); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression leftValue = left.evaluate(context); + ConstantExpression rightValue = right.evaluate(context); + + if (Type.DOUBLE == leftValue.getType() + || Type.DOUBLE == rightValue.getType()) { + + return new ConstantBoolean(compute(leftValue.asNumber().doubleValue(), + rightValue.asNumber().doubleValue())); + } + else if (Type.FLOAT == leftValue.getType() + || Type.FLOAT == rightValue.getType()) { + + return new ConstantBoolean(compute(leftValue.asNumber().floatValue(), + rightValue.asNumber().floatValue())); + } + else if (Type.LONG == leftValue.getType() + || Type.LONG == rightValue.getType()) { + + return new ConstantBoolean(compute(leftValue.asNumber().longValue(), + rightValue.asNumber().longValue())); + } + else { + return new ConstantBoolean(compute(leftValue.asNumber().intValue(), + rightValue.asNumber().intValue())); + } + } + + protected abstract boolean compute(double leftValue, double rightValue); + protected abstract boolean compute(float leftValue, float rightValue); + protected abstract boolean compute(long leftValue, long rightValue); + protected abstract boolean compute(int leftValue, int rightValue); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryShiftExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryShiftExpression.java new file mode 100644 index 000000000..32ccea209 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BinaryShiftExpression.java @@ -0,0 +1,66 @@ +/* gnu.classpath.tools.gjdoc.expr.BinaryShiftExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class BinaryShiftExpression + extends BinaryExpression +{ + protected BinaryShiftExpression(Expression left, Expression right) + { + super(left, right); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression leftValue = left.evaluate(context); + ConstantExpression rightValue = right.evaluate(context); + + if (Type.LONG == leftValue.getType()) { + return new ConstantLong(compute(leftValue.asNumber().longValue(), + rightValue.asNumber().intValue())); + } + else { + return new ConstantInteger(compute(leftValue.asNumber().intValue(), + rightValue.asNumber().intValue())); + } + } + + protected abstract long compute(long leftValue, int rightValue); + protected abstract int compute(int leftValue, int rightValue); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BitShiftRightExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BitShiftRightExpression.java new file mode 100644 index 000000000..eca1a5257 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/BitShiftRightExpression.java @@ -0,0 +1,57 @@ +/* gnu.classpath.tools.gjdoc.expr.BitShiftRightExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class BitShiftRightExpression + extends BinaryShiftExpression +{ + public BitShiftRightExpression(Expression left, Expression right) + { + super(left, right); + } + + protected long compute(long leftValue, int rightValue) + { + return leftValue >>> rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue >>> rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/CircularExpressionException.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/CircularExpressionException.java new file mode 100644 index 000000000..a9bfacc89 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/CircularExpressionException.java @@ -0,0 +1,52 @@ +/* gnu.classpath.tools.gjdoc.expr.IllegalExpressionException + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +public class CircularExpressionException + extends IllegalExpressionException +{ + public CircularExpressionException(String message) + { + super(message); + } + + public CircularExpressionException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConditionalExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConditionalExpression.java new file mode 100644 index 000000000..d9532efb0 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConditionalExpression.java @@ -0,0 +1,74 @@ +/* gnu.classpath.tools.gjdoc.expr.ConditionalExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConditionalExpression + implements Expression +{ + private Expression condition; + private Expression ifTrue; + private Expression ifFalse; + + ConditionalExpression(Expression condition, Expression ifTrue, Expression ifFalse) + { + this.condition = condition; + this.ifTrue = ifTrue; + this.ifFalse = ifFalse; + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression conditionValue = condition.evaluate(context); + ConstantExpression ifTrueValue = ifTrue.evaluate(context); + ConstantExpression ifFalseValue = ifFalse.evaluate(context); + + if (Type.BOOLEAN != conditionValue.getType()) { + throw new IllegalExpressionException("condition must be boolean"); + } + else { + boolean cond = ((ConstantBoolean)conditionValue).booleanValue(); + if (cond) { + return ifTrueValue; + } + else { + return ifFalseValue; + } + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantBoolean.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantBoolean.java new file mode 100644 index 000000000..83faf1f20 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantBoolean.java @@ -0,0 +1,84 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantBoolean + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantBoolean + extends ConstantExpression +{ + private boolean value; + + public ConstantBoolean(String stringValue) + { + this.value = "true".equals(stringValue); + } + + public ConstantBoolean(boolean booleanValue) + { + this.value = booleanValue; + } + + public boolean booleanValue() + { + return value; + } + + public Type getType() + { + return Type.BOOLEAN; + } + + public Number asNumber() + { + return null; + } + + public boolean isNumber() + { + return true; + } + + public Object asObject() + { + return new Boolean(value); + } + + public String toString() + { + return Boolean.toString(value); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantByte.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantByte.java new file mode 100644 index 000000000..e206e567f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantByte.java @@ -0,0 +1,74 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantByte + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantByte + extends ConstantExpression +{ + private byte value; + + public ConstantByte(byte byteValue) + { + this.value = byteValue; + } + + public Type getType() + { + return Type.BYTE; + } + + public Number asNumber() + { + return new Byte(value); + } + + public boolean isNumber() + { + return true; + } + + public Object asObject() + { + return asNumber(); + } + + public String toString() + { + return Byte.toString(value); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantChar.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantChar.java new file mode 100644 index 000000000..3cbeac126 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantChar.java @@ -0,0 +1,98 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantChar + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantChar + extends ConstantExpression +{ + private char value; + + public ConstantChar(String stringValue) + { + this.value = stringValue.charAt(1); // FIXME + if (value == '\\') { + switch (stringValue.charAt(2)) { + case 'n': value = '\n'; break; + case 't': value = '\t'; break; + case 'f': value = '\f'; break; + case 'r': value = '\r'; break; + case 'b': value = '\b'; break; + case 'u': + { + String stringVal = stringValue.substring(3, stringValue.length() - 1); + /* + while (stringVal.length() > 1 && stringVal.charAt(0) == '0') { + stringVal = stringVal.substring(1); + } + */ + value = (char)Integer.parseInt(stringVal, 16); break; + } + } + } + } + + public ConstantChar(char charValue) + { + this.value = charValue; + } + + public Type getType() + { + return Type.CHAR; + } + + public Number asNumber() + { + return new Integer((int)value); + } + + public Object asObject() + { + return new Character(value); + } + + public boolean isNumber() + { + return true; + } + + public String toString() + { + return Character.toString(value); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantDouble.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantDouble.java new file mode 100644 index 000000000..5faa5a17b --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantDouble.java @@ -0,0 +1,79 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantDouble + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantDouble + extends ConstantExpression +{ + private double value; + + public ConstantDouble(String stringValue) + { + this.value = Double.parseDouble(stringValue); + } + + public ConstantDouble(double doubleValue) + { + this.value = doubleValue; + } + + public Type getType() + { + return Type.DOUBLE; + } + + public Number asNumber() + { + return new Double(value); + } + + public boolean isNumber() + { + return true; + } + + public Object asObject() + { + return asNumber(); + } + + public String toString() + { + return Double.toString(value); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantExpression.java new file mode 100644 index 000000000..93923e6ae --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantExpression.java @@ -0,0 +1,52 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class ConstantExpression + implements Expression +{ + public abstract Type getType(); + public abstract Number asNumber(); + public abstract Object asObject(); + public abstract boolean isNumber(); + + public ConstantExpression evaluate(Context context) + { + return this; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantFloat.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantFloat.java new file mode 100644 index 000000000..46895500d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantFloat.java @@ -0,0 +1,79 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantFloat + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantFloat + extends ConstantExpression +{ + private float value; + + public ConstantFloat(String stringValue) + { + this.value = Float.parseFloat(stringValue); + } + + public ConstantFloat(float floatValue) + { + this.value = floatValue; + } + + public Type getType() + { + return Type.FLOAT; + } + + public Number asNumber() + { + return new Float(value); + } + + public boolean isNumber() + { + return true; + } + + public Object asObject() + { + return asNumber(); + } + + public String toString() + { + return "ConstantFloat{" + value + "}"; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantInteger.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantInteger.java new file mode 100644 index 000000000..10804a695 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantInteger.java @@ -0,0 +1,79 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantInteger + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantInteger + extends ConstantExpression +{ + private long value; + + public ConstantInteger(String stringValue) + { + this.value = Evaluator.parseLong(stringValue); + } + + public ConstantInteger(int integerValue) + { + this.value = integerValue; + } + + public Type getType() + { + return Type.INTEGER; + } + + public Number asNumber() + { + return new Long(value); + } + + public boolean isNumber() + { + return true; + } + + public Object asObject() + { + return new Integer((int)value); + } + + public String toString() + { + return Long.toString(value); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantLong.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantLong.java new file mode 100644 index 000000000..a0a2f4b80 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantLong.java @@ -0,0 +1,84 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantLong + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantLong + extends ConstantExpression +{ + private long value; + + public ConstantLong(String stringValue) + { + if ('l' == Character.toLowerCase(stringValue.charAt(stringValue.length() - 1))) { + this.value = Evaluator.parseLong(stringValue.substring(0, stringValue.length() - 1)); + } + else { + this.value = Evaluator.parseInt(stringValue.substring(0, stringValue.length() - 1)); + } + } + + public ConstantLong(long longValue) + { + this.value = longValue; + } + + public Type getType() + { + return Type.LONG; + } + + public Number asNumber() + { + return new Long(value); + } + + public boolean isNumber() + { + return true; + } + + public Object asObject() + { + return asNumber(); + } + + public String toString() + { + return Long.toString(value); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantNull.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantNull.java new file mode 100644 index 000000000..c27b10b8a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantNull.java @@ -0,0 +1,66 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantNull + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantNull + extends ConstantExpression +{ + public ConstantNull() + { + } + + public Type getType() + { + return Type.NULL; + } + + public Number asNumber() + { + return null; + } + + public boolean isNumber() + { + return false; + } + + public Object asObject() + { + return null; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantShort.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantShort.java new file mode 100644 index 000000000..5f4f00e1c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantShort.java @@ -0,0 +1,74 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantShort + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantShort + extends ConstantExpression +{ + private short value; + + public ConstantShort(short shortValue) + { + this.value = shortValue; + } + + public Type getType() + { + return Type.SHORT; + } + + public Number asNumber() + { + return new Short(value); + } + + public boolean isNumber() + { + return true; + } + + public Object asObject() + { + return asNumber(); + } + + public String toString() + { + return Short.toString(value); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantString.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantString.java new file mode 100644 index 000000000..4e9ecdee1 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ConstantString.java @@ -0,0 +1,74 @@ +/* gnu.classpath.tools.gjdoc.expr.ConstantString + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ConstantString + extends ConstantExpression +{ + private String value; + + public ConstantString(String value) + { + this.value = value; + } + + public Type getType() + { + return Type.STRING; + } + + public Number asNumber() + { + return null; + } + + public boolean isNumber() + { + return false; + } + + public Object asObject() + { + return value; + } + + public String toString() + { + return value; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Context.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Context.java new file mode 100644 index 000000000..f6d1ebc10 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Context.java @@ -0,0 +1,62 @@ +/* gnu.classpath.tools.gjdoc.expr.Context + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +import java.util.Set; + +class Context +{ + private EvaluatorEnvironment evaluatorEnvironment; + private Set visitedFields; + + Context(EvaluatorEnvironment evaluatorEnvironment, Set visitedFields) + { + this.evaluatorEnvironment = evaluatorEnvironment; + this.visitedFields = visitedFields; + } + + public EvaluatorEnvironment getEvaluatorEnvironment() + { + return evaluatorEnvironment; + } + + public Set getVisitedFields() + { + return visitedFields; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/DivisionExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/DivisionExpression.java new file mode 100644 index 000000000..85206d4fb --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/DivisionExpression.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.expr.DivisionExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class DivisionExpression + extends BinaryComputationExpression +{ + public DivisionExpression(Expression left, Expression right) + { + super(left, right); + } + + protected double compute(double leftValue, double rightValue) + { + return leftValue / rightValue; + } + + protected float compute(float leftValue, float rightValue) + { + return leftValue / rightValue; + } + + protected long compute(long leftValue, long rightValue) + { + return leftValue / rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue / rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/EqualExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/EqualExpression.java new file mode 100644 index 000000000..d7385fd7c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/EqualExpression.java @@ -0,0 +1,72 @@ +/* gnu.classpath.tools.gjdoc.expr.EqualExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class EqualExpression + extends BinaryEqualityExpression +{ + public EqualExpression(Expression left, Expression right) + { + super(left, right); + } + + protected boolean compute(double leftValue, double rightValue) + { + return leftValue == rightValue; + } + + protected boolean compute(float leftValue, float rightValue) + { + return leftValue == rightValue; + } + + protected boolean compute(long leftValue, long rightValue) + { + return leftValue == rightValue; + } + + protected boolean compute(int leftValue, int rightValue) + { + return leftValue == rightValue; + } + + protected boolean compute(boolean leftValue, boolean rightValue) + { + return leftValue == rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Evaluator.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Evaluator.java new file mode 100644 index 000000000..d12da3519 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Evaluator.java @@ -0,0 +1,148 @@ +/* gnu.classpath.tools.gjdoc.expr.Evaluator + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +import java.io.StringReader; +import java.math.BigInteger; +import antlr.RecognitionException; +import antlr.TokenStreamException; +import java.util.Set; + +public class Evaluator +{ + /** + * Try to evaluate the given Java expression in the context of the + * given environment. + * + * @param expression the Java expression to evaluate. The + * expression string must not include a terminating semicolon. + * + * @param source the FieldDoc (part of) whose constant field value + * expression is being evaluated. Used to prevent circular + * references. + * + * @param environment callback hook used by the Evaluator to query + * the value of static fields referenced in the expression. + * + * @return a Java object representing the value of the expression, + * or <code>null</code> if the expression evaluates to + * <code>null</code>. + * + * @throws IllegalExpressionException if the expression is + * invalid, uses unsupported syntax constructs (e.g. method calls, + * array access) or references unknown static fields. + */ + public static Object evaluate(String expression, + Set visitedFields, + EvaluatorEnvironment environment) + throws IllegalExpressionException + { + try { + JavaLexer lexer = new JavaLexer(new StringReader(expression)); + JavaRecognizer recognizer = new JavaRecognizer(lexer); + Expression e = recognizer.expression(); + ConstantExpression value = e.evaluate(new Context(environment, visitedFields)); + return value.asObject(); + } + catch (RecognitionException e) { + throw new IllegalExpressionException(e); + } + catch (TokenStreamException e) { + throw new IllegalExpressionException(e); + } + } + + static int parseInt(String stringValue) + { + int base = 10; + + if (stringValue.startsWith("0x")) { + base = 16; + stringValue = stringValue.substring(2); + } + else if (stringValue.length() > 1 && stringValue.startsWith("0")) { + base = 8; + stringValue = stringValue.substring(1); + } + while (stringValue.length() > 1 && stringValue.startsWith("0")) { + stringValue = stringValue.substring(1); + } + + if (10 == base) { + return Integer.parseInt(stringValue); + } + else { + long result = Long.parseLong(stringValue, base); + + if (result > Integer.MAX_VALUE) { + result -= 0x100000000L; + } + + if (result > Integer.MAX_VALUE) { + throw new NumberFormatException(result + " > " + Integer.MAX_VALUE); + } + else if (result < Integer.MIN_VALUE) { + throw new NumberFormatException(result + " < " + Integer.MIN_VALUE); + } + else { + return (int)result; + } + } + } + + static long parseLong(String stringValue) + { + int base = 10; + + if (stringValue.startsWith("0x")) { + base = 16; + stringValue = stringValue.substring(2); + } + else if (stringValue.length() > 1 && stringValue.startsWith("0")) { + base = 8; + stringValue = stringValue.substring(1); + } + while (stringValue.length() > 1 && stringValue.startsWith("0")) { + stringValue = stringValue.substring(1); + } + + BigInteger bigInt = new BigInteger(stringValue, base); + long result = bigInt.longValue(); + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/EvaluatorEnvironment.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/EvaluatorEnvironment.java new file mode 100644 index 000000000..cf4df8938 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/EvaluatorEnvironment.java @@ -0,0 +1,46 @@ +/* gnu.classpath.tools.gjdoc.expr.EvaluatorEnvironment + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +import java.util.Set; + +public interface EvaluatorEnvironment +{ + public Object getValue(String identifier, Set visitedFields) + throws IllegalExpressionException, UnknownIdentifierException; +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ExclusiveOrExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ExclusiveOrExpression.java new file mode 100644 index 000000000..eb992fc28 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ExclusiveOrExpression.java @@ -0,0 +1,57 @@ +/* gnu.classpath.tools.gjdoc.expr.ExclusiveOrExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ExclusiveOrExpression + extends BinaryBitwiseExpression +{ + public ExclusiveOrExpression(Expression left, Expression right) + { + super(left, right); + } + + protected long compute(long leftValue, long rightValue) + { + return leftValue ^ rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue ^ rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Expression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Expression.java new file mode 100644 index 000000000..ca1ccc645 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Expression.java @@ -0,0 +1,44 @@ +/* gnu.classpath.tools.gjdoc.expr.Expression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +interface Expression +{ + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException; +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/GreaterThanExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/GreaterThanExpression.java new file mode 100644 index 000000000..995f7ae80 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/GreaterThanExpression.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.expr.GreaterThanExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class GreaterThanExpression + extends BinaryRelationExpression +{ + public GreaterThanExpression(Expression left, Expression right) + { + super(left, right); + } + + protected boolean compute(double leftValue, double rightValue) + { + return leftValue > rightValue; + } + + protected boolean compute(float leftValue, float rightValue) + { + return leftValue > rightValue; + } + + protected boolean compute(long leftValue, long rightValue) + { + return leftValue > rightValue; + } + + protected boolean compute(int leftValue, int rightValue) + { + return leftValue > rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/GreaterThanOrEqualExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/GreaterThanOrEqualExpression.java new file mode 100644 index 000000000..4e7ca4a01 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/GreaterThanOrEqualExpression.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.expr.GreaterThanOrEqualExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class GreaterThanOrEqualExpression + extends BinaryRelationExpression +{ + public GreaterThanOrEqualExpression(Expression left, Expression right) + { + super(left, right); + } + + protected boolean compute(double leftValue, double rightValue) + { + return leftValue >= rightValue; + } + + protected boolean compute(float leftValue, float rightValue) + { + return leftValue >= rightValue; + } + + protected boolean compute(long leftValue, long rightValue) + { + return leftValue >= rightValue; + } + + protected boolean compute(int leftValue, int rightValue) + { + return leftValue >= rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/IdentifierExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/IdentifierExpression.java new file mode 100644 index 000000000..8d254927c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/IdentifierExpression.java @@ -0,0 +1,89 @@ +/* gnu.classpath.tools.gjdoc.expr.IdentifierExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class IdentifierExpression + implements Expression +{ + private String identifier; + + public IdentifierExpression(String identifier) + { + this.identifier = identifier; + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + Object value = context.getEvaluatorEnvironment().getValue(identifier, context.getVisitedFields()); + + if (value instanceof Byte) { + return new ConstantByte(((Byte)value).byteValue()); + } + else if (value instanceof Short) { + return new ConstantShort(((Short)value).shortValue()); + } + else if (value instanceof Integer) { + return new ConstantInteger(((Integer)value).intValue()); + } + else if (value instanceof Long) { + return new ConstantLong(((Long)value).longValue()); + } + else if (value instanceof Float) { + return new ConstantFloat(((Float)value).floatValue()); + } + else if (value instanceof Double) { + return new ConstantDouble(((Double)value).doubleValue()); + } + else if (value instanceof Boolean) { + return new ConstantBoolean(((Boolean)value).booleanValue()); + } + else if (value instanceof Character) { + return new ConstantChar(((Character)value).charValue()); + } + else if (value instanceof String) { + return new ConstantString((String)value); + } + else if (null != value) { + throw new IllegalExpressionException("Unsupported type " + value.getClass().getName() + " for identifier " + identifier); + } + else { + throw new IllegalExpressionException("Cannot resolve identifier " + identifier); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/IllegalExpressionException.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/IllegalExpressionException.java new file mode 100644 index 000000000..65cd5da11 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/IllegalExpressionException.java @@ -0,0 +1,52 @@ +/* gnu.classpath.tools.gjdoc.expr.IllegalExpressionException + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +public class IllegalExpressionException + extends Exception +{ + public IllegalExpressionException(String message) + { + super(message); + } + + public IllegalExpressionException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/InclusiveOrExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/InclusiveOrExpression.java new file mode 100644 index 000000000..2b0250133 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/InclusiveOrExpression.java @@ -0,0 +1,57 @@ +/* gnu.classpath.tools.gjdoc.expr.InclusiveOrExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class InclusiveOrExpression + extends BinaryBitwiseExpression +{ + public InclusiveOrExpression(Expression a, Expression b) + { + super(a, b); + } + + protected long compute(long leftValue, long rightValue) + { + return leftValue | rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue | rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LessThanExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LessThanExpression.java new file mode 100644 index 000000000..4e784cdcc --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LessThanExpression.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.expr.LessThanExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class LessThanExpression + extends BinaryRelationExpression +{ + public LessThanExpression(Expression left, Expression right) + { + super(left, right); + } + + protected boolean compute(double leftValue, double rightValue) + { + return leftValue < rightValue; + } + + protected boolean compute(float leftValue, float rightValue) + { + return leftValue < rightValue; + } + + protected boolean compute(long leftValue, long rightValue) + { + return leftValue < rightValue; + } + + protected boolean compute(int leftValue, int rightValue) + { + return leftValue < rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LessThanOrEqualExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LessThanOrEqualExpression.java new file mode 100644 index 000000000..803ad5ee3 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LessThanOrEqualExpression.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.expr.LessThanOrEqualExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class LessThanOrEqualExpression + extends BinaryRelationExpression +{ + public LessThanOrEqualExpression(Expression left, Expression right) + { + super(left, right); + } + + protected boolean compute(double leftValue, double rightValue) + { + return leftValue <= rightValue; + } + + protected boolean compute(float leftValue, float rightValue) + { + return leftValue <= rightValue; + } + + protected boolean compute(long leftValue, long rightValue) + { + return leftValue <= rightValue; + } + + protected boolean compute(int leftValue, int rightValue) + { + return leftValue <= rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalAndExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalAndExpression.java new file mode 100644 index 000000000..3b0409155 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalAndExpression.java @@ -0,0 +1,52 @@ +/* gnu.classpath.tools.gjdoc.expr.LogicalAndExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class LogicalAndExpression + extends BinaryLogicalExpression +{ + LogicalAndExpression(Expression left, Expression right) + { + super(left, right); + } + + protected boolean compute(boolean leftValue, boolean rightValue) + { + return leftValue && rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalNotExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalNotExpression.java new file mode 100644 index 000000000..4c7bf70f7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalNotExpression.java @@ -0,0 +1,60 @@ +/* gnu.classpath.tools.gjdoc.expr.LogicalNotExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class LogicalNotExpression + extends UnaryExpression +{ + protected LogicalNotExpression(Expression expr) + { + super(expr); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression value = expr.evaluate(context); + + if (Type.BOOLEAN == value.getType()) { + return new ConstantBoolean(!((ConstantBoolean)value).booleanValue()); + } + else { + throw new IllegalExpressionException("Operator ! cannot be applied to " + value.getType()); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalOrExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalOrExpression.java new file mode 100644 index 000000000..625a6e5e8 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/LogicalOrExpression.java @@ -0,0 +1,52 @@ +/* gnu.classpath.tools.gjdoc.expr.LogicalOrExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class LogicalOrExpression + extends BinaryLogicalExpression +{ + LogicalOrExpression(Expression a, Expression b) + { + super(a, b); + } + + protected boolean compute(boolean leftValue, boolean rightValue) + { + return leftValue || rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ModuloExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ModuloExpression.java new file mode 100644 index 000000000..6aca379dc --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ModuloExpression.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.expr.ModuloExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ModuloExpression + extends BinaryComputationExpression +{ + public ModuloExpression(Expression left, Expression right) + { + super(left, right); + } + + protected double compute(double leftValue, double rightValue) + { + return leftValue % rightValue; + } + + protected float compute(float leftValue, float rightValue) + { + return leftValue % rightValue; + } + + protected long compute(long leftValue, long rightValue) + { + return leftValue % rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue % rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/MultiplicationExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/MultiplicationExpression.java new file mode 100644 index 000000000..3d3071a50 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/MultiplicationExpression.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.expr.MultiplicationExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class MultiplicationExpression + extends BinaryComputationExpression +{ + public MultiplicationExpression(Expression left, Expression right) + { + super(left, right); + } + + protected double compute(double leftValue, double rightValue) + { + return leftValue * rightValue; + } + + protected float compute(float leftValue, float rightValue) + { + return leftValue * rightValue; + } + + protected long compute(long leftValue, long rightValue) + { + return leftValue * rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue * rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NegateExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NegateExpression.java new file mode 100644 index 000000000..5b66fecd2 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NegateExpression.java @@ -0,0 +1,66 @@ +/* gnu.classpath.tools.gjdoc.expr.NegateExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class NegateExpression + extends UnaryExpression +{ + protected NegateExpression(Expression expr) + { + super(expr); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression value = expr.evaluate(context); + + if (Type.LONG == value.getType()) { + return new ConstantLong(-value.asNumber().longValue()); + } + else if (Type.DOUBLE == value.getType()) { + return new ConstantDouble(-value.asNumber().doubleValue()); + } + else if (Type.FLOAT == value.getType()) { + return new ConstantDouble(-value.asNumber().floatValue()); + } + else { + return new ConstantInteger(-value.asNumber().intValue()); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NotEqualExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NotEqualExpression.java new file mode 100644 index 000000000..eb49bc087 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NotEqualExpression.java @@ -0,0 +1,72 @@ +/* gnu.classpath.tools.gjdoc.expr.NotEqualExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class NotEqualExpression + extends BinaryEqualityExpression +{ + public NotEqualExpression(Expression left, Expression right) + { + super(left, right); + } + + protected boolean compute(double leftValue, double rightValue) + { + return leftValue != rightValue; + } + + protected boolean compute(float leftValue, float rightValue) + { + return leftValue != rightValue; + } + + protected boolean compute(long leftValue, long rightValue) + { + return leftValue != rightValue; + } + + protected boolean compute(int leftValue, int rightValue) + { + return leftValue != rightValue; + } + + protected boolean compute(boolean leftValue, boolean rightValue) + { + return leftValue != rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NotExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NotExpression.java new file mode 100644 index 000000000..7359a055a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/NotExpression.java @@ -0,0 +1,60 @@ +/* gnu.classpath.tools.gjdoc.expr.NotExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class NotExpression + extends UnaryExpression +{ + protected NotExpression(Expression expr) + { + super(expr); + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression value = expr.evaluate(context); + + if (Type.LONG == value.getType()) { + return new ConstantLong(~value.asNumber().longValue()); + } + else { + return new ConstantInteger(~value.asNumber().intValue()); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ShiftLeftExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ShiftLeftExpression.java new file mode 100644 index 000000000..ad72efbfb --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ShiftLeftExpression.java @@ -0,0 +1,57 @@ +/* gnu.classpath.tools.gjdoc.expr.ShiftLeftExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ShiftLeftExpression + extends BinaryShiftExpression +{ + public ShiftLeftExpression(Expression left, Expression right) + { + super(left, right); + } + + protected long compute(long leftValue, int rightValue) + { + return leftValue << rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue << rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ShiftRightExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ShiftRightExpression.java new file mode 100644 index 000000000..f9d7820bb --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/ShiftRightExpression.java @@ -0,0 +1,57 @@ +/* gnu.classpath.tools.gjdoc.expr.ShiftRightExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class ShiftRightExpression + extends BinaryShiftExpression +{ + public ShiftRightExpression(Expression left, Expression right) + { + super(left, right); + } + + protected long compute(long leftValue, int rightValue) + { + return leftValue >> rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue >> rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/SubtractionExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/SubtractionExpression.java new file mode 100644 index 000000000..dc0101ffc --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/SubtractionExpression.java @@ -0,0 +1,67 @@ +/* gnu.classpath.tools.gjdoc.expr.SubtractionExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class SubtractionExpression + extends BinaryComputationExpression +{ + public SubtractionExpression(Expression left, Expression right) + { + super(left, right); + } + + protected double compute(double leftValue, double rightValue) + { + return leftValue - rightValue; + } + + protected float compute(float leftValue, float rightValue) + { + return leftValue - rightValue; + } + + protected long compute(long leftValue, long rightValue) + { + return leftValue - rightValue; + } + + protected int compute(int leftValue, int rightValue) + { + return leftValue - rightValue; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Type.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Type.java new file mode 100644 index 000000000..92382c41a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/Type.java @@ -0,0 +1,60 @@ +/* gnu.classpath.tools.gjdoc.expr.Type + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class Type +{ + public static final Type LONG = new Type(Long.TYPE); + public static final Type INTEGER = new Type(Integer.TYPE); + public static final Type BOOLEAN = new Type(Boolean.TYPE); + public static final Type DOUBLE = new Type(Double.TYPE); + public static final Type FLOAT = new Type(Float.TYPE); + public static final Type CHAR = new Type(Character.TYPE); + public static final Type BYTE = new Type(Byte.TYPE); + public static final Type SHORT = new Type(Short.TYPE); + public static final Type VOID = new Type(Void.TYPE); + public static final Type STRING = new Type(String.class); + public static final Type NULL = new Type(null); + + private Class clazz; + + private Type(Class clazz) + { + this.clazz = clazz; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/TypeCastExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/TypeCastExpression.java new file mode 100644 index 000000000..c434ae870 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/TypeCastExpression.java @@ -0,0 +1,87 @@ +/* gnu.classpath.tools.gjdoc.expr.TypeCastExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +class TypeCastExpression + extends UnaryExpression +{ + private Type type; + + public TypeCastExpression(Type type, Expression expr) + { + super(expr); + this.type = type; + } + + public ConstantExpression evaluate(Context context) + throws IllegalExpressionException + { + ConstantExpression value = expr.evaluate(context); + + if (Type.BYTE == type) { + return new ConstantByte(value.asNumber().byteValue()); + } + else if (Type.SHORT == type) { + return new ConstantShort(value.asNumber().shortValue()); + } + else if (Type.INTEGER == type) { + return new ConstantInteger(value.asNumber().intValue()); + } + else if (Type.LONG == type) { + return new ConstantLong(value.asNumber().longValue()); + } + else if (Type.CHAR == type) { + return new ConstantChar((char)value.asNumber().intValue()); + } + else if (Type.FLOAT == type) { + return new ConstantFloat((float)value.asNumber().intValue()); + } + else if (Type.DOUBLE == type) { + return new ConstantDouble((float)value.asNumber().intValue()); + } + else if (Type.BOOLEAN == type && Type.BOOLEAN == value.getType()) { + return value; + } + else if (Type.STRING == type && Type.STRING == value.getType()) { + return value; + } + else { + throw new IllegalExpressionException("Cannot cast " + value.getType() + " to " + type); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/UnaryExpression.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/UnaryExpression.java new file mode 100644 index 000000000..33befcb84 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/UnaryExpression.java @@ -0,0 +1,49 @@ +/* gnu.classpath.tools.gjdoc.expr.UnaryExpression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +abstract class UnaryExpression + implements Expression +{ + protected Expression expr; + + protected UnaryExpression(Expression expr) + { + this.expr = expr; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/UnknownIdentifierException.java b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/UnknownIdentifierException.java new file mode 100644 index 000000000..894a71716 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/UnknownIdentifierException.java @@ -0,0 +1,47 @@ +/* gnu.classpath.tools.gjdoc.expr.UnknownIdentifierException + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.gjdoc.expr; + +public class UnknownIdentifierException + extends IllegalExpressionException +{ + public UnknownIdentifierException(String identifier) + { + super(identifier); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/java-expression.g b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/java-expression.g new file mode 100644 index 000000000..4a21a8618 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/gjdoc/expr/java-expression.g @@ -0,0 +1,471 @@ +/* + * This grammar is derived from the Java 1.3 Recognizer + * (http://www.antlr.org/grammar/java/java.g) by Mitchell, Parr, Lilley, + * Stanchfield, Mohnen, Williams, Jacobs, Messick and Pybus, Version + * 1.21. + * + * This grammar recognizes simple Java expressions. The following + * language elements are NOT supported: + * + * - type casts to non-primitive types + * - method calls + * - constructor calls + * - array access + * - comma expressions + * - increment and decrement operators (both prefix/postfix) + * - expressions involving constant classes (Abc.class) + */ + +header { + package gnu.classpath.tools.gjdoc.expr; +} + +class JavaRecognizer extends Parser; +options { + k = 2; // two token lookahead + exportVocab=Java; // Call its vocabulary "Java" + codeGenMakeSwitchThreshold = 2; // Some optimizations + codeGenBitsetTestThreshold = 3; + defaultErrorHandler = false; // Don't generate parser error handlers + buildAST = true; +} + +tokens { + BLOCK; MODIFIERS; OBJBLOCK; SLIST; CTOR_DEF; METHOD_DEF; VARIABLE_DEF; + INSTANCE_INIT; STATIC_INIT; TYPE; CLASS_DEF; INTERFACE_DEF; + PACKAGE_DEF; ARRAY_DECLARATOR; EXTENDS_CLAUSE; IMPLEMENTS_CLAUSE; + PARAMETERS; PARAMETER_DEF; LABELED_STAT; TYPECAST; INDEX_OP; + POST_INC; POST_DEC; METHOD_CALL; EXPR; ARRAY_INIT; + IMPORT; UNARY_MINUS; UNARY_PLUS; CASE_GROUP; ELIST; FOR_INIT; FOR_CONDITION; + FOR_ITERATOR; EMPTY_STAT; FINAL="final"; ABSTRACT="abstract"; + STRICTFP="strictfp"; SUPER_CTOR_CALL; CTOR_CALL; +} + +// A builtin type specification is a builtin type with possible brackets +// afterwards (which would make it an array type). +builtInTypeSpec[boolean addImagNode] returns [Type t = null] + : t=builtInType (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* + { + if ( addImagNode ) { + #builtInTypeSpec = #(#[TYPE,"TYPE"], #builtInTypeSpec); + } + } + ; + +// A type name. which is either a (possibly qualified) class name or +// a primitive (builtin) type +type returns [Type t] + : t=builtInType + ; + +// The primitive types. +builtInType returns [Type t = null] + : "void" {t=Type.VOID;} + | "boolean" {t=Type.BOOLEAN;} + | "byte" {t=Type.BYTE;} + | "char" {t=Type.CHAR;} + | "short" {t=Type.SHORT;} + | "int" {t=Type.INTEGER;} + | "float"{t=Type.FLOAT;} + | "long" {t=Type.LONG;} + | "double" {t=Type.DOUBLE;} + | "String" {t=Type.STRING;} + ; + +// A (possibly-qualified) java identifier. We start with the first IDENT +// and expand its name by adding dots and following IDENTS +identifier returns [String s = null;] + : i:IDENT {s=i.getText();} ( DOT^ i2:IDENT {s+="."+i2.getText();} )* + ; + +expression returns [Expression e = null] + : e=conditionalExpression EOF! + ; + +// conditional test (level 12) +conditionalExpression returns [Expression e = null] { Expression a,b,c; } + : e=logicalOrExpression + ( QUESTION^ b=conditionalExpression COLON! c=conditionalExpression {e=new ConditionalExpression(e,b,c);} )? + ; + + +// logical or (||) (level 11) +logicalOrExpression returns [Expression e = null] { Expression a,b; } + : e=logicalAndExpression (LOR^ b=logicalAndExpression {e=new LogicalOrExpression(e,b);})* + ; + + +// logical and (&&) (level 10) +logicalAndExpression returns [Expression e = null] { Expression a,b; } + : e=inclusiveOrExpression (LAND^ b=inclusiveOrExpression {e=new LogicalAndExpression(e,b);})* + ; + + +// bitwise or non-short-circuiting or (|) (level 9) +inclusiveOrExpression returns [Expression e = null] { Expression a,b; } + : e=exclusiveOrExpression (BOR^ b=exclusiveOrExpression {e=new InclusiveOrExpression(e,b);})* + ; + + +// exclusive or (^) (level 8) +exclusiveOrExpression returns [Expression e = null] { Expression a,b; } + : e=andExpression (BXOR^ b=andExpression {e=new ExclusiveOrExpression(e,b);})* + ; + + +// bitwise or non-short-circuiting and (&) (level 7) +andExpression returns [Expression e = null] { Expression a,b; } + : e=equalityExpression (BAND^ b=equalityExpression {e=new AndExpression(e,b);})* + ; + + +// equality/inequality (==/!=) (level 6) +equalityExpression returns [Expression e = null] { Expression a,b; } + : e=relationalExpression ((NOT_EQUAL^ a=relationalExpression {e=new NotEqualExpression(e,a);} | EQUAL^ a=relationalExpression {e=new EqualExpression(e,a);}))* + ; + + +// boolean relational expressions (level 5) +relationalExpression returns [Expression e = null] { Expression a,b; } + : e=shiftExpression + ( ( ( LT^ a=shiftExpression {e=new LessThanExpression(e,a);} + | GT^ a=shiftExpression {e=new GreaterThanExpression(e,a);} + | LE^ a=shiftExpression {e=new LessThanOrEqualExpression(e,a);} + | GE^ a=shiftExpression {e=new GreaterThanOrEqualExpression(e,a);} + ) + + )* + ) + ; + + +// bit shift expressions (level 4) +shiftExpression returns [Expression e = null] { Expression a,b; } + : e=additiveExpression ((SL^ a=additiveExpression {e=new ShiftLeftExpression(e,a);} | SR^ a=additiveExpression {e=new ShiftRightExpression(e,a);} | BSR^ a=additiveExpression {e=new BitShiftRightExpression(e,a);}))* + ; + + +// binary addition/subtraction (level 3) +additiveExpression returns [Expression e = null] { Expression a,b; } + : e=multiplicativeExpression ((PLUS^ a=multiplicativeExpression {e=new AdditionExpression(e,a);} | MINUS^ a=multiplicativeExpression {e=new SubtractionExpression(e,a);}))* + ; + + +// multiplication/division/modulo (level 2) +multiplicativeExpression returns [Expression e = null] { Expression a,b; } + : e=unaryExpression ((STAR^ a=unaryExpression {e=new MultiplicationExpression(e,a);} | DIV^ a=unaryExpression {e=new DivisionExpression(e,a);} | MOD^ a=unaryExpression {e=new ModuloExpression(e,a);} ))* + ; + + +unaryExpression returns [Expression e = null] { Expression a,b; } + : MINUS^ {#MINUS.setType(UNARY_MINUS);} a=unaryExpression {e=new NegateExpression(a);} + | PLUS^ {#PLUS.setType(UNARY_PLUS);} e=unaryExpression + | e=unaryExpressionNotPlusMinus + ; + +unaryExpressionNotPlusMinus returns [Expression e = null] { Expression a; Type t; } + : BNOT^ a=unaryExpression {e=new NotExpression(a);} + | LNOT^ a=unaryExpression {e=new LogicalNotExpression(a);} + + // use predicate to skip cases like: (int.class) + | (LPAREN builtInTypeSpec[true] RPAREN) => + lpb:LPAREN^ {#lpb.setType(TYPECAST);} t=builtInTypeSpec[true] RPAREN! + a=unaryExpression {e=new TypeCastExpression(t,a);} + + | e=primaryExpression + ; + +// the basic element of an expression +primaryExpression returns [Expression e = null; String i = null;] + : e=constant + | i=identifier {e=new IdentifierExpression(i);} + | "true" { e=new ConstantBoolean(true); } + | "false" { e=new ConstantBoolean(false); } + | "null" { e=new ConstantNull(); } + | LPAREN! e=conditionalExpression RPAREN! + ; + +/** Match a, a.b.c refs + */ +identPrimary returns [Expression e = null] + : IDENT + ( + options { + // .ident could match here or in postfixExpression. + // We do want to match here. Turn off warning. + greedy=true; + } + : DOT^ IDENT + )* + ; + +constant returns [Expression e = null] + : l1:NUM_INT {e=new ConstantInteger(l1.getText());} + | l2:CHAR_LITERAL {e=new ConstantChar(l2.getText());} + | l3:STRING_LITERAL {e=new ConstantString(l3.getText().substring(1, l3.getText().length()-1)); } + | l4:NUM_FLOAT {e=new ConstantFloat(l4.getText());} + | l5:NUM_LONG {e=new ConstantLong(l5.getText());} + | l6:NUM_DOUBLE {e=new ConstantDouble(l6.getText());} + ; + + +//---------------------------------------------------------------------------- +// The Java scanner +//---------------------------------------------------------------------------- +class JavaLexer extends Lexer; + +options { + exportVocab=Java; // call the vocabulary "Java" + testLiterals=false; // don't automatically test for literals + k=4; // four characters of lookahead + charVocabulary='\u0003'..'\uFFFF'; + // without inlining some bitset tests, couldn't do unicode; + // I need to make ANTLR generate smaller bitsets; see + // bottom of JavaLexer.java + codeGenBitsetTestThreshold=20; +} + + + +// OPERATORS +QUESTION : '?' ; +LPAREN : '(' ; +RPAREN : ')' ; +LBRACK : '[' ; +RBRACK : ']' ; +LCURLY : '{' ; +RCURLY : '}' ; +COLON : ':' ; +COMMA : ',' ; +//DOT : '.' ; +ASSIGN : '=' ; +EQUAL : "==" ; +LNOT : '!' ; +BNOT : '~' ; +NOT_EQUAL : "!=" ; +DIV : '/' ; +DIV_ASSIGN : "/=" ; +PLUS : '+' ; +PLUS_ASSIGN : "+=" ; +INC : "++" ; +MINUS : '-' ; +MINUS_ASSIGN : "-=" ; +DEC : "--" ; +STAR : '*' ; +STAR_ASSIGN : "*=" ; +MOD : '%' ; +MOD_ASSIGN : "%=" ; +SR : ">>" ; +SR_ASSIGN : ">>=" ; +BSR : ">>>" ; +BSR_ASSIGN : ">>>=" ; +GE : ">=" ; +GT : ">" ; +SL : "<<" ; +SL_ASSIGN : "<<=" ; +LE : "<=" ; +LT : '<' ; +BXOR : '^' ; +BXOR_ASSIGN : "^=" ; +BOR : '|' ; +BOR_ASSIGN : "|=" ; +LOR : "||" ; +BAND : '&' ; +BAND_ASSIGN : "&=" ; +LAND : "&&" ; +SEMI : ';' ; + + +// Whitespace -- ignored +WS : ( ' ' + | '\t' + | '\f' + // handle newlines + | ( options {generateAmbigWarnings=false;} + : "\r\n" // Evil DOS + | '\r' // Macintosh + | '\n' // Unix (the right way) + ) + { newline(); } + )+ + { _ttype = Token.SKIP; } + ; + +// Single-line comments +SL_COMMIT + : "//" + (~('\n'|'\r'))* ('\n'|'\r'('\n')?) + {$setType(Token.SKIP); newline();} + ; + +// multiple-line comments +ML_COMMENT + : "/*" + ( /* '\r' '\n' can be matched in one alternative or by matching + '\r' in one iteration and '\n' in another. I am trying to + handle any flavor of newline that comes in, but the language + that allows both "\r\n" and "\r" and "\n" to all be valid + newline is ambiguous. Consequently, the resulting grammar + must be ambiguous. I'm shutting this warning off. + */ + options { + generateAmbigWarnings=false; + } + : + { LA(2)!='/' }? '*' + | '\r' '\n' {newline();} + | '\r' {newline();} + | '\n' {newline();} + | ~('*'|'\n'|'\r') + )* + "*/" + {$setType(Token.SKIP);} + ; + + +// character literals +CHAR_LITERAL + : '\'' ( ESC | ~('\''|'\n'|'\r'|'\\') ) '\'' + ; + +// string literals +STRING_LITERAL + : '"' (ESC|~('"'|'\\'|'\n'|'\r'))* '"' + ; + + +// escape sequence -- note that this is protected; it can only be called +// from another lexer rule -- it will not ever directly return a token to +// the parser +// There are various ambiguities hushed in this rule. The optional +// '0'...'9' digit matches should be matched here rather than letting +// them go back to STRING_LITERAL to be matched. ANTLR does the +// right thing by matching immediately; hence, it's ok to shut off +// the FOLLOW ambig warnings. +protected +ESC + : '\\' + ( 'n' + | 'r' + | 't' + | 'b' + | 'f' + | '"' + | '\'' + | '\\' + | ('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT + | '0'..'3' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + )? + )? + | '4'..'7' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + )? + ) + ; + + +// hexadecimal digit (again, note it's protected!) +protected +HEX_DIGIT + : ('0'..'9'|'A'..'F'|'a'..'f') + ; + + +// a dummy rule to force vocabulary to be all characters (except special +// ones that ANTLR uses internally (0 to 2) +protected +VOCAB + : '\3'..'\377' + ; + + +// an identifier. Note that testLiterals is set to true! This means +// that after we match the rule, we look in the literals table to see +// if it's a literal or really an identifer +IDENT + options {testLiterals=true;} + : ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'$')* + ; + + +// a numeric literal +NUM_INT + {boolean isDecimal=false; Token t=null;} + : '.' {_ttype = DOT;} + ( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})? + { + if (t != null && t.getText().toUpperCase().indexOf('F')>=0) { + _ttype = NUM_FLOAT; + } + else { + _ttype = NUM_DOUBLE; // assume double + } + } + )? + + | ( '0' {isDecimal = true;} // special case for just '0' + ( ('x'|'X') + ( // hex + // the 'e'|'E' and float suffix stuff look + // like hex digits, hence the (...)+ doesn't + // know when to stop: ambig. ANTLR resolves + // it correctly by matching immediately. It + // is therefor ok to hush warning. + options { + warnWhenFollowAmbig=false; + } + : HEX_DIGIT + )+ + + | //float or double with leading zero + (('0'..'9')+ ('.'|EXPONENT|FLOAT_SUFFIX)) => ('0'..'9')+ + + | ('0'..'7')+ // octal + )? + | ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal + ) + ( ('l'|'L') { _ttype = NUM_LONG; } + + // only check to see if it's a float if looks like decimal so far + | {isDecimal}? + ( '.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})? + | EXPONENT (f3:FLOAT_SUFFIX {t=f3;})? + | f4:FLOAT_SUFFIX {t=f4;} + ) + { + if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) { + _ttype = NUM_FLOAT; + } + else { + _ttype = NUM_DOUBLE; // assume double + } + } + )? + ; + + +// a couple protected methods to assist in matching floating point numbers +protected +EXPONENT + : ('e'|'E') ('+'|'-')? ('0'..'9')+ + ; + + +protected +FLOAT_SUFFIX + : 'f'|'F'|'d'|'D' + ; diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Action.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Action.java new file mode 100644 index 000000000..6363157ae --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Action.java @@ -0,0 +1,51 @@ +/* Action.java - an action taken by the jar driver + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import java.io.IOException; + +public abstract class Action +{ + protected Action() + { + } + + public abstract void run(Main parameters) + throws IOException; +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Creator.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Creator.java new file mode 100644 index 000000000..be8d2a16f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Creator.java @@ -0,0 +1,251 @@ +/* Creator.java - create a new jar file + Copyright (C) 2006, 2008 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; + +public class Creator + extends Action +{ + JarOutputStream outputStream; + HashSet<String> writtenItems = new HashSet<String>(); + // The manifest to use, or null if we don't want a manifest. + Manifest manifest; + + private long copyFile(CRC32 crc, InputStream is, OutputStream output) + throws IOException + { + byte[] buffer = new byte[1024]; + long size = 0; + while (true) + { + int len = is.read(buffer); + if (len == - 1) + break; + size += len; + output.write(buffer, 0, len); + crc.update(buffer, 0, len); + } + output.close(); + return size; + } + + protected void writeFile(boolean isDirectory, InputStream inputFile, + String filename, boolean verbose) + throws IOException + { + if (writtenItems.contains(filename)) + { + if (verbose) + { + String msg = MessageFormat.format(Messages.getString("Creator.Ignoring"), //$NON-NLS-1$ + new Object[] { filename }); + System.err.println(msg); + } + return; + } + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + CRC32 crc = new CRC32(); + long size; + if (isDirectory) + { + size = 0; + } + else + { + size = copyFile(crc, inputFile, out); + } + + ZipEntry entry = new ZipEntry(filename); + entry.setCrc(crc.getValue()); + entry.setSize(size); + + outputStream.putNextEntry(entry); + out.writeTo(outputStream); + outputStream.closeEntry(); + writtenItems.add(filename); + + if (verbose) + { + long csize = entry.getCompressedSize(); + long perc; + if (size == 0) + perc = 0; + else + perc = 100 - (100 * csize) / size; + String msg = MessageFormat.format(Messages.getString("Creator.Adding"), //$NON-NLS-1$ + new Object[] + { + filename, + Long.valueOf(size), + Long.valueOf(entry.getSize()), + Long.valueOf(perc) + }); + System.err.println(msg); + } + } + + protected void writeFile(File file, String filename, boolean verbose) + throws IOException + { + boolean isDirectory = file.isDirectory(); + if (isDirectory) + { + if (filename.charAt(filename.length() - 1) != '/') + filename += '/'; + writeFile(isDirectory, null, filename, verbose); + } + else + { + InputStream inputStream = new FileInputStream(file); + writeFile(isDirectory, inputStream, filename, verbose); + inputStream.close(); + } + } + + private void addEntries(ArrayList<Entry> result, Entry entry) + { + if (entry.file.isDirectory()) + { + String name = entry.name; + if (name.charAt(name.length() - 1) != '/') + { + name += '/'; + entry = new Entry(entry.file, name); + } + result.add(entry); + String[] files = entry.file.list(); + for (int i = 0; i < files.length; ++i) + addEntries(result, new Entry(new File(entry.file, files[i]), + entry.name + files[i])); + } + else + result.add(entry); + } + + private ArrayList<Entry> getAllEntries(Main parameters) + { + ArrayList<Entry> allEntries = new ArrayList<Entry>(); + for (Entry entry : parameters.entries) + addEntries(allEntries, entry); + return allEntries; + } + + private void writeCommandLineEntries(Main parameters) + throws IOException + { + // We've already written the manifest, make sure to mark it. + writtenItems.add("META-INF/"); //$NON-NLS-1$ + writtenItems.add(JarFile.MANIFEST_NAME); + + ArrayList<Entry> allEntries = getAllEntries(parameters); + for (Entry entry : allEntries) + writeFile(entry.file, entry.name, parameters.verbose); + } + + protected Manifest createManifest(Main parameters) + throws IOException + { + if (! parameters.wantManifest) + return null; + if (parameters.manifestFile != null) + { + // User specified a manifest file. + InputStream contents = new FileInputStream(parameters.manifestFile); + return new Manifest(contents); + } + return new Manifest(); + } + + protected void writeCommandLineEntries(Main parameters, OutputStream os) + throws IOException + { + manifest = createManifest(parameters); + /* If no version is specified, provide the same manifest version default + * as Sun's jar tool */ + if (parameters.wantManifest) + { + Attributes attr = manifest.getMainAttributes(); + if (attr.getValue(Attributes.Name.MANIFEST_VERSION) == null) + attr.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0"); + attr.putValue("Created-By", System.getProperty("java.version") + + " (" + System.getProperty("java.vendor") + ")"); + } + outputStream = new JarOutputStream(os, manifest); + // FIXME: this sets the method too late for the manifest file. + outputStream.setMethod(parameters.storageMode); + writeCommandLineEntries(parameters); + } + + protected void close() throws IOException + { + outputStream.finish(); + outputStream.close(); + } + + public void run(Main parameters) throws IOException + { + if (parameters.archiveFile == null || parameters.archiveFile.equals("-")) //$NON-NLS-1$ + writeCommandLineEntries(parameters, System.out); + else + { + OutputStream os + = new BufferedOutputStream(new FileOutputStream(parameters.archiveFile)); + writeCommandLineEntries(parameters, os); + } + close(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Entry.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Entry.java new file mode 100644 index 000000000..b9108798a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Entry.java @@ -0,0 +1,70 @@ +/* Entry.java - represent a single file to write to a jar + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import java.io.File; + +public class Entry +{ + public File file; + + public String name; + + public Entry(File file, String name) + { + this.file = file; + + /* Removes any './' prefixes automatically. Those caused trouble + * in (boot) classpath use-cases. See #32516. + */ + int start = 0; + while (name.length() > start + 2 + && name.codePointAt(start) == '.' + && name.codePointAt(start + 1) == File.separatorChar) + start += 2; + + this.name = name.substring(start); + } + + public Entry(File file) + { + this(file, file.toString()); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Extractor.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Extractor.java new file mode 100644 index 000000000..203ff0566 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Extractor.java @@ -0,0 +1,127 @@ +/* Extractor.java - action to extract from a jar file + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class Extractor + extends Action +{ + // This is a set of all the items specified on the command line. + private WorkSet allItems; + + private void copyFile(InputStream input, File output) throws IOException + { + FileOutputStream os = new FileOutputStream(output); + byte[] buffer = new byte[1024]; + while (true) + { + int len = input.read(buffer); + if (len == - 1) + break; + os.write(buffer, 0, len); + } + os.close(); + } + + public void run(Main parameters) throws IOException + { + // Figure out what we want to extract. + allItems = new WorkSet(parameters.entries); + // Open the input file. + ZipInputStream zis; + File zfile = parameters.archiveFile; + if (zfile == null || "-".equals(zfile.getName())) //$NON-NLS-1$ + zis = new ZipInputStream(System.in); + else + { + InputStream ins = new BufferedInputStream(new FileInputStream(zfile)); + zis = new ZipInputStream(ins); + } + // Extract stuff. + while (true) + { + ZipEntry entry = zis.getNextEntry(); + if (entry == null) + break; + if (! allItems.contains(entry.getName())) + continue; + File file = new File(entry.getName()); + if (entry.isDirectory()) + { + if (file.mkdirs()) + { + if (parameters.verbose) + { + String msg + = MessageFormat.format(Messages.getString("Extractor.Created"), //$NON-NLS-1$ + new Object[] { file }); + System.err.println(msg); + } + } + continue; + } + + File parent = file.getParentFile(); + if (parent != null) + parent.mkdirs(); + + copyFile(zis, file); + + if (parameters.verbose) + { + String fmt; + if (entry.getMethod() == ZipEntry.STORED) + fmt = Messages.getString("Extractor.Extracted"); //$NON-NLS-1$ + else + fmt = Messages.getString("Extractor.Inflated"); //$NON-NLS-1$ + String msg = MessageFormat.format(fmt, new Object[] { file }); + System.err.println(msg); + } + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Indexer.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Indexer.java new file mode 100644 index 000000000..ae79b26e9 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Indexer.java @@ -0,0 +1,142 @@ +/* Indexer.java -- add index.list file to jar + Copyright (C) 2006, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import gnu.java.net.IndexListParser; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +public class Indexer + extends Updater +{ + private void indexJarFile(StringBuilder result, File fileName, + boolean verbose) + throws IOException + { + if (verbose) + { + String msg = MessageFormat.format(Messages.getString("Indexer.Indexing"), //$NON-NLS-1$ + new Object[] { fileName }); + System.err.println(msg); + } + JarFile jf = new JarFile(fileName); + + // Index the files in this jar. + // The results look a little better if we keep them + // in insertion order. + LinkedHashSet<String> entries = new LinkedHashSet<String>(); + Enumeration e = jf.entries(); + while (e.hasMoreElements()) + { + JarEntry entry = (JarEntry) e.nextElement(); + String name = entry.getName(); + if (name.startsWith("META-INF/")) //$NON-NLS-1$ + continue; + int index = name.lastIndexOf('/'); + if (index != -1) + name = name.substring(0, index); + entries.add(name); + } + if (! entries.isEmpty()) + { + result.append(fileName); + // Any line ending will do. + result.append('\n'); + for (String s : entries) + { + result.append(s); + result.append('\n'); + } + // Paragraph break. + result.append('\n'); + } + + // Now read pointed-to jars. + Manifest m = jf.getManifest(); + if (m != null) + { + File parent = fileName.getParentFile(); + Attributes attrs = m.getMainAttributes(); + String jars = attrs.getValue(Attributes.Name.CLASS_PATH); + if (jars != null) + { + StringTokenizer st = new StringTokenizer(jars, " "); //$NON-NLS-1$ + while (st.hasMoreTokens()) + { + String name = st.nextToken(); + indexJarFile(result, new File(parent, name), verbose); + } + } + } + + jf.close(); + } + + protected void writeCommandLineEntries(Main parameters, OutputStream os) + throws IOException + { + // This is a pretty lame design. We know the super call will + // only have side effects and won't actually write anything important. + super.writeCommandLineEntries(parameters, os); + + // Now compute our index file and write it. + StringBuilder contents = new StringBuilder(); + indexJarFile(contents, parameters.archiveFile, parameters.verbose); + if (contents.length() != 0) + { + // Insert in reverse order to avoid computing anything. + contents.insert(0, "1.0\n\n"); //$NON-NLS-1$ + contents.insert(0, IndexListParser.JAR_INDEX_VERSION_KEY); + ByteArrayInputStream in + = new ByteArrayInputStream(contents.toString().getBytes()); + writeFile(false, in, IndexListParser.JAR_INDEX_FILE, parameters.verbose); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Lister.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Lister.java new file mode 100644 index 000000000..98275f789 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Lister.java @@ -0,0 +1,112 @@ +/* Lister.java - action to list contents of a jar file + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; +import java.util.Date; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class Lister + extends Action +{ + private WorkSet allItems; + + private long readUntilEnd(InputStream is) throws IOException + { + byte[] buffer = new byte[5 * 1024]; + long result = 0; + while (true) + { + int r = is.read(buffer); + if (r == -1) + break; + result += r; + } + return result; + } + + private void listJar(ZipInputStream zis, boolean verbose) throws IOException + { + MessageFormat format = null; + if (verbose) + format = new MessageFormat(" {0,date,E M dd HH:mm:ss z yyyy} {1}"); + while (true) + { + ZipEntry entry = zis.getNextEntry(); + if (entry == null) + break; + if (! allItems.contains(entry.getName())) + continue; + if (verbose) + { + // Read the stream; entry.getSize() is unreliable. + // (Also, we're just going to read it anyway.) + long size = readUntilEnd(zis); + // No easy way to right-justify the size using + // MessageFormat -- how odd. + String s = " " + size; + int index = Math.min(s.length() - 5, 5); + System.out.print(s.substring(index)); + Object[] values = new Object[] { new Date(entry.getTime()), + entry.getName() }; + System.out.println(format.format(values)); + } + else + System.out.println(entry.getName()); + } + } + + public void run(Main parameters) throws IOException + { + allItems = new WorkSet(parameters.entries); + File file = parameters.archiveFile; + ZipInputStream zis; + if (file == null || "-".equals(file.getName())) + zis = new ZipInputStream(System.in); + else + zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(file))); + listJar(zis, parameters.verbose); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Main.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Main.java new file mode 100644 index 000000000..34ef928fe --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Main.java @@ -0,0 +1,293 @@ +/* Main.java - jar program main() + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.FileArgumentCallback; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.zip.ZipOutputStream; + +public class Main +{ + /** The mode of operation. This is the class representing + * the action; we make a new instance before using it. It + * must be a subclass of Action. 'null' means the mode + * has not yet been set. */ + Class operationMode; + + /** The archive file name. */ + File archiveFile; + + /** The zip storage mode. */ + int storageMode = ZipOutputStream.DEFLATED; + + /** True if we should read file names from stdin. */ + boolean readNamesFromStdin = false; + + /** True for verbose mode. */ + boolean verbose = false; + + /** True if we want a manifest file. */ + boolean wantManifest = true; + + /** Name of manifest file to use. */ + File manifestFile; + + /** A list of Entry objects, each describing a file to write. */ + ArrayList<Entry> entries = new ArrayList<Entry>(); + + /** Used only while parsing, holds the first argument for -C. */ + String changedDirectory; + + void setArchiveFile(String filename) throws OptionException + { + if (archiveFile != null) + { + String fmt = MessageFormat.format(Messages.getString("Main.ArchiveAlreadySet"), //$NON-NLS-1$ + new Object[] { archiveFile }); + throw new OptionException(fmt); + } + archiveFile = new File(filename); + } + + class HandleFile + extends FileArgumentCallback + { + public void notifyFile(String fileArgument) + { + Entry entry; + if (changedDirectory != null) + { + entry = new Entry(new File(changedDirectory, fileArgument), + fileArgument); + changedDirectory = null; + } + else + entry = new Entry(new File(fileArgument)); + entries.add(entry); + } + } + + // An option that knows how to set the operation mode. + private class ModeOption + extends Option + { + private Class mode; + + public ModeOption(char shortName, String description, Class mode) + { + super(shortName, description); + this.mode = mode; + } + + public ModeOption(char shortName, String description, String argName, + Class mode) + { + super(shortName, description, argName); + this.mode = mode; + } + + public void parsed(String argument) throws OptionException + { + if (operationMode != null) + throw new OptionException(Messages.getString("Main.ModeAlreaySet")); //$NON-NLS-1$ + operationMode = mode; + // We know this is only the case for -i. + if (argument != null) + setArchiveFile(argument); + } + } + + private class JarParser extends ClasspathToolParser + { + public JarParser(String name) + { + super(name); + } + + protected void validate() throws OptionException + { + if (operationMode == null) + throw new OptionException(Messages.getString("Main.MustSpecify")); //$NON-NLS-1$ + if (changedDirectory != null) + throw new OptionException(Messages.getString("Main.TwoArgsReqd")); //$NON-NLS-1$ + if (! wantManifest && manifestFile != null) + throw new OptionException(Messages.getString("Main.CantHaveBoth")); //$NON-NLS-1$ + if (operationMode == Indexer.class) + { + // Some extra validation for -i. + if (! entries.isEmpty()) + throw new OptionException(Messages.getString("Main.NoFilesWithi")); //$NON-NLS-1$ + if (! wantManifest) + throw new OptionException(Messages.getString("Main.NoMAndi")); //$NON-NLS-1$ + if (manifestFile != null) + throw new OptionException(Messages.getString("Main.AnotherNomAndi")); //$NON-NLS-1$ + } + } + } + + private ClasspathToolParser initializeParser() + { + ClasspathToolParser p = new JarParser("jar"); //$NON-NLS-1$ + p.setHeader(Messages.getString("Main.Usage")); //$NON-NLS-1$ + + OptionGroup grp = new OptionGroup(Messages.getString("Main.OpMode")); //$NON-NLS-1$ + grp.add(new ModeOption('c', Messages.getString("Main.Create"), Creator.class)); //$NON-NLS-1$ + grp.add(new ModeOption('x', Messages.getString("Main.Extract"), Extractor.class)); //$NON-NLS-1$ + grp.add(new ModeOption('t', Messages.getString("Main.List"), Lister.class)); //$NON-NLS-1$ + grp.add(new ModeOption('u', Messages.getString("Main.Update"), Updater.class)); //$NON-NLS-1$ + // Note that -i works in-place and explicitly requires a file name. + grp.add(new ModeOption('i', Messages.getString("Main.Index"), Messages.getString("Main.FileArg"), Indexer.class)); //$NON-NLS-1$ //$NON-NLS-2$ + p.add(grp); + + grp = new OptionGroup(Messages.getString("Main.OpMods")); //$NON-NLS-1$ + grp.add(new Option('f', Messages.getString("Main.ArchiveName"), Messages.getString("Main.FileArg2")) //$NON-NLS-1$ //$NON-NLS-2$ + { + public void parsed(String argument) throws OptionException + { + setArchiveFile(argument); + } + }); + grp.add(new Option('0', Messages.getString("Main.NoZip")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + storageMode = ZipOutputStream.STORED; + } + }); + grp.add(new Option('v', Messages.getString("Main.Verbose")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + grp.add(new Option('M', Messages.getString("Main.NoManifest")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + wantManifest = false; + } + }); + grp.add(new Option('m', Messages.getString("Main.ManifestName"), Messages.getString("Main.ManifestArgName")) //$NON-NLS-1$ //$NON-NLS-2$ + { + public void parsed(String argument) throws OptionException + { + manifestFile = new File(argument); + } + }); + // -@ + p.add(grp); + + grp = new OptionGroup(Messages.getString("Main.FileNameGroup")); //$NON-NLS-1$ + grp.add(new Option('C', Messages.getString("Main.ChangeDir"), //$NON-NLS-1$ + Messages.getString("Main.ChangeDirArg")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + changedDirectory = argument; + } + }); + grp.add(new Option('@', Messages.getString("Main.Stdin")) + { + public void parsed(String argument) throws OptionException + { + readNamesFromStdin = true; + } + }); + p.add(grp); + + return p; + } + + private void readNames() + { + String line; + try + { + BufferedReader br + = new BufferedReader(new InputStreamReader(System.in)); + while ((line = br.readLine()) != null) + entries.add(new Entry(new File(line))); + } + catch (IOException _) + { + // Ignore. + } + } + + private void run(String[] args) + throws InstantiationException, IllegalAccessException, IOException + { + ClasspathToolParser p = initializeParser(); + // Special hack to emulate old tar-style commands. + if (args.length > 0 && args[0].charAt(0) != '-') + args[0] = '-' + args[0]; + p.parse(args, new HandleFile(), true); + if (readNamesFromStdin) + readNames(); + Action t = (Action) operationMode.newInstance(); + t.run(this); + } + + public static void main(String[] args) + { + Main jarprogram = new Main(); + try + { + jarprogram.run(args); + } + catch (Exception e) + { + System.err.println(Messages.getString("Main.InternalError")); //$NON-NLS-1$ + e.printStackTrace(System.err); + System.exit(1); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Messages.java new file mode 100644 index 000000000..ea54bd08f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- localization support for jar + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.jar.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/Updater.java b/libjava/classpath/tools/gnu/classpath/tools/jar/Updater.java new file mode 100644 index 000000000..e1df852ff --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/Updater.java @@ -0,0 +1,98 @@ +/* Updater.java - action to update a jar file + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +public class Updater + extends Creator +{ + JarFile inputJar; + + protected Manifest createManifest(Main parameters) throws IOException + { + Manifest result = inputJar.getManifest(); + if (result == null) + return super.createManifest(parameters); + if (parameters.manifestFile != null) + result.read(new FileInputStream(parameters.manifestFile)); + return result; + } + + public void run(Main parameters) throws IOException + { + // Set this early so that createManifest can use it. + inputJar = new JarFile(parameters.archiveFile); + + // Write all the new entries to a temporary file. + File tmpFile = File.createTempFile("jarcopy", null, + parameters.archiveFile.getParentFile()); + OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile)); + writeCommandLineEntries(parameters, os); + + // Now read the old file and copy extra entries to the new file. + Enumeration e = inputJar.entries(); + while (e.hasMoreElements()) + { + ZipEntry entry = (ZipEntry) e.nextElement(); + if (writtenItems.contains(entry.getName())) + continue; + writeFile(entry.isDirectory(), inputJar.getInputStream(entry), + entry.getName(), parameters.verbose); + } + + close(); + if (!tmpFile.renameTo(parameters.archiveFile)) + { + throw new IOException("Couldn't rename new JAR file " + tmpFile + + "to " + parameters.archiveFile); + } + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jar/WorkSet.java b/libjava/classpath/tools/gnu/classpath/tools/jar/WorkSet.java new file mode 100644 index 000000000..d4b7f1eac --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jar/WorkSet.java @@ -0,0 +1,83 @@ +/* WorkSet.java -- Helper to track what files to work on + Copyright (C) 2006, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import java.util.ArrayList; +import java.util.HashSet; + +public class WorkSet +{ + private HashSet<String> allItems; + + private void initSet(ArrayList<Entry> entries) + { + if (entries == null || entries.isEmpty()) + return; + allItems = new HashSet<String>(); + for (Entry entry : entries) + { + int len = entry.name.length(); + while (len > 0 && entry.name.charAt(len - 1) == '/') + --len; + String name = entry.name.substring(0, len); + allItems.add(name); + } + } + + public WorkSet(ArrayList<Entry> entries) + { + initSet(entries); + } + + public boolean contains(String filename) + { + if (allItems == null) + return true; + while (filename.length() > 0) + { + if (allItems.contains(filename)) + return true; + int index = filename.lastIndexOf('/'); + if (index == -1) + break; + filename = filename.substring(0, index); + } + return false; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jarsigner/HashUtils.java b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/HashUtils.java new file mode 100644 index 000000000..c9921dd70 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/HashUtils.java @@ -0,0 +1,124 @@ +/* Utils.java -- Utility methods for JAR file signing/verification + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.classpath.Configuration; +import gnu.java.security.hash.Sha160; +import gnu.java.util.Base64; +import gnu.java.util.jar.JarUtils; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.logging.Logger; + +/** + * Collection of utility methods used in JAR file signing and verification. + */ +class HashUtils +{ + private static final Logger log = Logger.getLogger(HashUtils.class.getName()); + private Sha160 sha = new Sha160(); + + // default 0-arguments constructor + + /** + * @param stream the input stream to digest. + * @return a base-64 representation of the resulting SHA-1 digest of the + * contents of the designated input stream. + * @throws IOException if an I/O related exception occurs during the process. + */ + String hashStream(InputStream stream) throws IOException + { + BufferedInputStream bis = new BufferedInputStream(stream, 4096); + byte[] buffer = new byte[4096]; + int count = 0; + int n; + while ((n = bis.read(buffer)) != - 1) + if (n > 0) + { + sha.update(buffer, 0, n); + count += n; + } + byte[] hash = sha.digest(); + if (Configuration.DEBUG) + log.finest("Hashed " + count + " byte(s)"); + String result = Base64.encode(hash); + return result; + } + + /** + * @param ba the byte array to digest. + * @return a base-64 representation of the resulting SHA-1 digest of the + * contents of the designated buffer. + */ + String hashByteArray(byte[] ba) throws IOException + { + sha.update(ba); + byte[] hash = sha.digest(); + if (Configuration.DEBUG) + log.finest("Hashed " + ba.length + " byte(s)"); + String result = Base64.encode(hash); + return result; + } + + /** + * @param name the JAR entry name + * @param entryHash the hash of the entry file which appears in the + * manifest. + * @return the base-64 encoded form of the hash of the corresponding Manifest + * JAR entry which will appear in the SF file under the entry with the + * same name. + * @throws UnsupportedEncodingException If UTF-8 character encoding is not + * supported on this platform. + */ + String hashManifestEntry(String name, String entryHash) + throws UnsupportedEncodingException + { + sha.update((JarUtils.NAME + ": " + name).getBytes("UTF-8")); + sha.update(JarUtils.CRLF); + sha.update((Main.DIGEST + ": " + entryHash).getBytes("UTF-8")); + sha.update(JarUtils.CRLF); + sha.update(JarUtils.CRLF); + byte[] sfHash = sha.digest(); + String result = Base64.encode(sfHash); + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jarsigner/JarSigner.java b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/JarSigner.java new file mode 100644 index 000000000..87db8b952 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/JarSigner.java @@ -0,0 +1,173 @@ +/* JarSigner.java -- The signing handler of the gjarsigner tool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.classpath.Configuration; +import gnu.classpath.SystemProperties; +import gnu.java.util.jar.JarUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.logging.Logger; + +/** + * The JAR signing handler of the <code>gjarsigner</code> tool. + */ +public class JarSigner +{ + private static final Logger log = Logger.getLogger(JarSigner.class.getName()); + /** The owner tool of this handler. */ + private Main main; + + JarSigner(Main main) + { + super(); + + this.main = main; + } + + void start() throws Exception + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + JarFile jarFile = new JarFile(main.getJarFileName()); + SFHelper sfHelper = new SFHelper(jarFile); + + sfHelper.startSigning(); + + // 1. compute the digests + for (Enumeration e = jarFile.entries(); e.hasMoreElements(); ) + { + JarEntry je = (JarEntry) e.nextElement(); + String jeName = je.getName(); + if (jeName.equals(JarFile.MANIFEST_NAME) + || jeName.endsWith(File.separator)) + continue; + + sfHelper.updateEntry(je); + if (main.isVerbose()) + System.out.println(Messages.getString("JarSigner.1") + jeName); //$NON-NLS-1$ + } + + sfHelper.finishSigning(main.isSectionsOnly()); + if (main.isVerbose()) + System.out.println(Messages.getString("JarSigner.2") + JarFile.MANIFEST_NAME); //$NON-NLS-1$ + + // 2. write jar entries and manifest + File signedJarFile = File.createTempFile("gcp-", ".jar"); //$NON-NLS-1$ //$NON-NLS-2$ + FileOutputStream fos = new FileOutputStream(signedJarFile); + JarOutputStream outSignedJarFile = new JarOutputStream(fos, + sfHelper.getManifest()); + for (Enumeration e = jarFile.entries(); e.hasMoreElements(); ) + { + JarEntry je = (JarEntry) e.nextElement(); + String jeName = je.getName(); + if (jeName.equals(JarFile.MANIFEST_NAME) + || jeName.endsWith(File.separator)) + continue; + + log.finest("Processing " + jeName); //$NON-NLS-1$ + JarEntry newEntry = new JarEntry(jeName); + newEntry.setTime(je.getTime()); + outSignedJarFile.putNextEntry(newEntry); + InputStream jeis = jarFile.getInputStream(je); + copyFromTo(jeis, outSignedJarFile); + } + + // 3. create the .SF file + String signaturesFileName = main.getSigFileName(); + String sfFileName = JarUtils.META_INF + signaturesFileName + + JarUtils.SF_SUFFIX; + if (Configuration.DEBUG) + log.fine("Processing " + sfFileName); //$NON-NLS-1$ + JarEntry sfEntry = new JarEntry(sfFileName); + sfEntry.setTime(System.currentTimeMillis()); + outSignedJarFile.putNextEntry(sfEntry); + sfHelper.writeSF(outSignedJarFile); + if (Configuration.DEBUG) + log.fine("Created .SF file"); //$NON-NLS-1$ + if (main.isVerbose()) + System.out.println(Messages.getString("JarSigner.8") + sfFileName); //$NON-NLS-1$ + + // 4. create the .DSA file + String dsaFileName = JarUtils.META_INF + signaturesFileName + + JarUtils.DSA_SUFFIX; + if (Configuration.DEBUG) + log.fine("Processing " + dsaFileName); //$NON-NLS-1$ + JarEntry dsaEntry = new JarEntry(dsaFileName); + dsaEntry.setTime(System.currentTimeMillis()); + outSignedJarFile.putNextEntry(dsaEntry); + sfHelper.writeDSA(outSignedJarFile, + main.getSignerPrivateKey(), + main.getSignerCertificateChain(), + main.isInternalSF()); + if (Configuration.DEBUG) + log.fine("Created .DSA file"); //$NON-NLS-1$ + if (main.isVerbose()) + System.out.println(Messages.getString("JarSigner.8") + dsaFileName); //$NON-NLS-1$ + + // cleanup + outSignedJarFile.close(); + fos.close(); + signedJarFile.renameTo(new File(main.getSignedJarFileName())); + if (Configuration.DEBUG) + log.fine("Renamed signed JAR file"); //$NON-NLS-1$ + if (main.isVerbose()) + System.out.println(SystemProperties.getProperty("line.separator") //$NON-NLS-1$ + + Messages.getString("JarSigner.14")); //$NON-NLS-1$ + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + private void copyFromTo(InputStream in, JarOutputStream out) + throws IOException + { + byte[] buffer = new byte[8192]; + int n; + while ((n = in.read(buffer)) != -1) + if (n > 0) + out.write(buffer, 0, n); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jarsigner/JarVerifier.java b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/JarVerifier.java new file mode 100644 index 000000000..b920d74c1 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/JarVerifier.java @@ -0,0 +1,343 @@ +/* JarVerifier.java -- The verification handler of the gjarsigner tool + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.classpath.Configuration; +import gnu.java.security.OID; +import gnu.java.security.Registry; +import gnu.java.security.pkcs.PKCS7SignedData; +import gnu.java.security.pkcs.SignerInfo; +import gnu.java.security.sig.ISignature; +import gnu.java.security.sig.ISignatureCodec; +import gnu.java.security.sig.dss.DSSSignature; +import gnu.java.security.sig.dss.DSSSignatureX509Codec; +import gnu.java.security.sig.rsa.RSAPKCS1V1_5Signature; +import gnu.java.security.sig.rsa.RSAPKCS1V1_5SignatureX509Codec; +import gnu.java.security.util.Util; +import gnu.java.util.jar.JarUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.security.PublicKey; +import java.security.cert.CRLException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Logger; +import java.util.zip.ZipException; + +/** + * The JAR verification handler of the <code>gjarsigner</code> tool. + */ +public class JarVerifier +{ + private static final Logger log = Logger.getLogger(JarVerifier.class.getName()); + /** The owner tool of this handler. */ + private Main main; + private HashUtils util = new HashUtils(); + /** The JAR file to verify. */ + private JarFile jarFile; + /** Map of jar entry names to their hash. */ + private Map<String, String> entryHashes = new HashMap<String, String>(); + + JarVerifier(Main main) + { + super(); + + this.main = main; + } + + void start() throws Exception + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + String jarFileName = main.getJarFileName(); + jarFile = new JarFile(jarFileName); + + // 1. find all signature (.SF) files + List<String> sfFiles = new ArrayList<String>(); + for (Enumeration e = jarFile.entries(); e.hasMoreElements(); ) + { + JarEntry je = (JarEntry) e.nextElement(); + String jeName = je.getName(); + if (! (jeName.startsWith(JarUtils.META_INF) + && jeName.endsWith(JarUtils.SF_SUFFIX))) + continue; + + // only interested in .SF files in, and not deeper than, META-INF + String[] jeNameParts = jeName.split("/"); //$NON-NLS-1$ + if (jeNameParts.length != 2) + continue; + + String sfName = jeNameParts[1]; + String sigFileName = sfName.substring(0, sfName.length() - 3); + sfFiles.add(sigFileName); + } + + // 2. verify each one + if (sfFiles.isEmpty()) + System.out.println(Messages.getString("JarVerifier.2")); //$NON-NLS-1$ + else + { + int limit = sfFiles.size(); + int count = 0; + for (String alias : sfFiles) + if (verifySF(alias)) + if (verifySFEntries(alias)) + count++; + + if (count == 0) + System.out.println(Messages.getString("JarVerifier.3")); //$NON-NLS-1$ + else if (count != limit) + System.out.println(Messages.getFormattedString("JarVerifier.4", //$NON-NLS-1$ + new Integer[] {Integer.valueOf(count), + Integer.valueOf(limit)})); + else + System.out.println(Messages.getFormattedString("JarVerifier.7", //$NON-NLS-1$ + Integer.valueOf(limit))); + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + /** + * @param sigFileName the name of the signature file; i.e. the name to use for + * both the .SF and .DSA files. + * @return <code>true</code> if the designated file-name (usually a key-store + * <i>alias</i> name) has been successfully checked as the signer of the + * corresponding <code>.SF</code> file. Returns <code>false</code> otherwise. + * @throws IOException + * @throws ZipException + * @throws CertificateException + * @throws CRLException + */ + private boolean verifySF(String sigFileName) throws CRLException, + CertificateException, ZipException, IOException + { + if (Configuration.DEBUG) + { + log.entering(this.getClass().getName(), "verifySF"); //$NON-NLS-1$ + log.fine("About to verify signature of " + sigFileName + "..."); //$NON-NLS-1$ //$NON-NLS-2$ + } + // 1. find the corresponding .DSA file for this .SF file + JarEntry dsaEntry = jarFile.getJarEntry(JarUtils.META_INF + sigFileName + + JarUtils.DSA_SUFFIX); + if (dsaEntry == null) + throw new SecurityException(Messages.getFormattedString("JarVerifier.13", //$NON-NLS-1$ + sigFileName)); + // 2. read the .DSA file contents as a PKCS7 SignedData + InputStream in = jarFile.getInputStream(dsaEntry); + PKCS7SignedData pkcs7SignedData = new PKCS7SignedData(in); + + // 4. get the encrypted digest octet string from the first SignerInfo + // this octet string is the digital signature of the .SF file contents + Set signerInfos = pkcs7SignedData.getSignerInfos(); + if (signerInfos == null || signerInfos.isEmpty()) + throw new SecurityException(Messages.getString("JarVerifier.14")); //$NON-NLS-1$ + + SignerInfo signerInfo = (SignerInfo) signerInfos.iterator().next(); + byte[] encryptedDigest = signerInfo.getEncryptedDigest(); + if (encryptedDigest == null) + throw new SecurityException(Messages.getString("JarVerifier.16")); //$NON-NLS-1$ + + if (Configuration.DEBUG) + log.fine("\n" + Util.dumpString(encryptedDigest, "--- signedSFBytes ")); //$NON-NLS-1$ //$NON-NLS-2$ + + // 5. get the signer public key + Certificate cert = pkcs7SignedData.getCertificates()[0]; + PublicKey verifierKey = cert.getPublicKey(); + if (Configuration.DEBUG) + log.fine("--- verifier public key = " + verifierKey); //$NON-NLS-1$ + + // 6. verify the signature file signature + OID digestEncryptionAlgorithmOID = signerInfo.getDigestEncryptionAlgorithmId(); + ISignature signatureAlgorithm; + ISignatureCodec signatureCodec; + if (digestEncryptionAlgorithmOID.equals(Main.DSA_SIGNATURE_OID)) + { + signatureAlgorithm = new DSSSignature(); + signatureCodec = new DSSSignatureX509Codec(); + } + else + { + signatureAlgorithm = new RSAPKCS1V1_5Signature(Registry.MD5_HASH); + signatureCodec = new RSAPKCS1V1_5SignatureX509Codec(); + } + + Map signatureAttributes = new HashMap(); + signatureAttributes.put(ISignature.VERIFIER_KEY, verifierKey); + signatureAlgorithm.setupVerify(signatureAttributes); + + Object herSignature = signatureCodec.decodeSignature(encryptedDigest); + + // 7. verify the signature file contents + JarEntry sfEntry = jarFile.getJarEntry(JarUtils.META_INF + sigFileName + + JarUtils.SF_SUFFIX); + in = jarFile.getInputStream(sfEntry); + byte[] buffer = new byte[2048]; + int n; + while ((n = in.read(buffer)) != -1) + if (n > 0) + signatureAlgorithm.update(buffer, 0, n); + + boolean result = signatureAlgorithm.verify(herSignature); + if (Configuration.DEBUG) + { + log.fine("Signature block [" + sigFileName + "] is " //$NON-NLS-1$ //$NON-NLS-2$ + + (result ? "" : "NOT ") + "OK"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + log.exiting(this.getClass().getName(), "verifySF", Boolean.valueOf(result)); //$NON-NLS-1$ + } + return result; + } + + /** + * This method is called after at least one signer (usually a key-store + * <code>alias</code> name) was found to be trusted; i.e. his/her signature + * block in the corresponding <code>.DSA</code> file was successfully + * verified using his/her public key. + * <p> + * This method, uses the contents of the corresponding <code>.SF</code> file + * to compute and verify the hashes of the manifest entries in the JAR file. + * + * @param alias the name of the signature file; i.e. the name to use for both + * the .SF and .DSA files. + * @return <code>true</code> if all the entries in the corresponding + * <code>.SF</code> file have the same hash values as their + * alter-ego in the <i>manifest</i> file of the JAR file inquestion. + * @throws IOException if an I/O related exception occurs during the process. + */ + private boolean verifySFEntries(String alias) throws IOException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "verifySFEntries"); //$NON-NLS-1$ + // 1. read the signature file + JarEntry jarEntry = jarFile.getJarEntry(JarUtils.META_INF + alias + + JarUtils.SF_SUFFIX); + InputStream in = jarFile.getInputStream(jarEntry); + Attributes attr = new Attributes(); + Map<String, Attributes> entries = new HashMap<String, Attributes>(); + JarUtils.readSFManifest(attr, entries, in); + + // 2. The .SF file by default includes a header containing a hash of the + // entire manifest file. When the header is present, then the verification + // can check to see whether or not the hash in the header indeed matches + // the hash of the manifest file. + boolean result = false; + String hash = attr.getValue(Main.DIGEST_MANIFEST_ATTR); + if (hash != null) + result = verifyManifest(hash); + + // A verification is still considered successful if none of the files that + // were in the JAR file when the signature was generated have been changed + // since then, which is the case if the hashes in the non-header sections + // of the .SF file equal the hashes of the corresponding sections in the + // manifest file. + // + // 3. Read each file in the JAR file that has an entry in the .SF file. + // While reading, compute the file's digest, and then compare the result + // with the digest for this file in the manifest section. The digests + // should be the same, or verification fails. + if (! result) + for (Entry<String, Attributes> me : entries.entrySet()) + { + String name = me.getKey(); + attr = me.getValue(); + hash = attr.getValue(Main.DIGEST_ATTR); + result = verifySFEntry(name, hash); + if (! result) + break; + } + + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "verifySFEntries", //$NON-NLS-1$ + Boolean.valueOf(result)); + return result; + } + + /** + * @param hash Base-64 encoded form of the manifest's digest. + * @return <code>true</code> if our computation of the manifest's hash + * matches the given value; <code>false</code> otherwise. + * @throws IOException if unable to acquire the JAR's manifest entry. + */ + private boolean verifyManifest(String hash) throws IOException + { + return verifySFEntry(JarFile.MANIFEST_NAME, hash); + } + + /** + * @param name the name of a JAR entry to verify. + * @param hash Base-64 encoded form of the designated entry's digest. + * @return <code>true</code> if our computation of the JAR entry's hash + * matches the given value; <code>false</code> otherwise. + * @throws IOException if an exception occurs while returning the entry's + * input stream. + */ + private boolean verifySFEntry(String name, String hash) throws IOException + { + String expectedValue = getEntryHash(JarFile.MANIFEST_NAME); + boolean result = expectedValue.equalsIgnoreCase(hash); + if (Configuration.DEBUG) + log.fine("Is " + name + " OK? " + result); //$NON-NLS-1$ //$NON-NLS-2$ + return result; + } + + private String getEntryHash(String entryName) throws IOException + { + String result = (String) entryHashes.get(entryName); + if (result == null) + { + JarEntry manifest = jarFile.getJarEntry(entryName); + InputStream in = jarFile.getInputStream(manifest); + result = util.hashStream(in); + entryHashes.put(entryName, result); + } + + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jarsigner/Main.java b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/Main.java new file mode 100644 index 000000000..c7fb5856a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/Main.java @@ -0,0 +1,689 @@ +/* Main.java -- JAR signing and verification tool not unlike jarsigner + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.classpath.Configuration; +import gnu.classpath.SystemProperties; +import gnu.classpath.tools.common.CallbackUtil; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.common.ProviderUtil; +import gnu.classpath.tools.getopt.FileArgumentCallback; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.java.security.OID; +import gnu.java.security.Registry; +import gnu.javax.security.auth.callback.ConsoleCallbackHandler; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.Security; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Locale; +import java.util.jar.Attributes.Name; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The GNU Classpath implementation of the <i>jarsigner</i> tool. + * <p> + * The <i>jarsigner</i> tool is used to sign and verify JAR (Java ARchive) + * files. + * <p> + * This implementation is intended to be compatible with the behaviour + * described in the public documentation of the same tool included in JDK 1.4. + */ +public class Main +{ + protected static final Logger log = Logger.getLogger(Main.class.getName()); + static final String KEYTOOL_TOOL = "jarsigner"; //$NON-NLS-1$ + private static final Locale EN_US_LOCALE = new Locale("en", "US"); //$NON-NLS-1$ //$NON-NLS-2$ + static final String DIGEST = "SHA1-Digest"; //$NON-NLS-1$ + static final String DIGEST_MANIFEST = "SHA1-Digest-Manifest"; //$NON-NLS-1$ + static final Name DIGEST_ATTR = new Name(DIGEST); + static final Name DIGEST_MANIFEST_ATTR = new Name(DIGEST_MANIFEST); + static final OID DSA_SIGNATURE_OID = new OID(Registry.DSA_OID_STRING); + static final OID RSA_SIGNATURE_OID = new OID(Registry.RSA_OID_STRING); + + protected boolean verify; + protected String ksURL; + protected String ksType; + protected String password; + protected String ksPassword; + protected String sigFileName; + protected String signedJarFileName; + protected boolean verbose; + protected boolean certs; + protected boolean internalSF; + protected boolean sectionsOnly; + protected String providerClassName; + protected String jarFileName; + protected String alias; + + protected Provider provider; + private boolean providerInstalled; + private char[] ksPasswordChars; + private KeyStore store; + private char[] passwordChars; + private PrivateKey signerPrivateKey; + private Certificate[] signerCertificateChain; + /** The callback handler to use when needing to interact with user. */ + private CallbackHandler handler; + /** The command line parser. */ + private ToolParser cmdLineParser; + protected ArrayList<String> fileAndAlias = new ArrayList<String>(); + + private Main() + { + super(); + } + + public static final void main(String[] args) + { + if (Configuration.DEBUG) + log.entering(Main.class.getName(), "main", args); //$NON-NLS-1$ + Main tool = new Main(); + int result = 1; + try + { + tool.processArgs(args); + tool.start(); + result = 0; + } + catch (SecurityException x) + { + if (Configuration.DEBUG) + log.throwing(Main.class.getName(), "main", x); //$NON-NLS-1$ + System.err.println(Messages.getString("Main.7") + x.getMessage()); //$NON-NLS-1$ + } + catch (Exception x) + { + if (Configuration.DEBUG) + log.throwing(Main.class.getName(), "main", x); //$NON-NLS-1$ + System.err.println(Messages.getString("Main.9") + x); //$NON-NLS-1$ + } + finally + { + tool.teardown(); + } + if (Configuration.DEBUG) + log.exiting(Main.class.getName(), "main", Integer.valueOf(result)); //$NON-NLS-1$ + System.exit(result); + } + + /** + * Read the command line arguments setting the tool's parameters in + * preparation for the user desired action. + * + * @param args an array of options (strings). + * @throws Exception if an exception occurs during the process. + */ + private void processArgs(String[] args) throws Exception + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "processArgs", args); //$NON-NLS-1$ + cmdLineParser = new ToolParser(); + cmdLineParser.initializeParser(); + cmdLineParser.parse(args, new ToolParserCallback()); + + setupCommonParams(); + if (verify) + { + if (Configuration.DEBUG) + { + log.fine("Will verify with the following parameters:"); //$NON-NLS-1$ + log.fine(" jar-file = '" + jarFileName + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine("Options:"); //$NON-NLS-1$ + log.fine(" provider = '" + providerClassName + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" verbose ? " + verbose); //$NON-NLS-1$ + log.fine(" certs ? " + certs); //$NON-NLS-1$ + log.fine(" internalsf ? " + internalSF); //$NON-NLS-1$ + log.fine(" sectionsonly ? " + sectionsOnly); //$NON-NLS-1$ + } + } + else // sign + { + setupSigningParams(); + if (Configuration.DEBUG) + { + log.fine("Will sign with the following parameters:"); //$NON-NLS-1$ + log.fine(" jar-file = '" + jarFileName + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" alias = '" + alias + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine("Options:"); //$NON-NLS-1$ + log.fine(" keystore = '" + ksURL + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" storetype = '" + ksType + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" storepass = '" + ksPassword + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" keypass = '" + password + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" sigfile = '" + sigFileName + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" signedjar = '" + signedJarFileName + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" provider = '" + providerClassName + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + log.fine(" verbose ? " + verbose); //$NON-NLS-1$ + log.fine(" internalsf ? " + internalSF); //$NON-NLS-1$ + log.fine(" sectionsonly ? " + sectionsOnly); //$NON-NLS-1$ + } + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "processArgs"); //$NON-NLS-1$ + } + + /** + * Invokes the <code>start()</code> method of the concrete handler. + * <p> + * Depending on the result of processing the command line arguments, this + * handler may be one for signing the jar, or verifying it. + * + * @throws Exception if an exception occurs during the process. + */ + private void start() throws Exception + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + if (verify) + { + JarVerifier jv = new JarVerifier(this); + jv.start(); + } + else + { + JarSigner js = new JarSigner(this); + js.start(); + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + /** + * Ensures that the underlying JVM is left in the same state as we found it + * when we first launched the tool. Specifically, if we have installed a new + * security provider then now is the time to remove it. + * <p> + * Note (rsn): this may not be necessary if we terminate the JVM; i.e. call + * {@link System#exit(int)} at the end of the tool's invocation. Nevertheless + * it's good practive to return the JVM to its initial state. + */ + private void teardown() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + if (providerInstalled) + ProviderUtil.removeProvider(provider.getName()); + + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + } + + /** + * After processing the command line arguments, this method is invoked to + * process the common parameters which may have been encountered among the + * actual arguments. + * <p> + * Common parameters are those which are allowed in both signing and + * verification modes. + * + * @throws InstantiationException if a security provider class name is + * specified but that class name is that of either an interface or + * an abstract class. + * @throws IllegalAccessException if a security provider class name is + * specified but no 0-arguments constructor is defined for that + * class. + * @throws ClassNotFoundException if a security provider class name is + * specified but no such class was found in the classpath. + * @throws IOException if the JAR file name for signing, or verifying, does + * not exist, exists but denotes a directory, or is not readable. + */ + private void setupCommonParams() throws InstantiationException, + IllegalAccessException, ClassNotFoundException, IOException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "setupCommonParams"); //$NON-NLS-1$ + File jar = new File(jarFileName); + if (! jar.exists()) + throw new FileNotFoundException(jarFileName); + + if (jar.isDirectory()) + throw new IOException(Messages.getFormattedString("Main.70", jarFileName)); //$NON-NLS-1$ + + if (! jar.canRead()) + throw new IOException(Messages.getFormattedString("Main.72", jarFileName)); //$NON-NLS-1$ //$NON-NLS-2$ + + if (providerClassName != null && providerClassName.length() > 0) + { + provider = (Provider) Class.forName(providerClassName).newInstance(); + // is it already installed? + String providerName = provider.getName(); + Provider installedProvider = Security.getProvider(providerName); + if (installedProvider != null) + { + if (Configuration.DEBUG) + log.finer("Provider " + providerName + " is already installed"); //$NON-NLS-1$ //$NON-NLS-2$ + } + else // install it + installNewProvider(); + } + + if (! verbose && certs) + { + if (Configuration.DEBUG) + log.fine("Option <certs> is set but <verbose> is not. Ignored"); //$NON-NLS-1$ + certs = false; + } + + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "setupCommonParams"); //$NON-NLS-1$ + } + + /** + * Install the user defined security provider in the underlying JVM. + * <p> + * Also record this fact so we can remove it when we exit the tool. + */ + private void installNewProvider() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "installNewProvider"); //$NON-NLS-1$ + providerInstalled = ProviderUtil.addProvider(provider) != -1; + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "installNewProvider"); //$NON-NLS-1$ + } + + /** + * After processing the command line arguments, this method is invoked to + * process the parameters which may have been encountered among the actual + * arguments, and which are specific to the signing action of the tool. + * + * @throws KeyStoreException if no implementation of the designated (or + * default type) of a key store is availabe. + * @throws IOException if an I/O related exception occurs during the process. + * @throws NoSuchAlgorithmException if an implementation of an algorithm used + * by the key store is not available. + * @throws CertificateException if an exception occurs while reading a + * certificate from the key store. + * @throws UnsupportedCallbackException if no implementation of a password + * callback is available. + * @throws UnrecoverableKeyException if the wrong password was used to unlock + * the key store. + * @throws SecurityException if the designated alias is not known to the key + * store or is not an Alias of a Key Entry. + */ + private void setupSigningParams() throws KeyStoreException, IOException, + NoSuchAlgorithmException, CertificateException, + UnsupportedCallbackException, UnrecoverableKeyException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "setupSigningParams"); //$NON-NLS-1$ + if (ksURL == null || ksURL.trim().length() == 0) + { + String userHome = SystemProperties.getProperty("user.home"); //$NON-NLS-1$ + if (userHome == null || userHome.trim().length() == 0) + throw new SecurityException(Messages.getString("Main.85")); //$NON-NLS-1$ + + ksURL = "file:" + userHome.trim() + "/.keystore"; //$NON-NLS-1$ //$NON-NLS-2$ + } + else + { + ksURL = ksURL.trim(); + if (ksURL.indexOf(":") == -1) //$NON-NLS-1$ + ksURL = "file:" + ksURL; //$NON-NLS-1$ + } + + if (ksType == null || ksType.trim().length() == 0) + ksType = KeyStore.getDefaultType(); + else + ksType = ksType.trim(); + + store = KeyStore.getInstance(ksType); + + if (ksPassword == null) + { + // ask the user to provide one + PasswordCallback pcb = new PasswordCallback(Messages.getString("Main.92"), //$NON-NLS-1$ + false); + getCallbackHandler().handle(new Callback[] { pcb }); + ksPasswordChars = pcb.getPassword(); + } + else + ksPasswordChars = ksPassword.toCharArray(); + + URL url = new URL(ksURL); + InputStream stream = url.openStream(); + store.load(stream, ksPasswordChars); + + if (! store.containsAlias(alias)) + throw new SecurityException(Messages.getFormattedString("Main.6", alias)); //$NON-NLS-1$ + + if (! store.isKeyEntry(alias)) + throw new SecurityException(Messages.getFormattedString("Main.95", alias)); //$NON-NLS-1$ + + Key key; + if (password == null) + { + passwordChars = ksPasswordChars; + try + { + key = store.getKey(alias, passwordChars); + } + catch (UnrecoverableKeyException x) + { + // ask the user to provide one + String prompt = Messages.getFormattedString("Main.97", alias); //$NON-NLS-1$ + PasswordCallback pcb = new PasswordCallback(prompt, false); + getCallbackHandler().handle(new Callback[] { pcb }); + passwordChars = pcb.getPassword(); + // take 2 + key = store.getKey(alias, passwordChars); + } + } + else + { + passwordChars = password.toCharArray(); + key = store.getKey(alias, passwordChars); + } + + if (! (key instanceof PrivateKey)) + throw new SecurityException(Messages.getFormattedString("Main.99", alias)); //$NON-NLS-1$ + + signerPrivateKey = (PrivateKey) key; + signerCertificateChain = store.getCertificateChain(alias); + if (Configuration.DEBUG) + log.fine(String.valueOf(signerCertificateChain)); + + if (sigFileName == null) + sigFileName = alias; + + sigFileName = sigFileName.toUpperCase(EN_US_LOCALE); + if (sigFileName.length() > 8) + sigFileName = sigFileName.substring(0, 8); + + char[] chars = sigFileName.toCharArray(); + for (int i = 0; i < chars.length; i++) + { + char c = chars[i]; + if (! (Character.isLetter(c) + || Character.isDigit(c) + || c == '_' + || c == '-')) + chars[i] = '_'; + } + + sigFileName = new String(chars); + + if (signedJarFileName == null) + signedJarFileName = jarFileName; + + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "setupSigningParams"); //$NON-NLS-1$ + } + + boolean isVerbose() + { + return verbose; + } + + boolean isCerts() + { + return certs; + } + + String getSigFileName() + { + return this.sigFileName; + } + + String getJarFileName() + { + return this.jarFileName; + } + + boolean isSectionsOnly() + { + return this.sectionsOnly; + } + + boolean isInternalSF() + { + return this.internalSF; + } + + PrivateKey getSignerPrivateKey() + { + return this.signerPrivateKey; + } + + Certificate[] getSignerCertificateChain() + { + return signerCertificateChain; + } + + String getSignedJarFileName() + { + return this.signedJarFileName; + } + + /** + * Return a CallbackHandler which uses the Console (System.in and System.out) + * for interacting with the user. + * <p> + * This method first finds all currently installed security providers capable + * of providing such service and then in turn attempts to instantiate the + * handler from those providers. As soon as one provider returns a non-null + * instance of the callback handler, the search stops and that instance is + * set to be used from now on. + * <p> + * If no installed providers were found, this method falls back on the GNU + * provider, by-passing the Security search mechanism. The default console + * callback handler implementation is {@link ConsoleCallbackHandler}. + * + * @return a console-based {@link CallbackHandler}. + */ + protected CallbackHandler getCallbackHandler() + { + if (handler == null) + handler = CallbackUtil.getConsoleHandler(); + + return handler; + } + + private class ToolParserCallback + extends FileArgumentCallback + { + public void notifyFile(String fileArgument) + { + fileAndAlias.add(fileArgument); + } + } + + private class ToolParser + extends ClasspathToolParser + { + public ToolParser() + { + super(KEYTOOL_TOOL, true); + } + + protected void validate() throws OptionException + { + if (fileAndAlias.size() < 1) + throw new OptionException(Messages.getString("Main.133")); //$NON-NLS-1$ + + jarFileName = (String) fileAndAlias.get(0); + if (! verify) // must have an ALIAS. use "mykey" if undefined + if (fileAndAlias.size() < 2) + { + if (Configuration.DEBUG) + log.fine("Missing ALIAS argument. Will use [mykey] instead"); //$NON-NLS-1$ + alias = "mykey"; //$NON-NLS-1$ + } + else + alias = fileAndAlias.get(1); + } + + public void initializeParser() + { + setHeader(Messages.getString("Main.2")); //$NON-NLS-1$ + setFooter(Messages.getString("Main.1")); //$NON-NLS-1$ + OptionGroup signGroup = new OptionGroup(Messages.getString("Main.0")); //$NON-NLS-1$ + signGroup.add(new Option("keystore", //$NON-NLS-1$ + Messages.getString("Main.101"), //$NON-NLS-1$ + Messages.getString("Main.102")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + ksURL = argument; + } + }); + signGroup.add(new Option("storetype", //$NON-NLS-1$ + Messages.getString("Main.104"), //$NON-NLS-1$ + Messages.getString("Main.105")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + ksType = argument; + } + }); + signGroup.add(new Option("storepass", //$NON-NLS-1$ + Messages.getString("Main.107"), //$NON-NLS-1$ + Messages.getString("Main.108")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + ksPassword = argument; + } + }); + signGroup.add(new Option("keypass", //$NON-NLS-1$ + Messages.getString("Main.110"), //$NON-NLS-1$ + Messages.getString("Main.111")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + password = argument; + } + }); + signGroup.add(new Option("sigfile", //$NON-NLS-1$ + Messages.getString("Main.113"), //$NON-NLS-1$ + Messages.getString("Main.114")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + sigFileName = argument; + } + }); + signGroup.add(new Option("signedjar", //$NON-NLS-1$ + Messages.getString("Main.116"), //$NON-NLS-1$ + Messages.getString("Main.117")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + signedJarFileName = argument; + } + }); + add(signGroup); + + OptionGroup verifyGroup = new OptionGroup(Messages.getString("Main.118")); //$NON-NLS-1$ + verifyGroup.add(new Option("verify", //$NON-NLS-1$ + Messages.getString("Main.120")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verify = true; + } + }); + verifyGroup.add(new Option("certs", //$NON-NLS-1$ + Messages.getString("Main.122")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + certs = true; + } + }); + add(verifyGroup); + + OptionGroup commonGroup = new OptionGroup(Messages.getString("Main.123")); //$NON-NLS-1$ + commonGroup.add(new Option("verbose", //$NON-NLS-1$ + Messages.getString("Main.125")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + commonGroup.add(new Option("internalsf", //$NON-NLS-1$ + Messages.getString("Main.127")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + internalSF = true; + } + }); + commonGroup.add(new Option("sectionsonly", //$NON-NLS-1$ + Messages.getString("Main.129")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + sectionsOnly = true; + } + }); + commonGroup.add(new Option("provider", //$NON-NLS-1$ + Messages.getString("Main.131"), //$NON-NLS-1$ + Messages.getString("Main.132")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + providerClassName = argument; + } + }); + add(commonGroup); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jarsigner/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/Messages.java new file mode 100644 index 000000000..f27f79eae --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/Messages.java @@ -0,0 +1,119 @@ +/* Messages.java -- I18N related helper class + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.classpath.Configuration; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.logging.Logger; + +/** + * An initially generated Eclipse helper class to ease the use of localized + * messages. + * <p> + * Enriched to handle localized message formats. + */ +class Messages +{ + private static final Logger log = Logger.getLogger(Messages.class.getName()); + private static final String BUNDLE_NAME = "gnu.classpath.tools.jarsigner.messages"; + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + private static final Map<String, MessageFormat> CACHED_FORMATS = + new HashMap<String, MessageFormat>(5); + + private Messages() + { + super(); + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return constructMessage(key, null); + } + } + + public static String getFormattedString(String key, Object args) + { + MessageFormat mf = CACHED_FORMATS.get(key); + if (mf == null) + { + String formatString = getString(key); + if (formatString.startsWith("!")) + return constructMessage(key, args); + + mf = new MessageFormat(formatString); + CACHED_FORMATS.put(key, mf); + } + + // if the argument is not an array, then build one consisting of the + // sole argument before passing it to the format() method + try + { + if (args instanceof Object[]) + return mf.format(args); + + return mf.format(new Object[] { args }); + } + catch (IllegalArgumentException x) + { + if (Configuration.DEBUG) + log.fine("Exception while rendering a message format keyed by [" + + key + "]: " + mf.toPattern()); + return constructMessage(mf.toPattern(), args); + } + } + + private static final String constructMessage(String m, Object args) + { + if (args == null) + return '!' + m + '!'; + + return '!' + m + '!' + String.valueOf(args) + '!'; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/jarsigner/SFHelper.java b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/SFHelper.java new file mode 100644 index 000000000..9e4882f0e --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/jarsigner/SFHelper.java @@ -0,0 +1,491 @@ +/* SFHelper -- A .SF file helper + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jarsigner; + +import gnu.classpath.Configuration; +import gnu.java.security.OID; +import gnu.java.security.Registry; +import gnu.java.security.der.DER; +import gnu.java.security.der.DERValue; +import gnu.java.security.pkcs.PKCS7Data; +import gnu.java.security.pkcs.PKCS7SignedData; +import gnu.java.security.pkcs.SignerInfo; +import gnu.java.security.sig.ISignature; +import gnu.java.security.sig.ISignatureCodec; +import gnu.java.security.sig.dss.DSSSignature; +import gnu.java.security.sig.dss.DSSSignatureX509Codec; +import gnu.java.security.sig.rsa.RSAPKCS1V1_5Signature; +import gnu.java.security.sig.rsa.RSAPKCS1V1_5SignatureX509Codec; +import gnu.java.security.util.Util; +import gnu.java.util.jar.JarUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.PrivateKey; +import java.security.cert.CRLException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509CRL; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.RSAPrivateKey; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +import javax.security.auth.x500.X500Principal; +import java.security.cert.X509Certificate; + +/** + * A helper class for the .SF file found in signed jars. + */ +public class SFHelper +{ + private static final Logger log = Logger.getLogger(SFHelper.class.getName()); + private static final int READY = 0; + private static final int STARTED = 1; + private static final int FINISHED = 2; + private static final int SF_GENERATED = 3; + private static final int DSA_GENERATED = 4; + /** http://asn1.elibel.tm.fr/cgi-bin/oid/display?oid=1.3.14.3.2.26&action=display */ + private static final OID hashAlgorithmIdentifierSHA1 = new OID("1.3.14.3.2.26"); //$NON-NLS-1$ + + private int state; + private JarFile jar; + private Manifest manifest; + private Attributes sfMainAttributes; + private Map<String, Attributes> sfEntries; + private byte[] sfBytes; + private HashUtils util; + + /** + * @param jar the JAR archive the .SF file belongs to. + */ + public SFHelper(JarFile jar) + { + super(); + + this.jar = jar; + this.state = READY; + } + + /** + * Writes the contents of the <code>.SF</code> file to the designated JAR + * output stream. Line-endings are platform-independent and consist of the + * 2-codepoint sequence <code>0x0D</code> and <code>0x0A</code>. + * + * @param jar the JAR output stream to write a <code>.SF</code> file to. + * @throws IOException if an I/O related exception occurs during the process. + */ + void writeSF(JarOutputStream jar) throws IOException + { + if (this.state != FINISHED) + throw new IllegalStateException(Messages.getString("SFHelper.1")); //$NON-NLS-1$ + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JarUtils.writeSFManifest(sfMainAttributes, sfEntries, baos); + sfBytes = baos.toByteArray(); + if (Configuration.DEBUG) + log.fine("\n" + Util.dumpString(sfBytes, "+++ sfBytes ")); //$NON-NLS-1$ //$NON-NLS-2$ + jar.write(sfBytes); + jar.flush(); + + this.state = SF_GENERATED; + } + + /** + * The contents of the .DSA file is the DER encoded form of a PKCS#7 + * ContentInfo of the type SignedData. + * <p> + * The ContentInfo ASN.1 syntax is as described in the "PKCS#7 Cryptographic + * Message Syntax Standard" (RSA Labs) specifications: + * <pre> + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL + * } + * + * ContentType ::= OBJECT IDENTIFIER + * </pre> + * <p> + * The ContentType is an OID which determines the type of the contents field + * that follows it. For the .DSA file the OID is "1.2.840.113549.1.7.2", while + * the content field is the byte array representing the DER encoded form of a + * SignedData content-type. The ASN.1 syntax of the SignedData type is as + * follows: + * <pre> + * SignedData ::= SEQUENCE { + * version Version, -- always 1 for PKCS#7 1.5 + * digestAlgorithms DigestAlgorithmIdentifiers, + * contentInfo ContentInfo, + * certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos + * } + * + * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + * + * SignerInfos ::= SET OF SignerInfo + * </pre> + * <p> + * Finally the SignerInfo is a per-signer structure. Its ASN.1 syntax looks + * like so: + * <pre> + * SignerInfo ::= SEQUENCE { + * version Version, -- always 1 for PKCS#7 1.5 + * issuerAndSerialNumber IssuerAndSerialNumber, + * digestAlgorithm DigestAlgorithmIdentifier, + * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + * encryptedDigest EncryptedDigest, + * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL + * } + * + * EncryptedDigest ::= OCTET STRING + * </pre> + * + * @param jar the JAR output stream to write a <code>.DSA</code> file to. + * @param signerKey the private key to sign with. + * @param certificates the possibly null signer certificate chain. + * @param internalSF if <code>true</code> then include the .SF file contents + * in the signed .DSA file; otherwise don't. + * @throws IOException if an I/O related exception occurs during the process. + * @throws CRLException + * @throws CertificateEncodingException + */ + void writeDSA(JarOutputStream jar, PrivateKey signerKey, + Certificate[] certificates, boolean internalSF) + throws IOException, CertificateEncodingException, CRLException + { + if (this.state != SF_GENERATED) + throw new IllegalStateException(Messages.getString("SFHelper.4")); //$NON-NLS-1$ + + if (Configuration.DEBUG) + log.fine("+++ signer private key = " + signerKey); //$NON-NLS-1$ + ISignature signatureAlgorithm; + ISignatureCodec signatureCodec; + OID digestEncryptionAlgorithmOID; + if (signerKey instanceof DSAPrivateKey) + { + signatureAlgorithm = new DSSSignature(); + signatureCodec = new DSSSignatureX509Codec(); + digestEncryptionAlgorithmOID = Main.DSA_SIGNATURE_OID; + } + else if (signerKey instanceof RSAPrivateKey) + { + signatureAlgorithm = new RSAPKCS1V1_5Signature(Registry.MD5_HASH); + signatureCodec = new RSAPKCS1V1_5SignatureX509Codec(); + digestEncryptionAlgorithmOID = Main.RSA_SIGNATURE_OID; + } + else + throw new SecurityException(Messages.getString("SFHelper.6")); //$NON-NLS-1$ + + Map signatureAttributes = new HashMap(); + signatureAttributes.put(ISignature.SIGNER_KEY, signerKey); + signatureAlgorithm.setupSign(signatureAttributes); + signatureAlgorithm.update(sfBytes, 0, sfBytes.length); + Object signature = signatureAlgorithm.sign(); + byte[] signedSFBytes = signatureCodec.encodeSignature(signature); + if (Configuration.DEBUG) + log.fine("\n" + Util.dumpString(signedSFBytes, "+++ signedSFBytes ")); //$NON-NLS-1$ //$NON-NLS-2$ + + Set<DERValue> digestAlgorithms = new HashSet<DERValue>(); + List<DERValue> digestAlgorithm = new ArrayList<DERValue>(2); + DERValue derDigestAlgorithmOID = new DERValue(DER.OBJECT_IDENTIFIER, + hashAlgorithmIdentifierSHA1); + DERValue derDigestAlgorithmParams = new DERValue(DER.NULL, null); + digestAlgorithm.add(derDigestAlgorithmOID); + digestAlgorithm.add(derDigestAlgorithmParams); + DERValue derDigestAlgorithm = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + digestAlgorithm); + digestAlgorithms.add(derDigestAlgorithm); + + // TODO (rsn): test with internalsf == true + PKCS7Data data = internalSF ? new PKCS7Data(sfBytes) : null; + + X509CRL[] crls = null; + + Set<SignerInfo> signerInfos = new HashSet<SignerInfo>(); + X509Certificate cert = (X509Certificate) certificates[0]; + try + { + cert.checkValidity(); + } + catch (CertificateExpiredException x) + { + String issuerName = getIssuerName(cert); + String subjectName = getSubjectName(cert); + Date notAfterDate = getNotAfterDate(cert); + System.out.println(Messages.getFormattedString("SFHelper.0", //$NON-NLS-1$ + new Object[] { issuerName, + subjectName, + notAfterDate })); + } + catch (CertificateNotYetValidException x) + { + String issuerName = getIssuerName(cert); + String subjectName = getSubjectName(cert); + Date notBeforeDate = getNotBeforeDate(cert); + System.out.println(Messages.getFormattedString("SFHelper.11", //$NON-NLS-1$ + new Object[] { issuerName, + subjectName, + notBeforeDate })); + } + X500Principal issuer = cert.getIssuerX500Principal(); + BigInteger serialNumber = cert.getSerialNumber(); + byte[] authenticatedAttributes = null; + byte[] encryptedDigest = signedSFBytes; + byte[] unauthenticatedAttributes = null; + SignerInfo signerInfo = new SignerInfo(issuer, + serialNumber, + hashAlgorithmIdentifierSHA1, + authenticatedAttributes, + digestEncryptionAlgorithmOID, + encryptedDigest, + unauthenticatedAttributes); + signerInfos.add(signerInfo); + + PKCS7SignedData dsaContents = new PKCS7SignedData(digestAlgorithms, + data, + certificates, + crls, + signerInfos); + dsaContents.encode(jar); + + jar.flush(); + this.state = DSA_GENERATED; + } + + Manifest getManifest() + { + return this.manifest; + } + + void startSigning() throws IOException + { + if (this.state != READY) + throw new IllegalStateException(Messages.getString("SFHelper.9")); //$NON-NLS-1$ + + Manifest oldManifest = jar.getManifest(); + this.manifest = oldManifest == null ? new Manifest() + : new Manifest(oldManifest); + this.sfMainAttributes = new Attributes(); + this.sfEntries = new HashMap<String, Attributes>(); + util = new HashUtils(); + + this.state = STARTED; + } + + /** + * Hashes the designated JAR entry (the file itself); adds the resulting hash + * as an attribute to the manifest, and computes the hash of the added (to + * the Manifest) two headers and add the result as an attribute of the + * corresponding entry in the .SF file. + */ + void updateEntry(JarEntry entry) throws IOException + { + if (this.state != STARTED) + throw new IllegalStateException(Messages.getString("SFHelper.10")); //$NON-NLS-1$ + + String name = entry.getName(); + InputStream jeis = jar.getInputStream(entry); + String hash = util.hashStream(jeis); + if (Configuration.DEBUG) + log.fine("Hash of " + name + " = " + hash); //$NON-NLS-1$ //$NON-NLS-2$ + + Attributes mainfestAttributes = manifest.getAttributes(name); + if (mainfestAttributes == null) + { + mainfestAttributes = new Attributes(); + manifest.getEntries().put(name, mainfestAttributes); + } + + mainfestAttributes.putValue(Main.DIGEST, hash); + + // hash the newly added 2-header block and add it as an attribute to .SF + + String sfHash = util.hashManifestEntry(name, hash); + Attributes sfAttributes = sfEntries.get(name); + if (sfAttributes == null) + { + sfAttributes = new Attributes(); + sfEntries.put(name, sfAttributes); + } + + sfAttributes.putValue(Main.DIGEST, sfHash); + if (Configuration.DEBUG) + { + log.fine("Name: " + name); //$NON-NLS-1$ + log.fine(Main.DIGEST + ": " + sfHash); //$NON-NLS-1$ + log.fine(""); //$NON-NLS-1$ + } + } + + /** + * @param sectionsOnly whether to compute, in addition to the files, the hash + * of the mainfest itself (<code>false</code>) or not (<code>true</code>). + */ + void finishSigning(boolean sectionsOnly) throws IOException + { + if (state != STARTED) + throw new IllegalStateException(Messages.getString("SFHelper.10")); //$NON-NLS-1$ + + if (sectionsOnly) + return; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + manifest.write(baos); + baos.flush(); + String manifestHash = util.hashByteArray(baos.toByteArray()); + if (Configuration.DEBUG) + log.fine("Hashed Manifest " + manifestHash); //$NON-NLS-1$ + sfMainAttributes.putValue(Main.DIGEST_MANIFEST, manifestHash); + + this.state = FINISHED; + } + + /** + * Given an X.509 certificate this method returns the string representation of + * the Issuer Distinguished Name. + * + * @param cert an X.509 certificate. + * @return the string representation of the Issuer's DN. + */ + private String getIssuerName(X509Certificate cert) + { + X500Principal xp = cert.getIssuerX500Principal(); + if (xp == null) + { + if (Configuration.DEBUG) + log.fine("Certiticate, with serial number " + cert.getSerialNumber() //$NON-NLS-1$ + + ", has null Issuer. Return [unknown]"); //$NON-NLS-1$ + return Messages.getString("SFHelper.14"); //$NON-NLS-1$ + } + String result = xp.getName(); + if (result == null) + { + if (Configuration.DEBUG) + log.fine("Certiticate, with serial number " + cert.getSerialNumber() //$NON-NLS-1$ + + ", has an Issuer with null DN. Return [unnamed]"); //$NON-NLS-1$ + return Messages.getString("SFHelper.17"); //$NON-NLS-1$ + } + return result; + } + + /** + * Given an X.509 certificate this method returns the string representation of + * the Subject Distinguished Name. + * + * @param cert an X.509 certificate. + * @return the string representation of the Subject's DN. + */ + private String getSubjectName(X509Certificate cert) + { + X500Principal xp = cert.getSubjectX500Principal(); + if (xp == null) + { + if (Configuration.DEBUG) + log.fine("Certiticate, with serial number " + cert.getSerialNumber() //$NON-NLS-1$ + + ", has null Subject. Return [unknown]"); //$NON-NLS-1$ + return Messages.getString("SFHelper.14"); //$NON-NLS-1$ + } + String result = xp.getName(); + if (result == null) + { + if (Configuration.DEBUG) + log.fine("Certiticate, with serial number " + cert.getSerialNumber() //$NON-NLS-1$ + + ", has a Subject with null DN. Return [unnamed]"); //$NON-NLS-1$ + return Messages.getString("SFHelper.17"); //$NON-NLS-1$ + } + return result; + } + + /** + * Given an X.509 certificate this method returns the end validity date of + * this certificate. + * + * @param cert an X.509 certificate. + * @return the date when this certificate stops being valid. + */ + private Date getNotAfterDate(X509Certificate cert) + { + Date result = cert.getNotAfter(); + if (result == null) + { + if (Configuration.DEBUG) + log.fine("Certiticate, with serial number " + cert.getSerialNumber() //$NON-NLS-1$ + + ", has null start-validity date. Return epoch"); //$NON-NLS-1$ + return new Date(0); + } + return result; + } + + /** + * Given an X.509 certificate this method returns the start validity date of + * this certificate. + * + * @param cert an X.509 certificate. + * @return the date when this certificate starts being valid. + */ + private Date getNotBeforeDate(X509Certificate cert) + { + Date result = cert.getNotBefore(); + if (result == null) + { + if (Configuration.DEBUG) + log.fine("Certiticate, with serial number " + cert.getSerialNumber() //$NON-NLS-1$ + + ", has null end-validity date. Return epoch"); //$NON-NLS-1$ + return new Date(0); + } + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/java2xhtml/Java2xhtml.java b/libjava/classpath/tools/gnu/classpath/tools/java2xhtml/Java2xhtml.java new file mode 100644 index 000000000..90c6d4c38 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/java2xhtml/Java2xhtml.java @@ -0,0 +1,1354 @@ +/* gnu.classpath.tools.java2xhtml.Java2xhtml + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +/** Java2xhtml.java Version 0.9 + * Produces an XHTML file from Java source code with syntax highlighting, + * includes additional options (line numbering, tab spacing, etc.) + * <P> + * NOTE: Common java naming structure is assumed + * Capitalize the first letter that appears in a class or interface name + * Use lowercase for the first letter in a method or variable name + * Use only uppercase letters when naming constants + * + * @version 0.9, March 2003 + * @author Shayne Steele + */ +package gnu.classpath.tools.java2xhtml; + +import java.io.*; +import java.util.*; + +public class Java2xhtml +{ + //--- define CSS classes for individual output elements + + private static final String sourceCodeStyle = "source"; + private static final String lineNumberStyle = "line-number even"; + private static final String modulusLineNumberStyle = "line-number odd"; + + private static final String keywordStyle = "keyword"; + private static final String methodStyle = "method member"; + private static final String variableStyle = "variable member"; + private static final String singleLineCommentStyle = "line comment"; + private static final String traditionalCommentStyle = "c comment"; + private static final String javadocCommentStyle = "javadoc comment"; + private static final String javadocTagStyle = "javadoc tag"; + private static final String importNameStyle = "import header type"; + private static final String packageNameStyle = "package header type"; + private static final String primitiveTypeStyle = "primitive type"; + private static final String nonPrimitiveTypeStyle = "non-primitive type"; + private static final String constructorStyle = "constructor member"; + private static final String constantStyle = "constant member"; + private static final String doubleQuoteStyle = "double quote"; + private static final String singleQuoteStyle = "single quote"; + private static final String numericLiteralStyle = "numeric literal"; + private static final String primitiveLiteralStyle = "primitive literal"; + + private static final String iconStyle = "icon"; + + + + // parse the command line arguments + // give a decent responce for bad input + // call the HTMLifier on good input + public static void main(String args[]) + { + // parse the invokation arguments + if (args.length < 1 || args.length > 3) // invoked program incorrectly + { + System.out.println("Java2xhtml Version 0.9 (C) 2005 Free Software Foundation"); + System.out.println(" Produces an XHTML file of Java source" + + " code with syntax highlighting,"); + System.out.println(" includes additional options " + + "(line numbering, tab spacing, etc.)"); + System.out.println(" This tool is part of GNU Classpath."); + System.out.println(" GNU Classpath is free software; you can redistribute it and/or modify"); + System.out.println(" it under the terms of the GNU General Public License as published by"); + System.out.println(" the Free Software Foundation; either version 2, or (at your option)"); + System.out.println(" any later version."); + System.out.println(" NOTE: Common java naming structure is " + + "assumed"); + System.out.println(""); + System.out.println("USAGE:"); + System.out.println("java [java options] Java2xhtml " + + "source.java [options file] " + + "[output file]"); + System.out.println(""); + System.out.println(" - java is the name of the Java interpreter"); + System.out.println(" - [java options] are the optional options " + + "of the Java interpreter"); + System.out.println(" - Java2xhtml is the name of this " + + "application"); + System.out.println(" - source is a file or the directory to the " + + "Java source file(s)"); + System.out.println(" - [options file] is the optional " + + "path of a file with"); + System.out.println(" a structure like this:"); + System.out.println(" externalStyleSheetName=file_name" + + " (default style.css)"); + System.out.println(" tabSize=integer (default value is 4)"); + System.out.println(" extraIndentation=integer " + + "(default value is 0)"); + System.out.println(" lineModulus=integer (default value 5)"); + System.out.println(" isCodeSnippet=boolean" + + " (default false)"); + System.out.println(" isXHTML_1_1=boolean" + + " (default true)"); + System.out.println(" hasInternalStyleSheet=boolean" + + " (default true)"); + System.out.println(" hasExternalStyleSheet=boolean" + + " (default true)"); + System.out.println(" hasTitle=boolean" + + " (default false)"); + System.out.println(" hasLegend=boolean" + + " (default false)"); + System.out.println(" hasAllBoldSourceCode=boolean" + + " (default false)"); + System.out.println(" hasLineNumbers=boolean" + + " (default false)"); + System.out.println(" hasLineModulusDrawnLines=boolean" + + " (default false)"); + System.out.println(" hasLineModulusCodeBlocks=boolean" + + " (default false)"); + System.out.println(" hasFooter=boolean" + + " (default false)"); + System.out.println(" hasFooterIcons=boolean" + + " (default false)"); + System.out.println(" hasFooterDate=boolean" + + " (default true)"); + System.out.println(" NOTE: filename must end with '.prop'"); + System.out.println(" Default [options file] is " + + "options.prop"); + System.out.println(" - [output file] is name of the XHTML file " + + "that is produced"); + System.out.println(" Default [output file] is source_java.html"); + System.out.println(""); + System.out.println("Output: source.java --> [output file]"); + System.out.println(" Default Output is "); + System.out.println(" source.java --> source_java.html"); + System.out.println(""); + System.out.println("Examples of calling the program:"); + System.out.println(" process one file (say Java2xhtml.java):"); + System.out.println(" java Java2xhtml Java2xhtml.java"); + System.out.println(" process one directory (say C:\\HOME):"); + System.out.println(" java Java2xhtml C:\\HOME"); + System.out.println(" process one directory (say C:\\HOME with a " + + "given options file (options.prop)):"); + System.out.println(" java Java2xhtml C:\\HOME options.prop"); + } + else + { + // invoked program correctly, now get command line arguments + // get the source file name + String sourceName; + sourceName = args[0]; + // make sure that the source file exist and if so HTMLify it + File sourceFilePath = new File(sourceName); + if (sourceFilePath.exists()) + { + // good pathname so HTMLify it + // get the default html options file name + String propertiesFileName = "options.prop"; + // create a unique default html file name, + // bubba.java -> bubba_java.html + String htmlFileName = sourceName.replace('.', '_') + ".html"; + if (args.length == 2 || args.length == 3) + { + if (args[1].endsWith(".prop")) + { + // get the user supplied html options file name + propertiesFileName = args[1]; + } + else + { + // get the user supplied html outputfile name + htmlFileName = args[1]; + } + } + if (args.length == 3) + { + if (args[2].endsWith(".prop")) + { + // get the user supplied html options file name + propertiesFileName = args[2]; + } + else + { + // get the user supplied html outputfile name + htmlFileName = args[2]; + } + } + new Java2xhtml(propertiesFileName, sourceFilePath, + htmlFileName); + } + else // source file does not exist, print message and exit normally + { + System.out.println("The source parameter must be an existent" + + " file or directory"); + System.out.println("Run Java2xHtml without parameters for " + + "help"); + } + } + } + + // collect various sets of keywords + static Collection keywordCollection; + static Collection primitiveTypeCollection; + static Collection primitiveLiteralCollection; + static Collection javadocTagCollection; + + // all these variables are changeable by a options file + int extraIndentation = 0; + int tabSize = 4; + int lineModulus = 5; + boolean hasLegend = false; + boolean hasLineNumbers = false; + boolean hasLineModulusDrawnLines = false; + boolean hasLineModulusCodeBlocks = false; + boolean hasFooter = false; + boolean hasFooterIcons = false; + boolean hasFooterDate = true; + boolean isCodeSnippet = false; + boolean isXHTML_1_1 = true; + boolean hasTitle = false; + boolean hasAllBoldSourceCode = false; + boolean hasInternalStyleSheet = true; + boolean hasExternalStyleSheet = true; + String externalStyleSheetName = "style.css"; + + static + { + // collection type is Hashset for unique elements and fast retieval + String keywordArray[] = + { + "abstract", "default", "if", "private", + "do", "implements", "protected", "throws", + "break", "import", "public", "transient", + "else", "instanceof", "return", "try", + "case", "extends", "throw", "static", + "catch", "final", "interface", "while", + "volatile", "finally", "super", "synchronized", + "class", "native", "switch", "package", + "const", "for", "new", "goto", + "continue", "this", "assert", "strictfp" + }; + keywordCollection = new HashSet(Arrays.asList(keywordArray)); + String primitiveTypeArray[] = + { + "boolean", "char", "byte", "short", "int", + "long", "float", "double", "void" + }; + primitiveTypeCollection = + new HashSet(Arrays.asList(primitiveTypeArray)); + String primitiveLiteralArray[]= + { + "false", "null", "true" + }; + primitiveLiteralCollection = + new HashSet(Arrays.asList(primitiveLiteralArray)); + String javadocTagArray[]= + { + "see", "author", "version", "param", "return", "exception", + "deprecated", "throws", "link", "since", "serial", + "serialField","serialData", "beaninfo" + }; + javadocTagCollection = new HashSet(Arrays.asList(javadocTagArray)); + } + + public Java2xhtml() + { + } + + // create the various keyword collections + // parse the html options file + Java2xhtml(String propertiesFileName, File sourceFilePath, + String htmlFileName) + { + // get html properties (use defaults if necessary) + File propertiesFilePath = new File (propertiesFileName); + if (propertiesFilePath.exists()) + { + // html properies file exist try parsing it + try + { + InputStream propertiesFile = + new FileInputStream(propertiesFileName); + Properties htmlProperties = new Properties(); + htmlProperties.load(propertiesFile); + propertiesFile.close(); + setProperties(htmlProperties); + } + catch (IOException exception) + { + System.out.println(exception); + } + } + if (sourceFilePath.isFile()) + { + // process the file + processFile(sourceFilePath, htmlFileName); + } + else if (sourceFilePath.isDirectory()) + { + // process a directory + File [] sourceFilePathArray = sourceFilePath.listFiles(); + for (int i = 0; i < sourceFilePathArray.length; i++) + { + if (((sourceFilePathArray[i]).getName()).endsWith(".java")) + { + // process each file that ends in .java + // create a unique default html file name, + // bubba.java -> bubba_java.html + htmlFileName = ((sourceFilePathArray[i]).getName()).replace( + '.', '_') + ".html"; + processFile(sourceFilePathArray[i], htmlFileName); + } + } + } + } + + public void setProperties(Properties htmlProperties) + { + hasLegend + = Boolean.valueOf(htmlProperties.getProperty("hasLegend", + "false")).booleanValue(); + extraIndentation + = Integer.parseInt(htmlProperties.getProperty("extraIndentation", "0")); + tabSize + = Integer.parseInt(htmlProperties.getProperty("tabSize", "4")); + hasLineNumbers + = Boolean.valueOf(htmlProperties.getProperty("hasLineNumbers", + "false")).booleanValue(); + lineModulus + = Integer.parseInt(htmlProperties.getProperty("lineModulus", "5")); + hasLineModulusDrawnLines + = Boolean.valueOf(htmlProperties.getProperty("hasLineModulusDrawnLines", + "false")).booleanValue(); + hasLineModulusCodeBlocks + = Boolean.valueOf(htmlProperties.getProperty("hasLineModulusCodeBlocks", + "false")).booleanValue(); + hasFooter + = Boolean.valueOf(htmlProperties.getProperty("hasFooter", + "false")).booleanValue(); + hasFooterIcons + = Boolean.valueOf(htmlProperties.getProperty("hasFooterIcons", + "false")).booleanValue(); + hasFooterDate + = Boolean.valueOf(htmlProperties.getProperty("hasFooterDate", + "true")).booleanValue(); + isXHTML_1_1 + = Boolean.valueOf(htmlProperties.getProperty("isXHTML_1_1", + "true")).booleanValue(); + isCodeSnippet + = Boolean.valueOf(htmlProperties.getProperty("isCodeSnippet", + "false")).booleanValue(); + hasTitle + = Boolean.valueOf(htmlProperties.getProperty("hasTitle", + "false")).booleanValue(); + hasAllBoldSourceCode + = Boolean.valueOf(htmlProperties.getProperty("hasAllBoldSourceCode", + "false")).booleanValue(); + hasInternalStyleSheet + = Boolean.valueOf(htmlProperties.getProperty("hasInternalStyleSheet", + "true")).booleanValue(); + hasExternalStyleSheet + = Boolean.valueOf(htmlProperties.getProperty("hasExternalStyleSheet", + "true")).booleanValue(); + externalStyleSheetName + = htmlProperties.getProperty("externalStyleSheetName", "style.css"); + } + + + // read the file and put it into a stringbuffer + void processFile(File sourceFilePath, String htmlFileName) + { + // open the file, copy it to a Stringbuffer , process into an + // HTMLified String and convert result into an HTML file + try + { + BufferedReader sourceReader = + new BufferedReader(new FileReader(sourceFilePath)); + StringBuffer bufferIn = new StringBuffer(); + int readInInt = 0; + char presentChar = 0; + // copy file into a Stringbuffer + while (readInInt != -1) // -1 value means end of stream/file + { + // put the file into a Stringbuffer + readInInt= sourceReader.read(); + presentChar = ((readInInt >= 0) ? (char) readInInt : 0); + bufferIn.append(presentChar); + } + sourceReader.close(); + BufferedWriter tempBufferedWriter = + new BufferedWriter(new FileWriter(htmlFileName)); + tempBufferedWriter.write(makeHTML(bufferIn, + sourceFilePath.getName())); + tempBufferedWriter.close(); + System.out.println(sourceFilePath.getName() + " --> " + + htmlFileName); + } + catch (IOException exception) + { + System.out.println(exception); + } + } + + // constant 'States' java source code can be in + public final static class State + { + public final static State TEXT = new State(); + public final static State IMPORT_NAME = new State(); + public final static State PARAM_VARIABLE = new State(); + public final static State JAVADOC = new State(); + public final static State PACKAGE_NAME = new State(); + public final static State DOUBLE_QUOTE = new State(); + public final static State SINGLE_QUOTE = new State(); + public final static State TRADITIONAL_COMMENT = new State(); + public final static State LINE_COMMENT = new State(); + + // empty constructor + private State() + { + // empty body + } + } + + // Convert java source code StringBufffer into colorized (and tab spaced) + // HTML String . + // Assumes that Java naming convention is used + // Uses a very basic state machine design. + public String makeHTML(StringBuffer bufferIn, String sourceFileName) + { + int codeLineNumber = 0; + boolean isNewLine = true; + boolean isNewBlock = true; + int identifierLength = 0; + int qualifiedIdentifierLength = 0; + int presentIndex = -1; + int spaceLength = 0; + int saveIndex = 0; + char presentChar = 0; + State presentState = State.TEXT; + StringBuffer bufferOut = new StringBuffer(8192); + if (!isCodeSnippet) + { + bufferOut.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"); + if (isXHTML_1_1) + { + bufferOut.append("<!DOCTYPE html PUBLIC " + + "\"-//W3C//DTD XHTML 1.1//EN\"\r\n"); + bufferOut.append(" \"http://www.w3.org/TR/xhtml11/DTD/" + + "xhtml11.dtd\">\r\n"); + bufferOut.append("<html xmlns=\"http://www.w3.org/1999/xhtml\""+ + " xml:lang=\"en\">\r\n"); + } + else + { + bufferOut.append("<!DOCTYPE html PUBLIC " + + "\"-//W3C//DTD XHTML 1.0 Strict//EN\"\r\n"); + bufferOut.append(" \"http://www.w3.org/TR/xhtml1/DTD/" + + "xhtml1-strict.dtd\">\r\n"); + bufferOut.append("<html xmlns=\"http://www.w3.org/1999/xhtml\""+ + " xml:lang=\"en\" lang=\"en\">\r\n"); + } + bufferOut.append(" <head>\r\n"); + bufferOut.append(" <title>\r\n"); + bufferOut.append(" " + sourceFileName + "\r\n"); + bufferOut.append(" </title>\r\n"); + bufferOut.append(" <meta name=\"generator\"\r\n"); + bufferOut.append(" content=\"Java2xhtml 0.9\" />\r\n"); + if (hasInternalStyleSheet) + { + bufferOut.append(" <style type=\"text/css\">\r\n"); + bufferOut.append(" <!-- /* <![CDATA[ */\r\n"); + bufferOut.append(" ." + sourceCodeStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #000000;\r\n"); + bufferOut.append(" background-color: #FFFFFF;\r\n"); + if (hasAllBoldSourceCode) + { + bufferOut.append(" font-weight: bold;\r\n"); + } + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + lineNumberStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" font-weight: normal;\r\n"); + bufferOut.append(" color: #000000;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + if (lineModulus > 0) + { + bufferOut.append(" ." + modulusLineNumberStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" font-weight: bold;\r\n"); + bufferOut.append(" color: #000000;\r\n"); + bufferOut.append(" background-color: "); + bufferOut.append("transparent;\r\n"); + bufferOut.append(" }\r\n"); + if (hasLineModulusDrawnLines) + { + bufferOut.append(" .modulusLineStyle\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" text-decoration: "); + bufferOut.append("line-through;\r\n"); + bufferOut.append(" color: #000000;\r\n"); + bufferOut.append(" background-color: "); + bufferOut.append("transparent;\r\n"); + bufferOut.append(" }\r\n"); + } + if (hasLineModulusCodeBlocks) + { + bufferOut.append(" .modulusBlockPREStyle\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" margin: 0em\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" .modulusBlockStyle\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #000000;\r\n"); + bufferOut.append(" background-color: "); + bufferOut.append("#CCCCCC;\r\n"); + bufferOut.append(" }\r\n"); + } + } + bufferOut.append(" ." + keywordStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #9900FF;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + methodStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #0000FF;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + variableStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #CC9933;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + singleLineCommentStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #CC3333;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + traditionalCommentStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #FF0000;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + javadocCommentStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #CC0033;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + javadocTagStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #0099CC;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + importNameStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #33CCCC;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + packageNameStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #339999;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + primitiveTypeStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #009900;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + nonPrimitiveTypeStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #009966;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + constructorStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #3300CC;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + constantStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #666666;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + doubleQuoteStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #996633;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" font-style: italic;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + singleQuoteStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #663333;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" font-style: oblique;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + numericLiteralStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #333300;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" ." + primitiveLiteralStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" color: #006600;\r\n"); + bufferOut.append(" background-color: transparent;\r\n"); + bufferOut.append(" }\r\n"); + if (hasFooterIcons) + { + bufferOut.append(" ." + iconStyle + "\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" border-style: none;\r\n"); + bufferOut.append(" }\r\n"); + } + if (hasTitle) + { + bufferOut.append(" #title\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" text-align: center;\r\n"); + bufferOut.append(" font-size: xx-large;\r\n"); + bufferOut.append(" }\r\n"); + } + if (hasLegend) + { + bufferOut.append(" #legendTitle\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" text-align: center;\r\n"); + bufferOut.append(" font-size: x-large;\r\n"); + bufferOut.append(" }\r\n"); + bufferOut.append(" #legend\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" font-family: monospace;\r\n"); + bufferOut.append(" font-size: large;\r\n"); + bufferOut.append(" }\r\n"); + } + if (hasFooter) + { + bufferOut.append(" #footer\r\n"); + bufferOut.append(" {\r\n"); + bufferOut.append(" font-size: xx-small;\r\n"); + bufferOut.append(" }\r\n"); + } + bufferOut.append(" /* ]]> */ -->\r\n"); + bufferOut.append(" </style>\r\n"); + } + + if (hasExternalStyleSheet) + { + bufferOut.append(" <link rel=\"stylesheet\" " + + "type=\"text/css\" href=\"" + + externalStyleSheetName + "\" />\r\n"); + } + bufferOut.append(" </head>\r\n"); + bufferOut.append(" <body>\r\n"); + } + if (hasTitle) + { + bufferOut.append(" <div id=\"title\">\r\n"); + bufferOut.append(" " + sourceFileName + "\r\n"); + bufferOut.append(" </div>\r\n"); + bufferOut.append(" <hr />\r\n"); + } + if (hasLegend) + { + bufferOut.append(" <div id=\"legendTitle\">\r\n"); + bufferOut.append(" Legend\r\n"); + bufferOut.append(" </div>\r\n"); + bufferOut.append(" <div class=\"" + sourceCodeStyle + "\">\r\n"); + bufferOut.append(" <div id=\"legend\">\r\n"); + bufferOut.append(" <span class=\"" + keywordStyle + "\">"); + bufferOut.append("keyword</span>\r\n"); + bufferOut.append(" <span class=\"" + methodStyle + "\">"); + bufferOut.append("method</span>\r\n"); + bufferOut.append(" <span class=\"" + variableStyle + "\">variable" + + "</span>\r\n"); + bufferOut.append(" <span class=\"" + singleLineCommentStyle + "\">" + + "singleLineComment</span>\r\n"); + bufferOut.append(" <span class=\"" + traditionalCommentStyle + "\">" + + "traditionalComment</span>\r\n"); + bufferOut.append(" <span class=\"" + javadocCommentStyle + "\">" + + "javadocComment</span>\r\n"); + bufferOut.append(" <span class=\"" + javadocTagStyle + "\">javadocTag" + + "</span>\r\n"); + bufferOut.append(" <span class=\"" + importNameStyle + "\">" + + "importName</span>\r\n"); + bufferOut.append(" <span class=\"" + packageNameStyle + "\">" + + "packageName</span>\r\n"); + bufferOut.append(" <span class=\"" + primitiveTypeStyle + "\">" + + "primitiveType</span>\r\n"); + bufferOut.append(" <span class=\"" + nonPrimitiveTypeStyle + "\">" + + "nonPrimitiveType</span>\r\n"); + bufferOut.append(" <span class=\"" + constructorStyle + "\">" + + "constructor</span>\r\n"); + bufferOut.append(" <span class=\"" + constantStyle + "\">" + + "constant</span>\r\n"); + bufferOut.append(" <span class=\"" + doubleQuoteStyle + "\">" + + "doubleQuote</span>\r\n"); + bufferOut.append(" <span class=\"" + singleQuoteStyle + "\">" + + "singleQuote</span>\r\n"); + bufferOut.append(" <span class=\"" + numericLiteralStyle + "\">" + + "numericLiteral</span>\r\n"); + bufferOut.append(" <span class=\"" + primitiveLiteralStyle + "\">" + + "primitiveLiteral</span>\r\n"); + bufferOut.append(" </div>\r\n"); + bufferOut.append(" </div>\r\n"); + bufferOut.append(" <hr />\r\n"); + } + bufferOut.append(" <div class=\"" + sourceCodeStyle + "\">\r\n"); + if (hasLineModulusCodeBlocks) + { + bufferOut.append("<pre class=\"modulusBlockPREStyle\">\r\n"); + } + else + { + bufferOut.append("<pre>\r\n"); + } + // process the input Java code Stringbuffer + // subtract 2 from the bufferIn.length() to get EOF marker + while (presentIndex++ < (bufferIn.length() - 2)) + { + for (int i = 0; i < extraIndentation; i++) + { + bufferOut.append(" "); + } + if ((hasLineNumbers || hasLineModulusCodeBlocks) && isNewLine) + { + // add line numbers if desired + // line numbers are 1 - 9999 then rotate line numbers + codeLineNumber = (++codeLineNumber)%10000; + if ((lineModulus > 0) && hasLineModulusCodeBlocks && + (codeLineNumber%lineModulus == 1)) + { + if (isNewBlock) + { + if ((State.TRADITIONAL_COMMENT == presentState) || + (State.JAVADOC == presentState)) + { + bufferOut.insert((bufferOut.length() - + ("\r\n").length()), + "</span>"); + } + bufferOut.append("</pre>\r\n"); + bufferOut.append(" <div class="); + bufferOut.append("\"modulusBlockStyle\">"); + bufferOut.append("\r\n<pre class=\""); + bufferOut.append("modulusBlockPREStyle\">\r\n"); + if (State.TRADITIONAL_COMMENT == presentState) + { + bufferOut.append("<span class=" + + "\"" + traditionalCommentStyle + "\">"); + } + if (State.JAVADOC == presentState) + { + bufferOut.append("<span class=" + + "\"" + javadocCommentStyle + "\">"); + } + } + isNewBlock = !isNewBlock; + } + // make straight columns of line numbers + if (codeLineNumber < 1000) + { + bufferOut.append(" "); + } + if (codeLineNumber < 100) + { + bufferOut.append(" "); + } + if (codeLineNumber < 10) + { + bufferOut.append(" "); + } + bufferOut.append("<a name=\"line."); + bufferOut.append(codeLineNumber); + bufferOut.append("\">"); + + if (hasLineNumbers) + { + if ((lineModulus > 0) && (codeLineNumber%lineModulus == 0)) + { + bufferOut.append("<span class=" + + "\"" + modulusLineNumberStyle + "\">"); + bufferOut.append(codeLineNumber); + bufferOut.append(": </span>"); + if (hasLineModulusDrawnLines) + { + // compute spaceLength so a line can be drawn + while ((presentIndex != (bufferIn.length() - 1)) && + ((Character.isSpaceChar( + bufferIn.charAt(presentIndex))) || + (bufferIn.charAt(presentIndex) == '\t'))) + { + // for each tab, insert tabSize spaces + if (bufferIn.charAt(presentIndex) == '\t') + { + for (int i = 0; i < tabSize; i++) + { + bufferIn.insert(presentIndex + 1, " "); + } + presentIndex++; + continue; + } + if (' ' == bufferIn.charAt(presentIndex)) + { + // read a space so place a space + bufferOut.append(" "); + spaceLength += (" ").length(); + } + else + { + // a white space character was read + bufferOut.append(bufferIn.charAt( + presentIndex)); + ++spaceLength; + } + presentIndex++; + } + // check if line is empty + // (no printable characters on line) + if ((presentIndex == (bufferIn.length() - 1)) || + (Character.isWhitespace(bufferIn.charAt( + presentIndex)))) + { + spaceLength = 0; + } + // draw the line + if (spaceLength > 1) + { + bufferOut.insert((bufferOut.length() - + spaceLength), "<span class=" + + "\"modulusLineStyle\">"); + bufferOut.insert((bufferOut.length() - + (" ").length()), "</span>"); + } + spaceLength = 0; + } + } + else + { + // line numbers are in lineNumberColor + bufferOut.append("<span class=\"" + lineNumberStyle + "\">"); + bufferOut.append(codeLineNumber); + bufferOut.append(":</span> "); + } + } + isNewLine = false; + + bufferOut.append("</a>"); + } + // a state machine + presentChar = bufferIn.charAt(presentIndex); + if ((Character.isJavaIdentifierPart(presentChar)) || + ((State.IMPORT_NAME == presentState) && (presentChar == '*'))) + { + // this is an identifier + bufferOut.append(presentChar); + identifierLength++; + continue; // keep adding characters until identifier is done + } + if (identifierLength > 0) + { + // identifier + qualifiedIdentifierLength = + qualifiedIdentifierLength + identifierLength; + if (bufferIn.charAt(presentIndex) == '.') + { + // qualified identifier + bufferOut.append(presentChar); + qualifiedIdentifierLength++; + identifierLength = 0; + continue; // keep adding characters to qualified identifier + } + String identifier = + bufferOut.substring(bufferOut.length() - + identifierLength); + if ((State.PARAM_VARIABLE == presentState)) + { + // any identifier after a param in a javadoc is assumed to + // be a variable + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + variableStyle + "\">"); + bufferOut.append("</span>"); + presentState = State.JAVADOC; + } + else if (State.JAVADOC == presentState) + { + // in javadoc state + if ((javadocTagCollection.contains(identifier)) && + (bufferIn.charAt(presentIndex - + (identifierLength + 1)) == '@')) + { + // identifier is a javadocTag + bufferOut.insert(bufferOut.length() - identifierLength, + "<span class=\"" + javadocTagStyle + "\">"); + bufferOut.append("</span>"); + if (("param").equals(identifier)) + { + // any identifier after a param is assumed to + // be a variable, get into a state to do this + presentState = State.PARAM_VARIABLE; + } + } + } + else if (State.IMPORT_NAME == presentState) + { + // import identifier + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + importNameStyle + "\">"); + bufferOut.append("</span>"); + presentState = State.TEXT; + } + else if (State.PACKAGE_NAME == presentState) + { + // package identifier + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + packageNameStyle + "\">"); + bufferOut.append("</span>"); + presentState = State.TEXT; + } + else if (State.TEXT == presentState) + { + if (keywordCollection.contains(identifier)) + { + // identifier is a keyword + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + keywordStyle + "\">"); + bufferOut.append("</span>"); + if (("import").equals(identifier)) + { + // anything after an import in text mode must be + // an import name, so enter state to process this + presentState = State.IMPORT_NAME; + } + else if (("package").equals(identifier)) + { + // anything after an package in text mode must be + // an package name, so enter state to process this + presentState = State.PACKAGE_NAME; + } + } + else if (primitiveTypeCollection.contains(identifier)) + { + // identifier is a primitive type + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + primitiveTypeStyle + "\">"); + bufferOut.append("</span>"); + } + else if ((identifier.equals(identifier.toUpperCase())) && + (!(Character.isDigit(identifier.charAt(0))))) + { + // identifier is a constant + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + constantStyle + "\">"); + bufferOut.append("</span>"); + } + else if (Character.isUpperCase(identifier.charAt(0))) + { + // identifier is a constructor or non-primitive type + // eat white space + saveIndex = presentIndex; + while (Character.isWhitespace( + bufferIn.charAt(saveIndex++))) + { + //empty body + } + if (bufferIn.charAt(--saveIndex) == '(') + { // identifier is a constructor + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=" + + "\"" + constructorStyle + "\">"); + bufferOut.append("</span>"); + } + else + { + // identifier is a non-primitive type + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=" + + "\"" + nonPrimitiveTypeStyle + "\">"); + bufferOut.append("</span>"); + } + } + else if (!(Character.isDigit(identifier.charAt(0)) || + primitiveLiteralCollection.contains(identifier))) + { + // identifier is a method or a variable + // eat white space + saveIndex = presentIndex; + while (Character.isWhitespace( + bufferIn.charAt(saveIndex++))) + { + // empty body + } + --saveIndex; + // identifier is a method + if (bufferIn.charAt(saveIndex) == '(') + { + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + methodStyle + "\">"); + bufferOut.append("</span>"); + } + else if (bufferIn.charAt(saveIndex) == ',') + { + // comma seperated variables + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + variableStyle + "\">"); + bufferOut.append("</span>"); + } + else + { + // a variable + // take care of cases such as array[index].variable + if (bufferIn.charAt(presentIndex - + (qualifiedIdentifierLength + + 1)) == '.') + { + qualifiedIdentifierLength++; + } + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=\"" + variableStyle + "\">"); + bufferOut.append("</span>"); + } + } + else + { + if (primitiveLiteralCollection.contains(identifier)) + { + // primitiveLiteral (boolean or null) + bufferOut.insert(bufferOut.length() - + identifierLength, "<span class=" + + "\"" + primitiveLiteralStyle + "\">"); + bufferOut.append("</span>"); + } + // a numeric literal + else + { + if (((presentIndex - + (qualifiedIdentifierLength + 1)) > 0) && + (bufferIn.charAt(presentIndex - + (qualifiedIdentifierLength + 1)) == '.')) + { + qualifiedIdentifierLength++; + } + bufferOut.insert(bufferOut.length() - + qualifiedIdentifierLength, + "<span class=" + + "\"" + numericLiteralStyle + "\">"); + bufferOut.append("</span>"); + } + } + } + qualifiedIdentifierLength = 0; + identifierLength = 0; + } + // process characters NOT in identifiers + switch (presentChar) + { + case '&': //ampersand + bufferOut.append("&"); // HTMLify character + break; + case '<': // less than sign + bufferOut.append("<"); // HTMLify character + break; + case '>': // greater than sign + bufferOut.append(">"); // HTMLify character + break; + case '\"': // double quote + bufferOut.append("""); // HTMLify character + if (State.TEXT == presentState) + { + presentState = State.DOUBLE_QUOTE; + bufferOut.insert(bufferOut.length()-(""").length(), + "<span class=\"" + doubleQuoteStyle + "\">"); + } + else if (State.DOUBLE_QUOTE == presentState) + { + presentState = State.TEXT; + bufferOut.append("</span>"); + } + break; + case '\'': // single quote + bufferOut.append("\'"); + if (State.TEXT == presentState) + { + presentState = State.SINGLE_QUOTE; + bufferOut.insert(bufferOut.length() - ("\'").length(), + "<span class=\"" + singleQuoteStyle + "\">"); + } + else if (State.SINGLE_QUOTE == presentState) + { + presentState = State.TEXT; + bufferOut.append("</span>"); + } + break; + case '\\': // backslash + bufferOut.append("\\"); + if ((State.DOUBLE_QUOTE == presentState) || + (State.SINGLE_QUOTE == presentState)) + { + // treat as a character escape sequence + bufferOut.append(bufferIn.charAt(++presentIndex)); + } + break; + case '\t': // tab + // replace tabs with tabsize number of spaces + for (int i = 0; i < tabSize; i++) + { + bufferOut.append(' '); + } + break; + case '*': // star + bufferOut.append("*"); + if ((State.TEXT == presentState) && + (bufferIn.charAt(presentIndex - 1) == '/')) + { + if (((bufferIn.length() - 1) > presentIndex) && + (bufferIn.charAt(presentIndex + 1) == '*')) + { + presentState = State.JAVADOC; + bufferOut.insert(bufferOut.length() - + ("/*").length(), "<span class=" + + "\"" + javadocCommentStyle + "\">"); + } + else + { + presentState = State.TRADITIONAL_COMMENT; + bufferOut.insert(bufferOut.length() - + ("/*").length(), "<span class=" + + "\"" + traditionalCommentStyle + "\">"); + } + } + break; + case '/': // foward slash + bufferOut.append("/"); + if (((State.TRADITIONAL_COMMENT == presentState) || + (State.JAVADOC == presentState)) && + (bufferIn.charAt(presentIndex - 1) == '*')) + { + bufferOut.append("</span>"); + presentState = State.TEXT; + } + if ((State.TEXT == presentState) && + (presentIndex > 0) && + (bufferIn.charAt(presentIndex - 1) == '/')) + { + bufferOut.insert(bufferOut.length() - ("//").length(), + "<span class=" + + "\"" + singleLineCommentStyle + "\">"); + presentState = State.LINE_COMMENT; + } + break; + case '\r': // carriage return + // fall through + case '\n': // line feed + // all HTML lines end in \r\n + if ((bufferIn.charAt(presentIndex) == '\r') && + ((bufferIn.length() - 1) > presentIndex) && + (bufferIn.charAt(presentIndex + 1) == '\n')) + { + ++presentIndex; + } + // end single line comments + if (State.LINE_COMMENT == presentState) + { + bufferOut.append("</span>"); + presentState = State.TEXT; + } + // end of block + if ((lineModulus > 0) && hasLineModulusCodeBlocks && + ((codeLineNumber%lineModulus == 0) && !isNewBlock)) + { + // end multi-line spanning states + if ((State.TRADITIONAL_COMMENT == presentState) || + (State.JAVADOC == presentState)) + { + bufferOut.append("</span>"); + } + bufferOut.append("\r\n"); + bufferOut.append("</pre>\r\n"); + bufferOut.append(" </div>\r\n"); + bufferOut.append("<pre class=\""); + bufferOut.append("modulusBlockPREStyle\">\r\n"); + // restart multi-line spanning states + if (State.TRADITIONAL_COMMENT == presentState) + { + bufferOut.append("<span class=" + + "\"" + traditionalCommentStyle + "\">"); + } + if (State.JAVADOC == presentState) + { + bufferOut.append("<span class=" + + "\"" + javadocCommentStyle + "\">"); + } + } + else + { + // div automatically starts new line + bufferOut.append("\r\n"); + } + isNewLine = true; + break; + case 0: // nul character + if ((State.LINE_COMMENT == presentState) && + (presentIndex == (bufferIn.length() - 1))) + { + bufferOut.append("</span>"); + } + break; + default: // everything else + bufferOut.append(presentChar); + } + qualifiedIdentifierLength = 0; + } + if (presentState == State.LINE_COMMENT) { + bufferOut.append("</span>\r\n"); + } + + bufferOut.append("</pre>\r\n"); + // end block early if no more source code + if ((lineModulus > 0) && hasLineModulusCodeBlocks && !isNewBlock && + (codeLineNumber%lineModulus != 0)) + { + bufferOut.append(" </div>\r\n"); + } + bufferOut.append(" </div>\r\n"); // end div of sourceCodeStyle + // if code snippet then don't add ending tags of xhtml page + if (!isCodeSnippet) + { + // if footer mode then add a footer + if (hasFooter) + { + bufferOut.append(" <hr />\r\n"); + bufferOut.append(" <div id=\"footer\">\r\n"); + if (hasFooterIcons) + { + if (hasFooterDate) + { + bufferOut.append(" <script type=\"text/javaScript\""); + bufferOut.append(">\r\n"); + bufferOut.append(" <!-- // <![CDATA[\r\n"); + bufferOut.append(" document.write(\"Document last"); + bufferOut.append(" modified on \""); + bufferOut.append(" + document.lastModified + "); + bufferOut.append("\"<br />\");\r\n"); + bufferOut.append(" // ]]> -->\r\n"); + bufferOut.append(" </script>\r\n"); + } + bufferOut.append(" <a href=\""); + bufferOut.append("http://validator.w3.org/check/referer"); + bufferOut.append("\">\r\n"); + bufferOut.append(" <img class=\"" + iconStyle + "\" src=\""); + bufferOut.append("http://www.w3.org/Icons/"); + if (isXHTML_1_1) + { + bufferOut.append("valid-xhtml11\"\r\n"); + bufferOut.append(" alt=\"Valid XHTML 1.1!\""); + } + else + { + bufferOut.append("valid-xhtml10\"\r\n"); + bufferOut.append(" alt=\"Valid XHTML 1.0!\""); + } + bufferOut.append(" height=\"31\" "); + bufferOut.append("width=\"88\" />\r\n"); + bufferOut.append(" </a>\r\n"); + bufferOut.append("  \r\n"); + bufferOut.append(" <a href=\""); + bufferOut.append("http://jigsaw.w3.org"); + bufferOut.append("/css-validator/check/referer"); + bufferOut.append("\">\r\n"); + bufferOut.append(" <img class=\"" + iconStyle + "\" src=\""); + bufferOut.append("http://jigsaw.w3.org/"); + bufferOut.append("css-validator/images/vcss"); + bufferOut.append("\"\r\n"); + bufferOut.append(" alt=\"Valid CSS!\""); + bufferOut.append(" height=\"31\" width=\"88\" />\r\n"); + bufferOut.append(" </a>\r\n"); + } + else + { + bufferOut.append(" This is a valid\r\n"); + bufferOut.append(" <a href=\"http://"); + bufferOut.append("validator.w3.org/check/referer"); + if (isXHTML_1_1) + { + bufferOut.append("\">XHTML 1.1</a>\r\n"); + } + else + { + bufferOut.append("\">XHTML 1.0</a>\r\n"); + } + bufferOut.append(" with\r\n"); + bufferOut.append(" <a href=\"http://"); + bufferOut.append("jigsaw.w3.org"); + bufferOut.append("/css-validator/check/referer"); + bufferOut.append("\">CSS</a>\r\n"); + bufferOut.append(" document \r\n"); + if (hasFooterDate) + { + bufferOut.append(" <script type=\"text/javaScript\""); + bufferOut.append(">\r\n"); + bufferOut.append(" <!-- // <![CDATA[\r\n"); + bufferOut.append(" document.write(\"last modified"); + bufferOut.append(" on \" + document.lastModified);"); + bufferOut.append("\r\n"); + bufferOut.append(" // ]]> -->\r\n"); + bufferOut.append(" </script>\r\n"); + } + } + bufferOut.append(" </div>\r\n"); + } + bufferOut.append(" </body>\r\n"); + bufferOut.append("</html>\r\n"); + } + return bufferOut.toString(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/ClassWrapper.java b/libjava/classpath/tools/gnu/classpath/tools/javah/ClassWrapper.java new file mode 100644 index 000000000..ffecac35c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/ClassWrapper.java @@ -0,0 +1,376 @@ +/* ClassWrapper.java - wrap ASM class objects + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +public class ClassWrapper + extends ClassNode +{ + Main classpath; + + ClassWrapper superClass; + + ArrayList<ClassWrapper> interfaceClasses; + + // The virtual table for this class. + ArrayList<MethodNode> vtable; + + // A set of all the bridge method targets we've found. + HashSet<String> bridgeTargets; + + // A set of all the method names in this class. + HashSet<String> methodNames = new HashSet<String>(); + + // This maps a method name + descriptor, e.g. "method()V", to the + // name chosen for the method. This is used when computing the + // names of bridge method targets. + HashMap<String,String> methodNameMap = new HashMap<String,String>(); + + public ClassWrapper(Main classpath) + { + this.classpath = classpath; + } + + public boolean hasNativeMethod() + { + Iterator<?> i = methods.iterator(); + while (i.hasNext()) + { + MethodNode method = (MethodNode) i.next(); + if (Modifier.isNative(method.access)) + return true; + } + return false; + } + + public boolean isThrowable() throws IOException + { + linkSupers(); + ClassWrapper self = this; + while (self != null) + { + if (self.name.equals("java/lang/Throwable")) + return true; + self = self.superClass; + } + return false; + } + + void linkSupers() throws IOException + { + if (superName == null) + { + // Object, do nothing. + return; + } + if (superClass == null) + { + superClass = classpath.getClass(superName); + assert interfaceClasses == null; + interfaceClasses = new ArrayList<ClassWrapper>(); + for (int i = 0; i < interfaces.size(); ++i) + { + String ifname = (String) interfaces.get(i); + ClassWrapper iface = classpath.getClass(ifname); + iface.linkSupers(); + interfaceClasses.add(iface); + } + } + superClass.linkSupers(); + } + + private int findSlot(MethodNode method) + { + for (int i = vtable.size() - 1; i >= 0; --i) + { + MethodNode base = vtable.get(i); + if (MethodHelper.overrides(method, base)) + return i; + } + return - 1; + } + + private void addInterfaceMethods(ClassWrapper iface) + { + Iterator<?> i = iface.methods.iterator(); + while (i.hasNext()) + { + MethodNode im = (MethodNode) i.next(); + int slot = findSlot(im); + if (slot == - 1) + { + vtable.add(im); + // Also add it to our local methods. + methods.add(im); + } + } + addInterfaces(iface); + } + + private void addInterfaces(ClassWrapper base) + { + if (base.interfaceClasses == null) + return; + Iterator<?> i = base.interfaceClasses.iterator(); + while (i.hasNext()) + { + ClassWrapper iface = (ClassWrapper) i.next(); + addInterfaceMethods(iface); + } + } + + private void addLocalMethods() + { + Iterator<?> i = methods.iterator(); + while (i.hasNext()) + { + MethodNode meth = (MethodNode) i.next(); + methodNames.add(meth.name); + if (Modifier.isStatic(meth.access)) + continue; + int slot = findSlot(meth); + if (slot == - 1) + vtable.add(meth); + else + vtable.set(slot, meth); + } + } + + private void makeVtable() throws IOException + { + if (vtable != null) + return; + if (superClass != null) + { + superClass.makeVtable(); + vtable = new ArrayList<MethodNode>(superClass.vtable); + bridgeTargets = new HashSet<String>(superClass.bridgeTargets); + methodNameMap = new HashMap<String,String>(superClass.methodNameMap); + } + else + { + // Object. + vtable = new ArrayList<MethodNode>(); + bridgeTargets = new HashSet<String>(); + methodNameMap = new HashMap<String,String>(); + } + addLocalMethods(); + addInterfaces(this); + + // Make a set of all the targets of bridge methods. We rename + // bridge target methods to avoid problems with C++. You might + // think we could rename the bridge methods themselves, but bridge + // methods by definition override a method from the superclass -- + // and we have to consider the superclass' header as an + // unchangeable entity. + Iterator<?> i = methods.iterator(); + while (i.hasNext()) + { + MethodNode m = (MethodNode) i.next(); + String desc = MethodHelper.getBridgeTarget(m); + if (desc != null) + { + String sum = m.name + desc; + boolean newTarget = bridgeTargets.add(sum); + if (newTarget) + { + // Bridge target that is new in this class. + String cname = this.name; + int index = cname.lastIndexOf('/'); + cname = cname.substring(index + 1); + methodNameMap.put(sum, cname + "$" + m.name); + } + } + } + } + + private void printFields(CniPrintStream out) + { + Iterator<?> i = fields.iterator(); + ClassWrapper self = superClass; + while (i.hasNext()) + { + FieldNode f = (FieldNode) i.next(); + boolean hasMethodName = methodNames.contains(f.name); + if (FieldHelper.print(out, f, self, hasMethodName)) + self = null; + } + } + + private void printMethods(CniPrintStream out) throws IOException + { + makeVtable(); + + // A given method is either static, overrides a super method, or + // is already in vtable order. + Iterator<?> i = methods.iterator(); + while (i.hasNext()) + { + MethodNode m = (MethodNode) i.next(); + String nameToUse; + String sum = m.name + m.desc; + if (bridgeTargets.contains(sum)) + nameToUse = (String) methodNameMap.get(sum); + else + nameToUse = m.name; + methodNameMap.put(sum, nameToUse); + MethodHelper.print(out, m, this, nameToUse); + } + } + + private void printTextList(PrintStream out, int what, ArrayList<Text> textList) + { + if (textList == null) + return; + Iterator<Text> i = textList.iterator(); + boolean first = true; + while (i.hasNext()) + { + Text item = i.next(); + if (item.type == what) + { + if (first) + { + out.println(); + first = false; + } + if (what == Text.FRIEND) + out.print(" friend "); + out.println(item.text); + } + } + } + + public void print(CniPrintStream out) + { + out.print("::"); + out.printName(name); + } + + // This prints the body of a class to a CxxPrintStream. + private void printContents(CniPrintStream out, ArrayList<Text> textList) + throws IOException + { + printTextList(out, Text.PREPEND, textList); + out.println(); + + out.print("class "); + // Don't use our print() -- we don't want the leading "::". + out.printName(name); + if (superClass != null) + { + out.print(" : public "); + superClass.print(out); + } + out.println(); + out.println("{"); + + printTextList(out, Text.ADD, textList); + out.println(); + + // Note: methods must come first, as we build the list + // of method names while printing them. + printMethods(out); + printFields(out); + + out.setModifiers(Modifier.PUBLIC); + out.println(" static ::java::lang::Class class$;"); + + printTextList(out, Text.FRIEND, textList); + + out.print("}"); + if (Modifier.isInterface(access)) + out.print(" __attribute__ ((java_interface))"); + out.println(";"); + + printTextList(out, Text.APPEND, textList); + } + + public void printFully(PrintStream out) throws IOException + { + linkSupers(); + + ArrayList<Text> textList = classpath.getClassTextList(name); + + out.println("// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-"); + out.println(); + String xname = "__" + name.replaceAll("/", "_") + "__"; + out.println("#ifndef " + xname); + out.println("#define " + xname); + out.println(); + out.println("#pragma interface"); + out.println(); + + if (superClass != null) + { + out.print("#include <"); + out.print(superName); + out.println(".h>"); + } + + // Write the body of the stream here. This lets + // us emit the namespaces without a second pass. + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + CniPrintStream cxxOut = new CniPrintStream(bytes); + cxxOut.addClass(this); + printContents(cxxOut, textList); + cxxOut.printNamespaces(out); + bytes.writeTo(out); + + out.println(); + out.println("#endif // " + xname); + } + + public String toString() + { + return name; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/CniIncludePrinter.java b/libjava/classpath/tools/gnu/classpath/tools/javah/CniIncludePrinter.java new file mode 100644 index 000000000..8861541a5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/CniIncludePrinter.java @@ -0,0 +1,80 @@ +/* CniIncludePrinter.java - generate CNI header files + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +public class CniIncludePrinter + extends Printer +{ + protected CniIncludePrinter(Main classpath, File outFile, boolean isDir, + boolean force) + { + super(classpath, outFile, isDir, force); + } + + protected void writePreambleImpl(PrintStream ps) + { + // does nothing + } + + protected PrintStream getPrintStreamImpl(FileOutputStream fos, + ClassWrapper klass) + { + return new PrintStream(fos); + } + + public void printClass(File file, ClassWrapper klass) throws IOException + { + // Never write Object or Class. This is a hack, maybe + // the user would like to see what they look like... + if (klass.name.equals("java/lang/Object") + || klass.name.equals("java/lang/Class")) + return; + PrintStream ps = getPrintStream(file + ".h", klass); + if (ps == null) + return; + ps.println(); + klass.printFully(ps); + ps.close(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/CniPrintStream.java b/libjava/classpath/tools/gnu/classpath/tools/javah/CniPrintStream.java new file mode 100644 index 000000000..df2b723e7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/CniPrintStream.java @@ -0,0 +1,274 @@ +/* CniPrintStream.java - PrintStream that emits CNI declarations + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashSet; + +import org.objectweb.asm.Type; + +public class CniPrintStream + extends PrintStream +{ + int currentModifiers = Modifier.PRIVATE; + + // True if we saw an array type. + boolean sawArray; + + // All the classes referenced by this header. + HashSet<String> allClasses = new HashSet<String>(); + + String[] previousPackage = new String[0]; + + public CniPrintStream(OutputStream out) + { + super(out); + } + + public void addClass(ClassWrapper cw) + { + allClasses.add(cw.name); + } + + public void setModifiers(int newMods) + { + newMods &= (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE); + if (newMods != currentModifiers) + { + switch (newMods) + { + case Modifier.PUBLIC: + println("public:"); + break; + case Modifier.PROTECTED: + println("public: // actually protected"); + break; + case Modifier.PRIVATE: + println("private:"); + break; + default: + println("public: // actually package-private"); + break; + } + currentModifiers = newMods; + } + } + + private String getName(Type type) + { + if (type == Type.BOOLEAN_TYPE) + return "jboolean"; + else if (type == Type.BYTE_TYPE) + return "jbyte"; + else if (type == Type.CHAR_TYPE) + return "jchar"; + else if (type == Type.SHORT_TYPE) + return "jshort"; + else if (type == Type.INT_TYPE) + return "jint"; + else if (type == Type.LONG_TYPE) + return "jlong"; + else if (type == Type.FLOAT_TYPE) + return "jfloat"; + else if (type == Type.DOUBLE_TYPE) + return "jdouble"; + else + { + assert type == Type.VOID_TYPE; + return "void"; + } + } + + public String getClassName(Type type) + { + String name = type.toString(); + name = name.substring(1, name.length() - 1); + // Add the plain class name; we'll handle it when + // we process namespaces. + allClasses.add(name); + return name; + } + + // Print the C++ form of TYPE, mangling C++ keywords. + public void print(Type type) + { + int arrayCount = 0; + if (type.getSort() == Type.ARRAY) + { + arrayCount = type.getDimensions(); + for (int i = 0; i < arrayCount; ++i) + print("JArray< "); + type = type.getElementType(); + sawArray = true; + } + if (type.getSort() == Type.OBJECT) + { + print("::"); + printName(getClassName(type)); + print(" *"); + } + else + { + print(getName(type)); + } + if (arrayCount > 0) + { + while (arrayCount-- > 0) + { + print(" > *"); + } + } + } + + // Print NAME, converting into C++ syntax and mangling C++ keywords + // as we go. + public final static void printName(PrintStream out, String name) + { + String[] parts = name.split("::|/"); + for (int i = 0; i < parts.length; i++) + { + if (i != 0) + out.print("::"); + out.print(Keywords.getCxxName(parts[i])); + } + } + + // Println NAME, converting into C++ syntax and mangling C++ + // keywords as we go. + public final static void printlnName(PrintStream out, String name) + { + printName(out, name); + out.println(); + } + + // Print NAME, converting into C++ syntax and mangling C++ keywords + // as we go. + final void printName(String name) + { + printName(this, name); + } + + private void indent(PrintStream out, int n) + { + for (int i = 0; i < n; ++i) + { + out.print(" "); + } + } + + private void moveToPackage(PrintStream out, String[] pkgParts) + { + // Find greatest common part. + int commonIndex; + for (commonIndex = 0; commonIndex < previousPackage.length; ++commonIndex) + { + if (commonIndex >= pkgParts.length) + break; + if (! previousPackage[commonIndex].equals(pkgParts[commonIndex])) + break; + } + // Close old parts after the common part. + for (int j = previousPackage.length - 1; j >= commonIndex; --j) + { + indent(out, j + 1); + out.println("}"); + } + // Open new parts. + for (int j = commonIndex; j < pkgParts.length; ++j) + { + indent(out, j + 1); + out.print("namespace "); + printlnName(out, pkgParts[j]); + indent(out, j + 1); + out.println("{"); + } + previousPackage = pkgParts; + } + + private void writeClass(PrintStream out, String klass) + { + int index = klass.lastIndexOf('/'); + String pkg = index == -1 ? "" : klass.substring(0, index); + String[] pkgParts = index == -1 ? new String[0] : pkg.split("/"); + String className = index == -1 ? klass : klass.substring(index + 1); + moveToPackage(out, pkgParts); + indent(out, pkgParts.length + 2); + out.print("class "); + printName(out, className); + out.println(";"); + } + + public void printNamespaces(PrintStream out) + { + if (sawArray) + { + out.println("#include <gcj/array.h>"); + out.println(); + } + + String[] classes = allClasses.toArray(new String[0]); + Arrays.sort(classes); + + boolean first = true; + boolean seen = false; + for (int i = 0; i < classes.length; ++i) + { + String klass = classes[i]; + if (klass.startsWith("java/lang/") || klass.startsWith("java/io/") + || klass.startsWith("java/util/")) + continue; + if (first) + { + out.println("extern \"Java\""); + out.println("{"); + first = false; + seen = true; + } + writeClass(out, klass); + } + if (seen) + { + moveToPackage(out, new String[0]); + out.println("}"); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/CniStubPrinter.java b/libjava/classpath/tools/gnu/classpath/tools/javah/CniStubPrinter.java new file mode 100644 index 000000000..9b5dc2c36 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/CniStubPrinter.java @@ -0,0 +1,129 @@ +/* CniStubPrinter.java - Generate a CNI stub file + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.Iterator; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.MethodNode; + +public class CniStubPrinter + extends Printer +{ + protected CniStubPrinter(Main classpath, File outFile, boolean isDir, + boolean force) + { + super(classpath, outFile, isDir, force); + } + + private void printDecl(CniPrintStream out, String className, MethodNode method) + { + out.printName(className); + out.print("::"); + out.printName(method.name); + out.print("("); + Type[] argTypes = Type.getArgumentTypes(method.desc); + for (int j = 0; j < argTypes.length; ++j) + { + if (j > 0) + out.print(", "); + out.print(argTypes[j]); + } + out.print(")"); + } + + protected void writePreambleImpl(PrintStream out) + { + out.println("// This file is intended to give you a head start on implementing native"); + out.println("// methods using CNI."); + out.println("// Be aware: running 'gcjh -stubs' once more for this class may"); + out.println("// overwrite any edits you have made to this file."); + out.println(); + out.println("#include <gcj/cni.h>"); + out.println("#include <java/lang/UnsupportedOperationException.h>"); + } + + protected PrintStream getPrintStreamImpl(FileOutputStream fos, + ClassWrapper klass) + { + return new CniPrintStream(fos); + } + + public void printClass(File filename, ClassWrapper klass) throws IOException + { + if (! klass.hasNativeMethod()) + return; + String className = klass.name.replaceAll("/", "::"); + CniPrintStream out = (CniPrintStream) getPrintStream(filename + ".cc", + klass); + if (out == null) + return; + out.println(); + out.println("#include <" + klass.name + ".h>"); + out.println(); + + Iterator<?> i = klass.methods.iterator(); + boolean first = true; + while (i.hasNext()) + { + MethodNode method = (MethodNode) i.next(); + if (! Modifier.isNative(method.access)) + continue; + if (! first) + out.println(); + first = false; + out.print(Type.getReturnType(method.desc)); + out.println(); + printDecl(out, className, method); + out.println(); + out.println("{"); + out.print(" throw new ::java::lang::UnsupportedOperationException("); + out.print("JvNewStringLatin1 (\""); + printDecl(out, className, method); + out.println("\"));"); + out.println("}"); + } + out.close(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/FieldHelper.java b/libjava/classpath/tools/gnu/classpath/tools/javah/FieldHelper.java new file mode 100644 index 000000000..84b1fce8b --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/FieldHelper.java @@ -0,0 +1,99 @@ +/* FieldHelper.java - field helper methods for CNI + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.lang.reflect.Modifier; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.FieldNode; + +public class FieldHelper +{ + public static boolean print(CniPrintStream out, FieldNode field, + ClassWrapper superType, boolean hasMethodName) + { + out.setModifiers(field.access); + out.print(" "); + if (Modifier.isStatic(field.access)) + out.print("static "); + if ((field.value instanceof Integer) || (field.value instanceof Long)) + out.print("const "); + out.print(Type.getType(field.desc)); + out.print(" "); + if (Modifier.isVolatile(field.access)) + out.print("volatile "); + boolean result = false; + if (superType != null && ! Modifier.isStatic(field.access)) + { + out.print("__attribute__((aligned(__alignof__( "); + superType.print(out); + out.print(")))) "); + result = true; + } + out.printName(field.name); + if (hasMethodName) + out.print("__"); + if (Modifier.isStatic(field.access)) + { + if (field.value instanceof Integer) + { + out.print(" = "); + int val = ((Integer) field.value).intValue(); + if (val == Integer.MIN_VALUE) + out.print("-" + Integer.MAX_VALUE + " - 1"); + else + out.print(val); + } + else if (field.value instanceof Long) + { + out.print(" = "); + long val = ((Long) field.value).longValue(); + if (val == Long.MIN_VALUE) + out.print("-" + Long.MAX_VALUE + "LL - 1"); + else + { + out.print(val); + out.print("LL"); + } + } + } + out.println(";"); + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/GcjhMain.java b/libjava/classpath/tools/gnu/classpath/tools/javah/GcjhMain.java new file mode 100644 index 000000000..bf466484d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/GcjhMain.java @@ -0,0 +1,153 @@ +/* GcjhMain.java - gcjh main program + Copyright (C) 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import gnu.classpath.tools.common.ClasspathToolParser; + +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; + +import java.io.IOException; +import java.util.ArrayList; + +public class GcjhMain extends Main +{ + ArrayList<Text> commands = new ArrayList<Text>(); + + public GcjhMain() + { + cni = true; + } + + protected String getName() + { + return "gcjh"; + } + + protected ClasspathToolParser getParser() + { + ClasspathToolParser result = super.getParser(); + + result.setHeader("usage: gcjh [OPTION]... CLASS..."); + + OptionGroup text = new OptionGroup("CNI text options"); + text.add(new Option("add", "Insert TEXT into class body", "TEXT") + { + public void parsed(String arg) throws OptionException + { + commands.add(new Text(Text.ADD, arg)); + } + }); + text.add(new Option("append", "Append TEXT after class declaration", + "TEXT") + { + public void parsed(String arg) throws OptionException + { + commands.add(new Text(Text.APPEND, arg)); + } + }); + text.add(new Option("friend", "Insert TEXT as a 'friend' declaration", + "TEXT") + { + public void parsed(String arg) throws OptionException + { + commands.add(new Text(Text.FRIEND, arg)); + } + }); + text.add(new Option("prepend", "Insert TEXT before start of class", "TEXT") + { + public void parsed(String arg) throws OptionException + { + commands.add(new Text(Text.PREPEND, arg)); + } + }); + result.add(text); + + OptionGroup compat = new OptionGroup("Compatibility options (unused)"); + // gcjh itself had compatibility options -old and -trace. I + // didn't add them here since they should really be unused by now. + compat.add(new Option("td", "Unused compatibility option", "DIRECTORY") + { + public void parsed(String arg) throws OptionException + { + } + }); + // I don't believe anyone ever used these options. + compat.add(new Option("M", "Unused compatibility option") + { + public void parsed(String arg) throws OptionException + { + } + }); + compat.add(new Option("MM", "Unused compatibility option") + { + public void parsed(String arg) throws OptionException + { + } + }); + compat.add(new Option("MD", "Unused compatibility option") + { + public void parsed(String arg) throws OptionException + { + } + }); + compat.add(new Option("MMD", "Unused compatibility option") + { + public void parsed(String arg) throws OptionException + { + } + }); + + result.add(compat); + + return result; + } + + protected void postParse(String[] names) + { + for (int i = 0; i < names.length; ++i) + textMap.put(names[i].replace('.', '/'), commands); + } + + public static void main(String[] args) throws IOException + { + new GcjhMain().run(args); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/JniHelper.java b/libjava/classpath/tools/gnu/classpath/tools/javah/JniHelper.java new file mode 100644 index 000000000..9225444be --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/JniHelper.java @@ -0,0 +1,120 @@ +/* JniHelper.java - name mangling and other JNI support + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.IOException; + +import org.objectweb.asm.Type; + +public class JniHelper +{ + public static String getName(Main classpath, Type type) throws IOException + { + if (type == Type.BOOLEAN_TYPE) + return "jboolean"; + else if (type == Type.BYTE_TYPE) + return "jbyte"; + else if (type == Type.CHAR_TYPE) + return "jchar"; + else if (type == Type.SHORT_TYPE) + return "jshort"; + else if (type == Type.INT_TYPE) + return "jint"; + else if (type == Type.LONG_TYPE) + return "jlong"; + else if (type == Type.FLOAT_TYPE) + return "jfloat"; + else if (type == Type.DOUBLE_TYPE) + return "jdouble"; + else if (type == Type.VOID_TYPE) + return "void"; + + if (type.getSort() == Type.ARRAY) + { + Type elt = type.getElementType(); + int eltSort = elt.getSort(); + if (type.getDimensions() == 1 && eltSort != Type.OBJECT) + return getName(classpath, elt) + "Array"; + return "jobjectArray"; + } + + // assert type.getSort() == Type.OBJECT; + String className = type.getClassName(); + // FIXME: is this correct? + if (className.equals("java/lang/Class") + || className.equals("java.lang.Class")) + return "jclass"; + if (className.equals("java/lang/String") + || className.equals("java.lang.String")) + return "jstring"; + + ClassWrapper klass = classpath.getClass(className); + if (klass.isThrowable()) + return "jthrowable"; + return "jobject"; + } + + public static String mangle(String name) + { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < name.length(); ++i) + { + char c = name.charAt(i); + if (c == '_') + result.append("_1"); + else if (c == ';') + result.append("_2"); + else if (c == '[') + result.append("_3"); + else if (c == '/') + result.append("_"); + else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z')) + result.append(c); + else + { + result.append("_0"); + // Sigh. + String hex = "0000" + Integer.toHexString(c); + result.append(hex.substring(hex.length() - 4)); + } + } + return result.toString(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/JniIncludePrinter.java b/libjava/classpath/tools/gnu/classpath/tools/javah/JniIncludePrinter.java new file mode 100644 index 000000000..cb8bcd8d9 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/JniIncludePrinter.java @@ -0,0 +1,169 @@ +/* JniIncludePrinter.java - Generate a JNI header file + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.Iterator; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +public class JniIncludePrinter + extends Printer +{ + protected JniIncludePrinter(Main classpath, File outFile, boolean isDir, + boolean force) + { + super(classpath, outFile, isDir, force); + } + + private void writeFields(ClassWrapper klass, JniPrintStream out) + throws IOException + { + klass.linkSupers(); + boolean wroteAny = false; + for (; klass != null; klass = klass.superClass) + { + Iterator<?> i = klass.fields.iterator(); + while (i.hasNext()) + { + FieldNode field = (FieldNode) i.next(); + if (! Modifier.isStatic(field.access) + || ! Modifier.isFinal(field.access)) + continue; + if (! (field.value instanceof Integer) + && ! (field.value instanceof Long)) + continue; + + // Note that we don't want to mangle the field name. + String name = (JniHelper.mangle(klass.name) + "_" + field.name); + out.print("#undef "); + out.println(name); + out.print("#define "); + out.print(name); + out.print(" "); + out.print(field.value); + if (field.value instanceof Integer) + out.print("L"); + else if (field.value instanceof Long) + out.print("LL"); + out.println(); + wroteAny = true; + } + } + if (wroteAny) + out.println(); + } + + protected void writePreambleImpl(PrintStream out) + { + out.println("/* DO NOT EDIT THIS FILE - it is machine generated */"); + out.println(); + out.println("#include <jni.h>"); + } + + protected PrintStream getPrintStreamImpl(FileOutputStream fos, + ClassWrapper klass) + { + return new JniPrintStream(classpath, fos, klass); + } + + public void printClass(File file, ClassWrapper klass) throws IOException + { + // Note that we ignore the filename here. + String xname = JniHelper.mangle(klass.name); + + // mangle the filename a bit + String filename = klass.name; + + filename = filename.replace('/', '_'); + filename = filename.replace('$', '_'); + filename = filename + ".h"; + + JniPrintStream out = (JniPrintStream) getPrintStream(filename, klass); + + if (out == null) + return; + + out.println(); + out.print("#ifndef __"); + out.print(xname); + out.println("__"); + out.print("#define __"); + out.print(xname); + out.println("__"); + out.println(); + out.println("#ifdef __cplusplus"); + out.println("extern \"C\""); + out.println("{"); + out.println("#endif"); + out.println(); + + Iterator<?> i = klass.methods.iterator(); + while (i.hasNext()) + { + MethodNode method = (MethodNode) i.next(); + if (! Modifier.isNative(method.access)) + continue; + out.print("JNIEXPORT "); + out.print(Type.getReturnType(method.desc)); + out.print(" JNICALL "); + out.print(method, xname); + out.println(";"); + } + + out.println(); + + writeFields(klass, out); + + out.println("#ifdef __cplusplus"); + out.println("}"); + out.println("#endif"); + out.println(); + out.print("#endif /* __"); + out.print(xname); + out.println("__ */"); + out.close(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/JniPrintStream.java b/libjava/classpath/tools/gnu/classpath/tools/javah/JniPrintStream.java new file mode 100644 index 000000000..96f9e7d1a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/JniPrintStream.java @@ -0,0 +1,115 @@ +/* JniPrintStream.java - PrintStream that emits JNI declarations + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Iterator; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.MethodNode; + +public class JniPrintStream + extends PrintStream +{ + Main classpath; + + // This is used to determine whether a method has an overload. + HashMap<String,Integer> methodNameMap = new HashMap<String,Integer>(); + + public JniPrintStream(Main classpath, OutputStream out, ClassWrapper klass) + { + super(out); + this.classpath = classpath; + computeOverloads(klass); + } + + private void computeOverloads(ClassWrapper klass) + { + Iterator<?> i = klass.methods.iterator(); + while (i.hasNext()) + { + MethodNode method = (MethodNode) i.next(); + if (! Modifier.isNative(method.access)) + continue; + if (methodNameMap.containsKey(method.name)) + { + Integer val = methodNameMap.get(method.name); + methodNameMap.put(method.name, Integer.valueOf(val.intValue() + 1)); + } + else + methodNameMap.put(method.name, Integer.valueOf(1)); + } + } + + public void print(Type type) throws IOException + { + print(JniHelper.getName(classpath, type)); + } + + public void print(MethodNode method, String className) throws IOException + { + print("Java_"); + print(className); + print("_"); + print(JniHelper.mangle(method.name)); + Integer overloadCount = (Integer) methodNameMap.get(method.name); + if (overloadCount.intValue() > 1) + { + print("__"); + int lastOffset = method.desc.lastIndexOf(')'); + print(JniHelper.mangle(method.desc.substring(1, lastOffset))); + } + print(" (JNIEnv *env"); + if (Modifier.isStatic(method.access)) + print(", jclass"); + else + print(", jobject"); + Type[] types = Type.getArgumentTypes(method.desc); + for (int i = 0; i < types.length; ++i) + { + print(", "); + print(types[i]); + } + print(")"); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/JniStubPrinter.java b/libjava/classpath/tools/gnu/classpath/tools/javah/JniStubPrinter.java new file mode 100644 index 000000000..4a1803a27 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/JniStubPrinter.java @@ -0,0 +1,109 @@ +/* JniStubPrinter.java - Generate JNI stub files + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.Iterator; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.MethodNode; + +public class JniStubPrinter + extends Printer +{ + protected JniStubPrinter(Main classpath, File outFile, boolean isDir, + boolean force) + { + super(classpath, outFile, isDir, force); + } + + protected void writePreambleImpl(PrintStream out) + { + out.println("/* This file is intended to give you a head start on implementing native"); + out.println(" methods using JNI."); + out.println(" Be aware: running gcjh or compatible tool with '-stubs' option once more"); + out.println(" for the same input may overwrite any edits you have made to this file. */"); + } + + protected PrintStream getPrintStreamImpl(FileOutputStream fos, + ClassWrapper klass) + { + return new JniPrintStream(classpath, fos, klass); + } + + public void printClass(File filename, ClassWrapper klass) throws IOException + { + // Note that we ignore the filename here. + if (! klass.hasNativeMethod()) + return; + String xname = JniHelper.mangle(klass.name); + JniPrintStream out + = (JniPrintStream) getPrintStream(klass.name.replace('/', '_') + ".c", + klass); + if (out == null) + return; + out.println(); + out.print("#include <"); + out.print(klass.name.replace('/', '_')); + out.println(".h>"); + + Iterator<?> i = klass.methods.iterator(); + while (i.hasNext()) + { + MethodNode method = (MethodNode) i.next(); + if (! Modifier.isNative(method.access)) + continue; + out.println(); + out.print(Type.getReturnType(method.desc)); + out.println(); + out.print(method, xname); + out.println(); + out.println("{"); + out.print(" (*env)->FatalError (env, \""); + out.print(method, xname); + out.println(" not implemented\");"); + out.println("}"); + } + out.close(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/Keywords.java b/libjava/classpath/tools/gnu/classpath/tools/javah/Keywords.java new file mode 100644 index 000000000..9c76a3660 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/Keywords.java @@ -0,0 +1,172 @@ +/* Keywords.java - List of C++ keywords + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.util.HashSet; + +public class Keywords +{ +/* A sorted list of all C++ keywords. This is identical to the list + in gcc/java/mangle.c. */ + private static final String[] words = + { + "_Complex", + "__alignof", + "__alignof__", + "__asm", + "__asm__", + "__attribute", + "__attribute__", + "__builtin_va_arg", + "__complex", + "__complex__", + "__const", + "__const__", + "__extension__", + "__imag", + "__imag__", + "__inline", + "__inline__", + "__label__", + "__null", + "__real", + "__real__", + "__restrict", + "__restrict__", + "__signed", + "__signed__", + "__typeof", + "__typeof__", + "__volatile", + "__volatile__", + "and", + "and_eq", + "asm", + "auto", + "bitand", + "bitor", + "bool", + "break", + "case", + "catch", + "char", + "class", + "compl", + "const", + "const_cast", + "continue", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "else", + "enum", + "explicit", + "export", + "extern", + "false", + "float", + "for", + "friend", + "goto", + "if", + "inline", + "int", + "long", + "mutable", + "namespace", + "new", + "not", + "not_eq", + "operator", + "or", + "or_eq", + "private", + "protected", + "public", + "register", + "reinterpret_cast", + "return", + "short", + "signed", + "sizeof", + "static", + "static_cast", + "struct", + "switch", + "template", + "this", + "throw", + "true", + "try", + "typedef", + "typeid", + "typename", + "typeof", + "union", + "unsigned", + "using", + "virtual", + "void", + "volatile", + "wchar_t", + "while", + "xor", + "xor_eq" + }; + + private static final HashSet<String> keywords; + static + { + keywords = new HashSet<String>(); + for (int i = 0; i < words.length; ++i) + keywords.add(words[i]); + } + + public static String getCxxName(String name) + { + int i; + for (i = name.length() - 1; i >= 0 && name.charAt(i) == '$'; --i) + ; + if (keywords.contains(name.substring(0, i + 1))) + return name + "$"; + return name; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/Main.java b/libjava/classpath/tools/gnu/classpath/tools/javah/Main.java new file mode 100644 index 000000000..bec04f00d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/Main.java @@ -0,0 +1,468 @@ +/* Main.java - javah main program + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +import org.objectweb.asm.ClassReader; + +public class Main +{ + // This is an option group for classpath-related options, + // and also is used for loading classes. + PathOptionGroup classpath = new PathOptionGroup(); + + // The output directory. + String outputDir; + + // The output file name used if/when -o option is used. + String outFileName; + + // The loader that we use to load class files. + URLClassLoader loader; + + // In -all mode, the name of the directory to scan. + String allDirectory; + + // True for verbose mode. + boolean verbose; + + // True if we're emitting stubs. + boolean stubs; + + // True if we're emitting CNI code. + boolean cni; + + // True if we've seen -cni or -jni. + boolean cniOrJniSeen; + + // True if output files should always be written. + boolean force; + + // Map class names to class wrappers. + HashMap<String,ClassWrapper> classMap = new HashMap<String,ClassWrapper>(); + + // Map class names to lists of Text objects. + HashMap<String,ArrayList<Text>> textMap = new HashMap<String,ArrayList<Text>>(); + + void readCommandFile(String textFileName) throws OptionException + { + FileInputStream fis; + try + { + fis = new FileInputStream(textFileName); + } + catch (FileNotFoundException ignore) + { + throw new OptionException("file \"" + textFileName + "\" not found"); + } + BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); + String currentClass = null; + ArrayList<Text> currentValues = null; + while (true) + { + String line; + try + { + line = reader.readLine(); + } + catch (IOException _) + { + break; + } + if (line == null) + break; + line = line.trim(); + if (line.length() == 0 || line.charAt(0) == '#') + continue; + int index = line.indexOf(' '); + String cmd = line.substring(0, index); + String value = line.substring(index + 1); + int cmdValue; + if ("class".equals(cmd)) + { + if (currentClass != null) + { + textMap.put(currentClass, currentValues); + } + currentClass = value; + currentValues = new ArrayList<Text>(); + continue; + } + if (currentClass == null) + throw new OptionException("no class set"); + if ("add".equals(cmd)) + cmdValue = Text.ADD; + else if ("append".equals(cmd)) + cmdValue = Text.APPEND; + else if ("prepend".equals(cmd)) + cmdValue = Text.PREPEND; + else if ("friend".equals(cmd)) + cmdValue = Text.FRIEND; + else + throw new OptionException("unrecognized command: " + cmd); + currentValues.add(new Text(cmdValue, value)); + } + if (currentClass != null) + { + textMap.put(currentClass, currentValues); + } + } + + void scanDirectory(File dir, final HashSet<Object> results) + { + File[] files = dir.listFiles(new FileFilter() + { + public boolean accept(File pathname) + { + if (pathname.isDirectory()) + { + scanDirectory(pathname, results); + return false; + } + return pathname.getName().endsWith(".class"); + } + }); + if (files != null) + results.addAll(Arrays.asList(files)); + } + + protected String getName() + { + return "javah"; + } + + protected ClasspathToolParser getParser() + { + ClasspathToolParser result = new ClasspathToolParser(getName(), true); + result.setHeader("usage: javah [OPTIONS] CLASS..."); + result.add(classpath); + result.add(new Option('d', "Set output directory", "DIR") + { + public void parsed(String dir) throws OptionException + { + if (outputDir != null) + throw new OptionException("-d already seen"); + if (outFileName != null) + throw new OptionException("only one of -d or -o may be used"); + outputDir = dir; + } + }); + result.add(new Option('o', + "Set output file (only one of -d or -o may be used)", + "FILE") + { + public void parsed(String fileName) throws OptionException + { + if (outFileName != null) + throw new OptionException("-o already seen"); + if (outputDir != null) + throw new OptionException("only one of -d or -o may be used"); + outFileName = fileName; + } + }); + result.add(new Option("cmdfile", "Read command file", "FILE") + { + public void parsed(String file) throws OptionException + { + readCommandFile(file); + } + }); + result.add(new Option("all", "Operate on all class files under directory", + "DIR") + { + public void parsed(String arg) throws OptionException + { + // FIXME: lame restriction... + if (allDirectory != null) + throw new OptionException("-all already specified"); + allDirectory = arg; + } + }); + result.add(new Option("stubs", "Emit stub implementation") + { + public void parsed(String arg0) throws OptionException + { + stubs = true; + } + }); + result.add(new Option("jni", "Emit JNI stubs or header (default)") + { + public void parsed(String arg0) throws OptionException + { + if (cniOrJniSeen && cni) + throw new OptionException("only one of -jni or -cni may be used"); + cniOrJniSeen = true; + cni = false; + } + }); + result.add(new Option("cni", "Emit CNI stubs or header (default JNI)") + { + public void parsed(String arg0) throws OptionException + { + if (cniOrJniSeen && ! cni) + throw new OptionException("only one of -jni or -cni may be used"); + cniOrJniSeen = true; + cni = true; + } + }); + result.add(new Option("verbose", 'v', "Set verbose mode") + { + public void parsed(String arg0) throws OptionException + { + verbose = true; + } + }); + result.add(new Option("force", "Output files should always be written") + { + public void parsed(String arg0) throws OptionException + { + force = true; + } + }); + return result; + } + + private File makeOutputDirectory() throws IOException + { + File outputFile; + if (outputDir == null) + outputFile = new File("."); + else + outputFile = new File(outputDir); + return outputFile; + } + + /** + * @return The {@link File} object where the generated code will be written. + * Returns <code>null</code> if the option <code>-force</code> was + * specified on the command line and the designated file already + * exists. + * @throws IOException if <code>outFileName</code> is not a writable file. + */ + private File makeOutputFile() throws IOException + { + File result = new File(outFileName); + if (result.exists()) + { + if (! result.isFile()) + throw new IOException("'" + outFileName + "' is not a file"); + if (! force) + { + if (verbose) + System.err.println("["+ outFileName + + " already exists. Use -force to overwrite]"); + return null; + } + if (! result.delete()) + throw new IOException("Was unable to delete existing file: " + + outFileName); + } + return result; + } + + private void writeHeaders(HashMap<File,ClassWrapper> klasses, Printer printer) + throws IOException + { + Iterator<Map.Entry<File,ClassWrapper>> i = klasses.entrySet().iterator(); + while (i.hasNext()) + { + Map.Entry<File,ClassWrapper> e = i.next(); + File file = e.getKey(); + ClassWrapper klass = e.getValue(); + if (verbose) + System.err.println("[writing " + klass + " as " + file + "]"); + printer.printClass(file, klass); + } + } + + protected void postParse(String[] names) + { + // Nothing here. + } + + protected void run(String[] args) throws IOException + { + ClasspathToolParser p = getParser(); + String[] classNames = p.parse(args, true); + postParse(classNames); + loader = classpath.getLoader(); + + boolean isDirectory = outFileName == null; + File outputFile = isDirectory ? makeOutputDirectory() : makeOutputFile(); + if (outputFile == null) + return; + + Printer printer; + if (! cni) + { + if (stubs) + printer = new JniStubPrinter(this, outputFile, isDirectory, force); + else + printer = new JniIncludePrinter(this, outputFile, isDirectory, force); + } + else + { + if (stubs) + printer = new CniStubPrinter(this, outputFile, isDirectory, force); + else + printer = new CniIncludePrinter(this, outputFile, isDirectory, force); + } + + // First we load all of the files. That way if + // there are references between the files we will + // be loading the set that the user asked for. + HashSet<Object> klasses = new HashSet<Object>(); + if (allDirectory != null) + scanDirectory(new File(allDirectory), klasses); + // Add the command-line arguments. We use the type of + // an item in 'klasses' to decide how to load each class. + for (int i = 0; i < classNames.length; ++i) + { + if (classNames[i].endsWith(".class")) + { + klasses.add(new File(classNames[i])); + } + else + { + klasses.add(classNames[i]); + } + } + + Iterator<Object> i = klasses.iterator(); + HashMap<File,ClassWrapper> results = new HashMap<File,ClassWrapper>(); + while (i.hasNext()) + { + // Let user specify either kind of class name or a + // file name. + Object item = i.next(); + ClassWrapper klass; + File filename; + if (item instanceof File) + { + // Load class from file. + if (verbose) + System.err.println("[reading file " + item + "]"); + klass = getClass((File) item); + filename = new File(klass.name); + } + else + { + // Load class given the class name. + String className = ((String) item).replace('.', '/'); + if (verbose) + System.err.println("[reading class " + className + "]"); + // Use the name the user specified, even if it is + // different from the ultimate class name. + filename = new File(className); + klass = getClass(className); + } + results.put(filename, klass); + } + + writeHeaders(results, printer); + } + + public ArrayList<Text> getClassTextList(String name) + { + return textMap.get(name); + } + + private ClassWrapper readClass(InputStream is) throws IOException + { + ClassReader r = new ClassReader(is); + ClassWrapper result = new ClassWrapper(this); + r.accept(result, true); + is.close(); + return result; + } + + private ClassWrapper getClass(File fileName) throws IOException + { + InputStream is = new FileInputStream(fileName); + ClassWrapper result = readClass(is); + if (classMap.containsKey(result.name)) + throw new IllegalArgumentException("class " + result.name + + " already loaded"); + classMap.put(result.name, result); + return result; + } + + public ClassWrapper getClass(String name) throws IOException + { + if (! classMap.containsKey(name)) + { + String resource = name.replace('.', '/') + ".class"; + URL url = loader.findResource(resource); + if (url == null) + throw new IOException("can't find class file " + resource + + " in " + loader); + InputStream is = url.openStream(); + ClassWrapper result = readClass(is); + classMap.put(name, result); + } + return (ClassWrapper) classMap.get(name); + } + + public static void main(String[] args) throws IOException + { + Main m = new Main(); + m.run(args); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/MethodHelper.java b/libjava/classpath/tools/gnu/classpath/tools/javah/MethodHelper.java new file mode 100644 index 000000000..d65cc93d7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/MethodHelper.java @@ -0,0 +1,122 @@ +/* MethodHelper.java - helper class for manipulating methods + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.lang.reflect.Modifier; +import java.util.Iterator; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +public class MethodHelper +{ + + public static boolean overrides(MethodNode derived, MethodNode base) + { + if (! derived.name.equals(base.name)) + return false; + if (! derived.desc.equals(base.desc)) + return false; + // FIXME: permission madness? + return true; + } + + public static String getBridgeTarget(MethodNode meth) + { + if ((meth.access & Opcodes.ACC_BRIDGE) == 0) + return null; + Iterator<?> i = meth.instructions.iterator(); + while (i.hasNext()) + { + AbstractInsnNode insn = (AbstractInsnNode) i.next(); + if (! (insn instanceof MethodInsnNode)) + continue; + return ((MethodInsnNode) insn).desc; + } + return null; + } + + public static void print(CniPrintStream out, MethodNode meth, + ClassWrapper declarer, String realMethodName) + { + if ("<clinit>".equals(meth.name)) + return; + boolean isInit = "<init>".equals(meth.name); + out.setModifiers(meth.access); + out.print(" "); + if (Modifier.isStatic(meth.access)) + out.print("static "); + // If a class is final then we might as well skip 'virtual'. + // The reason here is that it is safe in this case for C++ + // ABI code to generate a direct call. The method does end + // up in the vtable (for BC code) but we don't care. Also, + // the class can't be derived from anyway. + else if (! isInit && ! Modifier.isPrivate(meth.access) + && ! Modifier.isFinal(declarer.access)) + out.print("virtual "); + if (! isInit) + { + out.print(Type.getReturnType(meth.desc)); + out.print(" "); + out.printName(realMethodName); + } + else + { + String name = declarer.name; + int index = name.lastIndexOf('/'); + name = name.substring(index + 1); + out.printName(name); + } + out.print("("); + Type[] argTypes = Type.getArgumentTypes(meth.desc); + for (int i = 0; i < argTypes.length; ++i) + { + if (i > 0) + out.print(", "); + out.print(argTypes[i]); + } + out.print(")"); + if (Modifier.isAbstract(meth.access)) + out.print(" = 0"); + out.println(";"); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/PackageWrapper.java b/libjava/classpath/tools/gnu/classpath/tools/javah/PackageWrapper.java new file mode 100644 index 000000000..11b38b20f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/PackageWrapper.java @@ -0,0 +1,54 @@ +/* PackageWrapper.java - represent a package + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +public class PackageWrapper +{ + // This is null if there is no parent package. + PackageWrapper parent; + + // Name of this package relative to its parent's name. + String name; + + public PackageWrapper(PackageWrapper parent, String name) + { + this.parent = parent; + this.name = name; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/PathOptionGroup.java b/libjava/classpath/tools/gnu/classpath/tools/javah/PathOptionGroup.java new file mode 100644 index 000000000..8eec4cc60 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/PathOptionGroup.java @@ -0,0 +1,147 @@ +/* PathOptionGroup.java - handle classpath-setting options + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; + +import java.io.File; +import java.io.FilenameFilter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.StringTokenizer; + +public class PathOptionGroup + extends OptionGroup +{ + ArrayList<String> classpath = new ArrayList<String>(); + + ArrayList<String> bootclasspath = new ArrayList<String>(); + + void setPath(ArrayList<String> list, String path) + { + list.clear(); + StringTokenizer st = new StringTokenizer(path, File.pathSeparator); + while (st.hasMoreTokens()) + { + list.add(st.nextToken()); + } + } + + void addExtDirs(ArrayList<String> list, String path) + { + StringTokenizer tok = new StringTokenizer(path, File.pathSeparator); + while (tok.hasMoreTokens()) + { + File dir = new File(tok.nextToken()); + list.addAll(Arrays.asList(dir.list(new FilenameFilter() + { + public boolean accept(File dir, String name) + { + return name.endsWith(".zip") || name.endsWith(".jar"); + } + }))); + } + } + + public PathOptionGroup() + { + super("Class path options"); + + // Use the VM's built-in boot class path by default. + String boot = System.getProperty("sun.boot.class.path"); + if (boot != null) + setPath(bootclasspath, boot); + + add(new Option("classpath", "Set the class path", "PATH") + { + public void parsed(String path) throws OptionException + { + setPath(classpath, path); + } + }); + add(new Option("cp", "Set the class path", "PATH") + { + public void parsed(String path) throws OptionException + { + setPath(classpath, path); + } + }); + add(new Option('I', "Add directory to class path", "DIR", true) + { + public void parsed(String path) throws OptionException + { + classpath.add(path); + } + }); + add(new Option("bootclasspath", "Set the boot class path", "PATH") + { + public void parsed(String path) throws OptionException + { + setPath(bootclasspath, path); + } + }); + add(new Option("extdirs", "Set the extension directory path", "PATH") + { + public void parsed(String path) throws OptionException + { + addExtDirs(classpath, path); + } + }); + } + + public URLClassLoader getLoader() throws MalformedURLException + { + ArrayList<URL> urls = new ArrayList<URL>(); + classpath.addAll(bootclasspath); + Iterator<String> i = classpath.iterator(); + while (i.hasNext()) + { + String f = i.next(); + urls.add(new File(f).toURL()); + } + URL[] urlArray = urls.toArray(new URL[0]); + return new URLClassLoader(urlArray); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/Printer.java b/libjava/classpath/tools/gnu/classpath/tools/javah/Printer.java new file mode 100644 index 000000000..7a896cf62 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/Printer.java @@ -0,0 +1,139 @@ +/* Print.java - abstract base class for printing classes + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +public abstract class Printer +{ + protected Main classpath; + + /** + * The {@link File} object that denotes either a directory (when the + * <code>-d</code> option was used), or a file (when the <code>-o</code> + * option was used) on the command line. + */ + protected File outputFileObject; + + /** + * Set to <code>true</code> if the field <code>outputFileObject</code> denotes + * a directory; i.e. for each input class file, one JNI header file will be + * generated in that directory. + * <p> + * Set to <code>false</code> if the field <code>outputFileObject</code> + * denotes a file; i.e. all generated headers will be written to that file. + */ + protected boolean isDirectory; + + /** + * Set to <code>true</code> if the output file(s) should always be written. + * <p> + * When set to <code>false</code>, the contents of the header/stub are only + * written to the file if it does not already exist. + */ + protected boolean force; + + /** + * Set to <code>true</code> if all output is directed to one file, and the + * common preamble text has already been generated. + */ + protected boolean wrotePreamble; + + protected Printer(Main classpath, File outFile, boolean isDir, boolean force) + { + this.classpath = classpath; + if (outFile == null) + throw new IllegalArgumentException("File argument MUST NOT be null"); + outputFileObject = outFile; + isDirectory = isDir; + if (! isDirectory) + { + File parent = outputFileObject.getParentFile(); + if (parent != null) + parent.mkdirs(); + } + this.force = force; + } + + public abstract void printClass(File filename, ClassWrapper klass) + throws IOException; + + protected abstract void writePreambleImpl(PrintStream ps); + + protected abstract PrintStream getPrintStreamImpl(FileOutputStream fos, + ClassWrapper klass); + + protected PrintStream getPrintStream(String fullName, ClassWrapper klass) + throws FileNotFoundException + { + PrintStream result; + FileOutputStream fos; + if (isDirectory) + { + File outFile = new File(outputFileObject, fullName); + if (outFile.exists() && ! force) + return null; + File parent = outFile.getParentFile(); + if (parent != null) + parent.mkdirs(); + fos = new FileOutputStream(outFile); + result = getPrintStreamImpl(fos, klass); + writePreamble(result); + } + else + { + // the first time we open this file, wrotePreamble is false + fos = new FileOutputStream(outputFileObject, wrotePreamble); + result = getPrintStreamImpl(fos, klass); + if (! wrotePreamble) + writePreamble(result); + } + return result; + } + + protected void writePreamble(PrintStream out) + { + writePreambleImpl(out); + wrotePreamble = true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/javah/Text.java b/libjava/classpath/tools/gnu/classpath/tools/javah/Text.java new file mode 100644 index 000000000..37a1ad669 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/javah/Text.java @@ -0,0 +1,60 @@ +/* Text.java - convenience class for CNI header text insertions + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.javah; + +public class Text +{ + public static final int ADD = 0; + + public static final int APPEND = 1; + + public static final int FRIEND = 2; + + public static final int PREPEND = 3; + + public int type; + + public String text; + + public Text(int type, String text) + { + this.type = type; + this.text = text; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/CACertCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/CACertCmd.java new file mode 100644 index 000000000..603385d19 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/CACertCmd.java @@ -0,0 +1,313 @@ +/* CACertCmd.java -- GNU specific cacert handler + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; + +import java.io.File; +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.logging.Logger; + +/** + * The <code>-cacert</code> keytol command handler is used to import a CA + * trusted X.509 certificate into a key store. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the file containing the trusted CA + * certificate to import. If omitted, the tool will process STDIN. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +public class CACertCmd + extends Command +{ + private static final Logger log = Logger.getLogger(CACertCmd.class.getName()); + /** Pathname of the file containing the CA certificate to import. */ + protected String _certFileName; + /** Type of the key store to use. */ + protected String _ksType; + /** The URL to the keystore where the trusted certificates will be added. */ + protected String _ksURL; + /** The password protecting the keystore. */ + protected String _ksPassword; + /** Class name of a security provider to use. */ + protected String _providerClassName; + /** Reference to the X.509 factory. */ + private CertificateFactory x509Factory; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certFileName = pathName; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + /* (non-Javadoc) + * @see gnu.classpath.tools.keytool.Command#setup() + */ + void setup() throws Exception + { + setInputStreamParam(_certFileName); + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + if (Configuration.DEBUG) + { + log.fine("-cacert handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -file=" + _certFileName); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws CertificateException, KeyStoreException, + NoSuchAlgorithmException, IOException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + alias = getAliasFromFileName(_certFileName); + if (store.containsAlias(alias)) + throw new IllegalArgumentException(Messages.getFormattedString("CACertCmd.0", //$NON-NLS-1$ + alias)); + x509Factory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + Certificate certificate = x509Factory.generateCertificate(inStream); + if (Configuration.DEBUG) + log.fine("certificate = " + certificate); //$NON-NLS-1$ + store.setCertificateEntry(alias, certificate); + saveKeyStore(); + if (verbose) + System.out.println(Messages.getFormattedString("CACertCmd.1", //$NON-NLS-1$ + new Object[] { _certFileName, + alias })); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + /* (non-Javadoc) + * @see gnu.classpath.tools.keytool.Command#getParser() + */ + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.CACERT_CMD, true); + result.setHeader(Messages.getString("CACertCmd.2")); //$NON-NLS-1$ + result.setFooter(Messages.getString("CACertCmd.3")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("CACertCmd.4")); //$NON-NLS-1$ + options.add(new Option(Main.FILE_OPT, + Messages.getString("CACertCmd.5"), //$NON-NLS-1$ + Messages.getString("CACertCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _certFileName = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("CACertCmd.7"), //$NON-NLS-1$ + Messages.getString("CACertCmd.8")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("CACertCmd.9"), //$NON-NLS-1$ + Messages.getString("CACertCmd.10")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("CACertCmd.11"), //$NON-NLS-1$ + Messages.getString("CACertCmd.12")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("CACertCmd.13"), //$NON-NLS-1$ + Messages.getString("CACertCmd.14")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("CACertCmd.15")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + /** + * Construct an Alias string from the name of the file containing the + * certificate to import. This method first removes the last dot (".") + * character and any subsequent characters from the input name, and then + * replaces any space and dot characters with underscores. For example the + * input string <code>brasil.gov.br.cert</code> will result in + * <code>brasil_gov_br</code> as its alias. + * + * @param fileName the name of the file containing the CA certificate + * @return a string which can, and will, be used as the Alias of this CA + * certificate. + */ + private String getAliasFromFileName(String fileName) + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getAliasFromFileName", fileName); //$NON-NLS-1$ + // get the basename + fileName = new File(fileName).getName(); + // remove '.' if at start + if (fileName.startsWith(".")) //$NON-NLS-1$ + fileName = fileName.substring(1); + + // remove last \..+ + int ndx = fileName.lastIndexOf('.'); + if (ndx > 0) + fileName = fileName.substring(0, ndx); + // replace spaces and dots with underscores + char[] chars = fileName.toCharArray(); + for (int i = 0; i < chars.length; i++) + { + char c = chars[i]; + if (c == ' ' || c == '.') + chars[i] = '_'; + } + String result = new String(chars); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getAliasFromFileName", result); //$NON-NLS-1$ + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/CertReqCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/CertReqCmd.java new file mode 100644 index 000000000..e14fa4916 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/CertReqCmd.java @@ -0,0 +1,475 @@ +/* CertReqCmd.java -- The certreq command handler of the keytool + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.security.OID; +import gnu.java.security.der.BitString; +import gnu.java.security.der.DER; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; +import gnu.java.util.Base64; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.logging.Logger; + +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.x500.X500Principal; + +/** + * The <b>-certreq</b> keytool command handler is used to generate a Certificate + * Signing Request (CSR) in PKCS#10 format. + * <p> + * The ASN.1 specification of a CSR, as stated in RFC-2986 is as follows: + * <p> + * <pre> + * CertificationRequest ::= SEQUENCE { + * certificationRequestInfo CertificationRequestInfo, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + * + * CertificationRequestInfo ::= SEQUENCE { + * version INTEGER -- v1(0) + * subject Name, + * subjectPKInfo SubjectPublicKeyInfo, + * attributes [0] IMPLICIT Attributes -- see note later + * } + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING + * } + * </pre> + * <b>IMPORTANT</b>: Some documentation (e.g. RSA examples) claims that the + * <code>attributes</code> field is <i>OPTIONAL</i> while <i>RFC-2986</i> + * implies the opposite. This implementation considers this field, by default, + * as <i>OPTIONAL</i>, unless the option <code>-attributes</code> is included + * on the command line. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-sigalg ALGORITHM</dt> + * <dd>The canonical name of the digital signature algorithm to use for + * signing the certificate. If this option is omitted, a default value will + * be chosen based on the type of the private key associated with the + * designated <i>Alias</i>. If the private key is a <code>DSA</code> one, + * the value for the signature algorithm will be <code>SHA1withDSA</code>. + * If on the other hand the private key is an <code>RSA</code> one, then + * the tool will use <code>MD5withRSA</code> as the signature algorithm. + * <p></dd> + * + * <dt>-file FILE_NAME</dt> + * + * <dt>-keypass PASSWORD</dt> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output. + * <p></dd> + * + * <dt>-attributes</dt> + * <dd>Use this option to force the tool to encode a NULL DER value in the + * CSR as the value of the Attributes field.</dd> + * </dl> + */ +class CertReqCmd extends Command +{ + private static final Logger log = Logger.getLogger(CertReqCmd.class.getName()); + private static final String ATTRIBUTES_OPT = "attributes"; //$NON-NLS-1$ + protected String _alias; + protected String _sigAlgorithm; + protected String _certReqFileName; + protected String _password; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + protected boolean nullAttributes; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** + * @param algorithm the canonical name of the digital signature algorithm to + * use. + */ + public void setSigalg(String algorithm) + { + this._sigAlgorithm = algorithm; + } + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certReqFileName = pathName; + } + + /** @param password the (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + /** + * @param flag whether to use, or not, a <code>NULL</code> DER value for + * the certificate's Attributes field. + */ + public void setAttributes(String flag) + { + this.nullAttributes = Boolean.valueOf(flag).booleanValue(); + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setOutputStreamParam(_certReqFileName); + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); + if (Configuration.DEBUG) + { + log.fine("-certreq handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -sigalg=" + _sigAlgorithm); //$NON-NLS-1$ + log.fine(" -file=" + _certReqFileName); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + log.fine(" -attributes=" + nullAttributes); //$NON-NLS-1$ + } + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, IOException, + UnsupportedCallbackException, UnrecoverableKeyException, + InvalidKeyException, SignatureException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + // 1. get the key entry and certificate chain associated to alias + Key privateKey = getAliasPrivateKey(); + Certificate[] chain = store.getCertificateChain(alias); + + // 2. get alias's DN and public key to use in the CSR + X509Certificate bottomCertificate = (X509Certificate) chain[0]; + X500Principal aliasName = bottomCertificate.getIssuerX500Principal(); + PublicKey publicKey = bottomCertificate.getPublicKey(); + + // 3. generate the CSR + setSignatureAlgorithmParam(_sigAlgorithm, privateKey); + byte[] derBytes = getCSR(aliasName, publicKey, (PrivateKey) privateKey); + + // 4. encode it in base-64 and write it to outStream + String encoded = Base64.encode(derBytes, 72); + PrintWriter writer = new PrintWriter(outStream, true); + writer.println("-----BEGIN NEW CERTIFICATE REQUEST-----"); //$NON-NLS-1$ + writer.println(encoded); + writer.println("-----END NEW CERTIFICATE REQUEST-----"); //$NON-NLS-1$ + + if (verbose) + { + if (! systemOut) + System.out.println(Messages.getFormattedString("CertReqCmd.27", //$NON-NLS-1$ + _certReqFileName)); + System.out.println(Messages.getString("CertReqCmd.28")); //$NON-NLS-1$ + } + + writer.close(); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.CERTREQ_CMD, true); + result.setHeader(Messages.getString("CertReqCmd.25")); //$NON-NLS-1$ + result.setFooter(Messages.getString("CertReqCmd.24")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("CertReqCmd.23")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("CertReqCmd.22"), //$NON-NLS-1$ + Messages.getString("CertReqCmd.21")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.SIGALG_OPT, + Messages.getString("CertReqCmd.20"), //$NON-NLS-1$ + Messages.getString("CertReqCmd.19")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _sigAlgorithm = argument; + } + }); + options.add(new Option(Main.FILE_OPT, + Messages.getString("CertReqCmd.18"), //$NON-NLS-1$ + Messages.getString("CertReqCmd.17")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _certReqFileName = argument; + } + }); + options.add(new Option(Main.KEYPASS_OPT, + Messages.getString("CertReqCmd.16"), //$NON-NLS-1$ + Messages.getString("CertReqCmd.9")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _password = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("CertReqCmd.14"), //$NON-NLS-1$ + Messages.getString("CertReqCmd.13")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("CertReqCmd.12"), //$NON-NLS-1$ + Messages.getString("CertReqCmd.11")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("CertReqCmd.10"), //$NON-NLS-1$ + Messages.getString("CertReqCmd.9")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("CertReqCmd.8"), //$NON-NLS-1$ + Messages.getString("CertReqCmd.7")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("CertReqCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + options.add(new Option(ATTRIBUTES_OPT, + Messages.getString("CertReqCmd.5")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + nullAttributes = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + /** + * @param aliasName + * @param publicKey + * @param privateKey + * @return the DER encoded Certificate Signing Request. + * @throws IOException + * @throws InvalidKeyException + * @throws SignatureException + */ + private byte[] getCSR(X500Principal aliasName, PublicKey publicKey, + PrivateKey privateKey) + throws IOException, InvalidKeyException, SignatureException + { + DERValue derVersion = new DERValue(DER.INTEGER, BigInteger.ZERO); + DERValue derSubject = new DERReader(aliasName.getEncoded()).read(); + DERValue derSubjectPKInfo = new DERReader(publicKey.getEncoded()).read(); + byte[] b = nullAttributes ? new byte[] { 0x05, 0x00 } : new byte[0]; + DERValue derAttributes = new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 0, + b.length, b, null); + ArrayList certRequestInfo = new ArrayList(4); + certRequestInfo.add(derVersion); + certRequestInfo.add(derSubject); + certRequestInfo.add(derSubjectPKInfo); + certRequestInfo.add(derAttributes); + DERValue derCertRequestInfo = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + certRequestInfo); + + OID sigAlgorithmID = getSignatureAlgorithmOID(); + DERValue derSigAlgorithmID = new DERValue(DER.OBJECT_IDENTIFIER, + sigAlgorithmID); + ArrayList sigAlgorithm = new ArrayList(2); + sigAlgorithm.add(derSigAlgorithmID); + if (! sigAlgorithmID.equals(Command.SHA1_WITH_DSA)) // it's an RSA-based + sigAlgorithm.add(new DERValue(DER.NULL, null)); + + sigAlgorithm.trimToSize(); + DERValue derSignatureAlgorithm = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + sigAlgorithm); + + signatureAlgorithm.initSign(privateKey); + signatureAlgorithm.update(derCertRequestInfo.getEncoded()); + byte[] sigBytes = signatureAlgorithm.sign(); + DERValue derSignature = new DERValue(DER.BIT_STRING, new BitString(sigBytes)); + + ArrayList csr = new ArrayList(3); + csr.add(derCertRequestInfo); + csr.add(derSignatureAlgorithm); + csr.add(derSignature); + DERValue derCSR = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, csr); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DERWriter.write(baos, derCSR); + byte[] result = baos.toByteArray(); + + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/Command.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/Command.java new file mode 100644 index 000000000..050e75b37 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/Command.java @@ -0,0 +1,1228 @@ +/* Command.java -- Abstract implementation of a keytool command handler + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.SystemProperties; +import gnu.classpath.tools.common.CallbackUtil; +import gnu.classpath.tools.common.ProviderUtil; +import gnu.classpath.tools.common.SecurityProviderInfo; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.security.OID; +import gnu.java.security.Registry; +import gnu.java.security.der.BitString; +import gnu.java.security.der.DER; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; +import gnu.java.security.hash.IMessageDigest; +import gnu.java.security.hash.MD5; +import gnu.java.security.hash.Sha160; +import gnu.java.security.util.Util; +import gnu.java.security.x509.X500DistinguishedName; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.net.URL; +import java.net.URLConnection; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.interfaces.DSAKey; +import java.security.interfaces.RSAKey; +import java.util.ArrayList; +import java.util.Date; +import java.util.logging.Logger; +import java.util.prefs.Preferences; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * A base class of the keytool command to facilitate implementation of concrete + * keytool Handlers. + */ +abstract class Command +{ + // Fields and constants ----------------------------------------------------- + + private static final Logger log = Logger.getLogger(Command.class.getName()); + /** Default value for the ALIAS argument. */ + private static final String DEFAULT_ALIAS = "mykey"; //$NON-NLS-1$ + /** Default algorithm for key-pair generation. */ + private static final String DEFAULT_KEY_ALGORITHM = "DSA"; //$NON-NLS-1$ + /** Default DSA digital signature algorithm to use with DSA keys. */ + private static final String DSA_SIGNATURE_ALGORITHM = "SHA1withDSA"; //$NON-NLS-1$ + /** Default RSA digital signature algorithm to use with RSA keys. */ + private static final String RSA_SIGNATURE_ALGORITHM = "MD5withRSA"; //$NON-NLS-1$ + /** Default validity (in days) of newly generated certificates. */ + private static final int DEFAULT_VALIDITY = 90; + /** OID of SHA1withDSA signature algorithm as stated in RFC-2459. */ + protected static final OID SHA1_WITH_DSA = new OID("1.2.840.10040.4.3"); //$NON-NLS-1$ + /** OID of MD2withRSA signature algorithm as stated in RFC-2459. */ + private static final OID MD2_WITH_RSA = new OID("1.2.840.113549.1.1.2"); //$NON-NLS-1$ + /** OID of MD5withRSA signature algorithm as stated in RFC-2459. */ + private static final OID MD5_WITH_RSA = new OID("1.2.840.113549.1.1.4"); //$NON-NLS-1$ + /** OID of SHA1withRSA signature algorithm as stated in RFC-2459. */ + private static final OID SHA1_WITH_RSA = new OID("1.2.840.113549.1.1.5"); //$NON-NLS-1$ + /** Number of milliseconds in one day. */ + private static final long MILLIS_IN_A_DAY = 24 * 60 * 60 * 1000L; + + /** The Alias to use. */ + protected String alias; + /** The password characters protecting a Key Entry. */ + protected char[] keyPasswordChars; + /** A security provider to add. */ + protected Provider provider; + /** The key store type. */ + protected String storeType; + /** The password characters protecting the key store. */ + protected char[] storePasswordChars; + /** The key store URL. */ + protected URL storeURL; + /** The input stream from the key store URL. */ + protected InputStream storeStream; + /** The key store instance to use. */ + protected KeyStore store; + /** The output stream the concrete handler will use. */ + protected OutputStream outStream; + /** Whether we are printing to System.out. */ + protected boolean systemOut; + /** The key-pair generation algorithm instance to use. */ + protected KeyPairGenerator keyPairGenerator; + /** The digital signature algorithm instance to use. */ + protected Signature signatureAlgorithm; + /** Validity period, in number of days, to use when generating certificates. */ + protected int validityInDays; + /** The input stream the concrete handler will use. */ + protected InputStream inStream; + /** Whether verbose output is required or not. */ + protected boolean verbose; + + /** MD5 hash to use when generating certificate fingerprints. */ + private IMessageDigest md5 = new MD5(); + /** SHA1 hash to use when generating certificate fingerprints. */ + private IMessageDigest sha = new Sha160(); + /** The new position of a user-defined provider if it is not already installed. */ + private int providerNdx = -2; + /** The callback handler to use when needing to interact with user. */ + private CallbackHandler handler; + /** The shutdown hook. */ + private ShutdownHook shutdownThread; + + // Constructor(s) ----------------------------------------------------------- + + protected Command() + { + super(); + shutdownThread = new ShutdownHook(); + Runtime.getRuntime().addShutdownHook(shutdownThread); + } + + // Methods ------------------------------------------------------------------ + + /** + * A public method to allow using any keytool command handler programmatically + * by using a JavaBeans style of parameter(s) initialization. The user is + * assumed to have set individually the required options through their + * respective setters before invoking this method. + * <p> + * If an exception is encountered during the processing of the command, this + * implementation attempts to release any resources that may have been + * allocated at the time the exception occurs, before re-throwing that + * exception. + * + * @throws Exception if an exception occurs during the processing of this + * command. For a more comprehensive list of exceptions that may + * occur, see the documentation of the {@link #setup()} and + * {@link #start()} methods. + */ + public void doCommand() throws Exception + { + try + { + setup(); + start(); + } + finally + { + teardown(); + if (shutdownThread != null) + Runtime.getRuntime().removeShutdownHook(shutdownThread); + } + } + + /** + * @param flag whether to use, or not, more verbose output while processing + * the command. + */ + public void setVerbose(String flag) + { + this.verbose = Boolean.valueOf(flag).booleanValue(); + } + + // life-cycle methods ------------------------------------------------------- + + /** + * Given a potential sub-array of options for this concrete handler, starting + * at position <code>startIndex + 1</code>, potentially followed by other + * commands and their options, this method sets up this concrete command + * handler with its own options and returns the index of the first unprocessed + * argument in the array. + * <p> + * The general contract of this method is that it is invoked with the + * <code>startIndex</code> argument pointing to the keyword argument that + * uniquelly identifies the command itself; e.g. <code>-genkey</code> or + * <code>-list</code>, etc... + * + * @param args an array of options for this handler and possibly other + * commands and their options. + * @return the remaining un-processed <code>args</code>. + */ + String[] processArgs(String[] args) + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "processArgs", args); //$NON-NLS-1$ + Parser cmdOptionsParser = getParser(); + String[] result = cmdOptionsParser.parse(args); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "processArgs", result); //$NON-NLS-1$ + return result; + } + + /** + * Initialize this concrete command handler for later invocation of the + * {@link #start()} or {@link #doCommand()} methods. + * <p> + * Handlers usually initialize their local variables and resources within the + * scope of this call. + * + * @throws IOException if an I/O related exception, such as opening an input + * stream, occurs during the execution of this method. + * @throws UnsupportedCallbackException if a requested callback handler + * implementation was not found, or was found but encountered an + * exception during its processing. + * @throws ClassNotFoundException if a designated security provider class was + * not found. + * @throws IllegalAccessException no 0-arguments constructor for the + * designated security provider class was found. + * @throws InstantiationException the designated security provider class is + * not instantiable. + * @throws KeyStoreException if an exception occurs during the instantiation + * of the KeyStore. + * @throws CertificateException if a certificate related exception, such as + * expiry, occurs during the loading of the KeyStore. + * @throws NoSuchAlgorithmException if no current security provider can + * provide a needed algorithm referenced by the KeyStore or one of + * its Key Entries or Certificates. + */ + abstract void setup() throws Exception; + + /** + * Do the real work this handler is supposed to do. + * <p> + * The code in this (abstract) class throws a <i>Not implemented yet</i> + * runtime exception. Concrete implementations MUST override this method. + * + * @throws CertificateException If no concrete implementation was found for a + * certificate Factory of a designated type. In this tool, the type + * is usually X.509 v1. + * @throws KeyStoreException if a keys-store related exception occurs; e.g. + * the key store has not been initialized. + * @throws IOException if an I/O related exception occurs during the process. + * @throws SignatureException if a digital signature related exception occurs. + * @throws InvalidKeyException if the genereated keys are invalid. + * @throws UnrecoverableKeyException if the password used to unlock a key in + * the key store was invalid. + * @throws NoSuchAlgorithmException if a concrete implementation of an + * algorithm used to store a Key Entry was not found at runtime. + * @throws UnsupportedCallbackException if a requested callback handler + * implementation was not found, or was found but encountered an + * exception during its processing. + */ + void start() throws Exception + { + throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$ + } + + /** + * Tear down the handler, releasing any resources which may have been + * allocated at setup time. + */ + void teardown() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + if (storeStream != null) + try + { + storeStream.close(); + } + catch (IOException ignored) + { + if (Configuration.DEBUG) + log.fine("Exception while closing key store URL stream. Ignored: " //$NON-NLS-1$ + + ignored); + } + + if (outStream != null) + { + try + { + outStream.flush(); + } + catch (IOException ignored) + { + } + + if (! systemOut) + try + { + outStream.close(); + } + catch (IOException ignored) + { + } + } + + if (inStream != null) + try + { + inStream.close(); + } + catch (IOException ignored) + { + } + + if (providerNdx > 0) + ProviderUtil.removeProvider(provider.getName()); + + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + } + + // parameter setup and validation methods ----------------------------------- + + /** + * @return a {@link Parser} that knows how to parse the concrete command's + * options. + */ + abstract Parser getParser(); + + /** + * Convenience method to setup the key store given its type, its password, its + * location and portentially a specialized security provider. + * <p> + * Calls the method with the same name and 5 arguments passing + * <code>false</code> to the first argument implying that no attempt to + * create the keystore will be made if one was not found at the designated + * location. + * + * @param className the potentially null fully qualified class name of a + * security provider to add at runtime, if no installed provider is + * able to provide a key store implementation of the desired type. + * @param type the potentially null type of the key store to request from the + * key store factory. + * @param password the potentially null password protecting the key store. + * @param url the URL of the key store. + */ + protected void setKeyStoreParams(String className, String type, + String password, String url) + throws IOException, UnsupportedCallbackException, KeyStoreException, + NoSuchAlgorithmException, CertificateException + { + setKeyStoreParams(false, className, type, password, url); + } + + /** + * Convenience method to setup the key store given its type, its password, its + * location and portentially a specialized security provider. + * + * @param createIfNotFound if <code>true</code> then create the keystore if + * it was not found; otherwise do not. + * @param className the potentially null fully qualified class name of a + * security provider to add at runtime, if no installed provider is + * able to provide a key store implementation of the desired type. + * @param type the potentially null type of the key store to request from the + * key store factory. + * @param password the potentially null password protecting the key store. + * @param url the URL of the key store. + */ + protected void setKeyStoreParams(boolean createIfNotFound, String className, + String type, String password, String url) + throws IOException, UnsupportedCallbackException, KeyStoreException, + NoSuchAlgorithmException, CertificateException + { + setProviderClassNameParam(className); + setKeystoreTypeParam(type); + setKeystoreURLParam(createIfNotFound, url, password); + } + + /** + * Set a security provider class name to (install and) use for key store + * related operations. + * + * @param className the possibly null, fully qualified class name of a + * security provider to add, if it is not already installed, to the + * set of available providers. + */ + private void setProviderClassNameParam(String className) + { + if (Configuration.DEBUG) + log.fine("setProviderClassNameParam(" + className + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + if (className != null && className.trim().length() > 0) + { + className = className.trim(); + SecurityProviderInfo spi = ProviderUtil.addProvider(className); + provider = spi.getProvider(); + if (provider == null) + { + if (Configuration.DEBUG) + log.fine("Was unable to add provider from class " + className); + } + providerNdx = spi.getPosition(); + } + } + + /** + * Set the type of key store to initialize, load and use. + * + * @param type the possibly null type of the key store. if this argument is + * <code>null</code>, or is an empty string, then this method sets + * the type of the key store to be the default value returned from + * the invocation of the {@link KeyStore#getDefaultType()} method. + * For GNU Classpath this is <i>gkr</i> which stands for the "Gnu + * KeyRing" specifications. + */ + private void setKeystoreTypeParam(String type) + { + if (Configuration.DEBUG) + log.fine("setKeystoreTypeParam(" + type + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + if (type == null || type.trim().length() == 0) + storeType = KeyStore.getDefaultType(); + else + storeType = type.trim(); + } + + /** + * Set the key password given a command line option argument. If no value was + * present on the command line then prompt the user to provide one. + * + * @param password a possibly null key password gleaned from the command line. + * @throws IOException if an I/O related exception occurs. + * @throws UnsupportedCallbackException if no concrete implementation of a + * password callback was found at runtime. + */ + protected void setKeyPasswordParam(String password) throws IOException, + UnsupportedCallbackException + { + setKeyPasswordNoPrompt(password); + if (keyPasswordChars == null) + setKeyPasswordParam(); + } + + /** + * Set the Alias to use when associating Key Entries and Trusted Certificates + * in the current key store. + * + * @param name the possibly null alias to use. If this arfument is + * <code>null</code>, then a default value of <code>mykey</code> + * will be used instead. + */ + protected void setAliasParam(String name) + { + alias = name == null ? DEFAULT_ALIAS : name.trim(); + } + + /** + * Set the key password given a command line option argument. + * + * @param password a possibly null key password gleaned from the command line. + */ + protected void setKeyPasswordNoPrompt(String password) + { + if (password != null) + keyPasswordChars = password.toCharArray(); + } + + /** + * Prompt the user to provide a password to protect a Key Entry in the key + * store. + * + * @throws IOException if an I/O related exception occurs. + * @throws UnsupportedCallbackException if no concrete implementation of a + * password callback was found at runtime. + * @throws SecurityException if no password is available, even after prompting + * the user. + */ + private void setKeyPasswordParam() throws IOException, + UnsupportedCallbackException + { + String prompt = Messages.getFormattedString("Command.21", alias); //$NON-NLS-1$ + PasswordCallback pcb = new PasswordCallback(prompt, false); + getCallbackHandler().handle(new Callback[] { pcb }); + keyPasswordChars = pcb.getPassword(); + pcb.clearPassword(); + if (keyPasswordChars == null) + throw new SecurityException(Messages.getString("Command.23")); //$NON-NLS-1$ + } + + private void setKeystorePasswordParam(String password) throws IOException, + UnsupportedCallbackException + { + if (password != null) + storePasswordChars = password.toCharArray(); + else // ask the user to provide one + { + String prompt = Messages.getString("Command.24"); //$NON-NLS-1$ + PasswordCallback pcb = new PasswordCallback(prompt, false); + getCallbackHandler().handle(new Callback[] { pcb }); + storePasswordChars = pcb.getPassword(); + pcb.clearPassword(); + } + } + + /** + * Set the key store URL to use. + * + * @param createIfNotFound when <code>true</code> an attempt to create a + * keystore at the designated location will be made. If + * <code>false</code> then no file creation is carried out, which + * may cause an exception to be thrown later. + * @param url the full, or partial, URL to the keystore location. + * @param password an eventually null string to use when loading the keystore. + * @throws IOException + * @throws KeyStoreException + * @throws UnsupportedCallbackException + * @throws NoSuchAlgorithmException + * @throws CertificateException + */ + private void setKeystoreURLParam(boolean createIfNotFound, String url, + String password) throws IOException, + KeyStoreException, UnsupportedCallbackException, NoSuchAlgorithmException, + CertificateException + { + if (Configuration.DEBUG) + log.fine("setKeystoreURLParam(" + url + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + if (url == null || url.trim().length() == 0) + { + String userHome = SystemProperties.getProperty("user.home"); //$NON-NLS-1$ + if (userHome == null || userHome.trim().length() == 0) + throw new InvalidParameterException(Messages.getString("Command.36")); //$NON-NLS-1$ + + url = userHome.trim() + "/.keystore"; //$NON-NLS-1$ + // if it does not exist create it if required + if (createIfNotFound) + new File(url).createNewFile(); + url = "file:" + url; //$NON-NLS-1$ + } + else + { + url = url.trim(); + if (url.indexOf(":") == -1) // if it does not exist create it //$NON-NLS-1$ + { + if (createIfNotFound) + new File(url).createNewFile(); + } + url = "file:" + url; //$NON-NLS-1$ + } + + boolean newKeyStore = false; + storeURL = new URL(url); + storeStream = storeURL.openStream(); + if (storeStream.available() == 0) + { + if (Configuration.DEBUG) + log.fine("Store is empty. Will use <null> when loading, to create it"); //$NON-NLS-1$ + newKeyStore = true; + } + + try + { + store = KeyStore.getInstance(storeType); + } + catch (KeyStoreException x) + { + if (provider != null) + throw x; + + if (Configuration.DEBUG) + log.fine("Exception while getting key store with default provider(s)." //$NON-NLS-1$ + + " Will prompt user for another provider and continue"); //$NON-NLS-1$ + String prompt = Messages.getString("Command.40"); //$NON-NLS-1$ + NameCallback ncb = new NameCallback(prompt); + getCallbackHandler().handle(new Callback[] { ncb }); + String className = ncb.getName(); + setProviderClassNameParam(className); // we may have a Provider + if (provider == null) + { + x.fillInStackTrace(); + throw x; + } + // try again + store = KeyStore.getInstance(storeType, provider); + } + + setKeystorePasswordParam(password); + + // now we have a KeyStore instance. load it + // KeyStore public API claims: "...In order to create an empty keystore, + // you pass null as the InputStream argument to the load method. + if (newKeyStore) + store.load(null, storePasswordChars); + else + store.load(storeStream, storePasswordChars); + + // close the stream + try + { + storeStream.close(); + storeStream = null; + } + catch (IOException x) + { + if (Configuration.DEBUG) + log.fine("Exception while closing the key store input stream: " + x //$NON-NLS-1$ + + ". Ignore"); //$NON-NLS-1$ + } + } + + protected void setOutputStreamParam(String fileName) throws SecurityException, + IOException + { + if (fileName == null || fileName.trim().length() == 0) + { + outStream = System.out; + systemOut = true; + } + else + { + fileName = fileName.trim(); + File outFile = new File(fileName); + if (! outFile.exists()) + { + boolean ok = outFile.createNewFile(); + if (!ok) + throw new InvalidParameterException(Messages.getFormattedString("Command.19", //$NON-NLS-1$ + fileName)); + } + else + { + if (! outFile.isFile()) + throw new InvalidParameterException(Messages.getFormattedString("Command.42", //$NON-NLS-1$ + fileName)); + if (! outFile.canWrite()) + throw new InvalidParameterException(Messages.getFormattedString("Command.44", //$NON-NLS-1$ + fileName)); + } + outStream = new FileOutputStream(outFile); + } + } + + protected void setInputStreamParam(String fileName) + throws FileNotFoundException + { + if (fileName == null || fileName.trim().length() == 0) + inStream = System.in; + else + { + fileName = fileName.trim(); + File inFile = new File(fileName); + if (! (inFile.exists() && inFile.isFile() && inFile.canRead())) + throw new InvalidParameterException(Messages.getFormattedString("Command.46", //$NON-NLS-1$ + fileName)); + inStream = new FileInputStream(inFile); + } + } + + /** + * Set both the key-pair generation algorithm, and the digital signature + * algorithm instances to use when generating new entries. + * + * @param kpAlg the possibly null name of a key-pair generator algorithm. + * if this argument is <code>null</code> or is an empty string, the + * "DSS" algorithm will be used. + * @param sigAlg the possibly null name of a digital signature algorithm. + * If this argument is <code>null</code> or is an empty string, this + * method uses the "SHA1withDSA" (Digital Signature Standard, a.k.a. + * DSA, with the Secure Hash Algorithm function) as the default + * algorithm if, and only if, the key-pair generation algorithm ends + * up being "DSS"; otherwise, if the key-pair generation algorithm + * was "RSA", then the "MD5withRSA" signature algorithm will be used. + * If the key-pair generation algorithm is neither "DSS" (or its + * alias "DSA"), nor is it "RSA", then an exception is thrown. + * @throws NoSuchAlgorithmException if no concrete implementation of the + * designated algorithm is available. + */ + protected void setAlgorithmParams(String kpAlg, String sigAlg) + throws NoSuchAlgorithmException + { + if (kpAlg == null || kpAlg.trim().length() == 0) + kpAlg = DEFAULT_KEY_ALGORITHM; + else + kpAlg = kpAlg.trim().toLowerCase(); + + keyPairGenerator = KeyPairGenerator.getInstance(kpAlg); + + if (sigAlg == null || sigAlg.trim().length() == 0) + if (kpAlg.equalsIgnoreCase(Registry.DSS_KPG) + || kpAlg.equalsIgnoreCase(Registry.DSA_KPG)) + sigAlg = DSA_SIGNATURE_ALGORITHM; + else if (kpAlg.equalsIgnoreCase(Registry.RSA_KPG)) + sigAlg = RSA_SIGNATURE_ALGORITHM; + else + throw new IllegalArgumentException( + Messages.getFormattedString("Command.20", //$NON-NLS-1$ + new String[] { sigAlg, kpAlg })); + else + sigAlg = sigAlg.trim().toLowerCase(); + + signatureAlgorithm = Signature.getInstance(sigAlg); + } + + /** + * Set the signature algorithm to use when digitally signing private keys, + * certificates, etc... + * <p> + * If the designated algorithm name is <code>null</code> or is an empty + * string, this method checks the private key (the second argument) and based + * on its type decides which algorithm to use. The keytool public + * specification states that if the private key is a DSA key, then the + * signature algorithm will be <code>SHA1withDSA</code>, otherwise if it is + * an RSA private key, then the signature algorithm will be + * <code>MD5withRSA</code>. If the private key is neither a private DSA nor + * a private RSA key, then this method throws an + * {@link IllegalArgumentException}. + * + * @param algorithm the possibly null name of a digital signature algorithm. + * @param privateKey an instance of a private key to use as a fal-back option + * when <code>algorithm</code> is invalid. + * @throws NoSuchAlgorithmException if no concrete implementation of the + * designated, or default, signature algorithm is available. + */ + protected void setSignatureAlgorithmParam(String algorithm, Key privateKey) + throws NoSuchAlgorithmException + { + if (algorithm == null || algorithm.trim().length() == 0) + if (privateKey instanceof DSAKey) + algorithm = DSA_SIGNATURE_ALGORITHM; + else if (privateKey instanceof RSAKey) + algorithm = RSA_SIGNATURE_ALGORITHM; + else + throw new InvalidParameterException(Messages.getString("Command.48")); //$NON-NLS-1$ + else + algorithm = algorithm.trim(); + + signatureAlgorithm = Signature.getInstance(algorithm); + } + + /** + * Set the validity period, in number of days, to use when issuing new + * certificates. + * + * @param days the number of days, as a string, the generated certificate will + * be valid for, starting from today's date. if this argument is + * <code>null</code>, a default value of <code>90</code> days + * will be used. + * @throws NumberFormatException if the designated string is not a decimal + * integer. + * @throws InvalidParameterException if the integer value of the non-null + * string is not greater than zero. + */ + protected void setValidityParam(String days) + { + if (days == null || days.trim().length() == 0) + validityInDays = DEFAULT_VALIDITY; + else + { + days = days.trim(); + validityInDays = Integer.parseInt(days); + if (validityInDays < 1) + throw new InvalidParameterException(Messages.getString("Command.51")); //$NON-NLS-1$ + } + } + + /** + * RFC-2459 (http://rfc.net/rfc2459.html) fully describes the structure and + * semantics of X.509 certificates. The ASN.1 structures below are gleaned + * from that reference. + * + * <pre> + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + * } + * + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo + * } + * + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * + * CertificateSerialNumber ::= INTEGER + * + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time + * } + * + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime + * } + * + * UniqueIdentifier ::= BIT STRING + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING + * } + * </pre> + * + * @param distinguishedName the X.500 Distinguished Name to use as both the + * Issuer and Subject of the self-signed certificate to generate. + * @param publicKey the public key of the issuer/subject. + * @param privateKey the private key of the issuer/signer. + * @return the DER encoded form of a self-signed X.509 v1 certificate. + * @throws IOException If an I/O related exception occurs during the process. + * @throws SignatureException If a digital signature related exception occurs. + * @throws InvalidKeyException if the designated private key is invalid. + * @throws InvalidParameterException if the concrete signature algorithm does + * not know its name, no OID is known/supported for that name, or we + * were unable to match the name to a known string for which we can + * use a standard OID. + */ + protected byte[] getSelfSignedCertificate(X500DistinguishedName distinguishedName, + PublicKey publicKey, + PrivateKey privateKey) + throws IOException, SignatureException, InvalidKeyException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getSelfSignedCertificate", //$NON-NLS-1$ + new Object[] { distinguishedName, publicKey, privateKey }); + byte[] versionBytes = new DERValue(DER.INTEGER, BigInteger.ZERO).getEncoded(); + DERValue derVersion = new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 0, + versionBytes.length, versionBytes, null); + + // NOTE (rsn): the next 3 lines should be atomic but they're not. + Preferences prefs = Preferences.systemNodeForPackage(this.getClass()); + int lastSerialNumber = prefs.getInt(Main.LAST_SERIAL_NUMBER, 0) + 1; + prefs.putInt(Main.LAST_SERIAL_NUMBER, lastSerialNumber); + DERValue derSerialNumber = new DERValue(DER.INTEGER, + BigInteger.valueOf(lastSerialNumber)); + + OID signatureID = getSignatureAlgorithmOID(); + DERValue derSignatureID = new DERValue(DER.OBJECT_IDENTIFIER, signatureID); + ArrayList signature = new ArrayList(1); + signature.add(derSignatureID); + // rfc-2459 states the following: + // + // for the DSA signature: + // ...Where the id-dsa-with-sha1 algorithm identifier appears as the + // algorithm field in an AlgorithmIdentifier, the encoding shall omit + // the parameters field. That is, the AlgorithmIdentifier shall be a + // SEQUENCE of one component - the OBJECT IDENTIFIER id-dsa-with-sha1. + // + // for RSA signatures: + // ...When any of these three OIDs (i.e. xxxWithRSAEncryption) appears + // within the ASN.1 type AlgorithmIdentifier, the parameters component of + // that type shall be the ASN.1 type NULL. + if (! signatureID.equals(SHA1_WITH_DSA)) + signature.add(new DERValue(DER.NULL, null)); + + DERValue derSignature = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + signature); + + DERValue derIssuer = new DERReader(distinguishedName.getDer()).read(); + + long notBefore = System.currentTimeMillis(); + long notAfter = notBefore + validityInDays * MILLIS_IN_A_DAY; + + ArrayList validity = new ArrayList(2); + validity.add(new DERValue(DER.UTC_TIME, new Date(notBefore))); + validity.add(new DERValue(DER.UTC_TIME, new Date(notAfter))); + DERValue derValidity = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + validity); + + // for a self-signed certificate subject and issuer are identical + DERValue derSubject = derIssuer; + + DERValue derSubjectPublicKeyInfo = new DERReader(publicKey.getEncoded()).read(); + + ArrayList tbsCertificate = new ArrayList(7); + tbsCertificate.add(derVersion); + tbsCertificate.add(derSerialNumber); + tbsCertificate.add(derSignature); + tbsCertificate.add(derIssuer); + tbsCertificate.add(derValidity); + tbsCertificate.add(derSubject); + tbsCertificate.add(derSubjectPublicKeyInfo); + DERValue derTBSCertificate = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + tbsCertificate); + + // The 'signature' field MUST contain the same algorithm identifier as the + // 'signatureAlgorithm' field in the sequence Certificate. + DERValue derSignatureAlgorithm = derSignature; + + signatureAlgorithm.initSign(privateKey); + signatureAlgorithm.update(derTBSCertificate.getEncoded()); + byte[] sigBytes = signatureAlgorithm.sign(); + DERValue derSignatureValue = new DERValue(DER.BIT_STRING, + new BitString(sigBytes)); + + ArrayList certificate = new ArrayList(3); + certificate.add(derTBSCertificate); + certificate.add(derSignatureAlgorithm); + certificate.add(derSignatureValue); + DERValue derCertificate = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + certificate); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DERWriter.write(baos, derCertificate); + byte[] result = baos.toByteArray(); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getSelfSignedCertificate"); //$NON-NLS-1$ + return result; + } + + /** + * This method attempts to find, and return, an OID representing the digital + * signature algorithm used to sign the certificate. The OIDs returned are + * those described in RFC-2459. They are listed here for the sake of + * completness. + * + * <pre> + * id-dsa-with-sha1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) x9-57 (10040) x9cm(4) 3 + * } + * + * md2WithRSAEncryption OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 2 + * } + * + * md5WithRSAEncryption OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 4 + * } + * + * sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 5 + * } + * </pre> + * + * <b>IMPORTANT</b>: This method checks the signature algorithm name against + * (a) The GNU algorithm implementation's name, and (b) publicly referenced + * names of the same algorithm. In other words this search is not + * comprehensive and may fail for uncommon names of the same algorithms. + * + * @return the OID of the signature algorithm in use. + * @throws InvalidParameterException if the concrete signature algorithm does + * not know its name, no OID is known/supported for that name, or we + * were unable to match the name to a known string for which we can + * return an OID. + */ + protected OID getSignatureAlgorithmOID() + { + String algorithm = signatureAlgorithm.getAlgorithm(); + // if we already have a non-null signature then the name was valid. the + // only case where algorithm is invalid would be if the implementation is + // flawed. check anyway + if (algorithm == null || algorithm.trim().length() == 0) + throw new InvalidParameterException(Messages.getString("Command.52")); //$NON-NLS-1$ + + algorithm = algorithm.trim(); + if (algorithm.equalsIgnoreCase(Registry.DSS_SIG) + || algorithm.equalsIgnoreCase("SHA1withDSA")) //$NON-NLS-1$ + return SHA1_WITH_DSA; + + if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$ + + Registry.MD2_HASH) + || algorithm.equalsIgnoreCase("MD2withRSA")) //$NON-NLS-1$ + return MD2_WITH_RSA; + + if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$ + + Registry.MD5_HASH) + || algorithm.equalsIgnoreCase("MD5withRSA") //$NON-NLS-1$ + || algorithm.equalsIgnoreCase("rsa")) //$NON-NLS-1$ + return MD5_WITH_RSA; + + if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$ + + Registry.SHA160_HASH) + || algorithm.equalsIgnoreCase("SHA1withRSA")) //$NON-NLS-1$ + return SHA1_WITH_RSA; + + throw new InvalidParameterException(Messages.getFormattedString("Command.60", //$NON-NLS-1$ + algorithm)); + } + + /** + * Saves the key store using the designated password. This operation is called + * by handlers if/when the key store password has changed, or amendements have + * been made to the contents of the store; e.g. addition of a new Key Entry or + * a Trusted Certificate. + * + * @param password the password protecting the key store. + * @throws IOException if an I/O related exception occurs during the process. + * @throws CertificateException if any of the certificates in the current key + * store could not be persisted. + * @throws NoSuchAlgorithmException if a required data integrity algorithm + * implementation was not found. + * @throws KeyStoreException if the key store has not been loaded previously. + */ + protected void saveKeyStore(char[] password) throws IOException, + KeyStoreException, NoSuchAlgorithmException, CertificateException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "saveKeyStore"); //$NON-NLS-1$ + URLConnection con = storeURL.openConnection(); + con.setDoOutput(true); + con.setUseCaches(false); + OutputStream out = con.getOutputStream(); + if (verbose) + System.out.println(Messages.getFormattedString("Command.63", storeURL.getPath())); //$NON-NLS-1$ + + store.store(out, password); + out.flush(); + out.close(); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "saveKeyStore"); //$NON-NLS-1$ + } + + /** + * Convenience method. Calls the method with the same name passing it the + * same password characters used to initially load the key-store. + * + * @throws IOException if an I/O related exception occurs during the process. + * @throws KeyStoreException if the key store has not been loaded previously. + * @throws NoSuchAlgorithmException if a required data integrity algorithm + * implementation was not found. + * @throws CertificateException if any of the certificates in the current key + * store could not be persisted. + */ + protected void saveKeyStore() throws IOException, KeyStoreException, + NoSuchAlgorithmException, CertificateException + { + saveKeyStore(storePasswordChars); + } + + /** + * Prints a human-readable form of the designated certificate to a designated + * {@link PrintWriter}. + * + * @param certificate the certificate to process. + * @param writer where to print it. + * @throws CertificateEncodingException if an exception occurs while obtaining + * the DER encoded form <code>certificate</code>. + */ + protected void printVerbose(Certificate certificate, PrintWriter writer) + throws CertificateEncodingException + { + X509Certificate x509 = (X509Certificate) certificate; + writer.println(Messages.getFormattedString("Command.66", x509.getSubjectDN())); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("Command.67", x509.getIssuerDN())); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("Command.68", x509.getSerialNumber())); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("Command.69", x509.getNotBefore())); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("Command.70", x509.getNotAfter())); //$NON-NLS-1$ + writer.println(Messages.getString("Command.71")); //$NON-NLS-1$ + byte[] derBytes = certificate.getEncoded(); + writer.println(Messages.getFormattedString("Command.72", digest(md5, derBytes))); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("Command.73", digest(sha, derBytes))); //$NON-NLS-1$ + } + + /** + * Convenience method. Prints a human-readable form of the designated + * certificate to <code>System.out</code>. + * + * @param certificate the certificate to process. + * @throws CertificateEncodingException if an exception occurs while obtaining + * the DER encoded form <code>certificate</code>. + */ + protected void printVerbose(Certificate certificate) + throws CertificateEncodingException + { + printVerbose(certificate, new PrintWriter(System.out, true)); + } + + /** + * Digest the designated contents with MD5 and return a string representation + * suitable for use as a fingerprint; i.e. sequence of hexadecimal pairs of + * characters separated by a colon. + * + * @param contents the non-null contents to digest. + * @return a sequence of hexadecimal pairs of characters separated by colons. + */ + protected String digestWithMD5(byte[] contents) + { + return digest(md5, contents); + } + + private String digest(IMessageDigest hash, byte[] encoded) + { + hash.update(encoded); + byte[] b = hash.digest(); + StringBuilder sb = new StringBuilder().append(Util.toString(b, 0, 1)); + for (int i = 1; i < b.length; i++) + sb.append(":").append(Util.toString(b, i, 1)); //$NON-NLS-1$ + + String result = sb.toString(); + return result; + } + + /** + * Ensure that the currently set Alias is contained in the currently set key + * store; otherwise throw an exception. + * + * @throws KeyStoreException if the keystore has not been loaded. + * @throws IllegalArgumentException if the currently set alias is not known to + * the currently set key store. + */ + protected void ensureStoreContainsAlias() throws KeyStoreException + { + if (! store.containsAlias(alias)) + throw new IllegalArgumentException(Messages.getFormattedString("Command.75", //$NON-NLS-1$ + alias)); + } + + /** + * Ensure that the currently set Alias is associated with a Key Entry in the + * currently set key store; otherwise throw an exception. + * + * @throws KeyStoreException if the keystore has not been loaded. + * @throws SecurityException if the currently set alias is not a Key Entry in + * the currently set key store. + */ + protected void ensureAliasIsKeyEntry() throws KeyStoreException + { + if (! store.isKeyEntry(alias)) + throw new SecurityException(Messages.getFormattedString("Command.77", //$NON-NLS-1$ + alias)); + } + + protected Key getAliasPrivateKey() throws KeyStoreException, + NoSuchAlgorithmException, IOException, UnsupportedCallbackException, + UnrecoverableKeyException + { + ensureAliasIsKeyEntry(); + Key result; + if (keyPasswordChars == null) + try + { + result = store.getKey(alias, storePasswordChars); + // it worked. assign to keyPasswordChars for later use + keyPasswordChars = storePasswordChars; + } + catch (UnrecoverableKeyException x) + { + // prompt the user to provide one + setKeyPasswordParam(); + result = store.getKey(alias, keyPasswordChars); + } + else + result = store.getKey(alias, keyPasswordChars); + + return result; + } + + /** + * Return a CallbackHandler which uses the Console (System.in and System.out) + * for interacting with the user. + * <p> + * This method first finds all currently installed security providers capable + * of providing such service and then in turn attempts to instantiate the + * handler from those providers. As soon as one provider returns a non-null + * instance of the callback handler, the search stops and that instance is + * set to be used from now on. + * <p> + * If no installed providers were found, this method falls back on the GNU + * provider, by-passing the Security search mechanism. The default console + * callback handler implementation is + * {@link gnu.javax.security.auth.callback.ConsoleCallbackHandler}. + * + * @return a console-based {@link CallbackHandler}. + */ + protected CallbackHandler getCallbackHandler() + { + if (handler == null) + handler = CallbackUtil.getConsoleHandler(); + + return handler; + } + + // Inner class(es) ========================================================== + + private class ShutdownHook + extends Thread + { + public void run() + { + teardown(); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/DeleteCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/DeleteCmd.java new file mode 100644 index 000000000..5a9cbfd1f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/DeleteCmd.java @@ -0,0 +1,280 @@ +/* DeleteCmd.java -- The delete command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; + +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-delete</b> keytool command handler is used to delete from the key + * store the entry associated with a designated alias. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class DeleteCmd extends Command +{ + private static final Logger log = Logger.getLogger(DeleteCmd.class.getName()); + protected String _alias; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setTheAlias(_alias); + if (Configuration.DEBUG) + { + log.fine("-delete handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + ensureStoreContainsAlias(); + store.deleteEntry(alias); + saveKeyStore(); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.DELETE_CMD, true); + result.setHeader(Messages.getString("DeleteCmd.18")); //$NON-NLS-1$ + result.setFooter(Messages.getString("DeleteCmd.17")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("DeleteCmd.16")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("DeleteCmd.15"), //$NON-NLS-1$ + Messages.getString("DeleteCmd.14")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("DeleteCmd.13"), //$NON-NLS-1$ + Messages.getString("DeleteCmd.12")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("DeleteCmd.11"), //$NON-NLS-1$ + Messages.getString("DeleteCmd.10")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("DeleteCmd.9"), //$NON-NLS-1$ + Messages.getString("DeleteCmd.8")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("DeleteCmd.7"), //$NON-NLS-1$ + Messages.getString("DeleteCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("DeleteCmd.5")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + /** + * Set the alias to delete from the key store. + * <p> + * Unlike in other keytool handlers, the default value (<i>mykey</i>) for the + * Alias is not used. Instead, if an alias was not found on the command line, + * the user is prompted to enter one. + * + * @param anAlias a possibly null Alias gleaned from the command line. + * @throws IOException if an I/O related exception occurs during the process. + * @throws UnsupportedCallbackException if no implementation of a password + * callback handler was found. + */ + private void setTheAlias(String anAlias) throws IOException, + UnsupportedCallbackException + { + if (anAlias == null || anAlias.trim().length() == 0) + { + String prompt = Messages.getString("DeleteCmd.19"); //$NON-NLS-1$ + NameCallback ncb = new NameCallback(prompt); + getCallbackHandler().handle(new Callback[] { ncb }); + anAlias = ncb.getName(); + if (anAlias == null || anAlias.trim().length() == 0) + throw new SecurityException(Messages.getString("DeleteCmd.20")); //$NON-NLS-1$ + } + alias = anAlias.trim(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/ExportCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/ExportCmd.java new file mode 100644 index 000000000..035fbab6a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/ExportCmd.java @@ -0,0 +1,328 @@ +/* ExportCmd.java -- The export command handler of the keytool + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.util.Base64; + +import java.io.IOException; +import java.io.PrintWriter; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.util.logging.Logger; + +/** + * The <b>-export</b> keytool command handler is used to read the certificate + * associated with a designated alias from the key store, and write it to a + * designated file. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the file where the certificate will be + * exported to. If omitted, STDOUT will be used instead. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-rfc</dt> + * <dd>Use RFC-1421 specifications when encoding the output. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Output the certificate in binary DER encoding. This is the default + * output format of the command if neither <code>-rfc</code> nor + * <code>-v</code> options were detected on the command line. If both this + * option and the <code>-rfc</code> option are detected on the command + * line, the tool will opt for the RFC-1421 style encoding.</dd> + * </dl> + */ +class ExportCmd extends Command +{ + private static final Logger log = Logger.getLogger(ExportCmd.class.getName()); + protected String _alias; + protected String _certFileName; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + protected boolean rfc; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certFileName = pathName; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + /** + * @param flag whether to use, or not, RFC-1421 format when exporting the + * certificate(s). + */ + public void setRfc(String flag) + { + this.rfc = Boolean.valueOf(flag).booleanValue(); + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setOutputStreamParam(_certFileName); + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + if (Configuration.DEBUG) + { + log.fine("-export handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -file=" + _certFileName); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -rfc=" + rfc); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws KeyStoreException, CertificateEncodingException, + IOException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + ensureStoreContainsAlias(); + Certificate certificate; + if (store.isCertificateEntry(alias)) + { + if (Configuration.DEBUG) + log.fine("Alias [" + alias + "] is a trusted certificate"); //$NON-NLS-1$ //$NON-NLS-2$ + certificate = store.getCertificate(alias); + } + else + { + if (Configuration.DEBUG) + log.fine("Alias [" + alias + "] is a key entry"); //$NON-NLS-1$ //$NON-NLS-2$ + Certificate[] chain = store.getCertificateChain(alias); + certificate = chain[0]; + } + + byte[] derBytes = certificate.getEncoded(); + if (rfc) + { + String encoded = Base64.encode(derBytes, 72); + PrintWriter pw = new PrintWriter(outStream, true); + pw.println("-----BEGIN CERTIFICATE-----"); //$NON-NLS-1$ + pw.println(encoded); + pw.println("-----END CERTIFICATE-----"); //$NON-NLS-1$ + } + else + outStream.write(derBytes); + + // stream is closed in Command.teardown() + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.EXPORT_CMD, true); + result.setHeader(Messages.getString("ExportCmd.17")); //$NON-NLS-1$ + result.setFooter(Messages.getString("ExportCmd.18")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("ExportCmd.19")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("ExportCmd.20"), //$NON-NLS-1$ + Messages.getString("ExportCmd.21")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.FILE_OPT, + Messages.getString("ExportCmd.22"), //$NON-NLS-1$ + Messages.getString("ExportCmd.23")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _certFileName = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("ExportCmd.24"), //$NON-NLS-1$ + Messages.getString("ExportCmd.25")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("ExportCmd.26"), //$NON-NLS-1$ + Messages.getString("ExportCmd.27")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("ExportCmd.28"), //$NON-NLS-1$ + Messages.getString("ExportCmd.29")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("ExportCmd.30"), //$NON-NLS-1$ + Messages.getString("ExportCmd.31")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.RFC_OPT, + Messages.getString("ExportCmd.32")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + rfc = true; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("ExportCmd.33")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/GenKeyCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/GenKeyCmd.java new file mode 100644 index 000000000..b892ca794 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/GenKeyCmd.java @@ -0,0 +1,602 @@ +/* GenKeyCmd.java -- The genkey command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.security.util.Util; +import gnu.java.security.x509.X500DistinguishedName; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.TextInputCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-genkey</b> keytool command handler is used to generate a key pair (a + * public, and associated private keys). It then generates a self-signed X509 v1 + * certificate (authenticating the public key) and stores this certificate and + * the private key in the key store associating both to a designated alias. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-keyalg ALGORITHM</dt> + * <dd>Use this option to specify the canonical name of the key-pair + * generation algorithm. The default value for this option is + * <code>DSS</code> (a synonym for the Digital Signature Algorithm also + * known as <code>DSA</code>). + * <p></dd> + * + * <dt>-keysize KEY_SIZE</dt> + * <dd>Use this option to specify the number of bits of the shared modulus + * (for both the public and private keys) to use when generating new keys. + * A default value of <code>1024</code> will be used if this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-sigalg ALGORITHM</dt> + * <dd>The canonical name of the digital signature algorithm to use for + * signing certificates. If this option is omitted, a default value will be + * chosen based on the type of the key-pair; i.e. the algorithm that ends + * up being used by the <code>-keyalg</code> option. If the key-pair + * generation algorithm is <code>DSA</code>, the value for the signature + * algorithm will be <code>SHA1withDSA</code>. If on the other hand the + * key-pair generation algorithm is <code>RSA</code>, then the tool will + * use <code>MD5withRSA</code> as the signature algorithm. + * <p></dd> + * + * <dt>-dname NAME</dt> + * <dd>This a mandatory value for this command. If this option is omitted + * the tool will prompt you to enter a <i>Distinguished Name</i> to use as + * both the <i>Owner</i> and <i>Issuer</i> of the generated self-signed + * certificate. + * <p> + * The syntax of a valid value for this option MUST follow RFC-2253 + * specifications. Namely the following components (with their accepted + * meaning) will be recognized. Note that the component name is case- + * insensitive: + * <dl> + * <dt>CN</dt> + * <dd>The Common Name; e.g. "host.domain.com"</dd> + * + * <dt>OU</dt> + * <dd>The Organizational Unit; e.g. "IT Department"</dd> + * + * <dt>O</dt> + * <dd>The Organization Name; e.g. "The Sample Company"</dd> + * + * <dt>L</dt> + * <dd>The Locality Name; e.g. "Sydney"</dd> + * + * <dt>ST</dt> + * <dd>The State Name; e.g. "New South Wales"</dd> + * + * <dt>C</dt> + * <dd>The 2-letter Country identifier; e.g. "AU"</dd> + * </dl> + * <p> + * When specified with a <code>-dname</code> option, each pair of component + * / value will be separated from the other with a comma. Each component + * and value pair MUST be separated by an equal sign. For example, the + * following is a valid DN value: + * <pre> + * CN=host.domain.com, O=The Sample Company, L=Sydney, ST=NSW, C=AU + * </pre> + * If this option is omitted, the tool will prompt you to enter the + * information through the console. + * <p></dd> + * + * <dt>-keypass PASSWORD</dt> + * <dd>Use this option to specify the password which the tool will use to + * protect the newly created Key Entry. + * <p> + * If this option is omitted, you will be prompted to provide a password. + * <p></dd> + * + * <dt>-validity DAY_COUNT</dt> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class GenKeyCmd extends Command +{ + private static final Logger log = Logger.getLogger(GenKeyCmd.class.getName()); + /** Default key size in bits. */ + private static final int DEFAULT_KEY_SIZE = 1024; + + protected String _alias; + protected String _keyAlgorithm; + protected String _keySizeStr; + protected String _sigAlgorithm; + protected String _dName; + protected String _password; + protected String _validityStr; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + private int keySize; + private X500DistinguishedName distinguishedName; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param algorithm the canonical name of the key-pair algorithm to use. */ + public void setKeyalg(String algorithm) + { + this._keyAlgorithm = algorithm; + } + + /** + * @param bits the string representation of the number of bits (a decimal + * positive integer) the modulus of the generated keys (private and + * public) should have. + */ + public void setKeysize(String bits) + { + this._validityStr = bits; + } + + /** + * @param algorithm the canonical name of the digital signature algorithm to + * use. + */ + public void setSigalg(String algorithm) + { + this._sigAlgorithm = algorithm; + } + + /** @param name the distiniguished name to use. */ + public void setDname(String name) + { + this._dName = name; + } + + /** @param password the (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** + * @param days the string representation of the number of days (a decimal, + * positive integer) to assign to the generated certificate. + */ + public void setValidity(String days) + { + this._validityStr = days; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setKeyStoreParams(true, _providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordParam(_password); + setAlgorithmParams(_keyAlgorithm, _sigAlgorithm); + setKeySize(_keySizeStr); + setDName(_dName); + setValidityParam(_validityStr); + if (Configuration.DEBUG) + { + log.fine("-genkey handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -keyalg=" + keyPairGenerator.getAlgorithm()); //$NON-NLS-1$ + log.fine(" -keysize=" + keySize); //$NON-NLS-1$ + log.fine(" -sigalg=" + signatureAlgorithm.getAlgorithm()); //$NON-NLS-1$ + log.fine(" -dname=" + distinguishedName); //$NON-NLS-1$ + log.fine(" -validity=" + validityInDays); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws CertificateException, KeyStoreException, + InvalidKeyException, SignatureException, IOException, + NoSuchAlgorithmException + { + if (Configuration.DEBUG) + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + log.fine("About to generate key-pair..."); //$NON-NLS-1$ + } + // 1. generate a new key-pair + keyPairGenerator.initialize(keySize); + KeyPair kp = keyPairGenerator.generateKeyPair(); + PublicKey publicKey = kp.getPublic(); + PrivateKey privateKey = kp.getPrivate(); + + // 2. generate a self-signed certificate + if (Configuration.DEBUG) + log.fine("About to generate a self-signed certificate..."); //$NON-NLS-1$ + byte[] derBytes = getSelfSignedCertificate(distinguishedName, + publicKey, + privateKey); + if (Configuration.DEBUG) + log.fine(Util.dumpString(derBytes, "derBytes ")); //$NON-NLS-1$ + CertificateFactory x509Factory = CertificateFactory.getInstance(Main.X_509); + ByteArrayInputStream bais = new ByteArrayInputStream(derBytes); + Certificate certificate = x509Factory.generateCertificate(bais); + if (Configuration.DEBUG) + log.fine("certificate = " + certificate); //$NON-NLS-1$ + + // 3. store it, w/ its private key, associating them to alias + Certificate[] chain = new Certificate[] { certificate }; + if (Configuration.DEBUG) + log.fine("About to store newly generated material in key store..."); //$NON-NLS-1$ + store.setKeyEntry(alias, privateKey, keyPasswordChars, chain); + + // 4. persist the key store + saveKeyStore(); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.GENKEY_CMD, true); + result.setHeader(Messages.getString("GenKeyCmd.57")); //$NON-NLS-1$ + result.setFooter(Messages.getString("GenKeyCmd.58")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("GenKeyCmd.59")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("GenKeyCmd.60"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.61")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.KEYALG_OPT, + Messages.getString("GenKeyCmd.62"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.63")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _keyAlgorithm = argument; + } + }); + options.add(new Option(Main.KEYSIZE_OPT, + Messages.getString("GenKeyCmd.64"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.65")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _keySizeStr = argument; + } + }); + options.add(new Option(Main.SIGALG_OPT, + Messages.getString("GenKeyCmd.66"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.63")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _sigAlgorithm = argument; + } + }); + options.add(new Option(Main.DNAME_OPT, + Messages.getString("GenKeyCmd.68"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.69")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _dName = argument; + } + }); + options.add(new Option(Main.KEYPASS_OPT, + Messages.getString("GenKeyCmd.70"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.71")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _password = argument; + } + }); + options.add(new Option(Main.VALIDITY_OPT, + Messages.getString("GenKeyCmd.72"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.73")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _validityStr = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("GenKeyCmd.74"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.75")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("GenKeyCmd.76"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.77")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("GenKeyCmd.78"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.71")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("GenKeyCmd.80"), //$NON-NLS-1$ + Messages.getString("GenKeyCmd.81")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("GenKeyCmd.82")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + /** + * @param size the desired key size as a string. + * @throws NumberFormatException if the string does not represent a valid + * decimal integer value. + */ + private void setKeySize(String size) + { + if (size == null || size.trim().length() == 0) + this.keySize = DEFAULT_KEY_SIZE; + else + { + size = size.trim(); + keySize = Integer.parseInt(size); + // When generating a DSA key pair, the key size must be in the range + // from 512 to 1024 bits, and must be a multiple of 64. The default + // key size for any algorithm is 1024 bits + if (keySize < 1) + throw new IllegalArgumentException(Messages.getString("GenKeyCmd.54")); //$NON-NLS-1$ + } + } + + /** + * @param name the X.500 distinguished name of the principal for whom the + * key/certificate are being generated. + * @throws UnsupportedCallbackException if no implementation of a name + * callback is available. + * @throws IOException if an I/O related exception occurs during the process. + * @throws IllegalArgumentException if the designated, or captured, value is + * not a valid X.500 distinguished name. + */ + private void setDName(String name) throws IOException, + UnsupportedCallbackException + { + if (name != null && name.trim().length() > 0) + name = name.trim(); + else + { + // prompt user to provide one + String dnTxt = Messages.getString("GenKeyCmd.0"); //$NON-NLS-1$ + String oDefault = Messages.getString("GenKeyCmd.6"); //$NON-NLS-1$ + String lDefault = Messages.getString("GenKeyCmd.7"); //$NON-NLS-1$ + String stDefault = Messages.getString("GenKeyCmd.8"); //$NON-NLS-1$ + String cDefault = Messages.getString("GenKeyCmd.9"); //$NON-NLS-1$ + String cnPrompt = Messages.getString("GenKeyCmd.10"); //$NON-NLS-1$ + String oPrompt = Messages.getFormattedString("GenKeyCmd.11", oDefault); //$NON-NLS-1$ + String ouPrompt = Messages.getString("GenKeyCmd.13"); //$NON-NLS-1$ + String lPrompt = Messages.getFormattedString("GenKeyCmd.14", lDefault); //$NON-NLS-1$ + String stPrompt = Messages.getFormattedString("GenKeyCmd.16", stDefault); //$NON-NLS-1$ + String cPrompt = Messages.getFormattedString("GenKeyCmd.18", cDefault); //$NON-NLS-1$ + + TextOutputCallback dnCB = new TextOutputCallback(TextOutputCallback.INFORMATION, + dnTxt); + TextInputCallback cnCB = new TextInputCallback(cnPrompt); + TextInputCallback oCB = new TextInputCallback(oPrompt, oDefault); + TextInputCallback ouCB = new TextInputCallback(ouPrompt); + TextInputCallback lCB = new TextInputCallback(lPrompt, lDefault); + TextInputCallback sCB = new TextInputCallback(stPrompt, stDefault); + TextInputCallback cCB = new TextInputCallback(cPrompt, cDefault); + getCallbackHandler().handle(new Callback[] { dnCB, cnCB, oCB, ouCB, lCB, sCB, cCB }); + StringBuilder sb = new StringBuilder(); + + // handle CN + name = parseUserPrompt(cnCB); + if (name != null && name.length() > 0) + sb.append("CN=").append(name); //$NON-NLS-1$ + + // handle O + name = parseUserPrompt(oCB); + if (name != null && name.length() > 0) + sb.append(",O=").append(name); //$NON-NLS-1$ + + // handle OU + name = parseUserPrompt(ouCB); + if (name != null && name.length() > 0) + sb.append(",OU=").append(name.trim()); //$NON-NLS-1$ + + // handle L + name = parseUserPrompt(lCB); + if (name != null && name.length() > 0) + sb.append(",L=").append(name.trim()); //$NON-NLS-1$ + + // handle ST + name = parseUserPrompt(sCB); + if (name != null && name.length() > 0) + sb.append(",ST=").append(name.trim()); //$NON-NLS-1$ + + // handle C + name = parseUserPrompt(cCB); + if (name != null && name.length() > 0) + sb.append(",C=").append(name.trim()); //$NON-NLS-1$ + + name = sb.toString().trim(); + } + if (Configuration.DEBUG) + log.fine("dName=[" + name + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + distinguishedName = new X500DistinguishedName(name); + } + + private String parseUserPrompt(TextInputCallback ticb) + { + String result = ticb.getText(); + if (result == null || result.trim().length() == 0) + result = ticb.getDefaultText(); + else if (result.trim().equals(".")) //$NON-NLS-1$ + result = null; + else + result = result.trim(); + + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/IdentityDBCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/IdentityDBCmd.java new file mode 100644 index 000000000..e52c90299 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/IdentityDBCmd.java @@ -0,0 +1,232 @@ +/* IdentityDBCmd.java -- The identitydb command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; + +import java.util.logging.Logger; + +/** + * <b>NOT IMPLEMENTED YET</b> + * <p> + * The <b>-identitydb</b> keytool command handler is used to read the JDK 1.1.x- + * style identity database and add its entries to the key store. If a key store + * does not exist, it is created. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the identity file to import. If this + * option is omitted, the tool will process STDIN. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class IdentityDBCmd extends Command +{ + private static final Logger log = Logger.getLogger(IdentityDBCmd.class.getName()); + protected String _idbFileName; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._idbFileName = pathName; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setInputStreamParam(_idbFileName); + setKeyStoreParams(true, _providerClassName, _ksType, _ksPassword, _ksURL); + if (Configuration.DEBUG) + { + log.fine("-identitydb handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -file=" + _idbFileName); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.IDENTITYDB_CMD, true); + result.setHeader(Messages.getString("IdentityDBCmd.7")); //$NON-NLS-1$ + result.setFooter(Messages.getString("IdentityDBCmd.8")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("IdentityDBCmd.9")); //$NON-NLS-1$ + options.add(new Option(Main.FILE_OPT, + Messages.getString("IdentityDBCmd.10"), //$NON-NLS-1$ + Messages.getString("IdentityDBCmd.11")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _idbFileName = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("IdentityDBCmd.12"), //$NON-NLS-1$ + Messages.getString("IdentityDBCmd.13")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("IdentityDBCmd.14"), //$NON-NLS-1$ + Messages.getString("IdentityDBCmd.15")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("IdentityDBCmd.16"), //$NON-NLS-1$ + Messages.getString("IdentityDBCmd.17")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("IdentityDBCmd.18"), //$NON-NLS-1$ + Messages.getString("IdentityDBCmd.19")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("IdentityDBCmd.20")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/ImportCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/ImportCmd.java new file mode 100644 index 000000000..b68760d09 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/ImportCmd.java @@ -0,0 +1,930 @@ +/* ImportCmd.java -- The import command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.SystemProperties; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.security.x509.X509CertPath; + +import java.io.FileInputStream; +import java.io.IOException; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXCertPathValidatorResult; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Collection; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.ConfirmationCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <code>-import</code> keytool command handler is used to read an X.509 + * certificate, or a PKCS#7 Certificate Reply from a designated input source and + * incorporate the certificates into the key store. + * <p> + * If the <i>Alias</i> does not already exist in the key store, the tool treats + * the certificate read from the input source as a new Trusted Certificate. It + * then attempts to discover a chain-of-trust, starting from that certificate + * and ending at another <i>Trusted Certificate</i>, already stored in the key + * store. If the <code>-trustcacerts</code> option is present, an additional + * key store, of type <code>JKS</code> named <code>cacerts</code>, and assumed + * to be present in <code>${JAVA_HOME}/lib/security</code> will also be + * consulted if found --<code>${JAVA_HOME}</code> refers to the location of an + * installed Java Runtime Environment (JRE). If no chain-of-trust can be + * established, and unless the <code>-noprompt</code> option has been specified, + * the certificate is printed to STDOUT and the user is prompted for a + * confirmation. + * <p> + * If <i>Alias</i> exists in the key store, the tool will treat the + * certificate(s) read from the input source as a <i>Certificate Reply</i>, + * which can be a chain of certificates, that eventually would replace the chain + * of certificates associated with the <i>Key Entry</i> of that <i>Alias</i>. + * The substitution of the certificates only occurs if a chain-of-trust can be + * established between the bottom certificate of the chain read from the input + * file and the <i>Trusted Certificates</i> already present in the key store. + * Again, if the <code>-trustcacerts</code> option is specified, additional + * <i>Trusted Certificates</i> in the same <code>cacerts</code> key store will + * be considered. If no chain-of-trust can be established, the operation will + * abort. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the file to read from. If omitted, the + * tool will process STDIN. + * <p></dd> + * + * <dt>-keypass PASSWORD</dt> + * <dd>Use this option to specify the password which the tool will use to + * protect the <i>Key Entry</i> associated with the designated <i>Alias</i>, + * when replacing this <i>Alias</i>' chain of certificates with that found + * in the certificate reply. + * <p> + * If this option is omitted, and the chain-of-trust for the certificate + * reply has been established, the tool will first attempt to unlock the + * <i>Key Entry</i> using the same password protecting the key store. If + * this fails, you will then be prompted to provide a password. + * <p></dd> + * + * <dt>-noprompt</dt> + * <dd>Use this option to prevent the tool from prompting the user. + * <p></dd> + * + * <dt>-trustcacerts</dt> + * <dd>Use this option to indicate to the tool that a key store, of type + * <code>JKS</code>, named <code>cacerts</code>, and usually located in + * <code>lib/security</code> in an installed Java Runtime Environment + * should be considered when trying to establish chain-of-trusts. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class ImportCmd extends Command +{ + private static final Logger log = Logger.getLogger(ImportCmd.class.getName()); + private static final String GKR = "gkr"; //$NON-NLS-1$ + private static final String JKS = "jks"; //$NON-NLS-1$ + private static final String LIB = "lib"; //$NON-NLS-1$ + private static final String SECURITY = "security"; //$NON-NLS-1$ + private static final String CACERTS = "cacerts"; //$NON-NLS-1$ + private static final String CACERTS_GKR = CACERTS + "." + GKR; //$NON-NLS-1$ + protected String _alias; + protected String _certFileName; + protected String _password; + protected boolean noPrompt; + protected boolean trustCACerts; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + private CertificateFactory x509Factory; + /** + * Pathname to a GKR-type cacerts file to use when trustCACerts is true. This + * is usually a file named "cacerts.gkr" located in lib/security in the folder + * specified by the system-property "gnu.classpath.home". + */ + private String gkrCaCertsPathName; + /** + * Pathname to a JKS-type cacerts file to use when trustCACerts is true. This + * is usually a file named "cacerts" located in lib/security in the folder + * specified by the system-property "java.home". + */ + private String jksCaCertsPathName; + /** Alias self-signed certificate. used when importing certificate replies. */ + private X509Certificate selfSignedCertificate; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the existing alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certFileName = pathName; + } + + /** @param password the existing (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** + * @param flag whether to prompt, or not, the user to verify certificate + * fingerprints. + */ + public void setNoprompt(String flag) + { + this.noPrompt = Boolean.valueOf(flag).booleanValue(); + } + + /** + * @param flag whether to trust, or not, certificates found in the + * <code>cacerts</code> key store. + */ + public void setTrustcacerts(String flag) + { + this.trustCACerts = Boolean.valueOf(flag).booleanValue(); + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setInputStreamParam(_certFileName); + setKeyStoreParams(true, _providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); + if (Configuration.DEBUG) + { + log.fine("-import handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -file=" + _certFileName); //$NON-NLS-1$ + log.fine(" -noprompt=" + noPrompt); //$NON-NLS-1$ + log.fine(" -trustcacerts=" + trustCACerts); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws CertificateException, KeyStoreException, IOException, + UnsupportedCallbackException, NoSuchAlgorithmException, + CertPathValidatorException, UnrecoverableKeyException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + if (trustCACerts) + { + String fs = SystemProperties.getProperty("file.separator"); //$NON-NLS-1$ + String classpathHome = SystemProperties.getProperty("gnu.classpath.home"); //$NON-NLS-1$ + gkrCaCertsPathName = new StringBuilder(classpathHome).append(fs) + .append(LIB).append(fs) + .append(SECURITY).append(fs) + .append(CACERTS_GKR).toString(); + String javaHome = SystemProperties.getProperty("java.home"); //$NON-NLS-1$ + jksCaCertsPathName = new StringBuilder(javaHome).append(fs) + .append(LIB).append(fs) + .append(SECURITY).append(fs) + .append(CACERTS).toString(); + } + x509Factory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + // the alias will tell us whether we're dealing with + // a new trusted certificate or a certificate reply + if (! store.containsAlias(alias)) + importNewTrustedCertificate(); + else + { + ensureAliasIsKeyEntry(); + importCertificateReply(); + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.IMPORT_CMD, true); + result.setHeader(Messages.getString("ImportCmd.27")); //$NON-NLS-1$ + result.setFooter(Messages.getString("ImportCmd.26")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("ImportCmd.25")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("ImportCmd.24"), //$NON-NLS-1$ + Messages.getString("ImportCmd.23")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.FILE_OPT, + Messages.getString("ImportCmd.22"), //$NON-NLS-1$ + Messages.getString("ImportCmd.21")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _certFileName = argument; + } + }); + options.add(new Option(Main.KEYPASS_OPT, + Messages.getString("ImportCmd.20"), //$NON-NLS-1$ + Messages.getString("ImportCmd.19")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _password = argument; + } + }); + options.add(new Option("noprompt", //$NON-NLS-1$ + Messages.getString("ImportCmd.18")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + noPrompt = true; + } + }); + options.add(new Option("trustcacerts", //$NON-NLS-1$ + Messages.getString("ImportCmd.17")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + trustCACerts = true; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("ImportCmd.16"), //$NON-NLS-1$ + Messages.getString("ImportCmd.15")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("ImportCmd.14"), //$NON-NLS-1$ + Messages.getString("ImportCmd.13")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("ImportCmd.12"), //$NON-NLS-1$ + Messages.getString("ImportCmd.11")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("ImportCmd.10"), //$NON-NLS-1$ + Messages.getString("ImportCmd.9")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("ImportCmd.8")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + /** + * When importing a new trusted certificate, <i>alias</i> MUST NOT yet exist + * in the key store. + * <p> + * Before adding the certificate to the key store and associate it with the + * designated Alias, this method tries to verify it by attempting to construct + * a chain of trust from that certificate to a self-signed certificate + * (belonging to a root CA), using (already) trusted certificates that are + * available in the key store. + * <p> + * If the <code>-trustcacerts</code> option was detected on the command + * line, additional trusted certificates are considered for establishing the + * chain of trust. Those additional certificates are assumed to be in a key + * store, of type <code>JKS</code> named <code>cacerts</code> and usually + * located in <code>${JAVA_HOME}/lib/security</code>, where + * <code>${JAVA_HOME}</code> is the root folder location of a Java runtime. + * <p> + * If this method fails to establish a trust path from the certificate to be + * imported up to a trusted self-signed certificate, the certificate is + * printed to <code>STDOUT</code>, and the user is prompted to verify it, + * with the option of aborting the import operation. If however the option + * <code>-noprompt</code> was detected on the command line, no interaction + * with the user will take place and the import operation will abort. + * + * @throws CertificateException + * @throws KeyStoreException + * @throws NoSuchAlgorithmException + * @throws UnsupportedCallbackException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws CertPathValidatorException + */ + private void importNewTrustedCertificate() throws CertificateException, + KeyStoreException, NoSuchAlgorithmException, IOException, + UnsupportedCallbackException, CertPathValidatorException, + UnrecoverableKeyException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "importNewTrustedCertificate"); //$NON-NLS-1$ + Certificate certificate = x509Factory.generateCertificate(inStream); + if (Configuration.DEBUG) + log.fine("certificate = " + certificate); //$NON-NLS-1$ + LinkedList orderedReply = new LinkedList(); + orderedReply.addLast(certificate); + + if (findTrustAndUpdate(orderedReply, ! noPrompt)) + { + store.setCertificateEntry(alias, certificate); + System.out.println(Messages.getString("ImportCmd.29")); //$NON-NLS-1$ + saveKeyStore(); + } + else + System.out.println(Messages.getString("ImportCmd.28")); //$NON-NLS-1$ + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "importNewTrustedCertificate"); //$NON-NLS-1$ + } + + /** + * A certificate reply is a certificate, whose Owner is stored in the key + * store associated to the designated Alias, and now signed by supposedly a + * trusted CA (Certificate Authority). In other words, the Subject in this + * certificate reply is Alias's own and the Issuer is a CA. + * <p> + * When importing a certificate reply, the reply is validated using trusted + * certificates from the key store, and optionally (if the option + * <code>-trustcacerts</code> was detected on the command line) certificates + * found in the key store, of type <code>JKS</code> named <code>cacerts</code> + * located in <code>${JAVA_HOME}/lib/security</code>, where + * <code>${JAVA_HOME}</code> is the root folder location of a Java runtime. + * + * @throws CertificateException + * @throws UnsupportedCallbackException + * @throws IOException + * @throws KeyStoreException + * @throws CertPathValidatorException + * @throws NoSuchAlgorithmException + * @throws UnrecoverableKeyException + */ + private void importCertificateReply() throws CertificateException, + IOException, UnsupportedCallbackException, KeyStoreException, + NoSuchAlgorithmException, CertPathValidatorException, + UnrecoverableKeyException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "importCertificateReply"); //$NON-NLS-1$ + Collection certificates = x509Factory.generateCertificates(inStream); + ensureReplyIsOurs(certificates); + // we now have established that the public keys are the same. + // find a chain-of-trust if one exists + if (certificates.size() == 1) + importCertificate((Certificate) certificates.iterator().next()); + else + importChain(certificates); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "importCertificateReply"); //$NON-NLS-1$ + } + + /** + * If the reply is a single X.509 certificate, keytool attempts to establish a + * trust chain, starting at the certificate reply and ending at a self-signed + * certificate (belonging to a root CA). The certificate reply and the + * hierarchy of certificates used to authenticate the certificate reply form + * the new certificate chain of alias. If a trust chain cannot be established, + * the certificate reply is not imported. In this case, keytool does not print + * out the certificate, nor does it prompt the user to verify it. This is + * because it is very hard (if not impossible) for a user to determine the + * authenticity of the certificate reply. + * + * @param certificate the certificate reply to import into the key store. + * @throws NoSuchAlgorithmException + * @throws CertPathValidatorException + * @throws UnsupportedCallbackException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws KeyStoreException + * @throws CertificateException + */ + private void importCertificate(Certificate certificate) + throws NoSuchAlgorithmException, CertPathValidatorException, + KeyStoreException, UnrecoverableKeyException, IOException, + UnsupportedCallbackException, CertificateException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "importCertificate", certificate); //$NON-NLS-1$ + LinkedList reply = new LinkedList(); + reply.addLast(certificate); + + if (! findTrustAndUpdate(reply, false)) + throw new CertPathValidatorException(Messages.getString("ImportCmd.34")); //$NON-NLS-1$ + + Certificate[] newChain = (Certificate[]) reply.toArray(new Certificate[0]); + Key privateKey = getAliasPrivateKey(); + store.setKeyEntry(alias, privateKey, keyPasswordChars, newChain); + saveKeyStore(); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "importCertificate"); //$NON-NLS-1$ + } + + /** + * If the reply is a PKCS#7 formatted certificate chain, the chain is first + * ordered (with the user certificate first and the self-signed root CA + * certificate last), before keytool attempts to match the root CA certificate + * provided in the reply with any of the trusted certificates in the key store + * or the "cacerts" keystore file (if the -trustcacerts option was specified). + * If no match can be found, the information of the root CA certificate is + * printed out, and the user is prompted to verify it, e.g., by comparing the + * displayed certificate fingerprints with the fingerprints obtained from some + * other (trusted) source of information, which might be the root CA itself. + * The user then has the option of aborting the import operation. If the + * -noprompt option is given, however, there will be no interaction with the + * user. + * + * @param chain the collection of certificates parsed from the user + * designated input. + * @throws UnsupportedCallbackException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws KeyStoreException + * @throws CertPathValidatorException + * @throws NoSuchAlgorithmException + * @throws CertificateException + */ + private void importChain(Collection chain) throws NoSuchAlgorithmException, + CertPathValidatorException, KeyStoreException, UnrecoverableKeyException, + IOException, UnsupportedCallbackException, CertificateException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "importChain", chain); //$NON-NLS-1$ + LinkedList reply = orderChain(chain); + if (findTrustAndUpdate(reply, ! noPrompt)) + { + Certificate[] newChain = (Certificate[]) reply.toArray(new Certificate[0]); + Key privateKey = getAliasPrivateKey(); + store.setKeyEntry(alias, privateKey, keyPasswordChars, newChain); + saveKeyStore(); + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "importChain"); //$NON-NLS-1$ + } + + /** + * Check to ensure that alias's public key is the subject of the first + * certificate in the passed certificate collection. Throws an exception if + * the public keys do not match. + * + * @param certificates a {@link Collection} of certificate replies (either a + * signle certificate reply, or a PKCS#7 certificate reply chain) + * usually sent by a CA as a response to a Certificate Signing + * Request (CSR). + * @throws IOException + * @throws UnsupportedCallbackException + * @throws KeyStoreException + */ + private void ensureReplyIsOurs(Collection certificates) throws IOException, + UnsupportedCallbackException, KeyStoreException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "ensureReplyIsOurs"); //$NON-NLS-1$ + Certificate certificate = (Certificate) certificates.iterator().next(); + if (Configuration.DEBUG) + log.fine("certificate = " + certificate); //$NON-NLS-1$ + Certificate[] chain = store.getCertificateChain(alias); + if (chain == null) + throw new IllegalArgumentException(Messages.getFormattedString("ImportCmd.37", //$NON-NLS-1$ + alias)); + selfSignedCertificate = (X509Certificate) chain[0]; + PublicKey anchorPublicKey = selfSignedCertificate.getPublicKey(); + PublicKey certPublicKey = certificate.getPublicKey(); + boolean sameKey; + if (anchorPublicKey instanceof DSAPublicKey) + { + DSAPublicKey pk1 = (DSAPublicKey) anchorPublicKey; + if (!(certPublicKey instanceof DSAPublicKey)) + throw new IllegalArgumentException(Messages.getString("ImportCmd.38")); //$NON-NLS-1$ + + sameKey = areEqual(pk1, (DSAPublicKey) certPublicKey); + } + else if (anchorPublicKey instanceof RSAPublicKey) + { + RSAPublicKey pk1 = (RSAPublicKey) anchorPublicKey; + if (!(certPublicKey instanceof RSAPublicKey)) + throw new IllegalArgumentException(Messages.getString("ImportCmd.38")); //$NON-NLS-1$ + + sameKey = areEqual(pk1, (RSAPublicKey) certPublicKey); + } + else + throw new IllegalArgumentException( + Messages.getFormattedString("ImportCmd.40", //$NON-NLS-1$ + new String[] { alias, + anchorPublicKey.getClass().getName() })); + if (! sameKey) + throw new IllegalArgumentException(Messages.getString("ImportCmd.41")); //$NON-NLS-1$ + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "ensureReplyIsOurs"); //$NON-NLS-1$ + } + + private boolean areEqual(DSAPublicKey pk1, DSAPublicKey pk2) + { + if (pk1.getY().compareTo(pk2.getY()) != 0) + return false; + + DSAParams p1 = pk1.getParams(); + DSAParams p2 = pk2.getParams(); + if (p1.getG().compareTo(p2.getG()) != 0) + return false; + + if (p1.getP().compareTo(p2.getP()) != 0) + return false; + + return p1.getQ().compareTo(p2.getQ()) == 0; + } + + private boolean areEqual(RSAPublicKey pk1, RSAPublicKey pk2) + { + if (pk1.getPublicExponent().compareTo(pk2.getPublicExponent()) != 0) + return false; + + return pk1.getModulus().compareTo(pk2.getModulus()) == 0; + } + + /** + * Given a collection of certificates returned as a certificate-reply, this + * method sorts the certificates in the collection so that the <i>Issuer</i> + * of the certificate at position <code>i</code> is the <i>Subject</i> of + * the certificate at position <code>i + 1</code>. + * <p> + * This method uses <code>selfSignedCertificate</code> to discover the first + * certificate in the chain. The <i>Trust Anchor</i> of the chain; i.e. the + * self-signed CA certificate, if it exsits, will be discovered/established + * later by an appropriate <i>Certificate Path Validator</i>. + * <p> + * An exception is thrown if (a) no initial certificate is found in the + * designated collection which can be used as the start of the chain, or (b) + * if a chain can not be constructed using all the certificates in the + * designated collection. + * + * @param chain a collection of certificates, not necessarily ordered, but + * assumed to include a CA certificate authenticating our alias + * public key, which is the subject of the alias self-signed + * certificate. + * @return the input collection, ordered with own certificate first, and CA's + * self-signed certificate last. + */ + private LinkedList orderChain(Collection chain) + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "orderChain"); //$NON-NLS-1$ + LinkedList in = new LinkedList(chain); + int initialCount = in.size(); + LinkedList result = new LinkedList(); + Principal issuer = selfSignedCertificate.getIssuerDN(); + ListIterator it; + outer: while (in.size() > 0) + { + for (it = in.listIterator(); it.hasNext();) + { + X509Certificate certificate = (X509Certificate) it.next(); + if (issuer.equals(certificate.getSubjectDN())) + { + it.remove(); + result.addLast(certificate); + issuer = certificate.getIssuerDN(); + continue outer; + } + } + throw new IllegalArgumentException( + Messages.getFormattedString(Messages.getString("ImportCmd.7"), //$NON-NLS-1$ + new Object[] { Integer.valueOf(result.size()), + Integer.valueOf(initialCount) })); + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "orderChain", result); //$NON-NLS-1$ + return result; + } + + /** + * Given an ordered list of certificates, this method attempts to validate the + * chain, and if successful, updates the key store entry for the designated + * alias. The list of certificates is expected to be ordered as a chain, where + * the first is the alias's own certificate and the last being a self-signed + * CA certificate. + * <p> + * if <code>promptUser</code> is <code>true</code>, then even if no + * anchor trust certificate is found, the user is prompted to approve, or not, + * the import operation. On the other hand if the <code>promptUser</code> + * parameter is <code>false</code> then this method will throw an exception + * if no trust anchor is to be found. + * + * @param reply an ordered certificate path, where the last entry is the CA's + * self-signed certificate. + * @param promptUser a boolean flag indicating whether or not to prompt the + * user for explicit trust in a CA certificate. + * @return <code>true</code> if the validation succeeds; or <code>false</code> + * otherwise. + * @throws NoSuchAlgorithmException + * @throws CertPathValidatorException + * @throws UnsupportedCallbackException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws KeyStoreException + * @throws CertificateEncodingException + */ + private boolean findTrustAndUpdate(LinkedList reply, boolean promptUser) + throws IOException, NoSuchAlgorithmException, CertPathValidatorException, + KeyStoreException, UnrecoverableKeyException, UnsupportedCallbackException, + CertificateEncodingException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "findTrustAndUpdate"); //$NON-NLS-1$ + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); //$NON-NLS-1$ + X509CertPath certPath = new X509CertPath(reply); + PKIXCertPathValidatorResult cpvr = findTrustInStore(certPath, validator); + if (cpvr == null && trustCACerts) // try cacerts.gkr - a GKR key store + { + PKIXParameters params = getCertPathParameters(GKR, gkrCaCertsPathName); + cpvr = validate(validator, certPath, params); + if (cpvr == null) // try cacerts - a JKS key store + { + params = getCertPathParameters(JKS, jksCaCertsPathName); + cpvr = validate(validator, certPath, params); + } + } + boolean result = false; + if (cpvr == null) + { + if (promptUser) + { + printVerbose((Certificate) reply.getLast()); + ConfirmationCallback ccb; + ccb = new ConfirmationCallback(Messages.getString("ImportCmd.32"), //$NON-NLS-1$ + ConfirmationCallback.INFORMATION, + ConfirmationCallback.YES_NO_OPTION, + ConfirmationCallback.NO); + getCallbackHandler().handle(new Callback[] { ccb }); + int answer = ccb.getSelectedIndex(); + result = answer == ConfirmationCallback.YES; + } + } + else + { + TrustAnchor anchor = cpvr.getTrustAnchor(); + log.fine("Found a chain-of-trust anchored by " + anchor); //$NON-NLS-1$ + Certificate trustedCert = anchor.getTrustedCert(); + reply.addLast(trustedCert); + result = true; + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "findTrustAndUpdate", //$NON-NLS-1$ + Boolean.valueOf(result)); + return result; + } + + private PKIXCertPathValidatorResult findTrustInStore(X509CertPath certPath, + CertPathValidator validator) + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "findTrustInStore"); //$NON-NLS-1$ + PKIXCertPathValidatorResult result; + try + { + PKIXParameters params = new PKIXParameters(store); + result = (PKIXCertPathValidatorResult) validator.validate(certPath, params); + } + catch (Exception x) + { + log.log(Level.FINE, + "Exception in findTrustInStore(). Ignore + Return NULL", //$NON-NLS-1$ + x); + result = null; + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "findTrustInStore", result); //$NON-NLS-1$ + return result; + } + + /** + * Return an instance of {@link PKIXParameters} constructed using a key store + * of the designated type and located at the designated path. + * + * @param type the type of the key-store to load. + * @param pathName the local File System fully qualified path name to the key + * store. + * @return an instance of <code>CertPathParameters</code> to use for + * validating certificates and certificate replies. + */ + private PKIXParameters getCertPathParameters(String type, String pathName) + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getCertPathParameters", //$NON-NLS-1$ + new Object[] { type, pathName }); + FileInputStream stream = null; + PKIXParameters result = null; + try + { + KeyStore cacerts = KeyStore.getInstance(type); + stream = new FileInputStream(pathName); + cacerts.load(stream, "changeit".toCharArray()); //$NON-NLS-1$ + result = new PKIXParameters(cacerts); + } + catch (Exception x) + { + if (Configuration.DEBUG) + log.log(Level.FINE, "Exception in getCertPathParameters(). Ignore", x); //$NON-NLS-1$ + } + finally + { + if (stream != null) + try + { + stream.close(); + } + catch (Exception ignored) + { + } + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getCertPathParameters", result); //$NON-NLS-1$ + return result; + } + + private PKIXCertPathValidatorResult validate(CertPathValidator validator, + X509CertPath certPath, + PKIXParameters params) + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "validate"); //$NON-NLS-1$ + PKIXCertPathValidatorResult result = null; + if (params != null) + try + { + result = (PKIXCertPathValidatorResult) validator.validate(certPath, + params); + } + catch (Exception x) + { + if (Configuration.DEBUG) + log.log(Level.FINE, "Exception in validate(). Ignore", x); //$NON-NLS-1$ + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "validate", result); //$NON-NLS-1$ + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/KeyCloneCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/KeyCloneCmd.java new file mode 100644 index 000000000..a05a5962f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/KeyCloneCmd.java @@ -0,0 +1,407 @@ +/* KeyCloneCmd.java -- The keyclone command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; + +import java.io.IOException; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-keyclone</b> keytool command handler is used to clone an existing + * key store entry associated with a designated alias, with its private key and + * chain of certificates. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-dest ALIAS</dt> + * <dd>Use this option to specify the new <i>Alias</i> which will be used + * to identify the cloned copy of the <i>Key Entry</i>. + * <p></dd> + * + * <dt>-keypass PASSWORD</dt> + * <dd>Use this option to specify the password which the tool will use to + * unlock the <i>Key Entry</i> associated with the designated <i>Alias</i>. + * <p> + * If this option is omitted, the tool will first attempt to unlock the + * <i>Key Entry</i> using the same password protecting the key store. If + * this fails, you will then be prompted to provide a password. + * <p></dd> + * + * <dt>-new PASSWORD</dt> + * <dd>Use this option to specify the password protecting the private key + * material of the newly cloned copy of the <i>Key Entry</i>. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class KeyCloneCmd extends Command +{ + private static final Logger log = Logger.getLogger(KeyCloneCmd.class.getName()); + protected String _alias; + protected String _destAlias; + protected String _password; + protected String _newPassword; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + private String destinationAlias; + private char[] newKeyPasswordChars; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the existing alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param alias the new alias to use. */ + public void setDest(String alias) + { + this._destAlias = alias; + } + + /** @param password the existing (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** @param password the new (private) key password to use. */ + public void setNew(String password) + { + this._newPassword = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); + setDestinationAlias(_destAlias); + if (Configuration.DEBUG) + { + log.fine("-keyclone handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -dest=" + destinationAlias); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, IOException, + UnsupportedCallbackException, UnrecoverableKeyException, + CertificateException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + if (store.containsAlias(destinationAlias)) + throw new SecurityException(Messages.getString("KeyCloneCmd.23")); //$NON-NLS-1$ + + Key privateKey = getAliasPrivateKey(); + + setNewKeyPassword(_newPassword); + Certificate[] chain = store.getCertificateChain(alias); + + store.setKeyEntry(destinationAlias, privateKey, newKeyPasswordChars, chain); + + saveKeyStore(); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.KEYCLONE_CMD, true); + result.setHeader(Messages.getString("KeyCloneCmd.22")); //$NON-NLS-1$ + result.setFooter(Messages.getString("KeyCloneCmd.21")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("KeyCloneCmd.20")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("KeyCloneCmd.19"), //$NON-NLS-1$ + Messages.getString("KeyCloneCmd.16")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.DEST_OPT, + Messages.getString("KeyCloneCmd.17"), //$NON-NLS-1$ + Messages.getString("KeyCloneCmd.16")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _destAlias = argument; + } + }); + options.add(new Option(Main.KEYPASS_OPT, + Messages.getString("KeyCloneCmd.15"), //$NON-NLS-1$ + Messages.getString("KeyCloneCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _password = argument; + } + }); + options.add(new Option(Main.NEW_OPT, + Messages.getString("KeyCloneCmd.13"), //$NON-NLS-1$ + Messages.getString("KeyCloneCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _newPassword = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("KeyCloneCmd.11"), //$NON-NLS-1$ + Messages.getString("KeyCloneCmd.10")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("KeyCloneCmd.9"), //$NON-NLS-1$ + Messages.getString("KeyCloneCmd.8")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("KeyCloneCmd.7"), //$NON-NLS-1$ + Messages.getString("KeyCloneCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("KeyCloneCmd.5"), //$NON-NLS-1$ + Messages.getString("KeyCloneCmd.4")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("KeyCloneCmd.3")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + private void setDestinationAlias(String name) throws IOException, + UnsupportedCallbackException + { + if (name == null || name.trim().length() == 0) // ask user to provide one + { + NameCallback ncb = new NameCallback(Messages.getString("KeyCloneCmd.26")); //$NON-NLS-1$ + getCallbackHandler().handle(new Callback[] { ncb }); + name = ncb.getName(); + if (name == null || name.trim().length() == 0) + throw new IllegalArgumentException(Messages.getString("KeyCloneCmd.27")); //$NON-NLS-1$ + } + + destinationAlias = name.trim(); + } + + private void setNewKeyPassword(String password) throws IOException, + UnsupportedCallbackException + { + if (password != null) + newKeyPasswordChars = password.toCharArray(); + else // ask user to provide one + { + boolean ok = false; + Callback[] prompts = new Callback[1]; + Callback[] errors = new Callback[1]; + for (int i = 0; i < 3; i++) + if (prompt4NewPassword(getCallbackHandler(), prompts, errors)) + { + ok = true; + break; + } + if (! ok) + throw new SecurityException(Messages.getString("StorePasswdCmd.19")); //$NON-NLS-1$ + } + } + + private boolean prompt4NewPassword(CallbackHandler handler, + Callback[] prompts, Callback[] errors) + throws IOException, UnsupportedCallbackException + { + String p = Messages.getFormattedString("KeyCloneCmd.28", //$NON-NLS-1$ + new String[] { destinationAlias, + String.valueOf(keyPasswordChars) }); + PasswordCallback pcb = new PasswordCallback(p, false); + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd1 = pcb.getPassword(); + pcb.clearPassword(); + if (pwd1 == null || pwd1.length == 0) + { + newKeyPasswordChars = (char[]) keyPasswordChars.clone(); + return true; + } + + if (pwd1.length < 6) + { + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, + Messages.getString("StorePasswdCmd.21")); //$NON-NLS-1$ + handler.handle(errors); + return false; + } + + newKeyPasswordChars = pwd1; + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java new file mode 100644 index 000000000..7652cc843 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java @@ -0,0 +1,395 @@ +/* KeyPasswdCmd.java -- The keypasswd command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.SystemProperties; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; + +import java.io.IOException; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-keypasswd</b> keytool command handler is used to change the password + * protecting the private key associated to a designated alias. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-keypass PASSWORD</dt> + * <dd>Use this option to specify the password which the tool will use to + * unlock the <i>Key Entry</i> associated with the designated <i>Alias</i>. + * <p> + * If this option is omitted, the tool will first attempt to unlock the + * <i>Key Entry</i> using the same password protecting the key store. If + * this fails, you will then be prompted to provide a password. + * <p></dd> + * + * <dt>-new PASSWORD</dt> + * <dd>The new, and different, password which will be used to protect the + * private key material of the designated Key Entry. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class KeyPasswdCmd extends Command +{ + private static final Logger log = Logger.getLogger(KeyPasswdCmd.class.getName()); + protected String _alias; + protected String _password; + protected String _newPassword; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + private char[] newPasswordChars; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param password the existing (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** @param password the new (private) key password to use. */ + public void setNew(String password) + { + this._newPassword = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); + if (Configuration.DEBUG) + { + log.fine("-keypasswd handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -new=" + _newPassword); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, IOException, + UnsupportedCallbackException, UnrecoverableKeyException, + CertificateException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + // 1. get the key entry and certificate chain associated to alias + Key privateKey = getAliasPrivateKey(); + Certificate[] chain = store.getCertificateChain(alias); + + // 2. replace the old entry + setNewKeyPassword(_newPassword); + store.setKeyEntry(alias, privateKey, newPasswordChars, chain); + + // 3. persist the key store + saveKeyStore(); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.KEYPASSWD_CMD, true); + result.setHeader(Messages.getString("KeyPasswdCmd.23")); //$NON-NLS-1$ + result.setFooter(Messages.getString("KeyPasswdCmd.22")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("KeyPasswdCmd.21")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("KeyPasswdCmd.20"), //$NON-NLS-1$ + Messages.getString("KeyPasswdCmd.19")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.KEYPASS_OPT, + Messages.getString("KeyPasswdCmd.18"), //$NON-NLS-1$ + Messages.getString("KeyPasswdCmd.9")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _password = argument; + } + }); + options.add(new Option(Main.NEW_OPT, + Messages.getString("KeyPasswdCmd.16"), //$NON-NLS-1$ + Messages.getString("KeyPasswdCmd.9")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _newPassword = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("KeyPasswdCmd.14"), //$NON-NLS-1$ + Messages.getString("KeyPasswdCmd.13")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("KeyPasswdCmd.12"), //$NON-NLS-1$ + Messages.getString("KeyPasswdCmd.11")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("KeyPasswdCmd.10"), //$NON-NLS-1$ + Messages.getString("KeyPasswdCmd.9")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("KeyPasswdCmd.8"), //$NON-NLS-1$ + Messages.getString("KeyPasswdCmd.7")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("KeyPasswdCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + /** + * Set the new password to use for protecting Alias's private key. + * + * @param password the new key password. if <code>null</code> prompt the + * user to provide one. When prompting, the password is entered twice + * and compared for a match. + * @throws IOException if an I/O related exception occurs during the process. + * @throws UnsupportedCallbackException if no implementation of a password + * callback handler was found. + */ + private void setNewKeyPassword(String password) throws IOException, + UnsupportedCallbackException + { + if (password != null) + newPasswordChars = password.toCharArray(); + else + { + boolean ok = false; + Callback[] prompts = new Callback[1]; + Callback[] errors = new Callback[1]; + for (int i = 0; i < 3; i++) + if (prompt4NewPassword(getCallbackHandler(), prompts, errors)) + { + ok = true; + break; + } + if (! ok) + throw new SecurityException(Messages.getString("StorePasswdCmd.19")); //$NON-NLS-1$ + } + } + + private boolean prompt4NewPassword(CallbackHandler handler, + Callback[] prompts, Callback[] errors) + throws IOException, UnsupportedCallbackException + { + // prompt user (1st time) to provide one + String p = Messages.getFormattedString("KeyPasswdCmd.24", alias); //$NON-NLS-1$ + PasswordCallback pcb = new PasswordCallback(p, false); + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd1 = pcb.getPassword(); + pcb.clearPassword(); + String ls = SystemProperties.getProperty("line.separator"); //$NON-NLS-1$ + if (pwd1 == null || pwd1.length < 6) + { + String m = Messages.getString("StorePasswdCmd.21") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + if (Arrays.equals(keyPasswordChars, pwd1)) + { + String m = Messages.getString("StorePasswdCmd.22") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + // prompt user (2nd time) for confirmation + p = Messages.getFormattedString("KeyPasswdCmd.28", alias); //$NON-NLS-1$ + pcb = new PasswordCallback(p, false); + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd2 = pcb.getPassword(); + pcb.clearPassword(); + if (! Arrays.equals(pwd1, pwd2)) + { + String m = Messages.getString("StorePasswdCmd.24") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + newPasswordChars = pwd2; + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/ListCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/ListCmd.java new file mode 100644 index 000000000..55c8c7683 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/ListCmd.java @@ -0,0 +1,432 @@ +/* ListCmd.java -- The list command handler of the keytool + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.util.Base64; + +import java.io.IOException; +import java.io.PrintWriter; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.util.Enumeration; +import java.util.logging.Logger; + +/** + * The <b>-list</b> keytool command handler is used to output one or all key + * store entries. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-rfc</dt> + * <dd>Use RFC-1421 specifications when encoding the output. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Output the certificate in human-readable format. If both this option + * and the <code>-rfc</code> option are detected on the command line, the + * tool will opt for the human-readable form and will not abort the + * command.</dd> + * </dl> + */ +class ListCmd extends Command +{ + private static final Logger log = Logger.getLogger(ListCmd.class.getName()); + protected String _alias; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + protected boolean rfc; + private boolean all; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + /** + * @param flag whether to use, or not, RFC-1421 format when listing the + * certificate(s). + */ + public void setRfc(String flag) + { + this.rfc = Boolean.valueOf(flag).booleanValue(); + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setOutputStreamParam(null); // use stdout + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + all = _alias == null; + if (! all) + setAliasParam(_alias); + + if (verbose & rfc) + { + if (Configuration.DEBUG) + log.fine("Both -v and -rfc options were found on the command line. " //$NON-NLS-1$ + + "Only the former will be considered"); //$NON-NLS-1$ + rfc = false; + } + if (Configuration.DEBUG) + { + log.fine("-list handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + log.fine(" -rfc=" + rfc); //$NON-NLS-1$ + } + } + + void start() throws KeyStoreException, CertificateEncodingException, + IOException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + PrintWriter writer = new PrintWriter(outStream, true); + writer.println(Messages.getFormattedString("ListCmd.21", store.getType())); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("ListCmd.22", //$NON-NLS-1$ + store.getProvider().getName())); + if (all) + { + if (Configuration.DEBUG) + log.fine("About to list all aliases in key store..."); //$NON-NLS-1$ + writer.println(); + writer.println(Messages.getFormattedString("ListCmd.24", //$NON-NLS-1$ + Integer.valueOf(store.size()))); + for (Enumeration e = store.aliases(); e.hasMoreElements(); ) + { + String anAlias = (String) e.nextElement(); + if (anAlias != null) + list1Alias(anAlias, writer); + } + } + else + list1Alias(alias, writer); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.LIST_CMD, true); + result.setHeader(Messages.getString("ListCmd.20")); //$NON-NLS-1$ + result.setFooter(Messages.getString("ListCmd.19")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("ListCmd.18")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("ListCmd.17"), //$NON-NLS-1$ + Messages.getString("ListCmd.16")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("ListCmd.15"), //$NON-NLS-1$ + Messages.getString("ListCmd.14")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("ListCmd.13"), //$NON-NLS-1$ + Messages.getString("ListCmd.12")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("ListCmd.11"), //$NON-NLS-1$ + Messages.getString("ListCmd.10")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("ListCmd.9"), //$NON-NLS-1$ + Messages.getString("ListCmd.8")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("ListCmd.7")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + options.add(new Option(Main.RFC_OPT, + Messages.getString("ListCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + rfc = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + /** + * Prints the certificate(s) associated with the designated alias. + * + * @param anAlias a non-null string denoting an alias in the key-store. + * @param writer where to print. + * @throws KeyStoreException if an exception occurs while obtaining the + * certificate associated to the designated alias. + * @throws CertificateEncodingException if an exception occurs while obtaining + * the DER encoded form of the certificate. + * @throws IOException if an I/O related exception occurs during the process. + */ + private void list1Alias(String anAlias, PrintWriter writer) + throws KeyStoreException, CertificateEncodingException, IOException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "list1Alias", anAlias); //$NON-NLS-1$ + writer.println(); + writer.println(Messages.getFormattedString("ListCmd.30", anAlias)); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("ListCmd.31", //$NON-NLS-1$ + store.getCreationDate(anAlias))); + if (store.isCertificateEntry(anAlias)) + { + writer.println(Messages.getString("ListCmd.32")); //$NON-NLS-1$ + Certificate certificate = store.getCertificate(anAlias); + print1Certificate(certificate, writer); + } + else if (store.isKeyEntry(anAlias)) + { + writer.println(Messages.getString("ListCmd.33")); //$NON-NLS-1$ + Certificate[] chain = store.getCertificateChain(anAlias); + print1Chain(chain, writer); + } + else + throw new IllegalArgumentException(Messages.getFormattedString("ListCmd.34", //$NON-NLS-1$ + anAlias)); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "list1Alias"); //$NON-NLS-1$ + } + + /** + * Prints the designated certificate chain, or a fingerprint of the first + * certificate (bottom) in the chain, depending on the values of the flags + * <code>v</code> (for verbose) and <code>rfc</code>. + * <p> + * If both flags are <code>false</code>, only the fingerprint is generated, + * otherwise, if the <code>v</code> flag is set, then a human readable output + * is generated. If <code>rfc</code> is set, then an RFC-1421 like output + * is generated. + * <p>Note that both <code>v</code> and <code>rfc</code> cannot both be + * <code>true</code> at the same time. + * + * @param chain the certificate chain to process. + * @param writer where to print. + * @throws CertificateEncodingException if an exception occurs while obtaining + * the DER encoded form of the certificate. + */ + private void print1Chain(Certificate[] chain, PrintWriter writer) + throws CertificateEncodingException + { + if (! verbose && ! rfc) + fingerprint(chain[0], writer); + else + { + int limit = chain.length; + writer.println(Messages.getFormattedString("ListCmd.38", //$NON-NLS-1$ + Integer.valueOf(limit))); + writer.println(Messages.getString("ListCmd.39")); //$NON-NLS-1$ + print1Certificate(chain[0], writer); + for (int i = 1; i < limit; i++) + { + writer.println(); + writer.println(Messages.getFormattedString("ListCmd.40", //$NON-NLS-1$ + Integer.valueOf(i + 1))); + print1Certificate(chain[i], writer); + } + writer.println(); + writer.println(Messages.getString("ListCmd.42")); //$NON-NLS-1$ + } + } + + /** + * Prints the designated certificate, or its fingerprint, depending on the + * values of the flags <code>v</code> (for verbose) and <code>rfc</code>. + * <p> + * If both flags are <code>false</code>, only a fingerprint is generated, + * otherwise, if the <code>v</code> flag is set, then a human readable output + * is generated. If <code>rfc</code> is set, then an RFC-1421 like output + * is generated. + * <p>Note that both <code>v</code> and <code>rfc</code> cannot both be + * <code>true</code> at the same time. + * + * @param certificate the certificate to process. + * @param writer where to print. + * @throws CertificateEncodingException if an exception occurs while obtaining + * the DER encoded form of the certificate. + */ + private void print1Certificate(Certificate certificate, PrintWriter writer) + throws CertificateEncodingException + { + if (verbose) + printVerbose(certificate, writer); + else if (rfc) + printRFC1421(certificate, writer); + else + fingerprint(certificate, writer); + } + + private void printRFC1421(Certificate certificate, PrintWriter writer) + throws CertificateEncodingException + { + byte[] derBytes = certificate.getEncoded(); + String encoded = Base64.encode(derBytes, 72); + writer.println(Messages.getString("ListCmd.43")); //$NON-NLS-1$ + writer.println(encoded); + writer.println(Messages.getString("ListCmd.44")); //$NON-NLS-1$ + } + + private void fingerprint(Certificate certificate, PrintWriter writer) + throws CertificateEncodingException + { + byte[] derBytes = certificate.getEncoded(); + String fingerPrint = digestWithMD5(derBytes); + writer.println(Messages.getFormattedString("ListCmd.45", fingerPrint)); //$NON-NLS-1$ + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/Main.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/Main.java new file mode 100644 index 000000000..2d5234ad0 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/Main.java @@ -0,0 +1,329 @@ +/* Main.java -- Implementation of the keytool security tool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.common.ProviderUtil; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.security.Registry; +import gnu.javax.crypto.jce.GnuCrypto; +import gnu.javax.security.auth.callback.GnuCallbacks; + +import java.util.logging.Logger; + +/** + * The GNU Classpath implementation of the keytool security tool. + * <p> + * Except for the <code>-identitydb</code> command, available for importing + * JDK 1.1 <i>identities</i> into a key store, this implementation is intended + * to be compatible with the behaviour described in the public documentation of + * the same tool included in JDK 1.4. + */ +public class Main +{ + private static final Logger log = Logger.getLogger(Main.class.getName()); + static final String KEYTOOL_TOOL = "keytool"; //$NON-NLS-1$ + static final String GENKEY_CMD = "genkey"; //$NON-NLS-1$ + static final String IMPORT_CMD = "import"; //$NON-NLS-1$ + static final String SELFCERT_CMD = "selfcert"; //$NON-NLS-1$ + static final String IDENTITYDB_CMD = "identitydb"; //$NON-NLS-1$ + static final String CERTREQ_CMD = "certreq"; //$NON-NLS-1$ + static final String EXPORT_CMD = "export"; //$NON-NLS-1$ + static final String LIST_CMD = "list"; //$NON-NLS-1$ + static final String PRINTCERT_CMD = "printcert"; //$NON-NLS-1$ + static final String KEYCLONE_CMD = "keyclone"; //$NON-NLS-1$ + static final String STOREPASSWD_CMD = "storepasswd"; //$NON-NLS-1$ + static final String KEYPASSWD_CMD = "keypasswd"; //$NON-NLS-1$ + static final String DELETE_CMD = "delete"; //$NON-NLS-1$ + static final String CACERT_CMD = "cacert"; //$NON-NLS-1$ + + static final String _GENKEY = "-" + GENKEY_CMD; //$NON-NLS-1$ + static final String _IMPORT = "-" + IMPORT_CMD; //$NON-NLS-1$ + static final String _SELFCERT = "-" + SELFCERT_CMD; //$NON-NLS-1$ + static final String _IDENTITYDB = "-" + IDENTITYDB_CMD; //$NON-NLS-1$ + static final String _CERTREQ = "-" + CERTREQ_CMD; //$NON-NLS-1$ + static final String _EXPORT = "-" + EXPORT_CMD; //$NON-NLS-1$ + static final String _LIST = "-" + LIST_CMD; //$NON-NLS-1$ + static final String _PRINTCERT = "-" + PRINTCERT_CMD; //$NON-NLS-1$ + static final String _KEYCLONE = "-" + KEYCLONE_CMD; //$NON-NLS-1$ + static final String _STOREPASSWD = "-" + STOREPASSWD_CMD; //$NON-NLS-1$ + static final String _KEYPASSWD = "-" + KEYPASSWD_CMD; //$NON-NLS-1$ + static final String _DELETE = "-" + DELETE_CMD; //$NON-NLS-1$ + static final String _HELP = "-help"; //$NON-NLS-1$ + static final String _CACERT = "-" + CACERT_CMD; //$NON-NLS-1$ + + static final String ALIAS_OPT = "alias"; //$NON-NLS-1$ + static final String SIGALG_OPT = "sigalg"; //$NON-NLS-1$ + static final String KEYALG_OPT = "keyalg"; //$NON-NLS-1$ + static final String KEYSIZE_OPT = "keysize"; //$NON-NLS-1$ + static final String KEYPASS_OPT = "keypass"; //$NON-NLS-1$ + static final String VALIDITY_OPT = "validity"; //$NON-NLS-1$ + static final String STORETYPE_OPT = "storetype"; //$NON-NLS-1$ + static final String STOREPASS_OPT = "storepass"; //$NON-NLS-1$ + static final String KEYSTORE_OPT = "keystore"; //$NON-NLS-1$ + static final String PROVIDER_OPT = "provider"; //$NON-NLS-1$ + static final String FILE_OPT = "file"; //$NON-NLS-1$ + static final String VERBOSE_OPT = "v"; //$NON-NLS-1$ + static final String DEST_OPT = "dest"; //$NON-NLS-1$ + static final String NEW_OPT = "new"; //$NON-NLS-1$ + static final String RFC_OPT = "rfc"; //$NON-NLS-1$ + static final String DNAME_OPT = "dname"; //$NON-NLS-1$ + + /** The Preferences key name for the last issued certificate serial nbr. */ + static final String LAST_SERIAL_NUMBER = "lastSerialNumber"; //$NON-NLS-1$ + /** Constant denoting the X.509 certificate type. */ + static final String X_509 = "X.509"; //$NON-NLS-1$ + + /** Whether we have already printed the help text or not. */ + private boolean helpPrinted; + /** The new position of GnuCRYPTO provider if it is not already installed. */ + private int gnuCryptoProviderNdx = -2; + /** The new position of GNU Callbacks provider if it is not already installed. */ + private int gnuCallbacksNdx = -2; + /** The command line parser. */ + private Parser cmdLineParser; + /** The shutdown hook. */ + private ShutdownHook shutdownThread; + + private Main() + { + super(); + shutdownThread = new ShutdownHook(); + Runtime.getRuntime().addShutdownHook(shutdownThread); + } + + public static final void main(String[] args) + { + if (Configuration.DEBUG) + log.entering(Main.class.getName(), "main", args); //$NON-NLS-1$ + Main tool = new Main(); + int result = 1; + try + { + tool.setup(); + tool.start(args); + result = 0; + } + catch (OptionException x) + { + System.err.println(x.getMessage()); + if (tool.cmdLineParser != null) + tool.cmdLineParser.printHelp(); + } + catch (SecurityException x) + { + if (Configuration.DEBUG) + log.throwing(Main.class.getName(), "main", x); //$NON-NLS-1$ + System.err.println(Messages.getFormattedString("Main.6", //$NON-NLS-1$ + x.getMessage())); + } + catch (Exception x) + { + if (Configuration.DEBUG) + log.throwing(Main.class.getName(), "main", x); //$NON-NLS-1$ + System.err.println(Messages.getFormattedString("Main.8", x)); //$NON-NLS-1$ + } + finally + { + tool.teardown(); + if (tool.shutdownThread != null) + Runtime.getRuntime().removeShutdownHook(tool.shutdownThread); + } + if (Configuration.DEBUG) + log.exiting(Main.class.getName(), "main", Integer.valueOf(result)); //$NON-NLS-1$ + System.exit(result); + } + + // helper methods ----------------------------------------------------------- + + private void setup() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "setup"); //$NON-NLS-1$ + cmdLineParser = getParser(); + gnuCryptoProviderNdx = ProviderUtil.addProvider(new GnuCrypto()); + gnuCallbacksNdx = ProviderUtil.addProvider(new GnuCallbacks()); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "setup"); //$NON-NLS-1$ + } + + private void start(String[] args) throws Exception + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + if (args == null || args.length == 0) + throw new OptionException(""); //$NON-NLS-1$ + + String opt; + Command cmd; + while (args.length > 0) + { + opt = args[0]; + cmd = null; + if (_GENKEY.equals(opt)) + cmd = new GenKeyCmd(); + else if (_IMPORT.equals(opt)) + cmd = new ImportCmd(); + else if (_SELFCERT.equals(opt)) + cmd = new SelfCertCmd(); + else if (_IDENTITYDB.equals(opt)) + cmd = new IdentityDBCmd(); + else if (_CERTREQ.equals(opt)) + cmd = new CertReqCmd(); + else if (_EXPORT.equals(opt)) + cmd = new ExportCmd(); + else if (_LIST.equals(opt)) + cmd = new ListCmd(); + else if (_PRINTCERT.equals(opt)) + cmd = new PrintCertCmd(); + else if (_KEYCLONE.equals(opt)) + cmd = new KeyCloneCmd(); + else if (_STOREPASSWD.equals(opt)) + cmd = new StorePasswdCmd(); + else if (_KEYPASSWD.equals(opt)) + cmd = new KeyPasswdCmd(); + else if (_DELETE.equals(opt)) + cmd = new DeleteCmd(); + else if (_CACERT.equals(opt)) + cmd = new CACertCmd(); + else if (_HELP.equals(opt)) + throw new OptionException(""); //$NON-NLS-1$ + else + throw new OptionException(Messages.getFormattedString("Main.18", //$NON-NLS-1$ + opt)); + + String[] cmdArgs = new String[args.length - 1]; + System.arraycopy(args, 1, cmdArgs, 0, cmdArgs.length); + args = cmd.processArgs(cmdArgs); + cmd.doCommand(); + } + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + private Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(KEYTOOL_TOOL, true); + result.setHeader(Messages.getString("Main.19")); //$NON-NLS-1$ + result.setFooter(Messages.getString("Main.20")); //$NON-NLS-1$ + OptionGroup cmdGroup = new OptionGroup(Messages.getString("Main.21")); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(GENKEY_CMD, + Messages.getString("Main.22"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(IMPORT_CMD, + Messages.getString("Main.23"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(SELFCERT_CMD, + Messages.getString("Main.24"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(IDENTITYDB_CMD, + Messages.getString("Main.25"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(CERTREQ_CMD, + Messages.getString("Main.26"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(EXPORT_CMD, + Messages.getString("Main.27"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(LIST_CMD, + Messages.getString("Main.28"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(PRINTCERT_CMD, + Messages.getString("Main.29"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(KEYCLONE_CMD, + Messages.getString("Main.30"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(STOREPASSWD_CMD, + Messages.getString("Main.31"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(KEYPASSWD_CMD, + Messages.getString("Main.32"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(DELETE_CMD, + Messages.getString("Main.33"))); //$NON-NLS-1$ + cmdGroup.add(new NoParseOption(CACERT_CMD, + Messages.getString("Main.5"))); //$NON-NLS-1$ + result.add(cmdGroup); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + void teardown() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + // if we added our own providers remove them + if (gnuCryptoProviderNdx > 0) + ProviderUtil.removeProvider(Registry.GNU_CRYPTO); + + if (gnuCallbacksNdx > 0) + ProviderUtil.removeProvider("GNU-CALLBACKS"); //$NON-NLS-1$ + + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + } + + // Inner class(es) + // ========================================================================== + + private class NoParseOption + extends Option + { + public NoParseOption(String name, String description) + { + super(name, description); + } + + public NoParseOption(String name, String description, String param) + { + super(name, description, param); + } + + public void parsed(String argument) throws OptionException + { + // do nothing + } + } + + private class ShutdownHook + extends Thread + { + public void run() + { + teardown(); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/Messages.java new file mode 100644 index 000000000..ea2825f10 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/Messages.java @@ -0,0 +1,118 @@ +/* Messages.java -- I18N related helper class + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.logging.Logger; + +/** + * An initially generated Eclipse helper class to ease the use of localized + * messages. + * <p> + * Enriched to handle localized message formats. + */ +class Messages +{ + private static final Logger log = Logger.getLogger(Messages.class.getName()); + private static final String BUNDLE_NAME = "gnu.classpath.tools.keytool.messages"; + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + private static final Map CACHED_FORMATS = new HashMap(5); + + private Messages() + { + super(); + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return constructMessage(key, null); + } + } + + public static String getFormattedString(String key, Object args) + { + MessageFormat mf = (MessageFormat) CACHED_FORMATS.get(key); + if (mf == null) + { + String formatString = getString(key); + if (formatString.startsWith("!")) + return constructMessage(key, args); + + mf = new MessageFormat(formatString); + CACHED_FORMATS.put(key, mf); + } + + // if the argument is not an array, then build one consisting of the + // sole argument before passing it to the format() method + try + { + if (args instanceof Object[]) + return mf.format(args); + + return mf.format(new Object[] { args }); + } + catch (IllegalArgumentException x) + { + if (Configuration.DEBUG) + log.fine("Exception while rendering a message format keyed by [" + + key + "]: " + mf.toPattern()); + return constructMessage(mf.toPattern(), args); + } + } + + private static final String constructMessage(String m, Object args) + { + if (args == null) + return '!' + m + '!'; + + return '!' + m + '!' + String.valueOf(args) + '!'; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/PrintCertCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/PrintCertCmd.java new file mode 100644 index 000000000..cb9d03513 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/PrintCertCmd.java @@ -0,0 +1,143 @@ +/* PrintCertCmd.java -- The printcert command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; + +import java.io.PrintWriter; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.logging.Logger; + +/** + * The <b>-printcert</b> keytool command handler is used to read a certificate + * from a designated file, and print its contents in a human-readable format. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the file to read the certificate from. + * If this option is omitted, the tool will process STDIN. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class PrintCertCmd extends Command +{ + private static final Logger log = Logger.getLogger(PrintCertCmd.class.getName()); + protected String _certFileName; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certFileName = pathName; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setInputStreamParam(_certFileName); + if (Configuration.DEBUG) + { + log.fine("-printcert handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -file=" + _certFileName); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws CertificateException + { + if (Configuration.DEBUG) + log.entering(getClass().getName(), "start"); //$NON-NLS-1$ + CertificateFactory x509Factory = CertificateFactory.getInstance(Main.X_509); + Certificate certificate = x509Factory.generateCertificate(inStream); + PrintWriter writer = new PrintWriter(System.out, true); + writer.println(); + printVerbose(certificate, writer); + if (Configuration.DEBUG) + log.exiting(getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.PRINTCERT_CMD, true); + result.setHeader(Messages.getString("PrintCertCmd.5")); //$NON-NLS-1$ + result.setFooter(Messages.getString("PrintCertCmd.6")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("PrintCertCmd.7")); //$NON-NLS-1$ + options.add(new Option(Main.FILE_OPT, + Messages.getString("PrintCertCmd.8"), //$NON-NLS-1$ + Messages.getString("PrintCertCmd.9")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _certFileName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("PrintCertCmd.10")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/SelfCertCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/SelfCertCmd.java new file mode 100644 index 000000000..395bfe2cd --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/SelfCertCmd.java @@ -0,0 +1,440 @@ +/* SelfCertCmd.java -- The selfcert command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.security.x509.X500DistinguishedName; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.logging.Logger; + +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.x500.X500Principal; + +/** + * The <b>-selfcert</b> keytool command handler is used to generate a self- + * signed X.509 version 1 certificate using key store credentials stored under a + * designated alias. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-sigalg ALGORITHM</dt> + * <dd>The canonical name of the digital signature algorithm to use for + * signing the certificate. If this option is omitted, a default value will + * be chosen based on the type of the private key associated with the + * designated <i>Alias</i>. If the private key is a <code>DSA</code> one, + * the value for the signature algorithm will be <code>SHA1withDSA</code>. + * If on the other hand the private key is an <code>RSA</code> one, then + * the tool will use <code>MD5withRSA</code> as the signature algorithm. + * <p></dd> + * + * <dt>-dname NAME</dt> + * <dd>Use this option to specify the <i>Distinguished Name</i> of the + * newly generated self-signed certificate. If this option is omitted, the + * existing <i>Distinguished Name</i> of the base certificate in the chain + * associated with the designated <i>Alias</i> will be used instead. + * <p> + * The syntax of a valid value for this option MUST follow RFC-2253 + * specifications. Namely the following components (with their accepted + * meaning) will be recognized. Note that the component name is case- + * insensitive: + * <dl> + * <dt>CN</dt> + * <dd>The Common Name; e.g. "host.domain.com"</dd> + * + * <dt>OU</dt> + * <dd>The Organizational Unit; e.g. "IT Department"</dd> + * + * <dt>O</dt> + * <dd>The Organization Name; e.g. "The Sample Company"</dd> + * + * <dt>L</dt> + * <dd>The Locality Name; e.g. "Sydney"</dd> + * + * <dt>ST</dt> + * <dd>The State Name; e.g. "New South Wales"</dd> + * + * <dt>C</dt> + * <dd>The 2-letter Country identifier; e.g. "AU"</dd> + * </dl> + * <p> + * When specified with a <code>-dname</code> option, each pair of component + * / value will be separated from the other with a comma. Each component + * and value pair MUST be separated by an equal sign. For example, the + * following is a valid DN value: + * <pre> + * CN=host.domain.com, O=The Sample Company, L=Sydney, ST=NSW, C=AU + * </pre> + * <p></dd> + * + * <dt>-validity DAY_COUNT</dt> + * + * <dt>-keypass PASSWORD</dt> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class SelfCertCmd extends Command +{ + private static final Logger log = Logger.getLogger(SelfCertCmd.class.getName()); + protected String _alias; + protected String _sigAlgorithm; + protected String _dName; + protected String _password; + protected String _validityStr; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + private X500DistinguishedName distinguishedName; + private int validityInDays; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** + * @param algorithm the canonical name of the digital signature algorithm to + * use. + */ + public void setSigalg(String algorithm) + { + this._sigAlgorithm = algorithm; + } + + /** + * @param name the distiniguished name of both the issuer and subject (since + * we are dealing with a self-signed certificate) to use. + */ + public void setDname(String name) + { + this._dName = name; + } + + /** + * @param days the string representation of the number of days (a decimal, + * positive integer) to assign to the generated (self-signed) + * certificate. + */ + public void setValidity(String days) + { + this._validityStr = days; + } + + /** @param password the (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); + setValidityParam(_validityStr); + if (Configuration.DEBUG) + { + log.fine("-selfcert handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -alias=" + alias); //$NON-NLS-1$ + log.fine(" -sigalg=" + _sigAlgorithm); //$NON-NLS-1$ + log.fine(" -dname=" + _dName); //$NON-NLS-1$ + log.fine(" -validity=" + validityInDays); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException, IOException, UnsupportedCallbackException, + InvalidKeyException, SignatureException, CertificateException + { + if (Configuration.DEBUG) + log.entering(getClass().getName(), "start"); //$NON-NLS-1$ + // 1. get the key entry and certificate chain associated to alias + Key privateKey = getAliasPrivateKey(); + Certificate[] chain = store.getCertificateChain(alias); + + // 2. if the user has not supplied a DN use one from the certificate chain + X509Certificate bottomCertificate = (X509Certificate) chain[0]; + X500Principal defaultPrincipal = bottomCertificate.getIssuerX500Principal(); + setDName(_dName, defaultPrincipal); + + // 4. get alias's public key from certificate's SubjectPublicKeyInfo + PublicKey publicKey = bottomCertificate.getPublicKey(); + + // 5. issue the self-signed certificate + setSignatureAlgorithmParam(_sigAlgorithm, privateKey); + + byte[] derBytes = getSelfSignedCertificate(distinguishedName, + publicKey, + (PrivateKey) privateKey); + CertificateFactory x509Factory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + ByteArrayInputStream bais = new ByteArrayInputStream(derBytes); + Certificate certificate = x509Factory.generateCertificate(bais); + + // 6. store it, w/ its private key, associating them to alias + chain = new Certificate[] { certificate }; + store.setKeyEntry(alias, privateKey, keyPasswordChars, chain); + + // 7. persist the key store + saveKeyStore(); + if (Configuration.DEBUG) + log.exiting(getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.SELFCERT_CMD, true); + result.setHeader(Messages.getString("SelfCertCmd.14")); //$NON-NLS-1$ + result.setFooter(Messages.getString("SelfCertCmd.15")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("SelfCertCmd.16")); //$NON-NLS-1$ + options.add(new Option(Main.ALIAS_OPT, + Messages.getString("SelfCertCmd.17"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.18")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _alias = argument; + } + }); + options.add(new Option(Main.SIGALG_OPT, + Messages.getString("SelfCertCmd.19"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.20")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _sigAlgorithm = argument; + } + }); + options.add(new Option(Main.DNAME_OPT, + Messages.getString("SelfCertCmd.21"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.22")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _dName = argument; + } + }); + options.add(new Option(Main.KEYPASS_OPT, + Messages.getString("SelfCertCmd.23"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.24")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _password = argument; + } + }); + options.add(new Option(Main.VALIDITY_OPT, + Messages.getString("SelfCertCmd.25"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.26")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _validityStr = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("SelfCertCmd.27"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.28")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("SelfCertCmd.29"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.30")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("SelfCertCmd.31"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.32")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("SelfCertCmd.33"), //$NON-NLS-1$ + Messages.getString("SelfCertCmd.34")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("SelfCertCmd.35")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + private void setDName(String name, X500Principal defaultName) + { + if (name != null && name.trim().length() > 0) + name = name.trim(); + else + { + // If dname is supplied at the command line, it is used as the X.500 + // Distinguished Name for both the issuer and subject of the certificate. + // Otherwise, the X.500 Distinguished Name associated with alias (at the + // bottom of its existing certificate chain) is used. + name = defaultName.toString().trim(); + } + + distinguishedName = new X500DistinguishedName(name); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/keytool/StorePasswdCmd.java b/libjava/classpath/tools/gnu/classpath/tools/keytool/StorePasswdCmd.java new file mode 100644 index 000000000..24a4b0fa1 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/keytool/StorePasswdCmd.java @@ -0,0 +1,318 @@ +/* StorePasswdCmd.java -- The storepasswd command handler of the keytool + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.keytool; + +import gnu.classpath.Configuration; +import gnu.classpath.SystemProperties; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; + +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-storepasswd</b> keytool command handler is used to change the + * password which protects the integrity of the key store. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-new PASSWORD</dt> + * <dd>The new, and different, password which will be used to protect the + * designated key store. + * <p></dd> + * + * <dt>-storetype STORE_TYPE</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class StorePasswdCmd extends Command +{ + private static final Logger log = Logger.getLogger(StorePasswdCmd.class.getName()); + protected String _newPassword; + protected String _ksType; + protected String _ksURL; + protected String _ksPassword; + protected String _providerClassName; + private char[] newStorePasswordChars; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param password the new key-store password to use. */ + public void setNew(String password) + { + this._newPassword = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setNewKeystorePassword(_newPassword); + if (Configuration.DEBUG) + { + log.fine("-storepasswd handler will use the following options:"); //$NON-NLS-1$ + log.fine(" -storetype=" + storeType); //$NON-NLS-1$ + log.fine(" -keystore=" + storeURL); //$NON-NLS-1$ + log.fine(" -provider=" + provider); //$NON-NLS-1$ + log.fine(" -v=" + verbose); //$NON-NLS-1$ + } + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + saveKeyStore(newStorePasswordChars); + if (Configuration.DEBUG) + log.exiting(getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + Parser getParser() + { + if (Configuration.DEBUG) + log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$ + Parser result = new ClasspathToolParser(Main.STOREPASSWD_CMD, true); + result.setHeader(Messages.getString("StorePasswdCmd.18")); //$NON-NLS-1$ + result.setFooter(Messages.getString("StorePasswdCmd.17")); //$NON-NLS-1$ + OptionGroup options = new OptionGroup(Messages.getString("StorePasswdCmd.16")); //$NON-NLS-1$ + options.add(new Option(Main.NEW_OPT, + Messages.getString("StorePasswdCmd.15"), //$NON-NLS-1$ + Messages.getString("StorePasswdCmd.8")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _newPassword = argument; + } + }); + options.add(new Option(Main.STORETYPE_OPT, + Messages.getString("StorePasswdCmd.13"), //$NON-NLS-1$ + Messages.getString("StorePasswdCmd.12")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksType = argument; + } + }); + options.add(new Option(Main.KEYSTORE_OPT, + Messages.getString("StorePasswdCmd.11"), //$NON-NLS-1$ + Messages.getString("StorePasswdCmd.10")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksURL = argument; + } + }); + options.add(new Option(Main.STOREPASS_OPT, + Messages.getString("StorePasswdCmd.9"), //$NON-NLS-1$ + Messages.getString("StorePasswdCmd.8")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _ksPassword = argument; + } + }); + options.add(new Option(Main.PROVIDER_OPT, + Messages.getString("StorePasswdCmd.7"), //$NON-NLS-1$ + Messages.getString("StorePasswdCmd.6")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + _providerClassName = argument; + } + }); + options.add(new Option(Main.VERBOSE_OPT, + Messages.getString("StorePasswdCmd.5")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + result.add(options); + if (Configuration.DEBUG) + log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$ + return result; + } + + protected void setNewKeystorePassword(String password) throws IOException, + UnsupportedCallbackException + { + if (password != null) + newStorePasswordChars = password.toCharArray(); + else + { + boolean ok = false; + Callback[] prompts = new Callback[1]; + Callback[] errors = new Callback[1]; + for (int i = 0; i < 3; i++) + if (prompt4NewPassword(getCallbackHandler(), prompts, errors)) + { + ok = true; + break; + } + if (! ok) + throw new SecurityException(Messages.getString("StorePasswdCmd.19")); //$NON-NLS-1$ + } + } + + private boolean prompt4NewPassword(CallbackHandler handler, + Callback[] prompts, Callback[] errors) + throws IOException, UnsupportedCallbackException + { + // prompt user (1st time) to provide one + String p = Messages.getString("StorePasswdCmd.20"); //$NON-NLS-1$ + PasswordCallback pcb = new PasswordCallback(p, false); + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd1 = pcb.getPassword(); + pcb.clearPassword(); + String ls = SystemProperties.getProperty("line.separator"); //$NON-NLS-1$ + if (pwd1 == null || pwd1.length < 6) + { + String m = Messages.getString("StorePasswdCmd.21") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + if (Arrays.equals(storePasswordChars, pwd1)) + { + String m = Messages.getString("StorePasswdCmd.22") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + // prompt user (2nd time) for confirmation + pcb = new PasswordCallback(Messages.getString("StorePasswdCmd.23"), false); //$NON-NLS-1$ + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd2 = pcb.getPassword(); + pcb.clearPassword(); + if (! Arrays.equals(pwd1, pwd2)) + { + String m = Messages.getString("StorePasswdCmd.24") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + newStorePasswordChars = pwd2; + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/native2ascii/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/native2ascii/Messages.java new file mode 100644 index 000000000..4c6bae4dc --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/native2ascii/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- translation support for native2ascii + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.native2ascii; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.native2ascii.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/native2ascii/Native2ASCII.java b/libjava/classpath/tools/gnu/classpath/tools/native2ascii/Native2ASCII.java new file mode 100644 index 000000000..524796d3b --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/native2ascii/Native2ASCII.java @@ -0,0 +1,194 @@ +/* Native2ASCII.java - native2ascii program + Copyright (C) 2003, 2007, 2008 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.native2ascii; + +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.FileArgumentCallback; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +/** + * Native2ASCII main program. + * @author Ito Kazumitsu <kaz@maczuka.gcd.org> + */ +public class Native2ASCII +{ + // Input file. + String input; + // Output file. + String output; + // Encoding to use. + String encoding; + // True for reverse operation. + boolean reversed; + + private class HandleFile extends FileArgumentCallback + { + public HandleFile() + { + } + + public void notifyFile(String fileArgument) + throws OptionException + { + if (input == null) + input = fileArgument; + else if (output == null) + output = fileArgument; + else + throw new OptionException(Messages.getString("Native2ASCII.TooManyFiles")); //$NON-NLS-1$ + } + } + + private Parser createParser() + { + Parser result = new ClasspathToolParser("native2ascii", true); //$NON-NLS-1$ + result.setHeader(Messages.getString("Native2ASCII.Usage")); //$NON-NLS-1$ + + result.add(new Option("encoding", Messages.getString("Native2ASCII.EncodingHelp"), Messages.getString("Native2ASCII.EncodingArgName")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + { + public void parsed(String argument) throws OptionException + { + if (encoding != null) + throw new OptionException(Messages.getString("Native2ASCII.EncodingSpecified")); //$NON-NLS-1$ + encoding = argument; + } + }); + result.add(new Option("reverse", Messages.getString("Native2ASCII.ReverseHelp")) //$NON-NLS-1$ //$NON-NLS-2$ + { + public void parsed(String argument) throws OptionException + { + reversed = true; + } + }); + + // We mistakenly added the extra "d" in "reversed"; now we don't + // want to remove it, for backward compatibility. + result.add(new Option("reversed", Messages.getString("Native2ASCII.ReversedHelpCompat")) //$NON-NLS-1$ //$NON-NLS-2$ + { + public void parsed(String argument) throws OptionException + { + reversed = true; + } + }); + + return result; + } + + private void run(String[] args) + { + Parser argParser = createParser(); + argParser.parse(args, new HandleFile()); + + if (encoding == null) + encoding = System.getProperty("file.encoding"); //$NON-NLS-1$ + try + { + InputStream is = (input == null ? System.in + : new FileInputStream(input)); + OutputStream os = (output == null ? (OutputStream) System.out + : new FileOutputStream(output)); + + BufferedReader rdr = new BufferedReader(new InputStreamReader(is, + encoding)); + PrintWriter wtr = new PrintWriter( + new BufferedWriter( + new OutputStreamWriter( + os, + encoding))); + while (true) + { + String s = rdr.readLine(); + if (s == null) + break; + StringBuilder sb = new StringBuilder(s.length() + 80); + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + if (reversed + && i + 6 <= s.length() + && s.charAt(i) == '\\' + && s.charAt(i + 1) == 'u') + { + int num = Integer.parseInt(s.substring(i + 2, i + 6), 16); + sb.append((char) num); + i += 5; + } + else if ((int)c <= 127 || reversed) + { + sb.append(c); + } + else + { + sb.append("\\u"); //$NON-NLS-1$ + if ((int)c <= 0xff) + sb.append("00"); //$NON-NLS-1$ + else if ((int)c <= 0xfff) + sb.append("0"); //$NON-NLS-1$ + sb.append(Integer.toHexString((int) c)); + } + } + wtr.println(sb.toString()); + } + rdr.close(); + wtr.flush(); + wtr.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public static void main(String[] args) + { + new Native2ASCII().run(args); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/orbd/Main.java b/libjava/classpath/tools/gnu/classpath/tools/orbd/Main.java new file mode 100644 index 000000000..7e970adfb --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/orbd/Main.java @@ -0,0 +1,226 @@ +/* NamingServicePersistent.java -- The persistent naming service. + Copyright (C) 2006, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.orbd; + +import gnu.CORBA.OrbFunctional; +import gnu.CORBA.IOR; +import gnu.CORBA.NamingService.Ext; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +import org.omg.CosNaming.NamingContextExt; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; + +/** + * The server for the GNU Classpath persistent naming service. + * + * GNU Classpath currently works with this naming service and is also + * interoperable with the Sun Microsystems naming services from releases 1.3 and + * 1.4, both transient <i>tnameserv</i> and persistent <i>orbd</i>. + * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class Main +{ + /** + * The default port (900), on that the naming service starts if no + * -ORBInitialPort is specified in the command line. + */ + public static final int PORT = 900; + + private int port = PORT; + private String iorf; + private boolean cold; + private String directory = ""; + + /** + * Get the object key for the naming service. The default key is the string + * "NameService" in ASCII. + * + * @return the byte array. + */ + public static byte[] getDefaultKey() + { + try + { // NameService + return "NameService".getBytes("UTF-8"); + } + catch (UnsupportedEncodingException ex) + { + throw new InternalError("UTF-8 unsupported"); + } + } + + private Parser initializeParser() + { + Parser parser = new ClasspathToolParser("orbd", true); //$NON-NLS-1$ + parser.setHeader(Messages.getString("Main.Usage")); //$NON-NLS-1$ + + parser.add(new Option("ORBInitialPort", //$NON-NLS-1$ + Messages.getString("Main.ORBInitialPort"), //$NON-NLS-1$ + Messages.getString("Main.Port")) //$NON-NLS-1$ + { + public void parsed(String portArgument) throws OptionException + { + port = Integer.parseInt(portArgument); + } + }); + + parser.add(new Option("ior", //$NON-NLS-1$ + Messages.getString("Main.IOR"), //$NON-NLS-1$ + Messages.getString("Main.IORFile")) //$NON-NLS-1$ + { + public void parsed(String fileArgument) throws OptionException + { + iorf = fileArgument; + } + }); + parser.add(new Option("directory", //$NON-NLS-1$ + Messages.getString("Main.Directory"), //$NON-NLS-1$ + Messages.getString("Main.DirectoryArgument")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + directory = argument; + } + }); + parser.add(new Option("restart", //$NON-NLS-1$ + Messages.getString("Main.Restart")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + cold = true; + } + }); + + return parser; + } + + private void run(String[] args) + { + Parser parser = initializeParser(); + parser.parse(args); + + try + { + // Create and initialize the ORB + final OrbFunctional orb = new OrbFunctional(); + OrbFunctional.setPort(port); + + // Create the servant and register it with the ORB + File dataDirectory = new File(directory); + System.out.println("Persistent data stored at " + + dataDirectory.getAbsolutePath()); + dataDirectory.mkdirs(); + + // / TODO support more starting modes. + NamingContextExt namer = new Ext( + new PersistentContext( + orb, + dataDirectory, + cold)); + + // Case with the key "NameService". + orb.connect(namer, "NameService".getBytes()); + + // Storing the IOR reference. + String ior = orb.object_to_string(namer); + IOR iorr = IOR.parse(ior); + if (iorf != null) + { + FileOutputStream f = new FileOutputStream(iorf); + PrintStream p = new PrintStream(f); + p.print(ior); + p.close(); + } + + System.out.println("GNU Classpath persistent naming service " + + "started at " + iorr.Internet.host + ":" + + iorr.Internet.port + " key 'NameService'.\n\n" + + "Copyright (C) 2011 Free Software Foundation\n" + + "This tool comes with ABSOLUTELY NO WARRANTY. " + + "This is free software, and you are\nwelcome to " + + "redistribute it under conditions, defined in " + + "GNU Classpath license.\n\n" + ior); + + new Thread() + { + public void run() + { + // Wait for invocations from clients. + orb.run(); + } + }.start(); + } + catch (FileNotFoundException e) + { + throw new RuntimeException(e); + } + finally + { + // Restore the default value for allocating ports for the subsequent + // objects. + OrbFunctional.setPort(OrbFunctional.DEFAULT_INITIAL_PORT); + } + } + + /** + * The persistent naming service entry point. + */ + public static void main(String[] args) + { + Main orbdprogram = new Main(); + try + { + orbdprogram.run(args); + } + catch (Exception e) + { + System.err.println(Messages.getString("Main.InternalError")); //$NON-NLS-1$ + e.printStackTrace(System.err); + System.exit(1); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/orbd/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/orbd/Messages.java new file mode 100644 index 000000000..c9bb371ad --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/orbd/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- localization support for orbd + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.orbd; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.orbd.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentContext.java b/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentContext.java new file mode 100644 index 000000000..4526813b8 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentContext.java @@ -0,0 +1,152 @@ +/* PersistentContext.java -- The persistent naming context. + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.orbd; + +import gnu.CORBA.NamingService.NameTransformer; +import gnu.CORBA.NamingService.TransientContext; + +import java.io.File; + +import org.omg.CORBA.ORB; +import org.omg.CosNaming.NameComponent; +import org.omg.CosNaming.NamingContext; +import org.omg.CosNaming.NamingContextPackage.AlreadyBound; +import org.omg.CosNaming.NamingContextPackage.CannotProceed; +import org.omg.CosNaming.NamingContextPackage.InvalidName; +import org.omg.CosNaming.NamingContextPackage.NotFound; + +/** + * This class implements the persistent naming service, defined by + * {@link NamingContext}. The 'persistent' means that the service remembers the + * mappings, stored between restarts. + * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class PersistentContext + extends TransientContext +{ + /** + * Use serial version UID for interoperability. + */ + private static final long serialVersionUID = 2; + + /** + * The folder, where the persistent context information is stored. + */ + File contextFolder; + + /** + * The uinque context identifier. + */ + static long num = System.currentTimeMillis(); + + /** + * The naming service orb. + */ + ORB orb; + + /** + * Create the persistent naming context that will store the files in the given + * folder of the local file system. This method also connects object to the + * passed ORB. + * + * @param an_orb the naming service ORB, used to obtain and produce the object + * stringified references. + * @param folder the folder, where the persistent information is stored. + * @param reset if true, the previous naming data are discarded. If false + * (normally expected), they are loaded from the persistent memory to + * provide the persistence. + */ + public PersistentContext(ORB an_orb, File folder, boolean reset) + { + super( + new PersistentContextMap(an_orb, new File(folder, "contexts.txt"), reset), + new PersistentMap(an_orb, new File(folder, "objects.txt"), reset)); + contextFolder = folder; + folder.mkdirs(); + orb = an_orb; + orb.connect(this); + } + + /** + * Get the unique context number; + * + * @return the context number + */ + static synchronized String getNum() + { + return Long.toHexString(num++); + } + + /** + * Create new persistent context. + */ + public NamingContext new_context() + { + File ctxFolder = new File(contextFolder, "ctx_"+getNum()); + return new PersistentContext(orb, ctxFolder, true); + } + + /** + * Create a new context and give it a given name (bound it) in the current + * context. The method benefits from passing the better readable context name. + * + * @param a_name the name being given to the new context. + * @return the newly created context. + * @throws AlreadyBound if the name is already in use. + * @throws InvalidName if the name has zero length or otherwise invalid. + */ + public NamingContext bind_new_context(NameComponent[] a_name) + throws NotFound, AlreadyBound, CannotProceed, InvalidName + { + if (named_contexts.containsKey(a_name[0]) + || named_objects.containsKey(a_name[0])) + throw new AlreadyBound(); + + NameTransformer transformer = new NameTransformer(); + + File ctxFolder = new File(contextFolder, + transformer.toString(a_name).replace('/', '.') + + ".v" + getNum()); + + NamingContext child = new PersistentContext(orb, ctxFolder, true); + bind_context(a_name, child); + return child; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentContextMap.java b/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentContextMap.java new file mode 100644 index 000000000..d83f2bf4c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentContextMap.java @@ -0,0 +1,87 @@ +/* PersistentContextMap.java -- The persistent context naming map + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.orbd; + +import java.io.File; + +import org.omg.CORBA.ORB; +import org.omg.CORBA.Object; + +/** + * The persistent context naming map for the persistent naming service. + * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class PersistentContextMap extends PersistentMap +{ + /** + * Create the persistent context map that stores information in the given + * file. + * + * @param an_orb the naming service ORB, used to obtain and produce the object + * stringified references. + * @param mapFile the file, where the persistent information is stored. + * @param reset if true, the previous naming data are discarded. If false + * (normally expected), they are loaded from the persistent memory to + * provide the persistence. + */ + public PersistentContextMap(ORB an_orb, File mapFile, boolean reset) + { + super(an_orb, mapFile, reset); + } + + /** + * This method expects the PersistentContext as its parameter. The returned + * description line is the name of the context parent folder. + */ + protected String object_to_string(Object object) + { + PersistentContext pc = (PersistentContext) object; + return pc.contextFolder.getAbsolutePath(); + } + + /** + * This method restores the PersistenContext. The description line is + * interpreted as the folder name, absolute path. + */ + protected Object string_to_object(String description) + { + return new PersistentContext(orb, new File(description), reset); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentMap.java b/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentMap.java new file mode 100644 index 000000000..a39ff28ba --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/orbd/PersistentMap.java @@ -0,0 +1,454 @@ +/* PersistentMap.java -- The persistent object naming map + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.orbd; + +import gnu.CORBA.NamingService.NamingMap; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.Map; + +import org.omg.CORBA.ORB; +import org.omg.CosNaming.NameComponent; +import org.omg.CosNaming.NamingContextPackage.AlreadyBound; +import org.omg.CosNaming.NamingContextPackage.InvalidName; + +/** + * The persistent object naming map for the persistent naming service. The + * inherited (super.) naming map implementation is transient and is used as a + * cache. During the normal work, the naming map does not read from the disk, + * just stores the changes there. Map only reads from the disk when it starts. + * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class PersistentMap + extends NamingMap +{ + /** + * The data entry. + */ + public static class Entry + { + String id; + + String kind; + + String ior; + + /** + * Get the name component node. + */ + public NameComponent getComponent() + { + return new NameComponent(id, kind); + } + + /** + * Write the naming map entry to the output stream. + */ + public void write(OutputStream out) throws IOException + { + // Format: id.kind <eoln> ior <eoln><eoln> + out.write(getKey(id, kind).getBytes()); + out.write('\n'); + out.write(ior.getBytes()); + out.write('\n'); + out.close(); + } + + /** + * Read the name component from the input stream + */ + public boolean read(BufferedReader in) throws IOException + { + String key = in.readLine(); + String xior = in.readLine(); + + if (key != null && xior != null) + { + if (key.length() < 2) + { + // A single char key cannot have the kind part. + id = key; + kind = ""; + } + else + { + // Search for the id/kind splitter, dot: + int iks = - 1; + for (int i = 1; i < key.length(); i++) + { + if (key.charAt(i) == '.') + // The id is separated from kind by dot, unless preceeded by + // the + // escape character, \. + if (key.charAt(i - 1) != '\\') + { + iks = i; + break; + } + } + + // May also end by dot, if the kind field is missing. + if (iks < 0) + { + id = key; + kind = ""; + } + else if (iks == key.length() - 1) + { + id = key.substring(0, key.length() - 1); + kind = ""; + } + else + { + id = key.substring(0, iks); + kind = key.substring(iks + 1); + } + } + ior = xior; + return true; + } + else + return false; + } + + /** + * Get the key value from the name component. + * + * @param id the component id + * @param kind the component kind + * @return the key value + */ + public String getKey(String id, String kind) + { + StringBuilder b = new StringBuilder(id.length() + 8); + appEscaping(b, id); + b.append('.'); + if (kind != null && kind.length() > 0) + appEscaping(b, kind); + return b.toString(); + } + + /** + * Append the contents of the string to this string buffer, inserting the + * escape sequences, where required. + * + * @param b a buffer to append the contents to. + * @param s a string to append. + */ + void appEscaping(StringBuilder b, String s) + { + char c; + for (int i = 0; i < s.length(); i++) + { + c = s.charAt(i); + switch (c) + { + case '.': + case '/': + case '\\': + b.append('\\'); + b.append(c); + break; + + default: + b.append(c); + break; + } + } + } + } + + /** + * The file, where the persistent naming map stores the information. The + * format of this file is n*(id LF kind LF ior LFLF). + */ + public final File file; + + /** + * The naming service ORB, used to obtain and produce the object stringified + * references. + */ + ORB orb; + + /** + * If true, all existing data on the file system are discarded. + */ + boolean reset; + + /** + * Create the persistent map that stores information in the given file. + * + * @param an_orb the naming service ORB, used to obtain and produce the object + * stringified references. + * @param mapFile the file, where the persistent information is stored. + * @param a_reset if true, the previous naming data are discarded. If false + * (normally expected), they are loaded from the persistent memory to + * provide the persistence. + */ + public PersistentMap(ORB an_orb, File mapFile, boolean a_reset) + { + super(); + orb = an_orb; + file = mapFile; + reset = a_reset; + + // Initialise the persistent map with existing data. + if (file.exists() && ! reset) + { + + BufferedReader in; + try + { + FileInputStream fin = new FileInputStream(file); + in = new BufferedReader(new InputStreamReader(fin)); + Entry e = new Entry(); + boolean ok; + + while (e.read(in)) + { + org.omg.CORBA .Object object = string_to_object(e.ior); + orb.connect(object); + map.put(e.getComponent(), object); + } + } + catch (Exception ex) + { + InternalError ierr = new InternalError(file.getAbsolutePath()); + ierr.initCause(ex); + throw ierr; + } + } + } + + /** + * Restore object from its string description. + * + * @param description the string, describing the object + * + * @return the object. + */ + protected org.omg.CORBA.Object string_to_object(String description) + { + return orb.string_to_object(description); + } + + /** + * Convert the object to its string description + * + * @param object the object to convert + * @return the string description of the object + */ + protected String object_to_string(org.omg.CORBA .Object object) + { + return orb.object_to_string(object); + } + + /** + * Put the given GIOP object, specifying the given name as a key. If the entry + * with the given name already exists, or if the given object is already + * mapped under another name, the {@link AlreadyBound} exception will be + * thrown. + * + * @param name the name + * @param object the object + */ + public void bind(NameComponent name, org.omg.CORBA.Object object) + throws AlreadyBound, InvalidName + { + if (!containsKey(name)) + { + super.bind(name, object); + register(name, object); + } + else + throw new AlreadyBound(name.id + "." + name.kind); + } + + /** + * Put the given CORBA object, specifying the given name as a key. Remove all + * pre - existing mappings for the given name and object. + * + * @param name the name. + * @param object the object + */ + public void rebind(NameComponent name, org.omg.CORBA.Object object) + throws InvalidName + { + if (containsKey(name)) + { + org.omg.CORBA.Object existing = get(name); + String ior = object_to_string(object); + String xior = object_to_string(existing); + + // Same name and same ior - nothing to do. + if (ior.equals(xior)) + return; + else + remove(name); + } + + Iterator iter = entries().iterator(); + Map.Entry item; + + // Remove the existing mapping for the given object, if present. + while (iter.hasNext()) + { + item = (Map.Entry) iter.next(); + if (item.getValue().equals(object)) + iter.remove(); + } + + map.put(name, object); + register(name, object); + } + + /** + * Removes the given name, if present. + * + * @param name a name to remove. + */ + public void remove(NameComponent name) + { + super.remove(name); + unregister(name); + } + + /** + * Register this name - object pair in the persistent storage. + * + * @param name the name. + * @param object the object + */ + public void register(NameComponent name, org.omg.CORBA.Object object) + { + // If this key is already known, and this is the same object, + // then return without action. + String ior = object_to_string(object); + + synchronized (file) + { + try + { + FileOutputStream fou; + + if (! file.exists()) + fou = new FileOutputStream(file); + else + fou = new FileOutputStream(file, true); + + Entry e = new Entry(); + e.id = name.id; + e.kind = name.kind; + e.ior = ior; + e.write(fou); + fou.close(); + } + catch (Exception e) + { + InternalError ierr = new InternalError(file.getAbsolutePath()); + ierr.initCause(e); + throw ierr; + } + } + } + + /** + * Remove this name from the persistent storage. + * + * @param name the name to remove + */ + public void unregister(NameComponent name) + { + synchronized (file) + { + try + { + File nf = new File(file.getParent(), file.getName() + "_t"); + FileInputStream fin = new FileInputStream(file); + FileOutputStream fou = new FileOutputStream(nf); + BufferedOutputStream ou = new BufferedOutputStream(fou); + + BufferedReader in = new BufferedReader(new InputStreamReader(fin)); + String s; + String nk = name.kind; + if (nk == null) + nk = ""; + + Entry e = new Entry(); + + while (e.read(in)) + { + if (e.id.equals(name.id) && e.kind.equals(nk)) + { + // Do nothing - skip. + } + else + { + e.write(ou); + } + } + + File deleteIt = new File(file.getParent(), file.getName() + "_d"); + if (deleteIt.exists()) + deleteIt.delete(); + + if (! file.renameTo(deleteIt)) + throw new IOException(file.getAbsolutePath() + " rename failed"); + + if (! nf.renameTo(file)) + throw new IOException(file.getAbsolutePath() + " rename failed"); + } + catch (Exception e) + { + InternalError ierr = new InternalError(file.getAbsolutePath()); + ierr.initCause(e); + throw ierr; + } + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/AbstractMethodGenerator.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/AbstractMethodGenerator.java new file mode 100644 index 000000000..7c6b7222f --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/AbstractMethodGenerator.java @@ -0,0 +1,53 @@ +/* AbstractMethodGenerator.java -- the abstract method generator + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + +public interface AbstractMethodGenerator +{ + /** + * Generate this method for the Stub (remote caller) class. + */ + String generateStubMethod(); + + /** + * Generate this method for the Tie (remote servant) class. + */ + String generateTieMethod(); + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/ClassRmicCompiler.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/ClassRmicCompiler.java new file mode 100644 index 000000000..9ac103c75 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/ClassRmicCompiler.java @@ -0,0 +1,1834 @@ +/* ClassRmicCompiler.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmic; + +import gnu.java.rmi.server.RMIHashes; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.rmi.MarshalException; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.UnexpectedException; +import java.rmi.UnmarshalException; +import java.rmi.server.Operation; +import java.rmi.server.RemoteCall; +import java.rmi.server.RemoteObject; +import java.rmi.server.RemoteRef; +import java.rmi.server.RemoteStub; +import java.rmi.server.Skeleton; +import java.rmi.server.SkeletonMismatchException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; + +public class ClassRmicCompiler + implements RmicBackend +{ + private String[] args; + private int next; + private List errors = new ArrayList(); + private boolean keep = false; + private boolean need11Stubs = true; + private boolean need12Stubs = true; + private boolean compile = true; + private boolean verbose; + private boolean noWrite; + private String destination; + private String classpath; + private ClassLoader loader; + private int errorCount = 0; + + private Class clazz; + private String classname; + private String classInternalName; + private String fullclassname; + private MethodRef[] remotemethods; + private String stubname; + private String skelname; + private List mRemoteInterfaces; + + /** + * @return true if run was successful + */ + public boolean run(String[] inputFiles) + { + args = inputFiles; + + if (next >= args.length) + return false; + + for (int i = next; i < args.length; i++) + { + try + { + if (verbose) + System.out.println("[Processing class " + args[i] + ".class]"); + processClass(args[i].replace(File.separatorChar, '.')); + } + catch (IOException e) + { + errors.add(e); + } + catch (RMICException e) + { + errors.add(e); + } + } + if (errors.size() > 0) + { + for (Iterator it = errors.iterator(); it.hasNext(); ) + { + Exception ex = (Exception) it.next(); + logError(ex); + } + } + + return errorCount == 0; + } + + private void processClass(String cls) throws IOException, RMICException + { + // reset class specific vars + clazz = null; + classname = null; + classInternalName = null; + fullclassname = null; + remotemethods = null; + stubname = null; + skelname = null; + mRemoteInterfaces = new ArrayList(); + + analyzeClass(cls); + generateStub(); + if (need11Stubs) + generateSkel(); + } + + private void analyzeClass(String cname) + throws RMICException + { + if (verbose) + System.out.println("[analyze class " + cname + "]"); + int p = cname.lastIndexOf('.'); + if (p != -1) + classname = cname.substring(p + 1); + else + classname = cname; + fullclassname = cname; + + findClass(); + findRemoteMethods(); + } + + /** + * @deprecated + */ + public Exception getException() + { + return errors.size() == 0 ? null : (Exception) errors.get(0); + } + + private void findClass() + throws RMICException + { + ClassLoader cl = (loader == null + ? ClassLoader.getSystemClassLoader() + : loader); + try + { + clazz = Class.forName(fullclassname, false, cl); + } + catch (ClassNotFoundException cnfe) + { + throw new RMICException + ("Class " + fullclassname + " not found in classpath", cnfe); + } + + if (! Remote.class.isAssignableFrom(clazz)) + { + throw new RMICException + ("Class " + clazz.getName() + + " does not implement a remote interface."); + } + } + + private static Type[] typeArray(Class[] cls) + { + Type[] t = new Type[cls.length]; + for (int i = 0; i < cls.length; i++) + { + t[i] = Type.getType(cls[i]); + } + + return t; + } + + private static String[] internalNameArray(Type[] t) + { + String[] s = new String[t.length]; + for (int i = 0; i < t.length; i++) + { + s[i] = t[i].getInternalName(); + } + + return s; + } + + private static String[] internalNameArray(Class[] c) + { + return internalNameArray(typeArray(c)); + } + + private static final String forName = "class$"; + + private static Object param(Method m, int argIndex) + { + List l = new ArrayList(); + l.add(m); + l.add(new Integer(argIndex)); + return l; + } + + private static void generateClassForNamer(ClassVisitor cls) + { + MethodVisitor cv = + cls.visitMethod + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, forName, + Type.getMethodDescriptor + (Type.getType(Class.class), new Type[] { Type.getType(String.class) }), + null, null); + + Label start = new Label(); + cv.visitLabel(start); + cv.visitVarInsn(Opcodes.ALOAD, 0); + cv.visitMethodInsn + (Opcodes.INVOKESTATIC, + Type.getInternalName(Class.class), + "forName", + Type.getMethodDescriptor + (Type.getType(Class.class), new Type[] { Type.getType(String.class) })); + cv.visitInsn(Opcodes.ARETURN); + + Label handler = new Label(); + cv.visitLabel(handler); + cv.visitVarInsn(Opcodes.ASTORE, 1); + cv.visitTypeInsn(Opcodes.NEW, typeArg(NoClassDefFoundError.class)); + cv.visitInsn(Opcodes.DUP); + cv.visitVarInsn(Opcodes.ALOAD, 1); + cv.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, + Type.getInternalName(ClassNotFoundException.class), + "getMessage", + Type.getMethodDescriptor(Type.getType(String.class), new Type[] {})); + cv.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(NoClassDefFoundError.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class) })); + cv.visitInsn(Opcodes.ATHROW); + cv.visitTryCatchBlock + (start, handler, handler, + Type.getInternalName(ClassNotFoundException.class)); + cv.visitMaxs(-1, -1); + } + + private void generateClassConstant(MethodVisitor cv, Class cls) { + if (cls.isPrimitive()) + { + Class boxCls; + if (cls.equals(Boolean.TYPE)) + boxCls = Boolean.class; + else if (cls.equals(Character.TYPE)) + boxCls = Character.class; + else if (cls.equals(Byte.TYPE)) + boxCls = Byte.class; + else if (cls.equals(Short.TYPE)) + boxCls = Short.class; + else if (cls.equals(Integer.TYPE)) + boxCls = Integer.class; + else if (cls.equals(Long.TYPE)) + boxCls = Long.class; + else if (cls.equals(Float.TYPE)) + boxCls = Float.class; + else if (cls.equals(Double.TYPE)) + boxCls = Double.class; + else if (cls.equals(Void.TYPE)) + boxCls = Void.class; + else + throw new IllegalArgumentException("unknown primitive type " + cls); + + cv.visitFieldInsn + (Opcodes.GETSTATIC, Type.getInternalName(boxCls), "TYPE", + Type.getDescriptor(Class.class)); + return; + } + cv.visitLdcInsn(cls.getName()); + cv.visitMethodInsn + (Opcodes.INVOKESTATIC, classInternalName, forName, + Type.getMethodDescriptor + (Type.getType(Class.class), + new Type[] { Type.getType(String.class) })); + } + + private void generateClassArray(MethodVisitor code, Class[] classes) + { + code.visitLdcInsn(new Integer(classes.length)); + code.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Class.class)); + for (int i = 0; i < classes.length; i++) + { + code.visitInsn(Opcodes.DUP); + code.visitLdcInsn(new Integer(i)); + generateClassConstant(code, classes[i]); + code.visitInsn(Opcodes.AASTORE); + } + } + + private void fillOperationArray(MethodVisitor clinit) + { + // Operations array + clinit.visitLdcInsn(new Integer(remotemethods.length)); + clinit.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Operation.class)); + clinit.visitFieldInsn + (Opcodes.PUTSTATIC, classInternalName, "operations", + Type.getDescriptor(Operation[].class)); + + for (int i = 0; i < remotemethods.length; i++) + { + Method m = remotemethods[i].meth; + + StringBuilder desc = new StringBuilder(); + desc.append(getPrettyName(m.getReturnType()) + " "); + desc.append(m.getName() + "("); + + // signature + Class[] sig = m.getParameterTypes(); + for (int j = 0; j < sig.length; j++) + { + desc.append(getPrettyName(sig[j])); + if (j + 1 < sig.length) + desc.append(", "); + } + + // push operations array + clinit.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "operations", + Type.getDescriptor(Operation[].class)); + + // push array index + clinit.visitLdcInsn(new Integer(i)); + + // instantiate operation and leave a copy on the stack + clinit.visitTypeInsn(Opcodes.NEW, typeArg(Operation.class)); + clinit.visitInsn(Opcodes.DUP); + clinit.visitLdcInsn(desc.toString()); + clinit.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(Operation.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class) })); + + // store in operations array + clinit.visitInsn(Opcodes.AASTORE); + } + } + + private void generateStaticMethodObjs(MethodVisitor clinit) + { + for (int i = 0; i < remotemethods.length; i++) + { + Method m = remotemethods[i].meth; + + /* + * $method_<i>m.getName()</i>_<i>i</i> = + * <i>m.getDeclaringClass()</i>.class.getMethod + * (m.getName(), m.getParameterType()) + */ + String methodVar = "$method_" + m.getName() + "_" + i; + generateClassConstant(clinit, m.getDeclaringClass()); + clinit.visitLdcInsn(m.getName()); + generateClassArray(clinit, m.getParameterTypes()); + clinit.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, + Type.getInternalName(Class.class), + "getMethod", + Type.getMethodDescriptor + (Type.getType(Method.class), + new Type[] { Type.getType(String.class), + Type.getType(Class[].class) })); + + clinit.visitFieldInsn + (Opcodes.PUTSTATIC, classInternalName, methodVar, + Type.getDescriptor(Method.class)); + } + } + + private void generateStub() + throws IOException + { + stubname = fullclassname + "_Stub"; + String stubclassname = classname + "_Stub"; + File file = new File((destination == null ? "." : destination) + + File.separator + + stubname.replace('.', File.separatorChar) + + ".class"); + + if (verbose) + System.out.println("[Generating class " + stubname + "]"); + + final ClassWriter stub = new ClassWriter(true); + classInternalName = stubname.replace('.', '/'); + final String superInternalName = + Type.getType(RemoteStub.class).getInternalName(); + + String[] remoteInternalNames = + internalNameArray((Class[]) mRemoteInterfaces.toArray(new Class[] {})); + stub.visit + (Opcodes.V1_2, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, classInternalName, + null, superInternalName, remoteInternalNames); + + if (need12Stubs) + { + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "serialVersionUID", + Type.LONG_TYPE.getDescriptor(), null, new Long(2L)); + } + + if (need11Stubs) + { + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, + "interfaceHash", Type.LONG_TYPE.getDescriptor(), null, + new Long(RMIHashes.getInterfaceHash(clazz))); + + if (need12Stubs) + { + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, "useNewInvoke", + Type.BOOLEAN_TYPE.getDescriptor(), null, null); + } + + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, + "operations", Type.getDescriptor(Operation[].class), null, null); + } + + // Set of method references. + if (need12Stubs) + { + for (int i = 0; i < remotemethods.length; i++) + { + Method m = remotemethods[i].meth; + String slotName = "$method_" + m.getName() + "_" + i; + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, slotName, + Type.getDescriptor(Method.class), null, null); + } + } + + MethodVisitor clinit = stub.visitMethod + (Opcodes.ACC_STATIC, "<clinit>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null); + + if (need11Stubs) + { + fillOperationArray(clinit); + if (! need12Stubs) + clinit.visitInsn(Opcodes.RETURN); + } + + if (need12Stubs) + { + // begin of try + Label begin = new Label(); + + // beginning of catch + Label handler = new Label(); + clinit.visitLabel(begin); + + // Initialize the methods references. + if (need11Stubs) + { + /* + * RemoteRef.class.getMethod("invoke", new Class[] { + * Remote.class, Method.class, Object[].class, long.class }) + */ + generateClassConstant(clinit, RemoteRef.class); + clinit.visitLdcInsn("invoke"); + generateClassArray + (clinit, new Class[] { Remote.class, Method.class, + Object[].class, long.class }); + clinit.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, + Type.getInternalName(Class.class), + "getMethod", + Type.getMethodDescriptor + (Type.getType(Method.class), + new Type[] { Type.getType(String.class), + Type.getType(Class[].class) })); + + // useNewInvoke = true + clinit.visitInsn(Opcodes.ICONST_1); + clinit.visitFieldInsn + (Opcodes.PUTSTATIC, classInternalName, "useNewInvoke", + Type.BOOLEAN_TYPE.getDescriptor()); + } + + generateStaticMethodObjs(clinit); + + // jump past handler + clinit.visitInsn(Opcodes.RETURN); + clinit.visitLabel(handler); + if (need11Stubs) + { + // useNewInvoke = false + clinit.visitInsn(Opcodes.ICONST_0); + clinit.visitFieldInsn + (Opcodes.PUTSTATIC, classInternalName, "useNewInvoke", + Type.BOOLEAN_TYPE.getDescriptor()); + clinit.visitInsn(Opcodes.RETURN); + } + else + { + // throw NoSuchMethodError + clinit.visitTypeInsn(Opcodes.NEW, typeArg(NoSuchMethodError.class)); + clinit.visitInsn(Opcodes.DUP); + clinit.visitLdcInsn("stub class initialization failed"); + clinit.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(NoSuchMethodError.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(String.class) })); + clinit.visitInsn(Opcodes.ATHROW); + } + + clinit.visitTryCatchBlock + (begin, handler, handler, + Type.getInternalName(NoSuchMethodException.class)); + + } + + clinit.visitMaxs(-1, -1); + + generateClassForNamer(stub); + + // Constructors + if (need11Stubs) + { + // no arg public constructor + MethodVisitor code = stub.visitMethod + (Opcodes.ACC_PUBLIC, "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), + null, null); + code.visitVarInsn(Opcodes.ALOAD, 0); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, superInternalName, "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); + code.visitInsn(Opcodes.RETURN); + + code.visitMaxs(-1, -1); + } + + // public RemoteRef constructor + MethodVisitor constructor = stub.visitMethod + (Opcodes.ACC_PUBLIC, "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)}), + null, null); + constructor.visitVarInsn(Opcodes.ALOAD, 0); + constructor.visitVarInsn(Opcodes.ALOAD, 1); + constructor.visitMethodInsn + (Opcodes.INVOKESPECIAL, superInternalName, "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)})); + constructor.visitInsn(Opcodes.RETURN); + constructor.visitMaxs(-1, -1); + + // Method implementations + for (int i = 0; i < remotemethods.length; i++) + { + Method m = remotemethods[i].meth; + Class[] sig = m.getParameterTypes(); + Class returntype = m.getReturnType(); + Class[] except = sortExceptions + ((Class[]) remotemethods[i].exceptions.toArray(new Class[0])); + + MethodVisitor code = stub.visitMethod + (Opcodes.ACC_PUBLIC, + m.getName(), + Type.getMethodDescriptor(Type.getType(returntype), typeArray(sig)), + null, + internalNameArray(typeArray(except))); + + final Variables var = new Variables(); + + // this and parameters are the declared vars + var.declare("this"); + for (int j = 0; j < sig.length; j++) + var.declare(param(m, j), size(sig[j])); + + Label methodTryBegin = new Label(); + code.visitLabel(methodTryBegin); + + if (need12Stubs) + { + Label oldInvoke = new Label(); + if (need11Stubs) + { + // if not useNewInvoke jump to old invoke + code.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "useNewInvoke", + Type.getDescriptor(boolean.class)); + code.visitJumpInsn(Opcodes.IFEQ, oldInvoke); + } + + // this.ref + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + code.visitFieldInsn + (Opcodes.GETFIELD, Type.getInternalName(RemoteObject.class), + "ref", Type.getDescriptor(RemoteRef.class)); + + // "this" is first arg to invoke + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + + // method object is second arg to invoke + String methName = "$method_" + m.getName() + "_" + i; + code.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, methName, + Type.getDescriptor(Method.class)); + + // args to remote method are third arg to invoke + if (sig.length == 0) + code.visitInsn(Opcodes.ACONST_NULL); + else + { + // create arg Object[] (with boxed primitives) and push it + code.visitLdcInsn(new Integer(sig.length)); + code.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Object.class)); + + var.allocate("argArray"); + code.visitVarInsn(Opcodes.ASTORE, var.get("argArray")); + + for (int j = 0; j < sig.length; j++) + { + int size = size(sig[j]); + int insn = loadOpcode(sig[j]); + Class box = sig[j].isPrimitive() ? box(sig[j]) : null; + + code.visitVarInsn(Opcodes.ALOAD, var.get("argArray")); + code.visitLdcInsn(new Integer(j)); + + // put argument on stack + if (box != null) + { + code.visitTypeInsn(Opcodes.NEW, typeArg(box)); + code.visitInsn(Opcodes.DUP); + code.visitVarInsn(insn, var.get(param(m, j))); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(box), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(sig[j]) })); + } + else + code.visitVarInsn(insn, var.get(param(m, j))); + + code.visitInsn(Opcodes.AASTORE); + } + + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("argArray")); + } + + // push remote operation opcode + code.visitLdcInsn(new Long(remotemethods[i].hash)); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteRef.class), + "invoke", + Type.getMethodDescriptor + (Type.getType(Object.class), + new Type[] { Type.getType(Remote.class), + Type.getType(Method.class), + Type.getType(Object[].class), + Type.LONG_TYPE })); + + if (! returntype.equals(Void.TYPE)) + { + int retcode = returnOpcode(returntype); + Class boxCls = + returntype.isPrimitive() ? box(returntype) : null; + code.visitTypeInsn + (Opcodes.CHECKCAST, typeArg(boxCls == null ? returntype : boxCls)); + if (returntype.isPrimitive()) + { + // unbox + code.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, + Type.getType(boxCls).getInternalName(), + unboxMethod(returntype), + Type.getMethodDescriptor + (Type.getType(returntype), new Type[] {})); + } + + code.visitInsn(retcode); + } + else + code.visitInsn(Opcodes.RETURN); + + + if (need11Stubs) + code.visitLabel(oldInvoke); + } + + if (need11Stubs) + { + + // this.ref.newCall(this, operations, index, interfaceHash) + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + code.visitFieldInsn + (Opcodes.GETFIELD, + Type.getInternalName(RemoteObject.class), + "ref", + Type.getDescriptor(RemoteRef.class)); + + // "this" is first arg to newCall + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + + // operations is second arg to newCall + code.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "operations", + Type.getDescriptor(Operation[].class)); + + // method index is third arg + code.visitLdcInsn(new Integer(i)); + + // interface hash is fourth arg + code.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "interfaceHash", + Type.LONG_TYPE.getDescriptor()); + + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteRef.class), + "newCall", + Type.getMethodDescriptor + (Type.getType(RemoteCall.class), + new Type[] { Type.getType(RemoteObject.class), + Type.getType(Operation[].class), + Type.INT_TYPE, + Type.LONG_TYPE })); + + // store call object on stack and leave copy on stack + var.allocate("call"); + code.visitInsn(Opcodes.DUP); + code.visitVarInsn(Opcodes.ASTORE, var.get("call")); + + Label beginArgumentTryBlock = new Label(); + code.visitLabel(beginArgumentTryBlock); + + // ObjectOutput out = call.getOutputStream(); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), + "getOutputStream", + Type.getMethodDescriptor + (Type.getType(ObjectOutput.class), new Type[] {})); + + for (int j = 0; j < sig.length; j++) + { + // dup the ObjectOutput + code.visitInsn(Opcodes.DUP); + + // get j'th arg to remote method + code.visitVarInsn(loadOpcode(sig[j]), var.get(param(m, j))); + + Class argCls = + sig[j].isPrimitive() ? sig[j] : Object.class; + + // out.writeFoo + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(ObjectOutput.class), + writeMethod(sig[j]), + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(argCls) })); + } + + // pop ObjectOutput + code.visitInsn(Opcodes.POP); + + Label iohandler = new Label(); + Label endArgumentTryBlock = new Label(); + code.visitJumpInsn(Opcodes.GOTO, endArgumentTryBlock); + code.visitLabel(iohandler); + + // throw new MarshalException(msg, ioexception); + code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + code.visitTypeInsn(Opcodes.NEW, typeArg(MarshalException.class)); + code.visitInsn(Opcodes.DUP); + code.visitLdcInsn("error marshalling arguments"); + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(MarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(String.class), + Type.getType(Exception.class) })); + code.visitInsn(Opcodes.ATHROW); + + code.visitLabel(endArgumentTryBlock); + code.visitTryCatchBlock + (beginArgumentTryBlock, iohandler, iohandler, + Type.getInternalName(IOException.class)); + + // this.ref.invoke(call) + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + code.visitFieldInsn + (Opcodes.GETFIELD, Type.getInternalName(RemoteObject.class), + "ref", Type.getDescriptor(RemoteRef.class)); + code.visitVarInsn(Opcodes.ALOAD, var.get("call")); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteRef.class), + "invoke", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(RemoteCall.class) })); + + // handle return value + boolean needcastcheck = false; + + Label beginReturnTryCatch = new Label(); + code.visitLabel(beginReturnTryCatch); + + int returncode = returnOpcode(returntype); + + if (! returntype.equals(Void.TYPE)) + { + // call.getInputStream() + code.visitVarInsn(Opcodes.ALOAD, var.get("call")); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), + "getInputStream", + Type.getMethodDescriptor + (Type.getType(ObjectInput.class), new Type[] {})); + + Class readCls = + returntype.isPrimitive() ? returntype : Object.class; + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(ObjectInput.class), + readMethod(returntype), + Type.getMethodDescriptor + (Type.getType(readCls), new Type[] {})); + + boolean castresult = false; + + if (! returntype.isPrimitive()) + { + if (! returntype.equals(Object.class)) + castresult = true; + else + needcastcheck = true; + } + + if (castresult) + code.visitTypeInsn(Opcodes.CHECKCAST, typeArg(returntype)); + + // leave result on stack for return + } + + // this.ref.done(call) + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + code.visitFieldInsn + (Opcodes.GETFIELD, + Type.getInternalName(RemoteObject.class), + "ref", + Type.getDescriptor(RemoteRef.class)); + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("call")); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteRef.class), + "done", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(RemoteCall.class) })); + + // return; or return result; + code.visitInsn(returncode); + + // exception handler + Label handler = new Label(); + code.visitLabel(handler); + code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + + // throw new UnmarshalException(msg, e) + code.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class)); + code.visitInsn(Opcodes.DUP); + code.visitLdcInsn("error unmarshalling return"); + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(UnmarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(String.class), + Type.getType(Exception.class) })); + code.visitInsn(Opcodes.ATHROW); + + Label endReturnTryCatch = new Label(); + + // catch IOException + code.visitTryCatchBlock + (beginReturnTryCatch, handler, handler, + Type.getInternalName(IOException.class)); + + if (needcastcheck) + { + // catch ClassNotFoundException + code.visitTryCatchBlock + (beginReturnTryCatch, handler, handler, + Type.getInternalName(ClassNotFoundException.class)); + } + } + + Label rethrowHandler = new Label(); + code.visitLabel(rethrowHandler); + // rethrow declared exceptions + code.visitInsn(Opcodes.ATHROW); + + boolean needgeneral = true; + for (int j = 0; j < except.length; j++) + { + if (except[j] == Exception.class) + needgeneral = false; + } + + for (int j = 0; j < except.length; j++) + { + code.visitTryCatchBlock + (methodTryBegin, rethrowHandler, rethrowHandler, + Type.getInternalName(except[j])); + } + + if (needgeneral) + { + // rethrow unchecked exceptions + code.visitTryCatchBlock + (methodTryBegin, rethrowHandler, rethrowHandler, + Type.getInternalName(RuntimeException.class)); + + Label generalHandler = new Label(); + code.visitLabel(generalHandler); + String msg = "undeclared checked exception"; + + // throw new java.rmi.UnexpectedException(msg, e) + code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + code.visitTypeInsn(Opcodes.NEW, typeArg(UnexpectedException.class)); + code.visitInsn(Opcodes.DUP); + code.visitLdcInsn(msg); + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(UnexpectedException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type [] { Type.getType(String.class), + Type.getType(Exception.class) })); + code.visitInsn(Opcodes.ATHROW); + + code.visitTryCatchBlock + (methodTryBegin, rethrowHandler, generalHandler, + Type.getInternalName(Exception.class)); + } + + code.visitMaxs(-1, -1); + } + + stub.visitEnd(); + byte[] classData = stub.toByteArray(); + if (!noWrite) + { + if (file.exists()) + file.delete(); + if (file.getParentFile() != null) + file.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream(file); + fos.write(classData); + fos.flush(); + fos.close(); + } + } + + private void generateSkel() throws IOException + { + skelname = fullclassname + "_Skel"; + String skelclassname = classname + "_Skel"; + File file = new File(destination == null ? "" : destination + + File.separator + + skelname.replace('.', File.separatorChar) + + ".class"); + if (verbose) + System.out.println("[Generating class " + skelname + "]"); + + final ClassWriter skel = new ClassWriter(true); + classInternalName = skelname.replace('.', '/'); + skel.visit + (Opcodes.V1_1, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, + classInternalName, Type.getInternalName(Object.class), null, + new String[] { Type.getType(Skeleton.class).getInternalName() }); + + skel.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "interfaceHash", + Type.LONG_TYPE.getDescriptor(), null, + new Long(RMIHashes.getInterfaceHash(clazz))); + + skel.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "operations", + Type.getDescriptor(Operation[].class), null, null); + + MethodVisitor clinit = skel.visitMethod + (Opcodes.ACC_STATIC, "<clinit>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null); + + fillOperationArray(clinit); + clinit.visitInsn(Opcodes.RETURN); + + clinit.visitMaxs(-1, -1); + + // no arg public constructor + MethodVisitor init = skel.visitMethod + (Opcodes.ACC_PUBLIC, "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null); + init.visitVarInsn(Opcodes.ALOAD, 0); + init.visitMethodInsn + (Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); + init.visitInsn(Opcodes.RETURN); + init.visitMaxs(-1, -1); + + /* + * public Operation[] getOperations() + * returns a clone of the operations array + */ + MethodVisitor getOp = skel.visitMethod + (Opcodes.ACC_PUBLIC, "getOperations", + Type.getMethodDescriptor + (Type.getType(Operation[].class), new Type[] {}), + null, null); + getOp.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "operations", + Type.getDescriptor(Operation[].class)); + getOp.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, Type.getInternalName(Object.class), + "clone", Type.getMethodDescriptor(Type.getType(Object.class), + new Type[] {})); + getOp.visitTypeInsn(Opcodes.CHECKCAST, typeArg(Operation[].class)); + getOp.visitInsn(Opcodes.ARETURN); + getOp.visitMaxs(-1, -1); + + // public void dispatch(Remote, RemoteCall, int opnum, long hash) + MethodVisitor dispatch = skel.visitMethod + (Opcodes.ACC_PUBLIC, + "dispatch", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(Remote.class), + Type.getType(RemoteCall.class), + Type.INT_TYPE, Type.LONG_TYPE }), null, + new String[] { Type.getInternalName(Exception.class) }); + + Variables var = new Variables(); + var.declare("this"); + var.declare("remoteobj"); + var.declare("remotecall"); + var.declare("opnum"); + var.declareWide("hash"); + + /* + * if opnum >= 0 + * XXX it is unclear why there is handling of negative opnums + */ + dispatch.visitVarInsn(Opcodes.ILOAD, var.get("opnum")); + Label nonNegativeOpnum = new Label(); + Label opnumSet = new Label(); + dispatch.visitJumpInsn(Opcodes.IFGE, nonNegativeOpnum); + + for (int i = 0; i < remotemethods.length; i++) + { + // assign opnum if hash matches supplied hash + dispatch.visitVarInsn(Opcodes.LLOAD, var.get("hash")); + dispatch.visitLdcInsn(new Long(remotemethods[i].hash)); + Label notIt = new Label(); + dispatch.visitInsn(Opcodes.LCMP); + dispatch.visitJumpInsn(Opcodes.IFNE, notIt); + + // opnum = <opnum> + dispatch.visitLdcInsn(new Integer(i)); + dispatch.visitVarInsn(Opcodes.ISTORE, var.get("opnum")); + dispatch.visitJumpInsn(Opcodes.GOTO, opnumSet); + dispatch.visitLabel(notIt); + } + + // throw new SkeletonMismatchException + Label mismatch = new Label(); + dispatch.visitJumpInsn(Opcodes.GOTO, mismatch); + + dispatch.visitLabel(nonNegativeOpnum); + + // if opnum is already set, check that the hash matches the interface + dispatch.visitVarInsn(Opcodes.LLOAD, var.get("hash")); + dispatch.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, + "interfaceHash", Type.LONG_TYPE.getDescriptor()); + dispatch.visitInsn(Opcodes.LCMP); + dispatch.visitJumpInsn(Opcodes.IFEQ, opnumSet); + + dispatch.visitLabel(mismatch); + dispatch.visitTypeInsn + (Opcodes.NEW, typeArg(SkeletonMismatchException.class)); + dispatch.visitInsn(Opcodes.DUP); + dispatch.visitLdcInsn("interface hash mismatch"); + dispatch.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(SkeletonMismatchException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class) })); + dispatch.visitInsn(Opcodes.ATHROW); + + // opnum has been set + dispatch.visitLabel(opnumSet); + + dispatch.visitVarInsn(Opcodes.ALOAD, var.get("remoteobj")); + dispatch.visitTypeInsn(Opcodes.CHECKCAST, typeArg(clazz)); + dispatch.visitVarInsn(Opcodes.ASTORE, var.get("remoteobj")); + + Label deflt = new Label(); + Label[] methLabels = new Label[remotemethods.length]; + for (int i = 0; i < methLabels.length; i++) + methLabels[i] = new Label(); + + // switch on opnum + dispatch.visitVarInsn(Opcodes.ILOAD, var.get("opnum")); + dispatch.visitTableSwitchInsn + (0, remotemethods.length - 1, deflt, methLabels); + + // Method dispatch + for (int i = 0; i < remotemethods.length; i++) + { + dispatch.visitLabel(methLabels[i]); + Method m = remotemethods[i].meth; + generateMethodSkel(dispatch, m, var); + } + + dispatch.visitLabel(deflt); + dispatch.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class)); + dispatch.visitInsn(Opcodes.DUP); + dispatch.visitLdcInsn("invalid method number"); + dispatch.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(UnmarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class) })); + dispatch.visitInsn(Opcodes.ATHROW); + + dispatch.visitMaxs(-1, -1); + + skel.visitEnd(); + byte[] classData = skel.toByteArray(); + if (!noWrite) + { + if (file.exists()) + file.delete(); + if (file.getParentFile() != null) + file.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream(file); + fos.write(classData); + fos.flush(); + fos.close(); + } + } + + private void generateMethodSkel(MethodVisitor cv, Method m, Variables var) + { + Class[] sig = m.getParameterTypes(); + + Label readArgs = new Label(); + cv.visitLabel(readArgs); + + boolean needcastcheck = false; + + // ObjectInput in = call.getInputStream(); + cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall")); + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), "getInputStream", + Type.getMethodDescriptor + (Type.getType(ObjectInput.class), new Type[] {})); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("objectinput")); + + for (int i = 0; i < sig.length; i++) + { + // dup input stream + cv.visitVarInsn(Opcodes.ALOAD, var.get("objectinput")); + + Class readCls = sig[i].isPrimitive() ? sig[i] : Object.class; + + // in.readFoo() + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(ObjectInput.class), + readMethod(sig[i]), + Type.getMethodDescriptor + (Type.getType(readCls), new Type [] {})); + + if (! sig[i].isPrimitive() && ! sig[i].equals(Object.class)) + { + needcastcheck = true; + cv.visitTypeInsn(Opcodes.CHECKCAST, typeArg(sig[i])); + } + + // store arg in variable + cv.visitVarInsn + (storeOpcode(sig[i]), var.allocate(param(m, i), size(sig[i]))); + } + + var.deallocate("objectinput"); + + Label doCall = new Label(); + Label closeInput = new Label(); + + cv.visitJumpInsn(Opcodes.JSR, closeInput); + cv.visitJumpInsn(Opcodes.GOTO, doCall); + + // throw new UnmarshalException + Label handler = new Label(); + cv.visitLabel(handler); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + cv.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class)); + cv.visitInsn(Opcodes.DUP); + cv.visitLdcInsn("error unmarshalling arguments"); + cv.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + cv.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(UnmarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class), + Type.getType(Exception.class) })); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("toThrow")); + cv.visitJumpInsn(Opcodes.JSR, closeInput); + cv.visitVarInsn(Opcodes.ALOAD, var.get("toThrow")); + cv.visitInsn(Opcodes.ATHROW); + + cv.visitTryCatchBlock + (readArgs, handler, handler, Type.getInternalName(IOException.class)); + if (needcastcheck) + { + cv.visitTryCatchBlock + (readArgs, handler, handler, + Type.getInternalName(ClassCastException.class)); + } + + // finally block + cv.visitLabel(closeInput); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("retAddress")); + cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall")); + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), + "releaseInputStream", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); + cv.visitVarInsn(Opcodes.RET, var.deallocate("retAddress")); + var.deallocate("toThrow"); + + // do the call using args stored as variables + cv.visitLabel(doCall); + cv.visitVarInsn(Opcodes.ALOAD, var.get("remoteobj")); + for (int i = 0; i < sig.length; i++) + cv.visitVarInsn(loadOpcode(sig[i]), var.deallocate(param(m, i))); + cv.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, Type.getInternalName(clazz), m.getName(), + Type.getMethodDescriptor(m)); + + Class returntype = m.getReturnType(); + if (! returntype.equals(Void.TYPE)) + { + cv.visitVarInsn + (storeOpcode(returntype), var.allocate("result", size(returntype))); + } + + // write result to result stream + Label writeResult = new Label(); + cv.visitLabel(writeResult); + cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall")); + cv.visitInsn(Opcodes.ICONST_1); + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), + "getResultStream", + Type.getMethodDescriptor + (Type.getType(ObjectOutput.class), + new Type[] { Type.BOOLEAN_TYPE })); + + if (! returntype.equals(Void.TYPE)) + { + // out.writeFoo(result) + cv.visitVarInsn(loadOpcode(returntype), var.deallocate("result")); + Class writeCls = returntype.isPrimitive() ? returntype : Object.class; + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(ObjectOutput.class), + writeMethod(returntype), + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(writeCls) })); + } + + cv.visitInsn(Opcodes.RETURN); + + // throw new MarshalException + Label marshalHandler = new Label(); + cv.visitLabel(marshalHandler); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + cv.visitTypeInsn(Opcodes.NEW, typeArg(MarshalException.class)); + cv.visitInsn(Opcodes.DUP); + cv.visitLdcInsn("error marshalling return"); + cv.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + cv.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(MarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class), + Type.getType(Exception.class) })); + cv.visitInsn(Opcodes.ATHROW); + cv.visitTryCatchBlock + (writeResult, marshalHandler, marshalHandler, + Type.getInternalName(IOException.class)); + } + + private static String typeArg(Class cls) + { + if (cls.isArray()) + return Type.getDescriptor(cls); + + return Type.getInternalName(cls); + } + + private static String readMethod(Class cls) + { + if (cls.equals(Void.TYPE)) + throw new IllegalArgumentException("can not read void"); + + String method; + if (cls.equals(Boolean.TYPE)) + method = "readBoolean"; + else if (cls.equals(Byte.TYPE)) + method = "readByte"; + else if (cls.equals(Character.TYPE)) + method = "readChar"; + else if (cls.equals(Short.TYPE)) + method = "readShort"; + else if (cls.equals(Integer.TYPE)) + method = "readInt"; + else if (cls.equals(Long.TYPE)) + method = "readLong"; + else if (cls.equals(Float.TYPE)) + method = "readFloat"; + else if (cls.equals(Double.TYPE)) + method = "readDouble"; + else + method = "readObject"; + + return method; + } + + private static String writeMethod(Class cls) + { + if (cls.equals(Void.TYPE)) + throw new IllegalArgumentException("can not read void"); + + String method; + if (cls.equals(Boolean.TYPE)) + method = "writeBoolean"; + else if (cls.equals(Byte.TYPE)) + method = "writeByte"; + else if (cls.equals(Character.TYPE)) + method = "writeChar"; + else if (cls.equals(Short.TYPE)) + method = "writeShort"; + else if (cls.equals(Integer.TYPE)) + method = "writeInt"; + else if (cls.equals(Long.TYPE)) + method = "writeLong"; + else if (cls.equals(Float.TYPE)) + method = "writeFloat"; + else if (cls.equals(Double.TYPE)) + method = "writeDouble"; + else + method = "writeObject"; + + return method; + } + + private static int returnOpcode(Class cls) + { + int returncode; + if (cls.equals(Boolean.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Byte.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Character.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Short.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Integer.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Long.TYPE)) + returncode = Opcodes.LRETURN; + else if (cls.equals(Float.TYPE)) + returncode = Opcodes.FRETURN; + else if (cls.equals(Double.TYPE)) + returncode = Opcodes.DRETURN; + else if (cls.equals(Void.TYPE)) + returncode = Opcodes.RETURN; + else + returncode = Opcodes.ARETURN; + + return returncode; + } + + private static int loadOpcode(Class cls) + { + if (cls.equals(Void.TYPE)) + throw new IllegalArgumentException("can not load void"); + + int loadcode; + if (cls.equals(Boolean.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Byte.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Character.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Short.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Integer.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Long.TYPE)) + loadcode = Opcodes.LLOAD; + else if (cls.equals(Float.TYPE)) + loadcode = Opcodes.FLOAD; + else if (cls.equals(Double.TYPE)) + loadcode = Opcodes.DLOAD; + else + loadcode = Opcodes.ALOAD; + + return loadcode; + } + + private static int storeOpcode(Class cls) + { + if (cls.equals(Void.TYPE)) + throw new IllegalArgumentException("can not load void"); + + int storecode; + if (cls.equals(Boolean.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Byte.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Character.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Short.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Integer.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Long.TYPE)) + storecode = Opcodes.LSTORE; + else if (cls.equals(Float.TYPE)) + storecode = Opcodes.FSTORE; + else if (cls.equals(Double.TYPE)) + storecode = Opcodes.DSTORE; + else + storecode = Opcodes.ASTORE; + + return storecode; + } + + private static String unboxMethod(Class primitive) + { + if (! primitive.isPrimitive()) + throw new IllegalArgumentException("can not unbox nonprimitive"); + + String method; + if (primitive.equals(Boolean.TYPE)) + method = "booleanValue"; + else if (primitive.equals(Byte.TYPE)) + method = "byteValue"; + else if (primitive.equals(Character.TYPE)) + method = "charValue"; + else if (primitive.equals(Short.TYPE)) + method = "shortValue"; + else if (primitive.equals(Integer.TYPE)) + method = "intValue"; + else if (primitive.equals(Long.TYPE)) + method = "longValue"; + else if (primitive.equals(Float.TYPE)) + method = "floatValue"; + else if (primitive.equals(Double.TYPE)) + method = "doubleValue"; + else + throw new IllegalStateException("unknown primitive class " + primitive); + + return method; + } + + public static Class box(Class cls) + { + if (! cls.isPrimitive()) + throw new IllegalArgumentException("can only box primitive"); + + Class box; + if (cls.equals(Boolean.TYPE)) + box = Boolean.class; + else if (cls.equals(Byte.TYPE)) + box = Byte.class; + else if (cls.equals(Character.TYPE)) + box = Character.class; + else if (cls.equals(Short.TYPE)) + box = Short.class; + else if (cls.equals(Integer.TYPE)) + box = Integer.class; + else if (cls.equals(Long.TYPE)) + box = Long.class; + else if (cls.equals(Float.TYPE)) + box = Float.class; + else if (cls.equals(Double.TYPE)) + box = Double.class; + else + throw new IllegalStateException("unknown primitive type " + cls); + + return box; + } + + private static int size(Class cls) { + if (cls.equals(Long.TYPE) || cls.equals(Double.TYPE)) + return 2; + else + return 1; + } + + /** + * Sort exceptions so the most general go last. + */ + private Class[] sortExceptions(Class[] except) + { + for (int i = 0; i < except.length; i++) + { + for (int j = i + 1; j < except.length; j++) + { + if (except[i].isAssignableFrom(except[j])) + { + Class tmp = except[i]; + except[i] = except[j]; + except[j] = tmp; + } + } + } + return (except); + } + + public void setup(boolean keep, boolean need11Stubs, boolean need12Stubs, + boolean iiop, boolean poa, boolean debug, boolean warnings, + boolean noWrite, boolean verbose, boolean force, String classpath, + String bootclasspath, String extdirs, String outputDirectory) + { + this.keep = keep; + this.need11Stubs = need11Stubs; + this.need12Stubs = need12Stubs; + this.verbose = verbose; + this.noWrite = noWrite; + + // Set up classpath. + this.classpath = classpath; + StringTokenizer st = + new StringTokenizer(classpath, File.pathSeparator); + URL[] u = new URL[st.countTokens()]; + for (int i = 0; i < u.length; i++) + { + String path = st.nextToken(); + File f = new File(path); + try + { + u[i] = f.toURL(); + } + catch (java.net.MalformedURLException mue) + { + logError("malformed classpath component " + path); + return; + } + } + loader = new URLClassLoader(u); + + destination = outputDirectory; + } + + private void findRemoteMethods() + throws RMICException + { + List rmeths = new ArrayList(); + for (Class cur = clazz; cur != null; cur = cur.getSuperclass()) + { + Class[] interfaces = cur.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + { + if (java.rmi.Remote.class.isAssignableFrom(interfaces[i])) + { + Class remoteInterface = interfaces[i]; + if (verbose) + System.out.println + ("[implements " + remoteInterface.getName() + "]"); + + // check if the methods declare RemoteExceptions + Method[] meths = remoteInterface.getMethods(); + for (int j = 0; j < meths.length; j++) + { + Method m = meths[j]; + Class[] exs = m.getExceptionTypes(); + + boolean throwsRemote = false; + for (int k = 0; k < exs.length; k++) + { + if (exs[k].isAssignableFrom(RemoteException.class)) + throwsRemote = true; + } + + if (! throwsRemote) + { + throw new RMICException + ("Method " + m + " in interface " + remoteInterface + + " does not throw a RemoteException"); + } + + rmeths.add(m); + } + + mRemoteInterfaces.add(remoteInterface); + } + } + } + + // intersect exceptions for doubly inherited methods + boolean[] skip = new boolean[rmeths.size()]; + for (int i = 0; i < skip.length; i++) + skip[i] = false; + List methrefs = new ArrayList(); + for (int i = 0; i < rmeths.size(); i++) + { + if (skip[i]) continue; + Method current = (Method) rmeths.get(i); + MethodRef ref = new MethodRef(current); + for (int j = i+1; j < rmeths.size(); j++) + { + Method other = (Method) rmeths.get(j); + if (ref.isMatch(other)) + { + ref.intersectExceptions(other); + skip[j] = true; + } + } + methrefs.add(ref); + } + + // Convert into a MethodRef array and sort them + remotemethods = (MethodRef[]) + methrefs.toArray(new MethodRef[methrefs.size()]); + Arrays.sort(remotemethods); + } + + /** + * Prints an error to System.err and increases the error count. + */ + private void logError(Exception theError) + { + logError(theError.getMessage()); + if (verbose) + theError.printStackTrace(System.err); + } + + /** + * Prints an error to System.err and increases the error count. + */ + private void logError(String theError) + { + errorCount++; + System.err.println("error: " + theError); + } + + private static String getPrettyName(Class cls) + { + StringBuilder str = new StringBuilder(); + for (int count = 0;; count++) + { + if (! cls.isArray()) + { + str.append(cls.getName()); + for (; count > 0; count--) + str.append("[]"); + return (str.toString()); + } + cls = cls.getComponentType(); + } + } + + private static class MethodRef + implements Comparable + { + Method meth; + long hash; + List exceptions; + private String sig; + + MethodRef(Method m) { + meth = m; + sig = Type.getMethodDescriptor(meth); + hash = RMIHashes.getMethodHash(m); + // add exceptions removing subclasses + exceptions = removeSubclasses(m.getExceptionTypes()); + } + + public int compareTo(Object obj) { + MethodRef that = (MethodRef) obj; + int name = this.meth.getName().compareTo(that.meth.getName()); + if (name == 0) { + return this.sig.compareTo(that.sig); + } + return name; + } + + public boolean isMatch(Method m) + { + if (!meth.getName().equals(m.getName())) + return false; + + Class[] params1 = meth.getParameterTypes(); + Class[] params2 = m.getParameterTypes(); + if (params1.length != params2.length) + return false; + + for (int i = 0; i < params1.length; i++) + if (!params1[i].equals(params2[i])) return false; + + return true; + } + + private static List removeSubclasses(Class[] classes) + { + List list = new ArrayList(); + for (int i = 0; i < classes.length; i++) + { + Class candidate = classes[i]; + boolean add = true; + for (int j = 0; j < classes.length; j++) + { + if (classes[j].equals(candidate)) + continue; + else if (classes[j].isAssignableFrom(candidate)) + add = false; + } + if (add) list.add(candidate); + } + + return list; + } + + public void intersectExceptions(Method m) + { + List incoming = removeSubclasses(m.getExceptionTypes()); + + List updated = new ArrayList(); + + for (int i = 0; i < exceptions.size(); i++) + { + Class outer = (Class) exceptions.get(i); + boolean addOuter = false; + for (int j = 0; j < incoming.size(); j++) + { + Class inner = (Class) incoming.get(j); + + if (inner.equals(outer) || inner.isAssignableFrom(outer)) + addOuter = true; + else if (outer.isAssignableFrom(inner)) + updated.add(inner); + } + + if (addOuter) + updated.add(outer); + } + + exceptions = updated; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/CompilationError.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/CompilationError.java new file mode 100644 index 000000000..2bfea7cc4 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/CompilationError.java @@ -0,0 +1,69 @@ +/* CompilationError.java -- Thrown on compilation error. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + +/** + * This error is thrown when the target being compiled has illegal + * strutures. + * + * @author Audrius Meskauskas, Lithuania (audriusa@Bioinformatics.org) + */ +public class CompilationError extends Error +{ + /** + * Use serialVersionUID for interoperability. + */ + private static final long serialVersionUID = 1; + + /** + * Create error with explaining message and cause. + */ + public CompilationError(String message, Throwable cause) + { + super(message, cause); + } + + /** + * Create error with explaining message + */ + public CompilationError(String message) + { + super(message); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/Generator.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/Generator.java new file mode 100644 index 000000000..ba659d2c5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/Generator.java @@ -0,0 +1,145 @@ +/* Generator.java -- Generic code generator. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * Contains basic methods, used in code generation. + * + * @author Audrius Meskauskas, Lithuania (audriusa@Bioinformatics.org) + */ +public class Generator +{ + /** + * Get resource with the given name, as string. + * + * @param name the resource name + * @return the resourse string (in subfolder /templates). + */ + public String getResource(String name) + { + String resourcePath = "templates/" + name; + InputStream in = getClass().getResourceAsStream(resourcePath); + + if (in == null) + throw new InternalError(getClass().getName() + ": no resource " + + resourcePath); + + BufferedReader r = new BufferedReader(new InputStreamReader(in)); + StringBuilder b = new StringBuilder(); + + String s; + try + { + while ((s = r.readLine()) != null) + { + b.append(s); + b.append('\n'); + } + r.close(); + } + catch (IOException e) + { + InternalError ierr = new InternalError("No expected resource " + name); + ierr.initCause(e); + throw ierr; + } + + return b.toString(); + } + + /** + * Replace the variable references (starting from #) in the template string by + * the values, present in the given map. The strings, not present in the + * variable map, are ignored. + * + * @param template + * the template string + * @param variables + * the map of variables (name to value) to replace. + * @return the string with replaced values. + */ + public String replaceAll(String template, Map variables) + { + BufferedReader r = new BufferedReader(new StringReader(template)); + String s; + StringBuilder b = new StringBuilder(template.length()); + try + { + Iterator iter; + Collection vars = variables.keySet(); + while ((s = r.readLine()) != null) + { + // At least one variable must appear in the string to make + // the string scan sensible. + if (s.indexOf('#') >= 0) + { + iter = vars.iterator(); + String variable; + while (iter.hasNext()) + { + variable = (String) iter.next(); + if (s.indexOf(variable) >= 0) + s = s.replaceAll(variable, + (String) variables.get(variable)); + } + } + b.append(s); + b.append('\n'); + } + r.close(); + } + catch (IOException e) + { + // This should never happen. + InternalError ierr = new InternalError(""); + ierr.initCause(e); + throw ierr; + } + return b.toString(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/GiopIo.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/GiopIo.java new file mode 100644 index 000000000..c3b3bc0a4 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/GiopIo.java @@ -0,0 +1,129 @@ +/* GiopIo.java -- Generates GIOP input/output statements. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + +import java.rmi.Remote; + +import org.omg.CORBA.portable.ObjectImpl; + +/** + * Generates the code for reading and writing data over GIOP stream. + * + * @author Audrius Meskauskas, Lithuania (audriusa@Bioinformatics.org) + */ +public class GiopIo +{ + /** + * Get the statement for writing the variable of the given type to the GIOP ({@link org.omg.CORBA_2_3.portable.OutputStream) stream. The + * stream is always named "out". + * + * @param c + * the class of the object being written + * @param variable + * the variable, where the object value is stored + * @param r + * the parent generator, used to name the class + * @return the write statement. + */ + public static String getWriteStatement(Class c, String variable, SourceGiopRmicCompiler r) + { + if (c.equals(boolean.class)) + return "out.write_boolean(" + variable + ");"; + if (c.equals(byte.class)) + return "out.write_octet(" + variable + ");"; + else if (c.equals(short.class)) + return "out.write_int(" + variable + ");"; + else if (c.equals(int.class)) + return "out.write_long(" + variable + ");"; + else if (c.equals(long.class)) + return "out.write_long_long(" + variable + ");"; + else if (c.equals(double.class)) + return "out.write_double(" + variable + ");"; + else if (c.equals(float.class)) + return "out.write_float(" + variable + ");"; + else if (c.equals(char.class)) + return "out.write_char(" + variable + ");"; + else if (Remote.class.isAssignableFrom(c)) + return "Util.writeRemoteObject(out, " + variable + ");"; + else if (ObjectImpl.class.isAssignableFrom(c)) + return "out.write_Object(" + variable + ");"; + else + return "out.write_value(" + variable + ", " + r.name(c) + ".class);"; + } + + /** + * Get the statement for reading the value of the given type from to the GIOP ({@link org.omg.CORBA_2_3.portable.InputStream) stream. The + * stream is always named "in". + * + * @param c + * the class of the object being written + * @param r + * the parent generator, used to name the class + * @return the right side of the read statement. + */ + public static String getReadStatement(Class c, SourceGiopRmicCompiler r) + { + if (c.equals(boolean.class)) + return "in.read_boolean();"; + else if (c.equals(byte.class)) + return "in.read_octet();"; + else if (c.equals(short.class)) + return "in.read_int();"; + else if (c.equals(int.class)) + return "in.read_long();"; + else if (c.equals(long.class)) + return "in.read_long_long();"; + else if (c.equals(double.class)) + return "in.read_double();"; + else if (c.equals(float.class)) + return "in.read_float();"; + else if (c.equals(char.class)) + return "in.read_char();"; + else if (Remote.class.isAssignableFrom(c)) + return "(" + r.name(c) + + ") PortableRemoteObject.narrow(in.read_Object()," + r.name(c) + + ".class);"; + else if (ObjectImpl.class.isAssignableFrom(c)) + return "in.read_Object();"; + else + return "(" + r.name(c) + + ") in.read_value(" + r.name(c) + ".class);"; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/HashFinder.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/HashFinder.java new file mode 100644 index 000000000..7b6fc9bb5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/HashFinder.java @@ -0,0 +1,100 @@ +/* HashFinder.java -- finds the hash character. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + +import java.util.HashSet; + +/** + * This class finds the hash character (the most different character in + * the passed array of strings). This character is used to accelerate the + * method invocation by name. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public class HashFinder +{ + /** + * Find the hash char position in the given collection of strings. + * + * @param strings the string collection + * + * @return the optimal hash character position, always less then the + * length of the shortest string. + */ + public int findHashCharPosition(String[] strings) + { + // Find the length of the shortest string: + + int l = strings[0].length(); + for (int i = 1; i < strings.length; i++) + { + if (strings[i].length() < l) + l = strings[i].length(); + } + + // Find the position with the smallest number of the matching characters: + HashSet[] charLists = new HashSet[l]; + + for (int i = 0; i < charLists.length; i++) + { + charLists[i] = new HashSet(strings.length); + } + + for (int i = 0; i < strings.length; i++) + for (int p = 0; p < l; p++) + { + charLists[p].add(new Integer(strings[i].charAt(p))); + } + + int m = 0; + int v = charLists[0].size(); + + for (int i = 1; i < charLists.length; i++) + { + // Replace on equality also, seeking the hash char closer to the end + // of line. + if (charLists[i].size()>=v) + { + m = i; + v = charLists[i].size(); + } + } + return m; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/Main.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/Main.java new file mode 100644 index 000000000..868fc758e --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/Main.java @@ -0,0 +1,293 @@ +/* Main.java -- RMI stub generator. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +import java.util.ArrayList; + +/** + * Generates the ordinary stubs (not GIOP based) for java.rmi.* package. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public class Main +{ + private boolean noWrite; + private boolean warnings = true; + private boolean verbose; + private boolean force; + private String classpath = "."; + private String outputDirectory = "."; + private boolean poa; + private boolean need11Stubs = false; + private boolean need12Stubs = true; + private boolean keep; + private boolean iiop; + /** + * Specifies whether or not JRMP mode was explicitly requested. + */ + private boolean jrmp; + + private Parser initializeParser() + { + Parser parser = new ClasspathToolParser("rmic", true); //$NON-NLS-1$ + parser.setHeader(Messages.getString("Main.Usage")); //$NON-NLS-1$ + + parser.add(new Option("nowarn", //$NON-NLS-1$ + Messages.getString("Main.NoWarn")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + warnings = false; + } + }); + parser.add(new Option("nowrite", //$NON-NLS-1$ + Messages.getString("Main.NoWrite")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + noWrite = true; + } + }); + parser.add(new Option("verbose", //$NON-NLS-1$ + Messages.getString("Main.Verbose")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + parser.add(new Option("d", //$NON-NLS-1$ + Messages.getString("Main.DirOpt"), //$NON-NLS-1$ + Messages.getString("Main.DirArg")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + outputDirectory = argument; + } + }); + parser.add(new Option("classpath", //$NON-NLS-1$ + Messages.getString("Main.ClasspathOpt"), //$NON-NLS-1$ + Messages.getString("Main.ClasspathArg")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + classpath = argument; + } + }); + parser.add(new Option("bootclasspath", //$NON-NLS-1$ + Messages.getString("Main.BootclasspathOpt"), //$NON-NLS-1$ + Messages.getString("Main.BootclasspathArg")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + } + }); + parser.add(new Option("extdirs", //$NON-NLS-1$ + Messages.getString("Main.ExtdirsOpt"), //$NON-NLS-1$ + Messages.getString("Main.ExtdirsArg")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + } + }); + parser.add(new Option("iiop", //$NON-NLS-1$ + Messages.getString("Main.IIOP")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + iiop = true; + } + }); + parser.add(new Option("always", //$NON-NLS-1$ + Messages.getString("Main.Always")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + force = true; + } + }); + parser.add(new Option("alwaysgenerate", //$NON-NLS-1$ + Messages.getString("Main.AlwaysGenerate")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + force = true; + } + }); + parser.add(new Option("nolocalstubs", //$NON-NLS-1$ + Messages.getString("Main.NoLocalStubs")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + } + }); + parser.add(new Option("poa", //$NON-NLS-1$ + Messages.getString("Main.POA")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + poa = true; + } + }); + parser.add(new Option("keep", //$NON-NLS-1$ + Messages.getString("Main.Keep")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + keep = true; + } + }); + parser.add(new Option("keepgenerated", //$NON-NLS-1$ + Messages.getString("Main.KeepGenerated")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + keep = true; + } + }); + parser.add(new Option("v1.1", //$NON-NLS-1$ + Messages.getString("Main.v11")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + need11Stubs = true; + need12Stubs = false; + jrmp = true; + } + }); + parser.add(new Option("v1.2", //$NON-NLS-1$ + Messages.getString("Main.v12")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + jrmp = true; + } + }); + parser.add(new Option("vcompat", //$NON-NLS-1$ + Messages.getString("Main.vcompat")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + need11Stubs = true; + need12Stubs = true; + jrmp = true; + } + }); + parser.add(new Option("g", //$NON-NLS-1$ + Messages.getString("Main.DebugInfo")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + } + }); + + return parser; + } + + private void run(String[] args) + { + Parser p = initializeParser(); + String[] files = p.parse(args); + + if (files.length == 0) + { + p.printHelp(); + System.exit(1); + } + + ArrayList backends = new ArrayList(); + + // FIXME: need an IDL RmicBackend + // FIXME: need a ClassGiopRmicCompiler RmicBackend + if (iiop) + { + backends.add(new SourceGiopRmicCompiler()); + + if (jrmp) + { + // Both IIOP and JRMP stubs were requested. + backends.add(new ClassRmicCompiler()); + // FIXME: SourceRmicCompiler should support v1.1 + if (keep) + backends.add(new SourceRmicCompiler()); + } + } + else + { + backends.add(new ClassRmicCompiler()); + if (keep) + backends.add(new SourceRmicCompiler()); + } + + for (int i = 0; i < backends.size(); i++) + { + RmicBackend b = (RmicBackend) backends.get(i); + b.setup(keep, need11Stubs, need12Stubs, + iiop, poa, false, warnings, + noWrite, verbose, force, classpath, + null, null, outputDirectory); + if (!b.run(files)) + System.exit(1); + } + } + + /** + * The RMI compiler entry point. + */ + public static void main(String[] args) + { + Main rmicprogram = new Main(); + try + { + rmicprogram.run(args); + } + catch (Exception e) + { + System.err.println(Messages.getString("Main.InternalError")); //$NON-NLS-1$ + e.printStackTrace(System.err); + System.exit(1); + } + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/Messages.java new file mode 100644 index 000000000..5e67dd163 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- localization support for rmic + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.rmic.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/MethodGenerator.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/MethodGenerator.java new file mode 100644 index 000000000..27a4bd2e1 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/MethodGenerator.java @@ -0,0 +1,302 @@ +/* MethodGenerator.java -- Generates methods for GIOP rmic compiler. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + +import gnu.classpath.tools.rmic.AbstractMethodGenerator; + +import java.lang.reflect.Method; +import java.util.Properties; + +/** + * Keeps information about the single method and generates the code fragments, + * related to that method. + * + * @author Audrius Meskauskas, Lithuania (audriusa@Bioinformatics.org) + */ +public class MethodGenerator implements AbstractMethodGenerator +{ + /** + * The method being defined. + */ + Method method; + + /** + * The parent code generator. + */ + SourceGiopRmicCompiler rmic; + + /** + * The previous method in the list, null for the first element. + * Used to avoid repretetive inclusion of the same hash code label. + */ + MethodGenerator previous = null; + + /** + * The hash character position. + */ + int hashCharPosition; + + /** + * Create the new method generator for the given method. + * + * @param aMethod + * the related method. + * @param aRmic + * the Rmic generator instance, where more class - related + * information is defined. + */ + public MethodGenerator(Method aMethod, SourceGiopRmicCompiler aRmic) + { + method = aMethod; + rmic = aRmic; + } + + /** + * Get the method name. + * + * @return the name of the method. + */ + public String getGiopMethodName() + { + String m = method.getName(); + if (m.startsWith("get")) + return "_get_J" + m.substring("get".length()); + else if (m.startsWith("set")) + return "_set_J" + m.substring("set".length()); + else + return m; + } + + /** + * Get the method parameter declaration. + * + * @return the string - method parameter declaration. + */ + public String getArgumentList() + { + StringBuilder b = new StringBuilder(); + + Class[] args = method.getParameterTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(rmic.name(args[i])); + b.append(" p" + i); + if (i < args.length - 1) + b.append(", "); + } + return b.toString(); + } + + /** + * Get the method parameter list only (no type declarations). This is used to + * generate the method invocations statement. + * + * @return the string - method parameter list. + */ + public String getArgumentNames() + { + StringBuilder b = new StringBuilder(); + + Class[] args = method.getParameterTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(" p" + i); + if (i < args.length - 1) + b.append(", "); + } + return b.toString(); + } + + /** + * Get the list of exceptions, thrown by this method. + * + * @return the list of exceptions. + */ + public String getThrows() + { + StringBuilder b = new StringBuilder(); + + Class[] args = method.getExceptionTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(rmic.name(args[i])); + if (i < args.length - 1) + b.append(", "); + } + return b.toString(); + } + + /** + * Generate this method for the Stub class. + * + * @return the method body for the stub class. + */ + public String generateStubMethod() + { + String templateName; + + Properties vars = new Properties(rmic.vars); + vars.put("#return_type", rmic.name(method.getReturnType())); + vars.put("#method_name", method.getName()); + vars.put("#giop_method_name", getGiopMethodName()); + vars.put("#argument_list", getArgumentList()); + vars.put("#argument_names", getArgumentNames()); + + vars.put("#argument_write", getStubParaWriteStatement()); + + if (method.getReturnType().equals(void.class)) + vars.put("#read_return", "return;"); + else + vars.put("#read_return", + "return " + + GiopIo.getReadStatement(method.getReturnType(), rmic)); + String thr = getThrows(); + if (thr.length() > 0) + vars.put("#throws", "\n throws " + thr); + else + vars.put("#throws", ""); + + if (method.getReturnType().equals(void.class)) + templateName = "StubMethodVoid.jav"; + else + { + vars.put("#write_result", + GiopIo.getWriteStatement(method.getReturnType(), "result", + rmic)); + templateName = "StubMethod.jav"; + } + + String template = rmic.getResource(templateName); + String generated = rmic.replaceAll(template, vars); + return generated; + } + + /** + * Generate this method handling fragment for the Tie class. + * + * @return the fragment to handle this method for the Tie class. + */ + public String generateTieMethod() + { + String templateName; + + Properties vars = new Properties(rmic.vars); + vars.put("#return_type", rmic.name(method.getReturnType())); + vars.put("#method_name", method.getName()); + vars.put("#giop_method_name", getGiopMethodName()); + vars.put("#argument_list", getArgumentList()); + vars.put("#argument_names", getArgumentNames()); + + vars.put("#argument_write", getStubParaWriteStatement()); + + if (previous == null || previous.getHashChar()!=getHashChar()) + vars.put("#hashCodeLabel"," case '"+getHashChar()+"':"); + else + vars.put("#hashCodeLabel"," // also '"+getHashChar()+"':"); + + if (method.getReturnType().equals(void.class)) + templateName = "TieMethodVoid.jav"; + else + { + vars.put("#write_result", + GiopIo.getWriteStatement(method.getReturnType(), "result", + rmic)); + templateName = "TieMethod.jav"; + } + vars.put("#read_and_define_args", getRda()); + + String template = rmic.getResource(templateName); + String generated = rmic.replaceAll(template, vars); + return generated; + } + + /** + * Generate sentences for Reading and Defining Arguments. + * + * @return the sequence of sentences for reading and defining arguments. + */ + public String getRda() + { + StringBuilder b = new StringBuilder(); + Class[] args = method.getParameterTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(" "); + b.append(rmic.name(args[i])); + b.append(" "); + b.append("p"+i); + b.append(" = "); + b.append(GiopIo.getReadStatement(args[i], rmic)); + if (i<args.length-1) + b.append("\n"); + } + return b.toString(); + } + + /** + * Get the write statement for writing parameters inside the stub. + * + * @return the write statement. + */ + public String getStubParaWriteStatement() + { + StringBuilder b = new StringBuilder(); + Class[] args = method.getParameterTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(" "); + b.append(GiopIo.getWriteStatement(args[i], "p" + i, rmic)); + b.append("\n"); + } + return b.toString(); + } + + /** + * Get the hash char. + */ + public char getHashChar() + { + return getGiopMethodName().charAt(hashCharPosition); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/RMICException.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/RMICException.java new file mode 100644 index 000000000..ca9983d0a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/RMICException.java @@ -0,0 +1,70 @@ +/* RMICException.java -- + Copyright (c) 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmic; + +/** + * Thrown by the underlying compiler used by RMIC when it fails to compile a + * file. + * + * @author Dalibor Topic (robilad@kaffe.org) + */ +public class RMICException + extends Exception +{ + /** + * Create an exception with a message. The cause remains uninitialized. + * + * @param message the message string + * @see #initCause(Throwable) + */ + public RMICException(String message) + { + super(message); + } + + /** + * Create an exception with a message and a cause. + * + * @param message the message string + * @param cause the cause of this exception + */ + public RMICException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/RmiMethodGenerator.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/RmiMethodGenerator.java new file mode 100644 index 000000000..e02f086ef --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/RmiMethodGenerator.java @@ -0,0 +1,297 @@ +/* MethodGenerator.java -- Generates methods for rmi compiler. + Copyright (C) 2006 Free Software Foundation + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package gnu.classpath.tools.rmic; + +import gnu.classpath.tools.rmic.AbstractMethodGenerator; +import gnu.java.rmi.server.RMIHashes; + +import java.lang.reflect.Method; +import java.util.Properties; + +/** + * Keeps information about the single method and generates the code fragments, + * related to that method. + * + * @author Audrius Meskauskas, Lithuania (audriusa@Bioinformatics.org) + */ +public class RmiMethodGenerator + implements AbstractMethodGenerator +{ + /** + * The method being defined. + */ + Method method; + + /** + * The parent code generator. + */ + SourceRmicCompiler rmic; + + /** + * Create the new method generator for the given method. + * + * @param aMethod the related method. + * @param aRmic the Rmic generator instance, where more class - related + * information is defined. + */ + public RmiMethodGenerator(Method aMethod, SourceRmicCompiler aRmic) + { + method = aMethod; + rmic = aRmic; + if (method.getParameterTypes().length == 0) + rmic.addZeroSizeObjecArray = true; + } + + /** + * Get the method parameter declaration. + * + * @return the string - method parameter declaration. + */ + public String getArgumentList() + { + StringBuilder b = new StringBuilder(); + + Class[] args = method.getParameterTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(rmic.name(args[i])); + b.append(" p" + i); + if (i < args.length - 1) + b.append(", "); + } + return b.toString(); + } + + /** + * Get the method parameter list only (no type declarations). This is used to + * generate the method invocations statement. + * + * @return the string - method parameter list. + */ + public String getArgumentNames() + { + StringBuilder b = new StringBuilder(); + + Class[] args = method.getParameterTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(" p" + i); + if (i < args.length - 1) + b.append(", "); + } + return b.toString(); + } + + /** + * Get the list of exceptions, thrown by this method. + * + * @return the list of exceptions. + */ + public String getThrows() + { + StringBuilder b = new StringBuilder(); + + Class[] args = method.getExceptionTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(rmic.name(args[i])); + if (i < args.length - 1) + b.append(", "); + } + return b.toString(); + } + + /** + * Generate this method for the Stub class. + * + * @return the method body for the stub class. + */ + public String generateStubMethod() + { + String templateName; + + Properties vars = new Properties(rmic.vars); + vars.put("#return_type", rmic.name(method.getReturnType())); + vars.put("#method_name", method.getName()); + vars.put("#method_hash", getMethodHashCode()); + vars.put("#argument_list", getArgumentList()); + vars.put("#object_arg_list", getArgListAsObjectArray()); + vars.put("#declaring_class", rmic.name(method.getDeclaringClass())); + vars.put("#class_arg_list", getArgListAsClassArray()); + + String thr = getThrows(); + if (thr.length() > 0) + vars.put("#throws", "\n throws " + thr); + else + vars.put("#throws", ""); + + if (method.getReturnType().equals(void.class)) + templateName = "Stub_12MethodVoid.jav"; + else + { + templateName = "Stub_12Method.jav"; + vars.put("#return_statement", getReturnStatement()); + } + + String template = rmic.getResource(templateName); + String generated = rmic.replaceAll(template, vars); + return generated; + } + + /** + * Generate sentences for Reading and Defining Arguments. + * + * @return the sequence of sentences for reading and defining arguments. + */ + public String getStaticMethodDeclarations() + { + StringBuilder b = new StringBuilder(); + Class[] args = method.getParameterTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(" "); + b.append(rmic.name(args[i])); + b.append(" "); + b.append("p" + i); + b.append(" = "); + if (i < args.length - 1) + b.append("\n"); + } + return b.toString(); + } + + /** + * Get the write statement for writing parameters inside the stub. + * + * @return the write statement. + */ + public String getArgListAsObjectArray() + { + Class[] args = method.getParameterTypes(); + + if (args.length==0) + return "NO_ARGS"; + + StringBuilder b = new StringBuilder("new Object[] {"); + + for (int i = 0; i < args.length; i++) + { + if (!args[i].isPrimitive()) + b.append("p"+i); + else + { + b.append("new "+rmic.name(WrapUnWrapper.getWrappingClass(args[i]))); + b.append("(p"+i+")"); + } + if (i<args.length-1) + b.append(", "); + } + b.append("}"); + return b.toString(); + } + + /** + * Get the return statement, assuming that the returned object is placed into + * the variable "result". + */ + public String getReturnStatement() + { + Class r = method.getReturnType(); + if (r.equals(void.class)) + return ""; + else + { + if (r.isPrimitive()) + { + String wcd = rmic.name(WrapUnWrapper.getWrappingClass(r)); + return "return ((" + wcd + ") result)." + + WrapUnWrapper.getUnwrappingMethod(r) + ";"; + } + else + return "return (" + rmic.name(r) + ") result;"; + } + } + + /** + * Get argument list as class array. + */ + public String getArgListAsClassArray() + { + StringBuilder b = new StringBuilder(); + Class[] args = method.getParameterTypes(); + + for (int i = 0; i < args.length; i++) + { + b.append(rmic.name(args[i])); + b.append(".class"); + if (i < args.length - 1) + b.append(", "); + } + return b.toString(); + } + + /** + * RMI ties (previously named Skeletons) are no longer used since v 1.2. This + * method should never be called. + */ + public String generateTieMethod() + { + throw new InternalError(); + } + + /** + * Get the method hash code. + */ + public String getMethodHashCode() + { + return RMIHashes.getMethodHash(method)+"L"; + } + + /** + * Additional processing of the stub name (nothing to do for JRMP stubs). + */ + public String convertStubName(String name) + { + return name; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/RmicBackend.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/RmicBackend.java new file mode 100644 index 000000000..92f982d53 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/RmicBackend.java @@ -0,0 +1,48 @@ +/* RmicBackend.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmic; +public interface RmicBackend +{ + void setup(boolean keep, boolean need11Stubs, boolean need12Stubs, + boolean iiop, boolean poa, boolean debug, boolean warnings, + boolean noWrite, boolean verbose, boolean force, String classpath, + String bootclasspath, String extdirs, String outputDirectory); + + boolean run(String[] inputFiles); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/SourceGiopRmicCompiler.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/SourceGiopRmicCompiler.java new file mode 100644 index 000000000..dd35c2bd5 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/SourceGiopRmicCompiler.java @@ -0,0 +1,694 @@ +/* SourceGiopRmicCompiler -- Central GIOP-based RMI stub and tie compiler class. + Copyright (C) 2006, 2008 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. +*/ + +package gnu.classpath.tools.rmic; + +import gnu.classpath.tools.rmic.AbstractMethodGenerator; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.TreeSet; + +/** + * Provides the extended rmic functionality to generate the POA - based classes + * for GIOP (javax.rmi.CORBA package). + * + * @author Audrius Meskauskas, Lithuania (audriusa@Bioinformatics.org) + */ +public class SourceGiopRmicCompiler + extends Generator implements Comparator, RmicBackend +{ + /** The package name. */ + protected String packag; + + /** + * The "basic" name (normally, the interface name, unless several Remote - + * derived interfaces are implemented. + */ + protected String name; + + /** + * The name (without package) of the class, passed as the parameter. + */ + protected String implName; + + /** + * The proposed name for the stub. + */ + protected String stubName; + + /** + * The Remote's, implemented by this class. + */ + protected Collection implementedRemotes = new HashSet(); + + /** + * The extra classes that must be imported. + */ + protected Collection extraImports = new HashSet(); + + /** + * The methods we must implement. + */ + protected Collection methods = new HashSet(); + + /** + * The map of all code generator variables. + */ + public Properties vars = new Properties(); + + /** + * If this flag is set (true by default), the compiler generates the Servant + * based classes. If set to false, the compiler generates the old style + * ObjectImpl based classes. + */ + protected boolean poaMode = true; + + /** + * If this flag is set (true by default), the compiler emits warnings. + */ + protected boolean warnings = true; + + /** + * If this flag is set (false by default), the compiler does not + * write output files. + */ + protected boolean noWrite = false; + + /** + * If this flag is set (false by default), the compiler keeps source + * output files. For SourceGiopRmicCompiler this overrides + * -nowrite, since -nowrite doesn't apply to sources kept with + * -keep. + */ + protected boolean keep = false; + + /** + * Verbose output + */ + protected boolean verbose = false; + + /** + * Force mode - do not check the exceptions + */ + protected boolean force = false; + + /** + * The output directory for generated files. + */ + protected String outputDirectory; + + /** + * The class loader to load the class being compiled. + */ + ClassLoader classLoader; + + /** + * Clear data, preparing for the next compilation. + */ + public synchronized void reset() + { + packag = name = implName = stubName = null; + implementedRemotes.clear(); + extraImports.clear(); + methods.clear(); + vars.clear(); + } + + /** + * Set the class path (handle the -classpath key) + * + * @param classPath the class path to set. + */ + public void setClassPath(String classPath) + { + classLoader = Thread.currentThread().getContextClassLoader(); + StringTokenizer tok = new StringTokenizer(classPath, File.pathSeparator, + true); + ArrayList urls = new ArrayList(tok.countTokens()); + String s = null; + try + { + while (tok.hasMoreTokens()) + { + s = tok.nextToken(); + if (s.equals(File.pathSeparator)) + urls.add(new File(".").toURL()); + else + { + urls.add(new File(s).toURL()); + if (tok.hasMoreTokens()) + { + // Skip the separator. + tok.nextToken(); + // If the classpath ended with a separator, + // append the current directory. + if (! tok.hasMoreTokens()) + urls.add(new File(".").toURL()); + } + } + } + } + catch (MalformedURLException ex) + { + System.err.println("Malformed path '" + s + "' in classpath '" + + classPath + "'"); + System.exit(1); + } + URL[] u = new URL[urls.size()]; + for (int i = 0; i < u.length; i++) + { + u[i] = (URL) urls.get(i); + } + + classLoader = new URLClassLoader(u, classLoader); + } + + /** + * Loads the class with the given name (uses class path, if applicable) + * + * @param name the name of the class. + */ + public Class loadClass(String name) + { + ClassLoader loader = classLoader; + if (loader == null) + loader = Thread.currentThread().getContextClassLoader(); + try + { + return loader.loadClass(name); + } + catch (ClassNotFoundException e) + { + System.err.println(name+" not found on "+loader); + System.exit(1); + // Unreacheable code. + return null; + } + } + + /** + * Compile the given class (the instance of Remote), generating the stub and + * tie for it. + * + * @param remote + * the class to compile. + */ + public synchronized void compile(Class remote) + { + reset(); + String s; + + // Get the package. + s = remote.getName(); + int p = s.lastIndexOf('.'); + if (p < 0) + { + // Root package. + packag = ""; + implName = name = s; + } + else + { + packag = s.substring(0, p); + implName = name = s.substring(p + 1); + } + + name = convertStubName(name); + + stubName = name; + + vars.put("#name", name); + vars.put("#package", packag); + vars.put("#implName", implName); + + if (verbose) + System.out.println("Package " + packag + ", name " + name + " impl " + + implName); + + // Get the implemented remotes. + Class[] interfaces = remote.getInterfaces(); + + for (int i = 0; i < interfaces.length; i++) + { + if (Remote.class.isAssignableFrom(interfaces[i])) + { + if (! interfaces[i].equals(Remote.class)) + { + implementedRemotes.add(interfaces[i]); + } + } + } + + vars.put("#idList", getIdList(implementedRemotes)); + + // Collect and process methods. + Iterator iter = implementedRemotes.iterator(); + + while (iter.hasNext()) + { + Class c = (Class) iter.next(); + Method[] m = c.getMethods(); + + // Check if throws RemoteException. + for (int i = 0; i < m.length; i++) + { + Class[] exc = m[i].getExceptionTypes(); + boolean remEx = false; + + for (int j = 0; j < exc.length; j++) + { + if (exc[j].isAssignableFrom(RemoteException.class)) + { + remEx = true; + break; + } + } + if (! remEx && !force) + throw new CompilationError(m[i].getName() + ", defined in " + + c.getName() + + ", does not throw " + + RemoteException.class.getName()); + AbstractMethodGenerator mm = createMethodGenerator(m[i]); + methods.add(mm); + } + } + } + + /** + * Create the method generator for the given method. + * + * @param m the method + * + * @return the created method generator + */ + protected AbstractMethodGenerator createMethodGenerator(Method m) + { + return new MethodGenerator(m, this); + } + + /** + * Get the name of the given class. The class is added to imports, if not + * already present and not from java.lang and not from the current package. + * + * @param nameIt + * the class to name + * @return the name of class as it should appear in java language + */ + public synchronized String name(Class nameIt) + { + if (nameIt.isArray()) + { + // Mesure dimensions: + int dimension = 0; + Class finalComponent = nameIt; + while (finalComponent.isArray()) + { + finalComponent = finalComponent.getComponentType(); + dimension++; + } + + StringBuilder brackets = new StringBuilder(); + + for (int i = 0; i < dimension; i++) + { + brackets.append("[]"); + } + + return name(finalComponent) + " " + brackets; + } + else + { + String n = nameIt.getName(); + if (! nameIt.isArray() && ! nameIt.isPrimitive()) + if (! n.startsWith("java.lang") + && ! (packag != null && n.startsWith(packag))) + extraImports.add(n); + + int p = n.lastIndexOf('.'); + if (p < 0) + return n; + else + return n.substring(p + 1); + } + } + + /** + * Get the RMI-style repository Id for the given class. + * + * @param c + * the interface, for that the repository Id must be created. + * @return the repository id + */ + public String getId(Class c) + { + return "RMI:" + c.getName() + ":0000000000000000"; + } + + /** + * Get repository Id string array declaration. + * + * @param remotes + * the collection of interfaces + * @return the fully formatted string array. + */ + public String getIdList(Collection remotes) + { + StringBuilder b = new StringBuilder(); + + // Keep the Ids sorted, ensuring, that the same order will be preserved + // between compilations. + TreeSet sortedIds = new TreeSet(); + + Iterator iter = remotes.iterator(); + while (iter.hasNext()) + { + sortedIds.add(getId((Class) iter.next())); + } + + iter = sortedIds.iterator(); + while (iter.hasNext()) + { + b.append(" \"" + iter.next() + "\""); + if (iter.hasNext()) + b.append(", \n"); + } + return b.toString(); + } + + /** + * Generate stub. Can only be called from {@link #compile}. + * + * @return the string, containing the text of the generated stub. + */ + public String generateStub() + { + String template = getResource("Stub.jav"); + + // Generate methods. + StringBuilder b = new StringBuilder(); + Iterator iter = methods.iterator(); + while (iter.hasNext()) + { + AbstractMethodGenerator m = (AbstractMethodGenerator) iter.next(); + b.append(m.generateStubMethod()); + } + + vars.put("#stub_methods", b.toString()); + vars.put("#imports", getImportStatements()); + vars.put("#interfaces", getAllInterfaces()); + + String output = replaceAll(template, vars); + return output; + } + + /** + * Get the list of all interfaces, implemented by the class, that are + * derived from Remote. + * + * @return the string - all interfaces. + */ + public String getAllInterfaces() + { + StringBuilder b = new StringBuilder(); + Iterator iter = implementedRemotes.iterator(); + + while (iter.hasNext()) + { + b.append(name((Class) iter.next())); + if (iter.hasNext()) + b.append(", "); + } + + return b.toString(); + } + + /** + * Generate Tie. Can only be called from {@link #compile}. + * + * @return the string, containing the text of the generated Tie. + */ + public String generateTie() + { + String template; + if (poaMode) + template = getResource("Tie.jav"); + else + template = getResource("ImplTie.jav"); + + // Generate methods. + HashFinder hashFinder = new HashFinder(); + + // Find the hash character position: + Iterator iter = methods.iterator(); + String[] names = new String[methods.size()]; + int p = 0; + + for (int i = 0; i < names.length; i++) + names[i] = ((MethodGenerator) iter.next()).getGiopMethodName(); + + int hashCharPosition = hashFinder.findHashCharPosition(names); + + iter = methods.iterator(); + while (iter.hasNext()) + ((MethodGenerator) iter.next()).hashCharPosition = hashCharPosition; + + vars.put("#hashCharPos", Integer.toString(hashCharPosition)); + + ArrayList sortedMethods = new ArrayList(methods); + Collections.sort(sortedMethods, this); + + iter = sortedMethods.iterator(); + + StringBuilder b = new StringBuilder(); + + MethodGenerator prev = null; + + while (iter.hasNext()) + { + MethodGenerator m = (MethodGenerator) iter.next(); + m.previous = prev; + m.hashCharPosition = hashCharPosition; + prev = m; + b.append(m.generateTieMethod()); + } + + vars.put("#tie_methods", b.toString()); + + vars.put("#imports", getImportStatements()); + + String output = replaceAll(template, vars); + return output; + } + + public int compare(Object a, Object b) + { + MethodGenerator g1 = (MethodGenerator) a; + MethodGenerator g2 = (MethodGenerator) b; + + return g1.getHashChar() - g2.getHashChar(); + } + + /** + * Import the extra classes, used as the method parameters and return values. + * + * @return the additional import block. + */ + protected String getImportStatements() + { + TreeSet imp = new TreeSet(); + + Iterator it = extraImports.iterator(); + while (it.hasNext()) + { + String ic = it.next().toString(); + imp.add("import " + ic + ";\n"); + } + + StringBuilder b = new StringBuilder(); + it = imp.iterator(); + + while (it.hasNext()) + { + b.append(it.next()); + } + return b.toString(); + } + + /** + * If this flag is set (true by default), the compiler generates the Servant + * based classes. If set to false, the compiler generates the old style + * ObjectImpl based classes. + */ + public void setPoaMode(boolean mode) + { + poaMode = mode; + } + + /** + * Set the verbose output mode (false by default) + * + * @param isVerbose the verbose output mode + */ + public void setVerbose(boolean isVerbose) + { + verbose = isVerbose; + } + + /** + * If this flag is set (true by default), the compiler emits warnings. + */ + public void setWarnings(boolean warn) + { + warnings = warn; + } + + /** + * Set the error ignore mode. + */ + public void setForce(boolean isforce) + { + force = isforce; + } + + /** + * Get the package name. + */ + public String getPackageName() + { + return packag; + } + + /** + * Get the proposed stub name + */ + public String getStubName() + { + return stubName; + } + + /** + * Additional processing of the stub name. + */ + public String convertStubName(String name) + { + // Drop the Impl suffix, if one exists. + if (name.endsWith("Impl")) + return name.substring(0, name.length() - "Impl".length()); + else + return name; + } + + /** + * Assumes that output directory is already created. + */ + protected boolean outputTie(File fw, Class c) + { + try + { + String tie = generateTie(); + String tieName = "_" + name(c) + "_Tie.java"; + + OutputStream out = new FileOutputStream(new File(fw, tieName)); + out.write(tie.getBytes()); + out.close(); + } + catch (IOException ioex) + { + System.err.println("Output path not accessible"); + ioex.printStackTrace(); + return false; + } + return true; + } + + public void setup(boolean keep, boolean need11Stubs, boolean need12Stubs, + boolean iiop, boolean poa, boolean debug, boolean warnings, + boolean noWrite, boolean verbose, boolean force, String classpath, + String bootclasspath, String extdirs, String outputDirectory) + { + setWarnings(warnings); + setVerbose(verbose); + setForce(force); + setClassPath(classpath); + setPoaMode(poa); + this.outputDirectory = outputDirectory; + this.noWrite = noWrite; + this.keep = keep; + } + + public boolean run(String[] inputFiles) + { + for (int i = 0; i < inputFiles.length; i++) + { + reset(); + Class c = loadClass(inputFiles[i]); + + compile(c); + String packag = getPackageName().replace('.', '/'); + File fw = new File(outputDirectory, packag); + + // Generate stub. + String stub = generateStub(); + String subName = getStubName() + "_Stub.java"; + + // -keep overrides -nowrite for sources. + if (!noWrite || keep) + { + try + { + fw.mkdirs(); + OutputStream out = new FileOutputStream(new File(fw, + subName)); + out.write(stub.getBytes()); + out.close(); + + // Generate tie + if (!outputTie(fw, c)) + return false; + } + catch (IOException ioex) + { + System.err.println("Output path not accessible"); + ioex.printStackTrace(); + return false; + } + } + } + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/SourceRmicCompiler.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/SourceRmicCompiler.java new file mode 100644 index 000000000..413d91ad2 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/SourceRmicCompiler.java @@ -0,0 +1,189 @@ +/* SourceRmicCompiler.java -- RMI stub generator for java.rmi.* + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmic; + +import java.lang.reflect.Method; +import java.io.File; +import java.util.Iterator; + +import gnu.classpath.tools.rmic.AbstractMethodGenerator; + +/** + * RMI stub source code generator, required to support java.rmi.* + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public class SourceRmicCompiler extends SourceGiopRmicCompiler +{ + /** + * If true, the zero size object array is declared in the stub to reduce + * garbage generation. + */ + public boolean addZeroSizeObjecArray; + + /** + * Generate a RMI stub. + * + * @return the string, containing the text of the generated stub. + */ + public String generateStub() + { + String template = getResource("Stub_12.jav"); + + // Generate methods. + StringBuilder b = new StringBuilder(); + Iterator iter = methods.iterator(); + while (iter.hasNext()) + { + RmiMethodGenerator m = (RmiMethodGenerator) iter.next(); + b.append(m.generateStubMethod()); + } + + vars.put("#stub_methods", b.toString()); + vars.put("#imports", getImportStatements()); + vars.put("#interfaces", getAllInterfaces()); + vars.put("#stub_method_declarations", getStubMethodDeclarations()); + vars.put("#stub_method_initializations", getStubMethodInitializations()); + if (addZeroSizeObjecArray) + { + vars.put("#zeroSizeObjecArray", + "private static final Object[] NO_ARGS = new Object[0];"); + vars.put("#zeroSizeClassArray", + "final Class[] NO_ARGSc = new Class[0];"); + } + else + { + vars.put("#zeroSizeObjecArray",""); + vars.put("#zeroSizeClassArray",""); + } + + String output = replaceAll(template, vars); + return output; + } + + /** + * Create a method generator, applicable for RMI stub methods. + */ + protected AbstractMethodGenerator createMethodGenerator(Method m) + { + return new RmiMethodGenerator(m, this); + } + + /** + * Get the stub method declarations. + */ + public String getStubMethodDeclarations() + { + StringBuilder b = new StringBuilder(); + + Iterator iter = methods.iterator(); + + while (iter.hasNext()) + { + RmiMethodGenerator method = (RmiMethodGenerator) iter.next(); + b.append(" "); + b.append("private static final Method met_"); + b.append(method.method.getName()); + b.append(';'); + if (iter.hasNext()) + b.append('\n'); + } + return b.toString(); + } + + /** + * Get stub method initializations. These must be done in a try-catch + * statement to catch {@link NoSuchMethodException}. + */ + public String getStubMethodInitializations() + { + StringBuilder b = new StringBuilder(); + + Iterator iter = methods.iterator(); + + while (iter.hasNext()) + { + RmiMethodGenerator method = (RmiMethodGenerator) iter.next(); + b.append(" "); + b.append("met_"); + b.append(method.method.getName()); + b.append(" =\n "); + b.append(name(method.method.getDeclaringClass())); + b.append(".class.getMethod("); + b.append('"'); + b.append(method.method.getName()); + b.append("\", "); + if (method.method.getParameterTypes().length == 0) + b.append("NO_ARGSc);"); + else + { + b.append("new Class[]\n {\n "); + b.append(method.getArgListAsClassArray()); + b.append("\n }"); + b.append(");"); + } + b.append('\n'); + } + return b.toString(); + } + + /** + * Prepare for the compilation of the next class. + */ + public void reset() + { + addZeroSizeObjecArray = false; + super.reset(); + } + + /** + * Additional processing of the stub name (nothing to do for JRMP stubs). + */ + public String convertStubName(String name) + { + return name; + } + + /** + * Override to do nothing. + */ + protected boolean outputTie(File fw, Class c) + { + return true; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/Variables.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/Variables.java new file mode 100644 index 000000000..1fc6a8095 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/Variables.java @@ -0,0 +1,154 @@ +/* Variables.java -- + Copyright (c) 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmic; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +class Variables +{ + private final HashSet free = new HashSet(); + private final HashMap names = new HashMap(); + private final HashSet wides = new HashSet(); + private final HashSet declared = new HashSet(); + private boolean allocated = false; + + public void declare(Object name) + { + declare(name, 1); + } + + public void declareWide(Object name) + { + declare(name, 2); + } + + public void declare(Object name, int size) + { + if (allocated) + throw new IllegalStateException("cannot declare after allocating"); + if (size != 1 && size != 2) + throw new IllegalArgumentException("size must be 1 or 2"); + if (names.containsKey(name)) + throw new IllegalStateException("already allocated " + name); + + allocateNew(name, size); + declared.add(name); + } + + private int allocateNew(Object name, int size) + { + // total allocation size is first unallocated slot + int i = free.size() + names.size() + wides.size(); + names.put(name, new Integer(i)); + if (size == 2) wides.add(name); + return i; + } + + public int allocate(Object name) + { + return allocate(name, 1); + } + + public int allocateWide(Object name) + { + return allocate(name, 2); + } + + public int allocate(Object name, int size) + { + allocated = true; + if (size != 1 && size != 2) + throw new IllegalArgumentException("size must be 1 or 2"); + if (names.containsKey(name)) + throw new IllegalStateException("already allocated " + name); + + if (size == 2) + { + // look for consecutive free slots + for (Iterator it = free.iterator(); it.hasNext(); ) + { + Integer i = (Integer) it.next(); + Integer next = new Integer(i.intValue() + 1); + if (free.contains(next)) + { + free.remove(i); + free.remove(next); + wides.add(name); + names.put(name, i); + return i.intValue(); + } + } + } + else if (free.size() > 0) + { + Integer i = (Integer) free.iterator().next(); + free.remove(i); + names.put(name, i); + return i.intValue(); + } + + return allocateNew(name, size); + } + + public int deallocate(Object name) + { + if (! names.containsKey(name)) + throw new IllegalArgumentException("no variable " + name); + + if (declared.contains(name)) + throw new IllegalStateException(name + " can't be deallocated"); + + Integer i = (Integer) names.get(name); + names.remove(name); + free.add(i); + if (wides.remove(name)) + free.add(new Integer(i.intValue() + 1)); + return i.intValue(); + } + + public int get(Object name) + { + if (! names.containsKey(name)) + throw new IllegalArgumentException("no variable " + name); + + return ((Integer) names.get(name)).intValue(); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmic/WrapUnWrapper.java b/libjava/classpath/tools/gnu/classpath/tools/rmic/WrapUnWrapper.java new file mode 100644 index 000000000..cb412851b --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmic/WrapUnWrapper.java @@ -0,0 +1,99 @@ +/* WrapUnWrapper.java -- Wrapper and unwrapper for primitive types. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmic; + + +public class WrapUnWrapper +{ + /** + * Get the wrapper class for the primitive type + * + * @param primitive the class of the primitive type + * + * @return the wrapper class + */ + public static Class getWrappingClass(Class primitive) + { + if (primitive.equals(byte.class)) + return Byte.class; + if (primitive.equals(int.class)) + return Integer.class; + if (primitive.equals(long.class)) + return Long.class; + if (primitive.equals(boolean.class)) + return Boolean.class; + if (primitive.equals(double.class)) + return Double.class; + if (primitive.equals(float.class)) + return Float.class; + if (primitive.equals(char.class)) + return Character.class; + else + return null; + } + + /** + * Get the method, invocation of that would return the wrapped value. + * + * @param primitive the class of the primitive type. + * + * @return the wrapper method that unwraps the value to the primitive type. + */ + public static String getUnwrappingMethod(Class primitive) + { + if (primitive.equals(byte.class)) + return "byteValue()"; + if (primitive.equals(int.class)) + return "intValue()"; + if (primitive.equals(long.class)) + return "longValue()"; + if (primitive.equals(boolean.class)) + return "booleanValue()"; + if (primitive.equals(double.class)) + return "doubleValue()"; + if (primitive.equals(float.class)) + return "floatValue()"; + if (primitive.equals(char.class)) + return "charValue()"; + else + return null; + } + + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmid/ActivationSystemImpl.java b/libjava/classpath/tools/gnu/classpath/tools/rmid/ActivationSystemImpl.java new file mode 100644 index 000000000..fb2764873 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmid/ActivationSystemImpl.java @@ -0,0 +1,243 @@ +/* ActivationSystemImpl.java -- implementation of the activation system. + Copyright (c) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmid; + +import gnu.classpath.tools.common.Persistent; +import gnu.java.rmi.activation.ActivationSystemTransient; + +import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.rmi.MarshalledObject; +import java.rmi.RemoteException; +import java.rmi.activation.ActivationDesc; +import java.rmi.activation.ActivationException; +import java.rmi.activation.ActivationGroupDesc; +import java.rmi.activation.ActivationGroupID; +import java.rmi.activation.ActivationID; +import java.rmi.activation.ActivationInstantiator; +import java.rmi.activation.ActivationMonitor; +import java.rmi.activation.ActivationSystem; +import java.rmi.activation.Activator; +import java.rmi.activation.UnknownGroupException; +import java.rmi.activation.UnknownObjectException; + +/** + * Implements the rmid activation system. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public class ActivationSystemImpl extends ActivationSystemTransient implements + ActivationSystem, Activator, ActivationMonitor, Serializable +{ + /** + * Use for interoperability. + */ + private static final long serialVersionUID = 1; + + /** + * The singleton instance of this class. + */ + public static ActivationSystemImpl singleton2; + + /** + * Obtain the singleton instance of this class. + * + * @param folder the folder, where the activation system will keep its files. + * @param cold do the cold start if true, hot (usual) if false. + */ + public static ActivationSystem getInstance(File folder, boolean cold) + { + if (singleton2 == null) + singleton2 = new ActivationSystemImpl(folder, cold); + return singleton2; + } + + /** + * Creates the group with transient maps. + * + * @param folder + * the folder, where the activation system will keep its files. + * @param cold + * do the cold start if true, hot (usual) if false. + */ + protected ActivationSystemImpl(File folder, boolean cold) + { + super(new PersistentBidiHashTable(), new PersistentBidiHashTable()); + singleton2 = this; + ((PersistentBidiHashTable) groupDescs).init( + new File(folder, "asi_objects.data"), cold); + ((PersistentBidiHashTable) descriptions).init( + new File(folder, "asi_groups.data"), cold); + } + + /** @inheritDoc */ + public MarshalledObject activate(ActivationID id, boolean force) + throws ActivationException, UnknownObjectException, RemoteException + { + return super.activate(id, force); + } + + /** @inheritDoc */ + public ActivationMonitor activeGroup(ActivationGroupID id, + ActivationInstantiator group, + long incarnation) + throws UnknownGroupException, ActivationException, RemoteException + { + return super.activeGroup(id, group, incarnation); + } + + /** @inheritDoc */ + public void activeObject(ActivationID id, MarshalledObject obj) + throws UnknownObjectException, RemoteException + { + super.activeObject(id, obj); + } + + /** @inheritDoc */ + public ActivationDesc getActivationDesc(ActivationID id) + throws ActivationException, UnknownObjectException, RemoteException + { + return super.getActivationDesc(id); + } + + public ActivationGroupDesc getActivationGroupDesc(ActivationGroupID groupId) + throws ActivationException, UnknownGroupException, RemoteException + { + return super.getActivationGroupDesc(groupId); + } + + /** @inheritDoc */ + public void inactiveGroup(ActivationGroupID groupId, long incarnation) + throws UnknownGroupException, RemoteException + { + super.inactiveGroup(groupId, incarnation); + } + + /** @inheritDoc */ + public void inactiveObject(ActivationID id) throws UnknownObjectException, + RemoteException + { + super.inactiveObject(id); + } + + /** @inheritDoc */ + public ActivationGroupID registerGroup(ActivationGroupDesc groupDesc) + throws ActivationException, RemoteException + { + return super.registerGroup(groupDesc); + } + + /** @inheritDoc */ + public ActivationID registerObject(ActivationDesc desc) + throws ActivationException, UnknownGroupException, RemoteException + { + return super.registerObject(desc); + } + + /** @inheritDoc */ + public ActivationDesc setActivationDesc(ActivationID id, ActivationDesc desc) + throws ActivationException, UnknownObjectException, + UnknownGroupException, RemoteException + { + return super.setActivationDesc(id, desc); + } + + /** @inheritDoc */ + public ActivationGroupDesc setActivationGroupDesc( + ActivationGroupID groupId, ActivationGroupDesc groupDesc) + throws ActivationException, UnknownGroupException, RemoteException + { + return super.setActivationGroupDesc(groupId, groupDesc); + } + + /** + * This method saves the state of the activation system and then + * terminates in 10 seconds. + */ + public void shutdown() throws RemoteException + { + super.shutdown(); + System.out.println("Shutdown command received. Will terminate in 10 s"); + Persistent.timer.schedule(new Persistent.ExitTask(), 10000); + } + + /** @inheritDoc */ + public void unregisterGroup(ActivationGroupID groupId) + throws ActivationException, UnknownGroupException, RemoteException + { + super.unregisterGroup(groupId); + } + + /** @inheritDoc */ + public void unregisterObject(ActivationID id) throws ActivationException, + UnknownObjectException, RemoteException + { + super.unregisterObject(id); + } + + /** + * Read the object from the input stream. + * + * @param in the stream to read from + * + * @throws IOException if thrown by the stream + * @throws ClassNotFoundException + */ + private void readObject(ObjectInputStream in) throws IOException, + ClassNotFoundException + { + // Read no fields. + } + + /** + * Write the object to the output stream. + * + * @param out the stream to write int + * @throws IOException if thrown by the stream + * @throws ClassNotFoundException + */ + private void writeObject(ObjectOutputStream out) throws IOException, + ClassNotFoundException + { + // Write no fields. + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmid/ActivationSystemImpl_Stub.java b/libjava/classpath/tools/gnu/classpath/tools/rmid/ActivationSystemImpl_Stub.java new file mode 100644 index 000000000..446a87fb2 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmid/ActivationSystemImpl_Stub.java @@ -0,0 +1,556 @@ +/* ActivationSystemImpl.java -- implementation of the activation system. + Copyright (c) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmid; + +import java.rmi.MarshalledObject; +import java.rmi.RemoteException; +import java.rmi.activation.ActivationDesc; +import java.rmi.activation.ActivationException; +import java.rmi.activation.ActivationGroupDesc; +import java.rmi.activation.ActivationGroupID; +import java.rmi.activation.ActivationID; +import java.rmi.activation.ActivationInstantiator; +import java.rmi.activation.ActivationMonitor; +import java.rmi.activation.ActivationSystem; +import java.rmi.activation.Activator; +import java.rmi.activation.UnknownGroupException; +import java.rmi.activation.UnknownObjectException; + +import java.lang.reflect.Method; +import java.rmi.server.RemoteRef; +import java.rmi.server.RemoteStub; +import java.rmi.UnexpectedException; + +/** + * This class delegates its method calls to the remote RMI object, referenced + * by {@link RemoteRef}. + * + * It is normally generated with rmic. + */ +public final class ActivationSystemImpl_Stub + extends RemoteStub + implements ActivationMonitor, Activator, ActivationSystem +{ + /** + * Use serialVersionUID for interoperability + */ + private static final long serialVersionUID = 2; + + /** + * The explaining message for {@ling UnexpectedException}. + */ + private static final String exception_message = + "undeclared checked exception"; + + /* All remote methods, invoked by this stub: */ + private static final Method met_setActivationGroupDesc; + private static final Method met_inactiveGroup; + private static final Method met_unregisterObject; + private static final Method met_getActivationDesc; + private static final Method met_setActivationDesc; + private static final Method met_shutdown; + private static final Method met_activate; + private static final Method met_activeGroup; + private static final Method met_registerGroup; + private static final Method met_getActivationGroupDesc; + private static final Method met_activeObject; + private static final Method met_registerObject; + private static final Method met_inactiveObject; + private static final Method met_unregisterGroup; + private static final Object[] NO_ARGS = new Object[0]; + static + { + final Class[] NO_ARGSc = new Class[0]; + try + { + met_setActivationGroupDesc = + ActivationSystem.class.getMethod("setActivationGroupDesc", new Class[] + { + ActivationGroupID.class, ActivationGroupDesc.class + }); + met_inactiveGroup = + ActivationMonitor.class.getMethod("inactiveGroup", new Class[] + { + ActivationGroupID.class, long.class + }); + met_unregisterObject = + ActivationSystem.class.getMethod("unregisterObject", new Class[] + { + ActivationID.class + }); + met_getActivationDesc = + ActivationSystem.class.getMethod("getActivationDesc", new Class[] + { + ActivationID.class + }); + met_setActivationDesc = + ActivationSystem.class.getMethod("setActivationDesc", new Class[] + { + ActivationID.class, ActivationDesc.class + }); + met_shutdown = + ActivationSystem.class.getMethod("shutdown", NO_ARGSc); + met_activate = + Activator.class.getMethod("activate", new Class[] + { + ActivationID.class, boolean.class + }); + met_activeGroup = + ActivationSystem.class.getMethod("activeGroup", new Class[] + { + ActivationGroupID.class, ActivationInstantiator.class, long.class + }); + met_registerGroup = + ActivationSystem.class.getMethod("registerGroup", new Class[] + { + ActivationGroupDesc.class + }); + met_getActivationGroupDesc = + ActivationSystem.class.getMethod("getActivationGroupDesc", new Class[] + { + ActivationGroupID.class + }); + met_activeObject = + ActivationMonitor.class.getMethod("activeObject", new Class[] + { + ActivationID.class, MarshalledObject.class + }); + met_registerObject = + ActivationSystem.class.getMethod("registerObject", new Class[] + { + ActivationDesc.class + }); + met_inactiveObject = + ActivationMonitor.class.getMethod("inactiveObject", new Class[] + { + ActivationID.class + }); + met_unregisterGroup = + ActivationSystem.class.getMethod("unregisterGroup", new Class[] + { + ActivationGroupID.class + }); + + } + catch (NoSuchMethodException nex) + { + NoSuchMethodError err = new NoSuchMethodError( + "ActivationSystemImpl_Stub class initialization failed"); + err.initCause(nex); + throw err; + } + } + + /** + * Create the instance for _ActivationSystemImpl_Stub that forwards method calls to the + * remote object. + * + * @para the reference to the remote object. + */ + public ActivationSystemImpl_Stub(RemoteRef reference) + { + super(reference); + } + + /* Methods */ + /** @inheritDoc */ + public ActivationGroupDesc setActivationGroupDesc(ActivationGroupID p0, + ActivationGroupDesc p1) + throws ActivationException, UnknownGroupException, RemoteException + { + try + { + Object result = ref.invoke(this, met_setActivationGroupDesc, + new Object[] { p0, p1 }, + 1213918527826541191L); + return (ActivationGroupDesc) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void inactiveGroup(ActivationGroupID p0, long p1) + throws UnknownGroupException, RemoteException + { + try + { + ref.invoke(this, met_inactiveGroup, new Object[] { p0, new Long(p1) }, + -399287892768650944L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void unregisterObject(ActivationID p0) throws ActivationException, + UnknownObjectException, RemoteException + { + try + { + ref.invoke(this, met_unregisterObject, new Object[] { p0 }, + -6843850585331411084L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public ActivationDesc getActivationDesc(ActivationID p0) + throws ActivationException, UnknownObjectException, RemoteException + { + try + { + Object result = ref.invoke(this, met_getActivationDesc, + new Object[] { p0 }, 4830055440982622087L); + return (ActivationDesc) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public ActivationDesc setActivationDesc(ActivationID p0, ActivationDesc p1) + throws ActivationException, UnknownObjectException, + UnknownGroupException, RemoteException + { + try + { + Object result = ref.invoke(this, met_setActivationDesc, + new Object[] { p0, p1 }, + 7128043237057180796L); + return (ActivationDesc) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void shutdown() throws RemoteException + { + try + { + ref.invoke(this, met_shutdown, NO_ARGS, -7207851917985848402L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public MarshalledObject activate(ActivationID p0, boolean p1) + throws ActivationException, UnknownObjectException, RemoteException + { + try + { + Object result = ref.invoke(this, met_activate, + new Object[] { p0, new Boolean(p1) }, + -8767355154875805558L); + return (MarshalledObject) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public ActivationMonitor activeGroup(ActivationGroupID p0, + ActivationInstantiator p1, long p2) + throws UnknownGroupException, ActivationException, RemoteException + { + try + { + Object result = ref.invoke(this, met_activeGroup, + new Object[] { p0, p1, new Long(p2) }, + -4575843150759415294L); + return (ActivationMonitor) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public ActivationGroupID registerGroup(ActivationGroupDesc p0) + throws ActivationException, RemoteException + { + try + { + Object result = ref.invoke(this, met_registerGroup, + new Object[] { p0 }, 6921515268192657754L); + return (ActivationGroupID) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public ActivationGroupDesc getActivationGroupDesc(ActivationGroupID p0) + throws ActivationException, UnknownGroupException, RemoteException + { + try + { + Object result = ref.invoke(this, met_getActivationGroupDesc, + new Object[] { p0 }, -8701843806548736528L); + return (ActivationGroupDesc) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void activeObject(ActivationID p0, MarshalledObject p1) + throws UnknownObjectException, RemoteException + { + try + { + ref.invoke(this, met_activeObject, new Object[] { p0, p1 }, + 2543984342209939736L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public ActivationID registerObject(ActivationDesc p0) + throws ActivationException, UnknownGroupException, RemoteException + { + try + { + Object result = ref.invoke(this, met_registerObject, + new Object[] { p0 }, -3006759798994351347L); + return (ActivationID) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void inactiveObject(ActivationID p0) throws UnknownObjectException, + RemoteException + { + try + { + ref.invoke(this, met_inactiveObject, new Object[] { p0 }, + -4165404120701281807L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void unregisterGroup(ActivationGroupID p0) throws ActivationException, + UnknownGroupException, RemoteException + { + try + { + ref.invoke(this, met_unregisterGroup, new Object[] { p0 }, + 3768097077835970701L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmid/Main.java b/libjava/classpath/tools/gnu/classpath/tools/rmid/Main.java new file mode 100644 index 000000000..3cc2a12ad --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmid/Main.java @@ -0,0 +1,258 @@ +/* Main.java -- the RMI activation daemon. + Copyright (c) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmid; + +import gnu.classpath.tools.rmid.ActivationSystemImpl; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.java.rmi.activation.ActivationSystemTransient; +import gnu.java.rmi.server.UnicastServerRef; + +import java.io.File; +import java.net.InetAddress; +import java.rmi.Remote; +import java.rmi.activation.ActivationSystem; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.ObjID; +import java.rmi.server.RMIServerSocketFactory; + + +/** + * The persistent RMI activation daemon. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public class Main +{ + /** + * The RMI server socket factory. + */ + static RMIServerSocketFactory ACTIVATION_REGISTY_SOCKET_FACTORY = null; + + /** + * The activation registry port. + */ + static int ACTIVATION_REGISTRY_PORT = ActivationSystem.SYSTEM_PORT; + + /** + * The activation system name. + */ + static String ACTIVATION_SYSTEM_NAME = "java.rmi.activation.ActivationSystem"; + + // Parse parameters: + private boolean stop = false; + private String directory = "."; + private boolean cold = false; + private boolean persistent = false; + + private Parser initializeParser() + { + Parser parser = new ClasspathToolParser("rmiregistry", true); //$NON-NLS-1$ + parser.setHeader(Messages.getString("Main.Usage")); //$NON-NLS-1$ + + + OptionGroup controlGroup + = new OptionGroup(Messages.getString("Main.ControlGroup")); //$NON-NLS-1$ + controlGroup.add(new Option("port", //$NON-NLS-1$ + Messages.getString("Main.PortOption"), //$NON-NLS-1$ + Messages.getString("Main.Port")) //$NON-NLS-1$ + { + public void parsed(String portArgument) throws OptionException + { + ACTIVATION_REGISTRY_PORT = Integer.parseInt(portArgument); + } + }); + controlGroup.add(new Option("restart", //$NON-NLS-1$ + Messages.getString("Main.Restart")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + cold = true; + } + }); + controlGroup.add(new Option("stop", //$NON-NLS-1$ + Messages.getString("Main.Stop")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + stop = true; + } + }); + parser.add(controlGroup); + + OptionGroup persistenceGroup + = new OptionGroup(Messages.getString("Main.PersistenceGroup")); //$NON-NLS-1$ + persistenceGroup.add(new Option("persistent", //$NON-NLS-1$ + Messages.getString("Main.Persistent")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + persistent = true; + } + }); + persistenceGroup.add(new Option("directory", //$NON-NLS-1$ + Messages.getString("Main.Directory"), //$NON-NLS-1$ + Messages.getString("Main.DirectoryArgument")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + directory = argument; + } + }); + parser.add(persistenceGroup); + + OptionGroup debuggingGroup + = new OptionGroup(Messages.getString("Main.DebugGroup")); //$NON-NLS-1$ + debuggingGroup.add(new Option("verbose", //$NON-NLS-1$ + Messages.getString ("Main.Verbose")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + ActivationSystemTransient.debug = true; + } + }); + parser.add(debuggingGroup); + + return parser; + } + + private void run(String[] args) + { + Parser p = initializeParser(); + p.parse(args); + + try + { + if (!stop) + { + // Start the system. + File dataDirectory = new File(directory); + if (!dataDirectory.exists()) + dataDirectory.mkdirs(); + ActivationSystem system; + + if (!persistent) + system = ActivationSystemTransient.getInstance(); + else + system = ActivationSystemImpl.getInstance(dataDirectory, cold); + + // We must export with the specific activation id that is only + // possible when going into the gnu.java.rmi.activation. + UnicastServerRef sref = new UnicastServerRef( + new ObjID(ObjID.ACTIVATOR_ID), ACTIVATION_REGISTRY_PORT, + ACTIVATION_REGISTY_SOCKET_FACTORY); + Remote systemStub = sref.exportObject(system); + + // Start the naming system on the activation system port + // (if not already running). + + Registry r; + try + { + // Expect the naming service running first. + // The local host may want to use the shared registry + r = LocateRegistry.getRegistry(ACTIVATION_REGISTRY_PORT); + r.rebind(ACTIVATION_SYSTEM_NAME, systemStub); + } + catch (Exception ex) + { + // The naming service is not running. Start it. + r = LocateRegistry.createRegistry(ACTIVATION_REGISTRY_PORT); + r.rebind(ACTIVATION_SYSTEM_NAME, systemStub); + } + String host = InetAddress.getLocalHost().getCanonicalHostName(); + System.out.println("The RMI daemon is listening on " + host + + " (port " + + ACTIVATION_REGISTRY_PORT + ")"); + + } + else + { + // Stop the activation system. + Registry r; + try + { + System.out.print("Stopping RMI daemon at " + + ACTIVATION_REGISTRY_PORT+" ... "); + // Expect the naming service running first. + // The local host may want to use the shared registry + r = LocateRegistry.getRegistry(ACTIVATION_REGISTRY_PORT); + ActivationSystem asys = + (ActivationSystem) r.lookup(ACTIVATION_SYSTEM_NAME); + asys.shutdown(); + System.out.println("OK."); + } + catch (Exception ex) + { + System.out.println("The RMI daemon seems not running at " + + ACTIVATION_REGISTRY_PORT); + if (ActivationSystemTransient.debug) + ex.printStackTrace(); + } + } + } + catch (Exception e) + { + System.out.println("Failed to start the RMI daemon."); + if (ActivationSystemTransient.debug) + e.printStackTrace(); + } + } + + /** + * The activation system entry point. + */ + public static void main(String[] args) + { + Main rmidprogram = new Main(); + try + { + rmidprogram.run(args); + } + catch (Exception e) + { + System.err.println(Messages.getString("Main.InternalError")); //$NON-NLS-1$ + e.printStackTrace(System.err); + System.exit(1); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmid/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/rmid/Messages.java new file mode 100644 index 000000000..4365c6de7 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmid/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- localization support for rmid + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.rmid; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.rmid.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmid/PersistentBidiHashTable.java b/libjava/classpath/tools/gnu/classpath/tools/rmid/PersistentBidiHashTable.java new file mode 100644 index 000000000..9d9849663 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmid/PersistentBidiHashTable.java @@ -0,0 +1,269 @@ +/* PersistentBidiHasthable.java -- Bidirectional persistent hash table. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmid; + +import gnu.classpath.tools.common.Persistent; +import gnu.classpath.tools.rmid.ActivationSystemImpl; +import gnu.java.rmi.activation.BidiTable; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.TimerTask; + +/** + * The persistent bidirectional hash table, maps both a to b and b to a. The + * changes are written to dist after SAVE_AT_MOST_AFTER time from the latest + * database change or at most after ALWAYS_UPDATE, if the database is updated + * very frequently. To ensure that no information is lost, the shutdown method + * must be called before exit. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public class PersistentBidiHashTable extends BidiTable implements + Persistent +{ + class WriteToDiskTask extends TimerTask + { + /** + * Save the database. + */ + public void run() + { + writeContent(); + sheduled = null; + } + } + + /** + * Replaces instances of ActivationSystemImpl into the currently active + * instance of the ActivationSystemImpl + */ + class AdaptedReader extends ObjectInputStream + { + AdaptedReader(InputStream in) throws IOException + { + super(in); + enableResolveObject(true); + } + + protected Object resolveObject(Object obj) throws IOException + { + if (obj instanceof ActivationSystemImpl) + return ActivationSystemImpl.singleton2; + else + return obj; + } + } + + /** + * The database file. + */ + File database; + + /** + * The currently sheduled write to disk task, null if none. + */ + WriteToDiskTask sheduled = null; + + /** + * The time, when the disk database was last updated. + */ + long lastUpdated; + + /** + * Create the unitialised instance that must be initalised when + * ActivationSystemImpl.singleton2 is assigned. + */ + public PersistentBidiHashTable() + { + // Do not initalise the table fields - the initalise method must be + // called later. + super(0); + } + + /** + * Create a new persistent table that stores its information into the given + * file. The ActivationSystemImpl.singleton2 must be assigned. + * + * @param file + * the file, where the table stores its information. + * @param coldStart + * if true, the existing file with this name will be erased and + * ignored. Otherwise, it will be assumed that the file contains the + * persistent table information. + */ + public void init(File file, boolean coldStart) + { + try + { + database = file; + if (database.exists()) + { + if (coldStart) + { + k2v = new Hashtable(); + v2k = new Hashtable(); + database.delete(); + } + else + { + FileInputStream fi = new FileInputStream(file); + BufferedInputStream b = new BufferedInputStream(fi); + ObjectInputStream oin = new AdaptedReader(b); + + k2v = (Map) oin.readObject(); + oin.close(); + + v2k = new Hashtable(k2v.size()); + + // Reguild v2k from k2v: + Iterator en = k2v.keySet().iterator(); + Object key; + while (en.hasNext()) + { + key = en.next(); + v2k.put(k2v.get(key), key); + } + } + } + else + { + k2v = new Hashtable(); + v2k = new Hashtable(); + } + } + catch (Exception ioex) + { + InternalError ierr = new InternalError("Unable to intialize with file " + + file); + ierr.initCause(ioex); + throw ierr; + } + } + + /** + * Write the database content to the disk. + */ + public synchronized void writeContent() + { + try + { + FileOutputStream fou = new FileOutputStream(database); + BufferedOutputStream b = new BufferedOutputStream(fou); + ObjectOutputStream oout = new ObjectOutputStream(b); + oout.writeObject(k2v); + oout.close(); + } + catch (Exception ioex) + { + InternalError ierr = new InternalError( + "Failed to write database to disk: " + + database); + ierr.initCause(ioex); + throw ierr; + } + } + + /** + * Mark the modified database as modified. The database will be written after + * several seconds, unless another modification occurs. + */ + public void markDirty() + { + if (System.currentTimeMillis() - lastUpdated > ALWAYS_UPDATE) + { + // Force storing to disk under intensive operation. + writeContent(); + lastUpdated = System.currentTimeMillis(); + if (sheduled != null) + { + sheduled.cancel(); + sheduled = null; + } + } + else + { + // Otherwise coalesce the disk database copy update events. + if (sheduled != null) + sheduled.cancel(); + sheduled = new WriteToDiskTask(); + timer.schedule(sheduled, SAVE_AT_MOST_AFTER); + } + } + + /** + * Save the current database state to the disk before exit. + */ + public void shutdown() + { + if (sheduled != null) + { + writeContent(); + sheduled = null; + } + } + + /** + * Update the memory maps and mark as should be written to the disk. + */ + public void put(Object key, Object value) + { + super.put(key, value); + markDirty(); + } + + /** + * Update the memory maps and mark as should be written to the disk. + */ + public void removeKey(Object key) + { + super.removeKey(key); + markDirty(); + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/Main.java b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/Main.java new file mode 100644 index 000000000..f22e81b6e --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/Main.java @@ -0,0 +1,232 @@ +/* Main.java -- RMI registry starter. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmiregistry; + +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.FileArgumentCallback; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.OptionGroup; +import gnu.classpath.tools.getopt.Parser; +import gnu.classpath.tools.rmiregistry.RegistryImpl; +import gnu.java.rmi.server.UnicastServerRef; + +import java.io.File; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.ObjID; +import java.rmi.server.RMIServerSocketFactory; +import java.util.Hashtable; +import java.util.Map; + +/** + * The optionally persistent RMI registry implementation. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public class Main +{ + /** + * The stop command. + */ + public static String STOP = "gnu.classpath.tools.rmi.registry.command.STOP"; + + /** + * If true, the registry prints registration events to console. + */ + public static boolean verbose = false; + + /** + * Parsed parameters. + */ + private String directory = "."; + private boolean cold = false; + private boolean persistent = false; + private boolean stop = false; + private int port = Registry.REGISTRY_PORT; + private RMIServerSocketFactory ssf = null; + + private Parser initializeParser() + { + Parser parser = new ClasspathToolParser("rmiregistry", true); //$NON-NLS-1$ + parser.setHeader(Messages.getString("Main.Usage")); //$NON-NLS-1$ + + OptionGroup controlGroup + = new OptionGroup(Messages.getString("Main.ControlGroup")); //$NON-NLS-1$ + controlGroup.add(new Option("restart", //$NON-NLS-1$ + Messages.getString("Main.Restart")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + cold = true; + } + }); + controlGroup.add(new Option("stop", //$NON-NLS-1$ + Messages.getString("Main.Stop")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + stop = true; + } + }); + parser.add(controlGroup); + + OptionGroup persistenceGroup + = new OptionGroup(Messages.getString("Main.PersistenceGroup")); //$NON-NLS-1$ + persistenceGroup.add(new Option("persistent", //$NON-NLS-1$ + Messages.getString("Main.Persistent")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + persistent = true; + } + }); + persistenceGroup.add(new Option("directory", //$NON-NLS-1$ + Messages.getString("Main.Directory"), //$NON-NLS-1$ + Messages.getString("Main.DirectoryArgument")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + directory = argument; + } + }); + parser.add(persistenceGroup); + + OptionGroup debuggingGroup + = new OptionGroup(Messages.getString("Main.DebugGroup")); //$NON-NLS-1$ + debuggingGroup.add(new Option("verbose", //$NON-NLS-1$ + Messages.getString ("Main.Verbose")) //$NON-NLS-1$ + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + parser.add(debuggingGroup); + + return parser; + } + + private void run(String[] args) + { + Parser p = initializeParser(); + p.parse(args, new FileArgumentCallback() + { + public void notifyFile(String portArgument) + { + port = Integer.parseInt(portArgument); + } + }); + + if (!stop) + { + Map table; + if (!persistent) + table = new Hashtable(); + else + { + // Start the system. + File dataDirectory = new File(directory); + if (!dataDirectory.exists()) + dataDirectory.mkdirs(); + table = PersistentHashTable.createInstance( + new File(dataDirectory, "rmiregistry.data"), cold); + } + + RegistryImpl system = new RegistryImpl(table); + + // We must export with the specific activation id that is only + // possible when going into the gnu.java.rmi + try + { + UnicastServerRef sref = new UnicastServerRef( + new ObjID(ObjID.REGISTRY_ID), port, ssf); + + sref.exportObject(system); + System.out.println("The RMI naming service is listening at " + port); + } + catch (Exception ex) + { + System.out.println("Failed to start RMI naming service at " + port); + } + } + else + { + // Stop the naming service. + try + { + Registry r = LocateRegistry.getRegistry(port); + // Search for this specific line will command to stop the registry. + + // Our service returns null, but any other service will thrown + // NotBoundException. + r.unbind(STOP); + } + catch (RemoteException e) + { + System.out.println("Failed to stop RMI naming service at " + port); + } + catch (NotBoundException e) + { + System.out.println("The naming service at port " + port + " is not a " + + Main.class.getName()); + } + } + } + + /** + * The RMI registry implementation entry point. + */ + public static void main(String[] args) + { + Main rmiregistryprogram = new Main(); + try + { + rmiregistryprogram.run(args); + } + catch (Exception e) + { + System.err.println(Messages.getString("Main.InternalError")); //$NON-NLS-1$ + e.printStackTrace(System.err); + System.exit(1); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/Messages.java new file mode 100644 index 000000000..05bfcf62d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- localization support for rmiregistry + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.rmiregistry; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.rmiregistry.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/PersistentHashTable.java b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/PersistentHashTable.java new file mode 100644 index 000000000..3202a8405 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/PersistentHashTable.java @@ -0,0 +1,262 @@ +/* PersistentHasthable.java -- Persistent hash table. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmiregistry; + +import gnu.classpath.tools.common.Persistent; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Hashtable; +import java.util.Map; +import java.util.TimerTask; + +/** + * The persistent hash table. The changes are written to dist after + * SAVE_AT_MOST_AFTER time from the latest database change or at most after + * ALWAYS_UPDATE, if the database is updated very frequently. To ensure that no + * information is lost, the shutdown method must be called before exit. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public class PersistentHashTable + extends Hashtable + implements Serializable, Persistent +{ + + /** + * Use serialVersionUID for interoperability + */ + private static final long serialVersionUID = 1; + + class WriteToDiskTask extends TimerTask + { + /** + * Save the database. + */ + public void run() + { + writeContent(); + sheduled = null; + } + } + + /** + * The database file. + */ + File database; + + /** + * The currently sheduled write to disk task, null if none. + */ + WriteToDiskTask sheduled = null; + + /** + * The time, when the disk database was last updated. + */ + long lastUpdated; + + /** + * Setting to false prevents the automated disk update. + * The initial value is true to prevent writing while reading and is set + * to false in createInstance. + */ + transient boolean ready; + + /** + * Use static method to obtain the instance. + */ + private PersistentHashTable(File file) + { + if (file == null) + throw new NullPointerException("Null file provided"); + database = file; + } + + /** + * Create a new persistent table that stores its information into the given + * file. + * + * @param file + * the file, where the table stores its information. + * @param coldStart + * if true, the existing file with this name will be erased and + * ignored. Otherwise, it will be assumed that the file contains the + * persistent table information. + */ + public static Map createInstance(File file, boolean coldStart) + { + try + { + PersistentHashTable k2v; + System.out.println ("Here1"); + if (file.exists()) + { + System.out.println ("Here2"); + if (coldStart) + { + System.out.println ("Here2.5"); + file.delete(); + k2v = new PersistentHashTable(file); + } + else + { + System.out.println ("Here3"); + FileInputStream fi = new FileInputStream(file); + System.out.println ("Here3.1"); + BufferedInputStream b = new BufferedInputStream(fi); + System.out.println ("Here3.2"); + ObjectInputStream oin = new ObjectInputStream(b); + System.out.println ("Here3.3"); + + System.out.println ("Here4"); + k2v = (PersistentHashTable) oin.readObject(); + oin.close(); + System.out.println ("Here5"); + } + } + else + { + System.out.println ("Here6"); + k2v = new PersistentHashTable(file); + System.out.println ("Here7"); + } + + System.out.println ("Here8"); + k2v.ready = true; + return k2v; + } + catch (Exception ioex) + { + InternalError ierr = new InternalError("Unable to intialize with file " + + file); + ierr.initCause(ioex); + throw ierr; + } + } + + + /** + * Write the database content to the disk. + */ + public synchronized void writeContent() + { + try + { + FileOutputStream fou = new FileOutputStream(database); + BufferedOutputStream b = new BufferedOutputStream(fou); + ObjectOutputStream oout = new ObjectOutputStream(b); + oout.writeObject(this); + oout.close(); + } + catch (Exception ioex) + { + InternalError ierr = new InternalError( + "Failed to write database to disk: "+ database); + ierr.initCause(ioex); + throw ierr; + } + } + + /** + * Mark the modified database as modified. The database will be written after + * several seconds, unless another modification occurs. + */ + public void markDirty() + { + if (System.currentTimeMillis() - lastUpdated > ALWAYS_UPDATE) + { + // Force storing to disk under intensive operation. + writeContent(); + lastUpdated = System.currentTimeMillis(); + if (sheduled != null) + { + sheduled.cancel(); + sheduled = null; + } + } + else + { + // Otherwise coalesce the disk database copy update events. + if (sheduled != null) + sheduled.cancel(); + sheduled = new WriteToDiskTask(); + timer.schedule(sheduled, SAVE_AT_MOST_AFTER); + } + } + + /** + * Save the current database state to the disk before exit. + */ + public void shutdown() + { + if (sheduled != null) + { + writeContent(); + sheduled = null; + } + } + + /** + * Update the memory maps and mark as should be written to the disk. + */ + public Object put(Object key, Object value) + { + super.put(key, value); + if (ready) + markDirty(); + return value; + } + + /** + * Update the memory maps and mark as should be written to the disk. + */ + public Object remove(Object key) + { + Object removed = super.remove(key); + if (ready) + markDirty(); + return removed; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl.java b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl.java new file mode 100644 index 000000000..80d0fe0c1 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl.java @@ -0,0 +1,138 @@ +/* RegistryImpl.java -- the RMI registry implementation + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmiregistry; + +import gnu.classpath.tools.common.Persistent; + +import java.rmi.AccessException; +import java.rmi.AlreadyBoundException; +import java.rmi.NotBoundException; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.registry.Registry; +import java.util.ArrayList; +import java.util.Map; + +/** + * The optionally persistent registry implementation. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public class RegistryImpl implements Registry +{ + /** + * The binding table. + */ + Map bindings; + + /** + * Create the registry implementation that uses the given bidirectinal + * table to keep the data. + */ + public RegistryImpl(Map aTable) + { + bindings = aTable; + } + + /** @inheritDoc */ + public Remote lookup(String name) throws RemoteException, NotBoundException, + AccessException + { + Object obj = bindings.get(name); + if (obj == null) + throw new NotBoundException(name); + return ((Remote) obj); + } + + /** @inheritDoc */ + public void bind(String name, Remote obj) throws RemoteException, + AlreadyBoundException, AccessException + { + if (Main.verbose) + System.out.println("Bind "+name); + if (bindings.containsKey(name)) + throw new AlreadyBoundException(name); + bindings.put(name, obj); + } + + /** @inheritDoc */ + public void unbind(String name) throws RemoteException, NotBoundException, + AccessException + { + if (name.equals(Main.STOP)) + { + if (bindings instanceof Persistent) + ((Persistent) bindings).writeContent(); + // Terminate in 10 seconds. + System.out.println("Shutdown command received. Will terminate in 10 s"); + Persistent.timer.schedule(new Persistent.ExitTask(), 10000); + } + else + { + if (Main.verbose) + System.out.println("Unbind "+name); + + if (!bindings.containsKey(name)) + throw new NotBoundException(name); + else + bindings.remove(name); + } + } + + /** @inheritDoc */ + public void rebind(String name, Remote obj) throws RemoteException, + AccessException + { + if (Main.verbose) + System.out.println("Rebind "+name); + bindings.put(name, obj); + } + + /** @inheritDoc */ + public String[] list() throws RemoteException, AccessException + { + // Create a separated array to prevent race conditions. + ArrayList keys = new ArrayList(bindings.keySet()); + int n = keys.size(); + String[] rt = new String[n]; + for (int i = 0; i < n; i++) + rt[i] = (String) keys.get(i); + return rt; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl_Skel.java b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl_Skel.java new file mode 100644 index 000000000..fa717892a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl_Skel.java @@ -0,0 +1,278 @@ +/* RegistryImpl_Skel.java + Copyright (C) 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.rmiregistry; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.rmi.MarshalException; +import java.rmi.Remote; +import java.rmi.UnmarshalException; +import java.rmi.server.Operation; +import java.rmi.server.RemoteCall; +import java.rmi.server.SkeletonMismatchException; + +/** + * This skeleton supports unlikely cases when the naming service is + * contacted from other interoperable java implementation that still uses + * the old style skeleton-dependent invocations. + */ +public final class RegistryImpl_Skel + implements java.rmi.server.Skeleton +{ + private static final long interfaceHash = 4905912898345647071L; + + /** + * Repeated multiple times. + */ + static final String EUM = "error unmarshalling arguments for Registry"; + + /** + * Repeated multiple times. + */ + static final String EMR = "error marshalling return from Registry"; + + private static final Operation[] operations = + { + new Operation("void bind(java.lang.String, Remote"), + new Operation("java.lang.String[] list("), + new Operation("Remote lookup(java.lang.String"), + new Operation("void rebind(java.lang.String, Remote"), + new Operation("void unbind(java.lang.String") + }; + + public Operation[] getOperations() + { + return ((Operation[]) operations.clone()); + } + + public void dispatch(Remote obj, RemoteCall call, + int opnum, long hash) throws java.lang.Exception + { + if (opnum < 0) + { + if (hash == 7583982177005850366L) + opnum = 0; + else if (hash == 2571371476350237748L) + opnum = 1; + else if (hash == -7538657168040752697L) + opnum = 2; + else if (hash == -8381844669958460146L) + opnum = 3; + else if (hash == 7305022919901907578L) + opnum = 4; + else + throw new SkeletonMismatchException("interface hash mismatch"); + } + else if (hash != interfaceHash) + throw new SkeletonMismatchException("interface hash mismatch"); + + RegistryImpl server = (RegistryImpl) obj; + switch (opnum) + { + case 0: + { + java.lang.String $param_0; + Remote $param_1; + try + { + ObjectInput in = call.getInputStream(); + $param_0 = (java.lang.String) in.readObject(); + $param_1 = (Remote) in.readObject(); + + } + catch (IOException e) + { + throw new UnmarshalException(EUM, e); + } + catch (java.lang.ClassCastException e) + { + throw new UnmarshalException(EUM, e); + } + finally + { + call.releaseInputStream(); + } + server.bind($param_0, $param_1); + try + { + ObjectOutput out = call.getResultStream(true); + } + catch (IOException e) + { + throw new MarshalException(EMR, e); + } + break; + } + + case 1: + { + try + { + ObjectInput in = call.getInputStream(); + + } + catch (IOException e) + { + throw new UnmarshalException(EUM, e); + } + finally + { + call.releaseInputStream(); + } + java.lang.String[] $result = server.list(); + try + { + ObjectOutput out = call.getResultStream(true); + out.writeObject($result); + } + catch (IOException e) + { + throw new MarshalException(EMR, e); + } + break; + } + + case 2: + { + java.lang.String $param_0; + try + { + ObjectInput in = call.getInputStream(); + $param_0 = (java.lang.String) in.readObject(); + + } + catch (IOException e) + { + throw new UnmarshalException(EUM, e); + } + catch (java.lang.ClassCastException e) + { + throw new UnmarshalException(EUM, e); + } + finally + { + call.releaseInputStream(); + } + Remote $result = server.lookup($param_0); + try + { + ObjectOutput out = call.getResultStream(true); + out.writeObject($result); + } + catch (IOException e) + { + throw new MarshalException(EMR, e); + } + break; + } + + case 3: + { + java.lang.String $param_0; + Remote $param_1; + try + { + ObjectInput in = call.getInputStream(); + $param_0 = (java.lang.String) in.readObject(); + $param_1 = (Remote) in.readObject(); + + } + catch (IOException e) + { + throw new UnmarshalException(EUM, e); + } + catch (java.lang.ClassCastException e) + { + throw new UnmarshalException(EUM, e); + } + finally + { + call.releaseInputStream(); + } + server.rebind($param_0, $param_1); + try + { + ObjectOutput out = call.getResultStream(true); + } + catch (IOException e) + { + throw new MarshalException(EMR, e); + } + break; + } + + case 4: + { + java.lang.String $param_0; + try + { + ObjectInput in = call.getInputStream(); + $param_0 = (java.lang.String) in.readObject(); + + } + catch (IOException e) + { + throw new UnmarshalException(EUM, e); + } + catch (java.lang.ClassCastException e) + { + throw new UnmarshalException(EUM, e); + } + finally + { + call.releaseInputStream(); + } + server.unbind($param_0); + try + { + ObjectOutput out = call.getResultStream(true); + } + catch (IOException e) + { + throw new MarshalException(EMR, e); + } + break; + } + + default: + throw new UnmarshalException("invalid method number"); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl_Stub.java b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl_Stub.java new file mode 100644 index 000000000..dfb277fbd --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/rmiregistry/RegistryImpl_Stub.java @@ -0,0 +1,263 @@ +/* RegistryImpl_Stub.java -- Registry stub. + Copyright (c) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.rmiregistry; + +import java.rmi.AccessException; +import java.rmi.AlreadyBoundException; +import java.rmi.NotBoundException; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.registry.Registry; + +import java.lang.reflect.Method; +import java.rmi.server.RemoteRef; +import java.rmi.server.RemoteStub; +import java.rmi.UnexpectedException; + +/** + * This class delegates its method calls to the remote RMI object, referenced + * by {@link RemoteRef}. + * + * It is normally generated with rmic. + */ +public final class RegistryImpl_Stub + extends RemoteStub + implements Registry +{ + /** + * Use serialVersionUID for interoperability + */ + private static final long serialVersionUID = 3; + + /** + * The explaining message for {@ling UnexpectedException}. + */ + private static final String exception_message = + "undeclared checked exception"; + + /* All remote methods, invoked by this stub: */ + private static final Method met_list; + private static final Method met_rebind; + private static final Method met_unbind; + private static final Method met_lookup; + private static final Method met_bind; + private static final Object[] NO_ARGS = new Object[0]; + static + { + final Class[] NO_ARGSc = new Class[0]; + try + { + met_list = + Registry.class.getMethod("list", NO_ARGSc); + met_rebind = + Registry.class.getMethod("rebind", new Class[] + { + String.class, Remote.class + }); + met_unbind = + Registry.class.getMethod("unbind", new Class[] + { + String.class + }); + met_lookup = + Registry.class.getMethod("lookup", new Class[] + { + String.class + }); + met_bind = + Registry.class.getMethod("bind", new Class[] + { + String.class, Remote.class + }); + + } + catch (NoSuchMethodException nex) + { + NoSuchMethodError err = new NoSuchMethodError( + "RegistryImpl_Stub class initialization failed"); + err.initCause(nex); + throw err; + } + } + + /** + * Create the instance for _RegistryImpl_Stub that forwards method calls to the + * remote object. + * + * @para the reference to the remote object. + */ + public RegistryImpl_Stub(RemoteRef reference) + { + super(reference); + } + + /* Methods */ + /** @inheritDoc */ + public String [] list() + throws RemoteException, AccessException + { + try + { + Object result = ref.invoke(this, met_list, + NO_ARGS, + 2571371476350237748L); + return (String []) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void rebind(String p0, Remote p1) + throws RemoteException, AccessException + { + try + { + ref.invoke(this, met_rebind, + new Object[] {p0, p1}, + -8381844669958460146L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void unbind(String p0) + throws RemoteException, NotBoundException, AccessException + { + try + { + ref.invoke(this, met_unbind, + new Object[] {p0}, + 7305022919901907578L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public Remote lookup(String p0) + throws RemoteException, NotBoundException, AccessException + { + try + { + Object result = ref.invoke(this, met_lookup, + new Object[] {p0}, + -7538657168040752697L); + return (Remote) result; + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + /** @inheritDoc */ + public void bind(String p0, Remote p1) + throws RemoteException, AlreadyBoundException, AccessException + { + try + { + ref.invoke(this, met_bind, + new Object[] {p0, p1}, + 7583982177005850366L); + } + catch (RuntimeException e) + { + throw e; + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnexpectedException uex = new UnexpectedException(exception_message); + uex.detail = e; + throw uex; + } + } + + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/serialver/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/serialver/Messages.java new file mode 100644 index 000000000..a6ab67add --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/serialver/Messages.java @@ -0,0 +1,68 @@ +/* Messages.java -- translations for serialver tool + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.serialver; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.serialver.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + // TODO Auto-generated method stub + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/serialver/SerialVer.java b/libjava/classpath/tools/gnu/classpath/tools/serialver/SerialVer.java new file mode 100644 index 000000000..5fd7419b2 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/serialver/SerialVer.java @@ -0,0 +1,179 @@ +/* gnu.classpath.tools.SerialVer + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package gnu.classpath.tools.serialver; + +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.FileArgumentCallback; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +import java.io.File; +import java.io.ObjectStreamClass; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.StringTokenizer; + +/** + * This class is an implementation of the `serialver' program. Any number of + * class names can be passed as arguments, and the serial version unique + * identitfier for each class will be printed in a manner suitable for cuting + * and pasting into a Java source file. + */ +public class SerialVer +{ + // List of classes to load. + ArrayList<String> classes = new ArrayList<String>(); + // The class path to use. + String classpath; + + // FIXME: taken from ClassLoader, should share it. + private static void addFileURL(ArrayList<URL> list, String file) + { + try + { + list.add(new File(file).toURL()); + } + catch(java.net.MalformedURLException x) + { + } + } + + private ClassLoader getClassLoader() + { + // FIXME: this code is taken from ClassLoader. + // We should share it somewhere. + URL[] urls; + if (classpath == null) + urls = new URL[0]; + else + { + StringTokenizer tok = new StringTokenizer(classpath, + File.pathSeparator, true); + ArrayList<URL> list = new ArrayList<URL>(); + while (tok.hasMoreTokens()) + { + String s = tok.nextToken(); + if (s.equals(File.pathSeparator)) + addFileURL(list, "."); //$NON-NLS-1$ + else + { + addFileURL(list, s); + if (tok.hasMoreTokens()) + { + // Skip the separator. + tok.nextToken(); + // If the classpath ended with a separator, + // append the current directory. + if (!tok.hasMoreTokens()) + addFileURL(list, "."); //$NON-NLS-1$ + } + } + } + urls = new URL[list.size()]; + urls = (URL[]) list.toArray(urls); + } + return new URLClassLoader(urls); + } + + private void printMessage(String format, String klass) + { + System.err.println(MessageFormat.format(format, new Object[] { klass })); + } + + public void run(String[] args) + { + Parser p = new ClasspathToolParser("serialver", true) //$NON-NLS-1$ + { + protected void validate() throws OptionException + { + if (classes.isEmpty()) + throw new OptionException(Messages.getString("SerialVer.NoClassesSpecd")); //$NON-NLS-1$ + } + }; + p.setHeader(Messages.getString("SerialVer.HelpHeader")); //$NON-NLS-1$ + + p.add(new Option(Messages.getString("SerialVer.5"), Messages.getString("SerialVer.ClasspathHelp"), "PATH") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + { + public void parsed(String argument) throws OptionException + { + if (classpath != null) + throw new OptionException(Messages.getString("SerialVer.DupClasspath")); //$NON-NLS-1$ + classpath = argument; + } + }); + + p.parse(args, new FileArgumentCallback() + { + public void notifyFile(String fileArgument) throws OptionException + { + classes.add(fileArgument); + } + }); + + ClassLoader loader = getClassLoader(); + Iterator it = classes.iterator(); + while (it.hasNext()) + { + String name = (String) it.next(); + try + { + Class clazz = loader.loadClass(name); + ObjectStreamClass osc = ObjectStreamClass.lookup(clazz); + if (osc != null) + System.out.println(clazz.getName() + ": " //$NON-NLS-1$ + + "static final long serialVersionUID = " //$NON-NLS-1$ + + osc.getSerialVersionUID() + "L;"); //$NON-NLS-1$ + else + printMessage(Messages.getString("SerialVer.ClassNotSerial"), name); //$NON-NLS-1$ + } + catch (ClassNotFoundException e) + { + printMessage(Messages.getString("SerialVer.ClassNotFound"), name); //$NON-NLS-1$ + } + } + } + + public static void main(String[] args) + { + new SerialVer().run(args); + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/AuthorTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/AuthorTaglet.java new file mode 100644 index 000000000..1a1f32b7d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/AuthorTaglet.java @@ -0,0 +1,293 @@ +/* gnu.classpath.tools.taglets.AuthorTaglet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import java.util.Map; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import com.sun.tools.doclets.Taglet; + +import com.sun.javadoc.Tag; + +/** + * The default Taglet which handles Author information. + * + * @author Julian Scheid (julian@sektor37.de) + */ +public class AuthorTaglet implements Taglet { + + /** + * Enum class which denotes whether and how to replace email + * addresses in author tags. + */ + public static class EmailReplacement { + private EmailReplacement() {} + + /** + * Specifies that email addresses should not be replaced. + */ + public static final EmailReplacement NO_REPLACEMENT = new EmailReplacement(); + + /** + * Specifies that author tag text matching "Real Name + * (user@domain.tld)" is converted to "<a + * href="mailto:user@domain.tld">Real Name</a>. + */ + public static final EmailReplacement MAILTO_NAME = new EmailReplacement(); + + /** + * Specifies that author tag text matching "Real Name + * (user@domain.tld)" is converted to "Real Name (<a + * href="mailto:user@domain.tld">user@domain.tld</a>). + */ + public static final EmailReplacement NAME_MAILTO_ADDRESS = new EmailReplacement(); + + /** + * Specifies that author tag text matching "Real Name + * (user@domain.tld)" is converted to "Real Name (user AT + * domain DOT tld)", where the "AT" and "DOT" replacement are + * specified by AuthorTaglet.emailAtReplacement and + * AuthorTaglet.emailDotReplacement. + */ + public static final EmailReplacement NAME_MANGLED_ADDRESS = new EmailReplacement(); + } + + private static EmailReplacement emailReplacementType = EmailReplacement.NO_REPLACEMENT; + private static String atReplacement = " <b>at</b> "; + private static String dotReplacement = " <b>dot</b> "; + + private static final String NAME = "author"; + private static final String SINGLE_HEADER = "Author:"; + private static final String MULTI_HEADER = "Authors:"; + + private static boolean enabled = true; + + /** + * Matches <code>.</code> (dot). + */ + private static final Pattern dotPattern = Pattern.compile("[.]"); + + /** + * Matches <code>@</code> (at sign). + */ + private static final Pattern atPattern = Pattern.compile("[@]"); + + /** + * Matches <code>Real Name (user@domain.tld)</code>. + */ + private static final Pattern authorEmailPattern + = Pattern.compile("^" + + "\\s*" // optional whitespace + + "(" // group #1 start (real name) + + "(?:[^\t\r\n ]|\\()+" // first name + + "(?:\\s+(?:[^\t\r\n ]|\\()+)*" // additional names + + ")" // group #1 end + + "\\s*" // optional whitespace + + "[(<]" // opening paren + + "\\s*" // optional whitespace + + "(" // group #2 start (email address) + + "(" // group #3 start (email user) + + "[A-z0-9_\\-\\.]+" // username + + ")" // group #3 end + + "[@]" // at sign + + "[A-z0-9_\\-]+(?:[.][A-z0-9_\\-]+)+[A-z]" // domain + + ")" // group #2 end + + "\\s*" // optional whitespace + + "(?:\\)|>)" // closing paren + + "$"); + + public String getName() { + return NAME; + } + + public boolean inField() { + return true; + } + + public boolean inConstructor() { + return true; + } + + public boolean inMethod() { + return true; + } + + public boolean inOverview() { + return true; + } + + public boolean inPackage() { + return true; + } + + public boolean inType() { + return true; + } + + public boolean isInlineTag() { + return false; + } + + public static void register(Map tagletMap) { + AuthorTaglet authorTaglet = new AuthorTaglet(); + tagletMap.put(authorTaglet.getName(), authorTaglet); + } + + public String toString(Tag tag) { + if (enabled) { + return toString(new Tag[] { tag }); + } + else { + return null; + } + } + + public String toString(Tag[] tags) { + if (!enabled || tags.length == 0) { + return null; + } + else { + boolean haveValidTag = false; + for (int i = 0; i < tags.length && !haveValidTag; ++i) { + if (tags[i].text().length() > 0) { + haveValidTag = true; + } + } + + if (haveValidTag) { + StringBuffer result = new StringBuffer(); + result.append("<dl class=\"tag list\">"); + result.append("<dt class=\"tag section header\"><b>"); + if (tags.length == 1) { + result.append(SINGLE_HEADER); + } + else { + result.append(MULTI_HEADER); + } + result.append("</b></dt>"); + for (int i = 0; i < tags.length; i++) { + result.append("<dd class=\"tag item\">"); + result.append(replaceEmail(tags[i].text())); + result.append("</dd>"); + } + result.append("</dl>"); + return result.toString(); + } + else { + return null; + } + } + } + + /** + * Reformat the tag text according to {@link #emailReplacementType}. + */ + private String replaceEmail(String text) { + + if (EmailReplacement.NO_REPLACEMENT == emailReplacementType) { + return text; + } + else { + Matcher matcher = authorEmailPattern.matcher(text); + if (matcher.matches()) { + String realName = matcher.group(1); + String emailAddress = matcher.group(2); + if (EmailReplacement.MAILTO_NAME == emailReplacementType) { + return "<a href=\"mailto:" + emailAddress + "\">" + realName + "</a>"; + } + else if (EmailReplacement.NAME_MAILTO_ADDRESS == emailReplacementType) { + return realName + " (<a href=\"mailto:" + emailAddress + "\">" + emailAddress + "</a>)"; + } + else if (EmailReplacement.NAME_MANGLED_ADDRESS == emailReplacementType) { + Matcher dotMatcher = dotPattern.matcher(emailAddress); + Matcher atMatcher = atPattern.matcher(dotMatcher.replaceAll(dotReplacement)); + String mangledAddress = atMatcher.replaceAll(atReplacement); + return realName + " (" + mangledAddress + ")"; + } + else { + // this shouldn't happen + return text; + } + } + else { + return text; + } + } + } + + /** + * Set the email replacement type. + */ + public static void setEmailReplacementType(EmailReplacement emailReplacementType) + { + if (null == emailReplacementType) { + throw new NullPointerException(); + } + AuthorTaglet.emailReplacementType = emailReplacementType; + } + + /** + * Set the HTML text by which the <code>@</code> (at sign) in email + * addresses should be replaced if the email replacement type is + * <code>NAME_MANGLED_ADDRESS</code>. + */ + public static void setAtReplacement(String atReplacement) + { + AuthorTaglet.atReplacement = atReplacement; + } + + /** + * Set the HTML text by which the <code>.</code> (dot) in email + * addresses should be replaced if the email replacement type is + * <code>NAME_MANGLED_ADDRESS</code>. + */ + public static void setDotReplacement(String dotReplacement) + { + AuthorTaglet.dotReplacement = dotReplacement; + } + + /** + * Enables/disables this taglet. + */ + public static void setTagletEnabled(boolean enabled) + { + AuthorTaglet.enabled = enabled; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/CodeTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/CodeTaglet.java new file mode 100644 index 000000000..adc34f391 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/CodeTaglet.java @@ -0,0 +1,101 @@ +/* gnu.classpath.tools.taglets.CodeTaglet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import java.util.Map; + +import com.sun.tools.doclets.Taglet; + +import com.sun.javadoc.Doc; +import com.sun.javadoc.Tag; +import com.sun.javadoc.FieldDoc; +import com.sun.javadoc.MemberDoc; +import com.sun.javadoc.SeeTag; + +/** + * The default Taglet which shows its contents enclosed in a + * <code>code</code> tag. + * + * @author Julian Scheid (julian@sektor37.de) + */ +public class CodeTaglet + implements Taglet +{ + private static final String NAME = "code"; + + public String getName() { + return NAME; + } + + public boolean inField() { + return true; + } + + public boolean inConstructor() { + return true; + } + + public boolean inMethod() { + return true; + } + + public boolean inOverview() { + return true; + } + + public boolean inPackage() { + return true; + } + + public boolean inType() { + return true; + } + + public boolean isInlineTag() { + return true; + } + + public String toString(Tag tag) { + return "<code>" + tag.text() + "</code>"; + } + + public String toString(Tag[] tag) { + return null; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/CopyrightTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/CopyrightTaglet.java new file mode 100644 index 000000000..60e3e40a8 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/CopyrightTaglet.java @@ -0,0 +1,123 @@ +/* gnu.classpath.tools.taglets.CopyrightTaglet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import java.util.Map; + +import com.sun.tools.doclets.Taglet; + +import com.sun.javadoc.Tag; + +/** + * A simple Taglet which handles Copyright information. + */ +public class CopyrightTaglet implements Taglet { + + private static final String NAME = "copyright"; + private static final String HEADER = "Copyright:"; + + public String getName() { + return NAME; + } + + public boolean inField() { + return true; + } + + public boolean inConstructor() { + return true; + } + + public boolean inMethod() { + return true; + } + + public boolean inOverview() { + return true; + } + + public boolean inPackage() { + return true; + } + + public boolean inType() { + return true; + } + + public boolean isInlineTag() { + return false; + } + + public static void register(Map tagletMap) { + CopyrightTaglet copyrightTaglet = new CopyrightTaglet(); + tagletMap.put(copyrightTaglet.getName(), copyrightTaglet); + } + + public String toString(Tag tag) { + return toString(new Tag[] { tag }); + } + + public String toString(Tag[] tags) { + if (tags.length == 0) { + return null; + } + else { + boolean haveValidTag = false; + for (int i = 0; i < tags.length && !haveValidTag; ++i) { + if (tags[i].text().length() > 0) { + haveValidTag = true; + } + } + + if (haveValidTag) { + StringBuffer result = new StringBuffer(); + result.append("<dl>"); + for (int i = 0; i < tags.length; i++) { + if (tags[i].text().length() > 0) { + result.append("<dt><i>Copyright © " + tags[i].text() + "</i></dt>"); + } + } + result.append("</dl>"); + return result.toString(); + } + else { + return null; + } + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/DeprecatedTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/DeprecatedTaglet.java new file mode 100644 index 000000000..d292c7f3a --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/DeprecatedTaglet.java @@ -0,0 +1,132 @@ +/* gnu.classpath.tools.taglets.DeprecatedTaglet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import java.util.Map; + +import com.sun.tools.doclets.Taglet; + +import com.sun.javadoc.Tag; + +/** + * The default Taglet which handles deprecated information. + * + * @author Julian Scheid (julian@sektor37.de) + */ +public class DeprecatedTaglet implements Taglet { + + private static final String NAME = "deprecated"; + private static final String HEADER = "Deprecated:"; + + private static boolean enabled = true; + + public String getName() { + return NAME; + } + + public boolean inField() { + return true; + } + + public boolean inConstructor() { + return true; + } + + public boolean inMethod() { + return true; + } + + public boolean inOverview() { + return true; + } + + public boolean inPackage() { + return true; + } + + public boolean inType() { + return true; + } + + public boolean isInlineTag() { + return false; + } + + public static void register(Map tagletMap) { + DeprecatedTaglet deprecatedTaglet = new DeprecatedTaglet(); + tagletMap.put(deprecatedTaglet.getName(), deprecatedTaglet); + } + + public String toString(Tag tag) { + if (enabled) { + return toString(new Tag[] { tag }); + } + else { + return null; + } + } + + public String toString(Tag[] tags) { + if (!enabled || tags.length == 0) { + return null; + } + else { + + StringBuffer result = new StringBuffer(); + result.append("<div class=\"classdoc-tag-section-header\">"); + result.append(HEADER); + result.append("</div>"); + result.append("<dl class=\"classdoc-list\">"); + for (int i = 0; i < tags.length; i++) { + result.append("<dt>"); + result.append(tags[i].text()); + result.append("</dt>"); + } + result.append("</dl>"); + return result.toString(); + } + } + + /** + * Enables/disables this taglet. + */ + public static void setTagletEnabled(boolean enabled) + { + DeprecatedTaglet.enabled = enabled; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/GenericTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/GenericTaglet.java new file mode 100644 index 000000000..31ff28922 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/GenericTaglet.java @@ -0,0 +1,157 @@ +/* gnu.classpath.tools.taglets.GenericTaglet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import java.util.Map; + +import com.sun.tools.doclets.Taglet; + +import com.sun.javadoc.Tag; + +/** + * A taglet which can be configured at runtime. + * + * @author Julian Scheid (julian@sektor37.de) + */ +public class GenericTaglet implements Taglet { + + private String name = "since"; + private String header = "Since:"; + + private boolean scopeOverview; + private boolean scopePackage; + private boolean scopeType; + private boolean scopeConstructor; + private boolean scopeMethod; + private boolean scopeField; + + private boolean enabled = true; + + public GenericTaglet(String name, + String header, + boolean scopeOverview, + boolean scopePackage, + boolean scopeType, + boolean scopeConstructor, + boolean scopeMethod, + boolean scopeField) + { + this.name = name; + this.header = header; + this.scopeOverview = scopeOverview; + this.scopePackage = scopePackage; + this.scopeType = scopeType; + this.scopeConstructor = scopeConstructor; + this.scopeMethod = scopeMethod; + this.scopeField = scopeField; + } + + public String getName() { + return name; + } + + public boolean inField() { + return scopeField; + } + + public boolean inConstructor() { + return scopeConstructor; + } + + public boolean inMethod() { + return scopeMethod; + } + + public boolean inOverview() { + return scopeOverview; + } + + public boolean inPackage() { + return scopePackage; + } + + public boolean inType() { + return scopeType; + } + + public boolean isInlineTag() { + return false; + } + + public void register(Map tagletMap) { + tagletMap.put(getName(), this); + } + + public String toString(Tag tag) { + if (enabled) { + return toString(new Tag[] { tag }); + } + else { + return null; + } + } + + public String toString(Tag[] tags) { + if (!enabled || tags.length == 0) { + return null; + } + else { + + StringBuffer result = new StringBuffer(); + result.append("<div class=\"classdoc-tag-section-header\">"); + result.append(header); + result.append("</div>"); + result.append("<dl class=\"classdoc-list\">"); + for (int i = 0; i < tags.length; i++) { + result.append("<dt>"); + result.append(tags[i].text()); + result.append("</dt>"); + } + result.append("</dl>"); + return result.toString(); + } + } + + /** + * Enables/disables this taglet. + */ + public void setTagletEnabled(boolean enabled) + { + this.enabled = enabled; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/GnuExtendedTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/GnuExtendedTaglet.java new file mode 100644 index 000000000..e78019067 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/GnuExtendedTaglet.java @@ -0,0 +1,48 @@ +/* gnu.classpath.tools.taglets.GnuExtendedTaglet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import com.sun.javadoc.Tag; +import com.sun.tools.doclets.Taglet; + +public interface GnuExtendedTaglet + extends Taglet +{ + public String toString(Tag tag, TagletContext context); + public String toString(Tag[] tags, TagletContext context); +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/SinceTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/SinceTaglet.java new file mode 100644 index 000000000..c3737522d --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/SinceTaglet.java @@ -0,0 +1,161 @@ +/* gnu.classpath.tools.taglets.SinceTaglet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import java.util.Map; + +import com.sun.tools.doclets.Taglet; + +import com.sun.javadoc.Tag; + +import gnu.classpath.tools.doclets.InlineTagRenderer; + +/** + * The default Taglet which handles since information. + * + * @author Julian Scheid (julian@sektor37.de) + */ +public class SinceTaglet implements GnuExtendedTaglet { + + private static final String NAME = "since"; + private static final String HEADER = "Since:"; + + private static boolean enabled = true; + + private InlineTagRenderer inlineTagRenderer; + + public SinceTaglet(InlineTagRenderer inlineTagRenderer) + { + this.inlineTagRenderer = inlineTagRenderer; + } + + public String getName() { + return NAME; + } + + public boolean inField() { + return true; + } + + public boolean inConstructor() { + return true; + } + + public boolean inMethod() { + return true; + } + + public boolean inOverview() { + return true; + } + + public boolean inPackage() { + return true; + } + + public boolean inType() { + return true; + } + + public boolean isInlineTag() { + return false; + } + + public String toString(Tag tag) { + // should raise assertion + if (enabled) { + return toString(new Tag[] { tag }); + } + else { + return null; + } + } + + public String toString(Tag[] tags) { + // should raise assertion + return toString(tags, null); + } + + public String toString(Tag tag, TagletContext context) + { + return null; + } + + public String toString(Tag[] tags, TagletContext context) + { + if (!enabled || tags.length == 0) { + return null; + } + else { + boolean haveValidTag = false; + for (int i = 0; i < tags.length && !haveValidTag; ++i) { + if (tags[i].text().length() > 0) { + haveValidTag = true; + } + } + + if (haveValidTag) { + StringBuffer result = new StringBuffer(); + result.append("<dl class=\"tag list\">"); + result.append("<dt class=\"tag section header\"><b>"); + result.append(HEADER); + result.append("</b></dt>"); + for (int i = 0; i < tags.length; ++i) { + if (tags[i].text().length() > 0) { + result.append("<dd>"); + result.append(inlineTagRenderer.renderInlineTags(tags[i].inlineTags(), context)); + result.append("</dd>"); + } + } + result.append("</dl>"); + return result.toString(); + } + else { + return null; + } + } + } + + /** + * Enables/disables this taglet. + */ + public static void setTagletEnabled(boolean enabled) + { + SinceTaglet.enabled = enabled; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/TagletContext.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/TagletContext.java new file mode 100644 index 000000000..82d8aea26 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/TagletContext.java @@ -0,0 +1,60 @@ +/* TagletContext.java - + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import com.sun.javadoc.Doc; + +public class TagletContext +{ + protected Doc doc; + + public TagletContext(Doc doc) + { + this.doc = doc; + } + + public Doc getDoc() + { + return this.doc; + } + + public String toString() + { + return "TagletContext{doc=" + doc + "}"; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/ValueTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/ValueTaglet.java new file mode 100644 index 000000000..1b07ada8c --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/ValueTaglet.java @@ -0,0 +1,130 @@ +/* gnu.classpath.tools.taglets.ValueTaglet + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import java.util.Map; + +import com.sun.tools.doclets.Taglet; + +import com.sun.javadoc.Doc; +import com.sun.javadoc.Tag; +import com.sun.javadoc.FieldDoc; +import com.sun.javadoc.MemberDoc; +import com.sun.javadoc.SeeTag; + +/** + * The default Taglet which shows final static field values. + * + * @author Julian Scheid (julian@sektor37.de) + */ +public class ValueTaglet + implements GnuExtendedTaglet +{ + private static final String NAME = "value"; + + public String getName() { + return NAME; + } + + public boolean inField() { + return true; + } + + public boolean inConstructor() { + return true; + } + + public boolean inMethod() { + return true; + } + + public boolean inOverview() { + return true; + } + + public boolean inPackage() { + return true; + } + + public boolean inType() { + return true; + } + + public boolean isInlineTag() { + return true; + } + + public static void register(Map tagletMap) { + ValueTaglet valueTaglet = new ValueTaglet(); + tagletMap.put(valueTaglet.getName(), valueTaglet); + } + + public String toString(Tag tag) { + return null; + } + + public String toString(Tag tag, TagletContext context) { + if (0 == tag.inlineTags().length) { + if (context.getDoc().isField()) { + FieldDoc fieldDoc = (FieldDoc)context.getDoc(); + if (fieldDoc.isStatic() && fieldDoc.isFinal()) { + return fieldDoc.constantValueExpression(); + } + } + } + else { + MemberDoc referencedMember = ((SeeTag)tag).referencedMember(); + if (null != referencedMember && referencedMember.isField()) { + FieldDoc fieldDoc = (FieldDoc)referencedMember; + if (fieldDoc.isStatic() && fieldDoc.isFinal()) { + return fieldDoc.constantValueExpression(); + } + } + } + return ""; + } + + public String toString(Tag[] tags) { + return null; + } + + public String toString(Tag[] tags, TagletContext context) { + return null; + } + +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/taglets/VersionTaglet.java b/libjava/classpath/tools/gnu/classpath/tools/taglets/VersionTaglet.java new file mode 100644 index 000000000..fed926e06 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/taglets/VersionTaglet.java @@ -0,0 +1,153 @@ +/* gnu.classpath.tools.taglets.VersionTaglet + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.taglets; + +import java.util.Map; + +import com.sun.tools.doclets.Taglet; + +import com.sun.javadoc.Tag; + +/** + * The default Taglet which handles version information. + * + * @author Julian Scheid (julian@sektor37.de) + */ +public class VersionTaglet implements Taglet { + + private static final String NAME = "version"; + private static final String HEADER = "Version:"; + + private static boolean enabled = true; + + public String getName() { + return NAME; + } + + public boolean inField() { + return true; + } + + public boolean inConstructor() { + return true; + } + + public boolean inMethod() { + return true; + } + + public boolean inOverview() { + return true; + } + + public boolean inPackage() { + return true; + } + + public boolean inType() { + return true; + } + + public boolean isInlineTag() { + return false; + } + + public static void register(Map tagletMap) { + VersionTaglet versionTaglet = new VersionTaglet(); + tagletMap.put(versionTaglet.getName(), versionTaglet); + } + + public String toString(Tag tag) { + if (enabled) { + return toString(new Tag[] { tag }); + } + else { + return null; + } + } + + public String toString(Tag[] tags) { + if (!enabled || tags.length == 0) { + return null; + } + else { + boolean haveValidTag = false; + for (int i = 0; i < tags.length && !haveValidTag; ++i) { + if (tags[i].text().length() > 0) { + haveValidTag = true; + } + } + + if (haveValidTag) { + + StringBuffer result = new StringBuffer(); + result.append("<dl class=\"tag list\">"); + result.append("</dl>"); + result.append("<dt class=\"tag section header\"><b>"); + result.append(HEADER); + result.append("</b></dt><dd>"); + boolean firstEntry = true; + for (int i = 0; i < tags.length; i++) { + if (tags[i].text().length() > 0) { + if (!firstEntry) { + result.append(", "); + } + else { + firstEntry = false; + } + result.append(tags[i].text()); + } + } + result.append("</dd>"); + result.append("</dl>"); + return result.toString(); + } + else { + return null; + } + } + } + + /** + * Enables/disables this taglet. + */ + public static void setTagletEnabled(boolean enabled) + { + VersionTaglet.enabled = enabled; + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/tnameserv/Main.java b/libjava/classpath/tools/gnu/classpath/tools/tnameserv/Main.java new file mode 100644 index 000000000..b163fd539 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/tnameserv/Main.java @@ -0,0 +1,115 @@ +/* Main.java -- Transient GIOP naming service. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.tools.tnameserv; + +import gnu.CORBA.NamingService.NamingServiceTransient; +import gnu.classpath.tools.common.ClasspathToolParser; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +/** + * The implementation of the transient naming service. The naming service + * is a kind of the network server that registers local and remote objects + * by name, and later can provide the object reference in response to the + * given name. + * + * GNU Classpath currently works with this naming service and is also + * interoperable with the Sun Microsystems naming services from releases 1.3 and + * 1.4, both transient <i>tnameserv</i> and persistent <i>orbd</i>. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public class Main +{ + private int port = -1; + private String iorf; + + private Parser initializeParser() + { + Parser parser = new ClasspathToolParser("tnameserv", true); //$NON-NLS-1$ + parser.setHeader(Messages.getString("Main.Usage")); //$NON-NLS-1$ + + parser.add(new Option("ORBInitialPort", //$NON-NLS-1$ + Messages.getString("Main.ORBInitialPort"), //$NON-NLS-1$ + Messages.getString("Main.Port")) //$NON-NLS-1$ + { + public void parsed(String portArgument) throws OptionException + { + port = Integer.parseInt(portArgument); + } + }); + + parser.add(new Option("ior", //$NON-NLS-1$ + Messages.getString("Main.IOR"), //$NON-NLS-1$ + Messages.getString("Main.IORFile")) //$NON-NLS-1$ + { + public void parsed(String fileArgument) throws OptionException + { + iorf = fileArgument; + } + }); + + return parser; + } + + private void run(String[] args) + { + Parser p = initializeParser(); + p.parse(args); + NamingServiceTransient.start(port, iorf); + } + + /** + * The naming service entry point. + */ + public static void main(String[] args) + { + Main tnameservprogram = new Main(); + try + { + tnameservprogram.run(args); + } + catch (Exception e) + { + System.err.println(Messages.getString("Main.InternalError")); //$NON-NLS-1$ + e.printStackTrace(System.err); + System.exit(1); + } + } +} diff --git a/libjava/classpath/tools/gnu/classpath/tools/tnameserv/Messages.java b/libjava/classpath/tools/gnu/classpath/tools/tnameserv/Messages.java new file mode 100644 index 000000000..505397302 --- /dev/null +++ b/libjava/classpath/tools/gnu/classpath/tools/tnameserv/Messages.java @@ -0,0 +1,67 @@ +/* Messages.java -- localization support for tnameserv + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.classpath.tools.tnameserv; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME + = "gnu.classpath.tools.tnameserv.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE + = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} |