diff options
author | Mark Wielaard <mark@gcc.gnu.org> | 2006-05-18 17:29:21 +0000 |
---|---|---|
committer | Mark Wielaard <mark@gcc.gnu.org> | 2006-05-18 17:29:21 +0000 |
commit | 4f9533c7722fa07511a94d005227961f4a4dec23 (patch) | |
tree | 9f9c470de62ee62fba1331a396450d728d2b1fad /libjava/classpath/gnu/java | |
parent | eaec4980e139903ae9b274d1abcf3a13946603a8 (diff) | |
download | gcc-4f9533c7722fa07511a94d005227961f4a4dec23.zip gcc-4f9533c7722fa07511a94d005227961f4a4dec23.tar.gz gcc-4f9533c7722fa07511a94d005227961f4a4dec23.tar.bz2 |
Imported GNU Classpath 0.90
Imported GNU Classpath 0.90
* scripts/makemake.tcl: LocaleData.java moved to gnu/java/locale.
* sources.am: Regenerated.
* gcj/javaprims.h: Regenerated.
* Makefile.in: Regenerated.
* gcj/Makefile.in: Regenerated.
* include/Makefile.in: Regenerated.
* testsuite/Makefile.in: Regenerated.
* gnu/java/lang/VMInstrumentationImpl.java: New override.
* gnu/java/net/local/LocalSocketImpl.java: Likewise.
* gnu/classpath/jdwp/VMMethod.java: Likewise.
* gnu/classpath/jdwp/VMVirtualMachine.java: Update to latest
interface.
* java/lang/Thread.java: Add UncaughtExceptionHandler.
* java/lang/reflect/Method.java: Implements GenericDeclaration and
isSynthetic(),
* java/lang/reflect/Field.java: Likewise.
* java/lang/reflect/Constructor.java
* java/lang/Class.java: Implements Type, GenericDeclaration,
getSimpleName() and getEnclosing*() methods.
* java/lang/Class.h: Add new public methods.
* java/lang/Math.java: Add signum(), ulp() and log10().
* java/lang/natMath.cc (log10): New function.
* java/security/VMSecureRandom.java: New override.
* java/util/logging/Logger.java: Updated to latest classpath
version.
* java/util/logging/LogManager.java: New override.
From-SVN: r113887
Diffstat (limited to 'libjava/classpath/gnu/java')
155 files changed, 19285 insertions, 1918 deletions
diff --git a/libjava/classpath/gnu/java/awt/font/FontDelegate.java b/libjava/classpath/gnu/java/awt/font/FontDelegate.java new file mode 100644 index 0000000..a26510e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/FontDelegate.java @@ -0,0 +1,313 @@ +/* FontDelegate.java -- Interface implemented by all font delegates. + 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.java.awt.font; + +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.text.CharacterIterator; +import java.util.Locale; + + +/** + * The interface that all font delegate objects implement, + * irrespective of where they get their information from. + * + * <p><b>Thread Safety:</b> All classes that implement the + * <code>FontDelegate</code> interface must allow calling these + * methods from multiple concurrent threads. The delegates are + * responsible for performing the necessary synchronization. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public interface FontDelegate +{ + /** + * Returns the full name of this font face in the specified + * locale, for example <i>“Univers Light”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the face name. + */ + public String getFullName(Locale locale); + + + /** + * Returns the name of the family to which this font face belongs, + * for example <i>“Univers”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the family name. + */ + public String getFamilyName(Locale locale); + + + /** + * Returns the name of this font face inside the family, for example + * <i>“Light”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the name of the face inside its family. + */ + public String getSubFamilyName(Locale locale); + + + /** + * Returns the PostScript name of this font face, for example + * <i>“Helvetica-Bold”</i>. + * + * @return the PostScript name, or <code>null</code> if the font + * does not provide a PostScript name. + */ + public String getPostScriptName(); + + + /** + * Returns the number of glyphs in this font face. + */ + public int getNumGlyphs(); + + + /** + * Returns the index of the glyph which gets displayed if the font + * cannot map a Unicode code point to a glyph. Many fonts show this + * glyph as an empty box. + */ + public int getMissingGlyphCode(); + + + /** + * Creates a GlyphVector by mapping each character in a + * CharacterIterator to the corresponding glyph. + * + * <p>The mapping takes only the font’s <code>cmap</code> + * tables into consideration. No other operations (such as glyph + * re-ordering, composition, or ligature substitution) are + * performed. This means that the resulting GlyphVector will not be + * correct for text in languages that have complex + * character-to-glyph mappings, such as Arabic, Hebrew, Hindi, or + * Thai. + * + * @param font the font object that the created GlyphVector + * will return when it gets asked for its font. This argument is + * needed because the public API works with java.awt.Font, + * not with some private delegate like OpenTypeFont. + * + * @param frc the font rendering parameters that are used for + * measuring glyphs. The exact placement of text slightly depends on + * device-specific characteristics, for instance the device + * resolution or anti-aliasing. For this reason, any measurements + * will only be accurate if the passed + * <code>FontRenderContext</code> correctly reflects the relevant + * parameters. Hence, <code>frc</code> should be obtained from the + * same <code>Graphics2D</code> that will be used for drawing, and + * any rendering hints should be set to the desired values before + * obtaining <code>frc</code>. + * + * @param ci a CharacterIterator for iterating over the + * characters to be displayed. + */ + public GlyphVector createGlyphVector(Font font, + FontRenderContext frc, + CharacterIterator ci); + + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @param advance a point whose <code>x</code> and <code>y</code> + * fields will hold the advance in each direction. It is well + * possible that both values are non-zero, for example for rotated + * text or for Urdu fonts. + */ + public void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance); + + + /** + * Returns the shape of a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, this + * parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional + * metrics, <code>false</code> for rounding the result to a pixel + * boundary. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or <code>null</code> for bitmap fonts. + */ + public GeneralPath getGlyphOutline(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics); + + + /** + * Returns a name for the specified glyph. This is useful for + * generating PostScript or PDF files that embed some glyphs of a + * font. + * + * <p><b>Names are not unique:</b> Under some rare circumstances, + * the same name can be returned for different glyphs. It is + * therefore recommended that printer drivers check whether the same + * name has already been returned for antoher glyph, and make the + * name unique by adding the string ".alt" followed by the glyph + * index.</p> + * + * <p>This situation would occur for an OpenType or TrueType font + * that has a <code>post</code> table of format 3 and provides a + * mapping from glyph IDs to Unicode sequences through a + * <code>Zapf</code> table. If the same sequence of Unicode + * codepoints leads to different glyphs (depending on contextual + * position, for example, or on typographic sophistication level), + * the same name would get synthesized for those glyphs. + * + * @param glyphIndex the glyph whose name the caller wants to + * retrieve. + */ + public String getGlyphName(int glyphIndex); + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public float getAscent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); +} diff --git a/libjava/classpath/gnu/java/awt/font/FontFactory.java b/libjava/classpath/gnu/java/awt/font/FontFactory.java new file mode 100644 index 0000000..6c1084e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/FontFactory.java @@ -0,0 +1,90 @@ +/* FontFactory.java -- Factory for font delegates. + 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.java.awt.font; + +import java.nio.ByteBuffer; + +import java.awt.FontFormatException; +import gnu.java.awt.font.opentype.OpenTypeFontFactory; + + +/** + * A factory for creating font delegate objects. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class FontFactory +{ + /** + * The constructor is private so nobody can construct an instance + */ + private FontFactory() + { + } + + + /** + * Creates FontDelegate objects for the fonts in the specified buffer. + * The following font formats are currently recognized: + * recognized font formats are: + * + * <p><ul> + * <li>OpenType (*.otf);</li> + * <li>TrueType (*.ttf);</li> + * <li>TrueType Collections (*.ttc);</li> + * <li>Apple MacOS X data-fork font (*.dfont).</li></ul> + * + * <p>Some formats may contain more than a single font, for example + * *.ttc and *.dfont files. This is the reason why this function + * returns an array. + * + * <p>The implementation reads data from the buffer only when + * needed. Therefore, it greatly increases efficiency if + * <code>buf</code> has been obtained through mapping a file into + * the virtual address space. + * + * @throws FontFormatException if the font data is not in one of the + * known formats. + */ + public static FontDelegate[] createFonts(ByteBuffer buf) + throws FontFormatException + { + return OpenTypeFontFactory.createFonts(buf); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java b/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java new file mode 100644 index 0000000..9688698 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java @@ -0,0 +1,596 @@ +/* GNUGlyphVector.java -- The GNU implementation of GlyphVector. + 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.java.awt.font; + +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphVector; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + + +/** + * The GNU implementation of the abstract GlyphVector class, which + * uses the services provided by a FontDelegate for its functionality. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class GNUGlyphVector + extends GlyphVector +{ + private FontDelegate fontDelegate; + private Font font; + private FontRenderContext renderContext; + private int[] glyphs; + private float fontSize; + private AffineTransform transform; + private boolean valid; + + + /** + * The position of each glyph. The horizontal position of the + * <code>i</code>-th glyph is at <code>pos[i * 2]</code>, its + * vertical position at <code>pos[i * 2 + 1]</code>. The total + * advance width of the entire vector is stored at + * <code>pos[numGlyphs]</code>, the total advance height at + * <code>pos[numGlyphs + 1]</code>. + */ + private float[] pos; + + + private AffineTransform[] transforms; + private int layoutFlags; + + + /** + * Constructs a new GNUGlyphVector. + * + * @param fontDelegate the FontDelegate that creates this vector. + * + * @param font the Font that this GlyphVector will return for {@link + * #getFont()}. That object is also used to determine the point + * size, which affects the affine transformation used by the font + * scaler. + * + * @param renderContext an object with parameters for font + * rendering, such as whether anti-aliasing is enabled. + * + * @param glyphs the glyphs in this vector. + */ + public GNUGlyphVector(FontDelegate fontDelegate, + Font font, + FontRenderContext renderContext, + int[] glyphs) + { + this.fontDelegate = fontDelegate; + this.font = font; + this.renderContext = renderContext; + this.glyphs = glyphs; + + fontSize = font.getSize2D(); + transform = font.getTransform(); // returns a modifiable copy + transform.concatenate(renderContext.getTransform()); + } + + + + /** + * Returns the font of the glyphs in this GlyphVector. + */ + public Font getFont() + { + return font; + } + + + /** + * Returns the FontRenderContext that is used to calculate the + * extent and position of the glyphs. + */ + public FontRenderContext getFontRenderContext() + { + return renderContext; + } + + + /** + * Moves each glyph in the vector to its default position. + */ + public void performDefaultLayout() + { + float x, y, advanceWidth, advanceHeight; + int i, p; + AffineTransform tx; + Point2D.Float advance = new Point2D.Float(); + + pos = new float[(glyphs.length + 1) * 2]; + x = y = 0.0f; + p = 0; + for (i = p = 0; i < glyphs.length; i++) + { + p += 2; + + if ((transforms == null) || (tx = transforms[i]) == null) + tx = this.transform; + else + { + tx = new AffineTransform(tx); + tx.concatenate(this.transform); + } + + fontDelegate.getAdvance(glyphs[i], fontSize, tx, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true, + advance); + pos[p] = x += advance.x; + pos[p + 1] = y += advance.y; + } + valid = true; + } + + + /** + * Determines the number of glyphs in this GlyphVector. + */ + public int getNumGlyphs() + { + return glyphs.length; + } + + + /** + * Determines the glyph number by index in this vector. + * Glyph numbers are specific to each font, so two fonts + * will likely assign different numbers to the same glyph. + * + * @param glyphIndex the index of the glyph whose glyph number is to + * be retrieved. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> + * is not in the range <code[0 .. getNumGlyphs() - 1]</code>. + */ + public int getGlyphCode(int glyphIndex) + { + /* The exception is thrown automatically if the index is out + * of the valid bounds. + */ + return glyphs[glyphIndex]; + } + + + /** + * Returns a slice of this GlyphVector. + * + * @param firstGlyphIndex the index of the first glyph in the + * returned slice. + * + * @param numEntries the size of the returned slice. + * + * @param outCodes a pre-allocated array for storing the slice, + * or <code>null</code> to cause allocation of a new array. + * + * @return a slice of this GlyphVector. If <code>outCodes</code> + * is <code>null</code>, the slice will be stored into a freshly + * allocated array; otherwise, the result will be stored into + * <code>outCodes</code>. + */ + public int[] getGlyphCodes(int firstGlyphIndex, + int numEntries, + int[] outCodes) + { + if (numEntries < 0) + throw new IllegalArgumentException(); + if (outCodes == null) + outCodes = new int[numEntries]; + System.arraycopy(glyphs, firstGlyphIndex, outCodes, 0, numEntries); + return outCodes; + } + + + public Rectangle2D getLogicalBounds() + { + float ascent, descent; + + validate(); + + return new Rectangle2D.Float(0, 0, + pos[pos.length - 2], + getAscent() - getDescent()); + } + + + public Rectangle2D getVisualBounds() + { + validate(); + + // FIXME: Not yet implemented. + return getLogicalBounds(); + } + + + /** + * Returns the shape of this GlyphVector. + */ + public Shape getOutline() + { + validate(); + return getOutline(0.0f, 0.0f); + } + + + /** + * Returns the shape of this GlyphVector, translated to the + * specified position. + * + * @param x the horizontal position for rendering this vector. + * @param y the vertical position for rendering this vector. + */ + public Shape getOutline(float x, float y) + { + validate(); + + GeneralPath outline = new GeneralPath(); + int len = glyphs.length; + for (int i = 0; i < len; i++) + { + GeneralPath p = new GeneralPath(getGlyphOutline(i)); + outline.append(p, false); + } + AffineTransform t = new AffineTransform(); + t.translate(x, y); + outline.transform(t); + return outline; + } + + + /** + * Determines the shape of the specified glyph. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs()]</code>. + */ + public Shape getGlyphOutline(int glyphIndex) + { + AffineTransform tx, glyphTx; + GeneralPath path; + + validate(); + + if ((transforms != null) + && ((glyphTx = transforms[glyphIndex]) != null)) + { + tx = new AffineTransform(transform); + tx.concatenate(glyphTx); + } + else + tx = transform; + + path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics()); + + tx = new AffineTransform(); + tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]); + path.transform(tx); + return path; + } + + + /** + * Determines the position of the specified glyph, or the + * total advance width and height of the vector. + * + * @param glyphIndex the index of the glyph in question. + * If this value equals <code>getNumGlyphs()</code>, the + * position <i>after</i> the last glyph will be returned, + * which is the total advance width and height of the vector. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs()]</code>. + */ + public Point2D getGlyphPosition(int glyphIndex) + { + validate(); + return new Point2D.Float(pos[glyphIndex * 2], + pos[glyphIndex * 2 + 1]); + } + + + /** + * Moves the specified glyph to a new position, or changes the + * advance width and height of the entire glyph vector. + * + * <p>Note that the position of an individual glyph may also + * affected by its affine transformation. + * + * @param glyphIndex the index of the moved glyph. If + * <code>glyphIndex</code> equals the total number of glyphs in this + * vector, the advance width and height of the vector is changed. + * + * @param position the new position of the glyph. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs()]</code>. + */ + public void setGlyphPosition(int glyphIndex, Point2D position) + { + validate(); + pos[glyphIndex * 2] = (float) position.getX(); + pos[glyphIndex * 2 + 1] = (float) position.getY(); + } + + + /** + * Returns the affine transformation that is applied to the + * glyph at the specified index. + * + * @param glyphIndex the index of the glyph whose transformation + * is to be retrieved. + * + * @return an affine transformation, or <code>null</code> + * for the identity transformation. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs() - 1]</code>. + */ + public AffineTransform getGlyphTransform(int glyphIndex) + { + if (transforms == null) + return null; + else + return transforms[glyphIndex]; + } + + + /** + * Applies an affine transformation to the glyph at the specified + * index. + * + * @param glyphIndex the index of the glyph to which the + * transformation is applied. + * + * @param transform the affine transformation for the glyph, or + * <code>null</code> for an identity transformation. + */ + public void setGlyphTransform(int glyphIndex, + AffineTransform transform) + { + if (transforms == null) + transforms = new AffineTransform[glyphs.length]; + transforms[glyphIndex] = transform; + + /* If the GlyphVector has only a transform for a single glyph, and + * the caller clears its transform, the FLAG_HAS_TRANSFORMS bit + * should be cleared in layoutFlags. However, this would require + * that we keep track of the number of transformed glyphs, or that + * we count them when a transform is cleared. This would + * complicate the code quite a bit. Note that the only drawback of + * wrongly setting FLAG_HAS_TRANSFORMS is that a slower code path + * might be taken for rendering the vector. Right now, we never + * really look at the flag, so it does not make any difference. + */ + if (transform != null) + layoutFlags |= FLAG_HAS_TRANSFORMS; + valid = false; + } + + + /** + * Returns flags that can be used for optimizing the rendering + * of this GlyphVector. + * + * @return a bit mask with the applicable flags set. + * + * @since 1.4 + * + * @see GlyphVector#FLAG_HAS_POSITION_ADJUSTMENTS + * @see GlyphVector#FLAG_HAS_TRANSFORMS + * @see GlyphVector#FLAG_RUN_RTL + * @see GlyphVector#FLAG_COMPLEX_GLYPHS + * @see GlyphVector#FLAG_MASK + */ + public int getLayoutFlags() + { + return layoutFlags; + } + + + /** + * Returns the positions of a range of glyphs in this vector. + * + * @param firstGlyphIndex the index of the first glyph whose + * position is retrieved. + * + * @param numGlyphs the number of glyphs whose positions + * are retrieved. + * + * @param outPositions an array for storing the results + * (the length must be at least twice <code>numGlyphs</code>), + * or <code>null</code> for freshly allocating an array. + * + * @return an array with the glyph positions. The horizontal + * position of the <code>i</code>-th glyph is at index <code>2 * + * i</code>, the vertical position at index <code>2 * i + 1</code>. + * + * @throws IllegalArgumentException if <code>numGlyphs</code> + * is less than zero. + * + * @throws IndexOutOfBoundsException if either + * <code>firstGlyphIndex</code> or <code>(firstGlyphIndex + + * numGlyphs)</code> is not in the range <code>[0 .. getNumGlyphs() - + * 1]</code>. + */ + public float[] getGlyphPositions(int firstGlyphIndex, + int numGlyphs, + float[] outPositions) + { + if (numGlyphs < 0) + throw new IllegalArgumentException(); + + validate(); + if (outPositions == null) + outPositions = new float[numGlyphs * 2]; + + System.arraycopy(/*src */ pos, /* srcStart */ firstGlyphIndex * 2, + /* dest */ outPositions, /* destStart */ 0, + /* length */ numGlyphs * 2); + return outPositions; + } + + + private float getAscent() + { + return fontDelegate.getAscent(fontSize, transform, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true); + } + + + private float getDescent() + { + return fontDelegate.getDescent(fontSize, transform, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true); + } + + + public Shape getGlyphLogicalBounds(int glyphIndex) + { + float x, y, ascent; + + validate(); + ascent = getAscent(); + x = pos[glyphIndex * 2]; + y = pos[glyphIndex * 2 + 1]; + + return new Rectangle2D.Float(x, y - ascent, + pos[(glyphIndex + 1) * 2] - x, + ascent - getDescent()); + } + + + public Shape getGlyphVisualBounds(int glyphIndex) + { + return getGlyphOutline(glyphIndex).getBounds2D(); + } + + + /** + * Determines the metrics of the glyph at the specified index. + * + * @param glyphIndex the index of the glyph whose metrics is to be + * retrieved. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs() - 1]</code>. + */ + public GlyphMetrics getGlyphMetrics(int glyphIndex) + { + // FIXME: Not yet implemented. + throw new UnsupportedOperationException(); + } + + + /** + * Determines the justification information for the glyph at the + * specified index. + * + * @param glyphIndex the index of the glyph whose justification + * information is to be retrieved. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs() - 1]</code>. + */ + public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) + { + // FIXME: Not yet implemented. + throw new UnsupportedOperationException(); + } + + + /** + * Determines whether another GlyphVector is for the same font and + * rendering context, uses the same glyphs and positions them to the + * same location. + * + * @param other the GlyphVector to compare with. + * + * @return <code>true</code> if the two vectors are equal, + * <code>false</code> otherwise. + */ + public boolean equals(GlyphVector other) + { + GNUGlyphVector o; + if (!(other instanceof GNUGlyphVector)) + return false; + + o = (GNUGlyphVector) other; + if ((this.font != o.font) + || (this.fontDelegate != o.fontDelegate) + || (this.renderContext != o.renderContext) + || (this.glyphs.length != o.glyphs.length)) + return false; + + for (int i = 0; i < glyphs.length; i++) + if (this.glyphs[i] != o.glyphs[i]) + return false; + + validate(); + o.validate(); + for (int i = 0; i < pos.length; i++) + if (this.pos[i] != o.pos[i]) + return false; + + return true; + } + + private void validate() + { + if (!valid) + performDefaultLayout(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java b/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java new file mode 100644 index 0000000..6ada3b1 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java @@ -0,0 +1,1027 @@ +/* CharGlyphMap.java -- Manages the 'cmap' table of TrueType fonts + 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.java.awt.font.opentype; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.ShortBuffer; +import java.nio.IntBuffer; + + +/** + * A mapping from Unicode codepoints to glyphs. This mapping + * does not perform any re-ordering or decomposition, so it + * is not everything that is needed to support Unicode. + * + * <p>This class manages the <code>cmap</code> table of + * OpenType and TrueType fonts. + * + * @see <a href="http://partners.adobe.com/asn/tech/type/opentype/cmap.jsp"> + * the <code>cmap</code> part of Adobe’ OpenType Specification</a> + * + * @see <a href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6cmap.html"> + * the <code>cmap</code> section of Apple’s TrueType Reference + * Manual</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +abstract class CharGlyphMap +{ + private static final int PLATFORM_UNICODE = 0; + private static final int PLATFORM_MACINTOSH = 1; + private static final int PLATFORM_MICROSOFT = 3; + + + /** + * Determines the glyph index for a given Unicode codepoint. Users + * should be aware that the character-to-glyph mapping not not + * everything that is needed for full Unicode support. For example, + * the <code>cmap</code> table is not able to synthesize accented + * glyphs from the canonical decomposition sequence, even if the + * font would contain a glyph for the composed form. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. Surrogates + * (U+D800 to U+DFFF) cannot be passed, they must be mapped to + * UCS-4 first. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public abstract int getGlyph(int ucs4); + + + /** + * Reads a CharGlyphMap from an OpenType or TrueType <code>cmap</code> + * table. The current implementation works as follows: + * + * <p><ol><li>If the font has a type 4 cmap for the Unicode platform + * (encoding 0, 1, 2, 3 or 4), or a type 4 cmap for the Microsoft + * platform (encodings 1 or 10), that table is used to map Unicode + * codepoints to glyphs. Most recent fonts, both for Macintosh and + * Windows, should provide such a table.</li> + * + * <li>Otherwise, if the font has any type 0 cmap for the Macintosh + * platform, a Unicode-to-glyph mapping is synthesized from certain + * type 0 cmaps. The current implementation collects mappings from + * Roman, Icelandic, Turkish, Croatian, Romanian, Eastern European, + * Cyrillic, Greek, Hebrew, Arabic and Farsi cmaps.</li>.</ol> + * + * @param buf a buffer whose position is right at the start + * of the entire <code>cmap</code> table, and whose limit + * is at its end. + * + * @return a concrete subclass of <code>CharGlyphMap</code> + * that performs the mapping. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/opentype/cmap.jsp" + * >the <code>cmap</code> part of Adobe’ OpenType Specification</a> + * + * @see <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM06/Chap6cmap.html" + * >the <code>cmap</code> section of Apple’s TrueType Reference + * Manual</a> + */ + public static CharGlyphMap forTable(ByteBuffer buf) + { + boolean hasType0 = false; + int start4 = -1, platform4 = 0, encoding4 = 0; + int start12 = -1, platform12 = 0, encoding12 = 0; + int version; + int numTables; + int tableStart = buf.position(); + int limit = buf.limit(); + int format, platform, language, encoding, length, offset; + + version = buf.getChar(); + if (version != 0) + return null; + + numTables = buf.getChar(); + for (int i = 0; i < numTables; i++) + { + buf.limit(limit).position(tableStart + 4 + i * 8); + platform = buf.getChar(); + encoding = buf.getChar(); + offset = tableStart + buf.getInt(); + + buf.position(offset); + format = buf.getChar(); + + switch (format) + { + case 0: + hasType0 = true; + break; + + case 4: + length = buf.getChar(); + language = buf.getChar(); + if ((start4 == -1) + && Type4.isSupported(platform, language, encoding)) + { + start4 = offset; + platform4 = platform; + encoding4 = encoding; + } + break; + + case 12: + if ((start12 == -1) && Type12.isSupported(platform, encoding)) + { + start12 = offset; + platform12 = platform; + encoding12 = encoding; + } + break; + } + } + + + if (start12 >= 0) + { + try + { + buf.limit(limit).position(start12); + return new Type12(buf, platform12, encoding12); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + if (start4 >= 0) + { + try + { + buf.limit(limit).position(start4); + return Type4.readTable(buf, platform4, encoding4); + } + catch (Exception ex) + { + } + } + + if (hasType0) + { + try + { + buf.limit(limit).position(tableStart); + return new Type0(buf); + } + catch (Exception ex) + { + } + } + + return new Dummy(); + } + + + /** + * A dummy mapping that maps anything to the undefined glyph. + * Used if no other cmap is understood in a font. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Dummy + extends CharGlyphMap + { + public int getGlyph(int ucs4) + { + return 0; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 0 tables. These tables have serious limitations: Only the first + * 256 glyphs can be addressed, and the source of the mapping is not + * Unicode, but an encoding used on the Macintosh. + * + * <p>However, some fonts have only a Type 0 cmap. In this case, we + * process all the Type 0 tables we understand, and establish + * a reversed glyph-to-Unicode mapping. When a glyph is requested + * for a given Unicode character, we perform a linear search on the + * reversed table to find the glyph which maps to the requested + * character. While not blazingly fast, this gives a reasonable + * fallback for old fonts. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type0 + extends CharGlyphMap + { + /** + * An array whose <code>i</code>-th element indicates the + * Unicode code point of glyph <code>i</code> in the font. + */ + private char[] glyphToUCS2 = new char[256]; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Arabic encoding. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ARABIC.TXT" + * >the Unicode mapping table for the MacOS Arabic encoding</a> + */ + private static final String UPPER_ARABIC + = "\u007e\u0000\u00c4\u00a0\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u06ba\u00ab\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u2026\u00ee\u00ef\u00f1\u00f3\u00bb\u00f4\u00f6\u00f7" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u066a" + + "\u0026\u0027\u0028\u0029\u002a\u002b\u060c\u002d\u002e\u002f" + + "\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669" + + "\u003a\u061b\u003c\u003d\u003e\u061f\u274a\u0621\u0622\u0623" + + "\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d" + + "\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637" + + "\u0638\u0639\u063a\u005b\\\u005d\u005e\u005f\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u067e\u0679\u0686" + + "\u06d5\u06a4\u06af\u0688\u0691\u007b\u007c\u007d\u0698\u06d2"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS East European Roman encoding. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CENTEURO.TXT" + * >the Unicode mapping table for the MacOS Central European + * encoding</a> + */ + private static final String UPPER_EAST_EUROPEAN_ROMAN + = "\u007e\u0000\u00c4\u0100\u0101\u00c9\u0104\u00d6\u00dc\u00e1" + + "\u0105\u010c\u00e4\u010d\u0106\u0107\u00e9\u0179\u017a\u010e" + + "\u00ed\u010f\u0112\u0113\u0116\u00f3\u0117\u00f4\u00f6\u00f5" + + "\u00fa\u011a\u011b\u00fc\u2020\u00b0\u0118\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u0119\u00a8\u2260\u0123\u012e" + + "\u012f\u012a\u2264\u2265\u012b\u0136\u2202\u2211\u0142\u013b" + + "\u013c\u013d\u013e\u0139\u013a\u0145\u0146\u0143\u00ac\u221a" + + "\u0144\u0147\u2206\u00ab\u00bb\u2026\u00a0\u0148\u0150\u00d5" + + "\u0151\u014c\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u014d\u0154\u0155\u0158\u2039\u203a\u0159\u0156\u0157\u0160" + + "\u201a\u201e\u0161\u015a\u015b\u00c1\u0164\u0165\u00cd\u017d" + + "\u017e\u016a\u00d3\u00d4\u016b\u016e\u00da\u016f\u0170\u0171" + + "\u0172\u0173\u00dd\u00fd\u0137\u017b\u0141\u017c\u0122\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding for the Croatian language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CROATIAN.TXT" + * >the Unicode mapping table for the MacOS Croatian encoding</a> + */ + private static final String UPPER_CROATIAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u0160\u2122\u00b4\u00a8\u2260\u017d\u00d8" + + "\u221e\u00b1\u2264\u2265\u2206\u00b5\u2202\u2211\u220f\u0161" + + "\u222b\u00aa\u00ba\u03a9\u017e\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u0106\u00ab\u010c\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u0110\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\uf8ff\u00a9\u2044\u20ac\u2039\u203a\u00c6\u00bb\u2013\u00b7" + + "\u201a\u201e\u2030\u00c2\u0107\u00c1\u010d\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\u0111\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u03c0\u00cb\u02da\u00b8\u00ca\u00e6\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Cyrillic encoding. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CYRILLIC.TXT" + * >the Unicode mapping table for the MacOS Cyrillic encoding</a> + */ + private static final String UPPER_CYRILLIC + = "\u007e\u0000\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417" + + "\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421" + + "\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b" + + "\u042c\u042d\u042e\u042f\u2020\u00b0\u0490\u00a3\u00a7\u2022" + + "\u00b6\u0406\u00ae\u00a9\u2122\u0402\u0452\u2260\u0403\u0453" + + "\u221e\u00b1\u2264\u2265\u0456\u00b5\u0491\u0408\u0404\u0454" + + "\u0407\u0457\u0409\u0459\u040a\u045a\u0458\u0405\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u040b\u045b\u040c" + + "\u045c\u0455\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u201e" + + "\u040e\u045e\u040f\u045f\u2116\u0401\u0451\u044f\u0430\u0431" + + "\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b" + + "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445" + + "\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u20ac"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Arabic encoding with the Farsi language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/FARSI.TXT" + * >the Unicode mapping table for the MacOS Farsi encoding</a> + */ + private static final String UPPER_FARSI + = "\u007e\u0000\u00c4\u00a0\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u06ba\u00ab\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u2026\u00ee\u00ef\u00f1\u00f3\u00bb\u00f4\u00f6\u00f7" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u066a" + + "\u0026\u0027\u0028\u0029\u002a\u002b\u060c\u002d\u002e\u002f" + + "\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9" + + "\u003a\u061b\u003c\u003d\u003e\u061f\u274a\u0621\u0622\u0623" + + "\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d" + + "\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637" + + "\u0638\u0639\u063a\u005b\\\u005d\u005e\u005f\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u067e\u0679\u0686" + + "\u06d5\u06a4\u06af\u0688\u0691\u007b\u007c\u007d\u0698\u06d2"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Greek encoding. + * + * @see <a + * href="http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/GREEK.TXT" + * >the Unicode mapping table for the MacOS Greek encoding</a> + */ + private static final String UPPER_GREEK + = "\u007e\u0000\u00c4\u00b9\u00b2\u00c9\u00b3\u00d6\u00dc\u0385" + + "\u00e0\u00e2\u00e4\u0384\u00a8\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00a3\u2122\u00ee\u00ef\u2022\u00bd\u2030\u00f4\u00f6\u00a6" + + "\u20ac\u00f9\u00fb\u00fc\u2020\u0393\u0394\u0398\u039b\u039e" + + "\u03a0\u00df\u00ae\u00a9\u03a3\u03aa\u00a7\u2260\u00b0\u00b7" + + "\u0391\u00b1\u2264\u2265\u00a5\u0392\u0395\u0396\u0397\u0399" + + "\u039a\u039c\u03a6\u03ab\u03a8\u03a9\u03ac\u039d\u00ac\u039f" + + "\u03a1\u2248\u03a4\u00ab\u00bb\u2026\u00a0\u03a5\u03a7\u0386" + + "\u0388\u0153\u2013\u2015\u201c\u201d\u2018\u2019\u00f7\u0389" + + "\u038a\u038c\u038e\u03ad\u03ae\u03af\u03cc\u038f\u03cd\u03b1" + + "\u03b2\u03c8\u03b4\u03b5\u03c6\u03b3\u03b7\u03b9\u03be\u03ba" + + "\u03bb\u03bc\u03bd\u03bf\u03c0\u03ce\u03c1\u03c3\u03c4\u03b8" + + "\u03c9\u03c2\u03c7\u03c5\u03b6\u03ca\u03cb\u0390\u03b0\u00ad"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Hebrew encoding. + * + * <p>The codepoint 0x81 (HEBREW LIGATURE YIDDISH YOD YOD PATAH) + * has no composed Unicode equivalent, but is expressed as the + * sequence U+05F2 U+05B7 in Unicode. A similar situation exists + * with the codepoint 0xC0 (HEBREW LIGATURE LAMED HOLAM), which + * MacOS converts to U+F86A U+05DC U+05B9. To correctly deal + * with these sequences, we probably should synthesize a ligature + * table if a Hebrew font only provides a Type 0 CMAP. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/HEBREW.TXT" + * >the Unicode mapping table for the MacOS Hebrew encoding</a> + */ + private static final String UPPER_HEBREW + = "\u007e\u0000\u00c4\u0000\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u0025" + + "\u20aa\u0027\u0029\u0028\u002a\u002b\u002c\u002d\u002e\u002f" + + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039" + + "\u003a\u003b\u003c\u003d\u003e\u003f\u0000\u201e\uf89b\uf89c" + + "\uf89d\uf89e\u05bc\ufb4b\ufb35\u2026\u00a0\u05b8\u05b7\u05b5" + + "\u05b6\u05b4\u2013\u2014\u201c\u201d\u2018\u2019\ufb2a\ufb2b" + + "\u05bf\u05b0\u05b2\u05b1\u05bb\u05b9\u0000\u05b3\u05d0\u05d1" + + "\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db" + + "\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5" + + "\u05e6\u05e7\u05e8\u05e9\u05ea\u007d\u005d\u007b\u005b\u007c"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding with the Icelandic language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ICELAND.TXT" + * >the Unicode mapping table for the MacOS Icelandic encoding</a> + */ + private static final String UPPER_ICELANDIC + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u00dd\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u00d0\u00f0\u00de\u00fe\u00fd\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding for most languages. Exceptions include + * Croatian, Icelandic, Romanian, and Turkish. + * + * @see <a + * href="http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMAN.TXT" + * >the Unicode mapping table for the MacOS Roman encoding</a> + */ + private static final String UPPER_ROMAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u2039\u203a\ufb01\ufb02\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding with the Romanian language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMANIAN.TXT" + * >the Unicode mapping table for the MacOS Romanian encoding</a> + */ + private static final String UPPER_ROMANIAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u0102\u0218" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u0103\u0219\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u2039\u203a\u021a\u021b\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding with the Turkish language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/TURKISH.TXT" + * >the Unicode mapping table for the MacOS Turkish encoding</a> + */ + private static final String UPPER_TURKISH + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u011e\u011f\u0130\u0131\u015e\u015f\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\uf8a0" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * Constructs a CharGlyphMap.Type0 from all type 0 cmaps provided + * by the font. The implementation is able to fuse multiple type + * 0 cmaps, such as the MacRoman, Turkish, Icelandic and Croatian + * encoding, into a single map from Unicode characters to glyph + * indices. + * + * @param buf a ByteBuffer whose position is right at the + * beginning of the entire cmap table of the font (<i>not</i> + * at some subtable). + */ + public Type0(ByteBuffer buf) + { + int numTables; + int tableStart = buf.position(); + int limit = buf.limit(); + + /* The CMAP version must be 0. */ + if (buf.getChar() != 0) + throw new IllegalStateException(); + + numTables = buf.getChar(); + for (int i = 0; i < numTables; i++) + { + buf.limit(limit).position(tableStart + 4 + i * 8); + int platform = buf.getChar(); + int encoding = buf.getChar(); + int offset = tableStart + buf.getInt(); + + buf.position(offset); + int format = buf.getChar(); + int length = buf.getChar(); + buf.limit(offset + length); + int language = buf.getChar(); + + if (format == 0) + readSingleTable(buf, platform, language, encoding); + } + } + + + /** + * Processes a CMAP Type 0 table whose platform, encoding and + * language are already known. + * + * @param buf the buffer to read the table from, positioned + * right after the language tag. + */ + private void readSingleTable(ByteBuffer buf, + int platform, int language, + int encoding) + { + String upper = getUpper129(platform, encoding, language); + if (upper == null) + return; + + /* Skip the MacOS codepoints [0 .. 31] because they do not + * correspond to any Unicode codepoint. + */ + buf.position(buf.position() + 32); + + /* Irrespective of script and language, the MacOS codepoints + * [32 .. 126] correspond to the same Unicode codepoint. + */ + for (int i = 32; i < 126; i++) + glyphToUCS2[buf.get() & 0xff] = (char) i; + + for (int i = 127; i < 256; i++) + glyphToUCS2[buf.get() & 0xff] = upper.charAt(i - 127); + + /* Glyph 0 is always the undefined character, which has + * no codepoint in Unicode. + */ + glyphToUCS2[0] = 0; + } + + + /** + * Determines the glyph index for a given Unicode codepoint. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + /* This linear search is not exactly super fast. However, + * only really ancient fonts have only a type 0 cmap, + * so it should not hurt in very many cases. If it shows + * to be a performance problem, one could do a binary search + * on a 256-entry table sorted by Unicode codepoint. The + * matching index of that table could then be used to look + * up the glyph ID at that position. + */ + for (int i = 0; i < 256; i++) + if (glyphToUCS2[i] == ucs4) + return i; + return 0; + } + + + /** + * Returns a String whose <code>charAt(i)</code> is the Unicode + * character that corresponds to the codepoint <code>i + + * 127</code> in the encoding specified by the platform, script + * and language tag of a Type 0 CMAP. + * + * @param language the language tag in the cmap subtable. For the + * Macintosh platform, this is 0 to indicate language-neutral + * encoding, or the MacOS language code <i>plus one.</i> The + * Apple documentation does not mention that one needs to be + * added, but the Adobe OpenType specification does. + * + * @return a String for mapping the top 129 characters to + * UCS-2. If <code>platform</code> is not <code>1</code> + * (indicating Macintosh), or if the combination of + * <code>script</code> and <code>language</code> is not + * recognized, <code>null</code> will be returned. + */ + private static String getUpper129(int platform, int script, int language) + { + if (platform != PLATFORM_MACINTOSH) + return null; + + switch (script) + { + case 0: /* smRoman */ + if (language == /* langIcelandic+1 */ 16) + return UPPER_ICELANDIC; + else if (language == /* langTurkish+1 */ 18) + return UPPER_TURKISH; + else if (language == /* langCroatian+1 */ 19) + return UPPER_CROATIAN; + else if (language == /* langRomanian+1 */ 38) + return UPPER_ROMANIAN; + else if (language == /* language-neutral */ 0) + return UPPER_ROMAN; + else + return null; + + case 4: /* smArabic */ + if (language == /* langFarsi+1 */ 32) + return UPPER_FARSI; + else + return UPPER_ARABIC; + + case 5: /* smHebrew */ + return UPPER_HEBREW; + + case 6: /* smGreek */ + return UPPER_GREEK; + + case 7: /* smCyrillic */ + return UPPER_CYRILLIC; + + case 29: /* smSlavic == smEastEurRoman */ + return UPPER_EAST_EUROPEAN_ROMAN; + } + + return null; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 4 tables. These tables are able to map two-byte encoded text + * to glyph IDs, such as Unicode Basic Multilingual Plane which + * contains U+0000 .. U+FFFE without surrogates. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type4 + extends CharGlyphMap + { + /** + * Determines whether this implementation supports a combination + * of platform, language and encoding is supported for a type 4 + * <code>cmap</code> table. + * + * <p>Currently, we support the following combinations: + * + * <ul><li>the Unicode platform in encodings 0, 1, 2, 3 and + * 4;</li> + * + * <li>the Microsoft platform in encodings 1 (Basic Multilingual + * Plane) and 10 (full Unicode).</li></ul> + * + * <p>Most recent Macintosh fonts provide a type 4 + * <code>cmap</code> for Unicode. Microsoft recommends providing a + * type 4 <code>cmap</code> for encoding 1 of the Microsoft + * platform. The implementation of GNU Classpath supports both + * variants. + * + * <p>Not supported are ShiftJIS, Big5, Wansung, Johab, and other + * non-Unicode encodings. Text can easily be converted to Unicode + * using the java.nio.charset package. + */ + static boolean isSupported(int platform, int language, int encoding) + { + switch (platform) + { + case PLATFORM_UNICODE: + return (encoding >= 0) && (encoding <= 4); + + case PLATFORM_MICROSOFT: + return (encoding == /* Basic Multilingual Plane */ 1) + || (encoding == /* Full Unicode */ 10); + } + + return false; + } + + + /** + * Processes a CMAP Type 4 table whose platform, encoding and + * language are already known. We understand the Unicode platform + * with encodings 0, 1, 2, 3 and 4, and the Microsoft platform + * with encodings 1 (Unicode BMP) and 10 (UCS-4). + * + * @param buf the buffer to read the table from, positioned at + * its beginning. + * + * @return a Type4 table, or <code>null</code> if the combination + * of platform and encoding is not understood. + */ + static Type4 readTable(ByteBuffer buf, + int platform, int encoding) + { + int tableStart = buf.position(); + char format = buf.getChar(); + int length = buf.getChar(); + int language = buf.getChar(); + + if ((format != 4) || !isSupported(platform, language, encoding)) + throw new IllegalArgumentException(); + + buf.limit(tableStart + length); + + int segCountX2 = buf.getChar(); + int segCount = segCountX2 / 2; + int searchRange = buf.getChar(); + int entrySelector = buf.getChar(); + int rangeShift = buf.getChar(); + + CharBuffer endCode, startCode, idRangeOffset_glyphID; + ShortBuffer idDelta; + + int pos = buf.position(); + endCode = buf.asCharBuffer(); + pos += segCountX2 + /* reservedPad */ 2; + + buf.position(pos); + startCode = buf.asCharBuffer(); + pos += segCountX2; + + buf.position(pos); + idDelta = buf.asShortBuffer(); + pos += segCountX2; + + buf.position(pos); + idRangeOffset_glyphID = buf.asCharBuffer(); + + endCode.limit(segCount); + startCode.limit(segCount); + idDelta.limit(segCount); + idRangeOffset_glyphID.limit((buf.limit() - pos) / 2); + + return new Type4(segCount, + endCode, startCode, idDelta, + idRangeOffset_glyphID); + } + + + private CharBuffer lastChar; + private CharBuffer firstChar; + private ShortBuffer idDelta; + private CharBuffer rangeID; + private int numSegments; + + private Type4(int numSegments, + CharBuffer lastChar, CharBuffer firstChar, + ShortBuffer idDelta, CharBuffer rangeID) + { + this.numSegments = numSegments; + this.lastChar = lastChar; + this.firstChar = firstChar; + this.idDelta = idDelta; + this.rangeID = rangeID; + } + + + /** + * Determines the glyph index for a given Unicode codepoint. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + char c, segStart; + int segment, idRangeOffset; + + if (ucs4 > 0xffff) + return 0; + + c = (char) ucs4; + segment = find(c); + segStart = firstChar.get(segment); + if ((c < segStart) || (c > lastChar.get(segment))) + return 0; + + /* + * System.out.println("seg " + segment + * + ", range=" + (int) rangeID[segment] + * + ", delta=" + delta[segment]); + */ + + idRangeOffset = rangeID.get(segment); + if (idRangeOffset == 0) + return (int) (char) (((int) c) + idDelta.get(segment)); + int result = rangeID.get((idRangeOffset >> 1) + + (c - segStart) + segment); + if (result == 0) + return 0; + return (int) (char) (result + idDelta.get(segment)); + } + + + private int find(char c) + { + int min, max, mid; + + min = 0; + max = numSegments - 1; + mid = max >> 1; + + while (min < max) + { + // System.out.println("(" + min + "," + max + ") " + mid); + char val = lastChar.get(mid); + if (val == c) + break; + else if (val < c) + min = mid + 1; + else if (val > c) + max = mid; + mid = (min + max) >> 1; + } + + return mid; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 12 tables. These tables are able to map four-byte encoded text + * to glyph IDs, such as Unicode UCS-4. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type12 + extends CharGlyphMap + { + int numGroups; + IntBuffer data; + + + /** + * Determines whether this implementation supports a combination + * of platform and encoding for a type 12 <code>cmap</code> table. + * + * <p>Currently, we support the following combinations: + * + * <ul><li>the Unicode platform in encodings 0, 1, 2, 3 and + * 4;</li> + * + * <li>the Microsoft platform in encodings 1 (Basic Multilingual + * Plane) and 10 (full Unicode).</li></ul> + */ + static boolean isSupported(int platform, int encoding) + { + switch (platform) + { + case PLATFORM_UNICODE: + return (encoding >= 0) && (encoding <= 4); + + case PLATFORM_MICROSOFT: + return (encoding == /* Basic Multilingual Plane */ 1) + || (encoding == /* Full Unicode */ 10); + } + + return false; + } + + + /** + * Constructs a <code>cmap</code> type 12 table whose platform and + * encoding are already known. We understand the Unicode platform + * with encodings 0, 1, 2, 3 and 4, and the Microsoft platform + * with encodings 1 (Unicode BMP) and 10 (UCS-4). + * + * @param buf the buffer to read the table from, positioned at + * its beginning. + */ + Type12(ByteBuffer buf, int platform, int encoding) + { + int tableStart = buf.position(); + int format = buf.getChar(); + if ((format != 12) || !isSupported(platform, encoding)) + throw new IllegalStateException(); + + buf.getChar(); // skip reserved field + buf.limit(tableStart + buf.getInt()); + int language = buf.getInt(); + numGroups = buf.getInt(); + data = buf.asIntBuffer(); + } + + + /** + * Determines the glyph index for a given Unicode codepoint. Users + * should be aware that the character-to-glyph mapping not not + * everything that is needed for full Unicode support. For example, + * the <code>cmap</code> table is not able to synthesize accented + * glyphs from the canonical decomposition sequence, even if the + * font would contain a glyph for the composed form. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. Surrogates + * (U+D800 to U+DFFF) cannot be passed, they must be mapped to + * UCS-4 first. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + int min, max, mid, startCharCode, endCharCode; + + min = 0; + max = numGroups - 1; + mid = max >> 1; + do + { + startCharCode = data.get(3 * mid); + endCharCode = data.get(3 * mid + 1); + + + /* + System.out.println("group " + mid + " (U+" + + Integer.toHexString(startCharCode) + + " .. U+" + Integer.toHexString(endCharCode) + + "): glyph " + (int) data.get(mid*3+2)); + */ + + if ((startCharCode <= ucs4) && (ucs4 <= endCharCode)) + return ucs4 + - startCharCode + + /* startGlyphID */ data.get(mid * 3 + 2); + + if (endCharCode < ucs4) + min = mid + 1; + else + max = mid; + mid = (min + max) >> 1; + } + while (min < max); + + startCharCode = data.get(3 * mid); + endCharCode = data.get(3 * mid + 1); + if ((startCharCode <= ucs4) && (ucs4 <= endCharCode)) + return ucs4 + - startCharCode + + /* startGlyphID */ data.get(mid * 3 + 2); + + return 0; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java b/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java new file mode 100644 index 0000000..ea4b8e2 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java @@ -0,0 +1,1133 @@ +/* GlyphNamer.java -- Provides glyph names. + 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.java.awt.font.opentype; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.CharBuffer; + + +/** + * Provides names for glyphs, which is useful when embedding fonts + * in PostScript or PDF documents. + * + * <p>If the font has a <code>Zapf</code> table, it is used to map + * glyph IDs back to a sequence of Unicode codepoints, which then + * makes it possible to look up or synthesize a PostScript glyph name + * according to Adobe’s conventions. This allows to extract the + * original text from the generated PDF or PostScript file, which is + * important for indexing, searching and extracting. + * + * <p>Otherwise, glyph names are taken from the <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM06/Chap6post.html" + * ><code>post</code> table</a>. All known formats (1, 2, 2.5, 3 and + * 4) are supported. + * + * <p><b>Open Tasks:</b> The code could be cleaner structured by + * having separate sub-classes for each variant of the POST table. + * Also, the implementation should not read in all glyph names if a + * font provides them in a POST table of type 2. It would be + * sufficient to just read in the offsets and delay the String + * fetching and conversion to the time when the glyph name is actually + * requested. + * + * <p><b>Lack of Thread Safety:</b> The GlyphNamer class is + * intentionally <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the GlyphNamer. + * It would thus be wasteful to acquire additional locks for the + * GlyphNamer. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphNamer +{ + /** + * The 'post' table of the font. + */ + private ByteBuffer postTable; + + + /** + * The 'Zapf' table of the font, or null if the font has no + * such table. + */ + private ByteBuffer zapfTable; + + + /** + * The offset of each glyph relative to the Zapf table, + * or null if the font does not have a Zapf table. + */ + private IntBuffer zapfOffsets; + + + /** + * The offset from the start of the Zapf table to the start + * of the extra info area. + */ + private int zapfExtraInfo; + + + /** + * The format of the post table, a Fixed 16.16 number. + */ + private int postFormat; + + + /** + * An array of glyph names. Used for table formats 1, 2, 2.5. + */ + private String[] glyphNames; + + + /** + * An array from glyph to character codes. Similar to the + * workings of a Zapf table, but maps to CID instead of + * Unicode. Used for table format 4. + */ + private CharBuffer glyphCharacterCodes; + + + /** + * The PostScript names of the 258 standard Macintosh glyphs. Note + * that some of these glyphs are not in the Adobe Standard Glyph + * List for New Fonts, namely .notdef, .null, nonmarkingreturn, + * nonbreakingspace, apple, onesuperior, twosuperior, and + * threesuperior. + */ + private static final String[] STANDARD_POSTSCRIPT_GLYPH_NAMES = + { + ".notdef", // glyph #0 + ".null", // glyph #1 + "nonmarkingreturn", // glyph #2 + "space", // glyph #3 + "exclam", // glyph #4 + "quotedbl", // glyph #5 + "numbersign", // glyph #6 + "dollar", // glyph #7 + "percent", // glyph #8 + "ampersand", // glyph #9 + "quotesingle", // glyph #10 + "parenleft", // glyph #11 + "parenright", // glyph #12 + "asterisk", // glyph #13 + "plus", // glyph #14 + "comma", // glyph #15 + "hyphen", // glyph #16 + "period", // glyph #17 + "slash", // glyph #18 + "zero", // glyph #19 + "one", // glyph #20 + "two", // glyph #21 + "three", // glyph #22 + "four", // glyph #23 + "five", // glyph #24 + "six", // glyph #25 + "seven", // glyph #26 + "eight", // glyph #27 + "nine", // glyph #28 + "colon", // glyph #29 + "semicolon", // glyph #30 + "less", // glyph #31 + "equal", // glyph #32 + "greater", // glyph #33 + "question", // glyph #34 + "at", // glyph #35 + "A", // glyph #36 + "B", // glyph #37 + "C", // glyph #38 + "D", // glyph #39 + "E", // glyph #40 + "F", // glyph #41 + "G", // glyph #42 + "H", // glyph #43 + "I", // glyph #44 + "J", // glyph #45 + "K", // glyph #46 + "L", // glyph #47 + "M", // glyph #48 + "N", // glyph #49 + "O", // glyph #50 + "P", // glyph #51 + "Q", // glyph #52 + "R", // glyph #53 + "S", // glyph #54 + "T", // glyph #55 + "U", // glyph #56 + "V", // glyph #57 + "W", // glyph #58 + "X", // glyph #59 + "Y", // glyph #60 + "Z", // glyph #61 + "bracketleft", // glyph #62 + "backslash", // glyph #63 + "bracketright", // glyph #64 + "asciicircum", // glyph #65 + "underscore", // glyph #66 + "grave", // glyph #67 + "a", // glyph #68 + "b", // glyph #69 + "c", // glyph #70 + "d", // glyph #71 + "e", // glyph #72 + "f", // glyph #73 + "g", // glyph #74 + "h", // glyph #75 + "i", // glyph #76 + "j", // glyph #77 + "k", // glyph #78 + "l", // glyph #79 + "m", // glyph #80 + "n", // glyph #81 + "o", // glyph #82 + "p", // glyph #83 + "q", // glyph #84 + "r", // glyph #85 + "s", // glyph #86 + "t", // glyph #87 + "u", // glyph #88 + "v", // glyph #89 + "w", // glyph #90 + "x", // glyph #91 + "y", // glyph #92 + "z", // glyph #93 + "braceleft", // glyph #94 + "bar", // glyph #95 + "braceright", // glyph #96 + "asciitilde", // glyph #97 + "Adieresis", // glyph #98 + "Aring", // glyph #99 + "Ccedilla", // glyph #100 + "Eacute", // glyph #101 + "Ntilde", // glyph #102 + "Odieresis", // glyph #103 + "Udieresis", // glyph #104 + "aacute", // glyph #105 + "agrave", // glyph #106 + "acircumflex", // glyph #107 + "adieresis", // glyph #108 + "atilde", // glyph #109 + "aring", // glyph #110 + "ccedilla", // glyph #111 + "eacute", // glyph #112 + "egrave", // glyph #113 + "ecircumflex", // glyph #114 + "edieresis", // glyph #115 + "iacute", // glyph #116 + "igrave", // glyph #117 + "icircumflex", // glyph #118 + "idieresis", // glyph #119 + "ntilde", // glyph #120 + "oacute", // glyph #121 + "ograve", // glyph #122 + "ocircumflex", // glyph #123 + "odieresis", // glyph #124 + "otilde", // glyph #125 + "uacute", // glyph #126 + "ugrave", // glyph #127 + "ucircumflex", // glyph #128 + "udieresis", // glyph #129 + "dagger", // glyph #130 + "degree", // glyph #131 + "cent", // glyph #132 + "sterling", // glyph #133 + "section", // glyph #134 + "bullet", // glyph #135 + "paragraph", // glyph #136 + "germandbls", // glyph #137 + "registered", // glyph #138 + "copyright", // glyph #139 + "trademark", // glyph #140 + "acute", // glyph #141 + "dieresis", // glyph #142 + "notequal", // glyph #143 + "AE", // glyph #144 + "Oslash", // glyph #145 + "infinity", // glyph #146 + "plusminus", // glyph #147 + "lessequal", // glyph #148 + "greaterequal", // glyph #149 + "yen", // glyph #150 + "mu", // glyph #151 + "partialdiff", // glyph #152 + "summation", // glyph #153 + "product", // glyph #154 + "pi", // glyph #155 + "integral", // glyph #156 + "ordfeminine", // glyph #157 + "ordmasculine", // glyph #158 + "Omega", // glyph #159 + "ae", // glyph #160 + "oslash", // glyph #161 + "questiondown", // glyph #162 + "exclamdown", // glyph #163 + "logicalnot", // glyph #164 + "radical", // glyph #165 + "florin", // glyph #166 + "approxequal", // glyph #167 + "Delta", // glyph #168 + "guillemotleft", // glyph #169 + "guillemotright", // glyph #170 + "ellipsis", // glyph #171 + "nonbreakingspace", // glyph #172 + "Agrave", // glyph #173 + "Atilde", // glyph #174 + "Otilde", // glyph #175 + "OE", // glyph #176 + "oe", // glyph #177 + "endash", // glyph #178 + "emdash", // glyph #179 + "quotedblleft", // glyph #180 + "quotedblright", // glyph #181 + "quoteleft", // glyph #182 + "quoteright", // glyph #183 + "divide", // glyph #184 + "lozenge", // glyph #185 + "ydieresis", // glyph #186 + "Ydieresis", // glyph #187 + "fraction", // glyph #188 + "currency", // glyph #189 + "guilsinglleft", // glyph #190 + "guilsinglright", // glyph #191 + "fi", // glyph #192 + "fl", // glyph #193 + "daggerdbl", // glyph #194 + "periodcentered", // glyph #195 + "quotesinglbase", // glyph #196 + "quotedblbase", // glyph #197 + "perthousand", // glyph #198 + "Acircumflex", // glyph #199 + "Ecircumflex", // glyph #200 + "Aacute", // glyph #201 + "Edieresis", // glyph #202 + "Egrave", // glyph #203 + "Iacute", // glyph #204 + "Icircumflex", // glyph #205 + "Idieresis", // glyph #206 + "Igrave", // glyph #207 + "Oacute", // glyph #208 + "Ocircumflex", // glyph #209 + "apple", // glyph #210 + "Ograve", // glyph #211 + "Uacute", // glyph #212 + "Ucircumflex", // glyph #213 + "Ugrave", // glyph #214 + "dotlessi", // glyph #215 + "circumflex", // glyph #216 + "tilde", // glyph #217 + "macron", // glyph #218 + "breve", // glyph #219 + "dotaccent", // glyph #220 + "ring", // glyph #221 + "cedilla", // glyph #222 + "hungarumlaut", // glyph #223 + "ogonek", // glyph #224 + "caron", // glyph #225 + "Lslash", // glyph #226 + "lslash", // glyph #227 + "Scaron", // glyph #228 + "scaron", // glyph #229 + "Zcaron", // glyph #230 + "zcaron", // glyph #231 + "brokenbar", // glyph #232 + "Eth", // glyph #233 + "eth", // glyph #234 + "Yacute", // glyph #235 + "yacute", // glyph #236 + "Thorn", // glyph #237 + "thorn", // glyph #238 + "minus", // glyph #239 + "multiply", // glyph #240 + "onesuperior", // glyph #241 + "twosuperior", // glyph #242 + "threesuperior", // glyph #243 + "onehalf", // glyph #244 + "onequarter", // glyph #245 + "threequarters", // glyph #246 + "franc", // glyph #247 + "Gbreve", // glyph #248 + "gbreve", // glyph #249 + "Idotaccent", // glyph #250 + "Scedilla", // glyph #251 + "scedilla", // glyph #252 + "Cacute", // glyph #253 + "cacute", // glyph #254 + "Ccaron", // glyph #255 + "ccaron", // glyph #256 + "dcroat" // glyph #257 + }; + + + private GlyphNamer(int numGlyphs, + ByteBuffer postTable, + ByteBuffer zapfTable) + { + this.postTable = postTable; + this.zapfTable = zapfTable; + + if ((zapfTable != null) && (zapfTable.getInt(0) == 0x00010000)) + { + readZapf(numGlyphs); + return; + } + + readPost(); + } + + + /** + * Sets up the information which allows to retrieve the information + * on demand. + * + * @param numGlyphs the number of glyphs in the font. This value + * comes from the <code>maxp</code> table. + */ + public static GlyphNamer forTables(int numGlyphs, + ByteBuffer postTable, + ByteBuffer zapfTable) + { + return new GlyphNamer(numGlyphs, postTable, zapfTable); + } + + + /** + * Retrieves or synthesizes a PostScript name for the glyph. + * Although the code is reasonably fast, it is recommended + * to cache the results in the printer driver. + * + * <p>If a font provides a 'Zapf' table, the reverse mapping + * from glyph to UTF-16 sequence is performed, and a glyph + * name is synthesized following the recommendations by Adobe. + * This allows to extract the original text from the generated + * PostScript or PDF, which is a requirement for indexing + * and searching. + * + * <p>If a font does not provide a 'Zapf' table, the glyph name + * is taken from the 'post' table. Note that some fonts have + * wrong data in their post data, in which case the resulting + * name will be garbage. Usually, this does not hurt, unless + * the user wants to extract text from the generated PostScript + * or PDF file. The GNU implementation understands all known + * formats of the post table (1, 2, 2.5, 3 and 4). + * + * @param glyph the index of the glyph whose name is to be + * retrieved. + * + * @return the glyph name, such as <code>A</code>, + * <code>gcircumflex</code>, <code>z_uni0302</code>, or + * <code>u11C42</code>.</li> + */ + String getGlyphName(int glyph) + { + if (zapfOffsets != null) + { + zapfTable.position(zapfOffsets.get(glyph) + 8); + int numChars = zapfTable.getChar(); + char[] chars = new char[numChars]; + for (int i = 0; i < numChars; i++) + chars[i] = zapfTable.getChar(); + return getGlyphName(chars); + } + + + /* Type 1, Type 2, Type 2.5 */ + if (glyphNames != null) + return glyphNames[glyph]; + + /* Type 4: Synthesized glyph name. */ + if (glyphCharacterCodes != null) + return "a" + glyphCharacterCodes.get(glyph); + + /* Type 3: Arbitrary, but unique name for the glyph. + * + * To find out what a good naming scheme would be, we have printed + * a document containing the character U+201C in the font + * "Hiragino Kaku Gothic Pro W3" (by Dainippon Screen Mfg. Co., + * Ltd.) on Apple MacOS X 10.1.5. This font has a type 3 'post' + * table, and its 'cmap' maps U+201C to glyph #108. The generated + * PostScript file defined a character whose name was "g108". + * + * Therefore, we use 'g' as name prefix. According to the + * TrueType/OpenType specification, it should not matter what + * prefix we use. On the other hand, it does not hurt either to be + * compatible with a good printer driver. + * + * Actually, that specific font also contains a 'Zapf' table, + * which allows to generate glyph names according to Adobe's + * conventions, so that extracting text from and searching in the + * generated PostScript or PDF becomes possible. While the Apple + * PostScript printer driver does not seem to use the 'Zapf' table + * for this purpose, we do. + */ + return "g" + glyph; + } + + + /** + * Sets up some buffers which allow to quickly read information from + * the Zapf table. + * + * @see <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM06/Chap6Zapf.html"> + * Apple’s documentation of the <code>Zapf</code> table</a> + */ + private void readZapf(int numGlyphs) + { + zapfExtraInfo = zapfTable.getInt(4); + zapfTable.position(8); + zapfOffsets = zapfTable.asIntBuffer(); + zapfOffsets.limit(numGlyphs); + } + + + /** + * Reads in the PostScript data from a <code>post</code> table of a + * TrueType or OpenType font. The implementation currently + * understands the table formats 1, 2, 2.5, 3, and 4. + */ + private void readPost() + { + int numGlyphs, nameIndex, maxNameIndex; + char[] nameIndices; + String[] names; + byte[] pascalName; + + postTable.position(0); + postFormat = postTable.getInt(); + switch (postFormat) + { + case 0x00010000: + glyphNames = STANDARD_POSTSCRIPT_GLYPH_NAMES; + return; + + case 0x00020000: + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphNames = new String[numGlyphs]; + pascalName = new byte[255]; + nameIndices = new char[numGlyphs]; + maxNameIndex = 0; + for (int i = 0; i < numGlyphs; i++) + maxNameIndex = Math.max(maxNameIndex, + nameIndices[i] = postTable.getChar()); + + names = new String[Math.max(maxNameIndex - 258 + 1, 0)]; + for (int i = 258; i <= maxNameIndex; i++) + { + int nameLen = (postTable.get() & 0xff); + postTable.get(pascalName, 0, nameLen); + names[i - 258] = new String(pascalName, 0, nameLen); + } + for (int i = 0; i < numGlyphs; i++) + { + nameIndex = nameIndices[i]; + if (nameIndex < 258) + glyphNames[i] = STANDARD_POSTSCRIPT_GLYPH_NAMES[nameIndex]; + else + glyphNames[i] = names[nameIndex - 258]; + } + return; + + case 0x00025000: // in case some font has a wrong representation of 2.5 + case 0x00028000: + /* Format 2.5 is a re-ordering of the standard names. It has + * been deprecated in February 2000, but might still occasionally + * float around. Since it can be supported with so little code, + * we do so. + */ + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphNames = new String[numGlyphs]; + for (int i = 0; i < numGlyphs; i++) + glyphNames[i] = STANDARD_POSTSCRIPT_GLYPH_NAMES[i + postTable.get()]; + return; + + case 0x00030000: + /* Format 3 leaves it to the printer driver to choose whatever + * name it wants to. + */ + return; + + case 0x00040000: + /* Format 4 is used by Apple for composite fonts that have + * synthetic glyph names. The name of a glyph is "a" plus + * the integer (in decimal notation) that follows the table + * after numGlyphs. + */ + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphCharacterCodes = postTable.asCharBuffer(); + glyphCharacterCodes.limit(numGlyphs); + return; + } + } + + + + /* For generating the following tables, a quick-and-dirty Python + * script was used. It is unlikely that we ever need to run it + * again, but for information and convenient access, it is included + * below. Initial '#' characters need to be removed from the generated + * strings, they are present so that the lines not break in the middle + * of Java escape sequences (no, this is not very clean). + * + * import string + * + * javaEscapes = {0x22:'\\"', 0x5c:'\\\\'} + * def escape(c): + * if javaEscapes.has_key(c): + * return javaEscapes[c] + * elif 0x20 <= c <= 0x7e: + * return chr(c) + * else: + * return '\\u%04x' % c + * + * def dump(name, s, stride): + * s = ('#' * stride) + s + * print " private static final String %s" % name + * for i in range(0, len(s), 60): + * print ' + "%s"' % s[i:i+60] + * + * glyphs = {} + * for line in open('aglfn13.txt', 'r').readlines(): + * if line[0] == '#': continue + * [ucs, glyphName, desc] = line.split(';') + * glyph = int('0x' + ucs, 0) + * assert (not glyphs.has_key(glyph)) or (glyphs[glyph] == glyphName) + * glyphs[glyph] = glyphName + * del glyphs[0] # arrowvertex + * k = glyphs.keys() + * k.sort() + * numGlyphs = len(k) + * names = '' + * pos = [] + * for glyph in k: + * pos.append(len(names) + 1) + * names = names + '/' + glyphs[glyph] + * dump('AGLFN_GLYPHS', string.join(map(escape, k), ''), 5) + * dump('AGLFN_NAME_OFFSET', string.join(map(escape, pos), ''), 4) + * dump('AGLFN_NAMES', names + '/', 0) + */ + + + /** + * A String that contains the Unicode codepoint for each glyph + * in the Adobe Glyph List. The characters are in sorted order. + * + * Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/aglfn13.txt" >Adobe + * Glyph List for New Fonts</a> + */ + private static final String AGLFN_GLYPHS + = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU" + + "VWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u00a1\u00a2\u00a3" + + "\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ae" + + "\u00af\u00b0\u00b1\u00b4\u00b5\u00b6\u00b7\u00b8\u00ba\u00bb" + + "\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5" + + "\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf" + + "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9" + + "\u00da\u00db\u00dc\u00dd\u00de\u00df\u00e0\u00e1\u00e2\u00e3" + + "\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed" + + "\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7" + + "\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff\u0100\u0101" + + "\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b" + + "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115" + + "\u0116\u0117\u0118\u0119\u011a\u011b\u011c\u011d\u011e\u011f" + + "\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129" + + "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0132\u0133" + + "\u0134\u0135\u0136\u0137\u0138\u0139\u013a\u013b\u013c\u013d" + + "\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147" + + "\u0148\u0149\u014a\u014b\u014c\u014d\u014e\u014f\u0150\u0151" + + "\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b" + + "\u015c\u015d\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165" + + "\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d\u016e\u016f" + + "\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179" + + "\u017a\u017b\u017c\u017d\u017e\u017f\u0192\u01a0\u01a1\u01af" + + "\u01b0\u01e6\u01e7\u01fa\u01fb\u01fc\u01fd\u01fe\u01ff\u0218" + + "\u0219\u02bc\u02bd\u02c6\u02c7\u02d8\u02d9\u02da\u02db\u02dc" + + "\u02dd\u0300\u0301\u0303\u0309\u0323\u0384\u0385\u0386\u0387" + + "\u0388\u0389\u038a\u038c\u038e\u038f\u0390\u0391\u0392\u0393" + + "\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e" + + "\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03aa" + + "\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4" + + "\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bd\u03be\u03bf" + + "\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9" + + "\u03ca\u03cb\u03cc\u03cd\u03ce\u03d1\u03d2\u03d5\u03d6\u0401" + + "\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b" + + "\u040c\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416" + + "\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420" + + "\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a" + + "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434" + + "\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e" + + "\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448" + + "\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0451\u0452\u0453" + + "\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045e" + + "\u045f\u0462\u0463\u0472\u0473\u0474\u0475\u0490\u0491\u04d9" + + "\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9" + + "\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05d0" + + "\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da" + + "\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4" + + "\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2\u060c" + + "\u061b\u061f\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628" + + "\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632" + + "\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u0660\u0661\u0662" + + "\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u066a\u066d\u0679" + + "\u067e\u0686\u0688\u0691\u0698\u06a4\u06af\u06ba\u06d2\u06d5" + + "\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u200c\u200d" + + "\u200e\u200f\u2012\u2013\u2014\u2015\u2017\u2018\u2019\u201a" + + "\u201b\u201c\u201d\u201e\u2020\u2021\u2022\u2024\u2025\u2026" + + "\u202c\u202d\u202e\u2030\u2032\u2033\u2039\u203a\u203c\u2044" + + "\u20a1\u20a3\u20a4\u20a7\u20aa\u20ab\u20ac\u2105\u2111\u2113" + + "\u2116\u2118\u211c\u211e\u2122\u2126\u212e\u2135\u2153\u2154" + + "\u215b\u215c\u215d\u215e\u2190\u2191\u2192\u2193\u2194\u2195" + + "\u21a8\u21b5\u21d0\u21d1\u21d2\u21d3\u21d4\u2200\u2202\u2203" + + "\u2205\u2206\u2207\u2208\u2209\u220b\u220f\u2211\u2212\u2217" + + "\u221a\u221d\u221e\u221f\u2220\u2227\u2228\u2229\u222a\u222b" + + "\u2234\u223c\u2245\u2248\u2260\u2261\u2264\u2265\u2282\u2283" + + "\u2284\u2286\u2287\u2295\u2297\u22a5\u22c5\u2302\u2310\u2320" + + "\u2321\u2329\u232a\u2500\u2502\u250c\u2510\u2514\u2518\u251c" + + "\u2524\u252c\u2534\u253c\u2550\u2551\u2552\u2553\u2554\u2555" + + "\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f" + + "\u2560\u2561\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569" + + "\u256a\u256b\u256c\u2580\u2584\u2588\u258c\u2590\u2591\u2592" + + "\u2593\u25a0\u25a1\u25aa\u25ab\u25ac\u25b2\u25ba\u25bc\u25c4" + + "\u25ca\u25cb\u25cf\u25d8\u25d9\u25e6\u263a\u263b\u263c\u2640" + + "\u2642\u2660\u2663\u2665\u2666\u266a\u266b"; + + + /** + * The offset of each glyph name in AGLFN_NAMES. + * + * Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/aglfn13.txt" >Adobe + * Glyph List for New Fonts</a> + */ + private static final String AGLFN_NAME_OFFSET + = "\u0001\u0007\u000e\u0017\")1;GQ\\ejpw~\u0084\u0089\u008d" + + "\u0091\u0097\u009c\u00a1\u00a5\u00ab\u00b1\u00b6\u00bc\u00c6" + + "\u00cb\u00d1\u00d9\u00e2\u00e5\u00e7\u00e9\u00eb\u00ed\u00ef" + + "\u00f1\u00f3\u00f5\u00f7\u00f9\u00fb\u00fd\u00ff\u0101\u0103" + + "\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117" + + "\u0119\u0125\u012f\u013c\u0148\u0153\u0159\u015b\u015d\u015f" + + "\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173" + + "\u0175\u0177\u0179\u017b\u017d\u017f\u0181\u0183\u0185\u0187" + + "\u0189\u018b\u018d\u0197\u019b\u01a6\u01b1\u01bc\u01c1\u01ca" + + "\u01d3\u01d7\u01e1\u01e9\u01f2\u01fc\u0208\u0216\u0221\u022c" + + "\u0233\u023a\u0244\u024a\u024d\u0257\u0266\u026e\u027b\u028a" + + "\u0295\u029d\u02ab\u02b8\u02bf\u02c6\u02d2\u02d9\u02e3\u02e9" + + "\u02ec\u02f5\u02fc\u0303\u030f\u0319\u0320\u0327\u0333\u033d" + + "\u0341\u0348\u034f\u0356\u0362\u0369\u0373\u037c\u0383\u038a" + + "\u0391\u039d\u03a7\u03ae\u03b4\u03bf\u03c6\u03cd\u03d9\u03e0" + + "\u03ea\u03f0\u03f3\u03fc\u0403\u040a\u0416\u0420\u0427\u042e" + + "\u043a\u0444\u0448\u044f\u0456\u045d\u0469\u0470\u047a\u0481" + + "\u0488\u048f\u0496\u04a2\u04ac\u04b3\u04b9\u04c3\u04cb\u04d3" + + "\u04da\u04e1\u04e9\u04f1\u04f8\u04ff\u050b\u0517\u0522\u052d" + + "\u0534\u053b\u0542\u0549\u0550\u0557\u055f\u0567\u056e\u0575" + + "\u0580\u058b\u0593\u059b\u05a2\u05a9\u05b5\u05c1\u05c8\u05cf" + + "\u05da\u05e5\u05f2\u05ff\u060b\u0617\u061c\u0621\u0628\u062f" + + "\u0637\u063f\u0646\u064d\u0655\u065d\u0668\u0671\u0674\u0677" + + "\u0683\u068f\u069c\u06a9\u06b6\u06bd\u06c4\u06d1\u06de\u06e5" + + "\u06ec\u06f1\u06f6\u06fd\u0704\u070b\u0712\u071f\u072c\u0733" + + "\u073a\u0746\u074a\u074e\u0756\u075e\u0765\u076c\u077a\u0788" + + "\u078b\u078e\u0795\u079c\u07a9\u07b6\u07bd\u07c4\u07cb\u07d2" + + "\u07de\u07ea\u07f3\u07fc\u0803\u080a\u0817\u0824\u082b\u0832" + + "\u0837\u083c\u0843\u084a\u0852\u085a\u0861\u0868\u086e\u0874" + + "\u0882\u0890\u0898\u08a0\u08ac\u08b8\u08c4\u08d0\u08da\u08e1" + + "\u08e8\u08f3\u08fe\u0905\u090c\u0912\u0919\u091f\u0925\u092b" + + "\u0931\u0938\u093f\u094a\u0955\u095d\u0965\u0971\u097d\u098a" + + "\u0997\u09a1\u09ab\u09b6\u09bc\u09c2\u09cc\u09d1\u09d8\u09de" + + "\u09eb\u09f5\u09ff\u0a09\u0a17\u0a24\u0a2a\u0a38\u0a43\u0a4d" + + "\u0a5a\u0a63\u0a6d\u0a7a\u0a87\u0a92\u0aa4\u0aaa\u0aaf\u0ab5" + + "\u0abd\u0ac2\u0ac6\u0acc\u0ad1\u0ad7\u0ade\u0ae1\u0ae4\u0ae7" + + "\u0aef\u0af2\u0af6\u0afc\u0b00\u0b08\u0b0c\u0b10\u0b14\u0b21" + + "\u0b31\u0b3c\u0b49\u0b52\u0b5c\u0b71\u0b77\u0b7c\u0b82\u0b88" + + "\u0b90\u0b95\u0b99\u0b9f\u0ba4\u0baa\u0bb1\u0bb4\u0bb7\u0bbf" + + "\u0bc2\u0bc6\u0bcd\u0bd3\u0bd7\u0bdf\u0be3\u0be7\u0beb\u0bf1" + + "\u0bfe\u0c0e\u0c1b\u0c28\u0c33\u0c3a\u0c43\u0c48\u0c4f\u0c59" + + "\u0c63\u0c6d\u0c77\u0c81\u0c8b\u0c95\u0c9f\u0ca9\u0cb3\u0cbd" + + "\u0cc7\u0cd1\u0cdb\u0ce5\u0cef\u0cf9\u0d03\u0d0d\u0d17\u0d21" + + "\u0d2b\u0d35\u0d3f\u0d49\u0d53\u0d5d\u0d67\u0d71\u0d7b\u0d85" + + "\u0d8f\u0d99\u0da3\u0dad\u0db7\u0dc1\u0dcb\u0dd5\u0ddf\u0de9" + + "\u0df3\u0dfd\u0e07\u0e11\u0e1b\u0e25\u0e2f\u0e39\u0e43\u0e4d" + + "\u0e57\u0e61\u0e6b\u0e75\u0e7f\u0e89\u0e93\u0e9d\u0ea7\u0eb1" + + "\u0ebb\u0ec5\u0ecf\u0ed9\u0ee3\u0eed\u0ef7\u0f01\u0f0b\u0f15" + + "\u0f1f\u0f29\u0f33\u0f3d\u0f47\u0f51\u0f5b\u0f65\u0f6f\u0f79" + + "\u0f83\u0f8d\u0f97\u0fa1\u0fab\u0fb5\u0fbf\u0fc9\u0fd3\u0fdd" + + "\u0fe7\u0ff1\u0ffb\u1005\u100f\u1019\u1023\u102d\u1037\u1041" + + "\u104b\u1055\u105f\u1069\u1073\u107d\u1087\u1091\u109b\u10a5" + + "\u10af\u10b9\u10c3\u10cd\u10d7\u10e1\u10eb\u10f5\u10ff\u1109" + + "\u1113\u111d\u1127\u1131\u113b\u1145\u114f\u1159\u1163\u116d" + + "\u1177\u1181\u118b\u1195\u119f\u11a9\u11b3\u11bd\u11c7\u11d1" + + "\u11db\u11e5\u11ef\u11f9\u1203\u120d\u1217\u1221\u122b\u1235" + + "\u123f\u1249\u1253\u125d\u1267\u1271\u127b\u1285\u128f\u1299" + + "\u12a3\u12ad\u12b7\u12c1\u12cb\u12d5\u12df\u12e9\u12f3\u12fd" + + "\u1307\u1311\u131b\u1325\u132f\u1339\u1343\u134d\u1357\u1361" + + "\u136b\u1375\u137f\u1389\u1393\u139d\u13a7\u13b1\u13bb\u13c5" + + "\u13cf\u13d9\u13e3\u13ed\u13f7\u1401\u140b\u1415\u141f\u1429" + + "\u1433\u143d\u1447\u1451\u145b\u1465\u146f\u1479\u1483\u148d" + + "\u1497\u14a1\u14ab\u14b5\u14bf\u14c9\u14d3\u14dd\u14e7\u14f1" + + "\u14f8\u14ff\u1506\u150d\u1517\u1521\u1528\u152f\u1539\u1541" + + "\u1549\u1551\u155c\u1563\u156a\u1574\u1582\u158c\u1597\u15a6" + + "\u15b4\u15c1\u15cf\u15dc\u15e3\u15ed\u15f4\u1603\u1612\u161b" + + "\u1625\u162f\u1639\u1645\u164c\u1653\u1661\u1670\u167a\u1683" + + "\u1691\u1697\u169c\u16a3\u16ad\u16b2\u16b7\u16c1\u16ca\u16d4" + + "\u16de\u16ea\u16f3\u1700\u170a\u1710\u171a\u1720\u1729\u1733" + + "\u173d\u174a\u1756\u1763\u176d\u1775\u1780\u178a\u1794\u179e" + + "\u17ab\u17ba\u17c7\u17d2\u17e0\u17ed\u17fa\u1804\u1810\u181c" + + "\u1825\u182b\u1834\u183c\u1847\u1850\u1858\u1862\u1868\u1875" + + "\u187d\u188a\u1893\u189e\u18a4\u18af\u18b9\u18c6\u18cc\u18d5" + + "\u18df\u18e7\u18f1\u18fd\u1906\u1912\u191c\u1929\u1936\u1945" + + "\u194f\u195c\u196b\u1976\u1985\u1993\u199b\u19a1\u19af\u19ba" + + "\u19c5\u19cf\u19da\u19e3\u19ec\u19f5\u19fe\u1a07\u1a10\u1a19" + + "\u1a22\u1a2b\u1a34\u1a3d\u1a46\u1a4f\u1a58\u1a61\u1a6a\u1a73" + + "\u1a7c\u1a85\u1a8e\u1a97\u1aa0\u1aa9\u1ab2\u1abb\u1ac4\u1acd" + + "\u1ad6\u1adf\u1ae8\u1af1\u1afa\u1b03\u1b0c\u1b15\u1b1e\u1b27" + + "\u1b30\u1b39\u1b42\u1b4a\u1b52\u1b58\u1b60\u1b68\u1b70\u1b76" + + "\u1b7e\u1b88\u1b8f\u1b96\u1b9d\u1ba8\u1bb0\u1bb8\u1bc0\u1bc8" + + "\u1bd0\u1bd7\u1bde\u1be8\u1bf2\u1bfd\u1c07\u1c14\u1c18\u1c1f" + + "\u1c24\u1c2a\u1c2f\u1c35\u1c3d\u1c49"; + + + /** + * The name of each glyph in the Adobe Glyph List for New Fonts + * (AGLFN). The name of the n-th glyph starts at position + * AGLFN_NAME_OFFSET.charAt(n). It ends before the following + * slash (slashes cannot be part of a PostScript name, which + * is why we use it for separation). + * + * <p>Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/aglfn13.txt" >Adobe + * Glyph List for New Fonts</a> + */ + private static final String AGLFN_NAMES + = "/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/q" + + "uotesingle/parenleft/parenright/asterisk/plus/comma/hyphen/p" + + "eriod/slash/zero/one/two/three/four/five/six/seven/eight/nin" + + "e/colon/semicolon/less/equal/greater/question/at/A/B/C/D/E/F" + + "/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backsla" + + "sh/bracketright/asciicircum/underscore/grave/a/b/c/d/e/f/g/h" + + "/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/bracerigh" + + "t/asciitilde/exclamdown/cent/sterling/currency/yen/brokenbar" + + "/section/dieresis/copyright/ordfeminine/guillemotleft/logica" + + "lnot/registered/macron/degree/plusminus/acute/mu/paragraph/p" + + "eriodcentered/cedilla/ordmasculine/guillemotright/onequarter" + + "/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumfle" + + "x/Atilde/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumfl" + + "ex/Edieresis/Igrave/Iacute/Icircumflex/Idieresis/Eth/Ntilde/" + + "Ograve/Oacute/Ocircumflex/Otilde/Odieresis/multiply/Oslash/U" + + "grave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls/a" + + "grave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/" + + "egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumfle" + + "x/idieresis/eth/ntilde/ograve/oacute/ocircumflex/otilde/odie" + + "resis/divide/oslash/ugrave/uacute/ucircumflex/udieresis/yacu" + + "te/thorn/ydieresis/Amacron/amacron/Abreve/abreve/Aogonek/aog" + + "onek/Cacute/cacute/Ccircumflex/ccircumflex/Cdotaccent/cdotac" + + "cent/Ccaron/ccaron/Dcaron/dcaron/Dcroat/dcroat/Emacron/emacr" + + "on/Ebreve/ebreve/Edotaccent/edotaccent/Eogonek/eogonek/Ecaro" + + "n/ecaron/Gcircumflex/gcircumflex/Gbreve/gbreve/Gdotaccent/gd" + + "otaccent/Gcommaaccent/gcommaaccent/Hcircumflex/hcircumflex/H" + + "bar/hbar/Itilde/itilde/Imacron/imacron/Ibreve/ibreve/Iogonek" + + "/iogonek/Idotaccent/dotlessi/IJ/ij/Jcircumflex/jcircumflex/K" + + "commaaccent/kcommaaccent/kgreenlandic/Lacute/lacute/Lcommaac" + + "cent/lcommaaccent/Lcaron/lcaron/Ldot/ldot/Lslash/lslash/Nacu" + + "te/nacute/Ncommaaccent/ncommaaccent/Ncaron/ncaron/napostroph" + + "e/Eng/eng/Omacron/omacron/Obreve/obreve/Ohungarumlaut/ohunga" + + "rumlaut/OE/oe/Racute/racute/Rcommaaccent/rcommaaccent/Rcaron" + + "/rcaron/Sacute/sacute/Scircumflex/scircumflex/Scedilla/scedi" + + "lla/Scaron/scaron/Tcommaaccent/tcommaaccent/Tcaron/tcaron/Tb" + + "ar/tbar/Utilde/utilde/Umacron/umacron/Ubreve/ubreve/Uring/ur" + + "ing/Uhungarumlaut/uhungarumlaut/Uogonek/uogonek/Wcircumflex/" + + "wcircumflex/Ycircumflex/ycircumflex/Ydieresis/Zacute/zacute/" + + "Zdotaccent/zdotaccent/Zcaron/zcaron/longs/florin/Ohorn/ohorn" + + "/Uhorn/uhorn/Gcaron/gcaron/Aringacute/aringacute/AEacute/aea" + + "cute/Oslashacute/oslashacute/Scommaaccent/scommaaccent/afii5" + + "7929/afii64937/circumflex/caron/breve/dotaccent/ring/ogonek/" + + "tilde/hungarumlaut/gravecomb/acutecomb/tildecomb/hookaboveco" + + "mb/dotbelowcomb/tonos/dieresistonos/Alphatonos/anoteleia/Eps" + + "ilontonos/Etatonos/Iotatonos/Omicrontonos/Upsilontonos/Omega" + + "tonos/iotadieresistonos/Alpha/Beta/Gamma/Epsilon/Zeta/Eta/Th" + + "eta/Iota/Kappa/Lambda/Mu/Nu/Xi/Omicron/Pi/Rho/Sigma/Tau/Upsi" + + "lon/Phi/Chi/Psi/Iotadieresis/Upsilondieresis/alphatonos/epsi" + + "lontonos/etatonos/iotatonos/upsilondieresistonos/alpha/beta/" + + "gamma/delta/epsilon/zeta/eta/theta/iota/kappa/lambda/nu/xi/o" + + "micron/pi/rho/sigma1/sigma/tau/upsilon/phi/chi/psi/omega/iot" + + "adieresis/upsilondieresis/omicrontonos/upsilontonos/omegaton" + + "os/theta1/Upsilon1/phi1/omega1/afii10023/afii10051/afii10052" + + "/afii10053/afii10054/afii10055/afii10056/afii10057/afii10058" + + "/afii10059/afii10060/afii10061/afii10062/afii10145/afii10017" + + "/afii10018/afii10019/afii10020/afii10021/afii10022/afii10024" + + "/afii10025/afii10026/afii10027/afii10028/afii10029/afii10030" + + "/afii10031/afii10032/afii10033/afii10034/afii10035/afii10036" + + "/afii10037/afii10038/afii10039/afii10040/afii10041/afii10042" + + "/afii10043/afii10044/afii10045/afii10046/afii10047/afii10048" + + "/afii10049/afii10065/afii10066/afii10067/afii10068/afii10069" + + "/afii10070/afii10072/afii10073/afii10074/afii10075/afii10076" + + "/afii10077/afii10078/afii10079/afii10080/afii10081/afii10082" + + "/afii10083/afii10084/afii10085/afii10086/afii10087/afii10088" + + "/afii10089/afii10090/afii10091/afii10092/afii10093/afii10094" + + "/afii10095/afii10096/afii10097/afii10071/afii10099/afii10100" + + "/afii10101/afii10102/afii10103/afii10104/afii10105/afii10106" + + "/afii10107/afii10108/afii10109/afii10110/afii10193/afii10146" + + "/afii10194/afii10147/afii10195/afii10148/afii10196/afii10050" + + "/afii10098/afii10846/afii57799/afii57801/afii57800/afii57802" + + "/afii57793/afii57794/afii57795/afii57798/afii57797/afii57806" + + "/afii57796/afii57807/afii57839/afii57645/afii57841/afii57842" + + "/afii57804/afii57803/afii57658/afii57664/afii57665/afii57666" + + "/afii57667/afii57668/afii57669/afii57670/afii57671/afii57672" + + "/afii57673/afii57674/afii57675/afii57676/afii57677/afii57678" + + "/afii57679/afii57680/afii57681/afii57682/afii57683/afii57684" + + "/afii57685/afii57686/afii57687/afii57688/afii57689/afii57690" + + "/afii57716/afii57717/afii57718/afii57388/afii57403/afii57407" + + "/afii57409/afii57410/afii57411/afii57412/afii57413/afii57414" + + "/afii57415/afii57416/afii57417/afii57418/afii57419/afii57420" + + "/afii57421/afii57422/afii57423/afii57424/afii57425/afii57426" + + "/afii57427/afii57428/afii57429/afii57430/afii57431/afii57432" + + "/afii57433/afii57434/afii57440/afii57441/afii57442/afii57443" + + "/afii57444/afii57445/afii57446/afii57470/afii57448/afii57449" + + "/afii57450/afii57451/afii57452/afii57453/afii57454/afii57455" + + "/afii57456/afii57457/afii57458/afii57392/afii57393/afii57394" + + "/afii57395/afii57396/afii57397/afii57398/afii57399/afii57400" + + "/afii57401/afii57381/afii63167/afii57511/afii57506/afii57507" + + "/afii57512/afii57513/afii57508/afii57505/afii57509/afii57514" + + "/afii57519/afii57534/Wgrave/wgrave/Wacute/wacute/Wdieresis/w" + + "dieresis/Ygrave/ygrave/afii61664/afii301/afii299/afii300/fig" + + "uredash/endash/emdash/afii00208/underscoredbl/quoteleft/quot" + + "eright/quotesinglbase/quotereversed/quotedblleft/quotedblrig" + + "ht/quotedblbase/dagger/daggerdbl/bullet/onedotenleader/twodo" + + "tenleader/ellipsis/afii61573/afii61574/afii61575/perthousand" + + "/minute/second/guilsinglleft/guilsinglright/exclamdbl/fracti" + + "on/colonmonetary/franc/lira/peseta/afii57636/dong/Euro/afii6" + + "1248/Ifraktur/afii61289/afii61352/weierstrass/Rfraktur/presc" + + "ription/trademark/Omega/estimated/aleph/onethird/twothirds/o" + + "neeighth/threeeighths/fiveeighths/seveneighths/arrowleft/arr" + + "owup/arrowright/arrowdown/arrowboth/arrowupdn/arrowupdnbse/c" + + "arriagereturn/arrowdblleft/arrowdblup/arrowdblright/arrowdbl" + + "down/arrowdblboth/universal/partialdiff/existential/emptyset" + + "/Delta/gradient/element/notelement/suchthat/product/summatio" + + "n/minus/asteriskmath/radical/proportional/infinity/orthogona" + + "l/angle/logicaland/logicalor/intersection/union/integral/the" + + "refore/similar/congruent/approxequal/notequal/equivalence/le" + + "ssequal/greaterequal/propersubset/propersuperset/notsubset/r" + + "eflexsubset/reflexsuperset/circleplus/circlemultiply/perpend" + + "icular/dotmath/house/revlogicalnot/integraltp/integralbt/ang" + + "leleft/angleright/SF100000/SF110000/SF010000/SF030000/SF0200" + + "00/SF040000/SF080000/SF090000/SF060000/SF070000/SF050000/SF4" + + "30000/SF240000/SF510000/SF520000/SF390000/SF220000/SF210000/" + + "SF250000/SF500000/SF490000/SF380000/SF280000/SF270000/SF2600" + + "00/SF360000/SF370000/SF420000/SF190000/SF200000/SF230000/SF4" + + "70000/SF480000/SF410000/SF450000/SF460000/SF400000/SF540000/" + + "SF530000/SF440000/upblock/dnblock/block/lfblock/rtblock/ltsh" + + "ade/shade/dkshade/filledbox/H22073/H18543/H18551/filledrect/" + + "triagup/triagrt/triagdn/triaglf/lozenge/circle/H18533/invbul" + + "let/invcircle/openbullet/smileface/invsmileface/sun/female/m" + + "ale/spade/club/heart/diamond/musicalnote/musicalnotedbl/"; + + + /** + * Determines the name of a glyph according to the Adobe Glyph List + * for New Fonts (AGLFN). Because all glyphs in AGLFN correspond to + * a precomposed Unicode codepoint, the mismatch between characters + * and glyphs is not an issue here. + * + * @param c the Unicode codepoint that corresponds to the glyph, for + * example <code>0x010a</code> for <code>LATIN CAPITAL LETTER C WITH + * DOT ABOVE</code>. + * + * @return the glyph name, for example <code>Cdotaccent</code>. If + * the glyph is not in the <i>Adobe Glyph List for New Fonts</i>, + * <code>null</code> is returned. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/aglfn13.txt" >Adobe + * Glyph List for New Fonts (AGLFN), version 1.1 of April 17, + * 2003</a> + * + * @see <a href= + * "http://partners.adobe.com/asn/developer/type/unicodegn.html#6" + * >Adobe’s guidelines related to Unicode</a> + */ + private static String getAGLFNName(char c) + { + int min, max, mid; + char midChar; + + /* Performs a binary search in the sorted array (actually, a + * String) of glyphs in the Adobe Glyph List for New Fonts. + * + * A good compiler might be able to optimize a call to charAt for + * a static final String, but this routine is probably not that + * critical to performance. + */ + min = 0; + max = AGLFN_GLYPHS.length() - 1; + mid = max >> 1; + midChar = AGLFN_GLYPHS.charAt(mid); + do + { + if (midChar == c) + break; + else if (midChar < c) + min = mid + 1; + else + max = mid; + mid = (min + max) >> 1; + midChar = AGLFN_GLYPHS.charAt(mid); + } + while (min < max); + + if (midChar != c) + return null; + + int pos = AGLFN_NAME_OFFSET.charAt(mid); + return AGLFN_NAMES.substring(pos, AGLFN_NAMES.indexOf('/', pos)); + } + + + /** + * Returns the PostScript name of a glyph, given the sequence of + * Unicode characters that is required to produce the glyph. The + * returned name follows Adobe’s glyph naming recommendations + * in order to allow searching and indexing of the produced + * PostScript and PDF. + * + * <p>Some examples: + * <ul><li><code>U+0041</code> gives <code>A</code>;</li> + * <li><code>U+011D</code> gives <code>gcircumflex</code>;</li> + * <li><code>U+007A U+0302</code> gives <code>z_uni0302</code>;</li> + * <li><code>U+D807 U+DC42</code> (an UTF-16 escape sequence) + * gives <code>u11C42</code>;</li> + * </ul>. + * + * <p>The routine does <i>not</i> bring sequences in any canonical + * form. Therefore, the result for <code>U+0067 U+0302</code> (the + * decomposition of <code>U+011D</code>) will be + * <code>g_uni0302</code>, not <code>gcircumflex</code>. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/unicodegn.jsp" >Unicode + * and Glyph Names</a> and <a href= + * "http://partners.adobe.com/asn/tech/type/glyphnamelimits.jsp" + * >Glyph Names and Current Implementations</a> + */ + private static String getGlyphName(char[] chars) + { + char c; + String name; + int numChars; + boolean hasSurrogates = false; + + if ((chars == null) || ((numChars = chars.length) == 0)) + return ".notdef"; + + /* The vast majority of cases will be just a single character. + * Therefore, we have a special code path for this case. + */ + if (numChars == 1) + { + c = chars[0]; + name = getAGLFNName(c); + if (name != null) + return name; + } + + StringBuffer buf = new StringBuffer(numChars * 8); + for (int i = 0; i < numChars; i++) + { + if (i > 0) + buf.append('_'); + c = chars[i]; + + /* handle surrogate pairs */ + if (c >> 10 == 0x36) // U+D800 .. U+DBFF: High surrogate + { + /* Adobe recommends using the 'u' prefix only for + * characters outside the Unicode Basic Multilingual Plane, + * because Acrobat 4 and 5 understand only the "uni" prefix. + * The 'u' prefix will be supported by Acrobat 6 and later. + * + * For further information, please refer to this page: + * http://partners.adobe.com/asn/tech/type/glyphnamelimits.jsp#3 + */ + int ucs4 = (((c & 0x3ff) << 10) | (chars[++i] & 0x3ff)) + 0x10000; + buf.append('u'); + buf.append(Integer.toHexString(ucs4).toUpperCase()); + } + else + { + /* Try the Adobe Glyph List. */ + name = getAGLFNName(c); + if (name != null) + buf.append(name); + else + { + char nibble; + buf.append("uni"); + nibble = (char) (((c >> 12) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 8) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 4) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 0) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + } + } + } + return buf.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java b/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java new file mode 100644 index 0000000..8115e04 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java @@ -0,0 +1,235 @@ +/* MacResourceFork.java -- Parses MacOS resource forks. + 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.java.awt.font.opentype; + +import java.nio.ByteBuffer; + + +/** + * A class for accessing data that is stored in the resource fork of + * Macintosh files. Writing resource forks is currently not supported. + * + * <p>The gnu.java.awt.font package uses this class for accessing + * fonts in the MacOS X ".dfont" format, which is is a file in the + * format of a Macintosh resource fork, but stored in the normal data + * fork of the file. + * + * <p>The implementation has been designed to work efficiently with + * the virtual memory subsystem. It is recommended to pass an + * instance of {@link java.nio.MappedByteBuffer} to the constructor. + * + * <p>Thread Safety: All access is synchronized on the ByteBuffer + * that is passed to the constructor. + * + * @see <a href= + * "http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html" + * >Apple’ developer documentation about the Resource File + * Format</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class MacResourceFork +{ + int[] types; + Resource[][] resources; + ByteBuffer buf; + + public MacResourceFork(ByteBuffer buf) + { + int typeListOffset; + int refListOffset; + int nameListOffset; + int mapOffset, mapLen; + int dataOffset, dataLen; + int numTypes; + + synchronized (buf) + { + buf = buf.duplicate(); + this.buf = buf; + buf.position(0); + dataOffset = buf.getInt(); + mapOffset = buf.getInt(); + dataLen = buf.getInt(); + mapLen = buf.getInt(); + buf.position(mapOffset + 24); + refListOffset = mapOffset + buf.getChar(); + nameListOffset = mapOffset + buf.getChar(); + numTypes = buf.getChar() + 1; + types = new int[numTypes]; + resources = new Resource[numTypes][]; + + /* Parse resource type list. */ + typeListOffset = buf.position(); + for (int i = 0; i < numTypes; i++) + { + buf.position(typeListOffset + 8 * i); + int resType = buf.getInt(); + int numRes = buf.getChar() + 1; + + types[i] = resType; + resources[i] = new Resource[numRes]; + + buf.position(refListOffset + buf.getChar()); + for (int j = 0; j < numRes; j++) + { + short resID = buf.getShort(); + int resNameOffset = nameListOffset + buf.getChar(); + int resDataOffset = buf.getInt(); + byte resAttr = (byte) (resDataOffset >> 24); + resDataOffset = dataOffset + (resDataOffset & 0x00ffffff); + buf.getInt(); /* skip four reserved bytes */ + + Resource rsrc = new Resource(buf, resType, resID, resDataOffset, + resNameOffset); + resources[i][j] = rsrc; + } + } + } + } + + + public Resource[] getResources(int type) + { + synchronized (buf) + { + for (int i = 0; i < types.length; i++) + { + if (types[i] == type) + return resources[i]; + } + } + return null; + } + + + public Resource getResource(int type, short id) + { + Resource[] res; + + synchronized (buf) + { + for (int i = 0; i < types.length; i++) + { + if (types[i] != type) + continue; + + res = resources[i]; + for (int j = 0; j < res.length; j++) + if (res[j].getID() == id) + return res[j]; + } + } + + return null; + } + + + /** + * A single resource that is contained in a resource fork. + */ + public static final class Resource + { + int type; + short id; + byte attribute; + int nameOffset; + int dataOffset; + ByteBuffer buf; + + private Resource(ByteBuffer buf, + int type, short id, int dataOffset, int nameOffset) + { + this.buf = buf; + this.type = type; + this.id = id; + this.dataOffset = dataOffset; + this.nameOffset = nameOffset; + } + + + /** + * Returns the type of this resource. + * + * @return an <code>int</code> encoding a four-byte type tag, + * such as <code>0x464f4e54</code> for <code>'FONT'</code>. + */ + public int getType() + { + return type; + } + + + /** + * Returns the ID of this resource. + */ + public short getID() + { + return id; + } + + + /** + * Retrieves the content of the resource. Only one page of memory + * is touched, irrespective of the actual size of the resource. + */ + public ByteBuffer getContent() + { + synchronized (buf) + { + buf.limit(buf.capacity()); + int len = buf.getInt(dataOffset); + buf.position(dataOffset + 4).limit(dataOffset + 4 + len); + return buf.slice(); + } + } + + + /** + * Determines the length of the resource in bytes. + */ + public int getLength() + { + synchronized (buf) + { + return buf.getInt(dataOffset); + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java b/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java new file mode 100644 index 0000000..bc0c0df --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java @@ -0,0 +1,686 @@ +/* NameDecoder.java -- Decodes names of OpenType and TrueType fonts. + 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.java.awt.font.opentype; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.Locale; + + +/** + * A utility class that helps with decoding the names of OpenType + * and TrueType fonts. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +class NameDecoder +{ + public static final int NAME_COPYRIGHT = 0; + + + /** + * Specifies the name of the family to which a font belongs, for + * example “Univers”. + */ + public static final int NAME_FAMILY = 1; + + + /** + * Specified the name of the font inside its family, for + * example “Light”. + */ + public static final int NAME_SUBFAMILY = 2; + + + public static final int NAME_UNIQUE = 3; + + + /** + * Specifies the full human-readable name of a font, for example + * “Univers Light” + */ + public static final int NAME_FULL = 4; + + + public static final int NAME_VERSION = 5; + + + /** + * Specifies the PostScript name of a font, for example + * “Univers-Light”. + */ + public static final int NAME_POSTSCRIPT = 6; + + + public static final int NAME_TRADEMARK = 7; + public static final int NAME_MANUFACTURER = 8; + public static final int NAME_DESIGNER = 9; + public static final int NAME_DESCRIPTION = 10; + public static final int NAME_VENDOR_URL = 11; + public static final int NAME_DESIGNER_URL = 12; + public static final int NAME_LICENSE = 13; + public static final int NAME_LICENSE_URL = 14; + public static final int NAME_PREFERRED_FAMILY = 16; + public static final int NAME_PREFERRED_SUBFAMILY = 17; + public static final int NAME_FULL_MACCOMPATIBLE = 18; + public static final int NAME_SAMPLE_TEXT = 19; + public static final int NAME_POSTSCRIPT_CID = 20; + + + private static final int PLATFORM_MACINTOSH = 1; + private static final int PLATFORM_MICROSOFT = 3; + + + public static String getName(ByteBuffer nameTable, + int name, Locale locale) + { + int numRecords; + int macLanguage, msLanguage; + int offset; + int namePlatform, nameEncoding, nameLanguage, nameID, nameLen; + int nameStart; + String result; + boolean match; + + if (nameTable == null) + return null; + + nameTable.position(0); + /* We understand only format 0 of the name table. */ + if (nameTable.getChar() != 0) + return null; + + macLanguage = getMacLanguageCode(locale); + msLanguage = getMicrosoftLanguageCode(locale); + numRecords = nameTable.getChar(); + offset = nameTable.getChar(); + + for (int i = 0; i < numRecords; i++) + { + namePlatform = nameTable.getChar(); + nameEncoding = nameTable.getChar(); + nameLanguage = nameTable.getChar(); + nameID = nameTable.getChar(); + nameLen = nameTable.getChar(); + nameStart = offset + nameTable.getChar(); + + + if (nameID != name) + continue; + + match = false; + switch (namePlatform) + { + case PLATFORM_MACINTOSH: + if ((nameLanguage == macLanguage) || (locale == null)) + match = true; + else + { + switch (macLanguage) + { + case 49: /* Azerbaijani/Cyrillic */ + match = (nameLanguage == /* Azerbaijani/Arabic */ 50) + || (nameLanguage == /* Azerbaijani/Roman */ 150); + break; + + case 57: /* Mongolian/Mongolian */ + match = (nameLanguage == /* Mongolian/Cyrillic */ 58); + break; + + case 83: /* Malay/Roman */ + match = (nameLanguage == /* Malay/Arabic */ 84); + break; + } + } + break; + + case PLATFORM_MICROSOFT: + if (((nameLanguage & 0xff) == msLanguage) || (locale == null)) + match = true; + break; + } + + + if (match) + { + result = decodeName(namePlatform, nameEncoding, nameLanguage, + nameTable, nameStart, nameLen); + if (result != null) + return result; + } + } + + return null; + } + + + /** + * The language codes used by the Macintosh operating system. MacOS + * defines numeric language identifiers in the range [0 .. 95] and + * [128 .. 150]. To map this numeric identifier into an ISO 639 + * language code, multiply it by two and take the substring at that + * position. + * + * <p>ISO 639 has revised the code for some languages, namely + * <code>he</code> for Hebrew (formerly <code>iw</code>), + * <code>yi</code> (formerly <code>ji</code>), and <code>id</code> + * for Indonesian (formerly <code>in</code>). In those cases, this + * table intentionally contains the older, obsolete code. The + * reason is that this is the code which + * java.util.Locale.getLanguage() is specified to return. The + * implementation of {@link #getMacLanguageCode} depends on this. + * + * @see <a href= + * "http://www.unicode.org/unicode/onlinedat/languages.html" + * >Language Codes: ISO 639, Microsoft and Macintosh</a> + */ + private static final String macLanguageCodes + // 0 1 2 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + = "enfrdeitnlsvesdaptnoiwjaarfielismttrhrzhurhithkoltplhuetlv " + + // 3 4 5 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "fofaruzhnlgdsqrocssksljisrmkbgukbeuzkkazazhykamokytgtkmnmnps" + + // 6 7 8 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "kukssdbonesamrbnasgupaormlkntatesimykmloviintlmsmsamti sosw" + + // 9 10 11 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "rwrn mgeo " + + // 12 13 14 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + " cyeucalaqugnayttugtsjwsuglafbriugdgvgatoelkl" + + // 15 + // 0 + + "az"; + + + /** + * The primary language IDs used by the Microsoft operating systems. + * + * <p>ISO 639 has revised the code for some languages, namely + * <code>he</code> for Hebrew (formerly <code>iw</code>), + * <code>yi</code> (formerly <code>ji</code>), and <code>id</code> + * for Indonesian (formerly <code>in</code>). In those cases, this + * table intentionally contains the older, obsolete code. The + * reason is that this is the code which + * java.util.Locale.getLanguage() is specified to return. The + * implementation of {@link #getMicrosoftLanguageCode} depends on + * this. + * + * @see <a href= + * "http://www.unicode.org/unicode/onlinedat/languages.html" + * >Language Codes: ISO 639, Microsoft and Macintosh</a> + */ + private static final String microsoftLanguageCodes + // 0 1 2 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + = " arbgcazhcsdadeelenesfifriwhuisitjakonlnoplptrmrorushsksqsv" + + // 3 4 5 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "thtrurinukbesletlvlttgfavihyazeu mk ts xhzuafkafohimt " + + // 6 7 8 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "gajimskkkyswtkuzttbnpaguortateknmlasmrsamnbocykmlomygl sd" + + // 9 10 11 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + " si iuam ksnefypstl ha yo omtign laso"; + + + /** + * Maps a Java Locale into a MacOS language code. + * + * <p>For languages that are written in several script systems, + * MacOS defines multiple language codes. Java Locales have a + * variant which could be used for that purpose, but a small + * test program revealed that with Sun's JDK 1.4.1_01, only two + * of 134 available Locales have a variant tag (namely no_NO_NY + * and th_TH_TH).</p> + * + * <p>The following cases are problematic: + * + * <ul> <li>Azerbaijani (az): The MacOS language code is 49 if + * Azerbaijani is written in the Cyrillic script; 50 if written in + * the Arabic script; 150 if written in the Roman script. This + * method will always return 49 for the Azerbaijani locale.</li> + * + * <li>Mongolian (mn): The MacOS language code is 57 if Mongolian is + * written in the Mongolian script; 58 if written in the Cyrillic + * script. This method will always return 57 for the Mongolian + * locale.</li> + * + * <li>Malay (ms): The MacOS language code is 83 if Malay is written + * in the Roman script; 84 if written in the Arabic script. This + * method will always return 83 for the Malay locale.</li> </ul> + * + * @return a MacOS language code, or -1 if there is no such code for + * <code>loc</code>’s language. + */ + private static int getMacLanguageCode(Locale loc) + { + int code; + + if (loc == null) + return -1; + + code = findLanguageCode(loc.getLanguage(), macLanguageCodes); + switch (code) + { + case 19: + /* Traditional Chinese (MacOS language #19) and and Simplified + * Chinese (MacOS language #33) both have "zh" as their ISO 639 + * code. + */ + if (loc.equals(Locale.SIMPLIFIED_CHINESE)) + code = 33; + break; + + // Other special cases would be 49, 57 and 83, but we do not + // know what do do about them. See the method documentation for + // details. + } + + return code; + } + + + /** + * Maps a Java Locale into a Microsoft language code. + */ + private static int getMicrosoftLanguageCode(Locale locale) + { + String isoCode; + int code; + + if (locale == null) + return -1; + + isoCode = locale.getLanguage(); + code = findLanguageCode(isoCode, microsoftLanguageCodes); + if (code == -1) + { + if (isoCode.equals("hr") || isoCode.equals("sr")) + { + /* Microsoft uses code 26 for "sh" (Serbo-Croatian), + * "hr" (Croatian) and "sr" (Serbian). Our table contains + * "sh". + */ + code = 26; + } + else if (isoCode.equals("gd")) + { + /* Microsoft uses code 60 for "gd" (Scottish Gaelic) and + * "ga" (Irish Gaelic). Out table contains "ga". + */ + code = 60; + } + } + return code; + } + + + private static int findLanguageCode(String lang, String langCodes) + { + int index; + if (lang == null) + return -1; + + if (lang.length() != 2) + return -1; + + index = 0; + do + { + index = langCodes.indexOf(lang, index); + + /* The index must be even to be considered a match. Otherwise, we + * could match with the second letter of one language and the + * first of antoher one. + */ + } + while (!((index < 0) || ((index & 1) == 0))); + if (index < 0) + return -1; + + index = index / 2; + return index; + } + + + private static String decodeName(int platform, int encoding, int language, + ByteBuffer buffer, int offset, int len) + { + byte[] byteBuf; + String charsetName; + int oldPosition; + + charsetName = getCharsetName(platform, language, encoding); + if (charsetName == null) + return null; + + byteBuf = new byte[len]; + oldPosition = buffer.position(); + try + { + buffer.position(offset); + buffer.get(byteBuf); + try + { + return new String(byteBuf, charsetName); + } + catch (UnsupportedEncodingException uex) + { + } + } + finally + { + buffer.position(oldPosition); + } + + return null; + } + + + /** + * Maps a MacOS language code into a Java Locale. + * + * @param macLanguageCode the MacOS language code for + * the language whose Java locale is to be retrieved. + * + * @return an suitable Locale, or <code>null</code> if + * the mapping cannot be performed. + */ + private static Locale getMacLocale(int macLanguageCode) + { + String isoCode; + + switch (macLanguageCode) + { + case 0: return Locale.ENGLISH; + case 1: return Locale.FRENCH; + case 2: return Locale.GERMAN; + case 3: return Locale.ITALIAN; + case 11: return Locale.JAPANESE; + case 23: return Locale.KOREAN; + case 19: return Locale.TRADITIONAL_CHINESE; + case 33: return Locale.SIMPLIFIED_CHINESE; + } + + if ((macLanguageCode < 0) || (macLanguageCode > 150)) + return null; + + isoCode = macLanguageCodes.substring(macLanguageCode << 1, + (macLanguageCode + 1) << 1); + if (isoCode.charAt(0) == ' ') + return null; + + return new Locale(isoCode); + } + + + + /** + * Maps a Windows LCID into a Java Locale. + * + * @param lcid the Windows language ID whose Java locale + * is to be retrieved. + * + * @return an suitable Locale, or <code>null</code> if + * the mapping cannot be performed. + */ + private static Locale getWindowsLocale(int lcid) + { + /* FIXME: This is grossly incomplete. */ + switch (lcid) + { + case 0x0407: return Locale.GERMAN; + case 0x0408: return new Locale("el", "GR"); + case 0x0409: return Locale.ENGLISH; + case 0x040b: return new Locale("fi"); + case 0x040c: return Locale.FRENCH; + case 0x0416: return new Locale("pt"); + case 0x0807: return new Locale("de", "CH"); + case 0x0809: return new Locale("en", "UK"); + case 0x080c: return new Locale("fr", "BE"); + case 0x0816: return new Locale("pt", "BR"); + case 0x0c07: return new Locale("de", "AT"); + case 0x0c09: return new Locale("en", "AU"); + case 0x0c0c: return new Locale("fr", "CA"); + case 0x1007: return new Locale("de", "LU"); + case 0x1009: return new Locale("en", "CA"); + case 0x100c: return new Locale("fr", "CH"); + case 0x1407: return new Locale("de", "LI"); + case 0x1409: return new Locale("en", "NZ"); + case 0x140c: return new Locale("fr", "LU"); + case 0x1809: return new Locale("en", "IE"); + + default: + return null; + } + } + + + /** + * Maps a Macintosh Script Manager code to the name of the + * corresponding Java Charset. + * + * @param macScript a MacOS ScriptCode, for example + * 6 for <code>smGreek</code>. + * + * @return a String that can be used to retrieve a Java + * CharsetDecorder, for example <code>MacGreek</code>, or + * <code>null</code> if <code>macScript</code> has an + * unsupported value. + */ + private static String getMacCharsetName(int macScript) + { + switch (macScript) + { + case 0: return "MacRoman"; + case 1: return "MacJapanese"; + case 2: return "MacKorean"; + case 3: return "MacTradChinese"; + case 4: return "MacArabic"; + case 5: return "MacHebrew"; + case 6: return "MacGreek"; + case 7: return "MacCyrillic"; + case 8: return "MacRSymbol"; + case 9: return "MacDevanagari"; + case 10: return "MacGurmukhi"; + case 11: return "MacGujarati"; + case 12: return "MacOriya"; + case 13: return "MacBengali"; + case 14: return "MacTamil"; + case 15: return "MacTelugu"; + case 16: return "MacKannada"; + case 17: return "MacMalayalam"; + case 18: return "MacSinhalese"; + case 19: return "MacBurmese"; + case 20: return "MacKhmer"; + case 21: return "MacThai"; + case 22: return "MacLao"; + case 23: return "MacGeorgian"; + case 24: return "MacArmenian"; + case 25: return "MacSimpChinese"; + case 26: return "MacTibetan"; + case 27: return "MacMongolian"; + case 28: return "MacEthiopic"; + case 29: return "MacCentralEurope"; + case 30: return "MacVietnamese"; + case 31: return "MacExtArabic"; + + default: return null; + } + } + + + /** + * Maps a Microsoft locale ID (LCID) to the name of the + * corresponding Java Charset. + * + * @param lcid the Microsoft locale ID. + * + * @return a String that can be used to retrieve a Java + * CharsetDecorder, for example <code>windows-1252</code>, or + * <code>null</code> if <code>lcid</code> has an unsupported value. + */ + private static String getMicrosoftCharsetName(int lcid) + { + int lang; + char codePage = '?'; + + /* Extract the language code from the LCID. */ + lang = lcid & 0x3ff; + + /* In the majority of cases, the language alone determines the + * codepage. + */ + if (lang < 100) + codePage = (" 612D022322225022EC2202201?002A462110777 68 ?2 1 " + + " 2 2 2112 ?1 1 2 2 ") + .charAt(lang); + + /* There are a few exceptions, however, where multiple code pages + * are used for the same language. */ + if (codePage == '?') + { + switch (lcid) + { + case 0x041a: // Croatian --> Windows-1250 (Central Europe) + case 0x081a: // Serbian (Latin) --> Windows-1250 (Central Europe) + codePage = '0'; + break; + + case 0x42c: // Azeri (Latin) --> Windows-1254 (Turkish) + case 0x443: // Uzbek (Latin) --> Windows-1254 (Turkish) + codePage = '4'; + break; + + case 0x82c: // Azeri (Cyrillic) --> Windows-1251 (Cyrillic) + case 0x843: // Uzbek (Cyrillic) --> Windows-1251 (Cyrillic) + case 0xc1a: // Serbian (Cyrillic) --> Windows-1251 (Cyrillic) + codePage = '1'; + break; + } + } + + switch (codePage) + { + case '0': return "windows-1250"; // Central Europe + case '1': return "windows-1251"; // Cyrillic + case '2': return "windows-1252"; // Latin 1 + case '3': return "windows-1253"; // Greek + case '4': return "windows-1254"; // Turkish + case '5': return "windows-1255"; // Hebrew + case '6': return "windows-1256"; // Arabic + case '7': return "windows-1257"; // Baltic + case '8': return "windows-1258"; // Vietnam + case 'A': return "windows-874"; // Thai + case 'B': return "windows-936"; // Simplified Chinese, GBK + case 'C': return "windows-949"; // Korean + case 'D': return "windows-950"; // Traditional Chinese, Big5 + case 'E': return "windows-932"; // Japanese Shift-JIS + default: return null; + } + } + + + /** + * Returns the Locale of an OpenType name. + * + * @param platform the OpenType platform ID. + * + * @param language the language tag of the OpenType name. If + * <code>platform</code> is 1, this is the MacOS language code. + * + * @param encoding the encoding tag of the OpenType name. If + * <code>platform</code> is 1, this is the MacOS script code. + */ + public static Locale getLocale(int platform, int language, int encoding) + { + switch (platform) + { + case 1: /* Apple Macintosh */ + return getMacLocale(language); + + case 3: /* Microsoft Windows */ + return getWindowsLocale(language); + + default: + return null; + } + } + + + /** + * Determines the name of the charset for an OpenType font name. + * + * @param platform the OpenType platform ID. + * + * @param language the language tag of the OpenType name. If + * <code>platform</code> is 1, this is the MacOS language code. + * + * @param encoding the encoding tag of the OpenType name. If + * <code>platform</code> is 1, this is the MacOS script code. + * + * @return a charset name such as <code>"MacRoman"</code>, + * or <code>null</code> if the combination is not known. + */ + public static String getCharsetName(int platform, int language, int encoding) + { + switch (platform) + { + case 1: /* Apple Macintosh */ + return getMacCharsetName(encoding); + + case 3: /* Microsoft Windows */ + return getMicrosoftCharsetName(language); + + default: + return null; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java new file mode 100644 index 0000000..9ee28d7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java @@ -0,0 +1,825 @@ +/* OpenTypeFont.java -- Manages OpenType and TrueType fonts. + 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.java.awt.font.opentype; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.OpenType; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.nio.ByteBuffer; +import java.text.CharacterIterator; +import java.util.Locale; + +import gnu.java.awt.font.FontDelegate; +import gnu.java.awt.font.GNUGlyphVector; +import gnu.java.awt.font.opentype.truetype.TrueTypeScaler; + + +/** + * A font that takes its data from OpenType or TrueType font tables. + * + * <p>OpenType is an extension of the TrueType font format. In addition + * to tables for names, kerning or layout, it also stores the shapes + * of individual glyphs. Three formats are recognized for glyphs: + * Quadratic splines (classic TrueType), cubic splines (PostScript), + * and bitmaps. + * + * @see <a + * href="http://partners.adobe.com/asn/tech/type/opentype/">Adobe’s + * OpenType specification</a> + * + * @see <a + * href="http://developer.apple.com/fonts/TTRefMan/">Apple’s</code> + * TrueType specification</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class OpenTypeFont + implements FontDelegate +{ + static final int TAG_OTTO = 0x4f54544f; // 'OTTO' + static final int TAG_SFNT = 0x73666e74; // 'sfnt' + static final int TAG_TRUE = 0x74727565; // 'true' + static final int TAG_TTCF = 0x74746366; // 'ttcf' + static final int TAG_ZAPF = 0x5a617066; // 'Zapf' + + + /** + * A buffer containing the font data. Note that this may well be an + * instance of the subclass MappedByteBuffer, in which case the + * virtual memory subsystem can more efficiently handle requests for + * font data. This is especially recommended for large font files + * that contain many glyphs that are rarely accessed. + */ + ByteBuffer buf; + + + /** + * The number of glyphs in this font. + */ + final int numGlyphs; + + int[] tableTag, tableStart, tableLength; + + + /** + * The version of the font in 16.16 fixed-point encoding, for + * example 0x00010000 for version 1.0. There are also two special + * version IDs used by fonts for Apple Macintosh, namely 'true' + * (0x74727565) and 'typ1'. OpenType fonts sometimes have 'OTTO' as + * their version. + */ + private int version; + + + /** + * The number of font units per em. For fonts with TrueType + * outlines, this is usually a power of two (such as 2048). For + * OpenType fonts with PostScript outlines, other values are + * acceptable (such as 1000). + */ + private int unitsPerEm; + + + /** + * A factor to convert font units into ems. This value is <code>1 / + * unitsPerEm</code>. + */ + private float emsPerUnit; + + + /** + * The scaler to which the actual scaling work is delegated. + */ + private Scaler scaler; + + + /** + * A delegate object for mapping Unicode UCS-4 codepoints to glyph + * IDs. + */ + private CharGlyphMap cmap; + + + /** + * A delegate object for providing a name for each glyph. + */ + private GlyphNamer glyphNamer; + + + /** + * Constructs an OpenType or TrueType font. + * + * @param buf a buffer with the contents of the font file. It is + * recommended to use a <code>MappedByteBuffer</code> for very + * large font files. + * + * @param offsetTablePosition the position of the OpenType offset + * table in the font file. The offset table of most OpenType and + * TrueType fonts starts at position 0. However, so-called TrueType + * Collections support multiple OpenType fonts in a single file, + * which allows sharing some glyphs between fonts. If many glyphs + * are shared (for example all the Kanji glyphs between multiple + * Japanese fonts), the space savings can be considerable. In that + * case, the offset table of each individual font would start at its + * own position. + * + * @throws java.awt.FontFormatException if the font data is + * not in OpenType or TrueType format. + */ + OpenTypeFont(ByteBuffer buf, int offsetTablePosition) + throws FontFormatException + { + int numTables, searchRange, entrySelector, rangeShift; + + //buf = buf.duplicate(); + this.buf = buf; + buf.limit(buf.capacity()); + buf.position(offsetTablePosition); + + /* Check that the font data is in a supported format. */ + version = buf.getInt(); + switch (version) + { + case 0x00010000: // Microsoft TrueType + case OpenType.TAG_TYP1: // Adobe PostScript embeded in Apple SFNT ('typ1') + case TAG_SFNT: // Apple TrueType + case TAG_TRUE: // Apple TrueType + case TAG_OTTO: // OpenType + break; + + default: + throw new FontFormatException("not in OpenType or TrueType format"); + } + + numTables = buf.getShort(); + searchRange = buf.getShort(); + entrySelector = buf.getShort(); + rangeShift = buf.getShort(); + + tableTag = new int[numTables]; + tableStart = new int[numTables]; + tableLength = new int[numTables]; + int lastTag = 0; + for (int i = 0; i < numTables; i++) + { + tableTag[i] = buf.getInt(); + if (lastTag >= tableTag[i]) + throw new FontFormatException("unordered OpenType table"); + + buf.getInt(); // ignore checksum + tableStart[i] = buf.getInt(); + tableLength[i] = buf.getInt(); + + //System.out.println(tagToString(tableTag[i]) + ", " + tableLength[i]); + } + + ByteBuffer head = getFontTable(OpenType.TAG_HEAD); + if ((head.getInt(0) != 0x00010000) + || (head.getInt(12) != 0x5f0f3cf5)) + throw new FontFormatException("unsupported head version"); + + unitsPerEm = head.getChar(18); + emsPerUnit = 1.0f / (float) unitsPerEm; + + + ByteBuffer maxp = getFontTable(OpenType.TAG_MAXP); + int maxpVersion = maxp.getInt(0); + switch (maxpVersion) + { + case 0x00005000: /* version 0.5, with wrong fractional part */ + numGlyphs = maxp.getChar(4); + break; + + case 0x00010000: /* version 1.0 */ + numGlyphs = maxp.getChar(4); + scaler = new TrueTypeScaler(unitsPerEm, + getFontTable(OpenType.TAG_HHEA), + getFontTable(OpenType.TAG_HMTX), + getFontTable(OpenType.TAG_VHEA), + getFontTable(OpenType.TAG_VMTX), + maxp, + getFontTable(OpenType.TAG_CVT), + getFontTable(OpenType.TAG_FPGM), + /* loca format */ head.getShort(50), + getFontTable(OpenType.TAG_LOCA), + getFontTable(OpenType.TAG_GLYF), + getFontTable(OpenType.TAG_PREP)); + break; + + default: + throw new FontFormatException("unsupported maxp version"); + } + } + + + /** + * Determines the index of a table into the offset table. The + * result can be used to find the offset and length of a table, as + * in <code>tableStart[getTableIndex(TAG_NAME)]</code>. + * + * @param tag the table identifier, for instance + * <code>OpenType.TAG_NAME</code>. + * + * @return the index of that table into the offset table, or + * -1 if the font does not contain the table specified by + * <code>tag</code>. + */ + private int getTableIndex(int tag) + { + /* FIXME: Since the font specification requires tableTag[] to be + * ordered, one should do binary search here. + */ + for (int i = 0; i < tableTag.length; i++) + if (tableTag[i] == tag) + return i; + return -1; + } + + + + /** + * Returns the name of the family to which this font face belongs, + * for example <i>“Univers”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the family name. + */ + public synchronized String getFamilyName(Locale locale) + { + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_FAMILY, locale); + if (name == null) + name = getName(NameDecoder.NAME_FAMILY, Locale.ENGLISH); + if (name == null) + name = getName(NameDecoder.NAME_FAMILY, /* any language */ null); + if (name == null) + name = getName(NameDecoder.NAME_FULL, locale); + if (name == null) + name = getName(NameDecoder.NAME_FULL, /* any language */ null); + return name; + } + + + /** + * Returns the name of this font face inside the family, for example + * <i>“Light”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the name of the face inside its family. + */ + public synchronized String getSubFamilyName(Locale locale) + { + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_SUBFAMILY, locale); + if (name == null) + { + name = getName(NameDecoder.NAME_SUBFAMILY, Locale.ENGLISH); + if ("Regular".equals(name)) + name = null; + } + + if (name == null) + { + String lang = locale.getLanguage(); + if ("de".equals(lang)) + name = "Standard"; + else if ("fr".equals(lang)) + name = "Standard"; + else if ("it".equals(lang)) + name = "Normale"; + else if ("nl".equals(lang)) + name = "Normaal"; + else if ("fi".equals(lang)) + name = "Normaali"; + else if ("sv".equals(lang)) + name = "Normal"; + else + name = "Regular"; + } + + return name; + } + + + + /** + * Returns the full name of this font face, for example + * <i>“Univers Light”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the face name. + */ + public synchronized String getFullName(Locale locale) + { + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_FULL, locale); + if (name == null) + name = getName(NameDecoder.NAME_FULL, Locale.ENGLISH); + if (name == null) + name = getName(NameDecoder.NAME_FULL, /* any language */ null); + + return name; + } + + + /** + * Returns the PostScript name of this font face, for example + * <i>“Univers-Light”</i>. + * + * @return the PostScript name, or <code>null</code> if the font + * does not provide a PostScript name. + */ + public synchronized String getPostScriptName() + { + return getName(NameDecoder.NAME_POSTSCRIPT, /* any language */ null); + } + + + /** + * Returns the number of glyphs in this font face. + */ + public int getNumGlyphs() + { + /* No synchronization is needed because the number of glyphs is + * set in the constructor, and it cannot change during the + * lifetime of the object. + */ + return numGlyphs; + } + + + /** + * Returns the index of the glyph which gets displayed if the font + * cannot map a Unicode code point to a glyph. Many fonts show this + * glyph as an empty box. + */ + public int getMissingGlyphCode() + { + /* No synchronization is needed because the result is constant. */ + return 0; + } + + + /** + * The font’s name table, or <code>null</code> if this + * table has not yet been accessed. + */ + private ByteBuffer nameTable; + + + /** + * Extracts a String from the font’s name table. + * + * @param name the numeric TrueType or OpenType name ID. + * + * @param locale the locale for which names shall be localized, or + * <code>null</code> if the locale does mot matter because the name + * is known to be language-independent (for example, because it is + * the PostScript name). + */ + private String getName(int name, Locale locale) + { + if (nameTable == null) + nameTable = getFontTable(OpenType.TAG_NAME); + return NameDecoder.getName(nameTable, name, locale); + } + + + /** + * Returns the version of the font. + * + * @see java.awt.font.OpenType#getVersion + * + * @return the version in 16.16 fixed-point encoding, for example + * 0x00010000 for version 1.0. + */ + public int getVersion() + { + /* No synchronization is needed because the version is set in the + * constructor, and it cannot change during the lifetime of the + * object. + */ + return version; + } + + + /** + * Creates a view buffer for an OpenType table. The caller can + * access the returned buffer without needing to synchronize access + * from multiple threads. + * + * @param tag the table identifier, for example + * <code>OpenType.GLYF</code>. + * + * @return a slice of the underlying buffer containing the table, or + * <code>null</code> if the font does not contain the requested + * table. + */ + public synchronized ByteBuffer getFontTable(int tag) + { + int index, start, len; + ByteBuffer result; + + index = getTableIndex(tag); + if (index < 0) + return null; + + start = tableStart[index]; + len = tableLength[index]; + buf.limit(start + len).position(start); + result = buf.slice(); + result.limit(len); + return result; + } + + + /** + * Returns the size of one of the tables in the font, + * or -1 if the table does not exist. + */ + public int getFontTableSize(int tag) + { + int index = getTableIndex(tag); + if (index == -1) + return index; + return tableLength[index]; + } + + + private CharGlyphMap getCharGlyphMap() + { + if (cmap != null) + return cmap; + + synchronized (this) + { + if (cmap == null) + { + int index = getTableIndex(OpenType.TAG_CMAP); + int start = tableStart[index]; + buf.limit(start + tableLength[index]).position(start); + cmap = CharGlyphMap.forTable(buf); + } + return cmap; + } + } + + + + /** + * Looks up a glyph in the font’s <code>cmap</code> tables, + * without performing any glyph substitution or reordering. Because + * of this limitation, this method cannot be used for script systems + * that need advanced glyph mapping, such as Arabic, Korean, or even + * Latin with exotic accents. + * + * <p>It is safe to call this method from any thread. + * + * @param ucs4 the Unicode codepoint in the 32-bit Unicode character + * set UCS-4. Because UTF-16 surrogates do not correspond to a single + * glyph, it does not make sense to pass them here. + * + * @return the glyph index, or zero if the font does not contain + * a glyph for the specified codepoint. + */ + public int getGlyph(int ucs4) + { + return getCharGlyphMap().getGlyph(ucs4); + } + + + /** + * Creates a GlyphVector by mapping each character in a + * CharacterIterator to the corresponding glyph. + * + * <p>The mapping takes only the font’s <code>cmap</code> + * tables into consideration. No other operations (such as glyph + * re-ordering, composition, or ligature substitution) are + * performed. This means that the resulting GlyphVector will not be + * correct for text in languages that have complex + * character-to-glyph mappings, such as Arabic, Hebrew, Hindi, or + * Thai. + * + * @param font the font object that the created GlyphVector + * will return when it gets asked for its font. This argument is + * needed because the public API works with java.awt.Font, + * not with some private delegate like OpenTypeFont. + * + * @param frc the font rendering parameters that are used for + * measuring glyphs. The exact placement of text slightly depends on + * device-specific characteristics, for instance the device + * resolution or anti-aliasing. For this reason, any measurements + * will only be accurate if the passed + * <code>FontRenderContext</code> correctly reflects the relevant + * parameters. Hence, <code>frc</code> should be obtained from the + * same <code>Graphics2D</code> that will be used for drawing, and + * any rendering hints should be set to the desired values before + * obtaining <code>frc</code>. + * + * @param ci a CharacterIterator for iterating over the + * characters to be displayed. + */ + public synchronized GlyphVector createGlyphVector(Font font, + FontRenderContext frc, + CharacterIterator ci) + { + CharGlyphMap cmap; + int numGlyphs; + int[] glyphs; + int glyph; + int c; + + cmap = getCharGlyphMap(); + numGlyphs = ci.getEndIndex() - ci.getBeginIndex(); + glyphs = new int[numGlyphs]; + glyph = 0; + for (c = ci.first(); c != CharacterIterator.DONE; c = ci.next()) + { + /* handle surrogate pairs */ + if (c >> 10 == 0x36) // U+D800 .. U+DBFF: High surrogate + c = (((c & 0x3ff) << 10) | (ci.next() & 0x3ff)) + 0x10000; + glyphs[glyph] = cmap.getGlyph(c); + glyph += 1; + } + + /* If we had surrogates, the allocated array is too large. + * Because this will occur very rarely, it seems acceptable to + * re-allocate a shorter array and copy the contents around. + */ + if (glyph != numGlyphs) + { + int[] newGlyphs = new int[glyph]; + System.arraycopy(glyphs, 0, newGlyphs, 0, glyph); + glyphs = newGlyphs; + } + + return new GNUGlyphVector(this, font, frc, glyphs); + } + + + + /** + * Determines the advance width for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @param advance a point whose <code>x</code> and <code>y</code> + * fields will hold the advance in each direction. It is possible + * that both values are non-zero, for example if + * <code>transform</code> is a rotation, or in the case of Urdu + * fonts. + */ + public synchronized void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance) + { + /* Delegate the measurement to the scaler. The synchronization is + * needed because the scaler is not synchronized. + */ + scaler.getAdvance(glyphIndex, pointSize, transform, + antialias, fractionalMetrics, horizontal, + advance); + } + + + /** + * Returns the shape of a glyph. + * + * @param glyph the glyph whose advance width is to be determined + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, this + * parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional + * metrics, <code>false</code> for rounding the result to a pixel + * boundary. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or <code>null</code> for bitmap fonts. + */ + public synchronized GeneralPath getGlyphOutline(int glyph, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics) + { + /* The synchronization is needed because the scaler is not + * synchronized. + */ + return scaler.getOutline(glyph, pointSize, transform, + antialias, fractionalMetrics); + } + + + /** + * Returns a name for the specified glyph. This is useful for + * generating PostScript or PDF files that embed some glyphs of a + * font. + * + * <p><b>Names are not unique:</b> Under some rare circumstances, + * the same name can be returned for different glyphs. It is + * therefore recommended that printer drivers check whether the same + * name has already been returned for antoher glyph, and make the + * name unique by adding the string ".alt" followed by the glyph + * index.</p> + * + * <p>This situation would occur for an OpenType or TrueType font + * that has a <code>post</code> table of format 3 and provides a + * mapping from glyph IDs to Unicode sequences through a + * <code>Zapf</code> table. If the same sequence of Unicode + * codepoints leads to different glyphs (depending on contextual + * position, for example, or on typographic sophistication level), + * the same name would get synthesized for those glyphs. + * + * @param glyphIndex the glyph whose name the caller wants to + * retrieve. + */ + public synchronized String getGlyphName(int glyphIndex) + { + if (glyphNamer == null) + glyphNamer = GlyphNamer.forTables(numGlyphs, + getFontTable(OpenType.TAG_POST), + getFontTable(TAG_ZAPF)); + + return glyphNamer.getGlyphName(glyphIndex); + } + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public synchronized float getAscent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + return scaler.getAscent(pointSize, transform, + antialiased, fractionalMetrics, + horizontal); + } + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public synchronized float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + return scaler.getDescent(pointSize, transform, + antialiased, fractionalMetrics, + horizontal); + } + + + /** + * Converts a four-byte tag identifier into a String that can be + * displayed when debugging this class. + * + * @param tag the tag as an <code>int</code>. + * + * @return the tag in human-readable form, for example + * <code>name</code> or <code>glyf</code>. + */ + static String tagToString(int tag) + { + char[] c = new char[4]; + c[0] = (char) ((tag >> 24) & 0xff); + c[1] = (char) ((tag >> 16) & 0xff); + c[2] = (char) ((tag >> 8) & 0xff); + c[3] = (char) (tag & 0xff); + return new String(c); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java new file mode 100644 index 0000000..3a00dfb --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java @@ -0,0 +1,140 @@ +/* OpenTypeFontFactory.java -- Creates OpenType and TrueType fonts. + 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.java.awt.font.opentype; + +import gnu.java.awt.font.FontDelegate; +import java.awt.FontFormatException; +import java.awt.font.OpenType; +import java.nio.ByteBuffer; + + +/** + * A factory for creating fonts that are stored in an + * <i>sfnt</i>-housed format, for example OpenType or TrueType. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class OpenTypeFontFactory +{ + /** + * The constructor is private so nobody can construct an instance + * of this class. + */ + private OpenTypeFontFactory() + { + } + + + /** + * Creates FontDelegate objects for the fonts in the specified + * buffer. The following font formats are currently recognized: + * + * <p><ul> + * <li>OpenType (*.otf);</li> + * <li>TrueType (*.ttf);</li> + * <li>TrueType Collections (*.ttc);</li> + * <li>Apple MacOS X data-fork font (*.dfont).</li></ul> + * + * <p>Some formats may contain more than a single font, for example + * *.ttc and *.dfont files. This is the reason why this function + * returns an array. + * + * <p>The implementation reads data from the buffer only when + * needed. Therefore, it greatly increases efficiency if + * <code>buf</code> has been obtained through mapping a file into + * the virtual address space. + * + * @throws FontFormatException if the font data is not in one of the + * known formats. + */ + public static FontDelegate[] createFonts(ByteBuffer buf) + throws FontFormatException + { + OpenTypeFont[] fonts; + int version; + + version = buf.getInt(0); + switch (version) + { + case 0x00010000: // Microsoft Windows TrueType + case OpenType.TAG_TYP1: // Apple MacOS PostScript ('typ1') + case OpenTypeFont.TAG_SFNT: // Apple MacOS TrueType ('sfnt') + case OpenTypeFont.TAG_TRUE: // Apple MacOS TrueType ('true') + case OpenTypeFont.TAG_OTTO: // OpenType + return new OpenTypeFont[] { new OpenTypeFont(buf, 0) }; + } + + + /* TrueType Collection, see "TrueType Collections" in + * http://partners.adobe.com/asn/tech/type/opentype/otff.html + */ + if (version == OpenTypeFont.TAG_TTCF) + { + // This code has never been tested. + fonts = new OpenTypeFont[buf.getInt(8)]; + for (int i = 0; i < fonts.length; i++) + fonts[i] = new OpenTypeFont(buf, buf.getInt(16 + 4 * i)); + return fonts; + } + + + /* The MacOS X .dfont format is a Macintosh resource fork in + * a normal file, contaning one or several 'sfnt' resources. + * Unfortunately, MacOS resource forks have no magic code + * that could be used for identification. Instead, we just try + * to extract at least one 'sfnt'. + */ + try + { + MacResourceFork fork = new MacResourceFork(buf); + MacResourceFork.Resource[] rsrc; + + rsrc = fork.getResources(OpenTypeFont.TAG_SFNT); + fonts = new OpenTypeFont[rsrc.length]; + for (int i = 0; i < fonts.length; i++) + fonts[i] = new OpenTypeFont(rsrc[i].getContent(), 0); + + return fonts; + } + catch (Exception ex) + { + } + + throw new FontFormatException("not in OpenType or TrueType format"); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java b/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java new file mode 100644 index 0000000..499c3ea --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java @@ -0,0 +1,192 @@ +/* Scaler.java -- Common superclass for font scalers. + 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.java.awt.font.opentype; + +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; + + +/** + * An common superclass for all font scalers. The main task of font + * scaler is to retrieve a scaled and hinted outline for a glyph. + * + * <p>To make text more legible, high-quality fonts contain + * instructions (sometimes also called “hints”) for + * moving the scaled control points towards the coordinate grid of the + * display device. + * + * <p><b>Lack of Thread Safety:</b> Font scalers are intentionally + * <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font that uses this scaler already has obtained a lock before + * calling the scaler. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class Scaler +{ + /** + * Retrieves the scaled outline of a glyph, adjusting control points + * to the raster grid if necessary. + * + * @param glyph the glyph number whose outline is retrieved. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias whether or not the rasterizer will perform + * anti-aliasing on the returned path. + * + * @param fractionalMetrics <code>false</code> for adjusting glyph + * positions to the raster grid of device space. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or <code>null</code> for bitmap fonts. + */ + public abstract GeneralPath getOutline(int glyph, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics); + + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @param advance a point whose <code>x</code> and <code>y</code> + * fields will hold the advance in each direction. It is well + * possible that both values are non-zero, for example for rotated + * text or for Urdu fonts. + */ + public abstract void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance); + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public abstract float getAscent(float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal); + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public abstract float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java new file mode 100644 index 0000000..5d81c5d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java @@ -0,0 +1,161 @@ +/* Fixed.java -- Fixed-point arithmetics for TrueType coordinates. + 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.java.awt.font.opentype.truetype; + + +/** + * A utility class for fixed-point arithmetics, where numbers are + * represented with 26 dot 6 digits. This representation is used by + * TrueType coordinates. + * + * <p>A good compiler will inline calls of methods in this class. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class Fixed +{ + public static final int ONE = 1<<6; + + + /** + * The constructor is private so nobody can use it. + */ + private Fixed() + { + } + + + /** + * Multiplies two fixed-point numbers. + */ + public static int mul(int a, int b) + { + return (int) ((((long) a) * b) >> 6); + } + + + public static int div(int a, int b) + { + return (int) ((((long) a) << 6) / b); + } + + + + public static int ceil(int a) + { + return (a + 63) & -64; + } + + + public static int floor(int a) + { + return a & -64; + } + + + /** + * Calculates the length of a fixed-point vector. + */ + public static int vectorLength(int x, int y) + { + int shift; + float fx, fy; + + if (x == 0) + return Math.abs(y); + else if (y == 0) + return Math.abs(x); + + /* Use the FPU. */ + fx = ((float) x) / 64.0f; + fy = ((float) y) / 64.0f; + return (int) (Math.sqrt(fx * fx + fy * fy) * 64.0); + } + + + public static int intValue(int f) + { + return f >> 6; + } + + + public static float floatValue(int f) + { + return ((float) f) / 64; + } + + + public static double doubleValue(int f) + { + return ((double) f) / 64; + } + + + public static int valueOf(float f) + { + return (int) (f * 64); + } + + + public static int valueOf(double d) + { + return (int) (d * 64); + } + + + /** + * Makes a string representation of a fixed-point number. + */ + public static String toString(int f) + { + return String.valueOf(floatValue(f)); + } + + + public static String toString(int x, int y) + { + StringBuffer sbuf = new StringBuffer(40); + sbuf.append('('); + sbuf.append(((float) x) / 64); + sbuf.append(", "); + sbuf.append(((float) y) / 64); + sbuf.append(')'); + return sbuf.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java new file mode 100644 index 0000000..b12d778 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java @@ -0,0 +1,437 @@ +/* GlyphLoader.java -- Helper for loading TrueType glyph outlines. + 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.java.awt.font.opentype.truetype; + +import java.awt.geom.AffineTransform; +import java.nio.ByteBuffer; + + +/** + * A class for loading scaled and hinted glyph outlines. + * + * <p><b>Lack of Thread Safety:</b> Glyph loaders are intentionally + * <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the GlyphLoader. It would thus be wasteful to + * acquire additional locks for the GlyphLoader. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphLoader +{ + /** + * A helper object for locating glyph data. GlyphLocator is an + * abstract superclass, and there is a concretization for each glyph + * location table ('loca') format. + */ + private final GlyphLocator glyphLocator; + + + /** + * A helper object for measuring the advance width and height of a + * glyph. + */ + private final GlyphMeasurer glyphMeasurer; + + + /** + * The virtual machine for executing TrueType bytecodes. + */ + private final VirtualMachine vm; + + + /** + * The number of font units in one em. A typical value is 2048, + * but this depends on the font. + */ + private final int unitsPerEm; + + private final int[] contourEndPoints; + private final byte[] pointFlags; + + + /** + * Constructs a GlyphLoader. + */ + GlyphLoader(GlyphLocator glyphLocator, VirtualMachine vm, + int unitsPerEm, int maxContours, int maxPoints, + GlyphMeasurer glyphMeasurer) + { + this.glyphLocator = glyphLocator; + this.glyphMeasurer = glyphMeasurer; + this.unitsPerEm = unitsPerEm; + this.vm = vm; + + contourEndPoints = new int[maxContours]; + pointFlags = new byte[maxPoints]; + } + + + /** + * @param glyphIndex the number of the glyph whose outlines are to be + * retrieved. + */ + public void loadGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + Zone glyphZone) + { + glyphZone.setNumPoints(4); + loadSubGlyph(glyphIndex, pointSize, transform, antialias, glyphZone, + 0, 0); + } + + + private void loadSubGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + Zone glyphZone, + int preTranslateX, + int preTranslateY) + { + ByteBuffer glyph; + int numContours; + int xMin, yMin, xMax, yMax; + byte flag; + + glyph = glyphLocator.getGlyphData(glyphIndex); + + if (glyph == null) + { + glyphZone.setNumPoints(4); + setPhantomPoints(glyphIndex, 0, glyphZone); + glyphZone.transform(pointSize, transform, unitsPerEm, + preTranslateX, preTranslateY); + return; + } + + numContours = glyph.getShort(); + xMin = glyph.getChar(); + yMin = glyph.getChar(); + xMax = glyph.getChar(); + yMax = glyph.getChar(); + + + if (numContours >= 0) + loadSimpleGlyph(glyphIndex, pointSize, transform, antialias, + numContours, glyph, glyphZone, + preTranslateX, preTranslateY); + else + loadCompoundGlyph(glyphIndex, pointSize, transform, antialias, + glyph, glyphZone, + preTranslateX, preTranslateY); + } + + + private void loadSimpleGlyph(int glyphIndex, + double pointSize, AffineTransform transform, + boolean antialias, + int numContours, ByteBuffer glyph, + Zone glyphZone, + int preTranslateX, int preTranslateY) + { + int numPoints; + int posInstructions, numInstructions; + boolean execInstructions; + + execInstructions = vm.setup(pointSize, transform, antialias); + + /* Load the contour end points and determine the number of + * points. + */ + for (int i = 0; i < numContours; i++) + contourEndPoints[i] = glyph.getChar(); + if (numContours > 0) + numPoints = 1 + contourEndPoints[numContours - 1]; + else + numPoints = 0; + glyphZone.setNumPoints(numPoints + 4); + + numInstructions = glyph.getChar(); + posInstructions = glyph.position(); + glyph.position(posInstructions + numInstructions); + loadFlags(numPoints, glyph); + loadCoordinates(numPoints, glyph, glyphZone); + for (int i = 0; i < numContours; i++) + glyphZone.setContourEnd(contourEndPoints[i], true); + + setPhantomPoints(glyphIndex, numPoints, glyphZone); + glyphZone.transform(pointSize, transform, unitsPerEm, + preTranslateX, preTranslateY); + + if (execInstructions) + { + // FIXME: Hint the glyph. + } + } + + + private static final short ARGS_ARE_WORDS = 1; + private static final short ARGS_ARE_XY_VALUES = 2; + private static final short ROUND_XY_TO_GRID = 4; + private static final short WE_HAVE_A_SCALE = 8; + private static final short MORE_COMPONENTS = 32; + private static final short WE_HAVE_AN_X_AND_Y_SCALE = 64; + private static final short WE_HAVE_A_TWO_BY_TWO = 128; + private static final short WE_HAVE_INSTRUCTIONS = 256; + private static final short USE_MY_METRICS = 512; + private static final short OVERLAP_COMPOUND = 1024; + private static final short SCALED_COMPONENT_OFFSET = 2048; + private static final short UNSCALED_COMPONENT_OFFSET = 4096; + + private void loadCompoundGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + ByteBuffer glyph, + Zone glyphZone, + int preTranslateX, int preTranslateY) + { + short flags; + int subGlyphIndex; + int metricsGlyphIndex; + Zone subGlyphZone = new Zone(glyphZone.getCapacity()); + int arg1, arg2; + double a, b, c, d, e, f; + AffineTransform componentTransform = new AffineTransform(); + + /* By default, use the metrics of the compound glyph. The default + * is overridden if some component glyph has the USE_MY_METRICS + * flag set. + */ + metricsGlyphIndex = glyphIndex; + + do + { + flags = glyph.getShort(); + subGlyphIndex = glyph.getChar(); + + if ((flags & USE_MY_METRICS) != 0) + metricsGlyphIndex = subGlyphIndex; + + if ((flags & ARGS_ARE_WORDS) != 0) + { + arg1 = glyph.getShort(); + arg2 = glyph.getShort(); + } + else + { + arg1 = glyph.get(); + arg2 = glyph.get(); + } + + if ((flags & WE_HAVE_A_SCALE) != 0) + { + a = d = getDouble214(glyph); + b = c = 0.0; + } + else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0) + { + a = getDouble214(glyph); + d = getDouble214(glyph); + b = c = 0.0; + } + else if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0) + { + a = getDouble214(glyph); + b = getDouble214(glyph); + c = getDouble214(glyph); + d = getDouble214(glyph); + } + else + { + a = d = 1.0; + b = c = 0.0; + } + + double m = Math.max(Math.abs(a), Math.abs(b)); + double n = Math.max(Math.abs(c), Math.abs(d)); + + /* The Apple TrueType specification actually says that m is + * multiplied by two if + * + * abs(abs(a) - abs(c)) <= 33/65536, + * + * but this is probably a typo. On 2003-07-23, Sascha Brawer + * wrote an e-mail message to applefonts@apple.com, asking + * whether this might possibly be an error in the specification. + */ + if (Math.abs(Math.abs(a) - Math.abs(b)) <= 33.0/65536.0) + m = m * 2; + + if (Math.abs(Math.abs(c) - Math.abs(d)) <= 33.0/65536.0) + n = n * 2; + + if ((flags & ARGS_ARE_XY_VALUES) != 0) + { + e = m * arg1; + f = n * arg2; + } + else + e = f = 0.0; + + componentTransform.setTransform(a, b, c, d, 0.0, 0.0); + + // System.out.println("componentTransform = " + componentTransform + // + ", e=" + e + ", f=" + f); + componentTransform.concatenate(transform); + + int pos = glyph.position(); + int lim = glyph.limit(); + + loadSubGlyph(subGlyphIndex, pointSize, componentTransform, + antialias, subGlyphZone, + Math.round((float) e + preTranslateX), + Math.round(-((float) f + preTranslateY))); + glyphZone.combineWithSubGlyph(subGlyphZone, 4); + glyph.limit(lim).position(pos); + } + while ((flags & MORE_COMPONENTS) != 0); + + setPhantomPoints(metricsGlyphIndex, glyphZone.getSize() - 4, glyphZone); + } + + + private double getDouble214(ByteBuffer buf) + { + return ((double) buf.getShort()) / (1 << 14); + } + + + /** + * Loads the per-point flags of a glyph into the + * <code>pointFlags</code> field. + */ + private void loadFlags(int numPoints, ByteBuffer glyph) + { + byte flag; + int numRepetitions; + + for (int i = 0; i < numPoints; i++) + { + pointFlags[i] = flag = glyph.get(); + if ((flag & 8) != 0) + { + numRepetitions = ((int) glyph.get()) & 0xff; + while (numRepetitions > 0) + { + pointFlags[++i] = flag; + --numRepetitions; + } + } + } + } + + + private void loadCoordinates(int numPoints, ByteBuffer glyph, + Zone glyphZone) + { + int x, y; + byte flag; + + x = 0; + for (int i = 0; i < numPoints; i++) + { + flag = pointFlags[i]; + if ((flag & 2) == 0) + { + if ((flag & 16) == 0) + x += glyph.getShort(); + } + else + { + if ((flag & 16) != 0) + x += (glyph.get() & 0xff); + else + x -= (glyph.get() & 0xff); + } + glyphZone.setOriginalX(i, x); + glyphZone.setOnCurve(i, (flag & 1) == 1); + } + + y = 0; + for (int i = 0; i < numPoints; i++) + { + flag = pointFlags[i]; + if ((flag & 4) == 0) + { + if ((flag & 32) == 0) + y += glyph.getShort(); + } + else + { + if ((flag & 32) != 0) + y += (glyph.get() & 0xff); + else + y -= (glyph.get() & 0xff); + } + glyphZone.setOriginalY(i, -y); + } + } + + + private void setPhantomPoints(int glyphIndex, int numPoints, + Zone glyphZone) + { + /* Phantom point 0: Character origin. */ + glyphZone.setOriginalX(numPoints, 0); + glyphZone.setOriginalY(numPoints, 0); + + /* Phantom point 1: Horizontal advance point. */ + glyphZone.setOriginalX(numPoints + 1, + glyphMeasurer.getAdvanceWidth(glyphIndex, true)); + glyphZone.setOriginalY(numPoints + 1, + glyphMeasurer.getAdvanceHeight(glyphIndex, true)); + + /* Phantom point 2: Vertical origin. */ + int vertX = glyphMeasurer.getAscent(/* vertical */ false); + int vertY = glyphMeasurer.getAscent(/* horizontal */ true); + glyphZone.setOriginalX(numPoints + 2, vertX); + glyphZone.setOriginalY(numPoints + 2, vertY); + + /* Phantom point 3: Vertical advance point. */ + glyphZone.setOriginalX(numPoints + 3, + vertX + glyphMeasurer.getAdvanceWidth(glyphIndex, false)); + glyphZone.setOriginalY(numPoints + 3, + vertY + glyphMeasurer.getAdvanceHeight(glyphIndex, false)); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java new file mode 100644 index 0000000..a2db8ac --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java @@ -0,0 +1,187 @@ +/* GlyphLocator.java -- Locates outlines of TrueType glyphs. + 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.java.awt.font.opentype.truetype; + +import java.awt.FontFormatException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.IntBuffer; + + +/** + * Locates glyph outlines in a TrueType or OpenType <code>glyf</code> + * table. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/opentype/loca.html" + * >Adobe’s specification of the OpenType ‘loca’ + * table</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +abstract class GlyphLocator +{ + /** + * The actual glyph data of the font, which is contained in the + * 'glyf' table. + */ + protected ByteBuffer glyfTable; + + + /** + * Creates a new GlyphLocator for a <code>loca</code> table. + * + * @param format the format of the <code>loca</code> table. The + * value must be 0 for two-byte offsets, or 1 for four-byte + * offsets. TrueType and OpenType fonts indicate the format in the + * <code>indexToLoc</code> field of the <a href= + * "http://partners.adobe.com/asn/tech/type/opentype/head.html" + * >font header</a>. + * + * @param loca the <code>loca</code> table of the font, which + * contains the position of each glyph in the <code>glyf</code> + * table. + * + * @param glyf the <code>glyf</code> table of the font, which + * contains the outline data of each glyph. + * + * @throws FontFormatException if <code>format</code> is neither 0 + * nor 1. + */ + public static GlyphLocator forTable(int format, ByteBuffer loca, + ByteBuffer glyf) + throws FontFormatException + { + switch (format) + { + case 0: + return new GlyphLocator.TwoByte(loca, glyf); + + case 1: + return new GlyphLocator.FourByte(loca, glyf); + + default: + throw new FontFormatException("unsupported loca format"); + } + } + + + /** + * Locates the outline data for a glyph. + * + * <p>For efficiency, the glyph locator does not create a new buffer + * for each invocation. Instead, this method always returns the same + * buffer object. Therefore, the data of a glyph must have been read + * completely before another glyph of the same font gets requested + * through this method. + * + * @param glyph the number of the glyph whose outlines are to be + * retrieved. + * + * @return a buffer whose position is set to the first byte of glyph + * data, and whose limit is set to disallow accessing any data that + * does not belong to the glyph. If there is no outline data for the + * requested glyph, as would be the case for the space glyph, the + * result will be <code>null</code>. + */ + public abstract ByteBuffer getGlyphData(int glyph); + + + /** + * A GlyphLocator that locates glyphs using two-byte offsets, + * interpreting <code>loca</code> tables of format 0. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private final static class TwoByte + extends GlyphLocator + { + final CharBuffer indexToLoc; + + TwoByte(ByteBuffer loca, ByteBuffer glyf) + { + this.glyfTable = glyf; + indexToLoc = loca.asCharBuffer(); + } + + + public ByteBuffer getGlyphData(int glyph) + { + int offset, limit; + offset = ((int) indexToLoc.get(glyph)) << 1; + limit = ((int) indexToLoc.get(glyph + 1)) << 1; + if (offset >= limit) + return null; + + glyfTable.limit(limit).position(offset); + return glyfTable; + } + } + + + /** + * A GlyphLocator that locates glyphs using four-byte offsets, + * interpreting <code>loca</code> tables of format 1. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private final static class FourByte + extends GlyphLocator + { + final IntBuffer indexToLoc; + + FourByte(ByteBuffer loca, ByteBuffer glyf) + { + this.glyfTable = glyf; + indexToLoc = loca.asIntBuffer(); + } + + + public ByteBuffer getGlyphData(int glyph) + { + int offset, limit; + offset = indexToLoc.get(glyph); + limit = indexToLoc.get(glyph + 1); + if (offset >= limit) + return null; + + glyfTable.limit(limit).position(offset); + return glyfTable; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java new file mode 100644 index 0000000..bbd0b9b --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java @@ -0,0 +1,228 @@ +/* GlyphMeasurer.java -- Helper for measuring TrueType glyphs. + 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.java.awt.font.opentype.truetype; + +import java.awt.FontFormatException; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + + +/** + * A class for measuring TrueType and OpenType glyphs. + * + * <p><b>Lack of Thread Safety:</b> Glyph measurers are intentionally + * <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the GlyphMeasurer. It would thus be wasteful to + * acquire additional locks for the GlyphMeasurer. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphMeasurer +{ + /** + * A view buffer that allows accessing the contents of the + * font’s <code>hmtx</code> table as shorts. + */ + private final ShortBuffer horizontalGlyphMetrics; + + + /** + * A view buffer that allows accessing the contents of the + * font’s <code>vmtx</code> table as shorts. + */ + private final ShortBuffer verticalGlyphMetrics; + + + private final int numLongHorizontalMetricsEntries; + private final int numLongVerticalMetricsEntries; + + private final int horizontalAscent; + private final int verticalAscent; + + private final int horizontalDescent; + private final int verticalDescent; + + private final int horizontalLineGap; + + + /** + * Constructs a GlyphMeasurer from TrueType/OpenType font tables. + * + * @param hhea the <code>hhea</code> table, which contains + * information about horizontal metrics that is common to all + * glyphs. + * + * @param hmtx the <code>hmtx</code> table, which contains + * glyph-specific information about horizontal metrics. + * + * @param vhea the <code>vhea</code> table, which contains + * information about vertical metrics that is common to all + * glyphs. If a font does not provide such a table, pass + * <code>null</code>. + * + * @param vmtx the <code>vmtx</code> table, which contains + * glyph-specific information about vertical metrics. If a font + * does not provide such a table, pass <code>null</code>. + */ + GlyphMeasurer(ByteBuffer hhea, ByteBuffer hmtx, + ByteBuffer vhea, ByteBuffer vmtx) + throws FontFormatException + { + if ((hhea.getInt(0) != 0x00010000) || (hhea.getInt(30) != 0)) + throw new FontFormatException("unsupported hhea format"); + + horizontalAscent = hhea.getShort(4); + horizontalDescent = hhea.getShort(6); + horizontalLineGap = hhea.getShort(8); + + numLongHorizontalMetricsEntries = hhea.getChar(34); + horizontalGlyphMetrics = hmtx.asShortBuffer(); + + if (vhea != null) + { + verticalAscent = vhea.getShort(4); + verticalDescent = vhea.getShort(6); + numLongVerticalMetricsEntries = vhea.getChar(34); + verticalGlyphMetrics = vmtx.asShortBuffer(); + } + else + { + verticalAscent = /* advanceWidthMax */ hhea.getChar(10) / 2; + verticalDescent = -verticalAscent; + numLongVerticalMetricsEntries = 0; + verticalGlyphMetrics = null; + } + } + + + /** + * Returns the distance from the baseline to the highest ascender. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the maximal ascent, in font units. + */ + public int getAscent(boolean horizontal) + { + return horizontal ? horizontalAscent : verticalAscent; + } + + + /** + * Returns the distance from the baseline to the lowest descender. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the maximal descent, in font units. + */ + public int getDescent(boolean horizontal) + { + return horizontal ? horizontalDescent : verticalDescent; + } + + + /** + * Returns the typographic line gap. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the line gap, in font units. + */ + public int getLineGap(boolean horizontal) + { + return horizontalLineGap; + } + + + /** + * Determines the advance width of a glyph, without considering + * hinting. + * + * @param glyphIndex the index of the glyph whose advance width is + * to be determined. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the advance width, in font units. + */ + public int getAdvanceWidth(int glyphIndex, boolean horizontal) + { + if (!horizontal) + return 0; + + glyphIndex = Math.min(glyphIndex, + numLongHorizontalMetricsEntries - 1); + return horizontalGlyphMetrics.get(glyphIndex << 1); + } + + + /** + * Determines the advance width of a glyph, without considering + * hinting. + * + * @param glyphIndex the index of the glyph whose advance width is + * to be determined. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the advance width, in font units. + */ + public int getAdvanceHeight(int glyphIndex, boolean horizontal) + { + if (horizontal) + return 0; + + /* If a font does not provide vertical glyph metrics, advance + * by the height of one horizontal line. + */ + if (verticalGlyphMetrics == null) + return horizontalAscent - horizontalDescent + horizontalLineGap; + + glyphIndex = Math.min(glyphIndex, + numLongVerticalMetricsEntries - 1); + return verticalGlyphMetrics.get(glyphIndex << 1); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java new file mode 100644 index 0000000..e4d7309 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java @@ -0,0 +1,372 @@ +/* TrueTypeScaler.java -- Font scaler for TrueType outlines. + 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.java.awt.font.opentype.truetype; + +import gnu.java.awt.font.opentype.Scaler; + +import java.awt.FontFormatException; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.nio.ByteBuffer; + + +/** + * A scaler for fonts whose outlines are described in the TrueType + * format. + * + * <p><b>Lack of Thread Safety:</b> Font scalers are intentionally + * <i>not</i> safe to access from multiple concurrent threads. + * Synchronization needs to be performed externally. Usually, the font + * that uses this scaler already has obtained a lock before calling + * the scaler. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class TrueTypeScaler + extends Scaler +{ + /** + * The TrueType or OpenType table that contains the glyph outlines. + */ + private ByteBuffer glyfTable; + + + /** + * A helper object for loading glyph outlines. + */ + private GlyphLoader glyphLoader; + + + /** + * A helper object for measuring the advance width and height of a + * glyph. + */ + private final GlyphMeasurer glyphMeasurer; + + private final Zone glyphZone; + + + /** + * The number of units per em. A typical value is 2048, but some + * font use other numbers as well. + */ + private int unitsPerEm; + + + /** + * Constructs a new TrueTypeScaler. + * + * @param unitsPerEm the number of font units per em. This value can + * be retrieved from the font’s <code>head</code> table. + * + * @param maxp the <code>maxp</code> table of the font, which + * contains various constants needed for setting up the virtual + * machine that interprets TrueType bytecodes. + * + * @param controlValueTable the <code>cvt</code> table of the font, + * which contains the initial values of the control value table. + * + * @param fpgm the <code>fpgm</code> table of the font, which + * contains a font program that is executed exactly once. The + * purpose of the font program is to define functions and to patch + * the interpreter. + * + * @param locaFormat the format of the <code>loca</code> table. The + * value must be 0 for two-byte offsets, or 1 for four-byte + * offsets. TrueType and OpenType fonts indicate the format in the + * <code>indexToLoc</code> field of the <a href= + * "http://partners.adobe.com/asn/tech/type/opentype/head.html" + * >font header</a>. + * + * @param loca the <code>loca</code> table of the font, which + * contains for each glyph the offset of its outline data + * in <code>glyf</code>. + * + * @param glyf the <code>glyf</code> table of the font, which + * contains the outline data for all glyphs in the font. + * + * @param preProgram the <code>prep</code> table of the font, which + * contains a program that is executed whenever the point size or + * the device transform have changed. This program is called + * pre-program because it gets executed before the instructions of + * the individual glyphs. If the font does not contain a + * pre-program, pass <code>null</code>. + * + * @throws FontFormatException if <code>format</code> is neither 0 + * nor 1. + */ + public TrueTypeScaler(int unitsPerEm, + ByteBuffer hhea, + ByteBuffer htmx, + ByteBuffer vhea, + ByteBuffer vtmx, + ByteBuffer maxp, + ByteBuffer controlValueTable, + ByteBuffer fpgm, + int locaFormat, ByteBuffer loca, + ByteBuffer glyf, + ByteBuffer preProgram) + throws FontFormatException + { + int maxContours, maxPoints; + VirtualMachine vm; + + maxContours = Math.max(/* maxContours */ (int) maxp.getChar(8), + /* maxCompositeContours */ (int) maxp.getChar(12)) + + /* fix for some broken fonts */ 8; + maxPoints = Math.max(/* maxPoints */ (int) maxp.getChar(6), + /* maxCompositePoints */ (int) maxp.getChar(10)) + + /* fix for some broken fonts */ 12; + + + glyphZone = new Zone(maxPoints + /* four phantom points */ 4); + this.glyfTable = glyf; + vm = new VirtualMachine(unitsPerEm, maxp, + controlValueTable, fpgm, + preProgram); + + GlyphLocator locator = GlyphLocator.forTable(locaFormat, loca, glyf); + glyphMeasurer = new GlyphMeasurer(hhea, htmx, vhea, vtmx); + glyphLoader = new GlyphLoader(locator, vm, unitsPerEm, + maxContours, maxPoints, + glyphMeasurer); + + this.unitsPerEm = unitsPerEm; + } + + + /** + * Retrieves the scaled outline of a glyph, adjusting control points + * to the raster grid if necessary. + * + * @param glyphIndex the glyph number whose outline is retrieved. + * + * @param pointSize the point size for the glyph. + * + * @param deviceTransform an affine transformation for the device. + * + * @param antialias whether or not the rasterizer will perform + * anti-aliasing on the returned path. + * + * @param fractionalMetrics <code>false</code> for adjusting glyph + * positions to the raster grid of device space. + */ + public GeneralPath getOutline(int glyphIndex, + float pointSize, + AffineTransform deviceTransform, + boolean antialias, + boolean fractionalMetrics) + { + glyphLoader.loadGlyph(glyphIndex, pointSize, deviceTransform, + antialias, glyphZone); + return glyphZone.getPath(); + } + + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width and height is to + * be determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @param advance a point whose <code>x</code> and <code>y</code> + * fields will hold the advance in each direction. It is possible + * that both values are non-zero, for example if + * <code>transform</code> is a rotation, or in the case of Urdu + * fonts. + */ + public void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance) + { + double x, y; + double scaleFactor = (double) pointSize / unitsPerEm; + + /* FIXME: Should grid-fit if needed. Also, use cache if present + * in the font. + */ + advance.setLocation( + scaleFactor * glyphMeasurer.getAdvanceWidth(glyphIndex, horizontal), + scaleFactor * glyphMeasurer.getAdvanceHeight(glyphIndex, horizontal)); + + transform.transform(advance, advance); + } + + + /** + * Scales a value from font units to pixels, given the point size + * and the transform. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. + * + * @param fractionalMetrics <code>true</code> for fractional + * metrics, <code>false</code> for rounding the result to a pixel + * boundary. + * + * @param horizontal <code>true</code> if the <code>funits</code> + * value is along the x axis, <code>false</code> if it is along the + * y axis. + */ + private float scaleFromFUnits(int funits, + float pointSize, + AffineTransform transform, + boolean fractionalMetrics, + boolean horizontal) + { + double s; + + s = (double) pointSize / unitsPerEm; + if (transform != null) + s *= horizontal ? transform.getScaleY() : transform.getScaleX(); + s *= funits; + if (!fractionalMetrics) + s = Math.round(s); + return (float) s; + } + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public float getAscent(float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal) + { + /* Note that the ascent is orthogonal to the direction of line + * layout: If the line direction is horizontal, the measurement of + * ascent is along the vertical axis, and vice versa. + */ + return scaleFromFUnits(glyphMeasurer.getAscent(horizontal), + pointSize, + transform, + fractionalMetrics, + /* reverse */ !horizontal); + } + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + /* Note that the descent is orthogonal to the direction of line + * layout: If the line direction is horizontal, the measurement of + * descent is along the vertical axis, and vice versa. + */ + return scaleFromFUnits(glyphMeasurer.getDescent(horizontal), + pointSize, + transform, + fractionalMetrics, + /* reverse */ !horizontal); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java new file mode 100644 index 0000000..6f53af6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java @@ -0,0 +1,1809 @@ +/* VirtualMachine.java -- Virtual machine for TrueType bytecodes. + 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.java.awt.font.opentype.truetype; + +import java.awt.FontFormatException; +import java.awt.geom.AffineTransform; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + + +/** + * A virtual machine for interpreting TrueType bytecodes. + * + * <p><b>Lack of Thread Safety:</b> The virtual machine is + * intentionally <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the VM. It would be wasteful to acquire + * additional locks for the VM. + * + * <p><b>Implementation Status:</b> The current implementation can + * execute pre-programs of fonts, but it does not yet actually move + * any points. Control flow and arithmeti instructions are + * implemented, but most geometric instructions are not working + * yet. So, the VirtualMachine class is currently a no-op. However, + * not very much is missing. You are more than welcome to complete the + * implementation. + * + * <p><b>Patents:</b> Apple Computer holds three United States Patents + * for the mathematical algorithms that are used by TrueType + * instructions. The monopoly granted by these patents will expire in + * October 2009. Before the expiration date, a license must be + * obtained from Apple Computer to use the patented technology inside + * the United States. For other countries, different dates might + * apply, or no license might be needed. + * + * <p>The default build of this class does not use the patented + * algorithms. If you have obtained a license from Apple, or if the + * patent protection has expired, or if no license is required for + * your contry, you can set a flag in the source file which will + * enable the use of the patented mathematical algorithms.</p> + * + * <p>The relevant patents are listed subsequently.</p> + * + * <p><ol><li>United States Patent 5155805, <i>Method and Apparatus + * for Moving Control Points in Displaying Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 8, 1989. Date of patent: October 13, + * 1992.</li> + * + * <li>United States Patent 5159668, <i>Method and Apparatus for + * Manipulating Outlines in Improving Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 8, 1989. Date of patent: October 27, + * 1992.</li> + * + * <li>United States Patent 5325479, <i>Method and Apparatus for + * Moving Control Points in Displaying Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 28, 1989. Date of patent: June 28, 1994 + * (with a statement that “[t]he portion of the term of this + * patent subsequent to Oct. 13, 2009 has been + * disclaimed”).</li></ol> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +class VirtualMachine +{ + /** + * Indicates whether or not to perform hinting operations that are + * protected by a number of US patents, two of which will expire on + * October 13, 2009, and one of which will expire on October 27, + * 2009. + */ + private final static boolean PATENTED_HINTING = false; + + + /** + * Indicates whether the execution of the Virtual Machine is traced + * to System.out. + */ + private final static boolean TRACE_EXECUTION = false; + + + /** + * The value 1 in 2-dot-14 fixed notation. + */ + private static final short ONE_214 = 0x4000; // 1 << 14 + + + /** + * The storage area of the virtual machine. + */ + private final int[] storage; + + + /** + * The stack. The stack grows from bottom to top, so + * <code>sp[0]</code> gets used before <code>sp[1]</code>. + */ + private int[] stack; + + + /** + * The maximum number of stack elements. + */ + private final int maxStackElements; + + + /** + * The current stack pointer of the virtual machine. + */ + private int sp; + + + /** + * fdefBuffer[i] is the buffer that contains the TrueType + * instructions of function #i. Most of the time, functions are + * defined in the font program, but a font may also re-define + * functions in its CVT program. + */ + private ByteBuffer[] fdefBuffer; + + + /** + * fdefEntryPoint[i] is the position in fdefBuffer[i] where the + * first TrueType instruction after the FDEF is located. + */ + private int[] fdefEntryPoint; + + + /** + * The original Control Value Table, sometimes abbreviated as CVT. + * The table contains signed 16-bit FUnits. Some fonts have no CVT, + * in which case the field will be <code>null</code>. + */ + private ShortBuffer controlValueTable; + + + /** + * The scaled values inside the control value table. + */ + private int[] cvt; + + + /** + * A value that is used by rounding operations to compensate for dot + * gain. + */ + private int engineCompensation = 0; + + + /** + * The contents of the font’s <code>fpgm</code> table, or + * <code>null</code> after the font program has been executed once. + */ + private ByteBuffer fontProgram; + + + /** + * The <code>prep</code> table of the font, which contains a program + * that is executed whenever the point size or the device transform + * have changed. This program is called pre-program because it gets + * executed before the instructions of the individual glyphs. If + * the font does not contain a pre-program, the value of this field + * is <code>null</code>. + */ + private ByteBuffer preProgram; + + + /** + * The number of points in the Twilight Zone. + */ + private int numTwilightPoints; + + + /** + * The current point size of the scaled font. The value is in Fixed + * 26.6 notation. + */ + private int pointSize; // 26.6 + + private AffineTransform deviceTransform; + + private int scaleX, scaleY, shearX, shearY; // 26.6 + + + /** + * Indicates whether or not scan-line conversion will use + * anti-aliasing (with gray levels). Font programs can ask for this + * value with the <code>GETINFO</code> instruction, and some + * programs may behave differently according to this setting. + */ + private boolean antialiased; + + + /* Graphics State. FIXME: Move this to its own class? Some + * documentation would not hurt, either. + */ + private int cvtCutIn; // 26.6 + private int deltaBase; // uint32 + private int deltaShift; // uint32 + private short freeX; // 2.14 + private short freeY; // 2.14 + private int loop; // int + private int minimumDistance; // 26.6 + private short projX; // 2.14 + private short projY; // 2.14 + private short dualX; // 2.14 + private short dualY; // 2.14 + private int rp0, rp1, rp2; // point numbers + private boolean scanControl; + private int scanType; + private int singleWidthValue; // 26.6 + private Zone zp0, zp1, zp2; + + private Zone twilightZone; + private Zone glyphZone; + + + /** + * Indicates whether or not the instructions that are associated + * with individual glyphs shall be executed. Set as a side effect + * of executing the pre-program when the point size, device + * transform or some other relevant parameter have changed. + */ + private boolean executeGlyphInstructions; + + + /** + * Indicates whether to ignore any modifications to the control + * value table that the font’s pre-program might have + * performed. Set as a side effect of executing the pre-program + * when the point size, device transform or some other relevant + * parameter have changed. + */ + private boolean ignoreCVTProgram; + + + /** + * The length of the space between rounded values. A value + * of zero means that rounding has been switched off. + */ + private int roundPeriod; // 26.6 + + + /** + * The offset of the rounded values from multiples of + * <code>roundPeriod</code>. + */ + private int roundPhase; // 26.6 + + + private int roundThreshold; // 26.6 + + + /** + * A cache for the number of pixels per EM. The value is a normal + * integer, not a fixed point notation. + * + * @see #getPixelsPerEM() + */ + private int cachedPixelsPerEM; + + + /** + * The number of font units per EM. + */ + private int unitsPerEm; + + + /** + * Constructs a new Virtual Machine for executing TrueType + * instructions. + * + * @param unitsPerEm the number of font units in one typographic + * em. + * + * @param preProgram the <code>prep</code> table of the font, which + * contains a program that is executed whenever the point size or + * the device transform have changed. This program is called + * pre-program because it gets executed before the instructions of + * the individual glyphs. If the font does not contain a + * pre-program, pass <code>null</code>. + */ + VirtualMachine(int unitsPerEm, + ByteBuffer maxp, + ByteBuffer controlValueTable, + ByteBuffer fontProgram, + ByteBuffer preProgram) + throws FontFormatException + { + int maxStorage, numFunctionDefs, maxInstructionDefs; + + if (maxp.getInt(0) != 0x00010000) + throw new FontFormatException("unsupported maxp version"); + + this.unitsPerEm = unitsPerEm; + maxStorage = maxp.getChar(18); + + /* FreeType says that there exist some broken fonts (like + * "Keystrokes MT") that contain function defs, but have a zero + * value in their maxp table. + */ + numFunctionDefs = maxp.getChar(20); + if (numFunctionDefs == 0) + numFunctionDefs = 64; + fdefBuffer = new ByteBuffer[numFunctionDefs]; + fdefEntryPoint = new int[numFunctionDefs]; + + /* Read the contents of the Control Value Table. */ + if (controlValueTable != null) + this.controlValueTable = controlValueTable.asShortBuffer(); + + maxInstructionDefs = maxp.getChar(22); + maxStackElements = maxp.getChar(24); + storage = new int[maxStorage]; + this.fontProgram = fontProgram; + this.preProgram = preProgram; + numTwilightPoints = maxp.getChar(16); + } + + + /** + * Sets the graphics state to default values. + */ + private void resetGraphicsState() + { + /* The freedom, projection and dual vector default to the x axis. */ + freeX = projX = dualX = ONE_214; + freeY = projY = dualX = 0; + cachedPixelsPerEM = 0; + + cvtCutIn = 68; // 17/16 in 26.6 notation + deltaBase = 9; + deltaShift = 3; + loop = 1; + minimumDistance = Fixed.ONE; + singleWidthValue = 0; + rp0 = rp1 = rp2 = 0; + scanControl = false; + scanType = 2; + zp0 = zp1 = zp2 = getZone(1); + + setRoundingMode(Fixed.ONE, 0x48); // round to grid + } + + + /** + * Reloads the control value table and scales each entry from font + * units to pixel values. + */ + private void reloadControlValueTable() + { + /* Some TrueType fonts have no control value table. */ + if (controlValueTable == null) + return; + + /* Read in the Control Value Table. */ + if (cvt == null) + cvt = new int[controlValueTable.capacity()]; + + /* Scale the entries. */ + for (int i = 0; i < cvt.length; i++) + cvt[i] = funitsToPixels(controlValueTable.get(i)); + } + + + /** + * Scales a value from font unites to pixels. + * + * @return the scaled value. + */ + private int funitsToPixels(int funits) + { + return (int) (((long) funits * scaleY + (unitsPerEm>>1)) + / unitsPerEm); + } + + + /** + * Sets up the virtual machine for the specified parameters. If + * there is no change to the last set-up, the method will quickly + * return. Otherwise, the font’s pre-program will be + * executed. + * + * @param pointSize the point size of the scaled font. + * + * @param deviceTransform an affine transformation which gets + * applied in addition to scaling by <code>pointSize</code>. Font + * programs can separately inquire about the point size. For this + * reason, it is not recommended to pre-multiply the point size to + * the device transformation. + * + * @param antialiased <code>true</code> if the scan-line conversion + * algorithm will use gray levels to give a smoother appearance, + * <code>false</code> otherwise. Font programs can ask for this + * value with the <code>GETINFO</code> instruction, and some + * programs may behave differently according to this setting. + */ + public boolean setup(double pointSize, + AffineTransform deviceTransform, + boolean antialiased) + { + boolean changeCTM; + int pointSize_Fixed; + + if (stack == null) + stack = new int[maxStackElements]; + + if (twilightZone == null) + twilightZone = new Zone(numTwilightPoints); + + /* If the font program has not yet been executed, do so. */ + if (fontProgram != null) + { + resetGraphicsState(); + sp = -1; + execute(fontProgram, 0); + fontProgram = null; // prevent further execution + } + + /* Determine whether the transformation matrix has changed. */ + pointSize_Fixed = Fixed.valueOf(pointSize); + changeCTM = ((pointSize_Fixed != this.pointSize) + || !deviceTransform.equals(this.deviceTransform) + || (antialiased != this.antialiased)); + + if (changeCTM) + { + this.pointSize = pointSize_Fixed; + this.deviceTransform = deviceTransform; + this.antialiased = antialiased; + scaleX = (int) (deviceTransform.getScaleX() * pointSize * 64); + scaleY = (int) (deviceTransform.getScaleY() * pointSize * 64); + shearX = (int) (deviceTransform.getShearX() * pointSize * 64); + shearY = (int) (deviceTransform.getShearY() * pointSize * 64); + + resetGraphicsState(); + reloadControlValueTable(); + executeGlyphInstructions = true; + ignoreCVTProgram = false; + + if (preProgram != null) + { + sp = -1; + execute(preProgram, 0); + if (ignoreCVTProgram) + reloadControlValueTable(); + } + } + + return executeGlyphInstructions; + } + + + /** + * Executes a stream of TrueType instructions. + */ + private void execute(ByteBuffer instructions, int pos) + { + instructions.position(pos); + + // FIXME: SECURITY: Possible denial-of-service attack + // via instructions that have an endless loop. + while (instructions.hasRemaining() + && executeInstruction(instructions)) + ; + } + + + /** + * Writes a textual description of the current TrueType instruction, + * including the top stack elements, to <code>System.out</code>. + * This is useful for debugging. + * + * @param inst the instruction stream, positioned at the current + * instruction. + */ + private void dumpInstruction(ByteBuffer inst) + { + StringBuffer sbuf = new StringBuffer(40); + int pc = inst.position(); + int bcode = inst.get(pc) & 0xff; + int count; + int delta; + + char pcPrefix = 'c'; + for (int i = 0; i < fdefBuffer.length; i++) + { + if (fdefBuffer[i] == inst) + { + pcPrefix = 'f'; + break; + } + } + sbuf.append(pcPrefix); + + + sbuf.append(getHex((short) inst.position())); + sbuf.append(": "); + sbuf.append(getHex((byte) bcode)); + sbuf.append(" "); + sbuf.append(INST_NAME[bcode]); + + if (bcode == 0x40) // NPUSHB + { + count = inst.get(pc + 1) & 0xff; + sbuf.append(" ("); + sbuf.append(count); + sbuf.append(") "); + for (int i = 0; i < count; i++) + { + if (i > 0) + sbuf.append(" "); + sbuf.append('$'); + sbuf.append(getHex(inst.get(pc + 2 + i))); + } + } + if (bcode == 0x41) // NPUSHW + { + count = inst.get(pc + 1) & 0xff; + sbuf.append(" ("); + sbuf.append(count); + sbuf.append(") "); + for (int i = 0; i < count; i++) + { + if (i > 0) + sbuf.append(' '); + sbuf.append('$'); + sbuf.append(getHex(inst.getShort(pc + 2 + 2*i))); + } + } + else + { + count = getInstructionLength(bcode) - 1; + for (int i = 0; i < count; i++) + { + sbuf.append(" $"); + sbuf.append(getHex(inst.get(pc + 1 + i))); + } + } + + while (sbuf.length() < 30) + sbuf.append(' '); + sbuf.append('|'); + sbuf.append(sp + 1); + sbuf.append("| "); + for (int i = sp; i >= Math.max(0, sp - 5); i = i - 1) + { + if (i < sp) + sbuf.append(" "); + if ((stack[i] >> 16) != 0) + sbuf.append(getHex((short) (stack[i] >> 16))); + sbuf.append(getHex((short) stack[i])); + } + System.out.println(sbuf); + } + + + private static char getNibble(int i, int rightShift) + { + i = (i >> rightShift) & 15; + if (i < 10) + return (char) (i + '0'); + else + return (char) (i + 'a' - 10); + } + + + private static String getHex(byte b) + { + char[] a = new char[2]; + a[0] = getNibble(b, 4); + a[1] = getNibble(b, 0); + return new String(a); + } + + + private static String getHex(short b) + { + char[] a = new char[4]; + a[0] = getNibble(b, 12); + a[1] = getNibble(b, 8); + a[2] = getNibble(b, 4); + a[3] = getNibble(b, 0); + return new String(a); + } + + + /** + * Skips any instructions until the specified opcode has been + * encoutered. + * + * @param inst the current instruction stream. After the call, + * the position of <code>inst</code> is right after the first + * occurence of <code>opcode</code>. + * + * @param opcode1 the opcode for which to look. + * + * @param opcode2 another opcode for which to look. Pass -1 + * if only <code>opcode1</code> would terminate skipping. + * + * @param illegalCode1 an opcode that must not be encountered + * while skipping. Pass -1 if any opcode is acceptable. + * + * @param illegalCode2 another opcode that must not be encountered + * while skipping. Pass -1 to perform no check. + * + * @param handleNestedIfClauses <code>true</code> to handle + * nested <code>IF [ELSE] EIF</code> clauses, <code>false</code> + * to ignore them. From the TrueType specification document, + * one would think that nested if clauses would not be valid, + * but they do appear in some fonts. + * + * @throws IllegalStateException if <code>illegalCode1</code> or + * <code>illegalCode2</code> has been encountered while skipping. + */ + private static void skipAfter(ByteBuffer inst, + int opcode1, int opcode2, + int illegalCode1, int illegalCode2, + boolean handleNestedIfClauses) + { + int pos = inst.position(); + int curOpcode; + int instLen; + int nestingLevel = 0; // increased inside IF [ELSE] EIF sequences + + while (true) + { + curOpcode = inst.get(pos) & 0xff; + instLen = getInstructionLength(curOpcode); + + if (false && TRACE_EXECUTION) + { + for (int i = 0; i < nestingLevel; i++) + System.out.print("--"); + System.out.print("--" + pos + "-" + INST_NAME[curOpcode]); + if (nestingLevel > 0) + System.out.print(", ifNestingLevel=" + nestingLevel); + System.out.println(); + } + + if (curOpcode == 0x40) // NPUSHB + pos += 1 + (inst.get(pos + 1) & 0xff); + else if (curOpcode == 0x41) // NPUSHW + pos += 1 + 2 * (inst.get(pos + 1) & 0xff); + else + pos += instLen; + + if ((nestingLevel == 0) + && ((curOpcode == opcode1) || (curOpcode == opcode2))) + break; + + if (handleNestedIfClauses) + { + if (curOpcode == /* IF */ 0x58) + ++nestingLevel; + else if (curOpcode == /* EIF */ 0x59) + --nestingLevel; + } + + if ((nestingLevel < 0) + || (curOpcode == illegalCode1) + || (curOpcode == illegalCode2)) + throw new IllegalStateException(); + } + + inst.position(pos); + } + + + /** + * Returns the number of bytes that a TrueType instruction occupies. + * + * @param opcode the instruction. + * + * @return the number of bytes occupied by the instructions and its + * operands. For <code>NPUSHB</code> and <code>NPUSHW</code>, where + * the instruction length depends on the first operand byte, the + * result is -1. + */ + private static int getInstructionLength(int opcode) + { + /* NPUSHB, NPUSHW --> see following byte */ + if ((opcode == 0x40) || (opcode == 0x41)) + return -1; + + /* PUSHB[0] .. PUSHB[7] --> 2, 3, 4, 5, 6, 7, 8, 9 */ + if ((opcode >= 0xb0) && (opcode <= 0xb7)) + return opcode - 0xae; + + /* PUSHW[0] .. PUSHW[7] --> 3, 5, 6, 7, 11, 13, 15, 17*/ + if ((opcode >= 0xb8) && (opcode <= 0xbf)) + return 1 + ((opcode - 0xb7) << 1); + + return 1; + } + + + /** + * Executes a single TrueType instruction. This is the core + * routine of the Virtual Machine. + * + * @return <code>true</code> if another instruction shall be + * executed in the same call frame; <code>false</code> if the + * current call frame shall be popped. + */ + private boolean executeInstruction(ByteBuffer inst) + { + if (TRACE_EXECUTION) + dumpInstruction(inst); + + int i, count, e1, e2, e3, e4, x, y; + int bcode = inst.get() & 0xff; + + switch (bcode) + { + case 0x00: // SVTCA[0], Set freedom and proj. Vectors To Coord. Axis [y] + setFreedomVector((short) 0, ONE_214); + setProjectionVector((short) 0, ONE_214); + break; + + case 0x01: // SVTCA[1], Set freedom and proj. Vectors To Coord. Axis [x] + setFreedomVector(ONE_214, (short) 0); + setProjectionVector(ONE_214, (short) 0); + break; + + case 0x02: // SPVTCA[0], Set Projection Vector To Coordinate Axis [y] + setProjectionVector((short) 0, ONE_214); + break; + + case 0x03: // SPVTCA[1], Set Projection Vector To Coordinate Axis [x] + setProjectionVector(ONE_214, (short) 0); + break; + + case 0x0c: // GPV, Get Projection Vector + stack[++sp] = projX; + stack[++sp] = projY; + break; + + case 0x0d: // GPV, Get Freedom Vector + stack[++sp] = freeX; + stack[++sp] = freeY; + break; + + case 0x0F: // ISECT, move point p to the InterSECTION of two lines + sp -= 4; + handleISECT(stack[sp], stack[sp+1], stack[sp+2], + stack[sp+3], stack[sp+4]); + break; + + case 0x10: // SRP0, Set Reference Point 0 + rp0 = stack[sp--]; + break; + + case 0x11: // SRP1, Set Reference Point 1 + rp1 = stack[sp--]; + break; + + case 0x12: // SRP2, Set Reference Point 2 + rp2 = stack[sp--]; + break; + + case 0x13: // SZP0, Set Zone Pointer 0 + zp0 = getZone(stack[sp--]); + break; + + case 0x14: // SZP1, Set Zone Pointer 1 + zp1 = getZone(stack[sp--]); + break; + + case 0x15: // SZP2, Set Zone Pointer 2 + zp2 = getZone(stack[sp--]); + break; + + case 0x16: // SZPS, Set Zone PointerS + zp0 = zp1 = zp2 = getZone(stack[sp--]); + break; + + case 0x17: // SLOOP, Set LOOP variable + loop = stack[sp--]; + break; + + case 0x18: // RTG, Round To Grid + setRoundingMode(Fixed.ONE, 0x48); + break; + + case 0x19: // RTHG, Round To Half Grid + setRoundingMode(Fixed.ONE, 0x68); + break; + + case 0x1a: // SMD, Set Minimum Distance + minimumDistance = stack[sp--]; + break; + + case 0x1B: // ELSE, ELSE clause + skipAfter(inst, + /* look for: EIF, -- */ 0x59, -1, + /* illegal: --, -- */ -1, -1, + /* handle nested if clauses */ true); + break; + + case 0x1C: // JMPR, JuMP Relative + inst.position(inst.position() - 1 + stack[sp--]); + break; + + case 0x1D: // SCVTCI, Set Control Value Table Cut-In + cvtCutIn = stack[sp--]; + break; + + case 0x1F: // SSW, Set Single Width + singleWidthValue = stack[sp--]; + break; + + case 0x20: // DUP, DUPlicate top stack element + e1 = stack[sp]; + stack[++sp] = e1; + break; + + case 0x21: // POP, POP top stack element + sp--; + break; + + case 0x22: // CLEAR, CLEAR the stack + sp = -1; + break; + + case 0x23: // SWAP, SWAP the top two elements on the stack + e1 = stack[sp--]; + e2 = stack[sp]; + stack[sp] = e1; + stack[++sp] = e2; + break; + + case 0x24: // DEPTH, DEPTH of the stack + stack[++sp] = sp + 1; + break; + + case 0x25: // CINDEX, Copy the INDEXed element to the top of the stack + stack[sp] = stack[sp - stack[sp]]; + break; + + case 0x26: // MINDEX, Move the INDEXed element to the top of the stack + i = stack[sp]; + e1 = stack[sp - i]; + System.arraycopy(/* src */ stack, /* srcPos */ sp - i + 1, + /* dest */ stack, /* destPos*/ sp - i, + /* length */ i - 1); + --sp; + stack[sp] = e1; + break; + + case 0x2a: // LOOPCALL, LOOP and CALL function + i = stack[sp--]; + count = stack[sp--]; + e1 = inst.position(); + e2 = sp; + for (int j = 0; j < count; j++) + execute(fdefBuffer[i], fdefEntryPoint[i]); + inst.position(e1); + break; + + case 0x2B: // CALL, CALL function + i = stack[sp--]; + e1 = inst.position(); + e2 = sp; + execute(fdefBuffer[i], fdefEntryPoint[i]); + inst.position(e1); + break; + + case 0x2C: // FDEF, Function DEFinition + i = stack[sp--]; + fdefBuffer[i] = inst; + fdefEntryPoint[i] = inst.position(); + skipAfter(inst, + /* look for: ENDF */ 0x2d, + /* look for: --- */ -1, + /* illegal: IDEF */ 0x89, + /* illegal: FDEF */ 0x2c, + /* do not handle nested if clauses */ false); + break; + + case 0x2D: // ENDF, END Function definition + /* Pop the current stack frame. */ + return false; + + case 0x2e: // MDAP[0], Move Direct Absolute Point + handleMDAP(stack[sp--], /* round */ false); + break; + + case 0x2f: // MDAP[1], Move Direct Absolute Point + handleMDAP(stack[sp--], /* round */ true); + break; + + case 0x39: // IP, Interpolate Point by the last relative stretch + handleIP(); + break; + + case 0x3d: // RTDG, Round To Double Grid + setRoundingMode(Fixed.ONE, 0x08); + roundThreshold = roundThreshold / 64; // period/128 + break; + + case 0x3e: // MIAP[0], Move Indirect Absolute Point + e1 = stack[sp--]; + handleMIAP(e1, stack[sp--], /* round */ false); + break; + + case 0x3f: // MIAP[1], Move Indirect Absolute Point + e1 = stack[sp--]; + handleMIAP(e1, stack[sp--], /* round */ true); + break; + + case 0x40: // NPUSHB + count = inst.get() & 0xff; + for (i = 0; i < count; i++) + stack[++sp] = inst.get() & 0xff; + break; + + case 0x41: // NPUSHW + count = inst.get() & 0xff; + for (i = 0; i < count; i++) + stack[++sp] = inst.getShort(); + break; + + case 0x42: // WS, Write Store + e1 = stack[sp--]; i = stack[sp--]; + storage[i] = e1; + break; + + case 0x43: // RS, Read Store + stack[sp] = storage[stack[sp]]; + break; + + case 0x44: // WCVTP, Write Control Value Table in Pixel units + e1 = stack[sp--]; + i = stack[sp--]; + if (i < cvt.length) + cvt[i] = e1; + break; + + case 0x45: // RCVT, Read Control Value Table entry + if (stack[sp] < cvt.length) + stack[sp] = cvt[stack[sp]]; + else + stack[sp] = 0; + break; + + case 0x46: // GC[0], Get Coordinate projected onto the projection vector + stack[sp] = getProjection(zp2, stack[sp]); + break; + + case 0x47: // GC[1], Get Coordinate projected onto the projection vector + stack[sp] = getOriginalProjection(zp2, stack[sp]); + break; + + case 0x4B: // MPPEM, Measure Pixels Per EM + stack[++sp] = getPixelsPerEM(); + break; + + case 0x4c: // MPS, Measure Point Size + /* FreeType2 returns pixels per em here, because they think that + * the point size would be irrelevant in a given font program. + * This is extremely surprising, because the appearance of good + * fonts _should_ change with point size. For example, a good + * font should be wider at small point sizes, and the holes + * inside glyphs ("Punzen" in German, I do not know the correct + * English expression) should be larger. Note that this change + * of appearance is dependent on point size, _not_ the + * resolution of the display device. + */ + stack[++sp] = pointSize; + break; + + case 0x4f: // DEBUG, DEBUG call + sp--; + break; + + case 0x50: // LT, Less Than + e1 = stack[sp--]; + stack[sp] = (stack[sp] < e1) ? 1 : 0; + break; + + case 0x51: // LTEQ, Greater Than or EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] <= e1) ? 1 : 0; + break; + + case 0x52: // GT, Greater Than + e1 = stack[sp--]; + stack[sp] = (stack[sp] > e1) ? 1 : 0; + break; + + case 0x53: // GTEQ, Greater Than or EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] >= e1) ? 1 : 0; + break; + + case 0x54: // EQ, EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] == e1) ? 1 : 0; + break; + + case 0x55: // NEQ, Not EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] != e1) ? 1 : 0; + break; + + case 0x58: // IF, IF test + if (stack[sp--] == 0) + skipAfter(inst, + /* look for: ELSE */ 0x1B, + /* look for: EIF */ 0x59, + /* illegal: -- */ -1, + /* illegal: -- */ -1, + /* handle nested if clauses */ true); + break; + + case 0x59: // EIF, End IF + // Do nothing. + break; + + case 0x5A: // AND + e1 = stack[sp--]; + stack[sp] = ((e1 != 0) && (stack[sp] != 0)) ? 1 : 0; + break; + + case 0x5B: // OR + e1 = stack[sp--]; + stack[sp] = ((e1 != 0) || (stack[sp] != 0)) ? 1 : 0; + break; + + case 0x5e: // SDB, Set Delta Base in the graphics state + deltaBase = stack[sp--]; + break; + + case 0x5f: // SDS, Set Delta Shift in the graphics state + deltaShift = stack[sp--]; + break; + + case 0x60: // ADD + e1 = stack[sp--]; + stack[sp] += e1; + break; + + case 0x61: // SUB, SUBtract + e1 = stack[sp--]; + stack[sp] -= e1; + break; + + case 0x62: // DIV, DIVide + e1 = stack[sp--]; + stack[sp] = Fixed.div(e1, stack[sp]); + break; + + case 0x63: // MUL, MULtiply + e1 = stack[sp--]; + stack[sp] = Fixed.mul(e1, stack[sp]); + break; + + case 0x64: // ABS, ABSolute value + stack[sp] = Math.abs(stack[sp]); + break; + + case 0x65: // NEG, NEGate + stack[sp] = -stack[sp]; + break; + + case 0x66: // FLOOR + stack[sp] = Fixed.floor(stack[sp]); + break; + + case 0x67: // CEILING + stack[sp] = Fixed.ceil(stack[sp]); + break; + + case 0x68: // ROUND[0] -- round grey distance + stack[sp] = round(stack[sp], /* no engine compensation */ 0); + break; + + case 0x69: // ROUND[1] -- round black distance + stack[sp] = round(stack[sp], -engineCompensation); + break; + + case 0x6a: // ROUND[2] -- round white distance + stack[sp] = round(stack[sp], engineCompensation); + break; + + case 0x6b: // ROUND[3] -- round distance (not yet defined) + stack[sp] = round(stack[sp], /* no engine compensation */ 0); + break; + + case 0x6c: // NROUND[0] -- compensate grey distance + stack[sp] = nround(stack[sp], 0); + break; + + case 0x6d: // NROUND[1] -- compensate black distance + stack[sp] = nround(stack[sp], -engineCompensation); + break; + + case 0x6e: // NROUND[2] -- compensate white distance + stack[sp] = nround(stack[sp], engineCompensation); + break; + + case 0x6f: // NROUND[3] -- compensate distance (not yet defined) + stack[sp] = nround(stack[sp], 0); + break; + + case 0x70: // WCVTF, Write Control Value Table in Funits + e1 = stack[sp--]; + cvt[stack[sp--]] = e1 * getPixelsPerEM(); + break; + + case 0x73: // DELTAC1, DELTA exception C1 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 0); + break; + + case 0x74: // DELTAC2, DELTA exception C2 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 16); + break; + + case 0x75: // DELTAC3, DELTA exception C3 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 32); + break; + + case 0x76: // SROUND, Super ROUND + setRoundingMode(Fixed.ONE, stack[sp--]); + break; + + case 0x77: // S45ROUND, Super ROUND 45 degrees + setRoundingMode(/* sqrt(2)/2 */ 0x2d, stack[sp--]); + break; + + case 0x78: // JROT, Jump Relative On True + e1 = stack[sp--]; + i = inst.position() - 1 + stack[sp--]; + if (e1 != 0) + inst.position(i); + break; + + case 0x79: // JROF, Jump Relative On False + e1 = stack[sp--]; + i = inst.position() - 1 + stack[sp--]; + if (e1 == 0) + inst.position(i); + break; + + case 0x7a: // ROFF, Round OFF + roundPeriod = 0; + break; + + case 0x7c: // RUTG, Round Up To Grid + setRoundingMode(Fixed.ONE, 0x40); + break; + + case 0x7d: // RDTG, Round Down To Grid + setRoundingMode(Fixed.ONE, 0x40); + roundThreshold = 0; + break; + + case 0x7e: // SANGW, Set ANGle Weight (no-op according to TrueType spec) + case 0x7f: // AA, Adjust Angle (no-op according to TrueType spec) + sp--; + break; + + case 0x85: // SCANCTRL, SCAN conversion ConTRoL + e1 = stack[sp--]; + int ppemThreshold = e1 & 255; + scanControl = false; + boolean ppemCondition = (ppemThreshold == 255) + || ((ppemThreshold != 0) && (getPixelsPerEM() > ppemThreshold)); + if (((e1 & (1<<8)) != 0) && ppemCondition) + scanControl = true; + if (((e1 & (1<<9)) != 0) && isRotated()) + scanControl = true; + if (((e1 & (1<<10)) != 0) && isStretched()) + scanControl = true; + if (((e1 & (1<<11)) != 0) && !ppemCondition) + scanControl = false; + if (((e1 & (1<<12)) != 0) && !isRotated()) + scanControl = false; + if (((e1 & (1<<13)) != 0) && !isStretched()) + scanControl = false; + break; + + case 0x88: // GETINFO, GET INFOrmation + e1 = 0; + if ((stack[sp] & 1) != 0) // ask for rasterizer version + e1 |= 35; // "Microsoft Rasterizer version 1.7" (grayscale-capable) + if (((stack[sp] & 2) != 0) && isRotated()) + e1 |= 1 << 8; // bit 8: glyph has been rotated + if (((stack[sp] & 4) != 0) && isStretched()) + e1 |= 1 << 9; // bit 9: glyph has been stretched + if (((stack[sp] & 32) != 0) && antialiased) + e1 |= 1 << 12; // bit 12: antialiasing is active + stack[sp] = e1; + break; + + case 0x8a: // ROLL, ROLL the top three stack elements + e1 = stack[sp - 2]; + stack[sp - 2] = stack[sp - 1]; + stack[sp - 1] = stack[sp]; + stack[sp] = e1; + break; + + case 0x8b: // MAX, MAXimum of top two stack elements + e1 = stack[sp--]; + stack[sp] = Math.max(e1, stack[sp]); + break; + + case 0x8c: // MIN, MINimum of top two stack elements + e1 = stack[sp--]; + stack[sp] = Math.min(e1, stack[sp]); + break; + + case 0x8d: // SCANTYPE + scanType = stack[sp--]; + break; + + case 0x8e: // INSTCTRL, INSTRuction execution ConTRoL + e1 = stack[sp--]; // selector + e2 = stack[sp--]; // value + switch (e1) + { + case 1: + executeGlyphInstructions = (e2 == 0); + break; + + case 2: + ignoreCVTProgram = (e2 != 0); + break; + } + break; + + case 0xb0: // PUSHB[0] + case 0xb1: // PUSHB[1] + case 0xb2: // PUSHB[2] + case 0xb3: // PUSHB[3] + case 0xb4: // PUSHB[4] + case 0xb5: // PUSHB[5] + case 0xb6: // PUSHB[6] + case 0xb7: // PUSHB[7] + count = bcode - 0xb0 + 1; + for (i = 0; i < count; i++) + stack[++sp] = inst.get() & 0xff; + break; + + case 0xb8: // PUSHW[0] + case 0xb9: // PUSHW[1] + case 0xba: // PUSHW[2] + case 0xbb: // PUSHW[3] + case 0xbc: // PUSHW[4] + case 0xbd: // PUSHW[5] + case 0xbe: // PUSHW[6] + case 0xbf: // PUSHW[7] + count = bcode - 0xb8 + 1; + for (i = 0; i < count; i++) + stack[++sp] = inst.getShort(); + break; + + // MIRPxxxx, Move Indirect Relative Point + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + e1 = stack[sp--]; + handleMIRP(bcode, /* point */ e1, /* cvtIndex */ stack[sp--]); + break; + + default: + throw new IllegalStateException(); + } + + return true; + } + + + /** + * Sets the rounding mode. + * + * @param period the grid period in fixed-point notation, such as + * {@link Fixed#ONE} for the <code>SROUND</code> instruction or + * <code>sqrt(2)/2</code> for the <code>S45ROUND</code> instruction. + * + * @param mode a byte whose bits are set according to the TrueType + * specification for SROUND and S45ROUND parameters. + */ + private void setRoundingMode(int period, int mode) + { + /* Set the period. */ + switch ((mode & 0xc0) >> 6) + { + case 0: + roundPeriod = period / 2; + break; + + case 2: + roundPeriod = period * 2; + break; + + default: + roundPeriod = period; + break; + } + + /* Set the phase. */ + switch ((mode & 0x30) >> 4) + { + case 0: + roundPhase = 0; + break; + + case 1: + roundPhase = roundPeriod >> 2; // period/4 + break; + + case 2: + roundPhase = roundPeriod >> 1; // period/2 + break; + + case 3: + roundPhase = (roundPeriod >> 1) + (roundPeriod >> 2); // period * 3/4 + break; + } + + /* Set the threshold. */ + int threshold = mode & 0x0f; + if (threshold == 0) + roundThreshold = roundPeriod - Fixed.ONE; + else + roundThreshold = ((threshold - 4) * roundPeriod) / 8; + } + + + + /** + * Implements the DELTAC instructions. These instructions check + * whether the current number of pixels per em is contained in an + * exception table. If it is, a delta value is determined, and the + * specified entry in the Control Value Table is modified according + * to the delta. + * + * @param pairs the delta table. Because the delta table is on + * the stack, callers usually just want to pass the stack array. + * + * @param offset the offset of the first pair in <code>pairs</code>. + * + * @param numPairs the number of pairs. + * + * @param base 0 for <code>DELTAC1</code>, 16 for <code>DELTAC2</code>, + * or 32 for <code>DELTAC2</code>. + * + * @see <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC1" + * >Apple’s documentation for <code>DELTAC1</code></a>, <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC2" + * ><code>DELTAC2</code></a>, and <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC3" + * ><code>DELTAC3</code></a> + */ + private void deltaC(int[] pairs, int offset, int numPairs, int base) + { + int arg, relativePpem; + int ppemTrigger = getPixelsPerEM() - (deltaBase + base); + int delta, cvtIndex, rightShift; + for (int i = 0; i < numPairs; i++) + { + arg = pairs[offset + 2 * i]; + relativePpem = (arg >> 4) & 15; + if (relativePpem == ppemTrigger) + { + delta = (arg & 15) - 8; + if (delta >= 0) + ++delta; + + rightShift = deltaShift - 6; + if (rightShift > 0) + delta = delta >> rightShift; + else if (rightShift < 0) + delta = delta << (-rightShift); + cvt[pairs[offset + 2 * i + 1]] += delta; + + break; + } + } + } + + + private Zone getZone(int zoneNumber) + { + return (zoneNumber == 0) ? twilightZone : glyphZone; + } + + + /** + * Projects the specified vector along the current projection + * vector. + * + * @param x the x component of the input vector, in 26.6 fixed-point + * notation. + * + * @param y the y component of the input vector, in 26.6 fixed-point + * notation. + * + * @return the projected distance, in 26.6 fixed-point notation. + */ + private int getProjection(int x, int y) + { + return (int) (((((long) x) * projX + ((long) y) * projY)) >> 14); + } + + + /** + * Projects the specified vector along the current dual projection + * vector. + * + * @param x the x component of the input vector, in 26.6 fixed-point + * notation. + * + * @param y the y component of the input vector, in 26.6 fixed-point + * notation. + * + * @return the projected distance, in 26.6 fixed-point notation. + */ + private int getDualProjection(int x, int y) + { + return (int) (((((long) x) * dualX + ((long) y) * dualY)) >> 14); + } + + + private int getProjection(Zone zone, int point) + { + return getProjection(zone.getX(point), zone.getY(point)); + } + + + private int getOriginalProjection(Zone zone, int point) + { + return getDualProjection(zone.getOriginalX(point), + zone.getOriginalY(point)); + } + + + private void handleISECT(int a0, int a1, int b0, int b1, int p) + { + System.out.println("FIXME: Unimplemented ISECT " + p); + } + + + private static int muldiv(int a, int b, int c) + { + int s; + s = a; a = Math.abs(a); + s ^= b; b = Math.abs(b); + s ^= c; c = Math.abs(c); + a = (int) ((((long) a) * b + (c>>1)) / c); + return (s < 0) ? -a : a; + } + + + private int getFreeDotProj() + { + int result; + + result = ((((int) projX) * freeX) << 2) + + ((((int) projY) * freeY) << 2); + + /* FIXME: This seems somewhat bogus. Need to contact the + * developers of FreeType. + */ + if (Math.abs(result) < 0x4000000) + result = 0x40000000; + return result; + } + + + private void movePoint(Zone zone, int point, int distance) + { + int freeDotProj = getFreeDotProj(); + int c; + + if (freeX != 0) + { + c = zone.getX(point); + c += muldiv(distance, freeX << 16, freeDotProj); + zone.setX(point, c, /* touch */ true); + } + + if (freeY != 0) + { + c = zone.getY(point); + c += muldiv(distance, freeY << 16, freeDotProj); + zone.setY(point, c, /* touch */ true); + } + + if (TRACE_EXECUTION) + { + System.out.println("point[" + point + "] moved to " + + Fixed.toString(zone.getX(point), + zone.getY(point))); + dumpVectors(); + } + } + + private void dumpVectors() + { + System.out.println(" proj=" + Fixed.toString(projX>>8, projY>>8) + + ", free=" + Fixed.toString(freeX>>8, freeY>>8)); + } + + + private void handleIP() + { + // Implementation taken from FreeType. + int p, org_a, org_b, org_x, cur_a, cur_b, cur_x, distance; + int freeDotProj; + + org_a = getOriginalProjection(zp0, rp1); + cur_a = getProjection(zp0, rp1); + + org_b = getOriginalProjection(zp1, rp2); + cur_b = getProjection(zp1, rp2); + + while (--loop >= 0) + { + p = stack[sp--]; + org_x = getOriginalProjection(zp2, p); + cur_x = getProjection(zp2, p); + + if (((org_a <= org_b) && (org_x <= org_a)) + || ((org_a > org_b) && (org_x >= org_a))) + distance = (cur_a - org_a) + (org_x - cur_x); + else if (((org_a <= org_b) && (org_x >= org_b)) + || ((org_a > org_b) && (org_x < org_b))) + distance = (cur_b - org_b) + (org_x - cur_x); + else + distance = muldiv(cur_b - cur_a, org_x - org_a, org_b - org_a) + + (cur_a - cur_x); + movePoint(zp2, p, distance); + } + loop = 1; + } + + + private void handleMDAP(int point, boolean round) + { + System.out.println("FIXME: Unimplemented MDAP: point " + + point + "/" + zp0); + } + + + private void handleMIAP(int cvtIndex, int point, boolean round) + { + int previousPos, pos; + + previousPos = getProjection(zp0, point); + pos = cvt[cvtIndex]; + + if (round) + { + if (Math.abs(pos - previousPos) > cvtCutIn) + pos = previousPos; + pos = round(pos, /* no engine compensation */ 0); + } + movePoint(zp0, point, pos - previousPos); + rp0 = rp1 = point; + } + + + private void handleMIRP(int bcode, int point, int cvtIndex) + { + System.out.println("FIXME: Unimplemented mirp " + point + ", " + cvtIndex); + } + + + + private int round(int distance, int compensation) + { + int result; + + if (roundPeriod == 0) + return nround(distance, compensation); + + if (distance >= 0) + { + result = distance + compensation - roundPhase + roundThreshold; + result &= -roundPeriod; // truncate to the next lowest periodic value + return Math.max(result, 0) + roundPhase; + } + else + { + result = compensation - roundPhase + roundThreshold - distance; + result &= -roundPeriod; + return Math.max(-result, 0) - roundPhase; + } + } + + + private static int nround(int distance, int compensation) + { + if (distance >= 0) + return Math.max(distance + compensation, 0); + else + return Math.min(distance - compensation, 0); + } + + + /** + * Determines whether the current glyph is rotated. + * + * @return <code>false</code> if the shearing factors for the + * <i>x</i> and <i>y</i> axes are zero; <code>true</code> if they + * are non-zero. + */ + private boolean isRotated() + { + return (shearX != 0) || (shearY != 0); + } + + + /** + * Determines whether the current glyph is stretched. + * + * @return <code>false</code> if the scaling factors for the + * <i>x</i> and <i>y</i> axes are are equal; <code>true</code> if + * they differ. + */ + private boolean isStretched() + { + return scaleX != scaleY; + } + + + /** + * Returns how many pixels there are per EM, in direction of the + * current projection vector. The result is a normal integer, + * not a Fixed. + */ + private int getPixelsPerEM() + { + if (cachedPixelsPerEM == 0) + { + cachedPixelsPerEM = Fixed.intValue(Fixed.vectorLength( + applyCTM_x(projX >> 8, projY >> 8), + applyCTM_y(projX >> 8, projY >> 8))); + } + + return cachedPixelsPerEM; + } + + + private void setProjectionVector(short x, short y) + { + if (PATENTED_HINTING) + { + if ((x != projX) || (y != projY)) + cachedPixelsPerEM = 0; + + projX = x; + projY = y; + } + } + + + private void setFreedomVector(short x, short y) + { + if (PATENTED_HINTING) + { + freeX = x; + freeY = y; + } + } + + + private void setDualVector(short x, short y) + { + if (PATENTED_HINTING) + { + dualX = x; + dualY = y; + } + } + + + private int applyCTM_x(int x, int y) + { + return (int) (((long) scaleX * x + (long) shearX * y) >> 6); + } + + private int applyCTM_y(int x, int y) + { + return (int) (((long) shearY * x + (long) scaleY * y) >> 6); + } + + + private static final String[] INST_NAME = + { + /* 00 */ "SVTCA[0]", "SVTCA[1]", "SPVTCA[0]", "SPVTCA[1]", + /* 04 */ "INST_04", "INST_05", "INST_06", "INST_07", + /* 08 */ "INST_08", "INST_09", "INST_0A", "INST_0B", + /* 0c */ "GPV", "GFV", "INST_0E", "ISECT", + /* 10 */ "SRP0", "SRP1", "SRP2", "SZP0", + /* 14 */ "SZP1", "SZP2", "SZPS", "SLOOP", + /* 18 */ "RTG", "RTHG", "SMD", "ELSE", + /* 1c */ "JMPR", "SCVTCI", "INST_1E", "SSW", + /* 20 */ "DUP", "POP", "CLEAR", "SWAP", + /* 24 */ "DEPTH", "CINDEX", "MINDEX", "INST_27", + /* 28 */ "INST_28", "INST_29", "LOOPCALL", "CALL", + /* 2c */ "FDEF", "ENDF", "MDAP[0]", "MDAP[1]", + /* 30 */ "IUP[0]", "IUP[1]", "SHP[0]", "SHP[1]", + /* 34 */ "INST_34", "INST_35", "INST_36", "INST_37", + /* 38 */ "INST_38", "IP", "INST_3A", "INST_3B", + /* 3c */ "INST_3C", "RTDG", "MIAP[0]", "MIAP[1]", + /* 40 */ "NPUSHB", "NPUSHW", "WS", "RS", + /* 44 */ "WCVTP", "RCVT", "GC[0]", "GC[1]", + /* 48 */ "INST_48", "INST_49", "INST_4A", "MPPEM", + /* 4c */ "MPS", "FLIPON", "FLIPOFF", "DEBUG", + /* 50 */ "LT", "LTEQ", "GT", "GTEQ", + /* 54 */ "EQ", "NEQ", "INST_56", "INST_57", + /* 58 */ "IF", "EIF", "AND", "OR", + /* 5c */ "INST_5C", "INST_5D", "SDB", "SDS", + /* 60 */ "ADD", "SUB", "DIV", "MUL", + /* 64 */ "ABS", "NEG", "FLOOR", "CEILING", + /* 68 */ "ROUND[0]", "ROUND[1]", "ROUND[2]", "ROUND[3]", + /* 6c */ "NROUND[0]", "NROUND[1]", "NROUND[2]", "NROUND[3]", + /* 70 */ "WCVTF", "INST_71", "INST_72", "DELTAC1", + /* 74 */ "DELTAC2", "DELTAC3", "SROUND", "S45ROUND", + /* 78 */ "JROT", "JROF", "ROFF", "INST_7B", + /* 7c */ "RUTG", "RDTG", "SANGW", "AA", + /* 80 */ "FLIPPT", "FLIPRGON", "FLIPRGOFF", "INST_83", + /* 84 */ "INST_84", "SCANCTRL", "INST_86", "INST_87", + /* 88 */ "GETINFO", "INST_89", "ROLL", "MAX", + /* 8c */ "MIN", "SCANTYPE", "INSTCTRL", "INST_8F", + /* 90 */ "INST_90", "INST_91", "INST_92", "INST_93", + /* 94 */ "INST_94", "INST_95", "INST_96", "INST_97", + /* 98 */ "INST_98", "INST_99", "INST_9A", "INST_9B", + /* 9c */ "INST_9C", "INST_9D", "INST_9E", "INST_9F", + /* a0 */ "INST_A0", "INST_A1", "INST_A2", "INST_A3", + /* a4 */ "INST_A4", "INST_A5", "INST_A6", "INST_A7", + /* a8 */ "INST_A8", "INST_A9", "INST_AA", "INST_AB", + /* ac */ "INST_AC", "INST_AD", "INST_AE", "INST_AF", + /* b0 */ "PUSHB[0]", "PUSHB[1]", "PUSHB[2]", "PUSHB[3]", + /* b4 */ "PUSHB[4]", "PUSHB[5]", "PUSHB[6]", "PUSHB[7]", + /* b8 */ "PUSHW[0]", "PUSHW[1]", "PUSHW[2]", "PUSHW[3]", + /* bc */ "PUSHW[4]", "PUSHW[5]", "PUSHW[6]", "PUSHW[7]", + /* c0 */ "INST_C0", "INST_C1", "INST_C2", "INST_C3", + /* c4 */ "INST_C4", "INST_C5", "INST_C6", "INST_C7", + /* c8 */ "INST_C8", "INST_C9", "INST_CA", "INST_CB", + /* cc */ "INST_CC", "INST_CD", "INST_CE", "INST_CF", + /* d0 */ "INST_D0", "INST_D1", "INST_D2", "INST_D3", + /* d4 */ "INST_D4", "INST_D5", "INST_D6", "INST_D7", + /* d8 */ "INST_D8", "INST_D9", "INST_DA", "INST_DB", + /* dc */ "INST_DC", "INST_DD", "INST_DE", "INST_DF", + /* e0 */ "MIRP00000", "MIRP00001", "MIRP00010", "MIRP00011", + /* e4 */ "MIRP00100", "MIRP00101", "MIRP00110", "MIRP00111", + /* e8 */ "MIRP01000", "MIRP01001", "MIRP01010", "MIRP01011", + /* ec */ "MIRP01100", "MIRP01101", "MIRP01110", "MIRP01111", + /* f0 */ "MIRP10000", "MIRP10001", "MIRP10010", "MIRP10011", + /* f4 */ "MIRP10100", "MIRP10101", "MIRP10110", "MIRP10111", + /* f8 */ "MIRP11000", "MIRP11001", "MIRP11010", "MIRP11011", + /* fc */ "MIRP11100", "MIRP11101", "MIRP11110", "MIRP11111" + }; +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java new file mode 100644 index 0000000..c0a3947 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java @@ -0,0 +1,243 @@ +/* Zone.java -- A collection of points with some additional information. + 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.java.awt.font.opentype.truetype; + +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; + + +/** + * A collection of points with some additional information. + */ +final class Zone +{ + private final int[] pos; + private final int[] origPos; + private final byte[] flags; + private int numPoints; + + private static final int FLAG_TOUCHED_X = 1; + private static final int FLAG_TOUCHED_Y = 2; + private static final int FLAG_ON_CURVE = 4; + private static final int FLAG_CONTOUR_END = 8; + + public Zone(int maxNumPoints) + { + origPos = new int[maxNumPoints * 2]; + pos = new int[maxNumPoints * 2]; + flags = new byte[maxNumPoints]; + } + + + public int getCapacity() + { + return flags.length; + } + + + public int getSize() + { + return numPoints; + } + + + public int getX(int point) + { + return pos[2 * point]; + } + + + public void setX(int point, int value, boolean touch) + { + pos[2 * point] = value; + if (touch) + flags[point] |= FLAG_TOUCHED_X; + } + + + public void setY(int point, int value, boolean touch) + { + pos[2 * point + 1] = value; + if (touch) + flags[point] |= FLAG_TOUCHED_Y; + } + + + public int getY(int point) + { + return pos[2 * point + 1]; + } + + + public int getOriginalX(int point) + { + return origPos[2 * point]; + } + + + public int getOriginalY(int point) + { + return origPos[2 * point + 1]; + } + + + public void setOriginalX(int point, int x) + { + origPos[2 * point] = x; + } + + public void setOriginalY(int point, int y) + { + origPos[2 * point + 1] = y; + } + + public void setNumPoints(int numPoints) + { + this.numPoints = numPoints; + for (int i = 0; i < numPoints; i++) + flags[i] = 0; + for (int i = 0; i < 2 * numPoints; i++) + origPos[i] = pos[i] = 0; + } + + + public boolean isOnCurve(int point) + { + return (flags[point] & FLAG_ON_CURVE) != 0; + } + + + public void setOnCurve(int point, boolean onCurve) + { + if (onCurve) + flags[point] |= FLAG_ON_CURVE; + else + flags[point] &= ~FLAG_ON_CURVE; + } + + + public boolean isContourEnd(int point) + { + return (flags[point] & FLAG_CONTOUR_END) != 0; + } + + + public void setContourEnd(int point, boolean segEnd) + { + if (segEnd) + flags[point] |= FLAG_CONTOUR_END; + else + flags[point] &= ~FLAG_CONTOUR_END; + } + + + + + void transform(double pointSize, AffineTransform deviceTransform, + int unitsPerEm, int preTranslateX, int preTranslateY) + { + double scaleX, scaleY, shearX, shearY; + double factor; + + factor = pointSize / (double) unitsPerEm; + scaleX = deviceTransform.getScaleX() * factor; + scaleY = deviceTransform.getScaleY() * factor; + shearX = deviceTransform.getShearX() * factor; + shearY = deviceTransform.getShearY() * factor; + + for (int i = 0; i < numPoints; i++) + { + int x = origPos[2 * i] + preTranslateX; + int y = origPos[2 * i + 1] + preTranslateY; + + origPos[2*i] = pos[2 * i] = Fixed.valueOf(scaleX * x + shearX * y); + origPos[2*i+1] = pos[2 * i + 1] = Fixed.valueOf(shearY * x + scaleY * y); + } + } + + + + void combineWithSubGlyph(Zone zone, int numPhantomPoints) + { + int offset = this.numPoints - numPhantomPoints; + int count = zone.numPoints; + System.arraycopy(zone.origPos, 0, this.origPos, 2 * offset, + count * 2); + System.arraycopy(zone.pos, 0, this.pos, 2 * offset, + count * 2); + System.arraycopy(zone.flags, 0, this.flags, offset, count); + this.numPoints += count - numPhantomPoints; + } + + + private void dump() + { + for (int i = 0; i < numPoints; i++) + { + System.out.print(" " + i + ": "); + System.out.print(Fixed.toString(pos[i*2], pos[i*2+1])); + System.out.print(' '); + System.out.print(Fixed.toString(origPos[i*2], origPos[i*2+1])); + System.out.print(' '); + if (isOnCurve(i)) + System.out.print('.'); + else + System.out.print('c'); + if (isContourEnd(i)) + System.out.print('E'); + System.out.println(); + if (isContourEnd(i)) + System.out.println(); + } + } + + + public PathIterator getPathIterator() + { + return new ZonePathIterator(this); + } + + + public GeneralPath getPath() + { + GeneralPath p = new GeneralPath(GeneralPath.WIND_NON_ZERO, numPoints); + p.append(getPathIterator(), /* connect */ false); + return p; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java new file mode 100644 index 0000000..d000b9c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java @@ -0,0 +1,391 @@ +/* ZonePathIterator.java -- A PathIterator over glyph zones. + 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.java.awt.font.opentype.truetype; + +import java.awt.geom.PathIterator; + + +/** + * A PathIterator that enumerates the non-phantom points in a zone. + * + * <p><b>Lack of thread safety:</b> Instances of this class are + * <i>not</i> safe to access from multiple concurrent threads. + * + * @see Zone + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class ZonePathIterator + implements PathIterator +{ + /** + * If <code>state</code> has this value, <code>currentSegment</code> + * will emit a <code>SEG_LINETO</code> or <code>SEG_QUADTO</code> segment + * to the current point. For a discussion of subtleties of on-curve + * and off-curve points, please refer to the documentation for + * {@link #getSegment}. + */ + private static final int EMIT_SEGMENT = 0; + + + /** + * If <code>state</code> has this value, <code>currentSegment</code> + * will emit a <code>SEG_CLOSE</code> in order to close the sub-path + * for the current contour. + */ + private static final int EMIT_CLOSE = 1; + + + /** + * If <code>state</code> has this value, <code>currentSegment</code> + * will emit a <code>SEG_MOVETO</code> segment to the first point in + * the current contour. If the first point is off-curve, a suitable + * on-curve point is calculated. + * + * @see #getStartSegment + */ + private static final int EMIT_MOVETO = 2; + + + /** + * The state of the iterator, which is one of + * <code>EMIT_SEGMENT</code>, <code>EMIT_CLOSE</code>, or + * <code>EMIT_MOVETO</code>. + */ + private int state; + + + + /** + * The zone whose segments are enumerated by this iterator. + */ + private Zone zone; + + + /** + * The total number of points in the zone, not including the four + * phantom points at its end. + */ + private int numPoints; + + + /** + * The number of the current point. + */ + private int curPoint; + + + /** + * The number of the first point in the current contour. + */ + private int contourStart; + + + + /** + * Constructs a ZonePathIterator for the specified zone. + * + * @param zone the zone whose segments will be enumerated + * by this iterator. + */ + ZonePathIterator(Zone zone) + { + this.zone = zone; + numPoints = zone.getSize() - /* four phantom points */ 4; + + // The first segment that needs to be emitted is a SEG_MOVETO. + state = EMIT_MOVETO; + } + + + /** + * Returns the winding rule. TrueType glyphs always use the non-zero + * winding rule, so this method will always return {@link + * PathIterator#WIND_NON_ZERO}. + */ + public int getWindingRule() + { + return PathIterator.WIND_NON_ZERO; + } + + + + public boolean isDone() + { + return (state != EMIT_CLOSE) && (curPoint >= numPoints); + } + + + public void next() + { + boolean onCurve; + + /* If the current point is the end of a segment, and no SEG_CLOSE + * has been emitted yet, this will be the next segment. + */ + if (zone.isContourEnd(curPoint) && (state != EMIT_CLOSE)) + { + state = EMIT_CLOSE; + return; + } + + /* If the previously emitted segment was a SEG_CLOSE, we are now + * at the beginning of a new contour. + */ + if (state == EMIT_CLOSE) + { + contourStart = ++curPoint; + state = EMIT_MOVETO; + return; + } + + onCurve = zone.isOnCurve(curPoint); + + /* If the last segment was a moveto, and the current point + * (which is the first point in the contour) is off-curve, + * we need to emit a quadto segment for the first point. + */ + if ((state == EMIT_MOVETO) && !onCurve) + { + state = EMIT_SEGMENT; + return; + } + + + curPoint++; + + /* If the last point has been off-curve, and the now current + * point is on-curve, the last segment was a quadto that + * had the now current point at its end. In this case, we can + * skip a segment. + */ + if (!onCurve && zone.isOnCurve(curPoint)) + { + /* But if the skipped point is the end of a contour, we must not + * skip the SEG_CLOSE. An example where this matters is the 'o' + * glyph in the Helvetica font face that comes with MacOS X + * 10.1.5. + */ + if (zone.isContourEnd(curPoint)) + { + state = EMIT_CLOSE; + return; + } + + curPoint++; + } + + state = EMIT_SEGMENT; + } + + + /** + * Determines the successor of the current point in the current + * contour. The successor of the last point in a contour is the + * start of that contour. + * + * @return the number of the point that follows the current point in + * the same contour. + */ + private int getSuccessor(int p) + { + if (zone.isContourEnd(p)) + return contourStart; + else + return p + 1; + } + + + + /** + * Retrieves the current path segment using single-precision + * coordinate values. + */ + public int currentSegment(float[] coords) + { + switch (state) + { + case EMIT_CLOSE: + return PathIterator.SEG_CLOSE; + + case EMIT_MOVETO: + return getStartSegment(curPoint, coords); + + default: + return getSegment(curPoint, coords); + } + } + + + /** + * A helper array that is used by {@link + * #currentSegment(double[])}. + */ + float[] floats; + + + /** + * Retrieves the current path segment using double-precision + * coordinate values. + */ + public int currentSegment(double[] coords) + { + if (floats == null) + floats = new float[6]; + int result; + + result = currentSegment(floats); + for (int i = 0; i < 6; i++) + coords[i] = floats[i]; + return result; + } + + + /** + * Returns the segment for the specified point. + * + * <p><img src="doc-files/ZonePathIterator-1.png" width="426" + * height="194" alt="An example curve" /></p> + * + * <p>If <code>cur</code> is an on-curve point, the returned segment + * is a straight line to <code>cur</code>. In the illustration, this + * would be the case for <code>cur = 4</code>.</p> + * + * <p>If <code>cur</code> is an off-curve point, and + * <code>cur</code>’s successor <code>succ</code> is also + * off-curve, the returned segment is a quadratic Bézier + * spline whose control point is <code>cur</code>, and whose end + * point is located at the middle of the line connecting + * <code>cur</code> and <code>succ</code>. In the illustration, + * this would be the case for <code>cur = 5</code>.</p> + * + * <p>If <code>cur</code> is an off-curve point, and + * <code>cur</code>’s successor <code>succ</code> is + * on-curve, the returned segment is a quadratic Bézier + * spline whose control point is <code>cur</code>, and whose end + * point is <code>succ</code>. In the illustration, this would + * be the case for <code>cur = 6</code>.</p> + * + * @return either <code>PathIterator.SEG_LINETO</code> or + * <code>PathIterator.SEG_QUADTO</code>. + */ + private int getSegment(int cur, float[] coords) + { + int curX, curY; + int succ, succX, succY; + + curX = zone.getX(cur); + curY = zone.getY(cur); + coords[0] = Fixed.floatValue(curX); + coords[1] = Fixed.floatValue(curY); + + if (zone.isOnCurve(cur)) + return PathIterator.SEG_LINETO; + + succ = getSuccessor(cur); + succX = zone.getX(succ); + succY = zone.getY(succ); + + if (zone.isOnCurve(succ)) + { + coords[2] = Fixed.floatValue(succX); + coords[3] = Fixed.floatValue(succY); + } + else + { + coords[2] = Fixed.floatValue((curX + succX) / 2); + coords[3] = Fixed.floatValue((curY + succY) / 2); + } + return PathIterator.SEG_QUADTO; + } + + + /** + * Returns the start segment for the contour which starts + * at the specified point. + * + * <p>If the contour starts with an on-curve point, the returned + * segment is a <code>SEG_MOVETO</code> to that point.</p> + * + * <p>If the contour starts with an off-curve point, and the contour + * ends with an on-curve point, the returned segment is a + * <code>SEG_MOVETO</code> to the end point.</p> + * + * <p>If the contour starts with an off-curve point, and the contour + * also ends with an off-curve point, the returned segment is a + * <code>SEG_MOVETO</code> to the location at the middle between the + * start and end points of the contour.</p> + * + * @return <code>PathIterator.SEG_MOVETO</code>. + */ + private int getStartSegment(int contourStart, float[] coords) + { + int x, y; + + if (zone.isOnCurve(contourStart)) + { + x = zone.getX(contourStart); + y = zone.getY(contourStart); + } + else + { + /* Find the last point of the current contour. */ + int contourEnd = contourStart; + while (!zone.isContourEnd(contourEnd)) + ++contourEnd; + + if (zone.isOnCurve(contourEnd)) + { + /* An example is the 'o' glyph of the Helvetica which comes + * with Apple MacOS X 10.1.5. + */ + x = zone.getX(contourEnd); + y = zone.getY(contourEnd); + } + else + { + x = (zone.getX(contourStart) + zone.getX(contourEnd)) / 2; + y = (zone.getY(contourStart) + zone.getY(contourEnd)) / 2; + } + } + + coords[0] = Fixed.floatValue(x); + coords[1] = Fixed.floatValue(y); + return PathIterator.SEG_MOVETO; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia Binary files differnew file mode 100644 index 0000000..b715ea0 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png Binary files differnew file mode 100644 index 0000000..81d09d8 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png diff --git a/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java new file mode 100644 index 0000000..e93c43e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java @@ -0,0 +1,1951 @@ +/* AbstractGraphics2D.java -- Abstract Graphics2D implementation + 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.java.awt.java2d; + +import java.awt.AWTError; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.PaintContext; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.Toolkit; +import java.awt.RenderingHints.Key; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Implements general and shared behaviour for Graphics2D implementation. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public abstract class AbstractGraphics2D + extends Graphics2D + implements Cloneable +{ + + /** + * Accuracy of the sampling in the anti-aliasing shape filler. + * Lower values give more speed, while higher values give more quality. + * It is advisable to choose powers of two. + */ + private static final int AA_SAMPLING = 8; + + /** + * The transformation for this Graphics2D instance + */ + private AffineTransform transform; + + /** + * The foreground. + */ + private Paint paint; + + /** + * The background. + */ + private Color background; + + /** + * The current font. + */ + private Font font; + + /** + * The current composite setting. + */ + private Composite composite; + + /** + * The current stroke setting. + */ + private Stroke stroke; + + /** + * The current clip. This clip is in user coordinate space. + */ + private Shape clip; + + /** + * The rendering hints. + */ + private RenderingHints renderingHints; + + /** + * The paint raster. + */ + private Raster paintRaster; + + /** + * A cached pixel array. + */ + private int[] pixel; + + /** + * The raster of the destination surface. This is where the painting is + * performed. + */ + private WritableRaster destinationRaster; + + /** + * Stores the alpha values for a scanline in the anti-aliasing shape + * renderer. + */ + private transient int[] alpha; + + /** + * The edge table for the scanline conversion algorithms. + */ + private transient ArrayList[] edgeTable; + + /** + * Indicates if cerain graphics primitives can be rendered in an optimized + * fashion. This will be the case if the following conditions are met: + * - The transform may only be a translation, no rotation, shearing or + * scaling. + * - The paint must be a solid color. + * - The composite must be an AlphaComposite.SrcOver. + * - The clip must be a Rectangle. + * - The stroke must be a plain BasicStroke(). + * + * These conditions represent the standard settings of a new + * AbstractGraphics2D object and will be the most commonly used setting + * in Swing rendering and should therefore be optimized as much as possible. + */ + private boolean isOptimized; + + /** + * Creates a new AbstractGraphics2D instance. + */ + protected AbstractGraphics2D() + { + transform = new AffineTransform(); + background = Color.WHITE; + composite = AlphaComposite.SrcOver; + stroke = new BasicStroke(); + HashMap hints = new HashMap(); + hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + hints.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_DEFAULT); + renderingHints = new RenderingHints(hints); + + pixel = new int[4]; + } + + /** + * Draws the specified shape. The shape is passed through the current stroke + * and is then forwarded to {@link #fillShape}. + * + * @param shape the shape to draw + */ + public void draw(Shape shape) + { + // Stroke the shape. + Shape strokedShape = stroke.createStrokedShape(shape); + + // Clip the stroked shape. +// Shape clipped = clipShape(strokedShape); +// if (clipped != null) +// { +// // Fill the shape. +// fillShape(clipped, false); +// } + // FIXME: Clipping doesn't seem to work. + fillShape(strokedShape, false); + } + + public boolean drawImage(Image image, AffineTransform xform, ImageObserver obs) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public void drawRenderableImage(RenderableImage image, AffineTransform xform) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Draws the specified string at the specified location. + * + * @param text the string to draw + * @param x the x location, relative to the bounding rectangle of the text + * @param y the y location, relative to the bounding rectangle of the text + */ + public void drawString(String text, int x, int y) + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray()); + drawGlyphVector(gv, x, y); + } + + /** + * Draws the specified string at the specified location. + * + * @param text the string to draw + * @param x the x location, relative to the bounding rectangle of the text + * @param y the y location, relative to the bounding rectangle of the text + */ + public void drawString(String text, float x, float y) + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray()); + drawGlyphVector(gv, x, y); + } + + /** + * Draws the specified string (as AttributedCharacterIterator) at the + * specified location. + * + * @param iterator the string to draw + * @param x the x location, relative to the bounding rectangle of the text + * @param y the y location, relative to the bounding rectangle of the text + */ + public void drawString(AttributedCharacterIterator iterator, int x, int y) + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, iterator); + drawGlyphVector(gv, x, y); + } + + /** + * Draws the specified string (as AttributedCharacterIterator) at the + * specified location. + * + * @param iterator the string to draw + * @param x the x location, relative to the bounding rectangle of the text + * @param y the y location, relative to the bounding rectangle of the text + */ + public void drawString(AttributedCharacterIterator iterator, float x, float y) + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, iterator); + drawGlyphVector(gv, x, y); + } + + /** + * Fills the specified shape with the current foreground. + * + * @param shape the shape to fill + */ + public void fill(Shape shape) + { +// Shape clipped = clipShape(shape); +// if (clipped != null) +// fillShape(clipped, false); + fillShape(shape, false); + } + + public boolean hit(Rectangle rect, Shape text, boolean onStroke) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Sets the composite. + * + * @param comp the composite to set + */ + public void setComposite(Composite comp) + { + composite = comp; + if (! (comp.equals(AlphaComposite.SrcOver))) + isOptimized = false; + else + updateOptimization(); + } + + /** + * Sets the current foreground. + * + * @param p the foreground to set. + */ + public void setPaint(Paint p) + { + if (p != null) + { + paint = p; + + if (! (paint instanceof Color)) + isOptimized = false; + else + { + updateOptimization(); + rawSetForeground((Color) paint); + } + } + } + + /** + * Sets the stroke for this graphics object. + * + * @param s the stroke to set + */ + public void setStroke(Stroke s) + { + stroke = s; + if (! stroke.equals(new BasicStroke())) + isOptimized = false; + else + updateOptimization(); + } + + /** + * Sets the specified rendering hint. + * + * @param hintKey the key of the rendering hint + * @param hintValue the value + */ + public void setRenderingHint(Key hintKey, Object hintValue) + { + renderingHints.put(hintKey, hintValue); + } + + /** + * Returns the rendering hint for the specified key. + * + * @param hintKey the rendering hint key + * + * @return the rendering hint for the specified key + */ + public Object getRenderingHint(Key hintKey) + { + return renderingHints.get(hintKey); + } + + /** + * Sets the specified rendering hints. + * + * @param hints the rendering hints to set + */ + public void setRenderingHints(Map hints) + { + renderingHints.clear(); + renderingHints.putAll(hints); + } + + /** + * Adds the specified rendering hints. + * + * @param hints the rendering hints to add + */ + public void addRenderingHints(Map hints) + { + renderingHints.putAll(hints); + } + + /** + * Returns the current rendering hints. + * + * @return the current rendering hints + */ + public RenderingHints getRenderingHints() + { + return (RenderingHints) renderingHints.clone(); + } + + /** + * Translates the coordinate system by (x, y). + * + * @param x the translation X coordinate + * @param y the translation Y coordinate + */ + public void translate(int x, int y) + { + transform.translate(x, y); + + // Update the clip. We special-case rectangular clips here, because they + // are so common (e.g. in Swing). + if (clip != null) + { + if (clip instanceof Rectangle) + { + Rectangle r = (Rectangle) clip; + r.x -= x; + r.y -= y; + setClip(r); + } + else + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.translate(-x, -y); + updateClip(clipTransform); + } + } + } + + /** + * Translates the coordinate system by (tx, ty). + * + * @param tx the translation X coordinate + * @param ty the translation Y coordinate + */ + public void translate(double tx, double ty) + { + transform.translate(tx, ty); + + // Update the clip. We special-case rectangular clips here, because they + // are so common (e.g. in Swing). + if (clip != null) + { + if (clip instanceof Rectangle) + { + Rectangle r = (Rectangle) clip; + r.x -= tx; + r.y -= ty; + } + else + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.translate(-tx, -ty); + updateClip(clipTransform); + } + } + } + + /** + * Rotates the coordinate system by <code>theta</code> degrees. + * + * @param theta the angle be which to rotate the coordinate system + */ + public void rotate(double theta) + { + transform.rotate(theta); + if (clip != null) + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.rotate(-theta); + updateClip(clipTransform); + } + updateOptimization(); + } + + /** + * Rotates the coordinate system by <code>theta</code> around the point + * (x, y). + * + * @param theta the angle by which to rotate the coordinate system + * @param x the point around which to rotate, X coordinate + * @param y the point around which to rotate, Y coordinate + */ + public void rotate(double theta, double x, double y) + { + transform.rotate(theta, x, y); + if (clip != null) + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.rotate(-theta, x, y); + updateClip(clipTransform); + } + updateOptimization(); + } + + /** + * Scales the coordinate system by the factors <code>scaleX</code> and + * <code>scaleY</code>. + * + * @param scaleX the factor by which to scale the X axis + * @param scaleY the factor by which to scale the Y axis + */ + public void scale(double scaleX, double scaleY) + { + transform.scale(scaleX, scaleY); + if (clip != null) + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.scale(-scaleX, -scaleY); + updateClip(clipTransform); + } + updateOptimization(); + } + + /** + * Shears the coordinate system by <code>shearX</code> and + * <code>shearY</code>. + * + * @param shearX the X shearing + * @param shearY the Y shearing + */ + public void shear(double shearX, double shearY) + { + transform.shear(shearX, shearY); + if (clip != null) + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.shear(-shearX, -shearY); + updateClip(clipTransform); + } + updateOptimization(); + } + + /** + * Transforms the coordinate system using the specified transform + * <code>t</code>. + * + * @param t the transform + */ + public void transform(AffineTransform t) + { + transform.concatenate(t); + try + { + AffineTransform clipTransform = t.createInverse(); + updateClip(clipTransform); + } + catch (NoninvertibleTransformException ex) + { + // TODO: How can we deal properly with this? + ex.printStackTrace(); + } + updateOptimization(); + } + + /** + * Sets the transformation for this Graphics object. + * + * @param t the transformation to set + */ + public void setTransform(AffineTransform t) + { + // Transform clip into target space using the old transform. + updateClip(transform); + transform.setTransform(t); + // Transform the clip back into user space using the inverse new transform. + try + { + updateClip(transform.createInverse()); + } + catch (NoninvertibleTransformException ex) + { + // TODO: How can we deal properly with this? + ex.printStackTrace(); + } + updateOptimization(); + } + + /** + * Returns the transformation of this coordinate system. + * + * @return the transformation of this coordinate system + */ + public AffineTransform getTransform() + { + return (AffineTransform) transform.clone(); + } + + /** + * Returns the current foreground. + * + * @return the current foreground + */ + public Paint getPaint() + { + return paint; + } + + + /** + * Returns the current composite. + * + * @return the current composite + */ + public Composite getComposite() + { + return composite; + } + + /** + * Sets the current background. + * + * @param color the background to set. + */ + public void setBackground(Color color) + { + background = color; + } + + /** + * Returns the current background. + * + * @return the current background + */ + public Color getBackground() + { + return background; + } + + /** + * Returns the current stroke. + * + * @return the current stroke + */ + public Stroke getStroke() + { + return stroke; + } + + /** + * Intersects the clip of this graphics object with the specified clip. + * + * @param s the clip with which the current clip should be intersected + */ + public void clip(Shape s) + { + // Initialize clip if not already present. + if (clip == null) + clip = s; + + // This is so common, let's optimize this. + else if (clip instanceof Rectangle && s instanceof Rectangle) + { + Rectangle clipRect = (Rectangle) clip; + Rectangle r = (Rectangle) s; + computeIntersection(r.x, r.y, r.width, r.height, clipRect); + // Call setClip so that subclasses get notified. + setClip(clipRect); + } + else + { + Area current; + if (clip instanceof Area) + current = (Area) clip; + else + current = new Area(clip); + + Area intersect; + if (s instanceof Area) + intersect = (Area) s; + else + intersect = new Area(s); + + current.intersect(intersect); + clip = current; + isOptimized = false; + // Call setClip so that subclasses get notified. + setClip(clip); + } + } + + public FontRenderContext getFontRenderContext() + { + //return new FontRenderContext(transform, false, false); + return new FontRenderContext(new AffineTransform(), false, false); + } + + /** + * Draws the specified glyph vector at the specified location. + * + * @param gv the glyph vector to draw + * @param x the location, x coordinate + * @param y the location, y coordinate + */ + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + int numGlyphs = gv.getNumGlyphs(); + AffineTransform t = new AffineTransform(); + t.translate(x, y); + +// // TODO: We could use fill(gv.getOutline()), but that seems to be + // slightly more inefficient. + for (int i = 0; i < numGlyphs; i++) + { + //fill(gv.getGlyphVisualBounds(i)); + GeneralPath p = new GeneralPath(gv.getGlyphOutline(i)); + p.transform(t); + //Shape clipped = clipShape(p); + //if (clipped != null) + // fillShape(clipped, true); + // FIXME: Clipping doesn't seem to work correctly. + fillShape(p, true); + } + } + + /** + * Creates a copy of this graphics object. + * + * @return a copy of this graphics object + */ + public Graphics create() + { + AbstractGraphics2D copy = (AbstractGraphics2D) clone(); + return copy; + } + + /** + * Creates and returns a copy of this Graphics object. This should + * be overridden by subclasses if additional state must be handled when + * cloning. This is called by {@link #create()}. + * + * @return a copy of this Graphics object + */ + protected Object clone() + { + try + { + AbstractGraphics2D copy = (AbstractGraphics2D) super.clone(); + // Copy the clip. If it's a Rectangle, preserve that for optimization. + if (clip instanceof Rectangle) + copy.clip = new Rectangle((Rectangle) clip); + else + copy.clip = new GeneralPath(clip); + + copy.renderingHints = new RenderingHints(renderingHints); + copy.transform = new AffineTransform(transform); + // The remaining state is inmmutable and doesn't need to be copied. + return copy; + } + catch (CloneNotSupportedException ex) + { + AWTError err = new AWTError("Unexpected exception while cloning"); + err.initCause(ex); + throw err; + } + } + + /** + * Returns the current foreground. + */ + public Color getColor() + { + Color c = null; + if (paint instanceof Color) + c = (Color) paint; + return c; + } + + /** + * Sets the current foreground. + * + * @param color the foreground to set + */ + public void setColor(Color color) + { + setPaint(color); + } + + public void setPaintMode() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public void setXORMode(Color color) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Returns the current font. + * + * @return the current font + */ + public Font getFont() + { + return font; + } + + /** + * Sets the font on this graphics object. When <code>f == null</code>, the + * current setting is not changed. + * + * @param f the font to set + */ + public void setFont(Font f) + { + if (f != null) + font = f; + } + + /** + * Returns the font metrics for the specified font. + * + * @param font the font for which to fetch the font metrics + * + * @return the font metrics for the specified font + */ + public FontMetrics getFontMetrics(Font font) + { + return Toolkit.getDefaultToolkit().getFontMetrics(font); + } + + /** + * Returns the bounds of the current clip. + * + * @return the bounds of the current clip + */ + public Rectangle getClipBounds() + { + Rectangle b = null; + if (clip != null) + b = clip.getBounds(); + return b; + } + + /** + * Intersects the current clipping region with the specified rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + public void clipRect(int x, int y, int width, int height) + { + clip(new Rectangle(x, y, width, height)); + } + + /** + * Sets the clip to the specified rectangle. + * + * @param x the x coordinate of the clip rectangle + * @param y the y coordinate of the clip rectangle + * @param width the width of the clip rectangle + * @param height the height of the clip rectangle + */ + public void setClip(int x, int y, int width, int height) + { + setClip(new Rectangle(x, y, width, height)); + } + + /** + * Returns the current clip. + * + * @return the current clip + */ + public Shape getClip() + { + return clip; + } + + /** + * Sets the current clipping area to <code>clip</code>. + * + * @param c the clip to set + */ + public void setClip(Shape c) + { + clip = c; + if (! (clip instanceof Rectangle)) + isOptimized = false; + else + updateOptimization(); + } + + public void copyArea(int x, int y, int width, int height, int dx, int dy) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Draws a line from (x1, y1) to (x2, y2). + * + * This implementation transforms the coordinates and forwards the call to + * {@link #rawDrawLine}. + */ + public void drawLine(int x1, int y1, int x2, int y2) + { + if (isOptimized) + { + int tx = (int) transform.getTranslateX(); + int ty = (int) transform.getTranslateY(); + rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty); + } + else + { + Line2D line = new Line2D.Double(x1, y1, x2, y2); + draw(line); + } + } + + /** + * Fills a rectangle with the current paint. + * + * @param x the upper left corner, X coordinate + * @param y the upper left corner, Y coordinate + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + public void fillRect(int x, int y, int width, int height) + { + if (isOptimized) + { + int tx = (int) transform.getTranslateX(); + int ty = (int) transform.getTranslateY(); + rawFillRect(x + tx, y + ty, width, height); + } + else + { + fill(new Rectangle(x, y, width, height)); + } + } + + /** + * Fills a rectangle with the current background color. + * + * This implementation temporarily sets the foreground color to the + * background and forwards the call to {@link #fillRect(int, int, int, int)}. + * + * @param x the upper left corner, X coordinate + * @param y the upper left corner, Y coordinate + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + public void clearRect(int x, int y, int width, int height) + { + Paint savedForeground = getPaint(); + setPaint(getBackground()); + //System.err.println("clearRect transform type: " + transform.getType()); + fillRect(x, y, width, height); + setPaint(savedForeground); + } + + /** + * Draws a rounded rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + * @param arcWidth the width of the arcs + * @param arcHeight the height of the arcs + */ + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight) + { + draw(new RoundRectangle2D.Double(x, y, width, height, arcWidth, + arcHeight)); + } + + /** + * Fills a rounded rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + * @param arcWidth the width of the arcs + * @param arcHeight the height of the arcs + */ + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight) + { + fill(new RoundRectangle2D.Double(x, y, width, height, arcWidth, + arcHeight)); + } + + /** + * Draws the outline of an oval. + * + * @param x the upper left corner of the bounding rectangle of the ellipse + * @param y the upper left corner of the bounding rectangle of the ellipse + * @param width the width of the ellipse + * @param height the height of the ellipse + */ + public void drawOval(int x, int y, int width, int height) + { + draw(new Ellipse2D.Double(x, y, width, height)); + } + + /** + * Fills an oval. + * + * @param x the upper left corner of the bounding rectangle of the ellipse + * @param y the upper left corner of the bounding rectangle of the ellipse + * @param width the width of the ellipse + * @param height the height of the ellipse + */ + public void fillOval(int x, int y, int width, int height) + { + fill(new Ellipse2D.Double(x, y, width, height)); + } + + /** + * Draws an arc. + */ + public void drawArc(int x, int y, int width, int height, int arcStart, + int arcAngle) + { + draw(new Arc2D.Double(x, y, width, height, arcStart, arcAngle, + Arc2D.OPEN)); + } + + /** + * Fills an arc. + */ + public void fillArc(int x, int y, int width, int height, int arcStart, + int arcAngle) + { + fill(new Arc2D.Double(x, y, width, height, arcStart, arcAngle, + Arc2D.OPEN)); + } + + public void drawPolyline(int[] xPoints, int[] yPoints, int npoints) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Draws the outline of a polygon. + */ + public void drawPolygon(int[] xPoints, int[] yPoints, int npoints) + { + draw(new Polygon(xPoints, yPoints, npoints)); + } + + /** + * Fills the outline of a polygon. + */ + public void fillPolygon(int[] xPoints, int[] yPoints, int npoints) + { + fill(new Polygon(xPoints, yPoints, npoints)); + } + + public boolean drawImage(Image image, int x, int y, ImageObserver observer) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public boolean drawImage(Image image, int x, int y, int width, int height, + ImageObserver observer) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public boolean drawImage(Image image, int x, int y, Color bgcolor, + ImageObserver observer) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public boolean drawImage(Image image, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + ImageObserver observer) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, Color bgcolor, + ImageObserver observer) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Disposes this graphics object. + */ + public void dispose() + { + // Nothing special to do here. + } + + /** + * Fills the specified shape. The shape has already been clipped against the + * current clip. + * + * @param s the shape to fill + * @param isFont <code>true</code> if the shape is a font outline + */ + protected void fillShape(Shape s, boolean isFont) + { + // Determine if we need to antialias stuff. + boolean antialias = false; + if (isFont) + { + Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING); + // We default to antialiasing on for text as long as we have no + // good hinting implemented. + antialias = (v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON + || v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + } + else + { + Object v = renderingHints.get(RenderingHints.KEY_ANTIALIASING); + antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON); + } + + Rectangle2D userBounds = s.getBounds2D(); + + // Flatten the path. TODO: Determine the best flattening factor + // wrt to speed and quality. + PathIterator path = s.getPathIterator(getTransform(), 1.0); + + // Build up polygons and let the native backend render this using + // rawFillShape() which would provide a default implementation for + // drawPixel using a PolyScan algorithm. + double[] seg = new double[6]; + + // TODO: Use ArrayList<PolyEdge> here when availble. + ArrayList segs = new ArrayList(); + double segX = 0.; // The start point of the current edge. + double segY = 0.; + double polyX = 0.; // The start point of the current polygon. + double polyY = 0.; + + double minX = Integer.MAX_VALUE; + double maxX = Integer.MIN_VALUE; + double minY = Integer.MAX_VALUE; + double maxY = Integer.MIN_VALUE; + + //System.err.println("fill polygon"); + while (! path.isDone()) + { + int segType = path.currentSegment(seg); + minX = Math.min(minX, seg[0]); + maxX = Math.max(maxX, seg[0]); + minY = Math.min(minY, seg[1]); + maxY = Math.max(maxY, seg[1]); + + //System.err.println("segment: " + segType + ", " + seg[0] + ", " + seg[1]); + if (segType == PathIterator.SEG_MOVETO) + { + segX = seg[0]; + segY = seg[1]; + polyX = seg[0]; + polyY = seg[1]; + } + else if (segType == PathIterator.SEG_CLOSE) + { + // Close the polyline. + PolyEdge edge = new PolyEdge(segX, segY, polyX, polyY); + segs.add(edge); + } + else if (segType == PathIterator.SEG_LINETO) + { + PolyEdge edge = new PolyEdge(segX, segY, seg[0], seg[1]); + segs.add(edge); + segX = seg[0]; + segY = seg[1]; + } + path.next(); + } + if (segs.size() > 0) + { + if (antialias) + fillShapeAntialias(segs, minX, minY, maxX, maxY, userBounds); + else + rawFillShape(segs, minX, minY, maxX, maxY, userBounds); + } + } + + /** + * Draws one pixel in the target coordinate space. This method draws the + * specified pixel by getting the painting pixel for that coordinate + * from the paintContext and compositing the pixel with the compositeContext. + * The resulting pixel is then set by calling {@link #rawSetPixel}. + * + * @param x the x coordinate + * @param y the y coordinate + */ + protected void drawPixel(int x, int y) + { + // FIXME: Implement efficient compositing. + if (! (paint instanceof Color)) + { + int[] paintPixel = paintRaster.getPixel(x, y, pixel); + Color c = new Color(paintPixel[0], paintPixel[1], paintPixel[2]); + rawSetForeground(c); + } + rawSetPixel(x, y); + } + + /** + * Draws a pixel in the target coordinate space using the specified color. + * + * @param x the x coordinate + * @param y the y coordinate + */ + protected void rawSetPixel(int x, int y) + { + // FIXME: Provide default implementation or remove method. + } + + /** + * Sets the foreground color for drawing. + * + * @param c the color to set + */ + protected void rawSetForeground(Color c) + { + // Probably remove method. + } + + protected void rawSetForeground(int r, int g, int b) + { + rawSetForeground(new Color(r, g, b)); + } + + /** + * Returns the color model of this Graphics object. + * + * @return the color model of this Graphics object + */ + protected abstract ColorModel getColorModel(); + + /** + * Returns the bounds of the target. + * + * @return the bounds of the target + */ + protected Rectangle getDeviceBounds() + { + return destinationRaster.getBounds(); + } + + /** + * Returns the bounds of the drawing area in user space. + * + * @return the bounds of the drawing area in user space + */ + protected Rectangle2D getUserBounds() + { + PathIterator pathIter = getDeviceBounds().getPathIterator(getTransform()); + GeneralPath path = new GeneralPath(); + path.append(pathIter, true); + return path.getBounds(); + + } + /** + * Draws a line in optimization mode. The implementation should respect the + * clip but can assume that it is a rectangle. + * + * @param x0 the starting point, X coordinate + * @param y0 the starting point, Y coordinate + * @param x1 the end point, X coordinate + * @param y1 the end point, Y coordinate + */ + protected void rawDrawLine(int x0, int y0, int x1, int y1) + { + // This is an implementation of Bresenham's line drawing algorithm. + int dy = y1 - y0; + int dx = x1 - x0; + int stepx, stepy; + + if (dy < 0) + { + dy = -dy; + stepy = -1; + } + else + { + stepy = 1; + } + if (dx < 0) + { + dx = -dx; + stepx = -1; + } + else + { + stepx = 1; + } + dy <<= 1; + dx <<= 1; + + drawPixel(x0, y0); + if (dx > dy) + { + int fraction = dy - (dx >> 1); // same as 2*dy - dx + while (x0 != x1) + { + if (fraction >= 0) + { + y0 += stepy; + fraction -= dx; + } + x0 += stepx; + fraction += dy; + drawPixel(x0, y0); + } + } + else + { + int fraction = dx - (dy >> 1); + while (y0 != y1) + { + if (fraction >= 0) + { + x0 += stepx; + fraction -= dy; + } + y0 += stepy; + fraction += dx; + drawPixel(x0, y0); + } + } + } + + /** + * Fills a rectangle in optimization mode. The implementation should respect + * the clip but can assume that it is a rectangle. + * + * @param x the upper left corner, X coordinate + * @param y the upper left corner, Y coordinate + * @param w the width + * @param h the height + */ + protected void rawFillRect(int x, int y, int w, int h) + { + int x2 = x + w; + int y2 = y + h; + for (int xc = x; xc < x2; xc++) + { + for (int yc = y; yc < y2; yc++) + { + drawPixel(xc, yc); + } + } + } + + /** + * Fills the specified polygon. This should be overridden by backends + * that support accelerated (native) polygon filling, which is the + * case for most toolkit window and offscreen image implementations. + * + * The polygon is already clipped when this method is called. + */ + protected void rawFillShape(ArrayList segs, double minX, double minY, + double maxX, double maxY, Rectangle2D userBounds) + { + // This is an implementation of a polygon scanline conversion algorithm + // described here: + // http://www.cs.berkeley.edu/~ug/slide/pipeline/assignments/scan/ + + // Create table of all edges. + // The edge buckets, sorted and indexed by their Y values. + + Rectangle deviceBounds = new Rectangle((int) minX, (int) minY, + (int) Math.ceil(maxX) - (int) minX, + (int) Math.ceil(maxY) - (int) minY); + PaintContext pCtx = paint.createContext(getColorModel(), deviceBounds, + userBounds, transform, renderingHints); + + ArrayList[] edgeTable = new ArrayList[(int) Math.ceil(maxY) + - (int) Math.ceil(minY) + 1]; + + for (Iterator i = segs.iterator(); i.hasNext();) + { + PolyEdge edge = (PolyEdge) i.next(); + int yindex = (int) ((int) Math.ceil(edge.y0) - (int) Math.ceil(minY)); + if (edgeTable[yindex] == null) // Create bucket when needed. + edgeTable[yindex] = new ArrayList(); + edgeTable[yindex].add(edge); // Add edge to the bucket of its line. + } + + // TODO: The following could be useful for a future optimization. +// // Sort all the edges in the edge table within their buckets. +// for (int y = 0; y < edgeTable.length; y++) +// { +// if (edgeTable[y] != null) +// Collections.sort(edgeTable[y]); +// } + + // The activeEdges list contains all the edges of the current scanline + // ordered by their intersection points with this scanline. + ArrayList activeEdges = new ArrayList(); + PolyEdgeComparator comparator = new PolyEdgeComparator(); + + // Scan all relevant lines. + int minYInt = (int) Math.ceil(minY); + for (int y = minYInt; y <= maxY; y++) + { + ArrayList bucket = edgeTable[y - minYInt]; + // Update all the x intersections in the current activeEdges table + // and remove entries that are no longer in the scanline. + for (Iterator i = activeEdges.iterator(); i.hasNext();) + { + PolyEdge edge = (PolyEdge) i.next(); + if (y > edge.y1) + i.remove(); + else + { + edge.xIntersection += edge.slope; + //edge.xIntersection = edge.x0 + edge.slope * (y - edge.y0); + //System.err.println("edge.xIntersection: " + edge.xIntersection); + } + } + + if (bucket != null) + activeEdges.addAll(bucket); + + // Sort current edges. We are using a bubble sort, because the order + // of the intersections will not change in most situations. They + // will only change, when edges intersect each other. + int size = activeEdges.size(); + if (size > 1) + { + for (int i = 1; i < size; i++) + { + PolyEdge e1 = (PolyEdge) activeEdges.get(i - 1); + PolyEdge e2 = (PolyEdge) activeEdges.get(i); + if (comparator.compare(e1, e2) > 0) + { + // Swap e2 with its left neighbor until it 'fits'. + int j = i; + do + { + activeEdges.set(j, e1); + activeEdges.set(j - 1, e2); + j--; + if (j >= 1) + e1 = (PolyEdge) activeEdges.get(j - 1); + } while (j >= 1 && comparator.compare(e1, e2) > 0); + } + } + } + + // Now draw all pixels inside the polygon. + // This is the last edge that intersected the scanline. + PolyEdge previous = null; // Gets initialized below. + boolean active = false; + //System.err.println("scanline: " + y); + for (Iterator i = activeEdges.iterator(); i.hasNext();) + { + PolyEdge edge = (PolyEdge) i.next(); + // Only fill scanline, if the current edge actually intersects + // the scanline. There may be edges that lie completely + // within the current scanline. + //System.err.println("previous: " + previous); + //System.err.println("edge: " + edge); + if (active) + { + if (edge.y1 > y) + { + int x0 = (int) previous.xIntersection; + int x1 = (int) edge.xIntersection; + fillScanline(pCtx, x0, x1, y); + previous = edge; + active = false; + } + } + else + { + if (edge.y1 > y) + { + previous = edge; + active = true; + } + } + } + } + pCtx.dispose(); + } + + /** + * Paints a scanline between x0 and x1. + * + * @param x0 the left offset + * @param x1 the right offset + * @param y the scanline + */ + protected void fillScanline(PaintContext pCtx, int x0, int x1, int y) + { + Raster paintRaster = pCtx.getRaster(x0, y, x1 - x0, 1); + ColorModel paintColorModel = pCtx.getColorModel(); + CompositeContext cCtx = composite.createContext(paintColorModel, + getColorModel(), + renderingHints); + cCtx.compose(paintRaster, destinationRaster, destinationRaster); + updateRaster(destinationRaster, x0, y, x1 - x0, 1); + cCtx.dispose(); + } + + /** + * Fills arbitrary shapes in an anti-aliased fashion. + * + * @param segs the line segments which define the shape which is to be filled + * @param minX the bounding box, left X + * @param minY the bounding box, upper Y + * @param maxX the bounding box, right X + * @param maxY the bounding box, lower Y + */ + private void fillShapeAntialias(ArrayList segs, double minX, double minY, + double maxX, double maxY, + Rectangle2D userBounds) + { + // This is an implementation of a polygon scanline conversion algorithm + // described here: + // http://www.cs.berkeley.edu/~ug/slide/pipeline/assignments/scan/ + // The antialiasing is implemented using a sampling technique, we do + // not scan whole lines but fractions of the line. + + Rectangle deviceBounds = new Rectangle((int) minX, (int) minY, + (int) Math.ceil(maxX) - (int) minX, + (int) Math.ceil(maxY) - (int) minY); + PaintContext pCtx = paint.createContext(getColorModel(), deviceBounds, + userBounds, transform, + renderingHints); + + // This array will contain the oversampled transparency values for + // each pixel in the scanline. + int numScanlines = (int) Math.ceil(maxY) - (int) minY; + int numScanlinePixels = (int) Math.ceil(maxX) - (int) minX + 1; + if (alpha == null || alpha.length < (numScanlinePixels + 1)) + alpha = new int[numScanlinePixels + 1]; + + int firstLine = (int) minY; + //System.err.println("minY: " + minY); + int firstSubline = (int) (Math.ceil((minY - Math.floor(minY)) * AA_SAMPLING)); + double firstLineDouble = firstLine + firstSubline / (double) AA_SAMPLING; + //System.err.println("firstSubline: " + firstSubline); + + // Create table of all edges. + // The edge buckets, sorted and indexed by their Y values. + //System.err.println("numScanlines: " + numScanlines); + if (edgeTable == null + || edgeTable.length < numScanlines * AA_SAMPLING + AA_SAMPLING) + edgeTable = new ArrayList[numScanlines * AA_SAMPLING + AA_SAMPLING]; + + //System.err.println("firstLineDouble: " + firstLineDouble); + + for (Iterator i = segs.iterator(); i.hasNext();) + { + PolyEdge edge = (PolyEdge) i.next(); + int yindex = (int) (Math.ceil((edge.y0 - firstLineDouble) * AA_SAMPLING)); + //System.err.println("yindex: " + yindex + " for y0: " + edge.y0); + // Initialize edge's slope and initial xIntersection. + edge.slope = ((edge.x1 - edge.x0) / (edge.y1 - edge.y0)) / AA_SAMPLING; + if (edge.y0 == edge.y1) // Horizontal edge. + edge.xIntersection = Math.min(edge.x0, edge.x1); + else + { + double alignedFirst = Math.ceil(edge.y0 * AA_SAMPLING) / AA_SAMPLING; + edge.xIntersection = edge.x0 + (edge.slope * AA_SAMPLING) * (alignedFirst - edge.y0); + } + //System.err.println(edge); + // FIXME: Sanity check should not be needed when clipping works. + if (yindex >= 0 && yindex < edgeTable.length) + { + if (edgeTable[yindex] == null) // Create bucket when needed. + edgeTable[yindex] = new ArrayList(); + edgeTable[yindex].add(edge); // Add edge to the bucket of its line. + } + } + + // The activeEdges list contains all the edges of the current scanline + // ordered by their intersection points with this scanline. + ArrayList activeEdges = new ArrayList(); + PolyEdgeComparator comparator = new PolyEdgeComparator(); + + // Scan all lines. + int yindex = 0; + //System.err.println("firstLine: " + firstLine + ", maxY: " + maxY + ", firstSubline: " + firstSubline); + for (int y = firstLine; y <= maxY; y++) + { + for (int subY = firstSubline; subY < AA_SAMPLING; subY++) + { + //System.err.println("scanline: " + y + ", subScanline: " + subY); + ArrayList bucket = edgeTable[yindex]; + // Update all the x intersections in the current activeEdges table + // and remove entries that are no longer in the scanline. + for (Iterator i = activeEdges.iterator(); i.hasNext();) + { + PolyEdge edge = (PolyEdge) i.next(); + // TODO: Do the following using integer arithmetics. + if ((y + ((double) subY / (double) AA_SAMPLING)) > edge.y1) + i.remove(); + else + { + edge.xIntersection += edge.slope; + //System.err.println("edge: " + edge); + //edge.xIntersection = edge.x0 + edge.slope * (y - edge.y0); + //System.err.println("edge.xIntersection: " + edge.xIntersection); + } + } + + if (bucket != null) + { + activeEdges.addAll(bucket); + edgeTable[yindex].clear(); + } + + // Sort current edges. We are using a bubble sort, because the order + // of the intersections will not change in most situations. They + // will only change, when edges intersect each other. + int size = activeEdges.size(); + if (size > 1) + { + for (int i = 1; i < size; i++) + { + PolyEdge e1 = (PolyEdge) activeEdges.get(i - 1); + PolyEdge e2 = (PolyEdge) activeEdges.get(i); + if (comparator.compare(e1, e2) > 0) + { + // Swap e2 with its left neighbor until it 'fits'. + int j = i; + do + { + activeEdges.set(j, e1); + activeEdges.set(j - 1, e2); + j--; + if (j >= 1) + e1 = (PolyEdge) activeEdges.get(j - 1); + } while (j >= 1 && comparator.compare(e1, e2) > 0); + } + } + } + + // Now draw all pixels inside the polygon. + // This is the last edge that intersected the scanline. + PolyEdge previous = null; // Gets initialized below. + boolean active = false; + //System.err.println("scanline: " + y + ", subscanline: " + subY); + for (Iterator i = activeEdges.iterator(); i.hasNext();) + { + PolyEdge edge = (PolyEdge) i.next(); + // Only fill scanline, if the current edge actually intersects + // the scanline. There may be edges that lie completely + // within the current scanline. + //System.err.println("previous: " + previous); + //System.err.println("edge: " + edge); + if (active) + { + // TODO: Use integer arithmetics here. + if (edge.y1 > (y + (subY / (double) AA_SAMPLING))) + { + //System.err.println(edge); + // TODO: Eliminate the aligments. + int x0 = (int) Math.min(Math.max(previous.xIntersection, minX), maxX); + int x1 = (int) Math.min(Math.max(edge.xIntersection, minX), maxX); + //System.err.println("minX: " + minX + ", x0: " + x0 + ", x1: " + x1 + ", maxX: " + maxX); + // TODO: Pull out cast. + alpha[x0 - (int) minX]++; + alpha[x1 - (int) minX + 1]--; + previous = edge; + active = false; + } + } + else + { + // TODO: Use integer arithmetics here. + if (edge.y1 > (y + (subY / (double) AA_SAMPLING))) + { + //System.err.println(edge); + previous = edge; + active = true; + } + } + } + yindex++; + } + firstSubline = 0; + // Render full scanline. + //System.err.println("scanline: " + y); + fillScanlineAA(alpha, (int) minX, (int) y, numScanlinePixels, pCtx); + } + if (paint instanceof Color && composite == AlphaComposite.SrcOver) + rawSetForeground((Color) paint); + + pCtx.dispose(); + } + + /** + * Fills a horizontal line between x0 and x1 for anti aliased rendering. + * the alpha array contains the deltas of the alpha values from one pixel + * to the next. + * + * @param alpha the alpha values in the scanline + * @param x0 the beginning of the scanline + * @param y the y coordinate of the line + */ + private void fillScanlineAA(int[] alpha, int x0, int y, int numScanlinePixels, + PaintContext pCtx) + { + // FIXME: This doesn't work. Fixit. + CompositeContext cCtx = composite.createContext(pCtx.getColorModel(), + getColorModel(), + renderingHints); + Raster paintRaster = pCtx.getRaster(x0, y, numScanlinePixels, 1); + System.err.println("paintColorModel: " + pCtx.getColorModel()); + WritableRaster aaRaster = paintRaster.createCompatibleWritableRaster(); + int numBands = paintRaster.getNumBands(); + int[] pixels = new int[numScanlinePixels + paintRaster.getNumBands()]; + pixels = paintRaster.getPixels(x0, y, numScanlinePixels, 1, pixels); + ColorModel cm = pCtx.getColorModel(); + + double lastAlpha = 0.; + int lastAlphaInt = 0; + int[] components = new int[4]; + + for (int i = 0; i < pixels.length; i++) + { + if (alpha[i] != 0) + { + lastAlphaInt += alpha[i]; + lastAlpha = lastAlphaInt / AA_SAMPLING; + } + components = cm.getComponents(pixel[i], components, 0); + components[0] = (int) (components[0] * lastAlpha); + pixel[i] = cm.getDataElement(components, 0); + } + + aaRaster.setPixels(0, 0, numScanlinePixels, 1, pixels); + cCtx.compose(aaRaster, destinationRaster, destinationRaster); + updateRaster(destinationRaster, x0, y, numScanlinePixels, 1); + + cCtx.dispose(); + } + + + /** + * Initializes this graphics object. This must be called by subclasses in + * order to correctly initialize the state of this object. + */ + protected void init() + { + setPaint(Color.BLACK); + setFont(new Font("SansSerif", Font.PLAIN, 12)); + isOptimized = true; + + // FIXME: Should not be necessary. A clip of null should mean + // 'clip against device bounds. + clip = getDeviceBounds(); + destinationRaster = getDestinationRaster(); + } + + /** + * Returns a WritableRaster that is used by this class to perform the + * rendering in. It is not necessary that the target surface immediately + * reflects changes in the raster. Updates to the raster are notified via + * {@link #updateRaster}. + * + * @return the destination raster + */ + protected abstract WritableRaster getDestinationRaster(); + + /** + * Notifies the backend that the raster has changed in the specified + * rectangular area. The raster that is provided in this method is always + * the same as the one returned in {@link #getDestinationRaster}. + * Backends that reflect changes to this raster directly don't need to do + * anything here. + * + * @param raster the updated raster, identical to the raster returned + * by {@link #getDestinationRaster()} + * @param x the upper left corner of the updated region, X coordinate + * @param y the upper lef corner of the updated region, Y coordinate + * @param w the width of the updated region + * @param h the height of the updated region + */ + protected void updateRaster(Raster raster, int x, int y, int w, int h) + { + // Nothing to do here. Backends that need to update their surface + // to reflect the change should override this method. + } + + // Some helper methods. + + /** + * Helper method to check and update the optimization conditions. + */ + private void updateOptimization() + { + int transformType = transform.getType(); + boolean optimizedTransform = false; + if (transformType == AffineTransform.TYPE_IDENTITY + || transformType == AffineTransform.TYPE_TRANSLATION) + optimizedTransform = true; + + boolean optimizedClip = (clip == null || clip instanceof Rectangle); + isOptimized = optimizedClip + && optimizedTransform && paint instanceof Color + && composite == AlphaComposite.SrcOver + && stroke.equals(new BasicStroke()); + } + + /** + * Calculates the intersection of two rectangles. The result is stored + * in <code>rect</code>. This is basically the same + * like {@link Rectangle#intersection(Rectangle)}, only that it does not + * create new Rectangle instances. The tradeoff is that you loose any data in + * <code>rect</code>. + * + * @param x upper-left x coodinate of first rectangle + * @param y upper-left y coodinate of first rectangle + * @param w width of first rectangle + * @param h height of first rectangle + * @param rect a Rectangle object of the second rectangle + * + * @throws NullPointerException if rect is null + * + * @return a rectangle corresponding to the intersection of the + * two rectangles. An empty rectangle is returned if the rectangles + * do not overlap + */ + private static Rectangle computeIntersection(int x, int y, int w, int h, + Rectangle rect) + { + int x2 = (int) rect.x; + int y2 = (int) rect.y; + int w2 = (int) rect.width; + int h2 = (int) rect.height; + + int dx = (x > x2) ? x : x2; + int dy = (y > y2) ? y : y2; + int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx); + int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy); + + if (dw >= 0 && dh >= 0) + rect.setBounds(dx, dy, dw, dh); + else + rect.setBounds(0, 0, 0, 0); + + return rect; + } + + /** + * Helper method to transform the clip. This is called by the various + * transformation-manipulation methods to update the clip (which is in + * userspace) accordingly. + * + * The transform usually is the inverse transform that was applied to the + * graphics object. + * + * @param t the transform to apply to the clip + */ + private void updateClip(AffineTransform t) + { + if (! (clip instanceof GeneralPath)) + clip = new GeneralPath(clip); + + GeneralPath p = (GeneralPath) clip; + p.transform(t); + } + + /** + * Clips the specified shape using the current clip. If the resulting shape + * is empty, this will return <code>null</code>. + * + * @param s the shape to clip + * + * @return the clipped shape or <code>null</code> if the result is empty + */ + private Shape clipShape(Shape s) + { + Shape clipped = null; + + // Clip the shape if necessary. + if (clip != null) + { + Area a; + if (! (s instanceof Area)) + a = new Area(s); + else + a = (Area) s; + + Area clipArea; + if (! (clip instanceof Area)) + clipArea = new Area(clip); + else + clipArea = (Area) clip; + + a.intersect(clipArea); + if (! a.isEmpty()) + clipped = a; + } + else + { + clipped = s; + } + return clipped; + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java b/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java new file mode 100644 index 0000000..e67c921 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java @@ -0,0 +1,316 @@ +/* AlphaCompositeContext.java -- CompositeContext impl for AlphaComposite + 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.java.awt.java2d; + +import java.awt.AWTError; +import java.awt.AlphaComposite; +import java.awt.CompositeContext; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/** + * A CompositeContext implementation for {@link AlphaComposite}. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class AlphaCompositeContext + implements CompositeContext +{ + + /** + * The Composite object for which we perform compositing. + */ + private AlphaComposite composite; + + /** + * The source color model. + */ + private ColorModel srcColorModel; + + /** + * The destination color model. + */ + private ColorModel dstColorModel; + + /** + * The blending factor for the source. + */ + private float fs; + + /** + * The blending factor for the destination. + */ + private float fd; + + /** + * Creates a new AlphaCompositeContext. + * + * @param aComp the AlphaComposite object + * @param srcCM the source color model + * @param dstCM the destination color model + */ + public AlphaCompositeContext(AlphaComposite aComp, ColorModel srcCM, + ColorModel dstCM) + { + composite = aComp; + srcColorModel = srcCM; + dstColorModel = dstCM; + + + // Determine the blending factors according to the rule in the + // AlphaComposite. For some rules the factors must be determined + // dynamically because they depend on the actual pixel value. + switch (composite.getRule()) + { + case AlphaComposite.CLEAR: + fs = 0.F; + fd= 0.F; + break; + case AlphaComposite.DST: + fs = 0.F; + fd= 1.F; + break; + case AlphaComposite.DST_ATOP: + fs = 1.F; // Determined later as 1 - alpha_dst; + fd = 1.F; // Determined later as alpha_src; + break; + case AlphaComposite.DST_IN: + fs = 0.F; + fd = 0.F; // Determined later as alpha_src; + break; + case AlphaComposite.DST_OUT: + fs = 0.F; + fd = 0.F; // Determined later as 1 - alpha_src; + break; + case AlphaComposite.DST_OVER: + fs = 1.F; // Determined later as 1 - alpha_dst. + fd= 1.F; + break; + case AlphaComposite.SRC: + fs = 1.F; + fd= 0.F; + break; + case AlphaComposite.SRC_ATOP: + fs = 1.F; // Determined later as alpha_dst; + fd = 1.F; // Determined later as 1 - alpha_src; + break; + case AlphaComposite.SRC_IN: + fs = 0.F; // Determined later as alpha_dst; + fd = 0.F; + break; + case AlphaComposite.SRC_OUT: + fs = 0.F; // Determined later as 1 - alpha_dst; + fd = 0.F; + break; + case AlphaComposite.SRC_OVER: + fs = 1.F; + fd= 1.F; // Determined later as 1 - alpha_src. + break; + case AlphaComposite.XOR: + fs = 1.F; // Determined later as 1 - alpha_dst. + fd= 1.F; // Determined later as 1 - alpha_src. + break; + default: + throw new AWTError("Illegal AlphaComposite rule"); + } + + } + + /** + * Releases all resources held by this composite object. + */ + public void dispose() + { + // Nothing to do here yet. + } + + /** + * Performs compositing according to the rules specified in the + * AlphaComposite from the constructor. + */ + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) + { + + // TODO: This implementation is very general and highly inefficient. There + // are two possible ways to optimize this: + // 1. Special cased implementations for common ColorModels and transfer + // types. + // 2. Native implementation. + + int x0 = src.getMinX(); + int y0 = src.getMinY(); + int width = src.getWidth(); + int height = src.getHeight(); + int x1 = x0 + width; + int y1 = y0 + height; + + Object srcPixel = null; + Object dstPixel = null; + + // Prepare the array that holds the color and alpha components of the + // source pixels. + float[] srcComponents; + int srcComponentsLength = srcColorModel.getNumComponents(); + if (! srcColorModel.hasAlpha()) + srcComponentsLength += 1; + srcComponents = new float[srcComponentsLength]; + + // Prepare the array that holds the color and alpha components of the + // destination pixels. + float[] dstComponents; + int dstComponentsLength = dstColorModel.getNumComponents(); + if (! dstColorModel.hasAlpha()) + dstComponentsLength += 1; + dstComponents = new float[dstComponentsLength]; + + if (srcComponentsLength != dstComponentsLength) + throw new AWTError("The color models of the source and destination have" + + "incompatible number of color components"); + + int srcTransferType = srcColorModel.getTransferType(); + int dstTransferType = dstColorModel.getTransferType(); + + for (int y = y0; y < y1; y++) + { + for (int x = x0; x < x1; x++) + { + // Fetch source pixel. + srcPixel = src.getDataElements(x, y, (int[]) srcPixel); + // Fetch destination pixel. + dstPixel = dstIn.getDataElements(x, y, dstPixel); + // Get normalized components. This is the only type that is + // guaranteed to be supported by all ColorModels. + srcComponents = + srcColorModel.getNormalizedComponents(srcPixel, srcComponents, 0); + if (! srcColorModel.hasAlpha()) + srcComponents[srcComponentsLength - 1] = 1.0F; + dstComponents = + dstColorModel.getNormalizedComponents(dstPixel, dstComponents, 0); + if (! dstColorModel.hasAlpha()) + dstComponents[dstComponentsLength - 1] = 1.0F; + + // Prepare the input. + float compositeAlpha = composite.getAlpha(); + srcComponents[srcComponentsLength - 1] *= compositeAlpha; + if (srcColorModel.isAlphaPremultiplied()) + { + for (int i = srcComponentsLength - 2; i >= 0; i--) + srcComponents[i] *= compositeAlpha; + } + else + { + for (int i = srcComponentsLength - 1; i >= 0; i--) + srcComponents[i] *= srcComponents[srcComponentsLength - 1]; + } + if (! dstColorModel.isAlphaPremultiplied()) + { + for (int i = dstComponentsLength - 2; i >= 0; i--) + dstComponents[i] *= dstComponents[dstComponents.length - 1]; + } + + // Determine the blending factors according to the rule in the + // AlphaComposite. For some rules the factors must be determined + // dynamically because they depend on the actual pixel value. + float srcAlpha = srcComponents[srcComponentsLength - 1]; + float dstAlpha = dstComponents[dstComponentsLength - 1]; + switch (composite.getRule()) + { + case AlphaComposite.DST_ATOP: + fs = 1.F - dstAlpha; + fd = srcAlpha; + break; + case AlphaComposite.DST_IN: + fd = srcAlpha; + break; + case AlphaComposite.DST_OUT: + fd = 1.F - srcAlpha; + break; + case AlphaComposite.DST_OVER: + fs = 1.F - dstAlpha; + break; + case AlphaComposite.SRC_ATOP: + fs = srcAlpha; + fd = 1.F - srcAlpha; + break; + case AlphaComposite.SRC_IN: + fs = dstAlpha; + break; + case AlphaComposite.SRC_OUT: + fs = 1.F - dstAlpha; + break; + case AlphaComposite.SRC_OVER: + fd= 1.F - srcAlpha; + break; + case AlphaComposite.XOR: + fs = 1.F - dstAlpha; + fd= 1.F - srcAlpha; + break; + default: + // For the other cases the factors have already been determined + // in the constructor. + } + + // Apply the blending equation to the pixels. + for (int i = 0; i < srcComponentsLength; i++) + { + dstComponents[i] = srcComponents[i] * fs + + dstComponents[i] * fd; + } + + // Convert the result back when the destination is not + // alpha-premultiplied. + dstAlpha = dstComponents[dstComponentsLength - 1]; + if (!dstColorModel.isAlphaPremultiplied() && dstAlpha != 0.F) + { + for (int i = 0; i < dstComponentsLength - 1; i++) + { + dstComponents[i] = dstComponents[i] / dstAlpha; + } + } + + // Store the result in the destination raster. + dstPixel = dstColorModel.getDataElements(dstComponents, 0, + dstPixel); + dstOut.setDataElements(x, y, dstPixel); + } // End X loop. + } // End Y loop. + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/CubicSegment.java b/libjava/classpath/gnu/java/awt/java2d/CubicSegment.java new file mode 100644 index 0000000..1e568f7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/CubicSegment.java @@ -0,0 +1,128 @@ +/* CubicSegment.java -- Cubic segment used for BasicStroke + 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.java.awt.java2d; + + +import java.awt.geom.Point2D; + +/** + * Cubic Bezier curve segment + */ +public class CubicSegment extends Segment +{ + public Point2D cp1; // control points + public Point2D cp2; // control points + + /** + * Constructor - takes coordinates of the starting point, + * first control point, second control point and end point, + * respecively. + */ + public CubicSegment(double x1, double y1, double c1x, double c1y, + double c2x, double c2y, double x2, double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + cp1 = new Point2D.Double(c1x, c1y); + cp2 = new Point2D.Double(c2x, c2y); + } + + public CubicSegment(Point2D p1, Point2D cp1, Point2D cp2, Point2D p2) + { + super(); + P1 = p1; + P2 = p2; + this.cp1 = cp1; + this.cp2 = cp2; + } + + /** + * Clones this segment + */ + public Object clone() + { + return new CubicSegment(P1.getX(), P1.getY(), cp1.getX(), cp1.getY(), + cp2.getX(), cp2.getY(), P2.getX(), P2.getY()); + } + + /** + * Get the "top" and "bottom" segments of this segment. + * First array element is p0 + normal, second is p0 - normal. + */ + public Segment[] getDisplacedSegments(double radius) + { + this.radius = radius; + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + double[] p1 = normal(x0, y0, x1, y1); + double[] p2 = normal(x2, y2, x3, y3); + + + // FIXME: Doesn't compile. + // return new Segment[]{s1, s2}; + return new Segment[0]; + } + + public void reverse() + { + Point2D temp = P1; + P1 = P2; + P2 = temp; + temp = cp1; + cp1 = cp2; + cp2 = temp; + } + + public double[] first() + { + return new double[]{cp1.getX(), cp1.getY()}; + } + + public double[] last() + { + return new double[]{cp2.getX(), cp2.getY()}; + } +} // class CubicSegment diff --git a/libjava/classpath/gnu/java/awt/java2d/LineSegment.java b/libjava/classpath/gnu/java/awt/java2d/LineSegment.java new file mode 100644 index 0000000..9c0bcc7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/LineSegment.java @@ -0,0 +1,103 @@ +/* LineSegment.java -- Line segment used for BasicStroke + 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.java.awt.java2d; + + +import java.awt.geom.Point2D; + +public class LineSegment extends Segment +{ + public LineSegment(double x1, double y1, double x2, double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + } + + public LineSegment(Point2D p1, Point2D p2) + { + super(); + P1 = (Point2D) p1.clone(); + P2 = (Point2D) p2.clone(); + } + + /** + * Clones this segment + */ + public Object clone() + { + return new LineSegment(P1, P2); + } + + /** + * Get the "top" and "bottom" segments of this segment. + * First array element is p0 + normal, second is p0 - normal. + */ + public Segment[] getDisplacedSegments(double radius) + { + this.radius = radius; + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = P2.getX(); + double y1 = P2.getY(); + double[] p = normal(x0, y0, x1, y1); + Segment s1 = (new LineSegment(x0 + p[0], y0 + p[1], + x1 + p[0], y1 + p[1] )); + Segment s2 = (new LineSegment(x0 - p[0], y0 - p[1], + x1 - p[0], y1 - p[1] )); + return new Segment[]{s1, s2}; + } + + public void reverse() + { + Point2D p = P1; + P1 = P2; + P2 = p; + } + + public double[] first() + { + return new double[]{P2.getX(), P2.getY()}; + } + + public double[] last() + { + return new double[]{P1.getX(), P1.getY()}; + } +} // class LineSegment diff --git a/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java b/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java new file mode 100644 index 0000000..621bd3a --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java @@ -0,0 +1,117 @@ +/* PolyEdge.java -- An edge in a polygon, used for polygon filling + 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.java.awt.java2d; + +/** + * An edge in a polygon. This is used by the scanline conversion algorithm + * implemented in {@link AbstractGraphics2D#rawFillShape}. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class PolyEdge + implements Comparable +{ + + /** + * The start and end coordinates of the edge. y0 is always smaller or equal + * than y1. + */ + public double x0, y0, x1, y1; + + /** + * The slope of the edge. This is dx / dy. + */ + double slope; + + /** + * The intersection of this edge with the current scanline. + */ + double xIntersection; + + /** + * Creates a new PolyEdge with the specified coordinates. + * + * @param x0 the starting point, x coordinate + * @param y0 the starting point, y coordinate + * @param x1 the end point, x coordinate + * @param y1 the end point, y coordinate + */ + PolyEdge(double x0, double y0, double x1, double y1) + { + if (y0 < y1) + { + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + } + else + { + this.x0 = x1; + this.y0 = y1; + this.x1 = x0; + this.y1 = y0; + } + slope = (this.x1 - this.x0) / (this.y1 - this.y0); + if (this.y0 == this.y1) // Horizontal edge. + xIntersection = Math.min(this.x0, this.x1); + else + xIntersection = this.x0 + slope * (Math.ceil(this.y0) - this.y0); + } + + /** + * Sorts PolyEdges by the x coordinate from the minimum x value. + */ + public int compareTo(Object o) + { + PolyEdge other = (PolyEdge) o; + int comp = 0; + if (x0 < other.x0) + comp = -1; + else if (x0 > other.x0) + comp = 1; + return comp; + } + + public String toString() + { + return "Edge: " + x0 + ", " + y0 + ", " + x1 + ", " + y1 + ", slope: " + + slope + ", xIntersection: " + xIntersection; + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/PolyEdgeComparator.java b/libjava/classpath/gnu/java/awt/java2d/PolyEdgeComparator.java new file mode 100644 index 0000000..6706f22 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/PolyEdgeComparator.java @@ -0,0 +1,70 @@ +/* PolyEdgeComparator.java -- Sorts PolyEdges by their current intersection + points + 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.java.awt.java2d; + +import java.util.Comparator; + +/** + * Sorts {@link PolyEdge}s by their current intersection points. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class PolyEdgeComparator + implements Comparator +{ + + /** + * The current scanline. + */ + int y; + + public int compare(Object o1, Object o2) + { + PolyEdge edge1 = (PolyEdge) o1; + PolyEdge edge2 = (PolyEdge) o2; + int comp = 0; + if (edge1.xIntersection < edge2.xIntersection) + comp = -1; + else if (edge1.xIntersection > edge2.xIntersection) + comp = 1; + return comp; + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java b/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java new file mode 100644 index 0000000..9c15a8c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java @@ -0,0 +1,213 @@ +/* QuadSegment.java -- QuadCurve segment used for BasicStroke + 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.java.awt.java2d; + + +import java.awt.geom.Point2D; +import java.awt.geom.QuadCurve2D; + +/** + * Quadratic Bezier curve segment + * + * Note: Most peers don't support quadratics directly, so it might make + * sense to represent them as cubics internally and just be done with it. + * I think we should be peer-agnostic, however, and stay faithful to the + * input geometry types as far as possible. + */ +public class QuadSegment extends Segment +{ + public Point2D cp; // control point + + /** + * Constructor, takes the coordinates of the start, control, + * and end point, respectively. + */ + public QuadSegment(double x1, double y1, double cx, double cy, double x2, + double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + cp = new Point2D.Double(cx, cy); + } + + public QuadSegment(Point2D p1, Point2D cp, Point2D p2) + { + super(); + P1 = p1; + P2 = p2; + this.cp = cp; + } + + public QuadSegment(QuadCurve2D curve) + { + super(); + P1 = curve.getP1(); + P2 = curve.getP2(); + this.cp = curve.getCtrlPt(); + } + + /** + * Clones this segment + */ + public Object clone() + { + return new QuadSegment(P1.getX(), P1.getY(), cp.getX(), cp.getY(), + P2.getX(), P2.getY()); + } + + /** + * Get the "top" and "bottom" segments of a given segment. + * First array element is p0 + normal, second is p0 - normal. + */ + public Segment[] getDisplacedSegments(double radius) + { + this.radius = radius; + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + + QuadCurve2D left = new QuadCurve2D.Double(); + QuadCurve2D right = new QuadCurve2D.Double(); + QuadCurve2D orig = new QuadCurve2D.Double(x0, y0, x1, y1, x2, y2); + orig.subdivide(left, right); + + QuadSegment s1 = offsetSubdivided(left, true); + QuadSegment s2 = offsetSubdivided(left, false); + + s1.add( offsetSubdivided(right, true) ); + s2.add( offsetSubdivided(right, false) ); + + return new Segment[]{s1, s2}; + } + + private QuadSegment offsetSubdivided(QuadCurve2D curve, boolean plus) + { + double[] n1 = normal(curve.getX1(), curve.getY1(), + curve.getCtrlX(), curve.getCtrlY()); + double[] n2 = normal(curve.getCtrlX(), curve.getCtrlY(), + curve.getX2(), curve.getY2()); + + Point2D cp; + QuadSegment s; + if( plus ) + { + cp = lineIntersection(curve.getX1() + n1[0], + curve.getY1() + n1[1], + curve.getCtrlX() + n1[0], + curve.getCtrlY() + n1[1], + curve.getCtrlX() + n2[0], + curve.getCtrlY() + n2[1], + curve.getX2() + n2[0], + curve.getY2() + n2[1], true); + s = new QuadSegment(curve.getX1() + n1[0], curve.getY1() + n1[1], + cp.getX(), cp.getY(), + curve.getX2() + n2[0], curve.getY2() + n2[1]); + } + else + { + cp = lineIntersection(curve.getX1() - n1[0], + curve.getY1() - n1[1], + curve.getCtrlX() - n1[0], + curve.getCtrlY() - n1[1], + curve.getCtrlX() - n2[0], + curve.getCtrlY() - n2[1], + curve.getX2() - n2[0], + curve.getY2() - n2[1], true); + + s = new QuadSegment(curve.getX1() - n1[0], curve.getY1() - n1[1], + cp.getX(), cp.getY(), + curve.getX2() - n2[0], curve.getY2() - n2[1]); + } + + return s; + } + + private Point2D lineIntersection(double X1, double Y1, + double X2, double Y2, + double X3, double Y3, + double X4, double Y4, + boolean infinite) + { + double x1 = X1; + double y1 = Y1; + double rx = X2 - x1; + double ry = Y2 - y1; + + double x2 = X3; + double y2 = Y3; + double sx = X4 - x2; + double sy = Y4 - y2; + + double determinant = sx * ry - sy * rx; + double nom = (sx * (y2 - y1) + sy * (x1 - x2)); + + // lines can be considered parallel. + if (Math.abs(determinant) < 1E-6) + return null; + + nom = nom / determinant; + + // check if lines are within the bounds + if(!infinite && (nom > 1.0 || nom < 0.0)) + return null; + + return new Point2D.Double(x1 + nom * rx, y1 + nom * ry); + } + + public void reverse() + { + Point2D p = P1; + P1 = P2; + P2 = p; + } + + public double[] first() + { + return new double[]{cp.getX(), cp.getY()}; + } + + public double[] last() + { + return new double[]{cp.getX(), cp.getY()}; + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java b/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java new file mode 100644 index 0000000..4de8035 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java @@ -0,0 +1,101 @@ +/* RasterGraphics.java -- A Graphics2D impl for Rasters + 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.java.awt.java2d; + +import java.awt.GraphicsConfiguration; +import java.awt.image.ColorModel; +import java.awt.image.WritableRaster; + +/** + * A Graphics2D implementation that operates on Raster objects. This is + * primarily used for BufferedImages, but can theoretically be used on + * arbitrary WritableRasters. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class RasterGraphics + extends AbstractGraphics2D +{ + + /** + * The raster on which we operate. + */ + private WritableRaster raster; + + /** + * The color model of this Graphics instance. + */ + private ColorModel colorModel; + + public RasterGraphics(WritableRaster r, ColorModel cm) + { + raster = r; + colorModel = cm; + } + + /** + * Returns the color model of this Graphics object. + * + * @return the color model of this Graphics object + */ + protected ColorModel getColorModel() + { + return colorModel; + } + + /** + * Returns a WritableRaster that is used by this class to perform the + * rendering in. It is not necessary that the target surface immediately + * reflects changes in the raster. Updates to the raster are notified via + * {@link AbstractGraphics2D#updateRaster}. + * + * @return the destination raster + */ + protected WritableRaster getDestinationRaster() + { + return raster; + } + + public GraphicsConfiguration getDeviceConfiguration() + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/Segment.java b/libjava/classpath/gnu/java/awt/java2d/Segment.java new file mode 100644 index 0000000..9a985f6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/Segment.java @@ -0,0 +1,131 @@ +/* Segment.java -- Abstract segment used for BasicStroke + 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.java.awt.java2d; + +import java.awt.geom.Point2D; + +public abstract class Segment implements Cloneable +{ + // segment type, PathIterator segment types are used. + public Point2D P1; + public Point2D P2; + public Segment next; + public Segment last; + protected double radius; + + public Segment() + { + P1 = P2 = null; + next = null; + last = this; + } + + public void add(Segment newsegment) + { + last.next = newsegment; + last = last.next; + } + + /** + * Reverses the orientation of the whole polygon + */ + public void reverseAll() + { + reverse(); + Segment v = next; + Segment former = this; + next = null; + + while (v != null) + { + v.reverse(); + v.last = this; + Segment oldnext = v.next; + v.next = former; + + former = v; + v = oldnext; // move to the next in list + } + } + + public String toString() + { + return "Segment:"+P1+", "+P2; + } + + /** + * Get the normal vector to the slope of the line. + * Returns: 0.5*width*(norm of derivative of the (x0,y0)-(x1,y1) vector) + */ + protected double[] normal(double x0, double y0, double x1, double y1) + { + double dx = (x1 - x0); + double dy = (y1 - y0); + if( dy == 0 ) + { + dy = radius * ((dx > 0)?1:-1); + dx = 0; + } + else if( dx == 0 ) + { + dx = radius * ((dy > 0)?-1:1); + dy = 0; + } + else + { + double N = Math.sqrt(dx * dx + dy * dy); + double odx = dx; + dx = -radius * dy / N; + dy = radius * odx / N; + } + return new double[]{ dx, dy }; + } + + public abstract void reverse(); + + /** + * Get the "top" and "bottom" segments of a segment. + * First array element is p0 + normal, second is p0 - normal. + */ + public abstract Segment[] getDisplacedSegments(double radius); + + public abstract double[] first(); + public abstract double[] last(); + +} diff --git a/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java b/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java index daaa143d..88733b9 100644 --- a/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java @@ -1,5 +1,5 @@ /* GLightweightPeer.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -54,11 +54,14 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.awt.event.PaintEvent; import java.awt.image.ColorModel; import java.awt.image.ImageObserver; import java.awt.image.ImageProducer; import java.awt.image.VolatileImage; +import java.awt.peer.ComponentPeer; import java.awt.peer.ContainerPeer; import java.awt.peer.LightweightPeer; @@ -79,7 +82,7 @@ import java.awt.peer.LightweightPeer; * Lightweight components are painted directly onto their parent * containers through an Image object provided by the toolkit. */ -public class GLightweightPeer +public class GLightweightPeer implements LightweightPeer, ContainerPeer { private Component comp; @@ -247,7 +250,25 @@ public class GLightweightPeer public void setBounds(int x, int y, int width, int height) {} - public void setCursor(Cursor cursor) {} + /** + * Sets the cursor on the heavy-weight parent peer. + * Called by the MouseListener on mouse enter. + */ + public void setCursor(Cursor cursor) + { + Component p = comp.getParent(); + while (p != null && p.isLightweight()) + p = p.getParent(); + + if (p != null) + { + // Don't actually change the cursor of the component + // otherwise other childs inherit this cursor. + ComponentPeer peer = p.getPeer(); + if (peer != null) + peer.setCursor(cursor); + } + } public void setEnabled(boolean enabled) {} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontMetrics.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontMetrics.java index c79f403..b2ffed1 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontMetrics.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontMetrics.java @@ -110,14 +110,10 @@ public class GdkFontMetrics extends FontMetrics return stringWidth (new String (data, off, len)); } - /* - Sun's Motif implementation always returns 0 or 1 here (???), but - going by the X11 man pages, it seems as though we should return - font.ascent + font.descent. - */ public int getLeading () { - return 1; + // Sun always returns 0. + return 0; } public int getAscent () diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java index c6d42b3..8274448 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java @@ -1,5 +1,5 @@ /* GdkFontPeer.java -- Implements FontPeer with GTK+ - Copyright (C) 1999, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,6 +43,7 @@ import gnu.java.awt.peer.ClasspathFontPeer; import java.awt.Font; import java.awt.FontMetrics; +import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.font.LineMetrics; @@ -157,7 +158,7 @@ public class GdkFontPeer extends ClasspathFontPeer public String getPostScriptName(Font font) { - return null; + return this.familyName; } public boolean canDisplay (Font font, char c) @@ -301,7 +302,9 @@ public class GdkFontPeer extends ClasspathFontPeer public FontMetrics getFontMetrics (Font font) { - return new GdkFontMetrics (font); + // Get the font metrics through GtkToolkit to take advantage of + // the metrics cache. + return Toolkit.getDefaultToolkit().getFontMetrics (font); } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java index d7217aa..3c3cbdf 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java @@ -48,6 +48,7 @@ import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.Toolkit; import java.awt.image.ImageObserver; import java.text.AttributedCharacterIterator; @@ -218,8 +219,10 @@ public class GdkGraphics extends Graphics public boolean drawImage (Image img, int x, int y, Color bgcolor, ImageObserver observer) { - return drawImage(img, x, y, img.getWidth(null), img.getHeight(null), - bgcolor, observer); + if (img != null) + return drawImage(img, x, y, img.getWidth(null), img.getHeight(null), + bgcolor, observer); + return false; } public boolean drawImage (Image img, int x, int y, ImageObserver observer) @@ -227,16 +230,19 @@ public class GdkGraphics extends Graphics return drawImage (img, x, y, null, observer); } - public boolean drawImage (Image img, int x, int y, int width, int height, - Color bgcolor, ImageObserver observer) + public boolean drawImage(Image img, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) { - if (img instanceof GtkImage) - return ((GtkImage)img).drawImage (this, x, y, width, height, - bgcolor, observer); - else - return (new GtkImage(img.getSource())).drawImage (this, x, y, - width, height, - bgcolor, observer); + if (img != null) + { + if (img instanceof GtkImage) + return ((GtkImage) img).drawImage(this, x, y, width, height, bgcolor, + observer); + return (new GtkImage(img.getSource())).drawImage(this, x, y, width, + height, bgcolor, + observer); + } + return false; } public boolean drawImage (Image img, int x, int y, int width, int height, @@ -249,14 +255,16 @@ public class GdkGraphics extends Graphics int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { - if (img instanceof GtkImage) - return ((GtkImage)img).drawImage(this, dx1, dy1, dx2, dy2, - sx1, sy1, sx2, sy2, bgcolor, observer); - else - return (new GtkImage(img.getSource())).drawImage(this, dx1, dy1, - dx2, dy2, - sx1, sy1, sx2, sy2, - bgcolor, observer); + if (img != null) + { + if (img instanceof GtkImage) + return ((GtkImage) img).drawImage(this, dx1, dy1, dx2, dy2, sx1, sy1, + sx2, sy2, bgcolor, observer); + return (new GtkImage(img.getSource())).drawImage(this, dx1, dy1, dx2, + dy2, sx1, sy1, sx2, + sy2, bgcolor, observer); + } + return false; } public boolean drawImage (Image img, int dx1, int dy1, int dx2, int dy2, @@ -373,7 +381,9 @@ public class GdkGraphics extends Graphics public FontMetrics getFontMetrics (Font font) { - return new GdkFontMetrics (font); + // Get the font metrics through GtkToolkit to take advantage of + // the metrics cache. + return Toolkit.getDefaultToolkit().getFontMetrics (font); } native void setClipRectangle (int x, int y, int width, int height); diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java index 195304d..323d561 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java @@ -1229,7 +1229,10 @@ public class GdkGraphics2D extends Graphics2D drawPixels(pixels, r.getWidth(), r.getHeight(), r.getWidth(), i2u); updateBufferedImage(); - + + // Cairo seems loosing the current color. + setColor(fg); + return true; } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java index 054ebaa..72908ff 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java @@ -1,5 +1,5 @@ /* GdkPixbufDecoder.java -- Image data decoding object - Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -82,6 +82,14 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder initStaticState (); } + /** + * Lock that should be held for all gdkpixbuf operations. We don't use + * the global gdk_threads_enter/leave functions since gdkpixbuf + * operations can be done in parallel to drawing and manipulating gtk + * widgets. + */ + static Object pixbufLock = new Object(); + static native void initStaticState(); private final int native_state = GtkGenericPeer.getUniqueInteger (); @@ -92,6 +100,7 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder Vector curr; // interface to GdkPixbuf + // These native functions should be called with the pixbufLock held. native void initState (); native void pumpBytes (byte[] bytes, int len) throws IOException; native void pumpDone () throws IOException; @@ -171,11 +180,26 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder byte bytes[] = new byte[4096]; int len = 0; - initState(); + synchronized(pixbufLock) + { + initState(); + } needsClose = true; + + // Note: We don't want the pixbufLock while reading from the InputStream. while ((len = is.read (bytes)) != -1) - pumpBytes (bytes, len); - pumpDone(); + { + synchronized(pixbufLock) + { + pumpBytes (bytes, len); + } + } + + synchronized(pixbufLock) + { + pumpDone(); + } + needsClose = false; for (int i = 0; i < curr.size (); i++) @@ -189,7 +213,10 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder public void finalize() { - finish(needsClose); + synchronized(pixbufLock) + { + finish(needsClose); + } } @@ -495,8 +522,11 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder } processImageStarted(1); - streamImage(pixels, this.ext, width, height, model.hasAlpha(), - (DataOutput) this.getOutput()); + synchronized(pixbufLock) + { + streamImage(pixels, this.ext, width, height, model.hasAlpha(), + (DataOutput) this.getOutput()); + } processImageComplete(); } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java index dc21761..797d653 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java @@ -38,61 +38,22 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; -import java.awt.AWTEvent; import java.awt.Canvas; import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.event.PaintEvent; import java.awt.peer.CanvasPeer; public class GtkCanvasPeer extends GtkComponentPeer implements CanvasPeer { native void create (); + native void realize (); public GtkCanvasPeer (Canvas c) { super (c); } - public Graphics getGraphics () - { - if (GtkToolkit.useGraphics2D ()) - return new GdkGraphics2D (this); - else - return new GdkGraphics (this); - } - - public void handleEvent (AWTEvent event) - { - int id = event.getID(); - - switch (id) - { - case PaintEvent.PAINT: - case PaintEvent.UPDATE: - { - try - { - Graphics g = getGraphics (); - g.setClip (((PaintEvent)event).getUpdateRect()); - - if (id == PaintEvent.PAINT) - awtComponent.paint (g); - else - awtComponent.update (g); - - g.dispose (); - } - catch (InternalError e) - { - System.err.println (e); - } - } - break; - } - } - - /* Preferred size for a drawing widget is always what the user requested */ + // Preferred size for a drawing widget is always what the user + // requested. public Dimension getPreferredSize () { return awtComponent.getSize (); diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java index a502e1f..f520fe2 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java @@ -1,5 +1,5 @@ /* GtkClipboard.java - Copyright (C) 1999, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -48,59 +48,85 @@ import java.util.Iterator; public class GtkClipboard extends Clipboard { + /** + * The one and only gtk+ clipboard instance for the CLIPBOARD selection. + */ + final static GtkClipboard clipboard = new GtkClipboard("System Clipboard"); + + /** + * The one and only gtk+ clipboard instance for the PRIMARY selection. + */ + final static GtkClipboard selection = new GtkClipboard("System Selection"); // Given to the native side so it can signal special targets that // can be converted to one of the special predefined DataFlavors. - static final String stringMimeType; - static final String imageMimeType; - static final String filesMimeType; + static final String stringMimeType + = DataFlavor.stringFlavor.getMimeType(); + static final String imageMimeType + = DataFlavor.imageFlavor.getMimeType(); + static final String filesMimeType + = DataFlavor.javaFileListFlavor.getMimeType(); // Indicates whether the results of the clipboard selection can be // cached by GtkSelection. True if // gdk_display_supports_selection_notification. - static final boolean canCache; - - static - { - stringMimeType = DataFlavor.stringFlavor.getMimeType(); - imageMimeType = DataFlavor.imageFlavor.getMimeType(); - filesMimeType = DataFlavor.javaFileListFlavor.getMimeType(); - - canCache = initNativeState(stringMimeType, imageMimeType, filesMimeType); - } - - /** - * The one and only gtk+ clipboard instance. - */ - private static GtkClipboard instance = new GtkClipboard(); + static final boolean canCache = initNativeState(clipboard, selection, + stringMimeType, + imageMimeType, + filesMimeType); /** * Creates the clipboard and sets the initial contents to the * current gtk+ selection. */ - private GtkClipboard() + private GtkClipboard(String name) { - super("System Clipboard"); - setContents(new GtkSelection(), null); + super(name); + setContents(new GtkSelection(this), null); } /** - * Returns the one and only GtkClipboard instance. + * Returns the one and only GtkClipboard instance for the CLIPBOARD + * selection. */ + static GtkClipboard getClipboardInstance() + { + return clipboard; + } - static GtkClipboard getInstance() + /** + * Returns the one and only GtkClipboard instance for the PRIMARY + * selection. + */ + static GtkClipboard getSelectionInstance() { - return instance; + return selection; } /** * Sets the GtkSelection facade as new contents of the clipboard. * Called from gtk+ when another application grabs the clipboard and * we loose ownership. + * + * @param cleared If true this is a clear event (someone takes the + * clipboard from us) otherwise it is an owner changed event. */ - private static void setSystemContents() + private synchronized void setSystemContents(boolean cleared) { - GtkClipboardNotifier.announce(); + // We need to notify clipboard owner listeners when we were the + // owner (the selection was explictly set) and someone takes the + // clipboard away from us and asks us the clear any held storage, + // or if we weren't the owner of the clipboard to begin with, but + // the clipboard contents changed. We could refine this and check + // whether the actual available formats did in fact change, but we + // assume listeners will check for that anyway (and if possible we + // ask to cache the available formats so even if multiple + // listeners check after a notification the overhead should be + // minimal). + boolean owner = ! (contents instanceof GtkSelection); + boolean needNotification = (cleared && owner) || (! cleared && ! owner); + if (needNotification) + GtkClipboardNotifier.announce(this); } /** @@ -146,15 +172,12 @@ public class GtkClipboard extends Clipboard || flavor.isRepresentationClassReader()) text = true; - // XXX - We only support automatic image conversion for - // GtkImages at the moment. So explicitly check that we have - // one. if (! images && flavors[i].equals(DataFlavor.imageFlavor)) { try { Object o = contents.getTransferData(DataFlavor.imageFlavor); - if (o instanceof GtkImage) + if (o instanceof Image) images = true; } catch (UnsupportedFlavorException ufe) @@ -265,7 +288,11 @@ public class GtkClipboard extends Clipboard try { - return (GtkImage) contents.getTransferData(DataFlavor.imageFlavor); + Object o = contents.getTransferData(DataFlavor.imageFlavor); + if( o instanceof GtkImage ) + return (GtkImage) o; + else + return new GtkImage(((Image)o).getSource()); } catch (UnsupportedFlavorException ufe) { @@ -384,11 +411,13 @@ public class GtkClipboard extends Clipboard } /** - * Initializes the gtk+ clipboard and caches any native side + * Initializes the gtk+ clipboards and caches any native side * structures needed. Returns whether or not the contents of the * Clipboard can be cached (gdk_display_supports_selection_notification). */ - private static native boolean initNativeState(String stringTarget, + private static native boolean initNativeState(GtkClipboard clipboard, + GtkClipboard selection, + String stringTarget, String imageTarget, String filesTarget); } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java index a470fe1..fdc7d50 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java @@ -39,11 +39,15 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; import java.awt.datatransfer.*; +import java.util.*; class GtkClipboardNotifier extends Thread { - /** Whether or not to announce a GtkSelection change. */ - private static boolean announceChange; + /** Whether to announce a new GtkSelection has been set for CLIPBOARD. */ + static private boolean announceClipboardChange; + + /** Whether to announce a new GtkSelection has been set for PRIMARY. */ + static private boolean announcePrimaryChange; /** * The one and only instance. All operations are synchronized on @@ -64,24 +68,30 @@ class GtkClipboardNotifier extends Thread /** * Notifies that a new GtkSelection has to be announced. + * + * @param clipboard either the GtkClipboard.clipboard or the + * GtkClipboard.selection. */ - static void announce() + static void announce(GtkClipboard clipboard) { synchronized (notifier) { - announceChange = true; + if (clipboard == GtkClipboard.clipboard) + announceClipboardChange = true; + else + announcePrimaryChange = true; notifier.notifyAll(); } } public void run() { - final GtkClipboard clipboard = GtkClipboard.getInstance(); + GtkClipboard clipboard; while (true) { synchronized (this) { - while (!announceChange) + while (! announceClipboardChange && ! announcePrimaryChange) { try { @@ -92,14 +102,24 @@ class GtkClipboardNotifier extends Thread // ignore } } - announceChange = false; + + if (announceClipboardChange) + { + clipboard = GtkClipboard.clipboard; + announceClipboardChange = false; + } + else + { + clipboard = GtkClipboard.selection; + announcePrimaryChange = false; + } } // Do the actual announcement without the lock held. We will // notice a new change after this notification has finished. try { - clipboard.setContents(new GtkSelection(), null); + clipboard.setContents(new GtkSelection(clipboard), null); } catch (Throwable t) { diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java index 8211839..1a85de5 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java @@ -63,6 +63,7 @@ import java.awt.event.FocusEvent; import java.awt.event.ItemEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; import java.awt.event.PaintEvent; import java.awt.event.TextEvent; import java.awt.image.BufferedImage; @@ -86,8 +87,6 @@ public class GtkComponentPeer extends GtkGenericPeer Insets insets; - boolean isInRepaint; - /* this isEnabled differs from Component.isEnabled, in that it knows if a parent is disabled. In that case Component.isEnabled may return true, but our isEnabled will always return false */ @@ -100,8 +99,9 @@ public class GtkComponentPeer extends GtkGenericPeer native void gtkWidgetGetPreferredDimensions (int[] dim); native void gtkWindowGetLocationOnScreen (int[] point); native void gtkWidgetGetLocationOnScreen (int[] point); - native void gtkWidgetSetCursor (int type); - native void gtkWidgetSetCursorUnlocked (int type); + native void gtkWidgetSetCursor (int type, GtkImage image, int x, int y); + native void gtkWidgetSetCursorUnlocked (int type, GtkImage image, + int x, int y); native void gtkWidgetSetBackground (int red, int green, int blue); native void gtkWidgetSetForeground (int red, int green, int blue); native void gtkWidgetSetSensitive (boolean sensitive); @@ -150,6 +150,9 @@ public class GtkComponentPeer extends GtkGenericPeer setNativeEventMask (); realize (); + + if (awtComponent.isCursorSet()) + setCursor (); } void setParentAndBounds () @@ -176,16 +179,6 @@ public class GtkComponentPeer extends GtkGenericPeer gtkWidgetSetParent (p); } - void beginNativeRepaint () - { - isInRepaint = true; - } - - void endNativeRepaint () - { - isInRepaint = false; - } - /* * Set the bounds of this peer's AWT Component based on dimensions * returned by the native windowing system. Most Components impose @@ -250,6 +243,8 @@ public class GtkComponentPeer extends GtkGenericPeer return getToolkit().getFontMetrics(font); } + // getGraphics may be overridden by derived classes but it should + // never return null. public Graphics getGraphics () { if (GtkToolkit.useGraphics2D ()) @@ -291,30 +286,10 @@ public class GtkComponentPeer extends GtkGenericPeer switch (id) { case PaintEvent.PAINT: + paintComponent((PaintEvent) event); + break; case PaintEvent.UPDATE: - { - try - { - Graphics g = getGraphics(); - - if (!awtComponent.isShowing() || awtComponent.getWidth() < 1 - || awtComponent.getHeight() < 1 || g == null) - break; - - g.setClip(((PaintEvent) event).getUpdateRect()); - - if (id == PaintEvent.PAINT) - awtComponent.paint(g); - else - awtComponent.update(g); - - g.dispose(); - } - catch (InternalError e) - { - System.err.println(e); - } - } + updateComponent((PaintEvent) event); break; case KeyEvent.KEY_PRESSED: ke = (KeyEvent) event; @@ -328,7 +303,49 @@ public class GtkComponentPeer extends GtkGenericPeer break; } } - + + // This method and its overrides are the only methods in the peers + // that should call awtComponent.paint. + protected void paintComponent (PaintEvent event) + { + // Do not call Component.paint if the component is not showing or + // if its bounds form a degenerate rectangle. + if (!awtComponent.isShowing() + || (awtComponent.getWidth() < 1 || awtComponent.getHeight() < 1)) + return; + + // Creating and disposing a GdkGraphics every time paint is called + // seems expensive. However, the graphics state does not carry + // over between calls to paint, and resetting the graphics object + // may even be more costly than simply creating a new one. + Graphics g = getGraphics(); + + g.setClip(event.getUpdateRect()); + + awtComponent.paint(g); + + g.dispose(); + } + + // This method and its overrides are the only methods in the peers + // that should call awtComponent.update. + protected void updateComponent (PaintEvent event) + { + // Do not call Component.update if the component is not showing or + // if its bounds form a degenerate rectangle. + if (!awtComponent.isShowing() + || (awtComponent.getWidth() < 1 || awtComponent.getHeight() < 1)) + return; + + Graphics g = getGraphics(); + + g.setClip(event.getUpdateRect()); + + awtComponent.update(g); + + g.dispose(); + } + public boolean isFocusTraversable () { return true; @@ -369,7 +386,7 @@ public class GtkComponentPeer extends GtkGenericPeer public void repaint (long tm, int x, int y, int width, int height) { - if (x == 0 && y == 0 && width == 0 && height == 0) + if (width < 1 || height < 1) return; if (tm <= 0) @@ -490,10 +507,28 @@ public class GtkComponentPeer extends GtkGenericPeer public void setCursor (Cursor cursor) { + int x, y; + GtkImage image; + int type = cursor.getType(); + if (cursor instanceof GtkCursor) + { + GtkCursor gtkCursor = (GtkCursor) cursor; + image = gtkCursor.getGtkImage(); + Point hotspot = gtkCursor.getHotspot(); + x = hotspot.x; + y = hotspot.y; + } + else + { + image = null; + x = 0; + y = 0; + } + if (Thread.currentThread() == GtkToolkit.mainThread) - gtkWidgetSetCursorUnlocked (cursor.getType ()); + gtkWidgetSetCursorUnlocked(cursor.getType(), image, x, y); else - gtkWidgetSetCursor (cursor.getType ()); + gtkWidgetSetCursor(cursor.getType(), image, x, y); } public void setEnabled (boolean b) @@ -532,7 +567,7 @@ public class GtkComponentPeer extends GtkGenericPeer public void setVisible (boolean b) { // Only really set visible when component is bigger than zero pixels. - if (b) + if (b && ! (awtComponent instanceof Window)) { Rectangle bounds = awtComponent.getBounds(); b = (bounds.width > 0) && (bounds.height > 0); @@ -561,10 +596,22 @@ public class GtkComponentPeer extends GtkGenericPeer clickCount, popupTrigger)); } + /** + * Callback for component_scroll_cb. + */ + protected void postMouseWheelEvent(int id, long when, int mods, + int x, int y, int clickCount, + boolean popupTrigger, + int type, int amount, int rotation) + { + q().postEvent(new MouseWheelEvent(awtComponent, id, when, mods, + x, y, clickCount, popupTrigger, + type, amount, rotation)); + } + protected void postExposeEvent (int x, int y, int width, int height) { - if (!isInRepaint) - q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT, + q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT, new Rectangle (x, y, width, height))); } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkContainerPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkContainerPeer.java index 23737b0..06076e7 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkContainerPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkContainerPeer.java @@ -99,11 +99,6 @@ public class GtkContainerPeer extends GtkComponentPeer } } - public Graphics getGraphics () - { - return super.getGraphics(); - } - public void beginLayout () { } public void endLayout () { } public boolean isPaintPending () { return false; } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCursor.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCursor.java new file mode 100644 index 0000000..eb9e713 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCursor.java @@ -0,0 +1,72 @@ +/* GtkCursor.java -- Simple wrapper for custom cursor. + 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.java.awt.peer.gtk; + +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; + +/** + * Simple wrapper for custom Cursor. + */ +public class GtkCursor extends Cursor +{ + private final GtkImage image; + private final Point hotspot; + + GtkCursor(Image image, Point hotspot, String name) + { + super(name); + if (image instanceof GtkImage) + this.image = (GtkImage) image; + else + this.image = new GtkImage(image.getSource()); + this.hotspot = hotspot; + } + + GtkImage getGtkImage() + { + return image; + } + + Point getHotspot() + { + return hotspot; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkDialogPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkDialogPeer.java index 3e3125a..3254f56 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkDialogPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkDialogPeer.java @@ -51,34 +51,6 @@ public class GtkDialogPeer extends GtkWindowPeer { super (dialog); } - - public Graphics getGraphics () - { - Graphics g; - if (GtkToolkit.useGraphics2D ()) - g = new GdkGraphics2D (this); - else - g = new GdkGraphics (this); - g.translate (-insets.left, -insets.top); - return g; - } - - protected void postMouseEvent(int id, long when, int mods, int x, int y, - int clickCount, boolean popupTrigger) - { - super.postMouseEvent (id, when, mods, - x + insets.left, y + insets.top, - clickCount, popupTrigger); - } - - protected void postExposeEvent (int x, int y, int width, int height) - { - if (!isInRepaint) - q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT, - new Rectangle (x + insets.left, - y + insets.top, - width, height))); - } void create () { diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java index a0ae9e9..8650578 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java @@ -41,6 +41,7 @@ package gnu.java.awt.peer.gtk; import java.awt.Dialog; import java.awt.FileDialog; import java.awt.Graphics; +import java.awt.event.PaintEvent; import java.awt.peer.FileDialogPeer; import java.io.File; import java.io.FilenameFilter; @@ -166,10 +167,10 @@ public class GtkFileDialogPeer extends GtkDialogPeer implements FileDialogPeer return filter.accept(dir, filename); } - public Graphics getGraphics () + // Sun does not call FileDialog.update. + protected void updateComponent (PaintEvent event) { - // GtkFileDialog will repaint by itself - return null; + // Override GtkComponetPeer.updateComponent to do nothing. } // called back by native side: handle_response_cb diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFontPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFontPeer.java deleted file mode 100644 index 80ad158..0000000 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFontPeer.java +++ /dev/null @@ -1,225 +0,0 @@ -/* GtkFontPeer.java -- Implements FontPeer with GTK+ - Copyright (C) 1999, 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., 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.java.awt.peer.gtk; - -import gnu.java.awt.peer.ClasspathFontPeer; - -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.font.FontRenderContext; -import java.awt.font.GlyphVector; -import java.awt.font.LineMetrics; -import java.awt.geom.Rectangle2D; -import java.text.CharacterIterator; -import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -public class GtkFontPeer extends ClasspathFontPeer -{ - private static ResourceBundle bundle; - - static - { - try - { - bundle = ResourceBundle.getBundle ("gnu.java.awt.peer.gtk.font"); - } - catch (Throwable ignored) - { - bundle = null; - } - } - - private final String Xname; - - public GtkFontPeer (String name, int style) - { - // All fonts get a default size of 12 if size is not specified. - this(name, style, 12); - } - - public GtkFontPeer (String name, int style, int size) - { - super(name, style, size); - - String Xname = null; - if (bundle != null) - { - try - { - Xname = bundle.getString (name.toLowerCase () + "." + style); - } - catch (MissingResourceException mre) - { - // ignored - } - } - - if (Xname == null) - { - String weight; - String slant; - String spacing; - - if (style == Font.ITALIC || (style == (Font.BOLD+Font.ITALIC))) - slant = "i"; - else - slant = "r"; - if (style == Font.BOLD || (style == (Font.BOLD+Font.ITALIC))) - weight = "bold"; - else - weight = "medium"; - if (name.equals("Serif") || name.equals("SansSerif") - || name.equals("Helvetica") || name.equals("Times")) - spacing = "p"; - else - spacing = "c"; - - Xname = "-*-*-" + weight + "-" + slant + "-normal-*-*-" + size + "-*-*-" + spacing + "-*-*-*"; - } - - this.Xname = Xname; - } - - public String getXLFD () - { - return Xname; - } - - - /* remaining methods are for static compatibility with the newer - ClasspathFontPeer superclass; none of these methods ever existed or - worked on the older FontPeer interface, but we need to pretend to - support them anyways. */ - - public boolean canDisplay (Font font, char c) - { - throw new UnsupportedOperationException(); - } - - public int canDisplayUpTo (Font font, CharacterIterator i, int start, int limit) - { - throw new UnsupportedOperationException(); - } - - public String getSubFamilyName (Font font, Locale locale) - { - throw new UnsupportedOperationException(); - } - - public String getPostScriptName (Font font) - { - throw new UnsupportedOperationException(); - } - - public int getNumGlyphs (Font font) - { - throw new UnsupportedOperationException(); - } - - public int getMissingGlyphCode (Font font) - { - throw new UnsupportedOperationException(); - } - - public byte getBaselineFor (Font font, char c) - { - throw new UnsupportedOperationException(); - } - - public String getGlyphName (Font font, int glyphIndex) - { - throw new UnsupportedOperationException(); - } - - public GlyphVector createGlyphVector (Font font, - FontRenderContext frc, - CharacterIterator ci) - { - throw new UnsupportedOperationException(); - } - - public GlyphVector createGlyphVector (Font font, - FontRenderContext ctx, - int[] glyphCodes) - { - throw new UnsupportedOperationException(); - } - - public GlyphVector layoutGlyphVector (Font font, - FontRenderContext frc, - char[] chars, int start, - int limit, int flags) - { - throw new UnsupportedOperationException(); - } - - public FontMetrics getFontMetrics (Font font) - { - throw new UnsupportedOperationException(); - } - - public boolean hasUniformLineMetrics (Font font) - { - throw new UnsupportedOperationException(); - } - - public LineMetrics getLineMetrics (Font font, - CharacterIterator ci, - int begin, int limit, - FontRenderContext rc) - { - throw new UnsupportedOperationException(); - } - - public Rectangle2D getMaxCharBounds (Font font, - FontRenderContext rc) - { - throw new UnsupportedOperationException(); - } - - public Rectangle2D getStringBounds (Font font, - CharacterIterator ci, - int begin, int limit, - FontRenderContext frc) - { - throw new UnsupportedOperationException(); - } -} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java index f59e781..6ec0b72 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java @@ -1,5 +1,5 @@ /* GtkFramePeer.java -- Implements FramePeer with GTK - Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -122,25 +122,11 @@ public class GtkFramePeer extends GtkWindowPeer public void setBounds (int x, int y, int width, int height) { - // prevent window_configure_cb -> awtComponent.setSize -> - // peer.setBounds -> nativeSetBounds self-deadlock on GDK lock. - if (Thread.currentThread() == GtkToolkit.mainThread) - { - int menuBarWidth = width - insets.left - insets.right; - if (menuBar != null && menuBarWidth > 0) - setMenuBarWidthUnlocked (menuBar, menuBarWidth); - - return; - } - int menuBarWidth = width - insets.left - insets.right; if (menuBar != null && menuBarWidth > 0) setMenuBarWidth (menuBar, menuBarWidth); - nativeSetBounds (x, y, - width - insets.left - insets.right, - height - insets.top - insets.bottom - + menuBarHeight); + super.setBounds(x, y, width, height + menuBarHeight); } public void setResizable (boolean resizable) @@ -196,56 +182,21 @@ public class GtkFramePeer extends GtkWindowPeer } } - public Graphics getGraphics () - { - Graphics g; - if (GtkToolkit.useGraphics2D ()) - g = new GdkGraphics2D (this); - else - g = new GdkGraphics (this); - g.translate (-insets.left, -insets.top); - return g; - } - protected void postConfigureEvent (int x, int y, int width, int height) { - int frame_width = width + insets.left + insets.right; + if (menuBar != null && width > 0) + setMenuBarWidthUnlocked (menuBar, width); + // Since insets.top already includes the MenuBar's height, we need // to subtract the MenuBar's height from the top inset. - int frame_height = height + insets.top + insets.bottom - menuBarHeight; - - if (frame_width != awtComponent.getWidth() - || frame_height != awtComponent.getHeight()) - awtComponent.setSize(frame_width, frame_height); + int frame_height = height - menuBarHeight; - int frame_x = x - insets.left; // Likewise, since insets.top includes the MenuBar height, we need // to add back the MenuBar height to the frame's y position. If // no MenuBar exists in this frame, the MenuBar height will be 0. - int frame_y = y - insets.top + menuBarHeight; + int frame_y = y + menuBarHeight; - if (frame_x != awtComponent.getX() - || frame_y != awtComponent.getY()) - { - // awtComponent.setLocation(frame_x, frame_y); - } - } - - protected void postMouseEvent(int id, long when, int mods, int x, int y, - int clickCount, boolean popupTrigger) - { - super.postMouseEvent (id, when, mods, - x + insets.left, y + insets.top, - clickCount, popupTrigger); - } - - protected void postExposeEvent (int x, int y, int width, int height) - { - if (!isInRepaint) - q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT, - new Rectangle (x + insets.left, - y + insets.top, - width, height))); + super.postConfigureEvent(x, frame_y, width, frame_height); } public int getState () diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java index b48a204..5e5f1de 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java @@ -123,41 +123,50 @@ public class GtkImage extends Image /** * Returns a copy of the pixel data as a java array. + * Should be called with the GdkPixbufDecoder.pixbufLock held. */ private native int[] getPixels(); /** * Sets the pixel data from a java array. + * Should be called with the GdkPixbufDecoder.pixbufLock held. */ private native void setPixels(int[] pixels); /** * Loads an image using gdk-pixbuf from a file. + * Should be called with the GdkPixbufDecoder.pixbufLock held. */ private native boolean loadPixbuf(String name); /** * Loads an image using gdk-pixbuf from data. + * Should be called with the GdkPixbufDecoder.pixbufLock held. */ private native boolean loadImageFromData(byte[] data); /** * Allocates a Gtk Pixbuf or pixmap + * Should be called with the GdkPixbufDecoder.pixbufLock held. */ private native void createPixmap(); /** * Frees the above. + * Should be called with the GdkPixbufDecoder.pixbufLock held. */ private native void freePixmap(); /** * Sets the pixmap to scaled copy of src image. hints are rendering hints. + * Should be called with the GdkPixbufDecoder.pixbufLock held. */ private native void createScaledPixmap(GtkImage src, int hints); /** * Draws the image, optionally scaled and composited. + * Should be called with the GdkPixbufDecoder.pixbufLock held. + * Also acquires global gdk lock for drawing. */ private native void drawPixelsScaled (GdkGraphics gc, int bg_red, int bg_green, int bg_blue, @@ -166,6 +175,8 @@ public class GtkImage extends Image /** * Draws the image, optionally scaled flipped and composited. + * Should be called with the GdkPixbufDecoder.pixbufLock held. + * Also acquires global gdk lock for drawing. */ private native void drawPixelsScaledFlipped (GdkGraphics gc, int bg_red, int bg_green, @@ -219,12 +230,21 @@ public class GtkImage extends Image File f = new File(filename); try { - if (loadPixbuf(f.getCanonicalPath()) != true) - throw new IllegalArgumentException("Couldn't load image: "+filename); + String path = f.getCanonicalPath(); + synchronized(GdkPixbufDecoder.pixbufLock) + { + if (loadPixbuf(f.getCanonicalPath()) != true) + throw new IllegalArgumentException("Couldn't load image: " + + filename); + } } catch(IOException e) { - throw new IllegalArgumentException("Couldn't load image: "+filename); + IllegalArgumentException iae; + iae = new IllegalArgumentException("Couldn't load image: " + + filename); + iae.initCause(e); + throw iae; } isLoaded = true; @@ -241,8 +261,11 @@ public class GtkImage extends Image */ public GtkImage (byte[] data) { - if (loadImageFromData (data) != true) - throw new IllegalArgumentException ("Couldn't load image."); + synchronized(GdkPixbufDecoder.pixbufLock) + { + if (loadImageFromData (data) != true) + throw new IllegalArgumentException ("Couldn't load image."); + } isLoaded = true; observers = null; @@ -277,8 +300,12 @@ public class GtkImage extends Image { throw new IllegalArgumentException ("Couldn't load image."); } - if (loadImageFromData (baos.toByteArray()) != true) - throw new IllegalArgumentException ("Couldn't load image."); + byte[] array = baos.toByteArray(); + synchronized(GdkPixbufDecoder.pixbufLock) + { + if (loadImageFromData(array) != true) + throw new IllegalArgumentException ("Couldn't load image."); + } isLoaded = true; observers = null; @@ -296,7 +323,10 @@ public class GtkImage extends Image isLoaded = true; observers = null; offScreen = true; - createPixmap(); + synchronized(GdkPixbufDecoder.pixbufLock) + { + createPixmap(); + } } /** @@ -312,7 +342,10 @@ public class GtkImage extends Image offScreen = false; // Use the GDK scaling method. - createScaledPixmap(src, hints); + synchronized(GdkPixbufDecoder.pixbufLock) + { + createScaledPixmap(src, hints); + } } /** @@ -322,7 +355,10 @@ public class GtkImage extends Image GtkImage (Pointer pixbuf) { pixmap = pixbuf; - createFromPixbuf(); + synchronized(GdkPixbufDecoder.pixbufLock) + { + createFromPixbuf(); + } isLoaded = true; observers = null; offScreen = false; @@ -349,6 +385,7 @@ public class GtkImage extends Image /** * Native helper function for constructor that takes a pixbuf Pointer. + * Should be called with the GdkPixbufDecoder.pixbufLock held. */ private native void createFromPixbuf(); @@ -370,8 +407,11 @@ public class GtkImage extends Image isLoaded = true; deliver(); - createPixmap(); - setPixels(pixels); + synchronized(GdkPixbufDecoder.pixbufLock) + { + createPixmap(); + setPixels(pixels); + } } // java.awt.Image methods //////////////////////////////////////////////// @@ -408,7 +448,13 @@ public class GtkImage extends Image { if (!isLoaded) return null; - return new MemoryImageSource(width, height, nativeModel, getPixels(), + + int[] pixels; + synchronized(GdkPixbufDecoder.pixbufLock) + { + pixels = getPixels(); + } + return new MemoryImageSource(width, height, nativeModel, pixels, 0, width); } @@ -454,7 +500,10 @@ public class GtkImage extends Image { observers = new Vector(); isLoaded = false; - freePixmap(); + synchronized(GdkPixbufDecoder.pixbufLock) + { + freePixmap(); + } source.startProduction(new GtkImageConsumer(this, source)); } } @@ -462,7 +511,12 @@ public class GtkImage extends Image public void finalize() { if (isLoaded) - freePixmap(); + { + synchronized(GdkPixbufDecoder.pixbufLock) + { + freePixmap(); + } + } } /** @@ -529,23 +583,29 @@ public class GtkImage extends Image srcHeight = height - srcY; } + if ( this.width <= 0 || this.height <= 0 ) + return true; + if ( srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0) return true; - if(bgcolor != null) - drawPixelsScaledFlipped (g, bgcolor.getRed (), bgcolor.getGreen (), - bgcolor.getBlue (), - flipX, flipY, - srcX, srcY, - srcWidth, srcHeight, - dstX, dstY, - dstWidth, dstHeight, - true); - else - drawPixelsScaledFlipped (g, 0, 0, 0, flipX, flipY, - srcX, srcY, srcWidth, srcHeight, - dstX, dstY, dstWidth, dstHeight, - false); + synchronized(GdkPixbufDecoder.pixbufLock) + { + if(bgcolor != null) + drawPixelsScaledFlipped (g, bgcolor.getRed (), bgcolor.getGreen (), + bgcolor.getBlue (), + flipX, flipY, + srcX, srcY, + srcWidth, srcHeight, + dstX, dstY, + dstWidth, dstHeight, + true); + else + drawPixelsScaledFlipped (g, 0, 0, 0, flipX, flipY, + srcX, srcY, srcWidth, srcHeight, + dstX, dstY, dstWidth, dstHeight, + false); + } return true; } @@ -559,11 +619,17 @@ public class GtkImage extends Image if (addObserver(observer)) return false; - if(bgcolor != null) - drawPixelsScaled(g, bgcolor.getRed (), bgcolor.getGreen (), - bgcolor.getBlue (), x, y, width, height, true); - else - drawPixelsScaled(g, 0, 0, 0, x, y, width, height, false); + if ( this.width <= 0 || this.height <= 0 ) + return true; + + synchronized(GdkPixbufDecoder.pixbufLock) + { + if(bgcolor != null) + drawPixelsScaled(g, bgcolor.getRed (), bgcolor.getGreen (), + bgcolor.getBlue (), x, y, width, height, true); + else + drawPixelsScaled(g, 0, 0, 0, x, y, width, height, false); + } return true; } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkListPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkListPeer.java index 285f794..bd6ec0a 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkListPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkListPeer.java @@ -40,6 +40,7 @@ package gnu.java.awt.peer.gtk; import java.awt.AWTEvent; import java.awt.Dimension; +import java.awt.FontMetrics; import java.awt.List; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -120,11 +121,12 @@ public class GtkListPeer extends GtkComponentPeer public Dimension preferredSize (int rows) { - int dims[] = new int[2]; - - int visibleRows = ((List) awtComponent).getRows(); - getSize (rows, visibleRows, dims); - return new Dimension (dims[0], dims[1]); + // getSize returns the minimum size of the list. + // The width is too narrow for a typical list. + List l = (List) awtComponent; + Dimension d = getMinimumSize(); + FontMetrics fm = l.getFontMetrics(l.getFont()); + return new Dimension(d.width + fm.stringWidth("1234567890"), d.height); } public void removeAll () diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkPanelPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkPanelPeer.java index 88bb715..51fe2bc 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkPanelPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkPanelPeer.java @@ -39,9 +39,7 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; import java.awt.AWTEvent; -import java.awt.Graphics; import java.awt.Panel; -import java.awt.event.ComponentEvent; import java.awt.event.MouseEvent; import java.awt.event.PaintEvent; import java.awt.peer.PanelPeer; @@ -59,35 +57,10 @@ public class GtkPanelPeer extends GtkContainerPeer public void handleEvent(AWTEvent event) { int id = event.getID(); - switch (id) - { - case MouseEvent.MOUSE_PRESSED: - awtComponent.requestFocusInWindow(); - break; - case PaintEvent.UPDATE: - case PaintEvent.PAINT: - { - try - { - Graphics g = getGraphics(); - if (! awtComponent.isShowing() || awtComponent.getWidth() < 1 - || awtComponent.getHeight() < 1 || g == null) - return; - g.setClip(((PaintEvent) event).getUpdateRect()); - - // Do not want to clear anything before painting.); - awtComponent.paint(g); - - g.dispose(); - return; - } - catch (InternalError e) - { - System.err.println(e); - } - } - } + if (id == MouseEvent.MOUSE_PRESSED) + awtComponent.requestFocusInWindow(); + super.handleEvent(event); } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkSelection.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkSelection.java index 08b6b66..6a92f86 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkSelection.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkSelection.java @@ -55,9 +55,6 @@ import java.awt.Image; * that the available flavors might have changed. When requested it * (lazily) caches the targets, and (text, image, or files/uris) * clipboard contents. - * - * XXX - should only cache when - * gdk_display_supports_selection_notification is true. */ public class GtkSelection implements Transferable { @@ -67,6 +64,11 @@ public class GtkSelection implements Transferable static private Object requestLock = new Object(); /** + * Whether we belong to the Clipboard (true) or to the Primary selection. + */ + private final boolean clipboard; + + /** * Whether a request for mimetypes, text, images, uris or byte[] is * currently in progress. Should only be tested or set with * requestLock held. When true no other requests should be made till @@ -143,10 +145,13 @@ public class GtkSelection implements Transferable private byte[] bytes; /** - * Should only be created by the GtkClipboard class. + * Should only be created by the GtkClipboard class. The clipboard + * should be either GtkClipboard.clipboard or + * GtkClipboard.selection. */ - GtkSelection() + GtkSelection(GtkClipboard clipboard) { + this.clipboard = (clipboard == GtkClipboard.clipboard); } /** @@ -181,7 +186,7 @@ public class GtkSelection implements Transferable if (! mimeTypesDelivered) { requestInProgress = true; - requestMimeTypes(); + requestMimeTypes(clipboard); while (! mimeTypesDelivered) { try @@ -312,7 +317,7 @@ public class GtkSelection implements Transferable if (! textDelivered) { requestInProgress = true; - requestText(); + requestText(clipboard); while (! textDelivered) { try @@ -385,7 +390,7 @@ public class GtkSelection implements Transferable if (! imageDelivered) { requestInProgress = true; - requestImage(); + requestImage(clipboard); while (! imageDelivered) { try @@ -464,7 +469,7 @@ public class GtkSelection implements Transferable if (! urisDelivered) { requestInProgress = true; - requestURIs(); + requestURIs(clipboard); while (! urisDelivered) { try @@ -547,7 +552,7 @@ public class GtkSelection implements Transferable // Request bytes and wait till they are available. requestInProgress = true; - requestBytes(target); + requestBytes(clipboard, target); while (! bytesDelivered) { try @@ -653,12 +658,14 @@ public class GtkSelection implements Transferable * content is available the contentLock will be notified through * textAvailable, imageAvailable, urisAvailable or bytesAvailable and the * appropriate field is set. + * The clipboard argument is true if we want the Clipboard, and false + * if we want the (primary) selection. */ - private native void requestText(); - private native void requestImage(); - private native void requestURIs(); - private native void requestBytes(String target); + private native void requestText(boolean clipboard); + private native void requestImage(boolean clipboard); + private native void requestURIs(boolean clipboard); + private native void requestBytes(boolean clipboard, String target); /* Similar to the above but for requesting the supported targets. */ - private native void requestMimeTypes(); + private native void requestMimeTypes(boolean clipboard); } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java index 70e25a3..7757db0 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java @@ -361,7 +361,16 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit if (secman != null) secman.checkSystemClipboardAccess(); - return GtkClipboard.getInstance(); + return GtkClipboard.getClipboardInstance(); + } + + public Clipboard getSystemSelection() + { + SecurityManager secman = System.getSecurityManager(); + if (secman != null) + secman.checkSystemClipboardAccess(); + + return GtkClipboard.getSelectionInstance(); } /** @@ -570,6 +579,11 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit return q; } + public Cursor createCustomCursor(Image image, Point hotspot, String name) + { + return new GtkCursor(image, hotspot, name); + } + protected native void loadSystemColors (int[] systemColors); public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e) diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java index 6cc1390..d15beac 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java @@ -1,5 +1,5 @@ /* GtkWindowPeer.java -- Implements WindowPeer with GTK - Copyright (C) 1998, 1999, 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2002, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,7 +42,9 @@ import java.awt.AWTEvent; import java.awt.Component; import java.awt.Frame; import java.awt.Graphics; +import java.awt.Rectangle; import java.awt.Window; +import java.awt.event.ComponentEvent; import java.awt.event.PaintEvent; import java.awt.event.WindowEvent; import java.awt.peer.WindowPeer; @@ -62,20 +64,37 @@ public class GtkWindowPeer extends GtkContainerPeer private boolean hasBeenShown = false; private int oldState = Frame.NORMAL; + // Cached awt window component location, width and height. + private int x, y, width, height; + native void gtkWindowSetTitle (String title); native void gtkWindowSetResizable (boolean resizable); native void gtkWindowSetModal (boolean modal); native void realize (); + /** Returns the cached width of the AWT window component. */ + int getX () + { + return x; + } + + /** Returns the cached width of the AWT window component. */ + int getY () + { + return y; + } + + /** Returns the cached width of the AWT window component. */ int getWidth () { - return awtComponent.getWidth(); + return width; } + /** Returns the cached height of the AWT window component. */ int getHeight () { - return awtComponent.getHeight(); + return height; } native void create (int type, boolean decorated, GtkWindowPeer parent); @@ -85,6 +104,10 @@ public class GtkWindowPeer extends GtkContainerPeer Window window = (Window) awtComponent; GtkWindowPeer parent_peer = null; Component parent = awtComponent.getParent(); + x = awtComponent.getX(); + y = awtComponent.getY(); + height = awtComponent.getHeight(); + width = awtComponent.getWidth(); if (!window.isFocusableWindow()) type = GDK_WINDOW_TYPE_HINT_MENU; @@ -129,37 +152,28 @@ public class GtkWindowPeer extends GtkContainerPeer native void nativeSetLocation (int x, int y); native void nativeSetLocationUnlocked (int x, int y); - public void setLocation (int x, int y) + // Called from show. + protected void setLocation (int x, int y) { - // prevent window_configure_cb -> awtComponent.setSize -> - // peer.setBounds -> nativeSetBounds self-deadlock on GDK lock. - if (Thread.currentThread() == GtkToolkit.mainThread) - return; nativeSetLocation (x, y); } - public void setLocationUnlocked (int x, int y) - { - nativeSetLocationUnlocked (x, y); - } - public void setBounds (int x, int y, int width, int height) { - // prevent window_configure_cb -> awtComponent.setSize -> - // peer.setBounds -> nativeSetBounds self-deadlock on GDK lock. - if (Thread.currentThread() == GtkToolkit.mainThread) - return; - - nativeSetBounds (x, y, - width - insets.left - insets.right, - height - insets.top - insets.bottom); - } - - public void setBoundsUnlocked (int x, int y, int width, int height) - { - nativeSetBoundsUnlocked (x, y, - width - insets.left - insets.right, - height - insets.top - insets.bottom); + if (x != getX() + || y != getY() + || width != getWidth() + || height != getHeight()) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + nativeSetBounds (x, y, + width - insets.left - insets.right, + height - insets.top - insets.bottom); + } } public void setTitle (String title) @@ -167,15 +181,25 @@ public class GtkWindowPeer extends GtkContainerPeer gtkWindowSetTitle (title); } - native void setSize (int width, int height); - + // Called from setResizable + protected native void setSize (int width, int height); + + /** + * Needed by both GtkFramePeer and GtkDialogPeer subclasses, so + * implemented here. But never actually called on a GtkWindowPeer + * itself. + */ public void setResizable (boolean resizable) { // Call setSize; otherwise when resizable is changed from true to // false the window will shrink to the dimensions it had before it // was resizable. - setSize (awtComponent.getWidth() - insets.left - insets.right, - awtComponent.getHeight() - insets.top - insets.bottom); + x = awtComponent.getX(); + y = awtComponent.getY(); + width = awtComponent.getWidth(); + height = awtComponent.getHeight(); + setSize (width - insets.left - insets.right, + height - insets.top - insets.bottom); gtkWindowSetResizable (resizable); } @@ -195,23 +219,35 @@ public class GtkWindowPeer extends GtkContainerPeer int frame_width = width + insets.left + insets.right; int frame_height = height + insets.top + insets.bottom; - if (frame_width != awtComponent.getWidth() - || frame_height != awtComponent.getHeight()) - awtComponent.setSize(frame_width, frame_height); + if (frame_width != getWidth() + || frame_height != getHeight()) + { + this.width = frame_width; + this.height = frame_height; + q().postEvent(new ComponentEvent(awtComponent, + ComponentEvent.COMPONENT_RESIZED)); + } int frame_x = x - insets.left; int frame_y = y - insets.top; - if (frame_x != awtComponent.getX() - || frame_y != awtComponent.getY()) + if (frame_x != getX() + || frame_y != getY()) { - // awtComponent.setLocation(frame_x, frame_y); + this.x = frame_x; + this.y = frame_y; + q().postEvent(new ComponentEvent(awtComponent, + ComponentEvent.COMPONENT_MOVED)); } } public void show () { - setLocation(awtComponent.getX(), awtComponent.getY()); + x = awtComponent.getX(); + y = awtComponent.getY(); + width = awtComponent.getWidth(); + height = awtComponent.getHeight(); + setLocation(x, y); setVisible (true); } @@ -244,37 +280,62 @@ public class GtkWindowPeer extends GtkContainerPeer // TODO Auto-generated method stub } + + protected void postExposeEvent (int x, int y, int width, int height) + { + // Translate GTK co-ordinates, which do not include a window + // frame's insets, to AWT co-ordinates, which do include a window + // frame's insets. GtkWindowPeer should always have all-zero + // insets but GtkFramePeer and GtkDialogPeer insets will be + // non-zero. + q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT, + new Rectangle (x + insets.left, + y + insets.top, + width, height))); + } + public boolean requestWindowFocus() { // TODO Auto-generated method stub return false; } - public void handleEvent(AWTEvent event) + public Graphics getGraphics () { - int id = event.getID(); - if (id == PaintEvent.UPDATE || id == PaintEvent.PAINT) - { - try - { - Graphics g = getGraphics(); - if (! awtComponent.isShowing() || awtComponent.getWidth() < 1 - || awtComponent.getHeight() < 1 || g == null) - return; - - g.setClip(((PaintEvent) event).getUpdateRect()); - - // Do not want to clear anything before painting. - awtComponent.paint(g); - - g.dispose(); - return; - } - catch (InternalError e) - { - System.err.println(e); - } - } - super.handleEvent(event); + Graphics g = super.getGraphics (); + // Translate AWT co-ordinates, which include a window frame's + // insets, to GTK co-ordinates, which do not include a window + // frame's insets. GtkWindowPeer should always have all-zero + // insets but GtkFramePeer and GtkDialogPeer insets will be + // non-zero. + g.translate (-insets.left, -insets.top); + return g; + } + + protected void updateComponent (PaintEvent event) + { + // Do not clear anything before painting. Sun never calls + // Window.update, only Window.paint. + paintComponent(event); + } + + protected void postMouseEvent(int id, long when, int mods, int x, int y, + int clickCount, boolean popupTrigger) + { + // Translate AWT co-ordinates, which include a window frame's + // insets, to GTK co-ordinates, which do not include a window + // frame's insets. GtkWindowPeer should always have all-zero + // insets but GtkFramePeer and GtkDialogPeer insets will be + // non-zero. + super.postMouseEvent (id, when, mods, + x + insets.left, y + insets.top, + clickCount, popupTrigger); + } + + // We override this to keep it in sync with our internal + // representation. + public Rectangle getBounds() + { + return new Rectangle(x, y, width, height); } } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java index 5e34bc9..5d484e0 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java @@ -590,8 +590,7 @@ public class SwingComponentPeer */ public void setBounds(int x, int y, int width, int height) { - if (swingComponent != null) - swingComponent.getJComponent().setBounds(x, y, width, height); + reshape(x, y, width, height); } /** diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java index 37bea75..0b2fb99 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java @@ -61,7 +61,7 @@ public class SwingContainerPeer * * @param awtCont */ - public SwingContainerPeer(Container awtCont) + public SwingContainerPeer(Component awtCont) { init(awtCont, null); } @@ -92,12 +92,7 @@ public class SwingContainerPeer */ public Insets getInsets() { - Insets retVal; - if (swingComponent != null) - retVal = swingComponent.getJComponent().getInsets(); - else - retVal = new Insets(0, 0, 0, 0); - return retVal; + return insets(); } /** @@ -214,12 +209,15 @@ public class SwingContainerPeer protected void handleMouseEvent(MouseEvent ev) { Component comp = awtComponent.getComponentAt(ev.getPoint()); - ComponentPeer peer = comp.getPeer(); - if (awtComponent != comp && !comp.isLightweight() && peer instanceof SwingComponentPeer) + if (comp != null) { - ev.translatePoint(comp.getX(), comp.getY()); - ev.setSource(comp); - ((SwingComponentPeer) peer).handleMouseEvent(ev); + ComponentPeer peer = comp.getPeer(); + if (awtComponent != comp && !comp.isLightweight() && peer instanceof SwingComponentPeer) + { + ev.translatePoint(comp.getX(), comp.getY()); + ev.setSource(comp); + ((SwingComponentPeer) peer).handleMouseEvent(ev); + } } } @@ -231,11 +229,14 @@ public class SwingContainerPeer protected void handleMouseMotionEvent(MouseEvent ev) { Component comp = awtComponent.getComponentAt(ev.getPoint()); - ComponentPeer peer = comp.getPeer(); - if (awtComponent != comp && !comp.isLightweight() && peer instanceof SwingComponentPeer) + if (comp != null) { - ev.translatePoint(comp.getX(), comp.getY()); - ((SwingComponentPeer) peer).handleMouseMotionEvent(ev); + ComponentPeer peer = comp.getPeer(); + if (awtComponent != comp && !comp.isLightweight() && peer instanceof SwingComponentPeer) + { + ev.translatePoint(comp.getX(), comp.getY()); + ((SwingComponentPeer) peer).handleMouseMotionEvent(ev); + } } } } diff --git a/libjava/classpath/gnu/java/io/class-dependencies.conf b/libjava/classpath/gnu/java/io/class-dependencies.conf deleted file mode 100644 index 2500f6b..0000000 --- a/libjava/classpath/gnu/java/io/class-dependencies.conf +++ /dev/null @@ -1,75 +0,0 @@ -# This property file contains dependencies of classes, methods, and -# field on other methods or classes. -# -# Syntax: -# -# <used>: <needed 1> [... <needed N>] -# -# means that when <used> is included, <needed 1> (... <needed N>) must -# be included as well. -# -# <needed X> and <used> are of the form -# -# <class.methodOrField(signature)> -# -# or just -# -# <class> -# -# Within dependencies, variables can be used. A variable is defined as -# follows: -# -# {variable}: value1 value2 ... value<n> -# -# variables can be used on the right side of dependencies as follows: -# -# <used>: com.bla.blu.{variable}.Class.m()V -# -# The use of the variable will expand to <n> dependencies of the form -# -# <used>: com.bla.blu.value1.Class.m()V -# <used>: com.bla.blu.value2.Class.m()V -# ... -# <used>: com.bla.blu.value<n>.Class.m()V -# -# Variables can be redefined when building a system to select the -# required support for features like encodings, protocols, etc. -# -# Hints: -# -# - For methods and fields, the signature is mandatory. For -# specification, please see the Java Virtual Machine Specification by -# SUN. Unlike in the spec, field signatures (types) are in brackets. -# -# - Package names must be separated by '/' (and not '.'). E.g., -# java/lang/Class (this is necessary, because the '.' is used to -# separate method or field names from classes) -# -# - In case <needed> refers to a class, only the class itself will be -# included in the resulting binary, NOT necessarily all its methods -# and fields. If you want to refer to all methods and fields, you can -# write class.* as an abbreviation. -# -# - Abbreviations for packages are also possible: my/package/* means all -# methods and fields of all classes in my/package. -# -# - A line with a trailing '\' continues in the next line. - - -# All encodings supported are loaded via gnu/java/io/EncodingManager.findEncoderConstructor -# or gnu/java/io/EncodingManager.findDecoderConstructor from class -# gnu/java/io/decode/Decoder<encoding>. -# -# This introduces a dependency for all encodings. To allow an easy selection -# and addition of encodings, the library variable {encodings} can be set to -# the set of supported encodings. -# -{encodings}: 8859_1 8859_2 8859_3 8859_4 8859_5 UTF8 - -gnu/java/io/EncodingManager.findEncoderConstructor(Ljava/lang/String;Z)Ljava/lang/reflect/Constructor;: \ - gnu/java/io/decode/Decoder{encodings}.* - -gnu/java/io/EncodingManager.findDecoderConstructor(Ljava/lang/String;Z)Ljava/lang/reflect/Constructor;: \ - gnu/java/io/encode/Encoder{encodings}.* \ - -# end of file diff --git a/libjava/classpath/gnu/java/lang/ClassHelper.java b/libjava/classpath/gnu/java/lang/ClassHelper.java index 14c8a39..49dce21 100644 --- a/libjava/classpath/gnu/java/lang/ClassHelper.java +++ b/libjava/classpath/gnu/java/lang/ClassHelper.java @@ -81,6 +81,34 @@ public class ClassHelper return name.substring(lastInd + 1); } + /** + * Return the name of the class as written by the user. + * This is used by the various reflection toString methods. + * It differs from {@link Class#getName()} in that it prints + * arrays with trailing "[]"s. Note that it does not treat + * member classes specially, so a dollar sign may still appear + * in the result. This is intentional. + * @param klass the class + * @return a pretty form of the class' name + */ + public static String getUserName(Class klass) + { + int arrayCount = 0; + while (klass.isArray()) + { + ++arrayCount; + klass = klass.getComponentType(); + } + String name = klass.getName(); + if (arrayCount == 0) + return name; + StringBuilder b = new StringBuilder(name.length() + 2 * arrayCount); + b.append(name); + for (int i = 0; i < arrayCount; ++i) + b.append("[]"); + return b.toString(); + } + /** Cache of methods found in getAllMethods(). */ private static Map allMethods = new HashMap(); diff --git a/libjava/classpath/gnu/java/lang/InstrumentationImpl.java b/libjava/classpath/gnu/java/lang/InstrumentationImpl.java new file mode 100644 index 0000000..7bfed45 --- /dev/null +++ b/libjava/classpath/gnu/java/lang/InstrumentationImpl.java @@ -0,0 +1,243 @@ +/* InstrumentationImpl.java -- GNU implementation of + java.lang.instrument.Instrumentation + 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., 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.java.lang; + +import java.lang.instrument.Instrumentation; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.UnmodifiableClassException; +import java.lang.instrument.IllegalClassFormatException; + +import java.security.ProtectionDomain; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * An Instrumentation object has transformers that will + * be called each time a class is defined or redefined. + * The object is given to a <code>premain</code> function + * that is called before the <code>main</code> function. + * + * @author Nicolas Geoffray (nicolas.geoffray@menlina.com) + * @since 1.5 + */ +public final class InstrumentationImpl implements Instrumentation +{ + + /* List of transformers */ + /* FIXME[GENERICS]: Should be ClassFileTransformer list */ + private ArrayList transformers = new ArrayList(); + + + private InstrumentationImpl() + { + } + + /** + * Adds a <code>ClassFileTransformer</class> object + * to the instrumentation. Each time a class is defined + * or redefined, the <code>transform</code> method of the + * <code>transformer</code> object is called. + * + * @param transformer the transformer to add + * @throws NullPointerException if transformer is null + */ + public void addTransformer(ClassFileTransformer transformer) + { + if (transformer == null) + throw new NullPointerException(); + synchronized(transformers) + { + transformers.add(transformer); + } + } + + /** + * Removes the given transformer from the set of transformers + * this Instrumentation object has. + * + * @param transformer the transformer to remove + * @return true if the transformer was found and removed, false if + * the transformer was not found + * @throws NullPointerException if transformer is null + */ + public boolean removeTransformer(ClassFileTransformer transformer) + { + if (transformer == null) + throw new NullPointerException(); + + boolean result; + synchronized (transformers) + { + result = transformers.remove(transformer); + } + return result; + } + + /** + * Returns if the current JVM supports class redefinition + * + * @return true if the current JVM supports class redefinition + */ + public boolean isRedefineClassesSupported() + { + return VMInstrumentationImpl.isRedefineClassesSupported(); + } + + /** + * Redefine classes present in the definitions array, with + * the corresponding class files. + * + * @param definitions an array of classes to redefine + * + * @throws ClassNotFoundException if a class cannot be found + * @throws UnmodifiableClassException if a class cannot be modified + * @throws UnsupportedOperationException if the JVM does not support + * redefinition or the redefinition made unsupported changes + * @throws ClassFormatError if a class file is not valid + * @throws NoClassDefFoundError if a class name is not equal to the name + * in the class file specified + * @throws UnsupportedClassVersionError if the class file version numbers + * are unsupported + * @throws ClassCircularityError if circularity occured with the new + * classes + * @throws LinkageError if a linkage error occurs + * @throws NullPointerException if the definitions array is null, or any + * of its element + * + * @see isRedefineClassesSupported() + * @see addTransformer(java.lang.instrument.ClassFileTransformer) + * @see ClassFileTransformer + */ + public void redefineClasses(ClassDefinition[] definitions) + throws ClassNotFoundException, + UnmodifiableClassException + { + if (!isRedefineClassesSupported()) + throw new UnsupportedOperationException(); + + VMInstrumentationImpl.redefineClasses(this, definitions); + } + + + /** + * Get all the classes loaded by the JVM. + * + * @return an array containing all the classes loaded by the JVM. The array + * is empty if no class is loaded. + */ + public Class[] getAllLoadedClasses() + { + return VMInstrumentationImpl.getAllLoadedClasses(); + } + + /** + * Get all the classes loaded by a given class loader + * + * @param loader the loader + * + * @return an array containing all the classes loaded by the given loader. + * The array is empty if no class was loaded by the loader. + */ + public Class[] getInitiatedClasses(ClassLoader loader) + { + return VMInstrumentationImpl.getInitiatedClasses(loader); + } + + /** + * Get the size of an object. + * + * @param objectToSize the object + * @return the size of the object + * @throws NullPointerException if objectToSize is null. + */ + public long getObjectSize(Object objectToSize) + { + // We alleviate the VM work + if (objectToSize == null) + throw new NullPointerException(); + return VMInstrumentationImpl.getObjectSize(objectToSize); + } + + /** + * Called by the VM or redefineClasses to call each transformer + * + * @param loader the loader of the class + * @param className the name of the class with packages separated with "/" + * @param classBeingRedefined the class being redefined if it's the case, + * null otherwise + * @param protectionDomain the protection domain of the class being defined + * or redefined + * @param classfileBuffer the input byte buffer in class file format + * + * @return the new class file + */ + /* FIXME[GENERICS]: Should be Class<?> */ + public byte[] callTransformers(ClassLoader loader, String className, + Class classBeingRedefined, ProtectionDomain protectionDomain, + byte[] classfileBuffer) + { + byte[] newBuffer = null; + byte[] oldBuffer = classfileBuffer; + ClassFileTransformer current; + synchronized (transformers) + { + Iterator i = transformers.iterator(); + while (i.hasNext()) + { + /* FIXME[GENERICS]: Remove cast */ + current = (ClassFileTransformer) i.next(); + try + { + newBuffer = current.transform(loader, className, + classBeingRedefined, protectionDomain, oldBuffer); + } + catch (IllegalClassFormatException ignored) + { + //IGNORED + } + if (newBuffer != null) + oldBuffer = newBuffer; + } + } + return oldBuffer; + } +} diff --git a/libjava/classpath/gnu/java/lang/reflect/ClassSignatureParser.java b/libjava/classpath/gnu/java/lang/reflect/ClassSignatureParser.java new file mode 100644 index 0000000..0c9b962 --- /dev/null +++ b/libjava/classpath/gnu/java/lang/reflect/ClassSignatureParser.java @@ -0,0 +1,92 @@ +/* ClassSignatureParser.java + Copyright (C) 2005 + 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.java.lang.reflect; + +import java.lang.reflect.*; +import java.util.ArrayList; + +public class ClassSignatureParser extends GenericSignatureParser +{ + private TypeVariable[] typeParameters; + private Type superclassType; + private Type[] interfaceTypes; + + public ClassSignatureParser(Class c, String signature) + { + super(c, c.getClassLoader(), signature); + + if (peekChar() == '<') + { + typeParameters = readFormalTypeParameters(); + } + else + { + typeParameters = new TypeVariable[0]; + } + // SuperclassSignature + superclassType = readClassTypeSignature(); + ArrayList interfaces = new ArrayList(); + while (peekChar() == 'L') + { + // SuperinterfaceSignature + interfaces.add(readClassTypeSignature()); + } + interfaceTypes = new Type[interfaces.size()]; + interfaces.toArray(interfaceTypes); + end(); + } + + public TypeVariable[] getTypeParameters() + { + TypeImpl.resolve(typeParameters); + return typeParameters; + } + + public Type getSuperclassType() + { + superclassType = TypeImpl.resolve(superclassType); + return superclassType; + } + + public Type[] getInterfaceTypes() + { + TypeImpl.resolve(interfaceTypes); + return interfaceTypes; + } +} diff --git a/libjava/classpath/gnu/java/lang/reflect/FieldSignatureParser.java b/libjava/classpath/gnu/java/lang/reflect/FieldSignatureParser.java new file mode 100644 index 0000000..16622d3 --- /dev/null +++ b/libjava/classpath/gnu/java/lang/reflect/FieldSignatureParser.java @@ -0,0 +1,103 @@ +/* FieldSignatureParser.java + Copyright (C) 2005 + 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.java.lang.reflect; + +import java.lang.reflect.GenericSignatureFormatError; +import java.lang.reflect.Type; + +public final class FieldSignatureParser extends GenericSignatureParser +{ + private Type type; + + public FieldSignatureParser(Class container, String signature) + { + super(container, container.getClassLoader(), signature); + + switch (peekChar()) + { + case 'L': + case '[': + case 'T': + type = readFieldTypeSignature(); + break; + case 'Z': + consume('Z'); + type = boolean.class; + break; + case 'B': + consume('B'); + type = byte.class; + break; + case 'S': + consume('S'); + type = short.class; + break; + case 'C': + consume('C'); + type = char.class; + break; + case 'I': + consume('I'); + type = int.class; + break; + case 'F': + consume('F'); + type = float.class; + break; + case 'J': + consume('J'); + type = long.class; + break; + case 'D': + consume('D'); + type = double.class; + break; + default: + throw new GenericSignatureFormatError(); + } + + end(); + } + + public Type getFieldType() + { + type = TypeImpl.resolve(type); + return type; + } +} diff --git a/libjava/classpath/gnu/java/lang/reflect/GenericSignatureParser.java b/libjava/classpath/gnu/java/lang/reflect/GenericSignatureParser.java new file mode 100644 index 0000000..399f1bd --- /dev/null +++ b/libjava/classpath/gnu/java/lang/reflect/GenericSignatureParser.java @@ -0,0 +1,622 @@ +/* GenericSignatureParser.java + Copyright (C) 2005 + 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.java.lang.reflect; + +import java.lang.reflect.*; +import java.util.ArrayList; +import java.util.Arrays; + +final class TypeVariableImpl extends TypeImpl implements TypeVariable +{ + private GenericDeclaration decl; + private Type[] bounds; + private String name; + + TypeVariableImpl(GenericDeclaration decl, Type[] bounds, String name) + { + this.decl = decl; + this.bounds = bounds; + this.name = name; + } + + Type resolve() + { + return this; + } + + /* FIXME[GENERICS]: Remove cast */ + public Type[] getBounds() + { + resolve(bounds); + return (Type[]) bounds.clone(); + } + + public GenericDeclaration getGenericDeclaration() + { + return decl; + } + + public String getName() + { + return name; + } + + public boolean equals(Object obj) + { + if (obj instanceof TypeVariableImpl) + { + TypeVariableImpl other = (TypeVariableImpl)obj; + return decl.equals(other.decl) && name.equals(other.name); + } + return false; + } + + public int hashCode() + { + return 0x5f4d5156 ^ decl.hashCode() ^ name.hashCode(); + } + + public String toString() + { + return name; + } +} + +final class ParameterizedTypeImpl extends TypeImpl implements ParameterizedType +{ + private String rawTypeName; + private ClassLoader loader; + private Class rawType; + private Type owner; + private Type[] typeArgs; + + ParameterizedTypeImpl(String rawTypeName, ClassLoader loader, Type owner, + Type[] typeArgs) + { + this.rawTypeName = rawTypeName; + this.loader = loader; + this.owner = owner; + this.typeArgs = typeArgs; + } + + Type resolve() + { + if (rawType == null) + { + try + { + rawType = Class.forName(rawTypeName, false, loader); + } + catch (ClassNotFoundException x) + { + throw new TypeNotPresentException(rawTypeName, x); + } + } + if (typeArgs == null) + { + if (owner == null) + { + return rawType; + } + typeArgs = new Type[0]; + } + resolve(typeArgs); + owner = resolve(owner); + return this; + } + + /* FIXME[GENERICS]: Remove cast */ + public Type[] getActualTypeArguments() + { + return (Type[]) typeArgs.clone(); + } + + public Type getRawType() + { + return rawType; + } + + public Type getOwnerType() + { + return owner; + } + + public boolean equals(Object obj) + { + if (obj instanceof ParameterizedTypeImpl) + { + ParameterizedTypeImpl other = (ParameterizedTypeImpl)obj; + return rawType.equals(other.rawType) + && ((owner == null && other.owner == null) + || owner.equals(other.owner)) + && Arrays.deepEquals(typeArgs, other.typeArgs); + } + return false; + } + + public int hashCode() + { + int h = 0x58158970 ^ rawType.hashCode(); + if (owner != null) + { + h ^= Integer.reverse(owner.hashCode()); + } + for (int i = 0; i < typeArgs.length; i++) + { + h ^= Integer.rotateLeft(typeArgs[i].hashCode(), i); + } + return h; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + if (owner != null) + { + sb.append(owner); + sb.append('.'); + sb.append(rawType.getSimpleName()); + } + else + { + sb.append(rawTypeName); + } + if (typeArgs.length > 0) + { + sb.append('<'); + for (int i = 0; i < typeArgs.length; i++) + { + if (i > 0) + sb.append(", "); + if (typeArgs[i] instanceof Class) + { + sb.append(((Class)typeArgs[i]).getName()); + } + else + { + sb.append(typeArgs[i]); + } + } + sb.append('>'); + } + return sb.toString(); + } +} + +final class GenericArrayTypeImpl extends TypeImpl implements GenericArrayType +{ + private Type componentType; + + GenericArrayTypeImpl(Type componentType) + { + this.componentType = componentType; + } + + Type resolve() + { + componentType = resolve(componentType); + return this; + } + + public Type getGenericComponentType() + { + return componentType; + } + + public boolean equals(Object obj) + { + if (obj instanceof GenericArrayTypeImpl) + { + GenericArrayTypeImpl other = (GenericArrayTypeImpl)obj; + return componentType.equals(other.componentType); + } + return false; + } + + public int hashCode() + { + return 0x4be37a7f ^ componentType.hashCode(); + } + + public String toString() + { + return componentType + "[]"; + } +} + +final class UnresolvedTypeVariable extends TypeImpl implements Type +{ + private GenericDeclaration decl; + private String name; + + UnresolvedTypeVariable(GenericDeclaration decl, String name) + { + this.decl = decl; + this.name = name; + } + + Type resolve() + { + GenericDeclaration d = decl; + while (d != null) + { + TypeVariable[] vars = d.getTypeParameters(); + for (int a = 0; a < vars.length ; ++a) + { + if (vars[a].getName().equals(name)) + { + return vars[a]; + } + } + d = getParent(d); + } + throw new MalformedParameterizedTypeException(); + } + + private static GenericDeclaration getParent(GenericDeclaration d) + { + if (d instanceof Class) + { + Method m = ((Class)d).getEnclosingMethod(); + if (m != null) + { + return m; + } + Constructor c = ((Class)d).getEnclosingConstructor(); + if (c != null) + { + return c; + } + return ((Class)d).getEnclosingClass(); + } + else if (d instanceof Method) + { + return ((Method)d).getDeclaringClass(); + } + else if (d instanceof Constructor) + { + return ((Constructor)d).getDeclaringClass(); + } + else + { + // TODO figure out what this represents + throw new Error(); + } + } +} + +final class WildcardTypeImpl extends TypeImpl implements WildcardType +{ + private Type lower; + private Type upper; + + WildcardTypeImpl(Type lower, Type upper) + { + this.lower = lower; + this.upper = upper; + } + + Type resolve() + { + upper = resolve(upper); + lower = resolve(lower); + return this; + } + + public Type[] getUpperBounds() + { + if (upper == null) + { + return new Type[0]; + } + return new Type[] { upper }; + } + + public Type[] getLowerBounds() + { + if (lower == null) + { + return new Type[0]; + } + return new Type[] { lower }; + } + + public boolean equals(Object obj) + { + if (obj instanceof WildcardTypeImpl) + { + WildcardTypeImpl other = (WildcardTypeImpl)obj; + return Arrays.deepEquals(getUpperBounds(), other.getUpperBounds()) + && Arrays.deepEquals(getLowerBounds(), other.getLowerBounds()); + } + return false; + } + + public int hashCode() + { + int h = 0x75d074fd; + if (upper != null) + { + h ^= upper.hashCode(); + } + if (lower != null) + { + h ^= lower.hashCode(); + } + return h; + } + + public String toString() + { + if (lower != null) + { + return "? super " + lower; + } + if (upper == java.lang.Object.class) + { + return "?"; + } + return "? extends " + upper; + } +} + +class GenericSignatureParser +{ + private ClassLoader loader; + private GenericDeclaration container; + private String signature; + private int pos; + + GenericSignatureParser(GenericDeclaration container, ClassLoader loader, + String signature) + { + this.container = container; + this.loader = loader; + this.signature = signature; + } + + TypeVariable[] readFormalTypeParameters() + { + consume('<'); + ArrayList params = new ArrayList(); + do + { + // TODO should we handle name clashes? + params.add(readFormalTypeParameter()); + } while (peekChar() != '>'); + consume('>'); + TypeVariable[] list = new TypeVariable[params.size()]; + params.toArray(list); + return list; + } + + private TypeVariable readFormalTypeParameter() + { + String identifier = readIdentifier(); + consume(':'); + ArrayList bounds = new ArrayList(); + if (peekChar() != ':') + { + bounds.add(readFieldTypeSignature()); + } + while (peekChar() == ':') + { + consume(':'); + bounds.add(readFieldTypeSignature()); + } + Type[] b = new Type[bounds.size()]; + bounds.toArray(b); + return new TypeVariableImpl(container, b, identifier); + } + + Type readFieldTypeSignature() + { + switch (peekChar()) + { + case 'L': + return readClassTypeSignature(); + case '[': + return readArrayTypeSignature(); + case 'T': + return readTypeVariableSignature(); + default: + throw new GenericSignatureFormatError(); + } + } + + Type readClassTypeSignature() + { + consume('L'); + String className = ""; + for (;;) + { + String part = readIdentifier(); + if (peekChar() != '/') + { + className += part; + break; + } + consume('/'); + className += part + "."; + } + Type[] typeArguments = null; + if (peekChar() == '<') + { + typeArguments = readTypeArguments(); + } + Type type = new ParameterizedTypeImpl(className, loader, null, + typeArguments); + while (peekChar() == '.') + { + consume('.'); + className += "$" + readIdentifier(); + typeArguments = null; + if (peekChar() == '<') + { + typeArguments = readTypeArguments(); + } + type = new ParameterizedTypeImpl(className, loader, type, + typeArguments); + } + consume(';'); + return type; + } + + private Type[] readTypeArguments() + { + consume('<'); + ArrayList list = new ArrayList(); + do + { + list.add(readTypeArgument()); + } while ((peekChar() != '>')); + consume('>'); + Type[] arr = new Type[list.size()]; + list.toArray(arr); + return arr; + } + + private Type readTypeArgument() + { + char c = peekChar(); + if (c == '+') + { + consume('+'); + return new WildcardTypeImpl(null, readFieldTypeSignature()); + } + else if (c == '-') + { + consume('-'); + return new WildcardTypeImpl(readFieldTypeSignature(), + java.lang.Object.class); + } + else if (c == '*') + { + consume('*'); + return new WildcardTypeImpl(null, java.lang.Object.class); + } + else + { + return readFieldTypeSignature(); + } + } + + Type readArrayTypeSignature() + { + consume('['); + switch (peekChar()) + { + case 'L': + case '[': + case 'T': + return new GenericArrayTypeImpl(readFieldTypeSignature()); + case 'Z': + consume('Z'); + return boolean[].class; + case 'B': + consume('B'); + return byte[].class; + case 'S': + consume('S'); + return short[].class; + case 'C': + consume('C'); + return char[].class; + case 'I': + consume('I'); + return int[].class; + case 'F': + consume('F'); + return float[].class; + case 'J': + consume('J'); + return long[].class; + case 'D': + consume('D'); + return double[].class; + default: + throw new GenericSignatureFormatError(); + } + } + + Type readTypeVariableSignature() + { + consume('T'); + String identifier = readIdentifier(); + consume(';'); + return new UnresolvedTypeVariable(container, identifier); + } + + private String readIdentifier() + { + int start = pos; + char c; + do + { + readChar(); + c = peekChar(); + } while (";:./<>-+*".indexOf(c) == -1); + return signature.substring(start, pos); + } + + final char peekChar() + { + if (pos == signature.length()) + return '\u0000'; + else + return signature.charAt(pos); + } + + final char readChar() + { + return signature.charAt(pos++); + } + + final void consume(char c) + { + if (readChar() != c) + throw new GenericSignatureFormatError(); + } + + final void end() + { + if (pos != signature.length()) + throw new GenericSignatureFormatError(); + } +} diff --git a/libjava/classpath/gnu/java/lang/reflect/MethodSignatureParser.java b/libjava/classpath/gnu/java/lang/reflect/MethodSignatureParser.java new file mode 100644 index 0000000..16b1af9 --- /dev/null +++ b/libjava/classpath/gnu/java/lang/reflect/MethodSignatureParser.java @@ -0,0 +1,167 @@ +/* MethodSignatureParser.java + Copyright (C) 2005 + 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.java.lang.reflect; + +import java.lang.reflect.*; +import java.util.ArrayList; + +public class MethodSignatureParser extends GenericSignatureParser +{ + private TypeVariable[] typeParameters; + private Type[] argTypes; + private Type retType; + private Type[] throwsSigs; + + public MethodSignatureParser(Method method, String signature) + { + this(method, method.getDeclaringClass().getClassLoader(), signature); + } + + public MethodSignatureParser(Constructor method, String signature) + { + this(method, method.getDeclaringClass().getClassLoader(), signature); + } + + private MethodSignatureParser(GenericDeclaration wrapper, + ClassLoader loader, String signature) + { + super(wrapper, loader, signature); + + if (peekChar() == '<') + { + typeParameters = readFormalTypeParameters(); + } + else + { + typeParameters = new TypeVariable[0]; + } + consume('('); + ArrayList args = new ArrayList(); + while (peekChar() != ')') + { + args.add(readTypeSignature()); + } + argTypes = new Type[args.size()]; + args.toArray(argTypes); + consume(')'); + retType = readTypeSignature(); + ArrayList throwsSigs = new ArrayList(); + while (peekChar() == '^') + { + consume('^'); + if(peekChar() == 'T') + { + throwsSigs.add(readTypeVariableSignature()); + } + else + { + throwsSigs.add(readClassTypeSignature()); + } + } + this.throwsSigs = new Type[throwsSigs.size()]; + throwsSigs.toArray(this.throwsSigs); + end(); + } + + public TypeVariable[] getTypeParameters() + { + TypeImpl.resolve(typeParameters); + return typeParameters; + } + + public Type[] getGenericParameterTypes() + { + TypeImpl.resolve(argTypes); + return argTypes; + } + + public Type getGenericReturnType() + { + retType = TypeImpl.resolve(retType); + return retType; + } + + public Type[] getGenericExceptionTypes() + { + TypeImpl.resolve(throwsSigs); + return throwsSigs; + } + + private Type readTypeSignature() + { + switch (peekChar()) + { + case 'T': + return readTypeVariableSignature(); + case 'L': + return readClassTypeSignature(); + case '[': + return readArrayTypeSignature(); + case 'Z': + consume('Z'); + return boolean.class; + case 'B': + consume('B'); + return byte.class; + case 'S': + consume('S'); + return short.class; + case 'C': + consume('C'); + return char.class; + case 'I': + consume('I'); + return int.class; + case 'F': + consume('F'); + return float.class; + case 'J': + consume('J'); + return long.class; + case 'D': + consume('D'); + return double.class; + case 'V': + consume('V'); + return void.class; + default: + throw new GenericSignatureFormatError(); + } + } +} diff --git a/libjava/classpath/gnu/java/lang/reflect/TypeImpl.java b/libjava/classpath/gnu/java/lang/reflect/TypeImpl.java new file mode 100644 index 0000000..30906f6 --- /dev/null +++ b/libjava/classpath/gnu/java/lang/reflect/TypeImpl.java @@ -0,0 +1,63 @@ +/* TypeImpl.java + Copyright (C) 2005 + 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.java.lang.reflect; + +import java.lang.reflect.Type; + +abstract class TypeImpl implements Type +{ + abstract Type resolve(); + + static void resolve(Type[] types) + { + for (int i = 0; i < types.length; i++) + { + types[i] = resolve(types[i]); + } + } + + static Type resolve(Type type) + { + if (type instanceof TypeImpl) + { + type = ((TypeImpl) type).resolve(); + } + return type; + } +} diff --git a/libjava/classpath/gnu/java/locale/LocaleHelper.java b/libjava/classpath/gnu/java/locale/LocaleHelper.java index ff00293..cec0147 100644 --- a/libjava/classpath/gnu/java/locale/LocaleHelper.java +++ b/libjava/classpath/gnu/java/locale/LocaleHelper.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.locale; +import java.text.Collator; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; @@ -154,5 +155,57 @@ public class LocaleHelper } return localizedString; } + + /** + * Return an array of all the locales for which there is a + * {@link Collator} instance. A new array is returned each time. + */ + public static Locale[] getCollatorLocales() + { + // For now we don't bother caching. This is probably + // not called very frequently. And, we would have to + // clone the array anyway. + if (LocaleData.collatorLocaleNames.length == 0) + return new Locale[] { Locale.US }; + Locale[] result = new Locale[LocaleData.collatorLocaleNames.length]; + for (int i = 0; i < result.length; ++i) + { + String language; + String region = ""; + String variant = ""; + String name = LocaleData.collatorLocaleNames[i]; + + language = name.substring(0, 2); + + if (name.length() > 2) + region = name.substring(3); + + int index = region.indexOf("_"); + if (index > 0) + { + variant = region.substring(index + 1); + region = region.substring(0, index - 1); + } + + result[i] = new Locale(language, region, variant); + } + return result; + } + + /** + * Return the number of locales we know of. + */ + public static int getLocaleCount() + { + return LocaleData.localeNames.length; + } + + /** + * Return the Nth locale name. + */ + public static String getLocaleName(int n) + { + return LocaleData.localeNames[n]; + } } diff --git a/libjava/classpath/gnu/java/net/DefaultContentHandlerFactory.java b/libjava/classpath/gnu/java/net/DefaultContentHandlerFactory.java new file mode 100644 index 0000000..bb0a7ce --- /dev/null +++ b/libjava/classpath/gnu/java/net/DefaultContentHandlerFactory.java @@ -0,0 +1,94 @@ +/* DefaultContentHandlerFactory.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., 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.java.net; + +import java.io.IOException; +import java.net.ContentHandler; +import java.net.ContentHandlerFactory; +import java.net.URLConnection; +import java.util.Arrays; +import java.util.HashSet; + +/** Content Handler for Image types, using the AWT Toolkit's image decoder. */ +class ImageHandler extends ContentHandler +{ + static ImageHandler instance = new ImageHandler(); + + public Object getContent(URLConnection urlc) throws IOException + { + // FIXME: implement using ImageIO + // ClasspathToolkit tk = (ClasspathToolkit) Toolkit.getDefaultToolkit(); + // java.awt.image.ImageProducer ip = tk.createImageProducer(urlc.getURL()); + // return ip; + return null; + } +} + +/** + */ +public class DefaultContentHandlerFactory implements ContentHandlerFactory +{ + /** For now, hard code the list of types that we assume should + * be supported by the Toolkit. ClasspathToolkit should perhaps provide + * an API to express what Image MIME types the Toolkit understands. + */ + private static String[] known_image_types = + { + "image/bmp", + "image/gif", + "image/jpeg", + "image/png", + "image/tiff", + "image/x-portable-anymap", + "image/x-cmu-raster", + "image/x-xbitmap", + "image/x-xpixmap" + }; + + private static HashSet imageTypes + = new HashSet(Arrays.asList(known_image_types)); + + public ContentHandler createContentHandler(String mimeType) + { + if (imageTypes.contains(mimeType)) + return ImageHandler.instance; + // Currently, only image types are handled. + return null; + } +} diff --git a/libjava/classpath/gnu/java/net/local/LocalServerSocket.java b/libjava/classpath/gnu/java/net/local/LocalServerSocket.java new file mode 100644 index 0000000..15163f2 --- /dev/null +++ b/libjava/classpath/gnu/java/net/local/LocalServerSocket.java @@ -0,0 +1,172 @@ +/* LocalServerSocket.java -- a unix domain server socket. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.java.net.local; + +import java.io.IOException; + +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; + +public final class LocalServerSocket extends ServerSocket +{ + + // Fields. + // ------------------------------------------------------------------------- + + private LocalSocketImpl myImpl; + private boolean closed; + + // Constructors. + // ------------------------------------------------------------------------- + + public LocalServerSocket () throws IOException + { + myImpl = new LocalSocketImpl (); + } + + public LocalServerSocket (SocketAddress bindPoint) throws IOException + { + this (); + bind (bindPoint); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public void bind (SocketAddress bindPoint) throws IOException + { + bind (bindPoint, 0); + } + + public void bind (SocketAddress bindPoint, int backlog) throws IOException + { + myImpl.doCreate (); + myImpl.bind (bindPoint); + myImpl.listen (backlog); + } + + public InetAddress getInetAddress () + { + return null; + } + + public int getLocalPort () + { + return -1; + } + + public SocketAddress getLocalSocketAddress () + { + return myImpl.getLocalAddress (); + } + + public Socket accept () throws IOException + { + LocalSocket s = new LocalSocket (true); + myImpl.accept (s.getLocalImpl()); + s.localConnected = true; + return s; + } + + public void close () throws IOException + { + myImpl.close (); + myImpl.unlink (); + closed = true; + } + + public boolean isBound () + { + return myImpl.getLocalAddress () != null; + } + + public boolean isClosed () + { + return closed; + } + + public void setSoTimeout (int timeout) + { + throw new UnsupportedOperationException ("local sockets do not support timeouts"); + } + + public int getSoTimeout () + { + throw new UnsupportedOperationException ("local sockets do not support timeouts"); + } + + public void setReuseAddress (boolean b) + { + throw new UnsupportedOperationException ("local sockets do not support reuse address"); + } + + public boolean getReuseAddress () + { + throw new UnsupportedOperationException ("local sockets do not support reuse address"); + } + + public String toString () + { + return LocalServerSocket.class.getName() + " [ address=" + + myImpl.getLocalAddress() + " ]"; + } + + public void setReceiveBufferSize (int size) + { + throw new UnsupportedOperationException ("local sockets do not support buffer size"); + } + + public int getReceiveBufferSize () + { + throw new UnsupportedOperationException ("local sockets do not support buffer size"); + } + + public void setSendBufferSize (int size) + { + throw new UnsupportedOperationException ("local sockets do not support buffer size"); + } + + public int getSendBufferSize () + { + throw new UnsupportedOperationException ("local sockets do not support buffer size"); + } +} diff --git a/libjava/classpath/gnu/java/net/local/LocalSocket.java b/libjava/classpath/gnu/java/net/local/LocalSocket.java new file mode 100644 index 0000000..b977d69 --- /dev/null +++ b/libjava/classpath/gnu/java/net/local/LocalSocket.java @@ -0,0 +1,312 @@ +/* LocalSocket.java -- a unix domain client socket. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.java.net.local; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketImpl; + +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; + +/** + * A local, or unix-domain socket. Unix domain sockets are connected on the + * local filesystem itself, rather than a remote address. + */ +public final class LocalSocket extends Socket +{ + + // Fields. + // ------------------------------------------------------------------------- + + private final LocalSocketImpl localimpl; + boolean localClosed; + boolean localConnected; + + // Constructors. + // ------------------------------------------------------------------------- + + public LocalSocket () throws SocketException + { + super (); + localimpl = new LocalSocketImpl (); + } + + public LocalSocket (LocalSocketAddress addr) throws SocketException + { + this (); + try + { + connect (addr); + } + catch (IOException ioe) + { + SocketException se = new SocketException (); + se.initCause (ioe); + throw se; + } + } + + LocalSocket (boolean nocreate) throws IOException + { + super (); + localimpl = new LocalSocketImpl (nocreate); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public void bind (SocketAddress bindpoint) throws IOException + { + throw new SocketException ("binding local client sockets is nonsensical"); + } + + public void connect (SocketAddress endpoint, int timeout) throws IOException + { + if (isClosed ()) + { + throw new SocketException ("socket is closed"); + } + if (! (endpoint instanceof LocalSocketAddress)) + { + throw new IllegalArgumentException ("socket address is not a local address"); + } + if (getChannel() != null && !getChannel().isBlocking()) + { + throw new IllegalBlockingModeException (); + } + + try + { + localimpl.doCreate (); + localimpl.localConnect ((LocalSocketAddress) endpoint); + } + catch (IOException ioe) + { + close (); + throw ioe; + } + localConnected = true; + } + + public InetAddress getInetAddress () + { + return null; + } + + public InetAddress getLocalAddress () + { + return null; + } + + public int getPort () + { + return -1; + } + + public int getLocalPort () + { + return -1; + } + + public SocketChannel getChannel () + { + return null; + } + + public SocketAddress getLocalSocketAddress () + { + return localimpl.getLocalAddress (); + } + + public SocketAddress getRemoteSocketAddress () + { + return localimpl.getRemoteAddress (); + } + + public InputStream getInputStream () throws IOException + { + return localimpl.getInputStream (); + } + + public OutputStream getOutputStream () throws IOException + { + return localimpl.getOutputStream (); + } + + public void sendUrgentData (int b) throws IOException + { + localimpl.sendUrgentData (b); + } + + public synchronized void close () throws IOException + { + localimpl.close (); + localClosed = true; + } + + public void shutdownInput () throws IOException + { + localimpl.shutdownInput (); + } + + public void shutdownOutput () throws IOException + { + localimpl.shutdownOutput (); + } + + public boolean isClosed () + { + return localClosed; + } + + public boolean isBound () + { + return false; + } + + public boolean isConnected () + { + return localConnected; + } + + // Unsupported methods. + // ------------------------------------------------------------------------- + + public void setTcpNoDelay (boolean b) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public boolean getTcpNoDelay() throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setSoLinger (boolean b, int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getSoLinger () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setOOBInline (boolean b) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public boolean getOOBInline () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setSoTimeout (int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getSoTimeout () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setSendBufferSize (int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getSendBufferSize() throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setReceiveBufferSize (int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getReceiveBufferSize () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setKeepAlive (boolean b) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public boolean getKeepAlive () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setTrafficClass (int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getTrafficClass () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setReuseAddress (boolean b) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public boolean getReuseAddress () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + LocalSocketImpl getLocalImpl () + { + return localimpl; + } +} diff --git a/libjava/classpath/gnu/java/net/local/LocalSocketAddress.java b/libjava/classpath/gnu/java/net/local/LocalSocketAddress.java new file mode 100644 index 0000000..ac5c53d --- /dev/null +++ b/libjava/classpath/gnu/java/net/local/LocalSocketAddress.java @@ -0,0 +1,100 @@ +/* LocalSocketAddress.java -- unix-domain socket address. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.java.net.local; + +import java.net.SocketAddress; + +public final class LocalSocketAddress extends SocketAddress +{ + + // Fields. + // ------------------------------------------------------------------------- + + private final String path; + + // Constructor. + // ------------------------------------------------------------------------- + + /** + * Creates a new unix domain socket address. + * + * @param path The path to the socket. + * @throws NullPointerException If <i>path</i> is <tt>null</tt>. + */ + public LocalSocketAddress (String path) + { + if (path == null) + { + throw new NullPointerException (); + } + this.path = path; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + /** + * Returns the path of the socket. + * + * @return The path. + */ + public String getPath () + { + return path; + } + + public boolean equals (Object o) + { + if (!(o instanceof LocalSocketAddress)) + { + return false; + } + return getPath ().equals (((LocalSocketAddress) o).getPath ()); + } + + public int hashCode () + { + return path.hashCode(); + } + + public String toString () + { + return super.toString() + " [ " + path + " ]"; + } +} diff --git a/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java b/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java new file mode 100644 index 0000000..f907e5f --- /dev/null +++ b/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java @@ -0,0 +1,322 @@ +/* LocalSocketImpl.java -- a unix domain client socket implementation. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.java.net.local; + +import java.io.FileDescriptor; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; + +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketImpl; + +final class LocalSocketImpl extends SocketImpl +{ + + // Fields. + // ------------------------------------------------------------------------- + + private boolean created; + private InputStream in; + private OutputStream out; + private int socket_fd; + private LocalSocketAddress local; + private LocalSocketAddress remote; + + static + { + try + { + System.loadLibrary ("javanet"); + } + catch (Exception x) + { + x.printStackTrace (); + } + } + + // Constructor. + // ------------------------------------------------------------------------- + + LocalSocketImpl () + { + this (false); + } + + LocalSocketImpl (boolean nocreate) + { + created = nocreate; + socket_fd = -1; + fd = new FileDescriptor (); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public void setOption (int opt, Object value) throws SocketException + { + throw new SocketException ("local sockets do not support options"); + } + + public Object getOption (int opt) throws SocketException + { + throw new SocketException ("local sockets do not support options"); + } + + protected native void create (boolean stream) throws IOException; + protected native void listen (int timeout) throws IOException; + protected native void accept (LocalSocketImpl socket) throws IOException; + protected native int available () throws IOException; + protected native void close () throws IOException; + protected native void sendUrgentData (int data) throws IOException; + protected native void shutdownInput () throws IOException; + protected native void shutdownOutput () throws IOException; + + native void unlink () throws IOException; + native void localBind (LocalSocketAddress addr) throws IOException; + native void localConnect (LocalSocketAddress addr) throws IOException; + native int read (byte[] buf, int off, int len) throws IOException; + native void write (byte[] buf, int off, int len) throws IOException; + + void doCreate () throws IOException + { + if (!created) + { + create (true); + } + } + + LocalSocketAddress getLocalAddress () + { + return local; + } + + LocalSocketAddress getRemoteAddress () + { + return remote; + } + + protected InputStream getInputStream() + { + if (in == null) + { + in = new LocalInputStream (this); + } + + return in; + } + + protected OutputStream getOutputStream() + { + if (out == null) + { + out = new LocalOutputStream (this); + } + + return out; + } + + protected void accept (SocketImpl impl) throws IOException + { + if (! (impl instanceof LocalSocketImpl)) + { + throw new IllegalArgumentException ("not a local socket"); + } + accept ((LocalSocketImpl) impl); + } + + protected void connect (String host, int port) throws IOException + { + throw new SocketException ("this is a local socket"); + } + + protected void connect (InetAddress addr, int port) throws IOException + { + throw new SocketException ("this is a local socket"); + } + + protected void connect(SocketAddress addr, int timeout) throws IOException + { + if (! (addr instanceof LocalSocketAddress)) + { + throw new SocketException ("address is not local"); + } + localConnect ((LocalSocketAddress) addr); + } + + protected void bind (InetAddress addr, int port) throws IOException + { + throw new SocketException ("this is a local socket"); + } + + protected void bind (SocketAddress addr) throws IOException + { + if (! (addr instanceof LocalSocketAddress)) + { + throw new SocketException ("address is not local"); + } + localBind ((LocalSocketAddress) addr); + } + +// Inner classes. + // ------------------------------------------------------------------------- + + class LocalInputStream extends InputStream + { + + // Field. + // ----------------------------------------------------------------------- + + private final LocalSocketImpl impl; + + // Constructor. + // ----------------------------------------------------------------------- + + LocalInputStream (LocalSocketImpl impl) + { + this.impl = impl; + } + + // Instance methods. + // ----------------------------------------------------------------------- + + public int available () throws IOException + { + return impl.available(); + } + + public boolean markSupported () + { + return false; + } + + public void mark (int readLimit) + { + } + + public void reset () throws IOException + { + throw new IOException ("mark/reset not supported"); + } + + public void close () throws IOException + { + impl.close(); + } + + public int read () throws IOException + { + byte[] buf = new byte[1]; + int ret = read (buf); + if (ret != -1) + { + return buf[0] & 0xFF; + } + else + { + return -1; + } + } + + public int read (byte[] buf) throws IOException + { + return read (buf, 0, buf.length); + } + + public int read (byte[] buf, int off, int len) throws IOException + { + int ret = impl.read (buf, off, len); + + if (ret == 0) + { + return -1; + } + + return ret; + } + } + + class LocalOutputStream extends OutputStream + { + + // Field. + // ----------------------------------------------------------------------- + + private final LocalSocketImpl impl; + + // Constructor. + // ----------------------------------------------------------------------- + + LocalOutputStream (LocalSocketImpl impl) + { + this.impl = impl; + } + + // Instance methods. + // ----------------------------------------------------------------------- + + public void close () throws IOException + { + impl.close (); + } + + public void flush () throws IOException + { + } + + public void write (int b) throws IOException + { + byte[] buf = new byte [1]; + buf[0] = (byte) b; + write (buf); + } + + public void write (byte[] buf) throws IOException + { + write (buf, 0, buf.length); + } + + public void write (byte[] buf, int off, int len) throws IOException + { + impl.write (buf, off, len); + } + } +} diff --git a/libjava/classpath/gnu/java/net/protocol/file/Connection.java b/libjava/classpath/gnu/java/net/protocol/file/Connection.java index f7253b0..04278d4 100644 --- a/libjava/classpath/gnu/java/net/protocol/file/Connection.java +++ b/libjava/classpath/gnu/java/net/protocol/file/Connection.java @@ -160,7 +160,9 @@ public class Connection extends URLConnection else if (c > 127) { try { byte [] c_as_bytes = Character.toString(c).getBytes("utf-8"); - System.arraycopy(c_as_bytes, 0, buf, pos, c_as_bytes.length); + final int c_length = c_as_bytes.length; + System.arraycopy(c_as_bytes, 0, buf, pos, c_length); + pos += c_length; } catch (java.io.UnsupportedEncodingException x2) { throw (Error) new InternalError().initCause(x2); diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/FTPConnection.java b/libjava/classpath/gnu/java/net/protocol/ftp/FTPConnection.java index d0f4872..f5317d4 100644 --- a/libjava/classpath/gnu/java/net/protocol/ftp/FTPConnection.java +++ b/libjava/classpath/gnu/java/net/protocol/ftp/FTPConnection.java @@ -429,6 +429,9 @@ public class FTPConnection public boolean changeWorkingDirectory(String path) throws IOException { + // Do nothing if the path is empty. + if (path.length() == 0) + return true; String cmd = CWD + ' ' + path; send(cmd); FTPResponse response = getResponse(); diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java index 5c2af9e..0dce7c7 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java +++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java @@ -48,12 +48,8 @@ import java.io.OutputStream; import java.net.ProtocolException; import java.net.URL; import java.security.cert.Certificate; -import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.Map; import javax.net.ssl.HandshakeCompletedEvent; @@ -271,6 +267,8 @@ public class HTTPURLConnection secure = false; start = 7; int end = location.indexOf('/', start); + if (end == -1) + end = location.length(); host = location.substring(start, end); int ci = host.lastIndexOf(':'); if (ci != -1) @@ -292,6 +290,8 @@ public class HTTPURLConnection secure = true; start = 8; int end = location.indexOf('/', start); + if (end == -1) + end = location.length(); host = location.substring(start, end); int ci = host.lastIndexOf(':'); if (ci != -1) @@ -410,10 +410,7 @@ public class HTTPURLConnection } public String getRequestProperty(String key) - { - if (key == null) - return null; - + { return requestHeaders.getValue(key); } diff --git a/libjava/classpath/gnu/java/net/protocol/http/Headers.java b/libjava/classpath/gnu/java/net/protocol/http/Headers.java index b42faaa..a793bbd 100644 --- a/libjava/classpath/gnu/java/net/protocol/http/Headers.java +++ b/libjava/classpath/gnu/java/net/protocol/http/Headers.java @@ -50,7 +50,6 @@ import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Set; /** * A collection of HTTP header names and associated values. The @@ -65,12 +64,18 @@ class Headers { /** * A list of HeaderElements - * */ private final ArrayList headers = new ArrayList(); - static final DateFormat dateFormat = new HTTPDateFormat(); + /** + * The HTTP dateformat used to parse date header fields. + */ + private static final DateFormat dateFormat = new HTTPDateFormat(); + /** + * Class for a Header element consisting of + * a name and value String. + */ static class HeaderElement { String name; @@ -83,8 +88,12 @@ class Headers } } + /** + * Default constructor. + */ public Headers() { + // nothing to do } /** @@ -99,8 +108,11 @@ class Headers } /** - * Returns the value of the specified header as a string. If + * Returns the value of the specified header as a string. If * multiple values are present, the last one is returned. + * + * @param header the header name (case insensitive search) + * @return The header value or <code>null</code> if not found. */ public String getValue(String header) { @@ -116,8 +128,12 @@ class Headers } /** - * Returns the value of the specified header as an integer, - * or -1 if the header is not present or not an integer. + * Returns the value of the specified header as an integer. If + * multiple values are present, the last one is returned. + * + * @param header the header name (case insensitive search) + * @return The header value or <code>-1</code> if not present or + * not an integer value. */ public int getIntValue(String header) { @@ -132,13 +148,18 @@ class Headers } catch (NumberFormatException e) { + // fall through } return -1; } /** - * Returns the value of the specified header as a long, or -1 if the - * header is not present or cannot be parsed as a long. + * Returns the value of the specified header as a long. If + * multiple values are present, the last one is returned. + * + * @param header the header name (case insensitive search) + * @return The header value or <code>-1</code> if not present or + * not a long value. */ public long getLongValue(String header) { @@ -153,13 +174,18 @@ class Headers } catch (NumberFormatException e) { + // fall through } return -1; } /** - * Returns the value of the specified header as a date, - * or <code>null</code> if the header is not present or not a date. + * Returns the value of the specified header as a date. If + * multiple values are present, the last one is returned. + * + * @param header the header name (case insensitive search) + * @return The header value or <code>null</code> if not present or + * not a date value. */ public Date getDateValue(String header) { @@ -180,23 +206,35 @@ class Headers /** * Add a header to this set of headers. If there is an existing - * header with the same name, it is discarded. + * header with the same name it's value is replaced with the new value. + * If multiple headers of the same name exist only the last one's value + * is replaced. * * @param name the header name * @param value the header value * - * @see #addValue + * @see #addValue(String, String) */ public void put(String name, String value) - { - remove(name); - headers.add(headers.size(), new HeaderElement(name, value)); + { + for (int i = headers.size() - 1; i >= 0; i--) + { + HeaderElement e = (HeaderElement)headers.get(i); + if (e.name.equalsIgnoreCase(name)) + { + e.value = value; + return; + } + } + + // nothing was replaced so add it as new HeaderElement + addValue(name, value); } - + /** - * Add all headers from a set of headers to this set. If any of the - * headers to be added have the same name as existing headers, the - * existing headers will be discarded. + * Add all headers from a set of headers to this set. Any existing header + * with the same (case insensitive) name as one of the new headers will + * be overridden. * * @param o the headers to be added */ @@ -206,10 +244,6 @@ class Headers { HeaderElement e = (HeaderElement)it.next(); remove(e.name); - } - for (Iterator it = o.iterator(); it.hasNext(); ) - { - HeaderElement e = (HeaderElement)it.next(); addValue(e.name, e.value); } } @@ -234,6 +268,7 @@ class Headers * Parse the specified InputStream, adding headers to this collection. * * @param in the InputStream. + * @throws IOException if I/O error occured. */ public void parse(InputStream in) throws IOException @@ -303,7 +338,7 @@ class Headers * @param name the header name * @param value the header value * - * @see #put + * @see #put(String, String) */ public void addValue(String name, String value) { @@ -312,13 +347,14 @@ class Headers /** * Get a new Map containing all the headers. The keys of the Map - * are Strings (the header names). The values of the Map are + * are Strings (the header names). The headers will be included + * case-sensitive in the map so that querying must be done with the + * correct case of the needed header name. The values of the Map are * unmodifiable Lists containing Strings (the header values). * - * <p> - * - * The returned map is modifiable. Changing it will not effect this - * collection of Headers in any way. + * <p> + * The returned map is modifiable. Changing it will not effect this + * collection of Headers in any way.</p> * * @return a Map containing all the headers. */ @@ -352,9 +388,9 @@ class Headers * * @param i the header index. * - * @return the header name. + * @return The header name, or <code>null</code> if index outside of range. * - * @see #getHeaderValue + * @see #getHeaderValue(int) */ public String getHeaderName(int i) { @@ -369,9 +405,9 @@ class Headers * * @param i the header index. * - * @return the header value. + * @return the header value, or <code>null</code> if index outside of range. * - * @see #getHeaderName + * @see #getHeaderName(int) */ public String getHeaderValue(int i) { diff --git a/libjava/classpath/gnu/java/nio/ChannelReader.java b/libjava/classpath/gnu/java/nio/ChannelReader.java index 44fe662..1e7372d 100644 --- a/libjava/classpath/gnu/java/nio/ChannelReader.java +++ b/libjava/classpath/gnu/java/nio/ChannelReader.java @@ -92,120 +92,126 @@ public class ChannelReader extends Reader public int read(char[] buf, int offset, int count) throws IOException { - // I declared channel being null meaning that the reader is closed. - if (!channel.isOpen()) - throw new IOException("Reader was already closed."); - - // I declared decoder being null meaning that there is no more data to read - // and convert. - if (decoder == null) - return -1; - - // Stores the amount of character being read. It -1 so that if no conversion - // occured the caller will see this as an 'end of file'. - int sum = -1; - - // Copies any characters which may be left from the last invocation into the - // destination array. - if (charBuffer.remaining() > 0) + synchronized (lock) { - sum = Math.min(count, charBuffer.remaining()); - charBuffer.get(buf, offset, sum); - - // Updates the control variables according to the latest copy operation. - offset += sum; - count -= sum; - } - - // Copies the character which have not been put in the destination array to - // the beginning. If data is actually copied count will be 0. If no data is - // copied count is >0 and we can now convert some more characters. - charBuffer.compact(); - - int converted = 0; - boolean last = false; - - while (count != 0) - { - // Tries to convert some bytes (Which will intentionally fail in the - // first place because we have not read any bytes yet.) - CoderResult result = decoder.decode(byteBuffer, charBuffer, last); - if (result.isMalformed() || result.isUnmappable()) - { - // JDK throws exception when bytes are malformed for sure. - // FIXME: Unsure what happens when a character is simply - // unmappable. - result.throwException(); - } - - // Marks that we should end this loop regardless whether the caller - // wants more chars or not, when this was the last conversion. - if (last) + // I declared channel being null meaning that the reader is closed. + if (!channel.isOpen()) + throw new IOException("Reader was already closed."); + + // I declared decoder being null meaning that there is no more data to read + // and convert. + if (decoder == null) + return -1; + + // Stores the amount of character being read. It -1 so that if no conversion + // occured the caller will see this as an 'end of file'. + int sum = -1; + + // Copies any characters which may be left from the last invocation into the + // destination array. + if (charBuffer.remaining() > 0) { - decoder = null; + sum = Math.min(count, charBuffer.remaining()); + charBuffer.get(buf, offset, sum); + + // Updates the control variables according to the latest copy operation. + offset += sum; + count -= sum; } - else if (result.isUnderflow()) + + // Copies the character which have not been put in the destination array to + // the beginning. If data is actually copied count will be 0. If no data is + // copied count is >0 and we can now convert some more characters. + charBuffer.compact(); + + int converted = 0; + boolean last = false; + + while (count != 0) { - // We need more bytes to do the conversion. - - // Copies the not yet converted bytes to the beginning making it - // being able to receive more bytes. - byteBuffer.compact(); - - // Reads in another bunch of bytes for being converted. - if (channel.read(byteBuffer) == -1) + // Tries to convert some bytes (Which will intentionally fail in the + // first place because we have not read any bytes yet.) + CoderResult result = decoder.decode(byteBuffer, charBuffer, last); + if (result.isMalformed() || result.isUnmappable()) { - // If there is no more data available in the channel we mark - // that state for the final character conversion run which is - // done in the next loop iteration. - last = true; + // JDK throws exception when bytes are malformed for sure. + // FIXME: Unsure what happens when a character is simply + // unmappable. + result.throwException(); } - - // Prepares the byteBuffer for the next character conversion run. - byteBuffer.flip(); + + // Marks that we should end this loop regardless whether the caller + // wants more chars or not, when this was the last conversion. + if (last) + { + decoder = null; + } + else if (result.isUnderflow()) + { + // We need more bytes to do the conversion. + + // Copies the not yet converted bytes to the beginning making it + // being able to receive more bytes. + byteBuffer.compact(); + + // Reads in another bunch of bytes for being converted. + if (channel.read(byteBuffer) == -1) + { + // If there is no more data available in the channel we mark + // that state for the final character conversion run which is + // done in the next loop iteration. + last = true; + } + + // Prepares the byteBuffer for the next character conversion run. + byteBuffer.flip(); + } + + // Prepares the charBuffer for being drained. + charBuffer.flip(); + + converted = Math.min(count, charBuffer.remaining()); + charBuffer.get(buf, offset, converted); + + // Copies characters which have not yet being copied into the char-Array + // to the beginning making it possible to read them later (If data is + // really copied here, then the caller has received enough characters so + // far.). + charBuffer.compact(); + + // Updates the control variables according to the latest copy operation. + offset += converted; + count -= converted; + + // Updates the amount of transferred characters. + sum += converted; + + if (decoder == null) + { + break; + } + + // Now that more characters have been transfered we let the loop decide + // what to do next. } - - // Prepares the charBuffer for being drained. + + // Makes the charBuffer ready for reading on the next invocation. charBuffer.flip(); - - converted = Math.min(count, charBuffer.remaining()); - charBuffer.get(buf, offset, converted); - - // Copies characters which have not yet being copied into the char-Array - // to the beginning making it possible to read them later (If data is - // really copied here, then the caller has received enough characters so - // far.). - charBuffer.compact(); - - // Updates the control variables according to the latest copy operation. - offset += converted; - count -= converted; - - // Updates the amount of transferred characters. - sum += converted; - - if (decoder == null) - { - break; - } - - // Now that more characters have been transfered we let the loop decide - // what to do next. + + return sum; } - - // Makes the charBuffer ready for reading on the next invocation. - charBuffer.flip(); - - return sum; } public void close() throws IOException { - channel.close(); + synchronized (lock) + { + channel.close(); - // Makes sure all intermediate data is released by the decoder. - if (decoder != null) - decoder.reset(); + // Makes sure all intermediate data is released by the decoder. + if (decoder != null) + decoder.reset(); + } } } diff --git a/libjava/classpath/gnu/java/nio/ChannelWriter.java b/libjava/classpath/gnu/java/nio/ChannelWriter.java new file mode 100644 index 0000000..8e533cc --- /dev/null +++ b/libjava/classpath/gnu/java/nio/ChannelWriter.java @@ -0,0 +1,190 @@ +/* ChannelWriter.java -- nio / writer bridge + 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.java.nio; + +import java.io.IOException; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +/** + * A Writer implementation that works by wrapping an NIO channel. + */ +public class ChannelWriter + extends Writer +{ + private static final int DEFAULT_BUFFER_CAP = 8192; + + /** + * The output channel. + */ + private WritableByteChannel byteChannel; + + /** + * The encoder to use. + */ + private CharsetEncoder enc; + + /** + * The byte buffer. Translated characters are stored here on their way out. + */ + private ByteBuffer byteBuffer; + + /** + * The character buffer. Characters are stored here on their way into + * the encoder. + */ + private CharBuffer charBuffer; + + private void writeBuffer() throws IOException + { + byteBuffer.flip(); + byteChannel.write(byteBuffer); + } + + /** + * Create a new instance, given the output byte channel, the encoder + * to use, and the minimum buffer capacity. + */ + public ChannelWriter(WritableByteChannel ch, CharsetEncoder enc, + int minBufferCap) + { + this.byteChannel = ch; + this.enc = enc; + if (minBufferCap == -1) + minBufferCap = DEFAULT_BUFFER_CAP; + this.byteBuffer + = ByteBuffer.allocate((int) (minBufferCap * enc.maxBytesPerChar())); + this.charBuffer = CharBuffer.allocate(minBufferCap); + this.charBuffer.clear(); + } + + /* (non-Javadoc) + * @see java.io.Writer#flush() + */ + public void flush() throws IOException + { + // Presumably if we have characters in our buffer, it is + // due to an underflow. So we don't bother trying to flush + // that here. + } + + /* (non-Javadoc) + * @see java.io.Writer#close() + */ + public void close() throws IOException + { + synchronized (lock) + { + if (enc == null) + throw new IOException("writer already closed"); + + byteBuffer.clear(); + charBuffer.flip(); + CoderResult res = enc.encode(charBuffer, byteBuffer, true); + if (res.isError() || res.isMalformed() || res.isUnmappable()) + res.throwException(); + writeBuffer(); + + byteBuffer.clear(); + res = enc.flush(byteBuffer); + if (res.isError() || res.isMalformed() || res.isUnmappable()) + res.throwException(); + writeBuffer(); + enc = null; + } + } + + /* (non-Javadoc) + * @see java.io.Writer#write(char[], int, int) + */ + public void write(char[] buf, int offset, int len) throws IOException + { + synchronized (lock) + { + if (enc == null) + throw new IOException("writer already closed"); + int lastLen = -1; + while (len > 0) + { + // Copy data into our character buffer. + int allowed = Math.min(charBuffer.remaining(), len); + charBuffer.put(buf, offset, allowed); + // Update for the next pass through the loop. + offset += allowed; + len -= allowed; + charBuffer.flip(); + // If we didn't make any progress, we want to clean up + // and save our state for the next write(). + if (len == lastLen) + { + if (len <= charBuffer.remaining()) + { + charBuffer.put(buf, offset, len); + charBuffer.flip(); + } + else + { + CharBuffer ncb = CharBuffer.allocate(charBuffer.length() + + len); + ncb.put(charBuffer); + ncb.put(buf, offset, len); + charBuffer = ncb; + } + break; + } + lastLen = len; + + // Convert. + byteBuffer.clear(); + CoderResult res = enc.encode(charBuffer, byteBuffer, false); + // Compact here, as we want to leave the buffer in the + // right state for any future put()s. + charBuffer.compact(); + if (res.isError() || res.isMalformed() || res.isUnmappable()) + res.throwException(); + // Write the byte buffer to the output channel. + writeBuffer(); + } + } + } +} diff --git a/libjava/classpath/gnu/java/rmi/activation/ActivationSystemTransient.java b/libjava/classpath/gnu/java/rmi/activation/ActivationSystemTransient.java new file mode 100644 index 0000000..1557220 --- /dev/null +++ b/libjava/classpath/gnu/java/rmi/activation/ActivationSystemTransient.java @@ -0,0 +1,406 @@ +/* ActivationSystemTransient.java -- The transient RMI object 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.java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.RemoteException; +import java.rmi.activation.ActivationDesc; +import java.rmi.activation.ActivationException; +import java.rmi.activation.ActivationGroup; +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.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; + +/** + * Provides the default transient activation system. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public class ActivationSystemTransient + extends DefaultActivationSystem + implements ActivationSystem, ActivationMonitor, Activator +{ + /** + * Maps group identifiers into group descriptions. + */ + protected final BidiTable groupDescs; + + /** + * Maps object identifiers into object activation descriptions + */ + protected final BidiTable descriptions; + + /** + * Maps group identifiers into already activated groups. + */ + protected transient final Map groupInstantiators = new Hashtable(); + + /** + * The cache of the activated objects, maps activation ids to remote + * object stubs. + */ + protected transient final Map activatedObjects = new HashMap(); + + /** + * The object incarnation counter. + */ + static long groupIncarnations = 0; + + /** + * The singleton of this activation system + */ + static ActivationSystem singleton; + + /** + * Set to true to print the event messages to console. + */ + public static boolean debug = false; + + + /** + * Creates the group which uses the given maps to store the data. + */ + protected ActivationSystemTransient(BidiTable objectDescriptions, + BidiTable groupDescriptiopns) + { + descriptions = objectDescriptions; + groupDescs = groupDescriptiopns; + } + + /** + * Creates the group with transient maps. + */ + protected ActivationSystemTransient() + { + this (new BidiTable(), new BidiTable()); + } + + public static ActivationSystem getInstance() + { + if (singleton == null) + singleton = new ActivationSystemTransient(); + return singleton; + } + + /** + * Activate the given object (try cache first if force = false) + */ + public MarshalledObject activate(ActivationID id, boolean force) + throws ActivationException, UnknownObjectException, RemoteException + { + if (! force) + { + synchronized (activatedObjects) + { + MarshalledObject object = (MarshalledObject) activatedObjects.get(id); + if (object != null) + return object; + } + } + + ActivationDesc desc = (ActivationDesc) descriptions.get(id); + if (desc == null) + throw new UnknownObjectException("Activating unknown object "+ + id == null ? "null" : id.toString()); + + ActivationInstantiator group = + (ActivationInstantiator) groupInstantiators.get(desc.getGroupID()); + + if (group == null) + { + // The group is not active - must be activated. + ActivationGroupID gid = desc.getGroupID(); + ActivationGroupDesc adesc = (ActivationGroupDesc) groupDescs.get(gid); + + if (adesc == null) + throw new UnknownGroupException("Activating unknown group " + + gid + " for "+ id+" this "+this); + + synchronized (ActivationSystemTransient.class) + { + groupIncarnations++; + } + + group = ActivationGroup.createGroup(gid, adesc, groupIncarnations); + activeGroup(gid, group, groupIncarnations); + } + + MarshalledObject object = group.newInstance(id, desc); + + synchronized (activatedObjects) + { + activatedObjects.put(id, object); + } + return object; + } + + /** + * Returns the activation monitor (THIS) and remebers the instantiator, used + * by that group. + */ + public ActivationMonitor activeGroup(ActivationGroupID id, + ActivationInstantiator group, + long incarnation) + throws UnknownGroupException, ActivationException, RemoteException + { + groupInstantiators.put(id, group); + return this; + } + + /** + * Get the activation descriptor for the given activation id. + * + * @return the activation descriptor, never null. + * @throws UnknownObjectException if such object is unknown. + */ + public ActivationDesc getActivationDesc(ActivationID id) + throws ActivationException, UnknownObjectException, RemoteException + { + ActivationDesc desc = (ActivationDesc) descriptions.get(id); + if (desc == null) + throw new UnknownObjectException("No desc for "+ + id == null ? "null" : id.toString()); + return desc; + } + + /** + * Get the descriptor of the given activation group. + * + * @return the activation group descriptor, never null. + * @throws UnknownGroupException if such group is unknown + */ + public ActivationGroupDesc getActivationGroupDesc(ActivationGroupID groupId) + throws ActivationException, UnknownGroupException, RemoteException + { + ActivationGroupDesc desc = (ActivationGroupDesc) groupDescs.get(groupId); + if (desc == null) + throw new UnknownGroupException(groupId == null ? "null" + : groupId.toString()); + return desc; + } + + /** + * Create the activation group id and put this id-descriptor combination into + * the group map. The new ID will only be created if this description has not + * already been registered, otherwise the id of the registered description + * will be returned. + */ + public ActivationGroupID registerGroup(ActivationGroupDesc groupDesc) + throws ActivationException, RemoteException + { + ActivationGroupID id = (ActivationGroupID) groupDescs.getKey(groupDesc); + if (id == null) + { + id = new ActivationGroupID(this); + groupDescs.put(id, groupDesc); + } + if (debug) + System.out.println("Register group " + id +":"+groupDesc+" this "+this); + + return id; + } + + /** + * Create the object activation id and put this id-descriptor combination into + * the group map. The new ID will only be created if this description has not + * already been registered, otherwise the id of the registered description + * will be returned. + */ + public ActivationID registerObject(ActivationDesc desc) + throws ActivationException, UnknownGroupException, RemoteException + { + ActivationID id = (ActivationID) descriptions.getKey(desc); + if (id == null) + { + id = new ActivationID(this); + descriptions.put(id, desc); + } + + if (debug) + System.out.println("Register object " + id +":"+desc+" this "+this); + + return id; + } + + /** + * Replace the activation descriptor, return the previous descriptor. + */ + public ActivationDesc setActivationDesc(ActivationID id, ActivationDesc desc) + throws ActivationException, UnknownObjectException, + UnknownGroupException, RemoteException + { + ActivationDesc prev = getActivationDesc(id); + descriptions.put(id, desc); + return prev; + } + + /** + * Replace the activation group descriptor, return the previous descriptor. + */ + public ActivationGroupDesc setActivationGroupDesc( + ActivationGroupID groupId, + ActivationGroupDesc groupDesc) + throws ActivationException, UnknownGroupException, RemoteException + { + ActivationGroupDesc prev = getActivationGroupDesc(groupId); + groupDescs.put(groupId, groupDesc); + return prev; + } + + /** + * Calls .shutdown on all bidirectional tables (has no effect if these + * table are not persistent). + */ + public void shutdown() throws RemoteException + { + descriptions.shutdown(); + groupDescs.shutdown(); + } + + /** + * Remove the group from the group map + */ + public void unregisterGroup(ActivationGroupID groupId) throws ActivationException, + UnknownGroupException, RemoteException + { + if (! groupDescs.containsKey(groupId)) + throw new UnknownGroupException("Unknown group "+groupId); + + groupDescs.removeKey(groupId); + groupInstantiators.remove(groupId); + } + + /** + * Remove the object id from the active object and description maps. + */ + public void unregisterObject(ActivationID id) throws ActivationException, + UnknownObjectException, RemoteException + { + if (! descriptions.containsKey(id)) + throw new UnknownObjectException("Unregistering unknown object"); + descriptions.removeKey(id); + + synchronized (activatedObjects) + { + activatedObjects.remove(id); + } + } + + /** + * Put the object into active object map. + */ + public void activeObject(ActivationID id, MarshalledObject obj) + throws UnknownObjectException, RemoteException + { + if (! descriptions.containsKey(id)) + throw new UnknownObjectException("Activating unknown object "+ + id+" this "+this); + try + { + synchronized (activatedObjects) + { + activatedObjects.put(id, obj.get()); + } + } + catch (RemoteException e) + { + throw e; + } + catch (Exception e) + { + UnknownObjectException un = new UnknownObjectException( + "Cannot get Remote for MarshalledObject of "+id); + un.detail = e; + throw un; + } + } + + /** + * Check if the group is known. Remove all active objects, belonging to + * that group, from the active object cache. + */ + public void inactiveGroup(ActivationGroupID groupId, long incarnation) + throws UnknownGroupException, RemoteException + { + if (! groupInstantiators.containsKey(groupId)) + throw new UnknownGroupException("Inactivating unkwnon group"); + + groupInstantiators.remove(groupId); + + // Remove all members of this group from the cache. + synchronized (activatedObjects) + { + Iterator iter = activatedObjects.keySet().iterator(); + ActivationID id; + ActivationDesc desc; + while (iter.hasNext()) + { + id = (ActivationID) iter.next(); + desc = (ActivationDesc) descriptions.get(id); + if (desc.getGroupID().equals(groupId)) + activatedObjects.remove(id); + } + } + } + + /** + * Removes this id from the active object cache. + */ + public void inactiveObject(ActivationID id) throws UnknownObjectException, + RemoteException + { + if (! descriptions.containsKey(id)) + throw new UnknownObjectException("Inactivating unknown object"); + + synchronized (activatedObjects) + { + activatedObjects.remove(id); + } + } +} diff --git a/libjava/classpath/gnu/java/rmi/activation/BidiTable.java b/libjava/classpath/gnu/java/rmi/activation/BidiTable.java new file mode 100644 index 0000000..5e421fe --- /dev/null +++ b/libjava/classpath/gnu/java/rmi/activation/BidiTable.java @@ -0,0 +1,163 @@ +/* BidiHasthable.java -- Bidirectional 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.java.rmi.activation; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * The bidirectional hash table, maps both a to b and b to a. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public class BidiTable +{ + /** + * Use serialVerionUID for interoperability. + */ + private static final long serialVersionUID = 1; + + /** + * Maps keys to values + */ + protected Map k2v; + + /** + * Maps values to keys (in reverse) + */ + protected Map v2k; + + /** + * Create a new table that is ready to use. + */ + public BidiTable() + { + k2v = new HashMap(); + v2k = new HashMap(); + } + + /** + * Create a new instance where the hashtable fields are not initialised + * (called from derivatives that intialise hashtables in they own way. + * + * @param flags currently used to mark the different constructor only. + */ + protected BidiTable(int flags) + { + } + + /** + * Get key by value + */ + public synchronized Object getKey(Object value) + { + return v2k.get(value); + } + + /** + * Put key-value pair. + */ + public synchronized void put(Object key, Object value) + { + k2v.put(key, value); + v2k.put(value, key); + } + + /** + * Get value from key + */ + public synchronized Object get(Object key) + { + return k2v.get(key); + } + + /** + * Remove the key-value pair by key + */ + public synchronized void removeKey(Object key) + { + Object value = k2v.get(key); + if (value!=null) + { + k2v.remove(key); + v2k.remove(value); + } + } + + /** + * Check if the table contains this key. + */ + public synchronized boolean containsKey(Object key) + { + return k2v.containsKey(key); + } + + /** + * This method is called before exit and may be used to write the database + * to the disk. The default method does nothing. + */ + public synchronized void shutdown() + { + } + + /** + * Get the size. + */ + public synchronized int size() + { + return k2v.size(); + } + + /** + * Get the key collection. + */ + public synchronized Object[] keys() + { + Collection keys = k2v.keySet(); + Object[] k = new Object[keys.size()]; + + Iterator iter = keys.iterator(); + for (int i = 0; i < k.length; i++) + k[i] = iter.next(); + + return k; + } +} diff --git a/libjava/classpath/gnu/java/rmi/activation/DefaultActivationGroup.java b/libjava/classpath/gnu/java/rmi/activation/DefaultActivationGroup.java new file mode 100644 index 0000000..3a654f2 --- /dev/null +++ b/libjava/classpath/gnu/java/rmi/activation/DefaultActivationGroup.java @@ -0,0 +1,159 @@ +/* DefaultActivationGroup.java -- Default activation group. + 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.java.rmi.activation; + +import gnu.java.rmi.server.ActivatableServerRef; +import gnu.java.rmi.server.UnicastServer; + +import java.lang.reflect.Constructor; +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.activation.ActivationDesc; +import java.rmi.activation.ActivationException; +import java.rmi.activation.ActivationGroup; +import java.rmi.activation.ActivationGroupID; +import java.rmi.activation.ActivationID; +import java.rmi.activation.UnknownObjectException; + +/** + * The default activation group class. This activation group assumes that + * all classes are accessible via current thread context class loader. + * The remote class loading is not supported for security reasons. The + * activation always occurs in the current jre. + * + * @author Audrius Meskauskas (audriusa@Bioinformatics.org) + */ +public class DefaultActivationGroup + extends ActivationGroup +{ + /** + * Use the serialVersionUID for interoperability. + */ + private static final long serialVersionUID = 1; + + /** + * Used during the group creation (required constructor). + */ + static final Class[] cConstructorTypes = new Class[] + { + ActivationID.class, + MarshalledObject.class + }; + + + /** + * Create the new default activation group. + * + * @param id the group activation id. + * @param data may contain the group initialization data (unused and can be + * null) + * @throws RemoteException if the super constructor does + */ + public DefaultActivationGroup(ActivationGroupID id, MarshalledObject data) + throws RemoteException + { + super(id); + } + + + /** + * May be overridden and used as a hook. This method is called each time + * the new object is instantiated. + */ + public void activeObject(ActivationID id, Remote obj) + throws ActivationException, UnknownObjectException, RemoteException + { + // Nothing to do (the monitor is already notified in newInstance) + } + + /** + * Create the new instance of the object, using the class name and location + * information, stored in the passed descriptor. The method expects the object + * class to have the two parameter constructor, the first parameter being the + * {@link ActivationID} and the second the {@link MarshalledObject}. + * + * @param id the object activation id + * @param desc the activation descriptor, providing the information, necessary + * to create and activate the object + * @return the marshalled object, containing the exported stub of the created + * object + * @throws ActivationException if the activation fails due any reason + */ + public MarshalledObject newInstance(ActivationID id, ActivationDesc desc) + throws ActivationException, RemoteException + { + try + { + if (ActivationSystemTransient.debug) + System.out.println("Instantiating "+desc.getClassName()); + + Remote object; + Class objectClass; + + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + objectClass = loader.loadClass(desc.getClassName()); + Constructor constructor = objectClass.getConstructor(cConstructorTypes); + object = (Remote) constructor.newInstance( + new Object[] { id, desc.getData() }); + + // Make the object accessible and create the stub. + ActivatableServerRef ref = UnicastServer.getActivatableRef(id); + Remote stub = ref.exportObject(object); + + MarshalledObject marsh = new MarshalledObject(stub); + + // Notify the activation monitor. + activeObject(id, marsh); + + // Make call to the hook that may be overridden. + activeObject(id, stub); + + return marsh; + } + catch (Exception e) + { + ActivationException acex = new ActivationException( + "Unable to activate "+ desc.getClassName() + + " from "+ desc.getLocation(), e); + throw acex; + } + } + +} diff --git a/libjava/classpath/gnu/java/rmi/activation/DefaultActivationSystem.java b/libjava/classpath/gnu/java/rmi/activation/DefaultActivationSystem.java new file mode 100644 index 0000000..754b5dc --- /dev/null +++ b/libjava/classpath/gnu/java/rmi/activation/DefaultActivationSystem.java @@ -0,0 +1,118 @@ +/* DefaultActivationSystem.java -- Default RMI 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.java.rmi.activation; + +import java.rmi.activation.ActivationSystem; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +/** + * Finds and returns the default activation system for this jre. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public abstract class DefaultActivationSystem +{ + /** + * The activation system (assigned if once found). + */ + static ActivationSystem system; + + /** + * The default activation registry port. + */ + static int ACTIVATION_REGISTRY_PORT; + + /** + * The name of the activation system registry port property. + */ + static String AS_PORT_PROPERTY = "java.rmi.activation.port"; + + /** + * The defalut name of the activation system in the activation registry. + */ + static String ACTIVATION_SYSTEM_NAME = "java.rmi.activation.ActivationSystem"; + + /** + * Get the activation system, default for this jre. If no external activation + * system exists, the internal activation system will be activated. This + * internal system is limited in capabilities and should be used exclusively + * for automated testing, to avoid necessity of starting rmi daemon during + * testing process. + */ + public static ActivationSystem get() + { + if (system == null) + try + { + // Obtain the port: + String asr = System.getProperty("java.rmi.activation.port"); + + if (asr != null) + { + try + { + ACTIVATION_REGISTRY_PORT = Integer.parseInt(asr); + if (ACTIVATION_REGISTRY_PORT <= 0) + throw new InternalError("Invalid " + asr + " value, " + + ACTIVATION_REGISTRY_PORT); + } + catch (NumberFormatException e) + { + throw new InternalError("Unable to parse " + asr + + " to integer"); + } + } + else + ACTIVATION_REGISTRY_PORT = ActivationSystem.SYSTEM_PORT; + + // Expect the naming service running first. + // The local host may want to use the shared registry + Registry r = LocateRegistry.getRegistry(ACTIVATION_REGISTRY_PORT); + ActivationSystem system = (ActivationSystem) r.lookup(ACTIVATION_SYSTEM_NAME); + return system; + } + catch (Exception ex) + { + system = ActivationSystemTransient.getInstance(); + } + + return system; + } +} diff --git a/libjava/classpath/gnu/java/rmi/dgc/DGCImpl.java b/libjava/classpath/gnu/java/rmi/dgc/DGCImpl.java index a7bc094..a5c036e 100644 --- a/libjava/classpath/gnu/java/rmi/dgc/DGCImpl.java +++ b/libjava/classpath/gnu/java/rmi/dgc/DGCImpl.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.rmi.dgc; +import gnu.java.rmi.server.UnicastServer; import gnu.java.rmi.server.UnicastServerRef; import java.rmi.RemoteException; @@ -46,7 +47,8 @@ import java.rmi.dgc.Lease; import java.rmi.dgc.VMID; import java.rmi.server.ObjID; import java.rmi.server.RMISocketFactory; -import java.util.Hashtable; +import java.util.Collection; +import java.util.TimerTask; /** * The DGC implementation is used for the server side during the distributed @@ -68,14 +70,67 @@ public class DGCImpl */ /** + * Use the serial version UID for interoperability. + */ + private static final long serialVersionUID = 1; + + /** + * Protects the array of object Id's for the scheduled period of time + * (lease). After the time expires, the protector is automatically discarded, + * making the references unprotected and hence applicable for the garbage + * collection. + */ + class RefProtector extends TimerTask + { + /** + * The corresponding server references to protect. Each Id may contain + * multiple references that are stored to collection. + */ + Collection[] references; + + /** + * Create the new instance of the reference protector that protects the + * given array of ids and exists for the given period of time. + * + * @param ids the ids to protect. + */ + RefProtector(ObjID[] ids, long timeToLive) + { + references = new Collection[ids.length]; + for (int i = 0; i < ids.length; i++) + { + references[i] = UnicastServer.getExported(ids[i]); + } + + // Schedule the existence. + LeaseRenewingTask.timer.schedule(this, timeToLive); + } + + /** + * Break all links, ensuring easy collection of the references by the gc. + */ + public void run() + { + for (int i = 0; i < references.length; i++) + { + references[i].clear(); + references[i] = null; + } + } + } + + /** * This defauld lease value is used if the lease value, passed to the * {@link #dirty} is equal to zero. */ static final long LEASE_VALUE = 600000L; - - // leaseCache caches a LeaseRecord associated with a vmid - Hashtable leaseCache = new Hashtable(); - + + /** + * Create the new DGC implementation. + * + * @throws RemoteException if the super constructor throws or the + * socket factory fails. + */ public DGCImpl() throws RemoteException { super(new ObjID(ObjID.DGC_ID), 0, RMISocketFactory.getSocketFactory()); @@ -92,26 +147,20 @@ public class DGCImpl */ public Lease dirty(ObjID[] ids, long sequenceNum, Lease lease) throws RemoteException - { - VMID vmid = lease.getVMID(); - if (vmid == null) - vmid = new VMID(); - + { + // We do not fill in VMID because in this implementation it is not used. long leaseValue = lease.getValue(); + + // Grant the maximal default lease time if the passed value is zero. if (leaseValue <= 0) leaseValue = LEASE_VALUE; - - lease = new Lease(vmid, leaseValue); - LeaseRecord lr = (LeaseRecord) leaseCache.get(vmid); - if (lr != null) - lr.reset(leaseValue); - else - { - lr = new LeaseRecord(vmid, leaseValue, ids); - leaseCache.put(vmid, lr); - } - return (lease); + // Create (and shedule of the given existence) the new reference + // protector. + new RefProtector(ids, leaseValue); + + lease = new Lease(lease.getVMID(), leaseValue); + return lease; } /** @@ -130,65 +179,4 @@ public class DGCImpl // TODO implement } - /** - * LeaseRecord associates a vmid to expireTime. - */ - static class LeaseRecord - { - /** - * The lease id. - */ - final VMID vmid; - - /** - * The lease expiration time. - */ - long expireTime; - - /** - * The array of ObjeID's that must be protected from being garbage - * collected. - */ - final ObjID [] objects; - - /** - * Create the new lease record. - * - * @param vmid lease id. - * @param leaseValue lease value - */ - LeaseRecord(VMID vmid, long leaseValue, ObjID [] an_objects) - { - this.vmid = vmid; - reset(leaseValue); - objects = an_objects; - } - - /** - * Prolong the expiration time till current time + passed value - * - * @param leaseValue the value after that (since the current moment) - * the lease should expire in the future. - */ - void reset(long leaseValue) - { - long l = System.currentTimeMillis(); - expireTime = l + leaseValue; - } - - /** - * Check if the lease has been expired. - * - * @return true if the lease has been expired, false if it is still valid. - */ - boolean isExpired() - { - long l = System.currentTimeMillis(); - if (l > expireTime) - return true; - return false; - } - - } // End of LeaseRecord - } // End of DGCImpl diff --git a/libjava/classpath/gnu/java/rmi/dgc/LeaseRenewingTask.java b/libjava/classpath/gnu/java/rmi/dgc/LeaseRenewingTask.java new file mode 100644 index 0000000..ffb5560 --- /dev/null +++ b/libjava/classpath/gnu/java/rmi/dgc/LeaseRenewingTask.java @@ -0,0 +1,234 @@ +/* LeaseRenewingTask.java -- The task to renew the lease. + 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.java.rmi.dgc; + +import gnu.java.rmi.server.UnicastRef; + +import java.lang.ref.WeakReference; +import java.rmi.dgc.Lease; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Timer; +import java.util.TimerTask; +import java.util.WeakHashMap; + +/** + * The task to renew the lease to some object reference. The UnicastRef + * being renewed is stored as a weak reference. So the presence of the + * sheduled task does not prevent it from being garbage collected. If the + * reference has not been garbage collected, the task is resheduled after + * the lease is renewed. + * + * @author Audrius Meskauskas (Audriusa@Bioinformatics.org) + */ +public class LeaseRenewingTask +{ + /** + * The sheduled timer task to call the renew() method. + */ + class LeaseTimerTask extends TimerTask + { + public void run() + { + renew(); + } + } + + /** + * The default requested lease duration time (one minute by default). + */ + public static long REQUEST_LEASE_DURATION = 60000; + + /** + * The reference to the UnicastRef that must renew its lease until not + * garbage collected. The different members of this list may point to the + * different instances of the UnicastRef's, but these references are + * pointing to the same remote object (they .equals() returns + * true when comparing with each other). Using LinkedList to make + * frequent deletions from the middle easy. + */ + LinkedList ref = new LinkedList(); + + /** + * The granted (or supposed) lease. + */ + Lease lease = new Lease(null, REQUEST_LEASE_DURATION); + + /** + * The timer, shared by all lease renewing tasks. The same instance is also + * used for the reference protector discarding in DGCImpl. + */ + static Timer timer = new Timer(true); + + /** + * Maps the UnicastRef to its renewing task. + */ + static WeakHashMap existingTasks = new WeakHashMap(); + + /** + * Creates the lease renewing task that renews the lease of the given + * UnicastRef until it is not collected. This constructor requests the lease + * value from the server and schedules the lease renewal action. + * + * @param renewIt the reference that must be renewed. + */ + public LeaseRenewingTask(UnicastRef renewIt) + { + lease = notifyDGC(renewIt); + if (lease != null) + { + schedule(lease); + ref.add(new WeakReference(renewIt)); + } + } + + /** + * Schedule periodic leases for the given UnicastRef reference. + * + * @param renewIt the reference, for that the leases must be scheduled. + */ + public static void scheduleLeases(UnicastRef renewIt) + { + // No need to schedule leases for null. + if (renewIt == null) + return; + try { + synchronized (existingTasks) + { + // Check maybe the task for refreshing this remote object already + // exists. + LeaseRenewingTask task = (LeaseRenewingTask) existingTasks.get(renewIt); + + if (task != null) + { + // Extend the reference list only. The scheduling must be + // alredy done with the previous lease. + synchronized (task.ref) + { + task.ref.add(new WeakReference(renewIt)); + } + } + else + existingTasks.put(renewIt, new LeaseRenewingTask(renewIt)); + } + } + catch (Exception ex) + { + InternalError ierr = new InternalError("Lease for "+renewIt); + ierr.initCause(ex); + throw ierr; + } + } + + /** + * Shedule the renewing call, taking into consideration that the following + * lease was granted. + * + * @param lease the lease that was granted. + */ + public void schedule(Lease lease) + { + long value = lease.getValue(); + + // Shedule a 10 % earlier because some time is needed for the message + // to reach the server. + long reduced = (value * 90)/100; + if (reduced == 0) + reduced = value; + + timer.schedule(new LeaseTimerTask(), reduced); + } + + /** + * Renew the lease. + */ + public void renew() + { + Object renewIt = null; + // Iterate throw the list of associated references. If all are + // discarded, there is no need to renew. + synchronized (ref) + { + Iterator iter = ref.iterator(); + WeakReference w; + while (iter.hasNext() && renewIt == null) + { + w = (WeakReference) iter.next(); + renewIt = w.get(); + if (renewIt == null) + // Discard the weak reference if its target has been garbage + // collected. + iter.remove(); + } + } + + if (renewIt!=null) + { + Lease lease = notifyDGC( (UnicastRef) renewIt); + + // Schedule the next renewing session. + if (lease!=null) + schedule(lease); + } + { + // All references collected - discard this entry. + } + } + + /** + * Notify DGC that we still hold this reference. + * + * @param renewIt the reference we still have (must not be null). + */ + public Lease notifyDGC(UnicastRef renewIt) + { + try + { + return renewIt.notifyDGC(lease); + } + catch (Exception e) + { + // Failed to notify. + // TODO Take some relevant action in the case if we failed + // to notify the remote object owner. + return null; + } + } + +} diff --git a/libjava/classpath/gnu/java/rmi/server/ActivatableRef.java b/libjava/classpath/gnu/java/rmi/server/ActivatableRef.java new file mode 100644 index 0000000..d191c0c --- /dev/null +++ b/libjava/classpath/gnu/java/rmi/server/ActivatableRef.java @@ -0,0 +1,175 @@ +/* ActivatableRef.java -- Activatable server reference + 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.java.rmi.server; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.activation.ActivationException; +import java.rmi.activation.ActivationID; +import java.rmi.server.ObjID; +import java.rmi.server.RMIClientSocketFactory; +import java.rmi.server.RemoteObject; +import java.rmi.server.RemoteObjectInvocationHandler; +import java.rmi.server.RemoteRef; + +/** + * The activatable reference works like UnicastRef, but if the remote object + * appears to be not accessible, it tries to reactivate it before reporting + * any errors. Apart the fields of the UnicastRef, the activatable reference + * contains the ActivationID that is used for this activation. + * + * @author Audrius Meskauskas (Audriusa@Bioinformatics.org) + */ +public class ActivatableRef extends UnicastRef +{ + /** + * Use serial version UID for iteroperability + */ + private static final long serialVersionUID = 1; + + /** + * The activation id. + */ + ActivationID actId; + + /** + * Delegate call to the superclass. + */ + public ActivatableRef() + { + super(); + } + + /** + * Delegate call to the superclass. + */ + public ActivatableRef(ObjID objid, String host, int port, + RMIClientSocketFactory csf) + { + super(objid, host, port, csf); + } + + /** + * Delegate call to the superclass. + */ + public ActivatableRef(ObjID objid) + { + super(objid); + } + + /** + * Get the referencing class. + */ + public String getRefClass(ObjectOutput out) + { + return "ActivatableRef"; + } + + /** + * Read the content from the input stream. + */ + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException + { + super.readExternal(in); + actId = (ActivationID) in.readObject(); + } + + /** + * Write the content to the output stream. + */ + public void writeExternal(ObjectOutput out) throws IOException + { + super.writeExternal(out); + out.writeObject(actId); + } + + /** + * Invoke the remote method on the given object and try to activate the object + * if it is not reacheable with the current manager. + */ + protected Object invokeCommon(Remote obj, Method method, Object[] params, + int opnum, long hash) throws Exception + { + UnicastConnection conn; + try + { + conn = manager.getConnection(); + } + catch (IOException e1) + { + // Connection failed: try to activate. + Remote reactivated = actId.activate(false); + + if (reactivated instanceof RemoteObject) + { + RemoteRef ref = ((RemoteObject) reactivated).getRef(); + manager = ((UnicastRef) ref).manager; + } + else if (Proxy.isProxyClass(reactivated.getClass())) + { + RemoteObjectInvocationHandler hander = + (RemoteObjectInvocationHandler) + Proxy.getInvocationHandler(reactivated); + + RemoteRef ref = hander.getRef(); + manager = ((UnicastRef) ref).manager; + } + else + throw new ActivationException("Activating into unsupported class " + + reactivated.getClass()); + + try + { + conn = manager.getConnection(); + } + catch (IOException e2) + { + throw new RemoteException("connection failed to host: " + + manager.serverName, e1); + } + } + return invokeCommon(conn, obj, method, params, opnum, hash); + } +} diff --git a/libjava/classpath/gnu/java/rmi/server/ActivatableServerRef.java b/libjava/classpath/gnu/java/rmi/server/ActivatableServerRef.java new file mode 100644 index 0000000..09595ec --- /dev/null +++ b/libjava/classpath/gnu/java/rmi/server/ActivatableServerRef.java @@ -0,0 +1,227 @@ +/* ActivatableServerRef.java -- The activatable server reference + 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.java.rmi.server; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.activation.ActivationID; +import java.rmi.server.ObjID; +import java.rmi.server.RMIServerSocketFactory; +import java.rmi.server.RemoteStub; +import java.rmi.server.Skeleton; + +/** + * The activatable server reference works like UnicastServerReference, but it + * additionally activates the associated object on demand, during the first + * incoming call. When UnicastServerReference takes the working reference, + * the ActivatableServerRef takes the activation id instead. + * + * @author Audrius Meskauskas (Audriusa@Bioinformatics.org) + */ +public class ActivatableServerRef extends UnicastServerRef +{ + /** + * Use SVUID for interoperability + */ + private static final long serialVersionUID = 1; + + /** + * The object activation id. + */ + public ActivationID actId; + + /** + * Used by serialization only + */ + public ActivatableServerRef() + { + super(); + } + + /** + * Create the new activatable server reference that will activate object on + * the first call using the given activation id. + */ + public ActivatableServerRef(ObjID id, ActivationID anId, int aPort, + RMIServerSocketFactory ssFactory) + throws RemoteException + { + super(id, aPort, ssFactory); + actId = anId; + + // The object ID will be placed in the object map and should deliver + // incoming call to {@link #incommingMessageCall}. The object itself + // is currently null. + UnicastServer.exportActivatableObject(this); + } + + /** + * Inactivate the object (stop the server). + */ + public void inactivate() + { + manager.stopServer(); + } + + /** + * Activate the object (normally during the first call). + */ + protected void activate() throws RemoteException + { + try + { + Remote self = actId.activate(false); + + // This will call UnicastServer.exportObject, replacing null by + // the activated object (self) in the object map. + exportObject(self); + } + catch (RemoteException rex) + { + throw rex; + } + catch (Exception exc) + { + RemoteException rx = new RemoteException("Activation failed."); + rx.detail = exc; + throw rx; + } + } + + /** + * If the object is not active, activate it first. + */ + public Object incomingMessageCall(UnicastConnection conn, int method, + long hash) throws Exception + { + if (myself == null) + activate(); + return super.incomingMessageCall(conn, method, hash); + } + + /** + * Export object and ensure it is present in the server activation table + * as well. + */ + public Remote exportObject(Remote obj) throws RemoteException + { + Remote r = super.exportObject(obj); + UnicastServer.registerActivatable(this); + return r; + } + + /** + * Export object and ensure it is present in the server activation table as + * well. + * + * @param aClass the class being exported, must implement Remote. + */ + public Remote exportClass(Class aClass) throws RemoteException + { + if (!Remote.class.isAssignableFrom(aClass)) + throw new InternalError(aClass.getName()+" must implement Remote"); + + String ignoreStubs; + + ClassLoader loader =aClass.getClassLoader(); + + // Stubs are always searched for the bootstrap classes that may have + // obsolete pattern and may still need also skeletons. + if (loader==null) + ignoreStubs = "false"; + else + ignoreStubs = System.getProperty("java.rmi.server.ignoreStubClasses", + "false"); + + if (! ignoreStubs.equals("true")) + { + // Find and install the stub + Class cls = aClass; + + // where ist the _Stub? (check superclasses also) + Class expCls = expCls = findStubSkelClass(cls); + + if (expCls != null) + { + stub = (RemoteStub) getHelperClass(expCls, "_Stub"); + // Find and install the skeleton (if there is one) + skel = (Skeleton) getHelperClass(expCls, "_Skel"); + } + } + + if (stub == null) + stub = createProxyStub(aClass, this); + + // Build hash of methods which may be called. + buildMethodHash(aClass, true); + + UnicastServer.registerActivatable(this); + return stub; + } + + /** + * Get the referencing class. + */ + public String getRefClass(ObjectOutput out) + { + return "ActivatableRef"; + } + + /** + * Read the content from the input stream. + */ + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException + { + super.readExternal(in); + actId = (ActivationID) in.readObject(); + } + + /** + * Write the content to the output stream. + */ + public void writeExternal(ObjectOutput out) throws IOException + { + super.writeExternal(out); + out.writeObject(actId); + } + +} diff --git a/libjava/classpath/gnu/java/rmi/server/CombinedClassLoader.java b/libjava/classpath/gnu/java/rmi/server/CombinedClassLoader.java index 3d2e37d..6225fb3 100644 --- a/libjava/classpath/gnu/java/rmi/server/CombinedClassLoader.java +++ b/libjava/classpath/gnu/java/rmi/server/CombinedClassLoader.java @@ -77,8 +77,8 @@ public class CombinedClassLoader extends ClassLoader while (iter.hasNext()) { cl = iter.next(); - if (!sLoaders.contains(cl)) - sLoaders.add(iter.next()); + if (cl!=null && !sLoaders.contains(cl)) + sLoaders.add(cl); } loaders = new ClassLoader[sLoaders.size()]; @@ -96,7 +96,7 @@ public class CombinedClassLoader extends ClassLoader { try { - return findClass(name); + return loaders[i].loadClass(name); } catch (ClassNotFoundException e) { @@ -107,27 +107,13 @@ public class CombinedClassLoader extends ClassLoader } /** - * Find the library with the given name - */ - protected String findLibrary(String name) - { - for (int i = 0; i < loaders.length; i++) - { - String lib = findLibrary(name); - if (lib != null) - return lib; - } - return super.findLibrary(name); - } - - /** * Find resource with the given name. */ protected URL findResource(String name) { for (int i = 0; i < loaders.length; i++) { - URL resource = findResource(name); + URL resource = loaders[i].getResource(name); if (resource != null) return resource; } @@ -141,7 +127,7 @@ public class CombinedClassLoader extends ClassLoader { for (int i = 0; i < loaders.length; i++) { - Enumeration resource = findResources(name); + Enumeration resource = loaders[i].getResources(name); if (resource != null) return resource; } diff --git a/libjava/classpath/gnu/java/rmi/server/UnicastConnectionManager.java b/libjava/classpath/gnu/java/rmi/server/UnicastConnectionManager.java index 08f6a9b..9715d4a 100644 --- a/libjava/classpath/gnu/java/rmi/server/UnicastConnectionManager.java +++ b/libjava/classpath/gnu/java/rmi/server/UnicastConnectionManager.java @@ -311,6 +311,14 @@ private UnicastConnection getClientConnection() throws IOException { } /** + * Get the string representation, describing the connection. + */ +public String toString() +{ + return serverName+":"+serverPort+" ("+serverobj+")"; +} + +/** * Discard a connection when we're done with it - maybe it can be * recycled. */ @@ -443,4 +451,12 @@ public boolean equals(Object obj) { return (false); } + /** + * Get the string representation, describing the connection. + */ + public String toString() + { + return host+":"+port+" ("+other+")"; + } + } diff --git a/libjava/classpath/gnu/java/rmi/server/UnicastRef.java b/libjava/classpath/gnu/java/rmi/server/UnicastRef.java index 8097a04..def1acd 100644 --- a/libjava/classpath/gnu/java/rmi/server/UnicastRef.java +++ b/libjava/classpath/gnu/java/rmi/server/UnicastRef.java @@ -1,5 +1,5 @@ /* UnicastRef.java -- - Copyright (c) 1996, 1997, 1998, 1999, 2002, 2005 + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -36,8 +36,11 @@ 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.java.rmi.server; +import gnu.java.rmi.dgc.LeaseRenewingTask; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -50,6 +53,7 @@ import java.lang.reflect.Method; import java.rmi.ConnectException; import java.rmi.Remote; import java.rmi.RemoteException; +import java.rmi.dgc.Lease; import java.rmi.server.ObjID; import java.rmi.server.Operation; import java.rmi.server.RMIClientSocketFactory; @@ -59,221 +63,462 @@ import java.rmi.server.RemoteRef; import java.rmi.server.UID; public class UnicastRef - implements RemoteRef, ProtocolConstants { - -public ObjID objid; -UnicastConnectionManager manager; - -/** - * Used by serialization, and let subclass capable of having default constructor - */ -// must be public otherwise java.rmi.RemoteObject cannot instantiate this class -// -- iP -public UnicastRef() { -} - -public UnicastRef(ObjID objid, String host, int port, RMIClientSocketFactory csf) { - this(objid); - manager = UnicastConnectionManager.getInstance(host, port, csf); -} - -public UnicastRef(ObjID objid) { - this.objid = objid; -} - -public Object invoke(Remote obj, Method method, Object[] params, long opnum) throws Exception { - // Check if client and server are in the same VM, then local call can be used to + implements RemoteRef, ProtocolConstants +{ + + /** + * Use serial version UID for iteroperability + */ + private static final long serialVersionUID = 1; + + public ObjID objid; + + UnicastConnectionManager manager; + + /** + * Used by serialization, and let subclass capable of having default + * constructor + */ + // must be public otherwise java.rmi.RemoteObject cannot instantiate this + // class + // -- iP + public UnicastRef() + { + } + + public UnicastRef(ObjID objid, String host, int port, + RMIClientSocketFactory csf) + { + this(objid); + manager = UnicastConnectionManager.getInstance(host, port, csf); + } + + public UnicastRef(ObjID objid) + { + this.objid = objid; + } + + public Object invoke(Remote obj, Method method, Object[] params, long opnum) + throws Exception + { + // Check if client and server are in the same VM, then local call can be + // used to // replace remote call, but it's somewhat violating remote semantic. Object svrobj = manager.serverobj; - - // Make sure that the server object is compatible. It could be loaded from a different + + // Make sure that the server object is compatible. It could be loaded from a + // different // classloader --iP - if(svrobj != null && method.getDeclaringClass().isInstance(svrobj)){ - //local call - Object ret = null; - try{ - ret = method.invoke(svrobj, params); - }catch(InvocationTargetException e){ - throw (Exception)e.getTargetException(); - } - //System.out.println("\n\n ***** local call: " + method + "\nreturn: " + ret + "\n\n"); - return ret; - } - //System.out.println("***************** remote call:" + manager.serverPort); - return (invokeCommon(obj, method, params, -1, opnum)); -} + if (svrobj != null && method.getDeclaringClass().isInstance(svrobj)) + { + // local call + Object ret = null; + try + { + ret = method.invoke(svrobj, params); + } + catch (InvocationTargetException e) + { + throw (Exception) e.getTargetException(); + } + // System.out.println("\n\n ***** local call: " + method + "\nreturn: " + // + ret + "\n\n"); + return ret; + } + // System.out.println("***************** remote call:" + + // manager.serverPort); + return (invokeCommon(obj, method, params, - 1, opnum)); + } + + /** + * The ordinary number of the DGC messages. + */ + static long dgcSequence; + + /** + * The DGC object id, also serves as a synchronization target to increment the + * dgcSequence safely. + */ + static final ObjID dgcId = new ObjID(ObjID.DGC_ID); + + ObjID[] this_id; + + /** + * The number of the method "dirty" in the DGC. + */ + static int DIRTY = 1; + + /** + * The DGC interface hash code. + */ + static final long dgcInterfaceHash = - 669196253586618813L; + + /** + * Notify the DGC of the remote side that we still hold this object. + */ + public Lease notifyDGC(Lease lease) throws Exception + { + long seq; + synchronized (dgcId) + { + seq = dgcSequence++; + } + + if (this_id == null) + this_id = new ObjID[] { objid }; -private Object invokeCommon(Remote obj, Method method, Object[] params, int opnum, long hash) throws Exception { - UnicastConnection conn; - try { - conn = manager.getConnection(); - } - catch (IOException e1) { - throw new RemoteException("connection failed to host: " + manager.serverName, e1); - } - - ObjectOutputStream out; - DataOutputStream dout; - try { - dout = conn.getDataOutputStream(); - dout.writeByte(MESSAGE_CALL); - - out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream - - objid.write(out); - out.writeInt(opnum); - out.writeLong(hash); - - // must handle primitive class and their wrapper classes - Class clss[] = method.getParameterTypes(); - for(int i = 0; i < clss.length; i++) - ((RMIObjectOutputStream)out).writeValue(params[i], clss[i]); - - out.flush(); - } - catch (IOException e2) { - throw new RemoteException("call failed: ", e2); - } - - int returncode; - Object returnval; - DataInputStream din; - ObjectInputStream in; - UID ack; - try { - din = conn.getDataInputStream(); - - if ((returncode = din.readUnsignedByte()) != MESSAGE_CALL_ACK) { - conn.disconnect(); - throw new RemoteException("Call not acked:" + returncode); - } - - in = conn.startObjectInputStream(); // (re)start ObjectInputStream - returncode = in.readUnsignedByte(); - ack = UID.read(in); - - Class cls = method.getReturnType(); - - if (returncode == RETURN_NACK) { - returnval = in.readObject(); // get Exception - - } else if(cls == Void.TYPE) { + UnicastConnection conn; + try + { + conn = manager.getConnection(); + } + catch (IOException e1) + { + throw new RemoteException("connection failed to host: " + + manager.serverName, e1); + } + + ObjectOutputStream out; + DataOutputStream dout; + try + { + dout = conn.getDataOutputStream(); + dout.writeByte(MESSAGE_CALL); + + out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream + + dgcId.write(out); + // The number of the operation is 1 ("dirty") + out.writeInt(DIRTY); + out.writeLong(dgcInterfaceHash); + + RMIObjectOutputStream rout = (RMIObjectOutputStream) out; + + rout.writeValue(this_id, this_id.getClass()); + rout.writeLong(seq); + rout.writeValue(lease, lease.getClass()); + + out.flush(); + } + catch (IOException e2) + { + throw new RemoteException("DGC call failed: ", e2); + } + + int returncode; + Object returnval; + DataInputStream din; + ObjectInputStream in; + UID ack; + try + { + din = conn.getDataInputStream(); + + if ((returncode = din.readUnsignedByte()) != MESSAGE_CALL_ACK) + { + conn.disconnect(); + throw new RemoteException("DGC Call not acked:" + returncode); + } + + in = conn.startObjectInputStream(); // (re)start ObjectInputStream + returncode = in.readUnsignedByte(); + ack = UID.read(in); + + if (returncode == RETURN_NACK) + { + returnval = in.readObject(); // get Exception + + } + else + { + returnval = ((RMIObjectInputStream) in).readValue(Lease.class); + } + } + catch (IOException e3) + { + throw new RemoteException("DGC call return failed: ", e3); + } + + manager.discardConnection(conn); + + if (returncode != RETURN_ACK && returnval != null) + { + if (returncode == RETURN_NACK) + throw (Exception) returnval; + else + throw new RemoteException("DGC unexpected returncode: " + returncode); + } + + return (Lease) returnval; + } + /** + * Invoke the remote method on the given object. This part is overridden by + * the activatable objects. + */ + protected Object invokeCommon(Remote obj, Method method, Object[] params, + int opnum, long hash) throws Exception + { + UnicastConnection conn; + try + { + conn = manager.getConnection(); + return invokeCommon(conn, obj, method, params, opnum, hash); + } + catch (IOException e1) + { + throw new RemoteException("connection failed to host: " + + manager.serverName, e1); + } + } + + /** + * Invoke the remote method on the given object when connection is already + * established. + */ + protected Object invokeCommon(UnicastConnection conn, Remote obj, + Method method, Object[] params, int opnum, + long hash) throws Exception + { + ObjectOutputStream out; + DataOutputStream dout; + try + { + dout = conn.getDataOutputStream(); + dout.writeByte(MESSAGE_CALL); + + out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream + + objid.write(out); + out.writeInt(opnum); + out.writeLong(hash); + + // must handle primitive class and their wrapper classes + Class clss[] = method.getParameterTypes(); + for (int i = 0; i < clss.length; i++) + ((RMIObjectOutputStream) out).writeValue(params[i], clss[i]); + + out.flush(); + } + catch (IOException e2) + { + throw new RemoteException("call failed: ", e2); + } + + int returncode; + Object returnval; + DataInputStream din; + ObjectInputStream in; + UID ack; + try + { + din = conn.getDataInputStream(); + + if ((returncode = din.readUnsignedByte()) != MESSAGE_CALL_ACK) + { + conn.disconnect(); + throw new RemoteException("Call not acked:" + returncode); + } + + in = conn.startObjectInputStream(); // (re)start ObjectInputStream + returncode = in.readUnsignedByte(); + ack = UID.read(in); + + Class cls = method.getReturnType(); + + if (returncode == RETURN_NACK) + { + returnval = in.readObject(); // get Exception + + } + else if (cls == Void.TYPE) + { returnval = null; - // in.readObject() // not required! returntype 'void' means no field is returned. - } else { - returnval = ((RMIObjectInputStream)in).readValue(cls); // get returnvalue - } - } catch (IOException e3) { - //for debug: e3.printStackTrace(); - throw new RemoteException("call return failed: ", e3); - } - - /* if DGCAck is necessary?? - //According to RMI wire protocol, send a DGCAck - // to indicate receiving return value - dout.writeByte(MESSAGE_DGCACK); - ack.write(dout); - out.flush(); - */ - - manager.discardConnection(conn); - - if (returncode != RETURN_ACK && returnval != null) { - if (returncode == RETURN_NACK) throw (Exception)returnval; - else throw new RemoteException("unexpected returncode: " + returncode); - } - - return (returnval); -} - -/** - * @deprecated - */ -public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum, long hash) throws RemoteException { + // in.readObject() // not required! returntype 'void' means no field + // is returned. + } + else + { + returnval = ((RMIObjectInputStream) in).readValue(cls); // get + // returnvalue + } + } + catch (IOException e3) + { + // for debug: e3.printStackTrace(); + throw new RemoteException("call return failed: ", e3); + } + + /* + * if DGCAck is necessary?? //According to RMI wire protocol, send a DGCAck // + * to indicate receiving return value dout.writeByte(MESSAGE_DGCACK); + * ack.write(dout); out.flush(); + */ + + manager.discardConnection(conn); + + if (returncode != RETURN_ACK && returnval != null) + { + if (returncode == RETURN_NACK) + throw (Exception) returnval; + else + throw new RemoteException("unexpected returncode: " + returncode); + } + + return (returnval); + } + + /** + * @deprecated + */ + public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum, + long hash) throws RemoteException + { UnicastConnection conn; - - try { - conn = manager.getConnection(); - } - catch (IOException e1) { - throw new ConnectException("connection failed to host: " + manager.serverName, e1); - } - - //obj: useless? - - return (new UnicastRemoteCall(conn, objid, opnum, hash)); -} - -/** - * @deprecated - */ -public void invoke(RemoteCall call) throws Exception { - UnicastRemoteCall c = (UnicastRemoteCall)call; - call.executeCall(); -} -/** - * @deprecated - */ -public void done(RemoteCall call) throws RemoteException { - UnicastRemoteCall c = (UnicastRemoteCall)call; - try{ - c.done(); - } catch(IOException e){} + try + { + conn = manager.getConnection(); + } + catch (IOException e1) + { + throw new ConnectException("connection failed to host: " + + manager.serverName, e1); + } + + // obj: useless? + + return (new UnicastRemoteCall(conn, objid, opnum, hash)); + } + + /** + * @deprecated + */ + public void invoke(RemoteCall call) throws Exception + { + UnicastRemoteCall c = (UnicastRemoteCall) call; + call.executeCall(); + } + + /** + * @deprecated + */ + public void done(RemoteCall call) throws RemoteException + { + UnicastRemoteCall c = (UnicastRemoteCall) call; + try + { + c.done(); + } + catch (IOException e) + { + } UnicastConnection conn = c.getConnection(); - manager.discardConnection(conn); -} - -public void writeExternal(ObjectOutput out) throws IOException { - if (manager == null) { - throw new IOException("no connection"); - } - manager.write(out); - objid.write(out); - // This byte is somewhat confusing when interoperating with JDK - out.writeByte(0); //RETURN_ACK); -} - -public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - manager = UnicastConnectionManager.read(in); - objid = ObjID.read(in); - byte ack = in.readByte(); - // This byte is somewhat confusing when interoperating with JDK - if (ack != RETURN_ACK && ack != 0/*jdk ack value*/) { - throw new IOException("no ack found"); - } -} - -public boolean remoteEquals(RemoteRef ref) { - throw new Error("Not implemented"); -} - -public int remoteHashCode() { - throw new Error("Not implemented"); -} - -public String getRefClass(ObjectOutput out) { - return ("UnicastRef"); -} - -public String remoteToString() { - throw new Error("Not implemented"); -} - -public void dump(UnicastConnection conn) { - try { - DataInputStream din = conn.getDataInputStream(); - for (;;) { - int b = din.readUnsignedByte(); - System.out.print(Integer.toHexString(b)); - if (b >= 32 && b < 128) { - System.out.print(": " + (char)b); - } - System.out.println(); - } - } - catch (IOException _) { - } -} + manager.discardConnection(conn); + } + + public void writeExternal(ObjectOutput out) throws IOException + { + if (manager == null) + { + throw new IOException("no connection"); + } + manager.write(out); + objid.write(out); + // This byte is somewhat confusing when interoperating with JDK + out.writeByte(0); // RETURN_ACK); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException + { + manager = UnicastConnectionManager.read(in); + objid = ObjID.read(in); + byte ack = in.readByte(); + // This byte is somewhat confusing when interoperating with JDK + if (ack != RETURN_ACK && ack != 0/* jdk ack value */) + { + throw new IOException("no ack found"); + } + + // Notify the DGC of the remote side that we hold the reference to the + // received object. Do not notify if the client and server are on the + // same virtual machine. + if (manager.serverobj == null) + LeaseRenewingTask.scheduleLeases(this); + } + + public boolean remoteEquals(RemoteRef ref) + { + throw new Error("Not implemented"); + } + + public int remoteHashCode() + { + throw new Error("Not implemented"); + } + + public String getRefClass(ObjectOutput out) + { + return ("UnicastRef"); + } + + /** + * Return the string representing the remote reference information. + */ + public String remoteToString() + { + if (manager!=null) + return manager.toString(); + else + return "null manager"; + } + + public void dump(UnicastConnection conn) + { + try + { + DataInputStream din = conn.getDataInputStream(); + for (;;) + { + int b = din.readUnsignedByte(); + System.out.print(Integer.toHexString(b)); + if (b >= 32 && b < 128) + { + System.out.print(": " + (char) b); + } + System.out.println(); + } + } + catch (IOException _) + { + } + } + + /** + * Check if this UnicastRef points to the object as the passed UnicastRef. + * Both the object Id and manager must be the same. + * + * @return true if the passed reference points to the same remote object as + * this reference, false otherwise. + */ + public boolean equals(Object other) + { + if (other instanceof UnicastRef) + { + UnicastRef r = (UnicastRef) other; + return r.manager.equals(manager) && r.objid.equals(objid); + } + else + return false; + } + + /** + * Get the hash code of this UnicastRef, combining hash code of the manager + * with hash code of the object id. + */ + public int hashCode() + { + return manager.hashCode() ^ objid.hashCode(); + } } diff --git a/libjava/classpath/gnu/java/rmi/server/UnicastServer.java b/libjava/classpath/gnu/java/rmi/server/UnicastServer.java index a8da725..7fe5395 100644 --- a/libjava/classpath/gnu/java/rmi/server/UnicastServer.java +++ b/libjava/classpath/gnu/java/rmi/server/UnicastServer.java @@ -40,6 +40,7 @@ exception statement from your version. */ package gnu.java.rmi.server; import gnu.java.rmi.dgc.DGCImpl; +import gnu.java.util.WeakIdentityHashMap; import java.io.DataOutputStream; import java.io.IOException; @@ -49,122 +50,272 @@ import java.rmi.NoSuchObjectException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.ServerError; +import java.rmi.activation.ActivationException; +import java.rmi.activation.ActivationID; import java.rmi.server.ObjID; import java.rmi.server.UID; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; -import java.util.Map; import java.util.Hashtable; -import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.WeakHashMap; public class UnicastServer - implements ProtocolConstants { + implements ProtocolConstants +{ -static private Hashtable objects = new Hashtable(); //mapping OBJID to server ref -static private Map refcache = Collections.synchronizedMap(new IdentityHashMap()); //mapping obj itself to server ref -static private DGCImpl dgc; + /** + * Mapping OBJID to server ref by .equals(). + */ + static private Map objects = Collections.synchronizedMap(new WeakHashMap()); -public static void exportObject(UnicastServerRef obj) { - startDGC(); - objects.put(obj.objid, obj); - refcache.put(obj.myself, obj); - obj.manager.startServer(); -} + /** + * Mapping obj itself to server ref by identity. + */ + static private Map refcache = Collections.synchronizedMap(new WeakIdentityHashMap()); + + /** + * Mapping the registered activatable objects into they server references. + */ + public static Map actIds = new Hashtable(); + + /** + * The reference to the local distributed garbage collector. + */ + static private DGCImpl dgc; + + /** + * Connect this server reference to the server, allowing the local + * implementation, associated with this object, to receive remote calls. + * + * @param obj the server reference, encloses the (usually local) remote + * object. + */ + public static void exportObject(UnicastServerRef obj) + { + startDGC(); + objects.put(obj.objid, obj); + refcache.put(obj.myself, obj); + obj.manager.startServer(); + } + + /** + * Register the activatable object into the table of the activatable + * objects. + */ + public static void registerActivatable(ActivatableServerRef ref) + { + actIds.put(ref.actId, ref); + } + + /** + * Export tha activatable object. The object id is placed into the map, + * but the object itself not. This is enough to deliver call to + * the ref.incomingMessageCall where the object will be instantiated, + * if not present. + */ + public static void exportActivatableObject(ActivatableServerRef ref) + { + startDGC(); + objects.put(ref.objid, ref); + ref.manager.startServer(); + actIds.put(ref.actId, ref); + } + + + /** + * Get the activatable server reference that is handling activation of the + * given activation id. + */ + public static ActivatableServerRef getActivatableRef(ActivationID id) + throws ActivationException + { + ActivatableServerRef ref = (ActivatableServerRef) actIds.get(id); + if (ref == null) + throw new ActivationException(id + " was not registered with this server"); + return ref; + } + + /** + * Unregister the previously registered activatable server reference. + */ + public static void unregisterActivatable(ActivationID id) + { + actIds.remove(id); + } + + // FIX ME: I haven't handle force parameter + /** + * Remove the given server reference. The remote object, associated with + * this reference, will no longer receive remote calls via this server. + */ + public static boolean unexportObject(UnicastServerRef obj, boolean force) + { + objects.remove(obj.objid); + refcache.remove(obj.myself); + obj.manager.stopServer(); + + if (obj instanceof ActivatableServerRef) + { + ActivationID id = ((ActivatableServerRef) obj).actId; + unregisterActivatable(id); + } + return true; + } + + /** + * Get the exported reference of the given Remote. The identity map is used, + * the non-null value will only be returned if exactly the passed remote + * is part of the registered UnicastServerRef. + * + * @param remote the Remote that is connected to this server via + * {@link UnicastServerRef}. + * + * @return the UnicastServerRef that is used to connect the passed + * remote with this server or null, if this Remote is not connected + * to this server. + */ + public static UnicastServerRef getExportedRef(Remote remote) + { + return (UnicastServerRef) refcache.get(remote); + } -// FIX ME: I haven't handle force parameter -public static boolean unexportObject(UnicastServerRef obj, boolean force) { - objects.remove(obj.objid); - refcache.remove(obj.myself); - obj.manager.stopServer(); - return true; -} + /** + * Get the server references to the object, previously exported via this + * server. As the identity map is scanned, more than one reference may match + * this Id. + * + * @param id the id of the exported object + * @return the server reference to this object, null if none. + */ + public static Collection getExported(Object id) + { + synchronized (objects) + { + ArrayList list = new ArrayList(); + Iterator iter = objects.entrySet().iterator(); + Map.Entry e; + Object key; + while (iter.hasNext()) + { + e = (Map.Entry) iter.next(); + key = e.getKey(); + if (key != null && key.equals(id)) + list.add(e.getValue()); + } + return list; + } + } -public static UnicastServerRef getExportedRef(Remote remote){ - return (UnicastServerRef)refcache.get(remote); -} + private static synchronized void startDGC() + { + if (dgc == null) + { + try + { + dgc = new DGCImpl(); + // Changed DGCImpl to inherit UnicastServerRef directly + // ((UnicastServerRef)dgc.getRef()).exportObject(dgc); + dgc.exportObject(dgc); + } + catch (RemoteException e) + { + e.printStackTrace(); + } + } + } -private static synchronized void startDGC() { - if (dgc == null) { - try { - dgc = new DGCImpl(); - // Changed DGCImpl to inherit UnicastServerRef directly - //((UnicastServerRef)dgc.getRef()).exportObject(dgc); - dgc.exportObject(dgc); - } - catch (RemoteException e) { - e.printStackTrace(); - } - } -} + public static void dispatch(UnicastConnection conn) throws Exception + { + switch (conn.getDataInputStream().readUnsignedByte()) + { + case MESSAGE_CALL: + incomingMessageCall(conn); + break; + case MESSAGE_PING: + // jdk sends a ping before each method call -> answer it! + DataOutputStream out = conn.getDataOutputStream(); + out.writeByte(MESSAGE_PING_ACK); + out.flush(); + break; + default: + throw new Exception("bad method type"); + } + } + + /** + * This method is invoked when the remote call is received. The method + * dispatches the call to the responsible object, connected to this + * server via UnicastServerReference. + */ + private static void incomingMessageCall(UnicastConnection conn) + throws IOException + { + ObjectInputStream in = conn.startObjectInputStream(); // (re)start + // ObjectInputStream -public static void dispatch(UnicastConnection conn) throws Exception { - switch (conn.getDataInputStream().readUnsignedByte()) { - case MESSAGE_CALL: - incomingMessageCall(conn); - break; - case MESSAGE_PING: - // jdk sends a ping before each method call -> answer it! - DataOutputStream out = conn.getDataOutputStream(); - out.writeByte(MESSAGE_PING_ACK); - out.flush(); - break; - default: - throw new Exception("bad method type"); - } -} + ObjID objid = ObjID.read(in); + int method = in.readInt(); + long hash = in.readLong(); -private static void incomingMessageCall(UnicastConnection conn) throws IOException { - ObjectInputStream in = conn.startObjectInputStream(); // (re)start ObjectInputStream - - ObjID objid = ObjID.read(in); - int method = in.readInt(); - long hash = in.readLong(); - -//System.out.println("ObjID: " + objid + ", method: " + method + ", hash: " + hash); - - // Use the objid to locate the relevant UnicastServerRef - UnicastServerRef uref = (UnicastServerRef)objects.get(objid); - Object returnval; - int returncode = RETURN_ACK; - // returnval is from Method.invoke(), so we must check the return class to see - // if it's primitive type - Class returncls = null; - if (uref != null) { - try { - // Dispatch the call to it. - returnval = uref.incomingMessageCall(conn, method, hash); - returncls = uref.getMethodReturnType(method, hash); - } - catch (Exception e) { - returnval = e; - returncode = RETURN_NACK; - } - catch (Error e) { - returnval = new ServerError ("An Error is thrown while processing the invocation on the server", e); - returncode = RETURN_NACK; - } - } - else { - returnval = new NoSuchObjectException(""); - returncode = RETURN_NACK; - } - - conn.getDataOutputStream().writeByte(MESSAGE_CALL_ACK); - - ObjectOutputStream out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream - - out.writeByte(returncode); - (new UID()).write(out); - - //System.out.println("returnval=" + returnval + " returncls=" + returncls); - - if(returnval != null && returncls != null) - ((RMIObjectOutputStream)out).writeValue(returnval, returncls); - - // 1.1/1.2 void return type detection: - else if (!(returnval instanceof RMIVoidValue || returncls == Void.TYPE)) - out.writeObject(returnval); - - out.flush(); -} + // System.out.println("ObjID: " + objid + ", method: " + method + ", hash: " + // + hash); + + // Use the objid to locate the relevant UnicastServerRef + UnicastServerRef uref = (UnicastServerRef) objects.get(objid); + Object returnval; + int returncode = RETURN_ACK; + // returnval is from Method.invoke(), so we must check the return class to + // see + // if it's primitive type + Class returncls = null; + if (uref != null) + { + try + { + // Dispatch the call to it. + returnval = uref.incomingMessageCall(conn, method, hash); + returncls = uref.getMethodReturnType(method, hash); + } + catch (Exception e) + { + returnval = e; + returncode = RETURN_NACK; + } + catch (Error e) + { + returnval = new ServerError( + "Server error, ObjID: " + objid + + ", method: " + method + ", hash: "+ hash, e); + returncode = RETURN_NACK; + } + } + else + { + returnval = new NoSuchObjectException("ObjID: " + objid); + returncode = RETURN_NACK; + } + + conn.getDataOutputStream().writeByte(MESSAGE_CALL_ACK); + + ObjectOutputStream out = conn.startObjectOutputStream(); // (re)start + // ObjectOutputStream + + out.writeByte(returncode); + (new UID()).write(out); + + // System.out.println("returnval=" + returnval + " returncls=" + returncls); + + if (returnval != null && returncls != null) + ((RMIObjectOutputStream) out).writeValue(returnval, returncls); + + // 1.1/1.2 void return type detection: + else if (! (returnval instanceof RMIVoidValue || returncls == Void.TYPE)) + out.writeObject(returnval); + + out.flush(); + } } diff --git a/libjava/classpath/gnu/java/rmi/server/UnicastServerRef.java b/libjava/classpath/gnu/java/rmi/server/UnicastServerRef.java index dcb12a5..cd891a1 100644 --- a/libjava/classpath/gnu/java/rmi/server/UnicastServerRef.java +++ b/libjava/classpath/gnu/java/rmi/server/UnicastServerRef.java @@ -58,6 +58,10 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +/** + * This class connects the local, remotely available (exported) object to + * the local RMI server that accepts the remote calls. + */ public class UnicastServerRef extends UnicastRef { @@ -80,18 +84,18 @@ public class UnicastServerRef /** * The skeleton (if any), associated with the exported remote object. */ - private Skeleton skel; + protected Skeleton skel; /** * The stub, associated with the exported remote object (may be proxy class). */ - private Remote stub; + protected Remote stub; /** * The method table (RMI hash code to method) of the methods of the * exported object. */ - private Hashtable methods = new Hashtable(); + protected Hashtable methods = new Hashtable(); /** * Used by serialization. @@ -125,8 +129,7 @@ public class UnicastServerRef { myself = obj; // Save it to server manager, to let client calls in the same VM to - // issue - // local call + // issue local call manager.serverobj = obj; String ignoreStubs; @@ -202,7 +205,7 @@ public class UnicastServerRef * * @return the class having stub defined, null if none. */ - private Class findStubSkelClass(Class startCls) + protected Class findStubSkelClass(Class startCls) { Class cls = startCls; @@ -242,7 +245,7 @@ public class UnicastServerRef * @return the instantiated instance of the helper class or null if the * helper class cannot be found or instantiated. */ - private Object getHelperClass(Class cls, String type) + protected Object getHelperClass(Class cls, String type) { try { @@ -310,7 +313,7 @@ public class UnicastServerRef * @param build if true, the class methods are added to the table. If * false, they are removed from the table. */ - private void buildMethodHash(Class cls, boolean build) + protected void buildMethodHash(Class cls, boolean build) { Method[] meths = cls.getMethods(); for (int i = 0; i < meths.length; i++) @@ -339,7 +342,11 @@ public class UnicastServerRef else return null; } - + + /** + * This method is called from the {@link UnicastServer#incomingMessageCall} + * to deliver the remote call to this object. + */ public Object incomingMessageCall(UnicastConnection conn, int method, long hash) throws Exception { @@ -353,7 +360,8 @@ public class UnicastServerRef // meth); if (meth == null) { - throw new NoSuchMethodException(); + throw new NoSuchMethodException( + myself.getClass().getName()+" hash "+hash); } ObjectInputStream in = conn.getObjectInputStream(); @@ -413,9 +421,8 @@ public class UnicastServerRef else { if (skel == null) - { - throw new NoSuchMethodException(); - } + throw new NoSuchMethodException("JDK 1.1 call - Skeleton required"); + UnicastRemoteCall call = new UnicastRemoteCall(conn); skel.dispatch(myself, call, method, hash); if (! call.isReturnValue()) diff --git a/libjava/classpath/gnu/java/security/Properties.java b/libjava/classpath/gnu/java/security/Properties.java index 813888c..860b7d9 100644 --- a/libjava/classpath/gnu/java/security/Properties.java +++ b/libjava/classpath/gnu/java/security/Properties.java @@ -333,9 +333,9 @@ public final class Properties handleBooleanProperty(DO_RSA_BLINDING); // re-sync the 'known' properties - reproducible = new Boolean((String) props.get(REPRODUCIBLE_PRNG)).booleanValue(); - checkForWeakKeys = new Boolean((String) props.get(CHECK_WEAK_KEYS)).booleanValue(); - doRSABlinding = new Boolean((String) props.get(DO_RSA_BLINDING)).booleanValue(); + reproducible = Boolean.valueOf((String) props.get(REPRODUCIBLE_PRNG)).booleanValue(); + checkForWeakKeys = Boolean.valueOf((String) props.get(CHECK_WEAK_KEYS)).booleanValue(); + doRSABlinding = Boolean.valueOf((String) props.get(DO_RSA_BLINDING)).booleanValue(); // This does not change. props.put(VERSION, Registry.VERSION_STRING); diff --git a/libjava/classpath/gnu/java/security/Registry.java b/libjava/classpath/gnu/java/security/Registry.java index efb54e7..0cb9251 100644 --- a/libjava/classpath/gnu/java/security/Registry.java +++ b/libjava/classpath/gnu/java/security/Registry.java @@ -159,6 +159,9 @@ public interface Registry // Padding scheme names and synonyms........................................ + /** PKCS#5 padding scheme. */ + String PKCS5_PAD = "pkcs5"; + /** PKCS#7 padding scheme. */ String PKCS7_PAD = "pkcs7"; diff --git a/libjava/classpath/gnu/java/security/hash/Haval.java b/libjava/classpath/gnu/java/security/hash/Haval.java index f9f3282..1bf7565 100644 --- a/libjava/classpath/gnu/java/security/hash/Haval.java +++ b/libjava/classpath/gnu/java/security/hash/Haval.java @@ -567,7 +567,7 @@ public class Haval extends BaseHash { if (valid == null) { - valid = new Boolean(DIGEST0.equals(Util.toString(new Haval().digest()))); + valid = Boolean.valueOf(DIGEST0.equals(Util.toString(new Haval().digest()))); } return valid.booleanValue(); } diff --git a/libjava/classpath/gnu/java/security/hash/MD2.java b/libjava/classpath/gnu/java/security/hash/MD2.java index 41e8769..2a93c42 100644 --- a/libjava/classpath/gnu/java/security/hash/MD2.java +++ b/libjava/classpath/gnu/java/security/hash/MD2.java @@ -181,7 +181,7 @@ public class MD2 extends BaseHash { if (valid == null) { - valid = new Boolean(DIGEST0.equals(Util.toString(new MD2().digest()))); + valid = Boolean.valueOf(DIGEST0.equals(Util.toString(new MD2().digest()))); } return valid.booleanValue(); } diff --git a/libjava/classpath/gnu/java/security/hash/MD4.java b/libjava/classpath/gnu/java/security/hash/MD4.java index 54dda35..a09eb17 100644 --- a/libjava/classpath/gnu/java/security/hash/MD4.java +++ b/libjava/classpath/gnu/java/security/hash/MD4.java @@ -154,7 +154,7 @@ public class MD4 extends BaseHash { if (valid == null) { - valid = new Boolean(DIGEST0.equals(Util.toString(new MD4().digest()))); + valid = Boolean.valueOf(DIGEST0.equals(Util.toString(new MD4().digest()))); } return valid.booleanValue(); } diff --git a/libjava/classpath/gnu/java/security/hash/MD5.java b/libjava/classpath/gnu/java/security/hash/MD5.java index 4632929..1653923 100644 --- a/libjava/classpath/gnu/java/security/hash/MD5.java +++ b/libjava/classpath/gnu/java/security/hash/MD5.java @@ -358,7 +358,7 @@ public class MD5 extends BaseHash { if (valid == null) { - valid = new Boolean(DIGEST0.equals(Util.toString(new MD5().digest()))); + valid = Boolean.valueOf(DIGEST0.equals(Util.toString(new MD5().digest()))); } return valid.booleanValue(); } diff --git a/libjava/classpath/gnu/java/security/hash/RipeMD128.java b/libjava/classpath/gnu/java/security/hash/RipeMD128.java index 83e8f25..31874a0 100644 --- a/libjava/classpath/gnu/java/security/hash/RipeMD128.java +++ b/libjava/classpath/gnu/java/security/hash/RipeMD128.java @@ -283,7 +283,7 @@ public class RipeMD128 extends BaseHash { if (valid == null) { - valid = new Boolean + valid = Boolean.valueOf (DIGEST0.equals(Util.toString(new RipeMD128().digest()))); } return valid.booleanValue(); diff --git a/libjava/classpath/gnu/java/security/hash/RipeMD160.java b/libjava/classpath/gnu/java/security/hash/RipeMD160.java index 73ecc516..642ffb3 100644 --- a/libjava/classpath/gnu/java/security/hash/RipeMD160.java +++ b/libjava/classpath/gnu/java/security/hash/RipeMD160.java @@ -320,7 +320,7 @@ public class RipeMD160 extends BaseHash { if (valid == null) { - valid = new Boolean + valid = Boolean.valueOf (DIGEST0.equals(Util.toString(new RipeMD160().digest()))); } return valid.booleanValue(); diff --git a/libjava/classpath/gnu/java/security/hash/Sha160.java b/libjava/classpath/gnu/java/security/hash/Sha160.java index bf5f456..037b118 100644 --- a/libjava/classpath/gnu/java/security/hash/Sha160.java +++ b/libjava/classpath/gnu/java/security/hash/Sha160.java @@ -229,7 +229,7 @@ public class Sha160 extends BaseHash md.update((byte) 0x62); // b md.update((byte) 0x63); // c String result = Util.toString(md.digest()); - valid = new Boolean(DIGEST0.equals(result)); + valid = Boolean.valueOf(DIGEST0.equals(result)); } return valid.booleanValue(); } diff --git a/libjava/classpath/gnu/java/security/hash/Sha256.java b/libjava/classpath/gnu/java/security/hash/Sha256.java index 9ef70a1..284425e 100644 --- a/libjava/classpath/gnu/java/security/hash/Sha256.java +++ b/libjava/classpath/gnu/java/security/hash/Sha256.java @@ -217,7 +217,7 @@ public class Sha256 extends BaseHash md.update((byte) 0x62); // b md.update((byte) 0x63); // c String result = Util.toString(md.digest()); - valid = new Boolean(DIGEST0.equals(result)); + valid = Boolean.valueOf(DIGEST0.equals(result)); } return valid.booleanValue(); diff --git a/libjava/classpath/gnu/java/security/hash/Sha384.java b/libjava/classpath/gnu/java/security/hash/Sha384.java index 2f619dc..332f048 100644 --- a/libjava/classpath/gnu/java/security/hash/Sha384.java +++ b/libjava/classpath/gnu/java/security/hash/Sha384.java @@ -254,7 +254,7 @@ public class Sha384 extends BaseHash md.update((byte) 0x62); // b md.update((byte) 0x63); // c String result = Util.toString(md.digest()); - valid = new Boolean(DIGEST0.equals(result)); + valid = Boolean.valueOf(DIGEST0.equals(result)); } return valid.booleanValue(); } diff --git a/libjava/classpath/gnu/java/security/hash/Sha512.java b/libjava/classpath/gnu/java/security/hash/Sha512.java index 798b34d..da035dc 100644 --- a/libjava/classpath/gnu/java/security/hash/Sha512.java +++ b/libjava/classpath/gnu/java/security/hash/Sha512.java @@ -256,7 +256,7 @@ public class Sha512 extends BaseHash md.update((byte) 0x62); // b md.update((byte) 0x63); // c String result = Util.toString(md.digest()); - valid = new Boolean(DIGEST0.equals(result)); + valid = Boolean.valueOf(DIGEST0.equals(result)); } return valid.booleanValue(); } diff --git a/libjava/classpath/gnu/java/security/hash/Tiger.java b/libjava/classpath/gnu/java/security/hash/Tiger.java index f39fed3..be0921d 100644 --- a/libjava/classpath/gnu/java/security/hash/Tiger.java +++ b/libjava/classpath/gnu/java/security/hash/Tiger.java @@ -640,7 +640,7 @@ public class Tiger extends BaseHash { if (valid == null) { - valid = new Boolean(DIGEST0.equals(Util.toString(new Tiger().digest()))); + valid = Boolean.valueOf(DIGEST0.equals(Util.toString(new Tiger().digest()))); } return valid.booleanValue(); } diff --git a/libjava/classpath/gnu/java/security/hash/Whirlpool.java b/libjava/classpath/gnu/java/security/hash/Whirlpool.java index 8c5d9f3..b10fa53 100644 --- a/libjava/classpath/gnu/java/security/hash/Whirlpool.java +++ b/libjava/classpath/gnu/java/security/hash/Whirlpool.java @@ -42,25 +42,25 @@ import gnu.java.security.Registry; import gnu.java.security.util.Util; /** - * <p>Whirlpool, a new 512-bit hashing function operating on messages less than + * Whirlpool, a new 512-bit hashing function operating on messages less than * 2 ** 256 bits in length. The function structure is designed according to the * Wide Trail strategy and permits a wide variety of implementation trade-offs. - * </p> - * - * <p><b>IMPORTANT</b>: This implementation is not thread-safe.</p> - * - * <p>References:</p> - * + * <p> + * This implementation is of Whirlpool Version 3, described in [1] last revised + * on May 24th, 2003. + * <p> + * <b>IMPORTANT</b>: This implementation is not thread-safe. + * <p> + * References: * <ol> * <li><a href="http://planeta.terra.com.br/informatica/paulobarreto/WhirlpoolPage.html"> * The WHIRLPOOL Hashing Function</a>.<br> * <a href="mailto:paulo.barreto@terra.com.br">Paulo S.L.M. Barreto</a> and - * <a href="mailto:vincent.rijmen@esat.kuleuven.ac.be">Vincent Rijmen</a>.</li> + * <a href="mailto:vincent.rijmen@iaik.tugraz.at">Vincent Rijmen</a>.</li> * </ol> */ public final class Whirlpool extends BaseHash { - // Debugging methods and variables // ------------------------------------------------------------------------- @@ -74,45 +74,43 @@ public final class Whirlpool extends BaseHash private static final int BLOCK_SIZE = 64; // inner block size in bytes /** The digest of the 0-bit long message. */ - private static final String DIGEST0 = "470F0409ABAA446E49667D4EBE12A14387CEDBD10DD17B8243CAD550A089DC0F" - + "EEA7AA40F6C2AAAB71C6EBD076E43C7CFCA0AD32567897DCB5969861049A0F5A"; - - private static final int R = 10; // default number of rounds - - private static final String Sd = // p. 19 [WHIRLPOOL] - "\u1823\uc6E8\u87B8\u014F\u36A6\ud2F5\u796F\u9152" - + "\u60Bc\u9B8E\uA30c\u7B35\u1dE0\ud7c2\u2E4B\uFE57" - + "\u1577\u37E5\u9FF0\u4AdA\u58c9\u290A\uB1A0\u6B85" - + "\uBd5d\u10F4\ucB3E\u0567\uE427\u418B\uA77d\u95d8" - + "\uFBEE\u7c66\udd17\u479E\ucA2d\uBF07\uAd5A\u8333" - + "\u6302\uAA71\uc819\u49d9\uF2E3\u5B88\u9A26\u32B0" - + "\uE90F\ud580\uBEcd\u3448\uFF7A\u905F\u2068\u1AAE" - + "\uB454\u9322\u64F1\u7312\u4008\uc3Ec\udBA1\u8d3d" - + "\u9700\ucF2B\u7682\ud61B\uB5AF\u6A50\u45F3\u30EF" - + "\u3F55\uA2EA\u65BA\u2Fc0\udE1c\uFd4d\u9275\u068A" - + "\uB2E6\u0E1F\u62d4\uA896\uF9c5\u2559\u8472\u394c" - + "\u5E78\u388c\ud1A5\uE261\uB321\u9c1E\u43c7\uFc04" - + "\u5199\u6d0d\uFAdF\u7E24\u3BAB\ucE11\u8F4E\uB7EB" - + "\u3c81\u94F7\uB913\u2cd3\uE76E\uc403\u5644\u7FA9" - + "\u2ABB\uc153\udc0B\u9d6c\u3174\uF646\uAc89\u14E1" - + "\u163A\u6909\u70B6\ud0Ed\ucc42\u98A4\u285c\uF886"; - + private static final String DIGEST0 = + "19FA61D75522A4669B44E39C1D2E1726C530232130D407F89AFEE0964997F7A7" + + "3E83BE698B288FEBCF88E3E03C4F0757EA8964E59B63D93708B138CC42A66EB3"; + + /** Default number of rounds. */ + private static final int R = 10; + + /** Whirlpool S-box; p. 19. */ + private static final String S_box = // p. 19 [WHIRLPOOL] + "\u1823\uc6E8\u87B8\u014F\u36A6\ud2F5\u796F\u9152" + + "\u60Bc\u9B8E\uA30c\u7B35\u1dE0\ud7c2\u2E4B\uFE57" + + "\u1577\u37E5\u9FF0\u4AdA\u58c9\u290A\uB1A0\u6B85" + + "\uBd5d\u10F4\ucB3E\u0567\uE427\u418B\uA77d\u95d8" + + "\uFBEE\u7c66\udd17\u479E\ucA2d\uBF07\uAd5A\u8333" + + "\u6302\uAA71\uc819\u49d9\uF2E3\u5B88\u9A26\u32B0" + + "\uE90F\ud580\uBEcd\u3448\uFF7A\u905F\u2068\u1AAE" + + "\uB454\u9322\u64F1\u7312\u4008\uc3Ec\udBA1\u8d3d" + + "\u9700\ucF2B\u7682\ud61B\uB5AF\u6A50\u45F3\u30EF" + + "\u3F55\uA2EA\u65BA\u2Fc0\udE1c\uFd4d\u9275\u068A" + + "\uB2E6\u0E1F\u62d4\uA896\uF9c5\u2559\u8472\u394c" + + "\u5E78\u388c\ud1A5\uE261\uB321\u9c1E\u43c7\uFc04" + + "\u5199\u6d0d\uFAdF\u7E24\u3BAB\ucE11\u8F4E\uB7EB" + + "\u3c81\u94F7\uB913\u2cd3\uE76E\uc403\u5644\u7FA9" + + "\u2ABB\uc153\udc0B\u9d6c\u3174\uF646\uAc89\u14E1" + + "\u163A\u6909\u70B6\ud0Ed\ucc42\u98A4\u285c\uF886"; + + /** The 64-bit lookup tables; section 7.1 p. 13. */ private static final long[] T0 = new long[256]; - private static final long[] T1 = new long[256]; - private static final long[] T2 = new long[256]; - private static final long[] T3 = new long[256]; - private static final long[] T4 = new long[256]; - private static final long[] T5 = new long[256]; - private static final long[] T6 = new long[256]; - private static final long[] T7 = new long[256]; + /** The round constants. */ private static final long[] rc = new long[R]; /** caches the result of the correctness test, once executed. */ @@ -123,12 +121,10 @@ public final class Whirlpool extends BaseHash /** Work area for computing the round key schedule. */ private long k00, k01, k02, k03, k04, k05, k06, k07; - private long Kr0, Kr1, Kr2, Kr3, Kr4, Kr5, Kr6, Kr7; /** work area for transforming the 512-bit buffer. */ private long n0, n1, n2, n3, n4, n5, n6, n7; - private long nn0, nn1, nn2, nn3, nn4, nn5, nn6, nn7; /** work area for holding block cipher's intermediate values. */ @@ -140,72 +136,67 @@ public final class Whirlpool extends BaseHash { long time = System.currentTimeMillis(); - int ROOT = 0x11d; // para. 2.1 [WHIRLPOOL] + int ROOT = 0x11D; // para. 2.1 [WHIRLPOOL] int i, r, j; - long s, s2, s3, s4, s5, s8, s9, t; + long s1, s2, s4, s5, s8, s9, t; char c; final byte[] S = new byte[256]; for (i = 0; i < 256; i++) { - c = Sd.charAt(i >>> 1); + c = S_box.charAt(i >>> 1); - s = ((i & 1) == 0 ? c >>> 8 : c) & 0xFFL; - s2 = s << 1; + s1 = ((i & 1) == 0 ? c >>> 8 : c) & 0xFFL; + s2 = s1 << 1; if (s2 > 0xFFL) - { - s2 ^= ROOT; - } - s3 = s2 ^ s; + s2 ^= ROOT; + s4 = s2 << 1; if (s4 > 0xFFL) - { - s4 ^= ROOT; - } - s5 = s4 ^ s; + s4 ^= ROOT; + + s5 = s4 ^ s1; s8 = s4 << 1; if (s8 > 0xFFL) - { - s8 ^= ROOT; - } - s9 = s8 ^ s; + s8 ^= ROOT; - S[i] = (byte) s; - T0[i] = t = s << 56 | s << 48 | s3 << 40 | s << 32 | s5 << 24 - | s8 << 16 | s9 << 8 | s5; - T1[i] = t >>> 8 | t << 56; + s9 = s8 ^ s1; + + T0[i] = t = s1 << 56 | s1 << 48 | s4 << 40 | s1 << 32 + | s8 << 24 | s5 << 16 | s2 << 8 | s9; + T1[i] = t >>> 8 | t << 56; T2[i] = t >>> 16 | t << 48; T3[i] = t >>> 24 | t << 40; T4[i] = t >>> 32 | t << 32; T5[i] = t >>> 40 | t << 24; T6[i] = t >>> 48 | t << 16; - T7[i] = t >>> 56 | t << 8; + T7[i] = t >>> 56 | t << 8; } - for (r = 1, i = 0, j = 0; r < R + 1; r++) - { - rc[i++] = (S[j++] & 0xFFL) << 56 | (S[j++] & 0xFFL) << 48 - | (S[j++] & 0xFFL) << 40 | (S[j++] & 0xFFL) << 32 - | (S[j++] & 0xFFL) << 24 | (S[j++] & 0xFFL) << 16 - | (S[j++] & 0xFFL) << 8 | (S[j++] & 0xFFL); - } + for (r = 0, i = 0; r < R; ) + rc[r++] = (T0[i++] & 0xFF00000000000000L) + ^ (T1[i++] & 0x00FF000000000000L) + ^ (T2[i++] & 0x0000FF0000000000L) + ^ (T3[i++] & 0x000000FF00000000L) + ^ (T4[i++] & 0x00000000FF000000L) + ^ (T5[i++] & 0x0000000000FF0000L) + ^ (T6[i++] & 0x000000000000FF00L) + ^ (T7[i++] & 0x00000000000000FFL); time = System.currentTimeMillis() - time; - if (DEBUG && debuglevel > 8) { System.out.println("=========="); System.out.println(); System.out.println("Static data"); System.out.println(); - + System.out.println(); System.out.println("T0[]:"); for (i = 0; i < 64; i++) { for (j = 0; j < 4; j++) - { - System.out.print("0x" + Util.toString(T0[i * 4 + j]) + ", "); - } + System.out.print("0x" + Util.toString(T0[i * 4 + j]) + ", "); + System.out.println(); } System.out.println(); @@ -213,9 +204,8 @@ public final class Whirlpool extends BaseHash for (i = 0; i < 64; i++) { for (j = 0; j < 4; j++) - { - System.out.print("0x" + Util.toString(T1[i * 4 + j]) + ", "); - } + System.out.print("0x" + Util.toString(T1[i * 4 + j]) + ", "); + System.out.println(); } System.out.println(); @@ -223,9 +213,8 @@ public final class Whirlpool extends BaseHash for (i = 0; i < 64; i++) { for (j = 0; j < 4; j++) - { - System.out.print("0x" + Util.toString(T2[i * 4 + j]) + ", "); - } + System.out.print("0x" + Util.toString(T2[i * 4 + j]) + ", "); + System.out.println(); } System.out.println(); @@ -233,9 +222,8 @@ public final class Whirlpool extends BaseHash for (i = 0; i < 64; i++) { for (j = 0; j < 4; j++) - { - System.out.print("0x" + Util.toString(T3[i * 4 + j]) + ", "); - } + System.out.print("0x" + Util.toString(T3[i * 4 + j]) + ", "); + System.out.println(); } System.out.println(); @@ -243,9 +231,8 @@ public final class Whirlpool extends BaseHash for (i = 0; i < 64; i++) { for (j = 0; j < 4; j++) - { - System.out.print("0x" + Util.toString(T4[i * 4 + j]) + ", "); - } + System.out.print("0x" + Util.toString(T4[i * 4 + j]) + ", "); + System.out.println(); } System.out.println(); @@ -253,9 +240,8 @@ public final class Whirlpool extends BaseHash for (i = 0; i < 64; i++) { for (j = 0; j < 4; j++) - { - System.out.print("0x" + Util.toString(T5[i * 4 + j]) + ", "); - } + System.out.print("0x" + Util.toString(T5[i * 4 + j]) + ", "); + System.out.println(); } System.out.println(); @@ -263,9 +249,8 @@ public final class Whirlpool extends BaseHash for (i = 0; i < 64; i++) { for (j = 0; j < 4; j++) - { - System.out.print("0x" + Util.toString(T5[i * 4 + j]) + ", "); - } + System.out.print("0x" + Util.toString(T5[i * 4 + j]) + ", "); + System.out.println(); } System.out.println(); @@ -273,17 +258,15 @@ public final class Whirlpool extends BaseHash for (i = 0; i < 64; i++) { for (j = 0; j < 4; j++) - { - System.out.print("0x" + Util.toString(T5[i * 4 + j]) + ", "); - } + System.out.print("0x" + Util.toString(T5[i * 4 + j]) + ", "); + System.out.println(); } System.out.println(); System.out.println("rc[]:"); for (i = 0; i < R; i++) - { - System.out.println("0x" + Util.toString(rc[i])); - } + System.out.println("0x" + Util.toString(rc[i])); + System.out.println(); System.out.println(); @@ -340,38 +323,70 @@ public final class Whirlpool extends BaseHash protected void transform(byte[] in, int offset) { // apply mu to the input - n0 = (in[offset++] & 0xFFL) << 56 | (in[offset++] & 0xFFL) << 48 - | (in[offset++] & 0xFFL) << 40 | (in[offset++] & 0xFFL) << 32 - | (in[offset++] & 0xFFL) << 24 | (in[offset++] & 0xFFL) << 16 - | (in[offset++] & 0xFFL) << 8 | (in[offset++] & 0xFFL); - n1 = (in[offset++] & 0xFFL) << 56 | (in[offset++] & 0xFFL) << 48 - | (in[offset++] & 0xFFL) << 40 | (in[offset++] & 0xFFL) << 32 - | (in[offset++] & 0xFFL) << 24 | (in[offset++] & 0xFFL) << 16 - | (in[offset++] & 0xFFL) << 8 | (in[offset++] & 0xFFL); - n2 = (in[offset++] & 0xFFL) << 56 | (in[offset++] & 0xFFL) << 48 - | (in[offset++] & 0xFFL) << 40 | (in[offset++] & 0xFFL) << 32 - | (in[offset++] & 0xFFL) << 24 | (in[offset++] & 0xFFL) << 16 - | (in[offset++] & 0xFFL) << 8 | (in[offset++] & 0xFFL); - n3 = (in[offset++] & 0xFFL) << 56 | (in[offset++] & 0xFFL) << 48 - | (in[offset++] & 0xFFL) << 40 | (in[offset++] & 0xFFL) << 32 - | (in[offset++] & 0xFFL) << 24 | (in[offset++] & 0xFFL) << 16 - | (in[offset++] & 0xFFL) << 8 | (in[offset++] & 0xFFL); - n4 = (in[offset++] & 0xFFL) << 56 | (in[offset++] & 0xFFL) << 48 - | (in[offset++] & 0xFFL) << 40 | (in[offset++] & 0xFFL) << 32 - | (in[offset++] & 0xFFL) << 24 | (in[offset++] & 0xFFL) << 16 - | (in[offset++] & 0xFFL) << 8 | (in[offset++] & 0xFFL); - n5 = (in[offset++] & 0xFFL) << 56 | (in[offset++] & 0xFFL) << 48 - | (in[offset++] & 0xFFL) << 40 | (in[offset++] & 0xFFL) << 32 - | (in[offset++] & 0xFFL) << 24 | (in[offset++] & 0xFFL) << 16 - | (in[offset++] & 0xFFL) << 8 | (in[offset++] & 0xFFL); - n6 = (in[offset++] & 0xFFL) << 56 | (in[offset++] & 0xFFL) << 48 - | (in[offset++] & 0xFFL) << 40 | (in[offset++] & 0xFFL) << 32 - | (in[offset++] & 0xFFL) << 24 | (in[offset++] & 0xFFL) << 16 - | (in[offset++] & 0xFFL) << 8 | (in[offset++] & 0xFFL); - n7 = (in[offset++] & 0xFFL) << 56 | (in[offset++] & 0xFFL) << 48 - | (in[offset++] & 0xFFL) << 40 | (in[offset++] & 0xFFL) << 32 - | (in[offset++] & 0xFFL) << 24 | (in[offset++] & 0xFFL) << 16 - | (in[offset++] & 0xFFL) << 8 | (in[offset++] & 0xFFL); + n0 = (in[offset++] & 0xFFL) << 56 + | (in[offset++] & 0xFFL) << 48 + | (in[offset++] & 0xFFL) << 40 + | (in[offset++] & 0xFFL) << 32 + | (in[offset++] & 0xFFL) << 24 + | (in[offset++] & 0xFFL) << 16 + | (in[offset++] & 0xFFL) << 8 + | (in[offset++] & 0xFFL); + n1 = (in[offset++] & 0xFFL) << 56 + | (in[offset++] & 0xFFL) << 48 + | (in[offset++] & 0xFFL) << 40 + | (in[offset++] & 0xFFL) << 32 + | (in[offset++] & 0xFFL) << 24 + | (in[offset++] & 0xFFL) << 16 + | (in[offset++] & 0xFFL) << 8 + | (in[offset++] & 0xFFL); + n2 = (in[offset++] & 0xFFL) << 56 + | (in[offset++] & 0xFFL) << 48 + | (in[offset++] & 0xFFL) << 40 + | (in[offset++] & 0xFFL) << 32 + | (in[offset++] & 0xFFL) << 24 + | (in[offset++] & 0xFFL) << 16 + | (in[offset++] & 0xFFL) << 8 + | (in[offset++] & 0xFFL); + n3 = (in[offset++] & 0xFFL) << 56 + | (in[offset++] & 0xFFL) << 48 + | (in[offset++] & 0xFFL) << 40 + | (in[offset++] & 0xFFL) << 32 + | (in[offset++] & 0xFFL) << 24 + | (in[offset++] & 0xFFL) << 16 + | (in[offset++] & 0xFFL) << 8 + | (in[offset++] & 0xFFL); + n4 = (in[offset++] & 0xFFL) << 56 + | (in[offset++] & 0xFFL) << 48 + | (in[offset++] & 0xFFL) << 40 + | (in[offset++] & 0xFFL) << 32 + | (in[offset++] & 0xFFL) << 24 + | (in[offset++] & 0xFFL) << 16 + | (in[offset++] & 0xFFL) << 8 + | (in[offset++] & 0xFFL); + n5 = (in[offset++] & 0xFFL) << 56 + | (in[offset++] & 0xFFL) << 48 + | (in[offset++] & 0xFFL) << 40 + | (in[offset++] & 0xFFL) << 32 + | (in[offset++] & 0xFFL) << 24 + | (in[offset++] & 0xFFL) << 16 + | (in[offset++] & 0xFFL) << 8 + | (in[offset++] & 0xFFL); + n6 = (in[offset++] & 0xFFL) << 56 + | (in[offset++] & 0xFFL) << 48 + | (in[offset++] & 0xFFL) << 40 + | (in[offset++] & 0xFFL) << 32 + | (in[offset++] & 0xFFL) << 24 + | (in[offset++] & 0xFFL) << 16 + | (in[offset++] & 0xFFL) << 8 + | (in[offset++] & 0xFFL); + n7 = (in[offset++] & 0xFFL) << 56 + | (in[offset++] & 0xFFL) << 48 + | (in[offset++] & 0xFFL) << 40 + | (in[offset++] & 0xFFL) << 32 + | (in[offset++] & 0xFFL) << 24 + | (in[offset++] & 0xFFL) << 16 + | (in[offset++] & 0xFFL) << 8 + | (in[offset++] & 0xFFL); // transform K into the key schedule Kr; 0 <= r <= R k00 = H0; @@ -399,62 +414,70 @@ public final class Whirlpool extends BaseHash { // 1. compute intermediate round key schedule by applying ro[rc] // to the previous round key schedule --rc being the round constant - Kr0 = T0[(int) ((k00 >> 56) & 0xFFL)] ^ T1[(int) ((k07 >> 48) & 0xFFL)] - ^ T2[(int) ((k06 >> 40) & 0xFFL)] - ^ T3[(int) ((k05 >> 32) & 0xFFL)] - ^ T4[(int) ((k04 >> 24) & 0xFFL)] - ^ T5[(int) ((k03 >> 16) & 0xFFL)] - ^ T6[(int) ((k02 >> 8) & 0xFFL)] ^ T7[(int) (k01 & 0xFFL)] - ^ rc[r]; - - Kr1 = T0[(int) ((k01 >> 56) & 0xFFL)] ^ T1[(int) ((k00 >> 48) & 0xFFL)] - ^ T2[(int) ((k07 >> 40) & 0xFFL)] - ^ T3[(int) ((k06 >> 32) & 0xFFL)] - ^ T4[(int) ((k05 >> 24) & 0xFFL)] - ^ T5[(int) ((k04 >> 16) & 0xFFL)] - ^ T6[(int) ((k03 >> 8) & 0xFFL)] ^ T7[(int) (k02 & 0xFFL)]; - - Kr2 = T0[(int) ((k02 >> 56) & 0xFFL)] ^ T1[(int) ((k01 >> 48) & 0xFFL)] - ^ T2[(int) ((k00 >> 40) & 0xFFL)] - ^ T3[(int) ((k07 >> 32) & 0xFFL)] - ^ T4[(int) ((k06 >> 24) & 0xFFL)] - ^ T5[(int) ((k05 >> 16) & 0xFFL)] - ^ T6[(int) ((k04 >> 8) & 0xFFL)] ^ T7[(int) (k03 & 0xFFL)]; - - Kr3 = T0[(int) ((k03 >> 56) & 0xFFL)] ^ T1[(int) ((k02 >> 48) & 0xFFL)] - ^ T2[(int) ((k01 >> 40) & 0xFFL)] - ^ T3[(int) ((k00 >> 32) & 0xFFL)] - ^ T4[(int) ((k07 >> 24) & 0xFFL)] - ^ T5[(int) ((k06 >> 16) & 0xFFL)] - ^ T6[(int) ((k05 >> 8) & 0xFFL)] ^ T7[(int) (k04 & 0xFFL)]; - - Kr4 = T0[(int) ((k04 >> 56) & 0xFFL)] ^ T1[(int) ((k03 >> 48) & 0xFFL)] - ^ T2[(int) ((k02 >> 40) & 0xFFL)] - ^ T3[(int) ((k01 >> 32) & 0xFFL)] - ^ T4[(int) ((k00 >> 24) & 0xFFL)] - ^ T5[(int) ((k07 >> 16) & 0xFFL)] - ^ T6[(int) ((k06 >> 8) & 0xFFL)] ^ T7[(int) (k05 & 0xFFL)]; - - Kr5 = T0[(int) ((k05 >> 56) & 0xFFL)] ^ T1[(int) ((k04 >> 48) & 0xFFL)] - ^ T2[(int) ((k03 >> 40) & 0xFFL)] - ^ T3[(int) ((k02 >> 32) & 0xFFL)] - ^ T4[(int) ((k01 >> 24) & 0xFFL)] - ^ T5[(int) ((k00 >> 16) & 0xFFL)] - ^ T6[(int) ((k07 >> 8) & 0xFFL)] ^ T7[(int) (k06 & 0xFFL)]; - - Kr6 = T0[(int) ((k06 >> 56) & 0xFFL)] ^ T1[(int) ((k05 >> 48) & 0xFFL)] - ^ T2[(int) ((k04 >> 40) & 0xFFL)] - ^ T3[(int) ((k03 >> 32) & 0xFFL)] - ^ T4[(int) ((k02 >> 24) & 0xFFL)] - ^ T5[(int) ((k01 >> 16) & 0xFFL)] - ^ T6[(int) ((k00 >> 8) & 0xFFL)] ^ T7[(int) (k07 & 0xFFL)]; - - Kr7 = T0[(int) ((k07 >> 56) & 0xFFL)] ^ T1[(int) ((k06 >> 48) & 0xFFL)] - ^ T2[(int) ((k05 >> 40) & 0xFFL)] - ^ T3[(int) ((k04 >> 32) & 0xFFL)] - ^ T4[(int) ((k03 >> 24) & 0xFFL)] - ^ T5[(int) ((k02 >> 16) & 0xFFL)] - ^ T6[(int) ((k01 >> 8) & 0xFFL)] ^ T7[(int) (k00 & 0xFFL)]; + Kr0 = T0[(int)((k00 >> 56) & 0xFFL)] + ^ T1[(int)((k07 >> 48) & 0xFFL)] + ^ T2[(int)((k06 >> 40) & 0xFFL)] + ^ T3[(int)((k05 >> 32) & 0xFFL)] + ^ T4[(int)((k04 >> 24) & 0xFFL)] + ^ T5[(int)((k03 >> 16) & 0xFFL)] + ^ T6[(int)((k02 >> 8) & 0xFFL)] + ^ T7[(int)( k01 & 0xFFL)] ^ rc[r]; + Kr1 = T0[(int)((k01 >> 56) & 0xFFL)] + ^ T1[(int)((k00 >> 48) & 0xFFL)] + ^ T2[(int)((k07 >> 40) & 0xFFL)] + ^ T3[(int)((k06 >> 32) & 0xFFL)] + ^ T4[(int)((k05 >> 24) & 0xFFL)] + ^ T5[(int)((k04 >> 16) & 0xFFL)] + ^ T6[(int)((k03 >> 8) & 0xFFL)] + ^ T7[(int)( k02 & 0xFFL)]; + Kr2 = T0[(int)((k02 >> 56) & 0xFFL)] + ^ T1[(int)((k01 >> 48) & 0xFFL)] + ^ T2[(int)((k00 >> 40) & 0xFFL)] + ^ T3[(int)((k07 >> 32) & 0xFFL)] + ^ T4[(int)((k06 >> 24) & 0xFFL)] + ^ T5[(int)((k05 >> 16) & 0xFFL)] + ^ T6[(int)((k04 >> 8) & 0xFFL)] + ^ T7[(int)( k03 & 0xFFL)]; + Kr3 = T0[(int)((k03 >> 56) & 0xFFL)] + ^ T1[(int)((k02 >> 48) & 0xFFL)] + ^ T2[(int)((k01 >> 40) & 0xFFL)] + ^ T3[(int)((k00 >> 32) & 0xFFL)] + ^ T4[(int)((k07 >> 24) & 0xFFL)] + ^ T5[(int)((k06 >> 16) & 0xFFL)] + ^ T6[(int)((k05 >> 8) & 0xFFL)] + ^ T7[(int)( k04 & 0xFFL)]; + Kr4 = T0[(int)((k04 >> 56) & 0xFFL)] + ^ T1[(int)((k03 >> 48) & 0xFFL)] + ^ T2[(int)((k02 >> 40) & 0xFFL)] + ^ T3[(int)((k01 >> 32) & 0xFFL)] + ^ T4[(int)((k00 >> 24) & 0xFFL)] + ^ T5[(int)((k07 >> 16) & 0xFFL)] + ^ T6[(int)((k06 >> 8) & 0xFFL)] + ^ T7[(int)( k05 & 0xFFL)]; + Kr5 = T0[(int)((k05 >> 56) & 0xFFL)] + ^ T1[(int)((k04 >> 48) & 0xFFL)] + ^ T2[(int)((k03 >> 40) & 0xFFL)] + ^ T3[(int)((k02 >> 32) & 0xFFL)] + ^ T4[(int)((k01 >> 24) & 0xFFL)] + ^ T5[(int)((k00 >> 16) & 0xFFL)] + ^ T6[(int)((k07 >> 8) & 0xFFL)] + ^ T7[(int)( k06 & 0xFFL)]; + Kr6 = T0[(int)((k06 >> 56) & 0xFFL)] + ^ T1[(int)((k05 >> 48) & 0xFFL)] + ^ T2[(int)((k04 >> 40) & 0xFFL)] + ^ T3[(int)((k03 >> 32) & 0xFFL)] + ^ T4[(int)((k02 >> 24) & 0xFFL)] + ^ T5[(int)((k01 >> 16) & 0xFFL)] + ^ T6[(int)((k00 >> 8) & 0xFFL)] + ^ T7[(int)( k07 & 0xFFL)]; + Kr7 = T0[(int)((k07 >> 56) & 0xFFL)] + ^ T1[(int)((k06 >> 48) & 0xFFL)] + ^ T2[(int)((k05 >> 40) & 0xFFL)] + ^ T3[(int)((k04 >> 32) & 0xFFL)] + ^ T4[(int)((k03 >> 24) & 0xFFL)] + ^ T5[(int)((k02 >> 16) & 0xFFL)] + ^ T6[(int)((k01 >> 8) & 0xFFL)] + ^ T7[(int)( k00 & 0xFFL)]; k00 = Kr0; k01 = Kr1; @@ -466,54 +489,70 @@ public final class Whirlpool extends BaseHash k07 = Kr7; // 2. incrementally compute the cipher output - w0 = T0[(int) ((nn0 >> 56) & 0xFFL)] ^ T1[(int) ((nn7 >> 48) & 0xFFL)] - ^ T2[(int) ((nn6 >> 40) & 0xFFL)] - ^ T3[(int) ((nn5 >> 32) & 0xFFL)] - ^ T4[(int) ((nn4 >> 24) & 0xFFL)] - ^ T5[(int) ((nn3 >> 16) & 0xFFL)] ^ T6[(int) ((nn2 >> 8) & 0xFFL)] - ^ T7[(int) (nn1 & 0xFFL)] ^ Kr0; - w1 = T0[(int) ((nn1 >> 56) & 0xFFL)] ^ T1[(int) ((nn0 >> 48) & 0xFFL)] - ^ T2[(int) ((nn7 >> 40) & 0xFFL)] - ^ T3[(int) ((nn6 >> 32) & 0xFFL)] - ^ T4[(int) ((nn5 >> 24) & 0xFFL)] - ^ T5[(int) ((nn4 >> 16) & 0xFFL)] ^ T6[(int) ((nn3 >> 8) & 0xFFL)] - ^ T7[(int) (nn2 & 0xFFL)] ^ Kr1; - w2 = T0[(int) ((nn2 >> 56) & 0xFFL)] ^ T1[(int) ((nn1 >> 48) & 0xFFL)] - ^ T2[(int) ((nn0 >> 40) & 0xFFL)] - ^ T3[(int) ((nn7 >> 32) & 0xFFL)] - ^ T4[(int) ((nn6 >> 24) & 0xFFL)] - ^ T5[(int) ((nn5 >> 16) & 0xFFL)] ^ T6[(int) ((nn4 >> 8) & 0xFFL)] - ^ T7[(int) (nn3 & 0xFFL)] ^ Kr2; - w3 = T0[(int) ((nn3 >> 56) & 0xFFL)] ^ T1[(int) ((nn2 >> 48) & 0xFFL)] - ^ T2[(int) ((nn1 >> 40) & 0xFFL)] - ^ T3[(int) ((nn0 >> 32) & 0xFFL)] - ^ T4[(int) ((nn7 >> 24) & 0xFFL)] - ^ T5[(int) ((nn6 >> 16) & 0xFFL)] ^ T6[(int) ((nn5 >> 8) & 0xFFL)] - ^ T7[(int) (nn4 & 0xFFL)] ^ Kr3; - w4 = T0[(int) ((nn4 >> 56) & 0xFFL)] ^ T1[(int) ((nn3 >> 48) & 0xFFL)] - ^ T2[(int) ((nn2 >> 40) & 0xFFL)] - ^ T3[(int) ((nn1 >> 32) & 0xFFL)] - ^ T4[(int) ((nn0 >> 24) & 0xFFL)] - ^ T5[(int) ((nn7 >> 16) & 0xFFL)] ^ T6[(int) ((nn6 >> 8) & 0xFFL)] - ^ T7[(int) (nn5 & 0xFFL)] ^ Kr4; - w5 = T0[(int) ((nn5 >> 56) & 0xFFL)] ^ T1[(int) ((nn4 >> 48) & 0xFFL)] - ^ T2[(int) ((nn3 >> 40) & 0xFFL)] - ^ T3[(int) ((nn2 >> 32) & 0xFFL)] - ^ T4[(int) ((nn1 >> 24) & 0xFFL)] - ^ T5[(int) ((nn0 >> 16) & 0xFFL)] ^ T6[(int) ((nn7 >> 8) & 0xFFL)] - ^ T7[(int) (nn6 & 0xFFL)] ^ Kr5; - w6 = T0[(int) ((nn6 >> 56) & 0xFFL)] ^ T1[(int) ((nn5 >> 48) & 0xFFL)] - ^ T2[(int) ((nn4 >> 40) & 0xFFL)] - ^ T3[(int) ((nn3 >> 32) & 0xFFL)] - ^ T4[(int) ((nn2 >> 24) & 0xFFL)] - ^ T5[(int) ((nn1 >> 16) & 0xFFL)] ^ T6[(int) ((nn0 >> 8) & 0xFFL)] - ^ T7[(int) (nn7 & 0xFFL)] ^ Kr6; - w7 = T0[(int) ((nn7 >> 56) & 0xFFL)] ^ T1[(int) ((nn6 >> 48) & 0xFFL)] - ^ T2[(int) ((nn5 >> 40) & 0xFFL)] - ^ T3[(int) ((nn4 >> 32) & 0xFFL)] - ^ T4[(int) ((nn3 >> 24) & 0xFFL)] - ^ T5[(int) ((nn2 >> 16) & 0xFFL)] ^ T6[(int) ((nn1 >> 8) & 0xFFL)] - ^ T7[(int) (nn0 & 0xFFL)] ^ Kr7; + w0 = T0[(int)((nn0 >> 56) & 0xFFL)] + ^ T1[(int)((nn7 >> 48) & 0xFFL)] + ^ T2[(int)((nn6 >> 40) & 0xFFL)] + ^ T3[(int)((nn5 >> 32) & 0xFFL)] + ^ T4[(int)((nn4 >> 24) & 0xFFL)] + ^ T5[(int)((nn3 >> 16) & 0xFFL)] + ^ T6[(int)((nn2 >> 8) & 0xFFL)] + ^ T7[(int)( nn1 & 0xFFL)] ^ Kr0; + w1 = T0[(int)((nn1 >> 56) & 0xFFL)] + ^ T1[(int)((nn0 >> 48) & 0xFFL)] + ^ T2[(int)((nn7 >> 40) & 0xFFL)] + ^ T3[(int)((nn6 >> 32) & 0xFFL)] + ^ T4[(int)((nn5 >> 24) & 0xFFL)] + ^ T5[(int)((nn4 >> 16) & 0xFFL)] + ^ T6[(int)((nn3 >> 8) & 0xFFL)] + ^ T7[(int)( nn2 & 0xFFL)] ^ Kr1; + w2 = T0[(int)((nn2 >> 56) & 0xFFL)] + ^ T1[(int)((nn1 >> 48) & 0xFFL)] + ^ T2[(int)((nn0 >> 40) & 0xFFL)] + ^ T3[(int)((nn7 >> 32) & 0xFFL)] + ^ T4[(int)((nn6 >> 24) & 0xFFL)] + ^ T5[(int)((nn5 >> 16) & 0xFFL)] + ^ T6[(int)((nn4 >> 8) & 0xFFL)] + ^ T7[(int)( nn3 & 0xFFL)] ^ Kr2; + w3 = T0[(int)((nn3 >> 56) & 0xFFL)] + ^ T1[(int)((nn2 >> 48) & 0xFFL)] + ^ T2[(int)((nn1 >> 40) & 0xFFL)] + ^ T3[(int)((nn0 >> 32) & 0xFFL)] + ^ T4[(int)((nn7 >> 24) & 0xFFL)] + ^ T5[(int)((nn6 >> 16) & 0xFFL)] + ^ T6[(int)((nn5 >> 8) & 0xFFL)] + ^ T7[(int)( nn4 & 0xFFL)] ^ Kr3; + w4 = T0[(int)((nn4 >> 56) & 0xFFL)] + ^ T1[(int)((nn3 >> 48) & 0xFFL)] + ^ T2[(int)((nn2 >> 40) & 0xFFL)] + ^ T3[(int)((nn1 >> 32) & 0xFFL)] + ^ T4[(int)((nn0 >> 24) & 0xFFL)] + ^ T5[(int)((nn7 >> 16) & 0xFFL)] + ^ T6[(int)((nn6 >> 8) & 0xFFL)] + ^ T7[(int)( nn5 & 0xFFL)] ^ Kr4; + w5 = T0[(int)((nn5 >> 56) & 0xFFL)] + ^ T1[(int)((nn4 >> 48) & 0xFFL)] + ^ T2[(int)((nn3 >> 40) & 0xFFL)] + ^ T3[(int)((nn2 >> 32) & 0xFFL)] + ^ T4[(int)((nn1 >> 24) & 0xFFL)] + ^ T5[(int)((nn0 >> 16) & 0xFFL)] + ^ T6[(int)((nn7 >> 8) & 0xFFL)] + ^ T7[(int)( nn6 & 0xFFL)] ^ Kr5; + w6 = T0[(int)((nn6 >> 56) & 0xFFL)] + ^ T1[(int)((nn5 >> 48) & 0xFFL)] + ^ T2[(int)((nn4 >> 40) & 0xFFL)] + ^ T3[(int)((nn3 >> 32) & 0xFFL)] + ^ T4[(int)((nn2 >> 24) & 0xFFL)] + ^ T5[(int)((nn1 >> 16) & 0xFFL)] + ^ T6[(int)((nn0 >> 8) & 0xFFL)] + ^ T7[(int)( nn7 & 0xFFL)] ^ Kr6; + w7 = T0[(int)((nn7 >> 56) & 0xFFL)] + ^ T1[(int)((nn6 >> 48) & 0xFFL)] + ^ T2[(int)((nn5 >> 40) & 0xFFL)] + ^ T3[(int)((nn4 >> 32) & 0xFFL)] + ^ T4[(int)((nn3 >> 24) & 0xFFL)] + ^ T5[(int)((nn2 >> 16) & 0xFFL)] + ^ T6[(int)((nn1 >> 8) & 0xFFL)] + ^ T7[(int)( nn0 & 0xFFL)] ^ Kr7; nn0 = w0; nn1 = w1; @@ -547,7 +586,7 @@ public final class Whirlpool extends BaseHash // are 33 (1 for the 1-bit followed by the 0-bits and the encoding of // the count framed in a 256-bit block). our formula is then: // count + 33 + padding = 0 (mod BLOCK_SIZE) - int n = (int) ((count + 33) % BLOCK_SIZE); + int n = (int)((count + 33) % BLOCK_SIZE); int padding = n == 0 ? 33 : BLOCK_SIZE - n + 33; byte[] result = new byte[padding]; @@ -558,14 +597,14 @@ public final class Whirlpool extends BaseHash // save (right justified) the number of bits hashed long bits = count * 8; int i = padding - 8; - result[i++] = (byte) (bits >>> 56); - result[i++] = (byte) (bits >>> 48); - result[i++] = (byte) (bits >>> 40); - result[i++] = (byte) (bits >>> 32); - result[i++] = (byte) (bits >>> 24); - result[i++] = (byte) (bits >>> 16); - result[i++] = (byte) (bits >>> 8); - result[i] = (byte) bits; + result[i++] = (byte)(bits >>> 56); + result[i++] = (byte)(bits >>> 48); + result[i++] = (byte)(bits >>> 40); + result[i++] = (byte)(bits >>> 32); + result[i++] = (byte)(bits >>> 24); + result[i++] = (byte)(bits >>> 16); + result[i++] = (byte)(bits >>> 8); + result[i ] = (byte) bits; return result; } @@ -573,38 +612,24 @@ public final class Whirlpool extends BaseHash protected byte[] getResult() { // apply inverse mu to the context - byte[] result = new byte[] { (byte) (H0 >>> 56), (byte) (H0 >>> 48), - (byte) (H0 >>> 40), (byte) (H0 >>> 32), - (byte) (H0 >>> 24), (byte) (H0 >>> 16), - (byte) (H0 >>> 8), (byte) H0, - (byte) (H1 >>> 56), (byte) (H1 >>> 48), - (byte) (H1 >>> 40), (byte) (H1 >>> 32), - (byte) (H1 >>> 24), (byte) (H1 >>> 16), - (byte) (H1 >>> 8), (byte) H1, - (byte) (H2 >>> 56), (byte) (H2 >>> 48), - (byte) (H2 >>> 40), (byte) (H2 >>> 32), - (byte) (H2 >>> 24), (byte) (H2 >>> 16), - (byte) (H2 >>> 8), (byte) H2, - (byte) (H3 >>> 56), (byte) (H3 >>> 48), - (byte) (H3 >>> 40), (byte) (H3 >>> 32), - (byte) (H3 >>> 24), (byte) (H3 >>> 16), - (byte) (H3 >>> 8), (byte) H3, - (byte) (H4 >>> 56), (byte) (H4 >>> 48), - (byte) (H4 >>> 40), (byte) (H4 >>> 32), - (byte) (H4 >>> 24), (byte) (H4 >>> 16), - (byte) (H4 >>> 8), (byte) H4, - (byte) (H5 >>> 56), (byte) (H5 >>> 48), - (byte) (H5 >>> 40), (byte) (H5 >>> 32), - (byte) (H5 >>> 24), (byte) (H5 >>> 16), - (byte) (H5 >>> 8), (byte) H5, - (byte) (H6 >>> 56), (byte) (H6 >>> 48), - (byte) (H6 >>> 40), (byte) (H6 >>> 32), - (byte) (H6 >>> 24), (byte) (H6 >>> 16), - (byte) (H6 >>> 8), (byte) H6, - (byte) (H7 >>> 56), (byte) (H7 >>> 48), - (byte) (H7 >>> 40), (byte) (H7 >>> 32), - (byte) (H7 >>> 24), (byte) (H7 >>> 16), - (byte) (H7 >>> 8), (byte) H7 }; + byte[] result = new byte[] { + (byte)(H0 >>> 56), (byte)(H0 >>> 48), (byte)(H0 >>> 40), (byte)(H0 >>> 32), + (byte)(H0 >>> 24), (byte)(H0 >>> 16), (byte)(H0 >>> 8), (byte) H0, + (byte)(H1 >>> 56), (byte)(H1 >>> 48), (byte)(H1 >>> 40), (byte)(H1 >>> 32), + (byte)(H1 >>> 24), (byte)(H1 >>> 16), (byte)(H1 >>> 8), (byte) H1, + (byte)(H2 >>> 56), (byte)(H2 >>> 48), (byte)(H2 >>> 40), (byte)(H2 >>> 32), + (byte)(H2 >>> 24), (byte)(H2 >>> 16), (byte)(H2 >>> 8), (byte) H2, + (byte)(H3 >>> 56), (byte)(H3 >>> 48), (byte)(H3 >>> 40), (byte)(H3 >>> 32), + (byte)(H3 >>> 24), (byte)(H3 >>> 16), (byte)(H3 >>> 8), (byte) H3, + (byte)(H4 >>> 56), (byte)(H4 >>> 48), (byte)(H4 >>> 40), (byte)(H4 >>> 32), + (byte)(H4 >>> 24), (byte)(H4 >>> 16), (byte)(H4 >>> 8), (byte) H4, + (byte)(H5 >>> 56), (byte)(H5 >>> 48), (byte)(H5 >>> 40), (byte)(H5 >>> 32), + (byte)(H5 >>> 24), (byte)(H5 >>> 16), (byte)(H5 >>> 8), (byte) H5, + (byte)(H6 >>> 56), (byte)(H6 >>> 48), (byte)(H6 >>> 40), (byte)(H6 >>> 32), + (byte)(H6 >>> 24), (byte)(H6 >>> 16), (byte)(H6 >>> 8), (byte) H6, + (byte)(H7 >>> 56), (byte)(H7 >>> 48), (byte)(H7 >>> 40), (byte)(H7 >>> 32), + (byte)(H7 >>> 24), (byte)(H7 >>> 16), (byte)(H7 >>> 8), (byte) H7 + }; return result; } @@ -617,10 +642,8 @@ public final class Whirlpool extends BaseHash public boolean selfTest() { if (valid == null) - { - valid = new Boolean( - DIGEST0.equals(Util.toString(new Whirlpool().digest()))); - } + valid = Boolean.valueOf(DIGEST0.equals(Util.toString(new Whirlpool().digest()))); + return valid.booleanValue(); } } diff --git a/libjava/classpath/gnu/java/security/jce/hash/HavalSpi.java b/libjava/classpath/gnu/java/security/jce/hash/HavalSpi.java index e127779..05595ed 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/HavalSpi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/HavalSpi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * The implementation of the <code>HAVAL</code> <i>Service Provider Interface</i> * (<b>SPI</b>) Adapter.<p> - * - * @version Revision: $ */ public class HavalSpi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/MD2Spi.java b/libjava/classpath/gnu/java/security/jce/hash/MD2Spi.java index 5b6b0e1..001cbaf 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/MD2Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/MD2Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * <p>The implementation of the MD2 <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.</p> - * - * @version $Revision: 1.1 $ */ public class MD2Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/MD4Spi.java b/libjava/classpath/gnu/java/security/jce/hash/MD4Spi.java index 8be4499..41fef87 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/MD4Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/MD4Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * <p>The implementation of the MD4 <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.</p> - * - * @version $Revision: 1.1 $ */ public class MD4Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/MD5Spi.java b/libjava/classpath/gnu/java/security/jce/hash/MD5Spi.java index 92fb6ab..aa1a884 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/MD5Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/MD5Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * The implementation of the MD5 <i>Service Provider Interface</i> (<b>SPI</b>) * adapter.<p> - * - * @version $Revision: 1.1 $ */ public class MD5Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/MessageDigestAdapter.java b/libjava/classpath/gnu/java/security/jce/hash/MessageDigestAdapter.java index 9b8a73d..e30beca 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/MessageDigestAdapter.java +++ b/libjava/classpath/gnu/java/security/jce/hash/MessageDigestAdapter.java @@ -59,8 +59,6 @@ import java.security.MessageDigestSpi; * * All the implementations which subclass this object, and which are serviced by * the GNU Crypto provider implement the {@link java.lang.Cloneable} interface.<p> - * - * @version $Revision: 1.1 $ */ class MessageDigestAdapter extends MessageDigestSpi implements Cloneable { diff --git a/libjava/classpath/gnu/java/security/jce/hash/RipeMD128Spi.java b/libjava/classpath/gnu/java/security/jce/hash/RipeMD128Spi.java index b8e90d4..404214d 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/RipeMD128Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/RipeMD128Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * The implementation of the RIPEMD-128 <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.<p> - * - * @version $Revision: 1.1 $ */ public class RipeMD128Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/RipeMD160Spi.java b/libjava/classpath/gnu/java/security/jce/hash/RipeMD160Spi.java index 49615e2..841f46b 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/RipeMD160Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/RipeMD160Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * The implementation of the RIPEMD-160 <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.<p> - * - * @version $Revision: 1.1 $ */ public class RipeMD160Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/Sha160Spi.java b/libjava/classpath/gnu/java/security/jce/hash/Sha160Spi.java index a9b7263..4198843 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/Sha160Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/Sha160Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * The implementation of the SHA-1 (160-bit) <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.<p> - * - * @version $Revision: 1.1 $ */ public class Sha160Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/Sha256Spi.java b/libjava/classpath/gnu/java/security/jce/hash/Sha256Spi.java index 9eeaebd..f07e189 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/Sha256Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/Sha256Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * <p>The implementation of the SHA-2-1 (256-bit) <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.</p> - * - * @version $Revision: 1.1 $ */ public class Sha256Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/Sha384Spi.java b/libjava/classpath/gnu/java/security/jce/hash/Sha384Spi.java index 96e1e6e..fc17077 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/Sha384Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/Sha384Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * <p>The implementation of the SHA-2-2 (384-bit) <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.</p> - * - * @version $Revision: 1.1 $ */ public class Sha384Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/Sha512Spi.java b/libjava/classpath/gnu/java/security/jce/hash/Sha512Spi.java index 75c6170..0b4c3d6 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/Sha512Spi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/Sha512Spi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * <p>The implementation of the SHA-2-3 (512-bit) <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.</p> - * - * @version $Revision: 1.1 $ */ public class Sha512Spi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/TigerSpi.java b/libjava/classpath/gnu/java/security/jce/hash/TigerSpi.java index b355d78..5994374 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/TigerSpi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/TigerSpi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * <p>The implementation of the Tiger <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.</p> - * - * @version $Revision: 1.1 $ */ public class TigerSpi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/hash/WhirlpoolSpi.java b/libjava/classpath/gnu/java/security/jce/hash/WhirlpoolSpi.java index e42e74d..d1864bc 100644 --- a/libjava/classpath/gnu/java/security/jce/hash/WhirlpoolSpi.java +++ b/libjava/classpath/gnu/java/security/jce/hash/WhirlpoolSpi.java @@ -43,8 +43,6 @@ import gnu.java.security.Registry; /** * The implementation of the Whirlpool <i>Service Provider Interface</i> * (<b>SPI</b>) adapter.<p> - * - * @version $Revision: 1.1 $ */ public class WhirlpoolSpi extends MessageDigestAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/prng/SecureRandomAdapter.java b/libjava/classpath/gnu/java/security/jce/prng/SecureRandomAdapter.java index e7cb720..70d30f1 100644 --- a/libjava/classpath/gnu/java/security/jce/prng/SecureRandomAdapter.java +++ b/libjava/classpath/gnu/java/security/jce/prng/SecureRandomAdapter.java @@ -42,7 +42,7 @@ import gnu.java.security.prng.LimitReachedException; import gnu.java.security.prng.MDGenerator; import java.security.SecureRandomSpi; -import java.util.HashMap; +import java.util.Collections; /** * <p>The implementation of a generic {@link java.security.SecureRandom} adapter @@ -80,6 +80,7 @@ abstract class SecureRandomAdapter extends SecureRandomSpi super(); this.mdName = mdName; + adaptee.init (Collections.singletonMap (MDGenerator.MD_NAME, mdName)); } // Class methods @@ -118,9 +119,6 @@ abstract class SecureRandomAdapter extends SecureRandomSpi public void engineSetSeed(byte[] seed) { - HashMap attributes = new HashMap(); - attributes.put(MDGenerator.MD_NAME, mdName); - attributes.put(MDGenerator.SEEED, seed); - adaptee.init(attributes); + adaptee.addRandomBytes (seed); } } diff --git a/libjava/classpath/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java b/libjava/classpath/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java index 97e9594..44503b2 100644 --- a/libjava/classpath/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java +++ b/libjava/classpath/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java @@ -57,8 +57,6 @@ import java.util.HashMap; * In case the client does not explicitly initialize the KeyPairGenerator (via * a call to an <code>initialize()</code> method), the GNU Crypto provider * uses a default <i>modulus</i> size (keysize) of 1024 bits.<p> - * - * @version $Revision: 1.3 $ */ public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter implements DSAKeyPairGenerator diff --git a/libjava/classpath/gnu/java/security/jce/sig/DSSRawSignatureSpi.java b/libjava/classpath/gnu/java/security/jce/sig/DSSRawSignatureSpi.java index 16e4ddd..a63c51a 100644 --- a/libjava/classpath/gnu/java/security/jce/sig/DSSRawSignatureSpi.java +++ b/libjava/classpath/gnu/java/security/jce/sig/DSSRawSignatureSpi.java @@ -45,8 +45,6 @@ import gnu.java.security.sig.dss.DSSSignatureRawCodec; * The implementation of <i>Service Provider Interface</i> (<b>SPI</b>) adapter * for the DSS (Digital Signature Standard) signature scheme, encoded and/or * decoded in RAW format.<p> - * - * @version $Revision: 1.1 $ */ public class DSSRawSignatureSpi extends SignatureAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java b/libjava/classpath/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java index 6026829..edf19f6 100644 --- a/libjava/classpath/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java +++ b/libjava/classpath/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java @@ -63,8 +63,6 @@ import java.security.spec.AlgorithmParameterSpec; * supplies (and document) default values to be used. For example, the GNU * Crypto provider uses a default <i>modulus</i> size (keysize) of 1024 bits for * the DSS (Digital Signature Standard) a.k.a <i>DSA</i>.<p> - * - * @version $Revision: 1.3 $ */ public abstract class KeyPairGeneratorAdapter extends KeyPairGenerator { diff --git a/libjava/classpath/gnu/java/security/jce/sig/RSAKeyFactory.java b/libjava/classpath/gnu/java/security/jce/sig/RSAKeyFactory.java index fecf54c..674e2af 100644 --- a/libjava/classpath/gnu/java/security/jce/sig/RSAKeyFactory.java +++ b/libjava/classpath/gnu/java/security/jce/sig/RSAKeyFactory.java @@ -84,7 +84,7 @@ public class RSAKeyFactory PublicKey result; try { - result = new RSAKeyPairX509Codec().decodePublicKey(encoded); + return new RSAKeyPairX509Codec().decodePublicKey(encoded); } catch (RuntimeException x) { @@ -131,7 +131,7 @@ public class RSAKeyFactory PrivateKey result; try { - result = new RSAKeyPairPKCS8Codec().decodePrivateKey(encoded); + return new RSAKeyPairPKCS8Codec().decodePrivateKey(encoded); } catch (RuntimeException x) { diff --git a/libjava/classpath/gnu/java/security/jce/sig/RSAPSSRawSignatureSpi.java b/libjava/classpath/gnu/java/security/jce/sig/RSAPSSRawSignatureSpi.java index e44b8ad..f3548d8 100644 --- a/libjava/classpath/gnu/java/security/jce/sig/RSAPSSRawSignatureSpi.java +++ b/libjava/classpath/gnu/java/security/jce/sig/RSAPSSRawSignatureSpi.java @@ -44,8 +44,6 @@ import gnu.java.security.sig.rsa.RSAPSSSignatureRawCodec; /** * The implementation of <i>Service Provider Interface</i> (<b>SPI</b>) adapter * for the RSA-PSS signature scheme, encoded and/or decoded in RAW format.<p> - * - * @version $Revision: 1.1 $ */ public class RSAPSSRawSignatureSpi extends SignatureAdapter { diff --git a/libjava/classpath/gnu/java/security/jce/sig/SignatureAdapter.java b/libjava/classpath/gnu/java/security/jce/sig/SignatureAdapter.java index 6cb7c7c..4dcbe78 100644 --- a/libjava/classpath/gnu/java/security/jce/sig/SignatureAdapter.java +++ b/libjava/classpath/gnu/java/security/jce/sig/SignatureAdapter.java @@ -69,8 +69,6 @@ import java.util.logging.Logger; * * All the implementations which subclass this object, and which are serviced by * the GNU Crypto provider implement the {@link java.lang.Cloneable} interface.<p> - * - * @version $Revision: 1.2 $ */ class SignatureAdapter extends SignatureSpi implements Cloneable { diff --git a/libjava/classpath/gnu/java/security/key/IKeyPairCodec.java b/libjava/classpath/gnu/java/security/key/IKeyPairCodec.java index c64f928..965a317 100644 --- a/libjava/classpath/gnu/java/security/key/IKeyPairCodec.java +++ b/libjava/classpath/gnu/java/security/key/IKeyPairCodec.java @@ -48,8 +48,6 @@ import java.security.PublicKey; * cryptographic asymmetric keypairs. Codecs are useful for (a) externalising * public and private keys for storage and on-the-wire transmission, as well as * (b) re-creating their internal Java representation from external sources.</p> - * - * @version $Revision: 1.2 $ */ public interface IKeyPairCodec { diff --git a/libjava/classpath/gnu/java/security/key/IKeyPairGenerator.java b/libjava/classpath/gnu/java/security/key/IKeyPairGenerator.java index 219863d..2e0b299 100644 --- a/libjava/classpath/gnu/java/security/key/IKeyPairGenerator.java +++ b/libjava/classpath/gnu/java/security/key/IKeyPairGenerator.java @@ -43,8 +43,6 @@ import java.util.Map; /** * The visible methods of every asymmetric keypair generator.<p> - * - * @version $Revision: 1.1 $ */ public interface IKeyPairGenerator { diff --git a/libjava/classpath/gnu/java/security/key/KeyPairGeneratorFactory.java b/libjava/classpath/gnu/java/security/key/KeyPairGeneratorFactory.java index edcc186..8c2f348 100644 --- a/libjava/classpath/gnu/java/security/key/KeyPairGeneratorFactory.java +++ b/libjava/classpath/gnu/java/security/key/KeyPairGeneratorFactory.java @@ -49,8 +49,6 @@ import java.util.Set; /** * <p>A Factory to instantiate asymmetric keypair generators.</p> - * - * @version $Revision: 1.1 $ */ public class KeyPairGeneratorFactory { @@ -87,7 +85,7 @@ public class KeyPairGeneratorFactory name = name.trim(); IKeyPairGenerator result = null; if (name.equalsIgnoreCase(Registry.DSA_KPG) - || name.equals(Registry.DSS_KPG)) + || name.equalsIgnoreCase(Registry.DSS_KPG)) { result = new DSSKeyPairGenerator(); } @@ -118,6 +116,7 @@ public class KeyPairGeneratorFactory { HashSet hs = new HashSet(); hs.add(Registry.DSS_KPG); + hs.add(Registry.DSA_KPG); hs.add(Registry.RSA_KPG); hs.add(Registry.DH_KPG); hs.add(Registry.SRP_KPG); @@ -135,11 +134,9 @@ public class KeyPairGeneratorFactory } catch (Exception x) { - IllegalArgumentException iae = - new IllegalArgumentException ("strong crypto key pair generator not available: " - + clazz); - iae.initCause (x); - throw iae; + throw new IllegalArgumentException( + "strong crypto key pair generator not available: " + clazz, + x); } } diff --git a/libjava/classpath/gnu/java/security/key/dss/DSSKey.java b/libjava/classpath/gnu/java/security/key/dss/DSSKey.java index 428cab1..40aaea8 100644 --- a/libjava/classpath/gnu/java/security/key/dss/DSSKey.java +++ b/libjava/classpath/gnu/java/security/key/dss/DSSKey.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.security.key.dss; +import gnu.classpath.SystemProperties; import gnu.java.security.Registry; import gnu.java.security.util.FormatUtil; @@ -59,7 +60,6 @@ import java.security.spec.DSAParameterSpec; * the relevant <code>getEncoded()</code> methods of each of the private and * public keys.</p> * - * @version $Revision: 1.4 $ * @see DSSPrivateKey#getEncoded * @see DSSPublicKey#getEncoded */ @@ -95,6 +95,9 @@ public abstract class DSSKey implements Key, DSAKey */ protected final int defaultFormat; + /** String representation of this key. Cached for speed. */ + private transient String str; + // Constructor(s) // ------------------------------------------------------------------------- @@ -176,6 +179,22 @@ public abstract class DSSKey implements Key, DSAKey && g.equals(that.getParams().getG()); } + public String toString() + { + if (str == null) + { + String ls = SystemProperties.getProperty("line.separator"); + str = new StringBuilder().append(ls) + .append("defaultFormat=").append(defaultFormat).append(",").append(ls) + .append("p=0x").append(p.toString(16)).append(",").append(ls) + .append("q=0x").append(q.toString(16)).append(",").append(ls) + .append("g=0x").append(g.toString(16)) + .toString(); + } + + return str; + } + // abstract methods to be implemented by subclasses ------------------------ public abstract byte[] getEncoded(int format); diff --git a/libjava/classpath/gnu/java/security/key/dss/DSSKeyPairPKCS8Codec.java b/libjava/classpath/gnu/java/security/key/dss/DSSKeyPairPKCS8Codec.java index 30e30bd..3a115b9 100644 --- a/libjava/classpath/gnu/java/security/key/dss/DSSKeyPairPKCS8Codec.java +++ b/libjava/classpath/gnu/java/security/key/dss/DSSKeyPairPKCS8Codec.java @@ -55,6 +55,7 @@ import java.security.InvalidParameterException; import java.security.PrivateKey; import java.security.PublicKey; import java.util.ArrayList; +import java.util.logging.Logger; /** * An implementation of an {@link IKeyPairCodec} that knows how to encode / @@ -65,6 +66,7 @@ import java.util.ArrayList; public class DSSKeyPairPKCS8Codec implements IKeyPairCodec { + private static final Logger log = Logger.getLogger(DSSKeyPairPKCS8Codec.class.getName()); private static final OID DSA_ALG_OID = new OID(Registry.DSA_OID_STRING); // implicit 0-arguments constructor @@ -137,7 +139,9 @@ public class DSSKeyPairPKCS8Codec DERValue derAlgorithmID = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, algorithmID); - DERValue derPrivateKey = new DERValue(DER.OCTET_STRING, Util.trim(x)); + // The OCTET STRING is the DER encoding of an INTEGER. + DERValue derX = new DERValue(DER.INTEGER, x); + DERValue derPrivateKey = new DERValue(DER.OCTET_STRING, derX.getEncoded()); ArrayList pki = new ArrayList(3); pki.add(derVersion); @@ -180,6 +184,8 @@ public class DSSKeyPairPKCS8Codec */ public PrivateKey decodePrivateKey(byte[] input) { + log.entering("DSSKeyPairPKCS8Codec", "decodePrivateKey"); + if (input == null) throw new InvalidParameterException("Input bytes MUST NOT be null"); @@ -220,8 +226,13 @@ public class DSSKeyPairPKCS8Codec g = (BigInteger) val.getValue(); val = der.read(); + log.finest("val = " + val); byte[] xBytes = (byte[]) val.getValue(); - x = new BigInteger(1, xBytes); + log.finest(Util.dumpString(xBytes, "xBytes: ")); + DERReader der2 = new DERReader(xBytes); + val = der2.read(); + DerUtil.checkIsBigInteger(val, "Wrong X field"); + x = (BigInteger) val.getValue(); } catch (IOException e) { @@ -230,6 +241,7 @@ public class DSSKeyPairPKCS8Codec throw y; } + log.exiting("DSSKeyPairPKCS8Codec", "decodePrivateKey"); return new DSSPrivateKey(Registry.PKCS8_ENCODING_ID, p, q, g, x); } } diff --git a/libjava/classpath/gnu/java/security/key/dss/DSSKeyPairRawCodec.java b/libjava/classpath/gnu/java/security/key/dss/DSSKeyPairRawCodec.java index 86e5b0b..7c5491d 100644 --- a/libjava/classpath/gnu/java/security/key/dss/DSSKeyPairRawCodec.java +++ b/libjava/classpath/gnu/java/security/key/dss/DSSKeyPairRawCodec.java @@ -49,8 +49,6 @@ import java.security.PublicKey; /** * <p>An object that implements the {@link IKeyPairCodec} operations for the * <i>Raw</i> format to use with DSS keypairs.</p> - * - * @version $Revision: 1.1 $ */ public class DSSKeyPairRawCodec implements IKeyPairCodec { diff --git a/libjava/classpath/gnu/java/security/key/dss/DSSPrivateKey.java b/libjava/classpath/gnu/java/security/key/dss/DSSPrivateKey.java index c81eb93..fe59cb6 100644 --- a/libjava/classpath/gnu/java/security/key/dss/DSSPrivateKey.java +++ b/libjava/classpath/gnu/java/security/key/dss/DSSPrivateKey.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.security.key.dss; +import gnu.classpath.SystemProperties; import gnu.java.security.Registry; import gnu.java.security.key.IKeyPairCodec; @@ -48,21 +49,24 @@ import java.security.interfaces.DSAPrivateKey; /** * <p>An object that embodies a DSS (Digital Signature Standard) private key.</p> * - * @version $Revision: 1.2 $ * @see #getEncoded */ public class DSSPrivateKey extends DSSKey implements PrivateKey, DSAPrivateKey { - // Constants and variables // ------------------------------------------------------------------------- + private static final boolean DEBUG = false; + /** * <p>A randomly or pseudorandomly generated integer with <code>0 < x < * q</code>.</p> */ private final BigInteger x; + /** String representation of this key. Cached for speed. */ + private transient String str; + // Constructor(s) // ------------------------------------------------------------------------- @@ -198,4 +202,18 @@ public class DSSPrivateKey extends DSSKey implements PrivateKey, DSAPrivateKey DSAPrivateKey that = (DSAPrivateKey) obj; return super.equals(that) && x.equals(that.getX()); } + + public String toString() + { + if (str == null) + { + String ls = SystemProperties.getProperty("line.separator"); + str = new StringBuilder(this.getClass().getName()).append("(") + .append(super.toString()).append(",").append(ls) + .append("x=0x").append(DEBUG ? x.toString(16) : "**...*").append(ls) + .append(")").toString(); + } + + return str; + } } diff --git a/libjava/classpath/gnu/java/security/key/dss/DSSPublicKey.java b/libjava/classpath/gnu/java/security/key/dss/DSSPublicKey.java index 93bb640..5229213 100644 --- a/libjava/classpath/gnu/java/security/key/dss/DSSPublicKey.java +++ b/libjava/classpath/gnu/java/security/key/dss/DSSPublicKey.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.security.key.dss; +import gnu.classpath.SystemProperties; import gnu.java.security.Registry; import gnu.java.security.key.IKeyPairCodec; @@ -48,12 +49,10 @@ import java.security.interfaces.DSAPublicKey; /** * <p>An object that embodies a DSS (Digital Signature Standard) public key.</p> * - * @version $Revision: 1.2 $ * @see #getEncoded */ public class DSSPublicKey extends DSSKey implements PublicKey, DSAPublicKey { - // Constants and variables // ------------------------------------------------------------------------- @@ -63,6 +62,9 @@ public class DSSPublicKey extends DSSKey implements PublicKey, DSAPublicKey */ private final BigInteger y; + /** String representation of this key. Cached for speed. */ + private transient String str; + // Constructor(s) // ------------------------------------------------------------------------- @@ -198,4 +200,18 @@ public class DSSPublicKey extends DSSKey implements PublicKey, DSAPublicKey DSAPublicKey that = (DSAPublicKey) obj; return super.equals(that) && y.equals(that.getY()); } + + public String toString() + { + if (str == null) + { + String ls = SystemProperties.getProperty("line.separator"); + str = new StringBuilder(this.getClass().getName()).append("(") + .append(super.toString()).append(",").append(ls) + .append("y=0x").append(y.toString(16)).append(ls) + .append(")").toString(); + } + + return str; + } } diff --git a/libjava/classpath/gnu/java/security/key/dss/FIPS186.java b/libjava/classpath/gnu/java/security/key/dss/FIPS186.java index 74be626..5984bcc 100644 --- a/libjava/classpath/gnu/java/security/key/dss/FIPS186.java +++ b/libjava/classpath/gnu/java/security/key/dss/FIPS186.java @@ -53,8 +53,6 @@ import java.security.SecureRandom; * <a href="http://www.itl.nist.gov/fipspubs/fip186.htm">Digital Signature * Standard (DSS)</a>, Federal Information Processing Standards Publication 186. * National Institute of Standards and Technology. - * - * @version $Revision: 1.2 $ */ public class FIPS186 { diff --git a/libjava/classpath/gnu/java/security/key/rsa/GnuRSAKey.java b/libjava/classpath/gnu/java/security/key/rsa/GnuRSAKey.java index 72cd808..3009dd7 100644 --- a/libjava/classpath/gnu/java/security/key/rsa/GnuRSAKey.java +++ b/libjava/classpath/gnu/java/security/key/rsa/GnuRSAKey.java @@ -38,8 +38,8 @@ exception statement from your version. */ package gnu.java.security.key.rsa; +import gnu.classpath.SystemProperties; import gnu.java.security.Registry; -import gnu.java.security.key.IKeyPairCodec; import gnu.java.security.util.FormatUtil; import java.math.BigInteger; @@ -48,8 +48,6 @@ import java.security.interfaces.RSAKey; /** * <p>A base asbtract class for both public and private RSA keys.</p> - * - * @version $Revision: 1.3 $ */ public abstract class GnuRSAKey implements Key, RSAKey { @@ -69,6 +67,9 @@ public abstract class GnuRSAKey implements Key, RSAKey */ protected final int defaultFormat; + /** String representation of this key. Cached for speed. */ + private transient String str; + // Constructor(s) // ------------------------------------------------------------------------- @@ -113,7 +114,7 @@ public abstract class GnuRSAKey implements Key, RSAKey /** @deprecated see getEncoded(int). */ public byte[] getEncoded() { - return getEncoded(IKeyPairCodec.RAW_FORMAT); + return getEncoded(defaultFormat); } public String getFormat() @@ -175,6 +176,20 @@ public abstract class GnuRSAKey implements Key, RSAKey return n.equals(that.getModulus()); } + public String toString() + { + if (str == null) + { + String ls = SystemProperties.getProperty("line.separator"); + str = new StringBuilder().append(ls) + .append("defaultFormat=").append(defaultFormat).append(",").append(ls) + .append("n=0x").append(n.toString(16)).append(",").append(ls) + .append("e=0x").append(e.toString(16)) + .toString(); + } + return str; + } + // abstract methods to be implemented by subclasses ------------------------ public abstract byte[] getEncoded(int format); diff --git a/libjava/classpath/gnu/java/security/key/rsa/GnuRSAPrivateKey.java b/libjava/classpath/gnu/java/security/key/rsa/GnuRSAPrivateKey.java index f8acaa5..9205344 100644 --- a/libjava/classpath/gnu/java/security/key/rsa/GnuRSAPrivateKey.java +++ b/libjava/classpath/gnu/java/security/key/rsa/GnuRSAPrivateKey.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.security.key.rsa; +import gnu.classpath.SystemProperties; import gnu.java.security.Registry; import gnu.java.security.key.IKeyPairCodec; @@ -56,16 +57,15 @@ import java.security.interfaces.RSAPrivateKey; * Primitive specification and supporting documentation.<br> * Jakob Jonsson and Burt Kaliski.</li> * </ol> - * - * @version $Revision: 1.3 $ */ public class GnuRSAPrivateKey extends GnuRSAKey implements PrivateKey, RSAPrivateCrtKey { - // Constants and variables // ------------------------------------------------------------------------- + private static final boolean DEBUG = false; + /** The first prime divisor of the modulus. */ private final BigInteger p; @@ -86,6 +86,9 @@ public class GnuRSAPrivateKey extends GnuRSAKey implements PrivateKey, /** The CRT (Chinese Remainder Theorem) coefficient. */ private final BigInteger qInv; + /** String representation of this key. Cached for speed. */ + private transient String str; + // Constructor(s) // ------------------------------------------------------------------------- @@ -296,4 +299,22 @@ public class GnuRSAPrivateKey extends GnuRSAKey implements PrivateKey, } return false; } + + public String toString() + { + if (str == null) + { + String ls = SystemProperties.getProperty("line.separator"); + str = new StringBuilder(this.getClass().getName()).append("(") + .append(super.toString()).append(",").append(ls) + .append("d=0x").append(DEBUG ? d.toString(16) : "**...*").append(ls) + .append("p=0x").append(DEBUG ? p.toString(16) : "**...*").append(ls) + .append("q=0x").append(DEBUG ? q.toString(16) : "**...*").append(ls) + .append("dP=0x").append(DEBUG ? dP.toString(16) : "**...*").append(ls) + .append("dQ=0x").append(DEBUG ? dQ.toString(16) : "**...*").append(ls) + .append("qInv=0x").append(DEBUG ? qInv.toString(16) : "**...*").append(ls) + .append(")").toString(); + } + return str; + } } diff --git a/libjava/classpath/gnu/java/security/key/rsa/GnuRSAPublicKey.java b/libjava/classpath/gnu/java/security/key/rsa/GnuRSAPublicKey.java index f49027c..8badede 100644 --- a/libjava/classpath/gnu/java/security/key/rsa/GnuRSAPublicKey.java +++ b/libjava/classpath/gnu/java/security/key/rsa/GnuRSAPublicKey.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.security.key.rsa; +import gnu.classpath.SystemProperties; import gnu.java.security.Registry; import gnu.java.security.key.IKeyPairCodec; @@ -55,16 +56,16 @@ import java.security.interfaces.RSAPublicKey; * Primitive specification and supporting documentation.<br> * Jakob Jonsson and Burt Kaliski.</li> * </ol> - * - * @version $Revision: 1.2 $ */ public class GnuRSAPublicKey extends GnuRSAKey implements PublicKey, RSAPublicKey { - // Constants and variables // ------------------------------------------------------------------------- + /** String representation of this key. Cached for speed. */ + private transient String str; + // Constructor(s) // ------------------------------------------------------------------------- @@ -182,4 +183,16 @@ public class GnuRSAPublicKey extends GnuRSAKey implements PublicKey, return super.equals(that) && getPublicExponent().equals(that.getPublicExponent()); } + + public String toString() + { + if (str == null) + { + String ls = SystemProperties.getProperty("line.separator"); + str = new StringBuilder(this.getClass().getName()).append("(") + .append(super.toString()).append(",").append(ls) + .append(")").toString(); + } + return str; + } } diff --git a/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairGenerator.java b/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairGenerator.java index 9c7338f..3906338 100644 --- a/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairGenerator.java +++ b/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairGenerator.java @@ -50,6 +50,7 @@ import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.RSAKeyGenParameterSpec; import java.util.Map; +import java.util.logging.Logger; /** * <p>A key-pair generator for asymetric keys to use in conjunction with the RSA @@ -68,10 +69,11 @@ import java.util.Map; */ public class RSAKeyPairGenerator implements IKeyPairGenerator { - // Constants and variables // ------------------------------------------------------------------------- + private static final Logger log = Logger.getLogger(RSAKeyPairGenerator.class.getName()); + /** The BigInteger constant 1. */ private static final BigInteger ONE = BigInteger.ONE; @@ -150,6 +152,8 @@ public class RSAKeyPairGenerator implements IKeyPairGenerator */ public void setup(Map attributes) { + log.entering(this.getClass().getName(), "setup", attributes); + // do we have a SecureRandom, or should we use our own? rnd = (SecureRandom) attributes.get(SOURCE_OF_RANDOMNESS); @@ -177,6 +181,8 @@ public class RSAKeyPairGenerator implements IKeyPairGenerator Integer formatID = (Integer) attributes.get(PREFERRED_ENCODING_FORMAT); preferredFormat = formatID == null ? DEFAULT_ENCODING_FORMAT : formatID.intValue(); + + log.exiting(this.getClass().getName(), "setup"); } /** @@ -187,6 +193,8 @@ public class RSAKeyPairGenerator implements IKeyPairGenerator */ public KeyPair generate() { + log.entering(this.getClass().getName(), "generate"); + BigInteger p, q, n, d; // 1. Generate a prime p in the interval [2**(M-1), 2**M - 1], where @@ -234,7 +242,9 @@ public class RSAKeyPairGenerator implements IKeyPairGenerator PublicKey pubK = new GnuRSAPublicKey(preferredFormat, n, e); PrivateKey secK = new GnuRSAPrivateKey(preferredFormat, p, q, e, d); - return new KeyPair(pubK, secK); + KeyPair result = new KeyPair(pubK, secK); + log.exiting(this.getClass().getName(), "generate", result); + return result; } // helper methods ---------------------------------------------------------- diff --git a/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairPKCS8Codec.java b/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairPKCS8Codec.java index a7f65b6..0b98090 100644 --- a/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairPKCS8Codec.java +++ b/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairPKCS8Codec.java @@ -45,6 +45,7 @@ import java.security.InvalidParameterException; import java.security.PrivateKey; import java.security.PublicKey; import java.util.ArrayList; +import java.util.logging.Logger; import gnu.java.security.OID; import gnu.java.security.Registry; @@ -62,6 +63,7 @@ import gnu.java.security.util.DerUtil; public class RSAKeyPairPKCS8Codec implements IKeyPairCodec { + private static final Logger log = Logger.getLogger(RSAKeyPairPKCS8Codec.class.getName()); private static final OID RSA_ALG_OID = new OID(Registry.RSA_OID_STRING); // implicit 0-arguments constructor @@ -120,6 +122,8 @@ public class RSAKeyPairPKCS8Codec */ public byte[] encodePrivateKey(PrivateKey key) { + log.entering(this.getClass().getName(), "encodePrivateKey()", key); + if (! (key instanceof GnuRSAPrivateKey)) throw new InvalidParameterException("Wrong key type"); @@ -187,6 +191,7 @@ public class RSAKeyPairPKCS8Codec throw y; } + log.exiting(this.getClass().getName(), "encodePrivateKey()", result); return result; } @@ -208,6 +213,8 @@ public class RSAKeyPairPKCS8Codec */ public PrivateKey decodePrivateKey(byte[] input) { + log.entering(this.getClass().getName(), "decodePrivateKey()", input); + if (input == null) throw new InvalidParameterException("Input bytes MUST NOT be null"); @@ -278,7 +285,9 @@ public class RSAKeyPairPKCS8Codec throw y; } - return new GnuRSAPrivateKey(Registry.PKCS8_ENCODING_ID, n, e, d, p, q, - dP, dQ, qInv); + PrivateKey result = new GnuRSAPrivateKey(Registry.PKCS8_ENCODING_ID, n, e, + d, p, q, dP, dQ, qInv); + log.exiting(this.getClass().getName(), "decodePrivateKey()", result); + return result; } } diff --git a/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairX509Codec.java b/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairX509Codec.java index 1c36278..882d9c7 100644 --- a/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairX509Codec.java +++ b/libjava/classpath/gnu/java/security/key/rsa/RSAKeyPairX509Codec.java @@ -128,8 +128,9 @@ public class RSAKeyPairX509Codec DERValue derN = new DERValue(DER.INTEGER, n); DERValue derE = new DERValue(DER.INTEGER, e); - ArrayList algorithmID = new ArrayList(1); + ArrayList algorithmID = new ArrayList(2); algorithmID.add(derOID); + algorithmID.add(new DERValue(DER.NULL, null)); DERValue derAlgorithmID = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, algorithmID); diff --git a/libjava/classpath/gnu/java/security/pkcs/PKCS7Data.java b/libjava/classpath/gnu/java/security/pkcs/PKCS7Data.java new file mode 100644 index 0000000..3d3052b --- /dev/null +++ b/libjava/classpath/gnu/java/security/pkcs/PKCS7Data.java @@ -0,0 +1,69 @@ +/* PKCS7Data.java -- Reader/writer for PKCS#7 Data objects + 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.java.security.pkcs; + +import gnu.java.security.OID; + +/** + * A read/write helper class for PKCS#7 Data ASN.1 structures. + */ +public class PKCS7Data +{ + public static final OID PKCS7_DATA = new OID("1.2.840.113549.1.7.1"); + + private byte[] content; + + /** + * Constructs a new instance of <code>PKCS7Data</code> with the possibly + * null (implicetly referenced) content data. + * + * @param data the raw bytes of the data to use in a PKCS#7 framework. + */ + public PKCS7Data(byte[] data) + { + super(); + + this.content = data; + } + + public byte[] getEncoded() + { + return content; + } +} diff --git a/libjava/classpath/gnu/java/security/pkcs/PKCS7SignedData.java b/libjava/classpath/gnu/java/security/pkcs/PKCS7SignedData.java index ba5efc7..0781f4b 100644 --- a/libjava/classpath/gnu/java/security/pkcs/PKCS7SignedData.java +++ b/libjava/classpath/gnu/java/security/pkcs/PKCS7SignedData.java @@ -1,5 +1,5 @@ -/* PKCS7SignedData.java -- reader for PKCS#7 signedData objects - Copyright (C) 2004, 2005 Free Software Foundation, Inc. +/* PKCS7SignedData.java -- reader/writer for PKCS#7 signedData objects + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,19 +42,26 @@ import gnu.java.security.ber.BER; import gnu.java.security.ber.BEREncodingException; import gnu.java.security.ber.BERReader; import gnu.java.security.ber.BERValue; +import gnu.java.security.der.DER; import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; +import gnu.java.security.util.Util; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.math.BigInteger; import java.security.cert.CRL; import java.security.cert.CRLException; import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; +import java.security.cert.X509CRL; import java.util.ArrayList; import java.util.Collections; @@ -63,6 +70,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.logging.Logger; /** * The SignedData object in PKCS #7. This is a read-only implementation of @@ -72,8 +80,8 @@ import java.util.Set; */ public class PKCS7SignedData { + private static final Logger log = Logger.getLogger(PKCS7SignedData.class.getName()); - public static final OID PKCS7_DATA = new OID("1.2.840.113549.1.7.1"); public static final OID PKCS7_SIGNED_DATA = new OID("1.2.840.113549.1.7.2"); private BigInteger version; @@ -84,13 +92,6 @@ public class PKCS7SignedData private CRL[] crls; private Set signerInfos; - private static final boolean DEBUG = false; - private static void debug(String msg) - { - System.err.print("PKCS7SignedData >> "); - System.err.println(msg); - } - public PKCS7SignedData(InputStream in) throws CRLException, CertificateException, IOException { @@ -103,14 +104,12 @@ public class PKCS7SignedData * * <pre> * SignedData ::= SEQUENCE { - * version Version, - * digestAlgorithms DigestAlgorithmIdentifiers, - * contentInfo ContentInfo, - * certificates - * [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, - * crls - * [1] IMPLICIT CertificateRevocationLists OPTIONAL, - * signerInfos SignerInfos } + * version Version, -- always 1 for PKCS7 v1.5 + * digestAlgorithms DigestAlgorithmIdentifiers, + * contentInfo ContentInfo, + * certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos } * * Version ::= INTEGER * @@ -119,8 +118,8 @@ public class PKCS7SignedData * DigestAlgorithmIdentifier ::= AlgorithmIdentifier * * ContentInfo ::= SEQUENCE { - * contentType ContentType, - * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } * * ContentType ::= OBJECT IDENTIFIER * @@ -128,7 +127,7 @@ public class PKCS7SignedData * SET OF ExtendedCertificatesAndCertificate * * ExtendedCertificatesAndCertificate ::= CHOICE { - * certificate Certificate, -- from X.509 + * certificate Certificate, -- from X.509 * extendedCertificate [0] IMPLICIT ExtendedCertificate } * * CertificateRevocationLists ::= SET OF CertificateRevocationList @@ -137,15 +136,13 @@ public class PKCS7SignedData * SignerInfos ::= SET OF SignerInfo * * SignerInfo ::= SEQUENCE { - * version Version, - * issuerAndSerialNumber IssuerAndSerialNumber, - * digestAlgorithm DigestAlgorithmIdentifier, - * authenticatedAttributes - * [0] IMPLICIT Attributes OPTIONAL, - * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, - * encryptedDigest EncryptedDigest, - * unauthenticatedAttributes - * [1] IMPLICIT Attributes OPTIONAL } + * version Version, -- always 1 for PKCS7 v1.5 + * issuerAndSerialNumber IssuerAndSerialNumber, + * digestAlgorithm DigestAlgorithmIdentifier, + * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + * encryptedDigest EncryptedDigest, + * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL } * * EncryptedDigest ::= OCTET STRING * </pre> @@ -177,23 +174,21 @@ public class PKCS7SignedData if (!val.isConstructed()) throw new BEREncodingException("malformed SignedData"); - if (DEBUG) - debug("SignedData: " + val); + log.finest("SignedData: " + val); val = ber.read(); if (val.getTag() != BER.INTEGER) throw new BEREncodingException("expecting Version"); version = (BigInteger) val.getValue(); - if (DEBUG) - debug(" Version: " + version); + log.finest(" Version: " + version); digestAlgorithms = new HashSet(); val = ber.read(); if (!val.isConstructed()) throw new BEREncodingException("malformed DigestAlgorithmIdentifiers"); - if (DEBUG) - debug(" DigestAlgorithmIdentifiers: " + val); + + log.finest(" DigestAlgorithmIdentifiers: " + val); int count = 0; DERValue val2 = ber.read(); while (val2 != BER.END_OF_SEQUENCE && @@ -201,14 +196,14 @@ public class PKCS7SignedData { if (!val2.isConstructed()) throw new BEREncodingException("malformed AlgorithmIdentifier"); - if (DEBUG) - debug(" AlgorithmIdentifier: " + val2); + + log.finest(" AlgorithmIdentifier: " + val2); count += val2.getEncodedLength(); val2 = ber.read(); if (val2.getTag() != BER.OBJECT_IDENTIFIER) throw new BEREncodingException("malformed AlgorithmIdentifier"); - if (DEBUG) - debug(" ID: " + val2.getValue()); + + log.finest(" digestAlgorithmIdentifiers OID: " + val2.getValue()); List algId = new ArrayList(2); algId.add(val2.getValue()); val2 = ber.read(); @@ -219,29 +214,33 @@ public class PKCS7SignedData algId.add(null); else algId.add(val2.getEncoded()); - if (DEBUG) - debug(" params: " + new BigInteger(1, val2.getEncoded()).toString(16)); + if (val2.isConstructed()) ber.skip(val2.getLength()); + if (BERValue.isIndefinite(val)) val2 = ber.read(); } else algId.add(null); + + log.finest(" digestAlgorithmIdentifiers params: "); + log.finest(Util.dumpString((byte[]) algId.get(1), + " digestAlgorithmIdentifiers params: ")); digestAlgorithms.add(algId); } val = ber.read(); if (!val.isConstructed()) throw new BEREncodingException("malformed ContentInfo"); - if (DEBUG) - debug(" ContentInfo: " + val); + + log.finest(" ContentInfo: " + val); val2 = ber.read(); if (val2.getTag() != BER.OBJECT_IDENTIFIER) throw new BEREncodingException("malformed ContentType"); + contentType = (OID) val2.getValue(); - if (DEBUG) - debug(" ContentType: " + contentType); + log.finest(" ContentType OID: " + contentType); if (BERValue.isIndefinite(val) || (val.getLength() > 0 && val.getLength() > val2.getEncodedLength())) { @@ -251,18 +250,19 @@ public class PKCS7SignedData content = val2.getEncoded(); if (BERValue.isIndefinite(val)) val2 = ber.read(); - if (DEBUG) - debug(" Content: " + new BigInteger(1, content).toString(16)); } } + log.finest(" Content: "); + log.finest(Util.dumpString(content, " Content: ")); + val = ber.read(); if (val.getTag() == 0) { if (!val.isConstructed()) throw new BEREncodingException("malformed ExtendedCertificatesAndCertificates"); - if (DEBUG) - debug(" ExtendedCertificatesAndCertificates: " + val); + + log.finest(" ExtendedCertificatesAndCertificates: " + val); count = 0; val2 = ber.read(); List certs = new LinkedList(); @@ -271,8 +271,7 @@ public class PKCS7SignedData { Certificate cert = x509.generateCertificate(new ByteArrayInputStream(val2.getEncoded())); - if (DEBUG) - debug(" Certificate: " + cert); + log.finest(" Certificate: " + cert); certs.add(cert); count += val2.getEncodedLength(); ber.skip(val2.getLength()); @@ -287,8 +286,8 @@ public class PKCS7SignedData { if (!val.isConstructed()) throw new BEREncodingException("malformed CertificateRevocationLists"); - if (DEBUG) - debug(" CertificateRevocationLists: " + val); + + log.finest(" CertificateRevocationLists: " + val); count = 0; val2 = ber.read(); List crls = new LinkedList(); @@ -296,8 +295,7 @@ public class PKCS7SignedData (val.getLength() > 0 && val.getLength() > count)) { CRL crl = x509.generateCRL(new ByteArrayInputStream(val2.getEncoded())); - if (DEBUG) - debug (" CRL: " + crl); + log.finest(" CRL: " + crl); crls.add(crl); count += val2.getEncodedLength(); ber.skip(val2.getLength()); @@ -312,8 +310,7 @@ public class PKCS7SignedData if (!val.isConstructed()) throw new BEREncodingException("malformed SignerInfos"); - if (DEBUG) - debug(" SignerInfos: " + val); + log.finest(" SignerInfos: " + val); // FIXME read this more carefully. // Since we are just reading a file (probably) we just read until we @@ -327,6 +324,39 @@ public class PKCS7SignedData } } + /** + * Constructs a new instance of <code>PKCS7SignedData</code> given a + * designated set of fields. + * + * @param digestAlgorithms the collection of DigestAlgorithm elements. Each + * DigestAlgorithm is a {@link List} of two elements, the first is an + * OID while the second is dependent on the value of the OID element. + * @param data an instance of a PKCS#7 (non-signed) data. In its simplest form + * such an ASN.1 structure would consist of just the OID of a + * non-signed PKCS#7 Data. + * @param certificates the array of Certificates used to authenticate the + * enclosed (or referenced, in case the content is null) data. + * @param crls the array of certificate-revocation lists of the used + * certificates. + * @param signerInfos a set of {@link SignerInfo} elements, one per signer of + * the data referenced by this <code>PKCS7SignedData</code> + * instance. + */ + public PKCS7SignedData(Set digestAlgorithms, PKCS7Data data, + Certificate[] certificates, X509CRL[] crls, + Set signerInfos) + { + super(); + + this.version = BigInteger.ONE; + this.digestAlgorithms = digestAlgorithms; + this.contentType = PKCS7_SIGNED_DATA; + this.content = data == null ? null : data.getEncoded(); + this.certificates = certificates; + this.crls = crls; + this.signerInfos = signerInfos; + } + public BigInteger getVersion() { return version; @@ -361,4 +391,89 @@ public class PKCS7SignedData copy.add(it.next()); return Collections.unmodifiableSet(copy); } + + /** + * Writes to the designated output stream the DER encoding of the current + * contents of this instance. + * + * @param out the destination output stream. + * @throws IOException if an I/O related exception occurs during the process. + * @throws CRLException if an exception occurs while encoding the certificate + * revocation lists associated with this instance. + * @throws CertificateEncodingException if an exception occurs while encoding + * the certificate chains associated with this instance. + */ + public void encode(OutputStream out) throws IOException, CRLException, + CertificateEncodingException + { + DERValue derVersion = new DERValue(DER.INTEGER, version); + + DERValue derDigestAlgorithms = new DERValue(DER.CONSTRUCTED | DER.SET, + digestAlgorithms); + + DERValue derContentType = new DERValue(DER.OBJECT_IDENTIFIER, + PKCS7Data.PKCS7_DATA); + ArrayList contentInfo = new ArrayList(2); + contentInfo.add(derContentType); + if (content == null) + contentInfo.add(new DERValue(DER.NULL, null)); + else + contentInfo.add(content); + + DERValue derContentInfo = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + contentInfo); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); + for (int i = 0; i < certificates.length; i++) + baos.write(certificates[i].getEncoded()); + + baos.flush(); + byte[] b = baos.toByteArray(); + DERValue derExtendedCertificatesAndCertificates = + new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 0, b.length, b, null); + + DERValue derCertificateRevocationLists = null; + if (crls != null && crls.length > 0) + { + baos.reset(); + for (int i = 0; i < crls.length; i++) + baos.write(((X509CRL) crls[i]).getEncoded()); + + baos.flush(); + byte[] b2 = baos.toByteArray(); + derCertificateRevocationLists = + new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 1, b2.length, b2, null); + } + + baos.reset(); + for (Iterator it = signerInfos.iterator(); it.hasNext();) + { + SignerInfo signerInfo = (SignerInfo) it.next(); + signerInfo.encode(baos); + } + baos.flush(); + byte[] b3 = baos.toByteArray(); + DERValue derSignerInfos = new DERValue(DER.CONSTRUCTED | DER.SET, + b3.length, b3, null); + + ArrayList signedData = new ArrayList(6); + signedData.add(derVersion); + signedData.add(derDigestAlgorithms); + signedData.add(derContentInfo); + signedData.add(derExtendedCertificatesAndCertificates); + if (derCertificateRevocationLists != null) + signedData.add(derCertificateRevocationLists); + + signedData.add(derSignerInfos); + DERValue derSignedData = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + signedData); + // now the outer contents + ArrayList outer = new ArrayList(3); + outer.add(new DERValue(DER.OBJECT_IDENTIFIER, PKCS7_SIGNED_DATA)); + outer.add(new DERValue(DER.CONTEXT | 0, null)); + outer.add(derSignedData); + DERValue derOuter = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, outer); + + DERWriter.write(out, derOuter); + } } diff --git a/libjava/classpath/gnu/java/security/pkcs/SignerInfo.java b/libjava/classpath/gnu/java/security/pkcs/SignerInfo.java index c976799..7b38bfe 100644 --- a/libjava/classpath/gnu/java/security/pkcs/SignerInfo.java +++ b/libjava/classpath/gnu/java/security/pkcs/SignerInfo.java @@ -42,16 +42,25 @@ import gnu.java.security.ber.BER; import gnu.java.security.ber.BEREncodingException; import gnu.java.security.ber.BERReader; import gnu.java.security.ber.BERValue; +import gnu.java.security.der.DER; import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; +import gnu.java.security.util.Util; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.logging.Logger; import javax.security.auth.x500.X500Principal; public class SignerInfo { + private static final Logger log = Logger.getLogger(SignerInfo.class.getName()); + private final BigInteger version; private final BigInteger serialNumber; private final X500Principal issuer; @@ -63,67 +72,80 @@ public class SignerInfo private final byte[] encryptedDigest; private final byte[] unauthenticatedAttributes; - private static final boolean DEBUG = false; - private static void debug(String msg) - { - System.err.print("SignerInfo >> "); - System.err.println(msg); - } - /** * Parse a SignerInfo object. + * <p> + * A SignerInfo is a structure with the following ASN.1 syntax: + * <pre> + * SignerInfo ::= SEQUENCE { + * version Version, -- always 1 for PKCS7 v1.5 + * issuerAndSerialNumber IssuerAndSerialNumber, -- an INTEGER + * digestAlgorithm DigestAlgorithmIdentifier, + * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + * encryptedDigest EncryptedDigest, + * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL } + * + * IssuerAndSerialNumber ::= SEQUENCE { + * issuer Name, + * serialNumber CertificateSerialNumber + * } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * + * EncryptedDigest ::= OCTET STRING + * </pre> */ public SignerInfo(BERReader ber) throws IOException { DERValue val = ber.read(); - if (DEBUG) - debug("SignerInfo: " + val); + log.finest("SignerInfo: " + val); if (!val.isConstructed()) throw new BEREncodingException("malformed SignerInfo"); val = ber.read(); if (val.getTag() != BER.INTEGER) throw new BEREncodingException("malformed Version"); - version = (BigInteger) val.getValue(); - if (DEBUG) - debug(" Version: " + version); + version = (BigInteger) val.getValue(); + log.finest(" Version: " + version); val = ber.read(); if (!val.isConstructed()) throw new BEREncodingException("malformed IssuerAndSerialNumber"); - if (DEBUG) - debug(" IssuerAndSerialNumber: " + val); + log.finest(" IssuerAndSerialNumber: " + val); val = ber.read(); if (!val.isConstructed()) throw new BEREncodingException("malformed Issuer"); + issuer = new X500Principal(val.getEncoded()); ber.skip(val.getLength()); - if (DEBUG) - debug(" Issuer: " + issuer); + log.finest(" Issuer: " + issuer); val = ber.read(); if (val.getTag() != BER.INTEGER) throw new BEREncodingException("malformed SerialNumber"); + serialNumber = (BigInteger) val.getValue(); - if (DEBUG) - debug(" SerialNumber: " + serialNumber); + log.finest(" SerialNumber: " + serialNumber); val = ber.read(); if (!val.isConstructed()) throw new BEREncodingException("malformed DigestAlgorithmIdentifier"); - if (DEBUG) - debug(" DigestAlgorithmIdentifier: " + val); + + log.finest(" DigestAlgorithmIdentifier: " + val); int count = 0; DERValue val2 = ber.read(); if (val2.getTag() != BER.OBJECT_IDENTIFIER) throw new BEREncodingException("malformed AlgorithmIdentifier"); + digestAlgorithmId = (OID) val2.getValue(); - if (DEBUG) - debug(" OID: " + digestAlgorithmId); + log.finest(" digestAlgorithm OID: " + digestAlgorithmId); if (BERValue.isIndefinite(val)) { @@ -147,9 +169,10 @@ public class SignerInfo } else digestAlgorithmParams = null; - if(DEBUG) - debug(" params: " + (digestAlgorithmParams == null ? null - : new BigInteger(digestAlgorithmParams).toString(16))); + + log.finest(" digestAlgorithm params: "); + log.finest(Util.dumpString(digestAlgorithmParams, + " digestAlgorithm params: ")); val = ber.read(); if (val.getTag() == 0) @@ -158,24 +181,27 @@ public class SignerInfo val = ber.read(); if (val.isConstructed()) ber.skip(val.getLength()); - if (DEBUG) - debug(" AuthenticatedAttributes: " + val); + val = ber.read(); } else authenticatedAttributes = null; + log.finest(" AuthenticatedAttributes: "); + log.finest(Util.dumpString(authenticatedAttributes, + " AuthenticatedAttributes: ")); + if (!val.isConstructed()) throw new BEREncodingException("malformed DigestEncryptionAlgorithmIdentifier"); - if (DEBUG) - debug(" DigestEncryptionAlgorithmIdentifier: " + val); + + log.finest(" DigestEncryptionAlgorithmIdentifier: " + val); count = 0; val2 = ber.read(); if (val2.getTag() != BER.OBJECT_IDENTIFIER) throw new BEREncodingException("malformed AlgorithmIdentifier"); + digestEncryptionAlgorithmId = (OID) val2.getValue(); - if (DEBUG) - debug(" OID: " + digestEncryptionAlgorithmId); + log.finest(" digestEncryptionAlgorithm OID: " + digestEncryptionAlgorithmId); if (BERValue.isIndefinite(val)) { @@ -199,26 +225,74 @@ public class SignerInfo } else digestEncryptionAlgorithmParams = null; - if(DEBUG) - debug(" params: " + (digestEncryptionAlgorithmParams == null ? null - : new BigInteger(digestEncryptionAlgorithmParams).toString(16))); + + log.finest(" digestEncryptionAlgorithm params: "); + log.finest(Util.dumpString(digestEncryptionAlgorithmParams, + " digestEncryptionAlgorithm params: ")); val = ber.read(); if (val.getTag() != BER.OCTET_STRING) throw new BEREncodingException("malformed EncryptedDigest"); + encryptedDigest = (byte[]) val.getValue(); - if (DEBUG) - debug(" EncryptedDigest: " + new BigInteger(1, encryptedDigest).toString(16)); + log.finest(" EncryptedDigest: "); + log.finest(Util.dumpString(encryptedDigest, " EncryptedDigest: ")); if (ber.peek() == 1) unauthenticatedAttributes = ber.read().getEncoded(); else unauthenticatedAttributes = null; + log.finest(" UnauthenticatedAttributes: "); + log.finest(Util.dumpString(unauthenticatedAttributes, + " UnauthenticatedAttributes: ")); + if (ber.peek() == 0) ber.read(); } + /** + * Constructs a new instance of <code>SignerInfo</code> given a designated + * set of fields. + * + * @param issuer the X.500 Principal name of the signer referenced by this + * instance. + * @param serialNumber the serial number of the certificate being used. Both + * this and the previous arguments are gleaned from the signer's + * certificate. + * @param digestAlgorithmOID the OID of the digest algorithm. When + * constructing the DigestAlgorithmIdentifier with this OID, the + * parameters part will be NULL. + * @param authenticatedAttributes the encoding of the set of authenticated + * attributes to use. + * @param digestEncryptionAlgorithmOID the OID of the digest encryption + * algorithm. When constructing the + * DigestEncryptionAlgorithmIdentifier with this OID, the parameters + * part will be NULL. + * @param encryptedDigest the encrypted hash generated with this signer's + * private key. + * @param unauthenticatedAttributes the encoding of the set of + * unauthencticated attributes. + */ + public SignerInfo(X500Principal issuer, BigInteger serialNumber, + OID digestAlgorithmOID, byte[] authenticatedAttributes, + OID digestEncryptionAlgorithmOID, + byte[] encryptedDigest, byte[] unauthenticatedAttributes) + { + super(); + + this.version = BigInteger.ONE; + this.issuer = issuer; + this.serialNumber = serialNumber; + this.digestAlgorithmId = digestAlgorithmOID; + this.digestAlgorithmParams = null; + this.authenticatedAttributes = authenticatedAttributes; + this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmOID; + this.digestEncryptionAlgorithmParams = null; + this.encryptedDigest = encryptedDigest; + this.unauthenticatedAttributes = unauthenticatedAttributes; + } + public BigInteger getVersion() { return version; @@ -276,4 +350,65 @@ public class SignerInfo ? (byte[]) unauthenticatedAttributes.clone() : null); } + + /** + * Writes to the designated output stream the DER encoding of the current + * contents of this instance. + * + * @param out the destination output stream. + * @throws IOException if an I/O related exception occurs during the process. + */ + public void encode(OutputStream out) throws IOException + { + DERValue derVersion = new DERValue(DER.INTEGER, version); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); + baos.write(issuer.getEncoded()); + DERValue derSerialNumber = new DERValue(DER.INTEGER, serialNumber); + DERWriter.write(baos, derSerialNumber); + baos.flush(); + byte[] b = baos.toByteArray(); + DERValue derIssuerAndSerialNumber = + new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, b.length, b, null); + + DERValue derDigestAlgorithmOID = new DERValue(DER.OBJECT_IDENTIFIER, + digestAlgorithmId); + ArrayList digestAlgorithmIdentifier = new ArrayList(1); + digestAlgorithmIdentifier.add(derDigestAlgorithmOID); + DERValue derDigestAlgorithmIdentifier = + new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, digestAlgorithmIdentifier); + + DERValue derAuthenticatedAttributes; + if (authenticatedAttributes == null) + derAuthenticatedAttributes = new DERValue(DER.NULL, null); + else + derAuthenticatedAttributes = new DERValue(DER.CONSTRUCTED | DER.SET, + authenticatedAttributes); + + DERValue derDigestEncryptionAlgorithmOID = + new DERValue(DER.OBJECT_IDENTIFIER, digestEncryptionAlgorithmId); + ArrayList digestEncryptionAlgorithmIdentifier = new ArrayList(1); + digestEncryptionAlgorithmIdentifier.add(derDigestEncryptionAlgorithmOID); + DERValue derDigestEncryptionAlgorithmIdentifier = + new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, digestEncryptionAlgorithmIdentifier); + + DERValue derEncryptedDigest = new DERValue(DER.OCTET_STRING, encryptedDigest); + + DERValue derUnauthenticatedAttributes; + if (unauthenticatedAttributes == null) + derUnauthenticatedAttributes = new DERValue(DER.NULL, null); + else + derUnauthenticatedAttributes = new DERValue(DER.CONSTRUCTED | DER.SET, + unauthenticatedAttributes); + + ArrayList signerInfo = new ArrayList(5); + signerInfo.add(derVersion); + signerInfo.add(derIssuerAndSerialNumber); + signerInfo.add(derDigestAlgorithmIdentifier); + signerInfo.add(derDigestEncryptionAlgorithmIdentifier); + signerInfo.add(derEncryptedDigest); + DERValue derSignerInfo = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + signerInfo); + DERWriter.write(out, derSignerInfo); + } } diff --git a/libjava/classpath/gnu/java/security/prng/MDGenerator.java b/libjava/classpath/gnu/java/security/prng/MDGenerator.java index 255647d..073c559 100644 --- a/libjava/classpath/gnu/java/security/prng/MDGenerator.java +++ b/libjava/classpath/gnu/java/security/prng/MDGenerator.java @@ -122,6 +122,20 @@ public class MDGenerator extends BasePRNG implements Cloneable md.update(buffer, 0, buffer.length); } + public void addRandomByte (final byte b) + { + if (md == null) + throw new IllegalStateException ("not initialized"); + md.update (b); + } + + public void addRandomBytes (final byte[] buf, final int off, final int len) + { + if (md == null) + throw new IllegalStateException ("not initialized"); + md.update (buf, off, len); + } + // Cloneable interface implementation --------------------------------------- public Object clone() throws CloneNotSupportedException diff --git a/libjava/classpath/gnu/java/security/provider/DSAParameterGenerator.java b/libjava/classpath/gnu/java/security/provider/DSAParameterGenerator.java deleted file mode 100644 index ccec113..0000000 --- a/libjava/classpath/gnu/java/security/provider/DSAParameterGenerator.java +++ /dev/null @@ -1,128 +0,0 @@ -/* DSAParameterGenerator.java --- DSA Parameter Generator Implementation - Copyright (C) 1999 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.java.security.provider; - -import gnu.java.security.util.Prime; - -import java.math.BigInteger; -import java.security.AlgorithmParameterGeneratorSpi; -import java.security.AlgorithmParameters; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.DSAParameterSpec; -import java.security.spec.InvalidParameterSpecException; -import java.util.Random; - -public class DSAParameterGenerator extends AlgorithmParameterGeneratorSpi -{ - private int size; - private SecureRandom random = null; - - public DSAParameterGenerator() - { - size = 1024; - } - - public void engineInit(int size, SecureRandom random) - { - if( (size < 512) || (size > 1024) || ( (size % 64) != 0) ) - //throw new InvalidAlgorithmParameterException("Invalid Size"); - return; - this.size = size; - this.random = random; - } - - public void engineInit(AlgorithmParameterSpec genParamSpec, SecureRandom random) - throws InvalidAlgorithmParameterException - { - if( !( genParamSpec instanceof DSAParameterSpec ) ) - throw new InvalidAlgorithmParameterException("Must be DSAParameterSpec"); - - DSAParameterSpec dsaparameterspec = (DSAParameterSpec)genParamSpec; - int tmp = dsaparameterspec.getP().bitLength(); - - if( (tmp < 512) || (tmp > 1024) || ( (tmp % 64) != 0) ) - throw new InvalidAlgorithmParameterException("Invalid Size"); - - this.random = random; - } - - //For more information see IEEE P1363 A.16.1 (10/05/98 Draft) - public AlgorithmParameters engineGenerateParameters() - { - DSAParameterSpec dsaparameterspec; - - int L = size; - BigInteger r, p, k, h, g; - - //q 2^159 < q < 2^160 - r = Prime.generateRandomPrime( 159, 160, BigInteger.valueOf(1)); - - // 2^(L-1) < p < 2^L - p = Prime.generateRandomPrime( r, BigInteger.valueOf(1), L - 1, L, BigInteger.valueOf(1)); - - k = p.subtract( BigInteger.valueOf(1) ); - k = k.divide( r ); - - Random rand = new Random(); - h = BigInteger.valueOf(1); - - for(;;) { - h = h.add(BigInteger.valueOf( 1 ) ); - - g = h.modPow(k, p); - - if( g.compareTo( BigInteger.valueOf(1) ) != 1 ) - break; - } - - try { - dsaparameterspec = new DSAParameterSpec(p, r, g); - AlgorithmParameters ap = AlgorithmParameters.getInstance("DSA"); - ap.init( dsaparameterspec ); - return ap; - } catch ( NoSuchAlgorithmException nsae ) { - return null; - } catch ( InvalidParameterSpecException ipse) { - return null; - } - } -} diff --git a/libjava/classpath/gnu/java/security/provider/Gnu.java b/libjava/classpath/gnu/java/security/provider/Gnu.java index 0613566..6ea96c1 100644 --- a/libjava/classpath/gnu/java/security/provider/Gnu.java +++ b/libjava/classpath/gnu/java/security/provider/Gnu.java @@ -86,6 +86,7 @@ public final class Gnu extends Provider put("Alg.Alias.Signature.md5WithRSAEncryption", "MD5withRSA"); put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA"); put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); + put("Alg.Alias.Signature.RSA", "MD5withRSA"); put("Signature.SHA160withRSA", gnu.java.security.jce.sig.SHA160withRSA.class.getName()); diff --git a/libjava/classpath/gnu/java/security/sig/ISignature.java b/libjava/classpath/gnu/java/security/sig/ISignature.java index 77653ee..e77f39d 100644 --- a/libjava/classpath/gnu/java/security/sig/ISignature.java +++ b/libjava/classpath/gnu/java/security/sig/ISignature.java @@ -56,8 +56,6 @@ import java.util.Map; * Cryptography</a>, Alfred J. Menezes, Paul C. van Oorschot and Scott A. * Vanstone. Section 11.2.2 Digital signature schemes with appendix.</li> * </ol> - * - * @version $Revision: 1.1 $ */ public interface ISignature extends Cloneable { diff --git a/libjava/classpath/gnu/java/security/sig/ISignatureCodec.java b/libjava/classpath/gnu/java/security/sig/ISignatureCodec.java index 119eca5..b6ab0ba 100644 --- a/libjava/classpath/gnu/java/security/sig/ISignatureCodec.java +++ b/libjava/classpath/gnu/java/security/sig/ISignatureCodec.java @@ -45,8 +45,6 @@ import gnu.java.security.Registry; * cryptographic signatures. Codecs are useful for (a) externalising signature * output data for storage and on-the-wire transmission, as well as (b) re- * creating their internal Java representation from external sources.</p> - * - * @version $Revision: 1.1 $ */ public interface ISignatureCodec { diff --git a/libjava/classpath/gnu/java/security/sig/dss/DSSSignature.java b/libjava/classpath/gnu/java/security/sig/dss/DSSSignature.java index 6bedfae..370a938 100644 --- a/libjava/classpath/gnu/java/security/sig/dss/DSSSignature.java +++ b/libjava/classpath/gnu/java/security/sig/dss/DSSSignature.java @@ -111,8 +111,6 @@ import java.util.Random; * Signature Standard (DSS)</a>, Federal Information Processing Standards * Publication 186. National Institute of Standards and Technology.</li> * </ol> - * - * @version $Revision: 1.1 $ */ public class DSSSignature extends BaseSignature { diff --git a/libjava/classpath/gnu/java/security/sig/dss/DSSSignatureRawCodec.java b/libjava/classpath/gnu/java/security/sig/dss/DSSSignatureRawCodec.java index 02f6b1d..b0590a5 100644 --- a/libjava/classpath/gnu/java/security/sig/dss/DSSSignatureRawCodec.java +++ b/libjava/classpath/gnu/java/security/sig/dss/DSSSignatureRawCodec.java @@ -47,8 +47,6 @@ import java.math.BigInteger; /** * <p>An object that implements the {@link ISignatureCodec} operations for the * <i>Raw</i> format to use with DSS signatures.</p> - * - * @version $Revision: 1.1 $ */ public class DSSSignatureRawCodec implements ISignatureCodec { diff --git a/libjava/classpath/gnu/java/security/sig/rsa/EMSA_PKCS1_V1_5.java b/libjava/classpath/gnu/java/security/sig/rsa/EMSA_PKCS1_V1_5.java index 2ea8a30..d155fc8 100644 --- a/libjava/classpath/gnu/java/security/sig/rsa/EMSA_PKCS1_V1_5.java +++ b/libjava/classpath/gnu/java/security/sig/rsa/EMSA_PKCS1_V1_5.java @@ -57,8 +57,6 @@ import java.io.ByteArrayOutputStream; * RSA Cryptography Specifications Version 2.1.<br> * Jakob Jonsson and Burt Kaliski.</li> * </ol> - * - * @version $Revision: 1.2 $ */ public class EMSA_PKCS1_V1_5 implements Cloneable { diff --git a/libjava/classpath/gnu/java/security/sig/rsa/EMSA_PSS.java b/libjava/classpath/gnu/java/security/sig/rsa/EMSA_PSS.java index d11a861..c1c9760 100644 --- a/libjava/classpath/gnu/java/security/sig/rsa/EMSA_PSS.java +++ b/libjava/classpath/gnu/java/security/sig/rsa/EMSA_PSS.java @@ -67,8 +67,6 @@ import java.util.Arrays; * Primitive specification and supporting documentation.<br> * Jakob Jonsson and Burt Kaliski.</li> * </ol> - * - * @version $Revision: 1.1 $ */ public class EMSA_PSS implements Cloneable { diff --git a/libjava/classpath/gnu/java/security/sig/rsa/RSAPKCS1V1_5Signature.java b/libjava/classpath/gnu/java/security/sig/rsa/RSAPKCS1V1_5Signature.java index d4b69a7..e64d30b 100644 --- a/libjava/classpath/gnu/java/security/sig/rsa/RSAPKCS1V1_5Signature.java +++ b/libjava/classpath/gnu/java/security/sig/rsa/RSAPKCS1V1_5Signature.java @@ -67,8 +67,6 @@ import java.util.Arrays; * RSA Cryptography Specifications Version 2.1.<br> * Jakob Jonsson and Burt Kaliski.</li> * </ol> - * - * @version $Revision: 1.2 $ */ public class RSAPKCS1V1_5Signature extends BaseSignature { diff --git a/libjava/classpath/gnu/java/security/sig/rsa/RSAPSSSignature.java b/libjava/classpath/gnu/java/security/sig/rsa/RSAPSSSignature.java index 95a6653f..7ec6256 100644 --- a/libjava/classpath/gnu/java/security/sig/rsa/RSAPSSSignature.java +++ b/libjava/classpath/gnu/java/security/sig/rsa/RSAPSSSignature.java @@ -70,8 +70,6 @@ import java.security.interfaces.RSAPublicKey; * Primitive specification and supporting documentation.<br> * Jakob Jonsson and Burt Kaliski.</li> * </ol> - * - * @version $Revision: 1.2 $ */ public class RSAPSSSignature extends BaseSignature { diff --git a/libjava/classpath/gnu/java/security/sig/rsa/RSAPSSSignatureRawCodec.java b/libjava/classpath/gnu/java/security/sig/rsa/RSAPSSSignatureRawCodec.java index 64a972c..2be7916 100644 --- a/libjava/classpath/gnu/java/security/sig/rsa/RSAPSSSignatureRawCodec.java +++ b/libjava/classpath/gnu/java/security/sig/rsa/RSAPSSSignatureRawCodec.java @@ -46,8 +46,6 @@ import java.io.ByteArrayOutputStream; /** * <p>An object that implements the {@link gnu.crypto.sig.ISignatureCodec} * operations for the <i>Raw</i> format to use with RSA-PSS signatures.</p> - * - * @version $Revision: 1.1 $ */ public class RSAPSSSignatureRawCodec implements ISignatureCodec { diff --git a/libjava/classpath/gnu/java/security/util/Util.java b/libjava/classpath/gnu/java/security/util/Util.java index 53f8e3c..f39afb9 100644 --- a/libjava/classpath/gnu/java/security/util/Util.java +++ b/libjava/classpath/gnu/java/security/util/Util.java @@ -42,8 +42,6 @@ import java.math.BigInteger; /** * <p>A collection of utility methods used throughout this project.</p> - * - * @version $Revision: 1.1 $ */ public class Util { diff --git a/libjava/classpath/gnu/java/security/x509/X500DistinguishedName.java b/libjava/classpath/gnu/java/security/x509/X500DistinguishedName.java index daf746f..02adad7 100644 --- a/libjava/classpath/gnu/java/security/x509/X500DistinguishedName.java +++ b/libjava/classpath/gnu/java/security/x509/X500DistinguishedName.java @@ -1,5 +1,5 @@ /* X500DistinguishedName.java -- X.500 distinguished name. - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -61,7 +61,6 @@ import java.util.Set; public class X500DistinguishedName implements Principal { - // Constants and fields. // ------------------------------------------------------------------------- @@ -221,6 +220,10 @@ public class X500DistinguishedName implements Principal putComponent(DC, value); else if (name.equals("uid")) putComponent(UID, value); + else if (name.equals("o")) + putComponent(O, value); + else if (name.equals("ou")) + putComponent(OU, value); else putComponent(new OID(name), value); } @@ -328,16 +331,18 @@ public class X500DistinguishedName implements Principal { if (fixed && encoded != null) return (byte[]) encoded.clone(); + ArrayList name = new ArrayList(components.size()); for (Iterator it = components.iterator(); it.hasNext(); ) { Map m = (Map) it.next(); if (m.isEmpty()) continue; + Set rdn = new HashSet(); for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); ) { - Map.Entry e = (Map.Entry) it.next(); + Map.Entry e = (Map.Entry) it2.next(); ArrayList atav = new ArrayList(2); atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey())); atav.add(new DERValue(DER.UTF8_STRING, e.getValue())); @@ -486,6 +491,9 @@ public class X500DistinguishedName implements Principal throw new EOFException(); default: buf.append((char) ch); + ch = in.read(); + if (ch == -1) + return buf.toString(); } } } diff --git a/libjava/classpath/gnu/java/text/StringFormatBuffer.java b/libjava/classpath/gnu/java/text/StringFormatBuffer.java index 5772186..19b621c 100644 --- a/libjava/classpath/gnu/java/text/StringFormatBuffer.java +++ b/libjava/classpath/gnu/java/text/StringFormatBuffer.java @@ -118,4 +118,10 @@ public class StringFormatBuffer implements FormatBuffer { return buffer; } + + public String toString() + { + return buffer.toString(); + } + } diff --git a/libjava/classpath/gnu/java/util/jar/JarUtils.java b/libjava/classpath/gnu/java/util/jar/JarUtils.java new file mode 100644 index 0000000..c35daec --- /dev/null +++ b/libjava/classpath/gnu/java/util/jar/JarUtils.java @@ -0,0 +1,447 @@ +/* JarUtils.java -- Utility methods for reading/writing Manifest[-like] files + 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.java.util.jar; + +import gnu.classpath.SystemProperties; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.JarException; +import java.util.jar.Attributes.Name; +import java.util.logging.Logger; + +/** + * Utility methods for reading and writing JAR <i>Manifest</i> and + * <i>Manifest-like</i> files. + * <p> + * JAR-related files that resemble <i>Manifest</i> files are Signature files + * (with an <code>.SF</code> extension) found in signed JARs. + */ +public abstract class JarUtils +{ + private static final Logger log = Logger.getLogger(JarUtils.class.getName()); + public static final String META_INF = "META-INF/"; + public static final String DSA_SUFFIX = ".DSA"; + public static final String SF_SUFFIX = ".SF"; + public static final String NAME = "Name"; + + /** + * The original string representation of the manifest version attribute name. + */ + public static final String MANIFEST_VERSION = "Manifest-Version"; + + /** + * The original string representation of the signature version attribute + * name. + */ + public static final String SIGNATURE_VERSION = "Signature-Version"; + + /** Platform-independent line-ending. */ + public static final byte[] CRLF = new byte[] { 0x0D, 0x0A }; + private static final String DEFAULT_MF_VERSION = "1.0"; + private static final String DEFAULT_SF_VERSION = "1.0"; + private static final Name CREATED_BY = new Name("Created-By"); + private static final String CREATOR = SystemProperties.getProperty("java.version") + + " (" + + SystemProperties.getProperty("java.vendor") + + ")"; + + // default 0-arguments constructor + + // Methods for reading Manifest files from InputStream ---------------------- + + public static void + readMFManifest(Attributes attr, Map entries, InputStream in) + throws IOException + { + BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); + readMainSection(attr, br); + readIndividualSections(entries, br); + } + + public static void + readSFManifest(Attributes attr, Map entries, InputStream in) + throws IOException + { + BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); + String version_header = Name.SIGNATURE_VERSION.toString(); + try + { + String version = expectHeader(version_header, br); + attr.putValue(SIGNATURE_VERSION, version); + if (! DEFAULT_SF_VERSION.equals(version)) + log.warning("Unexpected version number: " + version + + ". Continue (but may fail later)"); + } + catch (IOException ioe) + { + throw new JarException("Signature file MUST start with a " + + version_header + ": " + ioe.getMessage()); + } + read_attributes(attr, br); + + // read individual sections + String s = br.readLine(); + while (s != null && s.length() > 0) + { + Attributes eAttr = readSectionName(s, br, entries); + read_attributes(eAttr, br); + s = br.readLine(); + } + } + + private static void readMainSection(Attributes attr, BufferedReader br) + throws IOException + { + // According to the spec we should actually call read_version_info() here. + read_attributes(attr, br); + // Explicitly set Manifest-Version attribute if not set in Main + // attributes of Manifest. + // XXX (rsn): why 0.0 and not 1.0? + if (attr.getValue(Name.MANIFEST_VERSION) == null) + attr.putValue(MANIFEST_VERSION, "0.0"); + } + + private static void readIndividualSections(Map entries, BufferedReader br) + throws IOException + { + String s = br.readLine(); + while (s != null && (! s.equals(""))) + { + Attributes attr = readSectionName(s, br, entries); + read_attributes(attr, br); + s = br.readLine(); + } + } + + /** + * Pedantic method that requires the next attribute in the Manifest to be the + * "Manifest-Version". This follows the Manifest spec closely but reject some + * jar Manifest files out in the wild. + */ + private static void readVersionInfo(Attributes attr, BufferedReader br) + throws IOException + { + String version_header = Name.MANIFEST_VERSION.toString(); + try + { + String value = expectHeader(version_header, br); + attr.putValue(MANIFEST_VERSION, value); + } + catch (IOException ioe) + { + throw new JarException("Manifest should start with a " + version_header + + ": " + ioe.getMessage()); + } + } + + private static String expectHeader(String header, BufferedReader br) + throws IOException + { + String s = br.readLine(); + if (s == null) + throw new JarException("unexpected end of file"); + + return expectHeader(header, br, s); + } + + private static void read_attributes(Attributes attr, BufferedReader br) + throws IOException + { + String s = br.readLine(); + while (s != null && (! s.equals(""))) + { + readAttribute(attr, s, br); + s = br.readLine(); + } + } + + private static void + readAttribute(Attributes attr, String s, BufferedReader br) throws IOException + { + try + { + int colon = s.indexOf(": "); + String name = s.substring(0, colon); + String value_start = s.substring(colon + 2); + String value = readHeaderValue(value_start, br); + attr.putValue(name, value); + } + catch (IndexOutOfBoundsException iobe) + { + throw new JarException("Manifest contains a bad header: " + s); + } + } + + private static String readHeaderValue(String s, BufferedReader br) + throws IOException + { + boolean try_next = true; + while (try_next) + { + // Lets see if there is something on the next line + br.mark(1); + if (br.read() == ' ') + s += br.readLine(); + else + { + br.reset(); + try_next = false; + } + } + return s; + } + + private static Attributes + readSectionName(String s, BufferedReader br, Map entries) throws JarException + { + try + { + String name = expectHeader(NAME, br, s); + Attributes attr = new Attributes(); + entries.put(name, attr); + return attr; + } + catch (IOException ioe) + { + throw new JarException("Section should start with a Name header: " + + ioe.getMessage()); + } + } + + private static String expectHeader(String header, BufferedReader br, String s) + throws IOException + { + try + { + String name = s.substring(0, header.length() + 1); + if (name.equalsIgnoreCase(header + ":")) + { + String value_start = s.substring(header.length() + 2); + return readHeaderValue(value_start, br); + } + } + catch (IndexOutOfBoundsException ignored) + { + } + // If we arrive here, something went wrong + throw new JarException("unexpected '" + s + "'"); + } + + // Methods for writing Manifest files to an OutputStream -------------------- + + public static void + writeMFManifest(Attributes attr, Map entries, OutputStream stream) + throws IOException + { + BufferedOutputStream out = stream instanceof BufferedOutputStream + ? (BufferedOutputStream) stream + : new BufferedOutputStream(stream, 4096); + writeVersionInfo(attr, out); + Iterator i; + Map.Entry e; + for (i = attr.entrySet().iterator(); i.hasNext();) + { + e = (Map.Entry) i.next(); + // Don't print the manifest version again + if (! Name.MANIFEST_VERSION.equals(e.getKey())) + writeAttributeEntry(e, out); + } + out.write(CRLF); + + Iterator j; + for (i = entries.entrySet().iterator(); i.hasNext();) + { + e = (Map.Entry) i.next(); + writeHeader(NAME, e.getKey().toString(), out); + Attributes eAttr = (Attributes) e.getValue(); + for (j = eAttr.entrySet().iterator(); j.hasNext();) + { + Map.Entry e2 = (Map.Entry) j.next(); + writeAttributeEntry(e2, out); + } + out.write(CRLF); + } + + out.flush(); + } + + public static void + writeSFManifest(Attributes attr, Map entries, OutputStream stream) + throws IOException + { + BufferedOutputStream out = stream instanceof BufferedOutputStream + ? (BufferedOutputStream) stream + : new BufferedOutputStream(stream, 4096); + writeHeader(Name.SIGNATURE_VERSION.toString(), DEFAULT_SF_VERSION, out); + writeHeader(CREATED_BY.toString(), CREATOR, out); + Iterator i; + Map.Entry e; + for (i = attr.entrySet().iterator(); i.hasNext();) + { + e = (Map.Entry) i.next(); + Name name = (Name) e.getKey(); + if (Name.SIGNATURE_VERSION.equals(name) || CREATED_BY.equals(name)) + continue; + + writeHeader(name.toString(), (String) e.getValue(), out); + } + out.write(CRLF); + + Iterator j; + for (i = entries.entrySet().iterator(); i.hasNext();) + { + e = (Map.Entry) i.next(); + writeHeader(NAME, e.getKey().toString(), out); + Attributes eAttr = (Attributes) e.getValue(); + for (j = eAttr.entrySet().iterator(); j.hasNext();) + { + Map.Entry e2 = (Map.Entry) j.next(); + writeHeader(e2.getKey().toString(), (String) e2.getValue(), out); + } + out.write(CRLF); + } + + out.flush(); + } + + private static void writeVersionInfo(Attributes attr, OutputStream out) + throws IOException + { + // First check if there is already a version attribute set + String version = attr.getValue(Name.MANIFEST_VERSION); + if (version == null) + version = DEFAULT_MF_VERSION; + + writeHeader(Name.MANIFEST_VERSION.toString(), version, out); + } + + private static void writeAttributeEntry(Map.Entry entry, OutputStream out) + throws IOException + { + String name = entry.getKey().toString(); + String value = entry.getValue().toString(); + if (name.equalsIgnoreCase(NAME)) + throw new JarException("Attributes cannot be called 'Name'"); + + if (name.startsWith("From")) + throw new JarException("Header cannot start with the four letters 'From'" + + name); + + writeHeader(name, value, out); + } + + /** + * The basic method for writing <code>Mainfest</code> attributes. This + * implementation respects the rule stated in the Jar Specification concerning + * the maximum allowed line length; i.e. + * + * <pre> + * No line may be longer than 72 bytes (not characters), in its UTF8-encoded + * form. If a value would make the initial line longer than this, it should + * be continued on extra lines (each starting with a single SPACE). + * </pre> + * + * and + * + * <pre> + * Because header names cannot be continued, the maximum length of a header + * name is 70 bytes (there must be a colon and a SPACE after the name). + * </pre> + * + * @param name the name of the attribute. + * @param value the value of the attribute. + * @param out the output stream to write the attribute's name/value pair to. + * @throws IOException if an I/O related exception occurs during the process. + */ + private static void writeHeader(String name, String value, OutputStream out) + throws IOException + { + String target = name + ": "; + byte[] b = target.getBytes("UTF-8"); + if (b.length > 72) + throw new IOException("Attribute's name already longer than 70 bytes"); + + if (b.length == 72) + { + out.write(b); + out.write(CRLF); + target = " " + value; + } + else + target = target + value; + + int n; + while (true) + { + b = target.getBytes("UTF-8"); + if (b.length < 73) + { + out.write(b); + break; + } + + // find an appropriate character position to break on + n = 72; + while (true) + { + b = target.substring(0, n).getBytes("UTF-8"); + if (b.length < 73) + break; + + n--; + if (n < 1) + throw new IOException("Header is unbreakable and longer than 72 bytes"); + } + + out.write(b); + out.write(CRLF); + target = " " + target.substring(n); + } + + out.write(CRLF); + } +} diff --git a/libjava/classpath/gnu/java/util/prefs/NodeWriter.java b/libjava/classpath/gnu/java/util/prefs/NodeWriter.java index c3cf8e8..574ddc2 100644 --- a/libjava/classpath/gnu/java/util/prefs/NodeWriter.java +++ b/libjava/classpath/gnu/java/util/prefs/NodeWriter.java @@ -168,7 +168,7 @@ public class NodeWriter { } else { bw.write("system"); } - bw.write("\"/>"); + bw.write("\">"); writeRootMap(); writeNode(); |